Вызов функций с оптимизацией LTCG через SDKCall на windows

komashchenko

Идиот
IDIOT
Меценат
Контент-мейкер
Сообщения
896
Реакции
2,411
Поскольку при использовании LTCG внутренние функции часто имеют собственное соглашение о вызовах, то просто так их вызвать через SDKCall нельзя, поэтому нужно сделать функцию "транслятор" которая будет соответствовать stdcall и вызывать нужную функцию.

Для примера возьмем CBaseAnimating::SequenceDuration она девает возврат через xmm0, функция "транслятор" получилась такая
C-подобный:
55                      push   ebp
89 e5                   mov    ebp,esp
8b 45 0c                mov    eax,DWORD PTR [ebp+0xc]
50                      push   eax
8b 45 08                mov    eax,DWORD PTR [ebp+0x8]
50                      push   eax
b8 00 00 00 00          mov    eax,0x0 ; в 0x0 запишем адрес "кривой" функции
ff d0                   call   eax
f3 0f 11 44 24 fc       movss  DWORD PTR [esp-0x4],xmm0 ; перемещаем значение из xmm0 в стек
d9 44 24 fc             fld    DWORD PTR [esp-0x4] ; перемещаем значение из стека в вершину FPU стека (куда и должно по нормальному перемещаться возвращаемое значение с плавающей точкой)
89 ec                   mov    esp,ebp
5d                      pop    ebp
c2 08 00                ret    0x8

Осталось самое главное реализовать это как-то на SP и тут на помощь приходит очень хорошая статья Блог Контика: Работа с памятью в SourcePawn основываясь на ней я сделал такой пример
PHP:
#include sdkhooks
#include sdktools
#pragma semicolon 1

Handle g_hConfig;

public void OnPluginStart()
{
    g_hConfig = LoadGameConfigFile("windows_pizdez");
    if(g_hConfig == null)
    {
        SetFailState("Геймдата не найдена");
        return;
    }
   
    RegConsoleCmd("sm_test", sm_test);
}

public Action sm_test(int iClient, int args)
{
    float fBuf = CBaseAnimating_SequenceDuration(iClient, 0, 0);
   
    PrintToServer("CBaseAnimating_SequenceDuration %f", fBuf);
   
    return Plugin_Handled;
}

float CBaseAnimating_SequenceDuration(int iClient, int pStudioHdr, int iSequence)
{
    //Функция делат возврат через xmm0 поэтому исправляем
   
    static Handle hSequenceDuration = null;
    static Address pMemory = Address_Null;
    static char ASMTRAMPOLINE[] = "\x55\x89\xE5\x8B\x45\x0C\x50\x8B\x45\x08\x50\xB8\x00\x00\x00\x00\xFF\xD0\xF3\x0F\x11\x44\x24\xFC\xD9\x44\x24\xFC\x89\xEC\x5D\xC2\x08\x00";
    /*
    https://defuse.ca/online-x86-assembler.htm
    55                      push   ebp
    89 e5                   mov    ebp,esp
    8b 45 0c                mov    eax,DWORD PTR [ebp+0xc]
    50                      push   eax
    8b 45 08                mov    eax,DWORD PTR [ebp+0x8]
    50                      push   eax
    b8 00 00 00 00          mov    eax,0x0 ; pSequenceDuration
    ff d0                   call   eax
    f3 0f 11 44 24 fc       movss  DWORD PTR [esp-0x4],xmm0
    d9 44 24 fc             fld    DWORD PTR [esp-0x4]
    89 ec                   mov    esp,ebp
    5d                      pop    ebp
    c2 08 00                ret    0x8
    */
   
    if(hSequenceDuration == null)
    {
        Address pSequenceDuration = GameConfGetAddress(g_hConfig, "CBaseAnimating::SequenceDuration");
        if(pSequenceDuration == Address_Null)
        {
            SetFailState("Не удалось получить адрес CBaseAnimating::SequenceDuration");
            return 0.0;
        }
       
        pMemory = CreateMemoryForSDKCall();
       
        StartPrepSDKCall(SDKCall_Entity);
        PrepSDKCall_SetAddress(pMemory);
        PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain);
        PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain);
        PrepSDKCall_SetReturnInfo(SDKType_Float, SDKPass_Plain);
        hSequenceDuration = EndPrepSDKCall();
       
        WriteDWORD(ASMTRAMPOLINE, pSequenceDuration, 12); //Запишем адрес функции
    }
   
    memcpy(pMemory, ASMTRAMPOLINE, sizeof ASMTRAMPOLINE);
   
    return SDKCall(hSequenceDuration, iClient, pStudioHdr, iSequence);
}

