As promised a while back, here's a working encrypt/decrypt call gate. The encryption is just XOR'ing against a rotating key, but it's fast and it will pretty much break Warden's hashing entirely.
The idea is that an external cryptor will:
- Pause the target (WoW, presumably)
- Get the data extent (base, size) of the code to be encrypted
- Redirect the calls into the code to be encrypted, to calls in a "jump gate" structure
- Inject the encryptor/decryptor code
- Hash the code to be encrypted once (so it starts encrypted)
- Resume the target process
The weakness of this implementation is that since you need to know the number of params for the target method (the function that you're encrypting, which may just be a hook from something else), you create a minor coupling between the encryption gate injector and the code to be encrypted. I'm still working on better ways to do this so that your hack code doesn't need to be coupled to the cryptor.
But for now, it works. My main() includes some basic tests to show it in action.
Note: this isn't platform-agnostic; it will fail -- badly -- on x64 native apps (sorry, Cypher). I know the code is messy -- I wrote it in about 30 minutes today so I haven't had time to prettify it Finally, this is POC. The real code would virtualalloc a code cave in the target and inject a real jumpgate and cryptor. I just wanted to show how it works in theory.
Code:
#include <stdio.h>
#include <windows.h>
static CRITICAL_SECTION csHashLock; // we need this to be thread-safe
static int buffer[256] = { 0 }; // this is our make-believe hack functions which the decrypt gate will be hashing
static int dwordCount = sizeof(buffer) >> 2; // convert bytes to dwords; to do this correctly we should round up
static int dwHashInit = GetTickCount(); // we can generate any pseudo-random seed
// ultimately, implement in raw ASM and VirtualAlloc by the injector. For now, POC.
__declspec(noinline)
void __stdcall HashPE()
{
int dwHash = dwHashInit;
for (int i = 0; i < dwordCount; ++i)
{
buffer[i] ^= dwHash; // xor "encryption"
__asm rol dwHash, 1 // keep rotating the key so we can't use a simple fixed-key brute force (rotation is obviously not complex, but it's beyond a simple Warden hash)
}
}
// a function to call "hooked" and "unhooked", shows some data, looks at the "encrypted" stuff, returns something
int __stdcall TestFunction_Original(int value)
{
printf("Hi there! I'm in TestFunction! You passed: %d (buffer[0] == 0x%08x\n", value, buffer[0]);
return value * 2;
}
// for ease of "hooking"
typedef int (__stdcall *TestFunction_ptr)(int);
TestFunction_ptr TestFunction = TestFunction_Original;
__declspec(naked)
__declspec(noinline)
void __stdcall DecryptGate(int argCount, DWORD targetHook)
{
printf("Entering DecryptGate!\n");
__asm
{
push ebp
mov ebp, esp
mov eax, [ebp + 0x4] // ebp+4 is the address of the first function to the decrypt gate (param count)
add eax, 3 // compensate for target addr and original return addr
cmp eax, 3
jbe StackCopyComplete
StackCopyLoop:
mov ebx, [ebp + eax * 4]
push ebx
sub eax, 1
cmp eax, 3
jg StackCopyLoop
StackCopyComplete:
push OFFSET csHashLock
call dword ptr [EnterCriticalSection]
call HashPE
call [ebp + 0x8] // [ebp + 0x8] contains the target fn addx
push eax
call HashPE
push OFFSET csHashLock
call dword ptr [LeaveCriticalSection]
pop eax
pop ebp
add esp, 0x8
ret
}
}
// in a real app, this jump table would be dynamically allocated and filled in with correct values
// one weakness of this approach is that we need to know the number of parameters, thus the cryptor
// is coupled with the hack code (!)
__declspec(naked)
__declspec(noinline)
void __stdcall JumpTable()
{
__asm
{
push TestFunction_Original
push 1
jmp DecryptGate
}
}
int main(int argc, char *argv[])
{
printf("Entering main...\n");
InitializeCriticalSection(&csHashLock);
int result = 0;
printf("About to call original (unhooked) function...\n");
TestFunction_ptr TestFunction = TestFunction_Original;
result = TestFunction(3);
printf("Back from (unhooked) TestFunction, result: %d\n", result);
// first, "encrypt" the "PE"
HashPE();
// store a random value to show that we're not trashing the stack
int x = 0xfeedbeef;
printf("About to call hooked function (buffer[0] == 0x%08x)...\n", buffer[0]);
TestFunction = (TestFunction_ptr)JumpTable;
result = TestFunction(4);
// show the stored stack value for verification
printf("Back from (hooked) TestFunction... result: %d (x == %08x, buffer[0] == 0x%08x)\n", result, x, buffer[0]);
printf("Press RETURN to exit...\n");
fgetc(stdin);
}