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
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
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
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;
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 }
/** * @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; }