by Ksbunker
This article is written in follow-up to Specific's "Hook process functions via dll injection Cpp". His article detailed how to hook functions using an injected DLL. Expanding on this concept, i'll detail how you can trampoline over the CALL or JMP instruction which forwards flow to the hook function; effectively rendering the HOOK void/useless. This method works well with any application that attempts to spy on or intercept specific functions, e.g. WPE, TrainerSpy, etc. The code is in ASM but generic enough to be ported to any other language with minimal fuss.
What we're going to do is disable TrainerSpy (XP) by invoking trampolined function calls. For those of you unfamiliar with TrainerSpy (or variants thereof), it's basically a tool that people (read: lamers) use to intercept function calls to kernel32.WriteProcessMemory() in trainers (or any application that calls WPM for that matter). It logs several paramters of the WPM() but most importantly, address of and bytes written to address. If I spend a few hours reversing a game, the last thing I want is some lamer to just spy on my addresses and release his own with my options. So let's disable that.
Before I get into the code, i'll quickly gloss over some ASM fundamentals. Open kernel32.dll in OllyDbg or IDA (or any other decent dissasembler/debugger) and find the function WriteProcessMemory (ctrl+N to bring up referenced API list).
First few lines of WPM();
What TrainerSpy does is;
By placing JMP instruction at the start of the function, it directs flow to the hook.Function, where it can log or modify parameters. To bypass this HOOK, we simply emulate the first three instructions then jump into the function 5 bytes, pseudo;
Some may ask, what exactly is this and why is it there? It's a procedural stack frame and indicates the start of most (most, not all) functions. Above and beyond helping us identify where functions start, it's imperative for arranging local storage on the stack.
Ok so, enough talking, here's the code;
The following snippet uses a custom function of mine to load a function address and store it into the supplied variable for later use within the MACRO trampCALL:
What the code does it load the address of WriteProcessMemory+5 and save it in pWriteProcessMemory. Then it prepares a call to WPM as per normal setting up the parameters. The macro trampCALL pWriteProcessMemory emulates the first 3 instructions and prepares the stack for the trampoline, finally jumping to WPM+5 and returning to EIP.
This article is written in follow-up to Specific's "Hook process functions via dll injection Cpp". His article detailed how to hook functions using an injected DLL. Expanding on this concept, i'll detail how you can trampoline over the CALL or JMP instruction which forwards flow to the hook function; effectively rendering the HOOK void/useless. This method works well with any application that attempts to spy on or intercept specific functions, e.g. WPE, TrainerSpy, etc. The code is in ASM but generic enough to be ported to any other language with minimal fuss.
What we're going to do is disable TrainerSpy (XP) by invoking trampolined function calls. For those of you unfamiliar with TrainerSpy (or variants thereof), it's basically a tool that people (read: lamers) use to intercept function calls to kernel32.WriteProcessMemory() in trainers (or any application that calls WPM for that matter). It logs several paramters of the WPM() but most importantly, address of and bytes written to address. If I spend a few hours reversing a game, the last thing I want is some lamer to just spy on my addresses and release his own with my options. So let's disable that.
Before I get into the code, i'll quickly gloss over some ASM fundamentals. Open kernel32.dll in OllyDbg or IDA (or any other decent dissasembler/debugger) and find the function WriteProcessMemory (ctrl+N to bring up referenced API list).
First few lines of WPM();
- 7C80220F > 8BFF MOV EDI,EDI
- 7C802211 55 PUSH EBP
- 7C802212 8BEC MOV EBP,ESP
- 7C802214 51 PUSH ECX
- 7C802215 51 PUSH ECX
- 7C802216 8B45 0C MOV EAX,DWORD PTR SS:[EBP+C]
- 7C802219 53 PUSH EBX
- 7C80221A 8B5D 14 MOV EBX,DWORD PTR SS:[EBP+14]
- 7C80221D 56 PUSH ESI
- 7C80221E 8B35 B812807C MOV ESI,DWORD PTR DS:[<&ntdll.NtProtectV>; ntdll.ZwProtectVirtualMemory
- ... cut
- 7C80220F >-E9 XXXXXXXX JMP hook.Function
- 7C802214 51 PUSH ECX
- 7C802215 51 PUSH ECX
- 7C802216 8B45 0C MOV EAX,DWORD PTR SS:[EBP+C]
- 7C802219 53 PUSH EBX
- 7C80221A 8B5D 14 MOV EBX,DWORD PTR SS:[EBP+14]
- 7C80221D 56 PUSH ESI
- 7C80221E 8B35 B812807C MOV ESI,DWORD PTR DS:[<&ntdll.NtProtectV>; ntdll.ZwProtectVirtualMemory
- ... cut
By placing JMP instruction at the start of the function, it directs flow to the hook.Function, where it can log or modify parameters. To bypass this HOOK, we simply emulate the first three instructions then jump into the function 5 bytes, pseudo;
- MOV EDI, EDI
- PUSH EBP
- MOV EBP, ESP
- JMP HookedFunction+5
Ok so, enough talking, here's the code;
The following snippet uses a custom function of mine to load a function address and store it into the supplied variable for later use within the MACRO trampCALL:
- TrampCALL MACRO arg
- push 0DEADBEEFh
- mov edi, edi
- push ebp
- mov ebp, esp
- mov [esp+4], offset @F
- jmp [arg]
- @@:
- EndM
- data
- szFunc db "WriteProcessMemory", 0
- szLib db "kernel32.dll", 0
- pWriteProcessMemory dd 0
- code
- nop ; trampCALL changes this to FFh
- start:
- push offset pWriteProcessMemory
- push offset szLib
- push offset szFunc
- call TrampolineDetour ; load pWriteProcessMemory with &WPM()+5
- push 0
- push 1h
- push 0FFh
- push offset start-1
- push -1
- trampCALL pWriteProcessMemory ; use MACRO to invoke trampoline
- push 0
- call ExitProcess
- TrampolineDetour PROC pszFunction:DWORD, pszLibrary:DWORD, dwAddr:DWORD
- Invoke LoadLibrary, pszLibrary
- cmp eax, 0
- je @error
- Invoke GetProcAddress, eax, pszFunction
- cmp eax, 0
- je @error
- add eax, 5
- mov ebx, dwAddr
- mov [ebx], eax
- mov eax, 1
- ret
- @error:
- mov eax, -1
- ret
- TrampolineDetour ENDP
- end start
If you load this application into TrainerSpy, you'll notice it logs no parameters. If you want to test this out, simply comment out the 'trampCALL ...' line and replace it with 'call WriteProcessMemory'. Attach trainer spy and watch it record the parameters. Viola! TrainerSpy/Hook bypassed.
0 comentarios:
Publicar un comentario