In this Tutorial/Source Code Release I attempt to give you an introduction to self modifying / polymorphic code.
Will It Help To Prevent VAC From Detecting My Hacks?
There is no guarantee that this prevent anti-cheats from detecting your hacks, but nevertheless it can help.
Tested on MSVC 2015.
Our Dummy Function For Testing:
Code:
// Our testfunction that we are going to encrypt
void TestFunction()
{
MessageBox(0, L"Hello From Testfunction!", L"Test", 0);
}
// Marks the end of testfunction()
void FunctionStub() { return; }
There is no official way of getting a size of a function in memory, but it can be done by subtracting the size of a stub function
from the original one. This
requires the functions to be placed next to each other (as in code) by the compiler in the object
file when the program is built.
In order to achieve this, first do not place
any code in between the two procedures, next disable all compiler optimizations
and last disable DEP (Data Execution Prevention) from advanced linker options (/NXCOMPAT:NO).
Now let's write our XOR Function (in inline asm)
Code:
void XorBlock(DWORD dwStartAddress, DWORD dwSize)
{
__asm
{
push eax
push ecx
mov ecx, dwStartAddress // Move Start Address to ECX
add ecx, dwSize // Add the size of the function to ECX
mov eax, dwStartAddress // Copy the Start Address to EAX
crypt_loop: // Start of the loop
xor byte ptr ds:[eax], 0x4D // XOR The current byte with 0x4D
inc eax // Increment EAX with dwStartAddress++
cmp eax,ecx // Check if every byte is XORed
jl crypt_loop; // Else jump back to the start label
pop ecx // pop ECX from stack
pop eax // pop EAX from stack
}
}
Next we'll write our function to get the size of a certain function:
Code:
DWORD GetFuncSize(DWORD* Function, DWORD* StubFunction)
{
DWORD dwFunctionSize = 0, dwOldProtect;
DWORD *fnA = NULL, *fnB = NULL;
fnA = (DWORD *)Function;
fnB = (DWORD *)StubFunction;
dwFunctionSize = (fnB - fnA);
VirtualProtect(fnA, dwFunctionSize, PAGE_EXECUTE_READWRITE, &dwOldProtect); // Need to modify our privileges to the memory
return dwFunctionSize;
}
Memory Hex Dumper (just to see the differences):
(credits to domnikl for:
C function to dump memory)
Code:
void HexDumpMemory(void *addr, int len)
{
int i;
unsigned char buff[17];
unsigned char *pc = (unsigned char*)addr;
// Process every byte in the data.
for (i = 0; i < len; i++) {
// Multiple of 16 means new line (with line offset).
if ((i % 16) == 0) {
// Just don't print ASCII for the zeroth line.
if (i != 0)
printf(" %s\n", buff);
// Output the offset.
printf(" %04X ", i);
}
// Now the hex code for the specific character.
printf(" %02X", pc[i]);
// And store a printable ASCII character for later.
if ((pc[i] < 0x20) || (pc[i] > 0x7e)) {
buff[i % 16] = '.';
}
else {
buff[i % 16] = pc[i];
}
buff[(i % 16) + 1] = '\0';
}
// Pad out last line if not exactly 16 characters.
while ((i % 16) != 0) {
printf(" ");
i++;
}
// And print the final ASCII bit.
printf(" %s\n", buff);
}
And lastly a little test:
Code:
int main()
{
DWORD dwFuncSize = GetFuncSize((DWORD*)&TestFunction, (DWORD*)&FunctionStub);
printf("\n\nEncrypted:\n");
XorBlock((DWORD)&TestFunction, dwFuncSize); // XOR encrypt the function
HexDumpMemory(&TestFunction, dwFuncSize);
//TestFunction(); // If you try to run the encrypted function you will get Access Violation Exception.
printf("\n\nDecrypted:\n");
XorBlock((DWORD)&TestFunction, dwFuncSize); // XOR decrypt the function
HexDumpMemory(&TestFunction, dwFuncSize);
TestFunction(); // Fine here
getchar();
return 0;
}
Q: How to use this to (potentially) prevent from VAC detecting my cheats?
A: Encrypt each function of your cheat at main() and when needed, just decrypt the function
and after used just encrypt it again.