Address CreateMemoryForSDKCall()
{
    static Address pZeroMem = Address_Null;
   
    if(pZeroMem != Address_Null)
    {
        return pZeroMem;
    }
   
    Address pServerBase = GameConfGetAddress(g_hConfig, "server");
   
    int pAddr = view_as<int>(pServerBase) + GetModuleSize(pServerBase) - 1;
   
    for(;;)
    {
        int b = LoadFromAddress(view_as<Address>(pAddr), NumberType_Int8);
        if(b != 0x00)
        {
            break;
        }
       
        pAddr--;
    }
   
    /* Align for safe code injection */
    pZeroMem = view_as<Address>(pAddr + 0x100 & 0xFFFFFF00); //255 bytes
   
    return pZeroMem;
}

int GetModuleSize(Address pAddr)
{
    int iOffset = LoadFromAddress(pAddr + view_as<Address>(0x3C), NumberType_Int32); // NT headers offset
    return LoadFromAddress(pAddr + view_as<Address>(iOffset + 0x50), NumberType_Int32); // nt->OptionalHeader.SizeOfImage
}

void memcpy(Address pAddr, char[] data, int iSize)
{
    //Чтобы ускорить копирование
    int iSize2 = iSize / 4;
   
    ____memcpy4b(pAddr, view_as<any>(data), iSize2);
   
    //Копирование остатка
    for(iSize2 *= 4, pAddr += view_as<Address>(iSize2); iSize2 < iSize; iSize2++)
    {
        StoreToAddress(pAddr++, data[iSize2], NumberType_Int8);
    }
}

void ____memcpy4b(Address pAddr, any[] data, int iSize)
{
    //Сразу копирует по 4 байта
    for(int i = 0; i < iSize; i++)
    {
        StoreToAddress(pAddr, data[i], NumberType_Int32);
        pAddr+=view_as<Address>(4);
    }
}

void WriteDWORD(char[] ASM, any pAddr, int iOffset = 0)
{
    ASM[iOffset] = pAddr & 0xFF;
    ASM[iOffset+1] = pAddr >> 8 & 0xFF;
    ASM[iOffset+2] = pAddr >> 16 & 0xFF;
    ASM[iOffset+3] = pAddr >> 24 & 0xFF;
}
C-подобный:
"Games"
{
    "#default"
    {
        "Addresses"
        {
            "server"
            {
                "signature" "Find_Server"
            }
            "CBaseAnimating::SequenceDuration"
            {
                "signature" "CBaseAnimating::SequenceDuration"
            }
        }
        "Signatures"
        {
            "Find_Server"
            {
                "library"    "server"
                "windows"    "\x4D\x5A"
            }
            "CBaseAnimating::SequenceDuration"
            {
                "library"    "server"
                "windows"    "\x55\x8B\xEC\x56\x8B\x75\x08\x57\x8B\xF9\x85\xF6\x75\x2A\x8B\x47\x60"
            }
        }
    }
}
 

Vit_ amin

Участник
Сообщения
1,340
Реакции
534
Доброго времени суток. Насколько я знаю LTCG есть только на MSVC, но есть ли что-то подобное на UNIX ? Потому что я никак не могу через SDKCall вызвать функцию получения IP адреса (вне зависимости за NAT сервер или нет), я про ISteamGameServer::GetPublicIP ( в моём случае игра CS: Source Steam), соответсвенно как я пытаюсь вызвать:
C-подобный:
    StartPrepSDKCall(SDKCall_Static);

    PrepSDKCall_SetFromConf(g_hGameData, SDKConf_Signature, "Steam3Server");

    PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain);

    Handle hSteam3Server = EndPrepSDKCall();

    Address pSteam3Server = LoadFromAddress(SDKCall(hSteam3Server) + OFFSET(0x04), DWORD);
    delete hSteam3Server;

    StartPrepSDKCall(SDKCall_Raw);
    PrepSDKCall_SetVirtual(36);        // ISteamGameServer::GetPublicIP()
    PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_ByRef, _ VENCODE_FLAG_COPYBACK);

    Handle hGetPublicIP = EndPrepSDKCall();

    int ip;
    SDKCall(hGetPublicIP, pSteam3Server, ip);
    delete hGetPublicIP;

