Veteran Member
Join Date: Jan 2012
Location: Russia
|
02-28-2023
, 12:14
SDKCall for non standart conv function
|
#1
|
Hello everyone, I have already published on the forum ways to call functions with __fastcall & __vectorcall conv or how to call functions where registers such as xmm are used to pass some parameters with SDKCall. I wanted to share a more elegant way in which Malloc is used to allocate memory for a function once. In this example, I fixed the call to the FX_FireBullets function that has __fastcall conv on the Windows. In addition, I include example of SDKCall for CGlobalEntityList::FindEntityInSphere which not have CGlobalEntityList as first parameter and float radius is passed in xmm3 register in CS:GO windows
Example:
Spoiler
PHP Code:
#include <sourcemod> #include <sdktools> #include <sdkhooks>
#define PLUGIN_CONFIG "plugin.test"
enum OS { OS_Unknown, OS_Windows, OS_Linux }; OS gPlatform; EngineVersion gEngine; Handle hMalloc; Handle hFree;
Address g_pMemAlloc;
ArrayList gMemoryPool;
#define MEMORYPOOL_NAME_MAX 124
enum struct MemoryPoolEntry { Address addr; char name[MEMORYPOOL_NAME_MAX]; }
Handle hSDKCallFireBullets; Handle hSDKCallFindEntityInSphere;
public void OnPluginStart() { RegAdminCmd("memory_dump", MemoryDump, ADMFLAG_ROOT, "Dumps active memory pool. Mainly for debugging."); GameData hConfig = LoadGameConfigFile(PLUGIN_CONFIG);
if (!hConfig) { SetFailState("Failed to load \"%s\" gamedata.", PLUGIN_CONFIG); return; }
gMemoryPool = new ArrayList(sizeof(MemoryPoolEntry)); g_pMemAlloc = hConfig.GetAddress("g_pMemAlloc");
gEngine = GetEngineVersion(); gPlatform = view_as<OS>(hConfig.GetOffset("OS"));
if (gEngine == Engine_CSS && gPlatform == OS_Linux) { { StartPrepSDKCall(SDKCall_Static); PrepSDKCall_SetFromConf(hConfig, SDKConf_Signature, "Malloc"); PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain);
if ((hMalloc = EndPrepSDKCall()) == null) { SetFailState("Failed to load SDK call \"Malloc\". Update signature in \"%s\"", PLUGIN_CONFIG); } } /*__________________________________________________________________________________________________*/ { StartPrepSDKCall(SDKCall_Static); PrepSDKCall_SetFromConf(hConfig, SDKConf_Signature, "Free"); PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); if ((hFree = EndPrepSDKCall()) == null) { SetFailState("Failed to load SDK call \"Free\". Update signature in \"%s\"", PLUGIN_CONFIG); } } } else { { StartPrepSDKCall(SDKCall_Raw); PrepSDKCall_SetFromConf(hConfig, SDKConf_Virtual, "Malloc"); PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); PrepSDKCall_SetReturnInfo(SDKType_PlainOldData, SDKPass_Plain);
if ((hMalloc = EndPrepSDKCall()) == null) { SetFailState("Failed to load SDK call \"Malloc\". Update virtual offset in \"%s\"", PLUGIN_CONFIG); } } /*__________________________________________________________________________________________________*/ { StartPrepSDKCall(SDKCall_Raw); PrepSDKCall_SetFromConf(hConfig, SDKConf_Virtual, "Free"); PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); if ((hFree = EndPrepSDKCall()) == null) { SetFailState("Failed to load SDK call \"Free\". Update virtual offset in \"%s\"", PLUGIN_CONFIG); } } } /*__________________________________________________________________________________________________*/
{ StartPrepSDKCall(SDKCall_Static); if (gPlatform == OS_Windows) { /** * @brief FX_FireBullets translator. * @link https://defuse.ca/online-x86-assembler.htm * * @code * 58 pop eax * 59 pop ecx * 5a pop edx * 50 push eax * b8 00 00 00 00 mov eax,pFireBullets * ff e0 jmp eax **/ char sTrampoline[] = "\x58\x59\x5A\x50\xB8\x00\x00\x00\x00\xFF\xE0"; // __fastcall workaround on Windows // we move first two params to the ecx, edx before jumping Address pSignature = hConfig.GetAddress("FX_FireBullets"); writeDWORD(sTrampoline, pSignature, 5); // 5 is where \x00\x00\x00\x00 is start in sTrampoline pSignature = Malloc(sizeof(sTrampoline), "FX_FireBullets"); memcpy(pSignature, sTrampoline, sizeof(sTrampoline)); PrepSDKCall_SetAddress(pSignature); } else { PrepSDKCall_SetFromConf(hConfig, SDKConf_Signature, "FX_FireBullets"); }
PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); PrepSDKCall_AddParameter(SDKType_CBaseEntity, SDKPass_Pointer); PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); PrepSDKCall_AddParameter(SDKType_Vector, SDKPass_ByRef); PrepSDKCall_AddParameter(SDKType_QAngle, SDKPass_ByRef); PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); PrepSDKCall_AddParameter(SDKType_Float, SDKPass_Plain); PrepSDKCall_AddParameter(SDKType_Float, SDKPass_Plain); PrepSDKCall_AddParameter(SDKType_Float, SDKPass_Plain); PrepSDKCall_AddParameter(SDKType_Float, SDKPass_Plain); PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); PrepSDKCall_AddParameter(SDKType_Float, SDKPass_Plain);
if ((hSDKCallFireBullets = EndPrepSDKCall()) == null) { SetFailState("Failed to load SDK call \"FX_FireBullets\". Update signature in \"%s\"", PLUGIN_CONFIG); } } /*__________________________________________________________________________________________________*/ if (gPlatform == OS_Windows) { /** * @brief CGlobalEntityList::FindEntityInSphere translator. * @link https://defuse.ca/online-x86-assembler.htm * * @code * f3 0f 10 5d 0c movss xmm3,DWORD PTR [ebp+12] * b8 00 00 00 00 mov eax,pFindEntityInSphere * ff e0 jmp eax **/ char sTrampoline[] = "\xF3\x0F\x10\x5D\x0C\xB8\x00\x00\x00\x00\xFF\xE0"; // on Windows we not have CGlobalEntityList as first param // float is passing in xmm3 register Address pSignature = hConfig.GetAddress("CGlobalEntityList::FindEntityInSphere"); writeDWORD(sTrampoline, pSignature, 6); // 6 is where \x00\x00\x00\x00 is start in sTrampoline
StartPrepSDKCall(SDKCall_Static);
PrepSDKCall_SetAddress(pSignature); } else { StartPrepSDKCall(SDKCall_EntityList); PrepSDKCall_SetFromConf(hConfig, SDKConf_Signature, "CGlobalEntityList::FindEntityInSphere"); }
PrepSDKCall_AddParameter(SDKType_CBaseEntity, SDKPass_Pointer); PrepSDKCall_AddParameter(SDKType_Vector, SDKPass_ByRef); PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain);
PrepSDKCall_SetReturnInfo(SDKType_CBaseEntity , SDKPass_Pointer); if ((hSDKCallFindEntityInSphere = EndPrepSDKCall()) == null) { SetFailState("Failed to load SDK call \"CGlobalEntityList::FindEntityInSphere\". Update signature in \"%s\"", PLUGIN_CONFIG); } delete hConfig; // To call FX_FireBullets we use ///SDKCall(hSDKCallFireBullets, client, weapon, 0/*CEconItemView*/, vPosition, vAngle, iMode, iSeed, flInaccuracy, flSpread, flFishTail, 0.0, iSoundType, flRecoilIndex); // on both platforms }
public void OnPluginEnd() { MemoryCleanUpPool(); }
/** * Console command callback (memory_dump) * @brief Dumps active memory pool. * * @param client The client index. * @param iArguments The number of arguments that were in the argument string. **/ public Action MemoryDump(int client, int iArguments) { if (gMemoryPool == null || (gMemoryPool && gMemoryPool.Length == 0)) { ReplyToCommand(client, "Theres's currently no active pool or it's empty!"); return Plugin_Handled; } MemoryPoolEntry entry; int iSize = gMemoryPool.Length; ReplyToCommand(client, "Active memory pool ({1}):", iSize); for (int i = 0; i < iSize; i++) { gMemoryPool.GetArray(i, entry, sizeof(MemoryPoolEntry)); ReplyToCommand(client, "[%i]: 0x%08X \"%s\"", i, entry.addr, entry.name); } return Plugin_Handled; }
/** * @brief Cleans up memory pool and free all allocated memory. **/ stock void MemoryCleanUpPool() { if (gMemoryPool == null) { return; } MemoryPoolEntry entry; int iSize = gMemoryPool.Length; for (int i = 0; i < iSize; i++) { gMemoryPool.GetArray(i, entry, sizeof(MemoryPoolEntry)); Free(entry.addr); } delete gMemoryPool; }
/** * @brief Adds already allocated memory to the pool. * * @param pAddress The pointer to allocated memory. * @param sName The memory block name. **/ stock void MemoryAddToPool(Address pAddress, const char[] sName) { MemoryPoolEntry entry; strcopy(entry.name, sizeof(MemoryPoolEntry::name), sName); entry.addr = pAddress; gMemoryPool.PushArray(entry); }
/** * @brief Allocates the requested memory and returns a pointer to it. * * @param iSize This is the size of the memory block, in bytes. * @param sName The memory block name. * * @return The pointer to allocated memory. **/ stock Address Malloc(int iSize, const char[] sName) { MemoryPoolEntry entry; strcopy(entry.name, sizeof(MemoryPoolEntry::name), sName); if (gEngine == Engine_CSS && gPlatform == OS_Linux) { entry.addr = SDKCall(hMalloc, 0, iSize); } else { entry.addr = SDKCall(hMalloc, g_pMemAlloc, iSize); } if (entry.addr == Address_Null) { SetFailState("Failed to allocate memory (size: %i)!", iSize); }
gMemoryPool.PushArray(entry); return entry.addr; }
/** * @brief Deallocates the memory previously allocated by a call to calloc, malloc, or realloc. * * @param pAddress The pointer to memory, which should be free. **/ stock void Free(Address pAddress) { int iD = gMemoryPool.FindValue(pAddress, MemoryPoolEntry::addr); if (iD == -1) { return; /// Memory wasn't allocated yet, return } gMemoryPool.Erase(iD); if (gEngine == Engine_CSS && gPlatform == OS_Linux) { SDKCall(hFree, 0, pAddress); } else { SDKCall(hFree, g_pMemAlloc, pAddress); } }
/** * @brief Copies the values of num bytes from the location pointed to by source directly to the memory block pointed to by destination. * * @param pDest The destination address where the content is to be copied. * @param sSource The source of data to be copied. * @param iSize The number of bytes to copy. **/ stock void memcpy(Address pDest, const char[] sSource, int iSize) { int i = iSize / 4; memcpy4b(pDest, view_as<any>(sSource), i); for (i *= 4, pDest += view_as<Address>(i); i < iSize; i++) { StoreToAddress(pDest++, sSource[i], NumberType_Int8); } }
/** * @brief Copies the 4 bytes from the location pointed to by source directly to the memory block pointed to by destination. * * @param pDest The destination address where the content is to be copied. * @param sSource The source of data to be copied. * @param iSize The number of bytes to copy. **/ stock void memcpy4b(Address pDest, const any[] sSource, int iSize) { for (int i = 0; i < iSize; i++) { StoreToAddress(pDest, sSource[i], NumberType_Int32); pDest += view_as<Address>(4); } }
/** * @brief Writes the DWord D (i.e. 4 bytes) to the string. * * @param asm The assemly string. * @param pAddress The address of the call. * @param iOffset (Optional) The address offset. (Where 0x0 starts) **/ stock void writeDWORD(const char[] asm, any pAddress, int iOffset = 0) { asm[iOffset] = pAddress & 0xFF; asm[iOffset+1] = pAddress >> 8 & 0xFF; asm[iOffset+2] = pAddress >> 16 & 0xFF; asm[iOffset+3] = pAddress >> 24 & 0xFF; }
Gamedata:
Spoiler
PHP Code:
"Games" { "#default" { "Offsets" { "OS" { "windows" "1" "linux" "2" } } } "csgo" { "Offsets" { "Malloc" { "windows" "1" "linux" "0" } "Free" { "windows" "5" "linux" "2" } } "Signatures" { "g_pMemAllocFunc" // Look for any function with "g_pMemAlloc" in it { "library" "server" "windows" "\x55\x8B\xEC\x56\x8B\xF1\x83\x4E\x30\x04" "linux" "\x55\x89\xE5\x57\x56\x53\x81\xEC\x3C\x01\x00\x00\x8B\x45\x08\x65\x8B\x0D\x14\x00\x00\x00\x89\x4D\xE4\x31\xC9\x89\xC1" } "FX_FireBullets" // Str: "FX_FireBullets" { "library" "server" "windows" "\x55\x8B\xEC\x83\xE4\xF8\x81\xEC\xE0\x01\x00\x00" "linux" "\x55\x89\xE5\x57\x56\x53\x81\xEC\xEC\x02\x00\x00\x8B\x45\x0C" } } "Addresses" { "g_pMemAlloc" { "windows" { "signature" "g_pMemAllocFunc" "read" "20" "read" "0" } "linux" { "signature" "g_pMemAllocFunc" "read" "191" } "read" "0" } "FX_FireBullets" { "signature" "FX_FireBullets" } } } "cstrike" { "Offsets" { "Malloc" { "windows" "1" } "Free" { "windows" "5" } } "Signatures" { "g_pMemAllocFunc" { "library" "server" "windows" "\x55\x8B\xEC\x83\xEC\x20\x8B\x45\x08\xB9\x2A\x2A\x2A\x2A" }
"Malloc" { "library" "engine" "linux" "@_ZL7SzAllocPvj" } "Free" { "library" "engine" "linux" "@_ZL6SzFreePvS_" } } "Addresses" { "g_pMemAlloc" { "windows" { "signature" "g_pMemAllocFunc" "read" "253" "read" "0" "read" "0" } } } } }
__________________
Last edited by gubka; 02-28-2023 at 19:21.
|
|