Псевдокод, откуда я взял получение singleton Steam3Server (из функции: CSteam3Server::OnLogonSuccess)
C-подобный:
        MsgAndLog("Connection to Steam servers successful.\n");
        v3 = *(Steam3Server + 4);
        if ( !v3
          || ((*(*v3 + 144))(&v12),
              MsgAndLog("   Public IP is %d.%d.%d.%d.\n", v12 >> 24, (v12 >> 16) & 0xFF, BYTE1(v12), v12),
              (result = *(Steam3Server + 4)) == 0) )
        {
Краш происходит в момент dereference EAX, вот вывод из GDB в функции получения IP (из steamclient.so):
C-подобный:
.text:003ADF20                                   var_4           = dword ptr -4
.text:003ADF20                                   arg_0           = dword ptr  8
.text:003ADF20                                   arg_4           = dword ptr  0Ch
.text:003ADF20
.text:003ADF20 000 55                                            push    ebp
.text:003ADF21 004 89 E5                                         mov     ebp, esp
.text:003ADF23 004 56                                            push    esi
.text:003ADF24 008 83 EC 14                                      sub     esp, 14h        ; Integer Subtraction
.text:003ADF27 01C 8B 45 0C                                      mov     eax, [ebp+arg_4]
.text:003ADF2A 01C 8B 75 08                                      mov     esi, [ebp+arg_0]
.text:003ADF2D 01C 8B 40 04                                      mov     eax, [eax+4]
=> .text:003ADF30 01C 8B 10                                         mov     edx, [eax] - CRASH POINT
.text:003ADF32 01C 89 44 24 04                                   mov     [esp+4], eax
.text:003ADF36 01C 89 34 24                                      mov     [esp], esi
.text:003ADF39 01C FF 92 9C 00 00 00                             call    dword ptr [edx+9Ch] ; Indirect Call Near Procedure
.text:003ADF3F 018 89 F0                                         mov     eax, esi
.text:003ADF41 018 8B 75 FC                                      mov     esi, [ebp+var_4]
.text:003ADF44 018 83 EC 04                                      sub     esp, 4          ; Integer Subtraction
.text:003ADF47 01C C9                                            leave                   ; High Level Procedure Exit
.text:003ADF48 000 C2 04 00                                      retn    4               ; Return Near from Procedure
  ...
Собственно как я ни пытался вызвать функцию получения IP адреса, все время ловлю краш, я так полагаю, что компилятор очень оптимизировал эту функцию какими то своимим алгоритами (я так полагаю) или есть предположение, что SDKCall не может по каким то соображениям вызвать эту функцию корректно, собственно сама функцию в том же isteamgameserver.h)
Сам псевдокод этой функции из steamclient.so:
C-подобный:
int __userpurge ISteamGameServer::GetPublicIP@<eax>(int a1, int a2)
{
  (*(**(a2 + 4) + 156))(a1);
  return a1;
}
Насколько можно верить псевдокоду данной функции я не знаю, может быть проблема с соглашением вызова ?
Если не ошибаюсь (где писал выше о недоверие к псевдокоду) то соглашение о вызове userpurge - говорит дизассемблеру о невозможности точно указать какие и сколько аргументов у функции
Собственно с другими вызовами виртуульных функций не было каких то пробел ...
Спасибо
Сообщения автоматически склеены:

Решено, спасибо @BHaType
C-подобный:
#pragma semicolon 1
#pragma newdecls required
 
#include <sourcemod>
#include <sdktools>
 
#include <sourcescramble>
 
Handle g_hGetSteam3Server, g_hGetIP;
Address g_pSteam3Server, ISteamServer;
Address pWrapper;

stock any Dereference( Address ptr, int offset = 0, NumberType type = NumberType_Int32 )
{
    #if defined _DEBUG
    if ( ptr == Address_Null )
    {
        ThrowError("ptr == Address_Null");
    }
    #endif
 
    return LoadFromAddress(ptr + view_as<Address>(offset), type);
} 

public void OnPluginStart()
{
    StartPrepSDKCall(SDKCall_Static);
    PrepSDKCall_SetSignature(SDKLibrary_Engine, "@_Z12Steam3Serverv", 0);
    PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain);
    g_hGetSteam3Server = EndPrepSDKCall();
 
    g_pSteam3Server = SDKCall(g_hGetSteam3Server);
    ISteamServer = Dereference(g_pSteam3Server, 4);
 
    StartPrepSDKCall(SDKCall_Raw);
    PrepSDKCall_SetAddress(Dereference(Dereference(ISteamServer), 36 * 4));
    PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain);
    g_hGetIP = EndPrepSDKCall();
 
    RegConsoleCmd("sm_getip", sm_getip);
}
 
public Action sm_getip( int client, int args )
{
    MemoryBlock hIP = new MemoryBlock(0x60);
 
    SDKCall(g_hGetIP, hIP.Address, ISteamServer);
 
    int ip = LoadFromAddress(hIP.Address, NumberType_Int32);
    PrintToServer("%i.%i.%i.%i", (ip >> 24) & 255, (ip >> 16) & 255, (ip >> 8) & 255, ip & 255 );
    return Plugin_Handled;
}
 
Последнее редактирование:
Сверху