Bypassing HOOK's

On lunes, 27 de diciembre de 2010 0 comentarios

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();
  1. 7C80220F > 8BFF             MOV EDI,EDI  
  2. 7C802211   55               PUSH EBP  
  3. 7C802212   8BEC             MOV EBP,ESP  
  4. 7C802214   51               PUSH ECX  
  5. 7C802215   51               PUSH ECX  
  6. 7C802216   8B45 0C          MOV EAX,DWORD PTR SS:[EBP+C]  
  7. 7C802219   53               PUSH EBX  
  8. 7C80221A   8B5D 14          MOV EBX,DWORD PTR SS:[EBP+14]  
  9. 7C80221D   56               PUSH ESI  
  10. 7C80221E   8B35 B812807C    MOV ESI,DWORD PTR DS:[<&ntdll.NtProtectV>; ntdll.ZwProtectVirtualMemory  
  11.  ... cut  
What TrainerSpy does is;
  1. 7C80220F >-E9 XXXXXXXX      JMP hook.Function  
  2. 7C802214   51               PUSH ECX  
  3. 7C802215   51               PUSH ECX  
  4. 7C802216   8B45 0C          MOV EAX,DWORD PTR SS:[EBP+C]  
  5. 7C802219   53               PUSH EBX  
  6. 7C80221A   8B5D 14          MOV EBX,DWORD PTR SS:[EBP+14]  
  7. 7C80221D   56               PUSH ESI  
  8. 7C80221E   8B35 B812807C    MOV ESI,DWORD PTR DS:[<&ntdll.NtProtectV>; ntdll.ZwProtectVirtualMemory  
  9.  ... 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;
  1. MOV EDI, EDI  
  2. PUSH EBP  
  3. MOV EBP, ESP  
  4. JMP HookedFunction+5  
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:
  1. TrampCALL MACRO arg  
  2.  push 0DEADBEEFh  
  3.  mov edi, edi  
  4.  push ebp  
  5.  mov ebp, esp  
  6.  mov [esp+4], offset @F  
  7.  jmp [arg]  
  8.  @@:  
  9. EndM  
  10.   
  11. data  
  12.   
  13. szFunc db "WriteProcessMemory", 0  
  14. szLib db "kernel32.dll", 0  
  15. pWriteProcessMemory dd 0  
  16.   
  17. code  
  18.   
  19.  nop     ; trampCALL changes this to FFh  
  20.   
  21. start:  
  22.   
  23. push offset pWriteProcessMemory  
  24. push offset szLib  
  25. push offset szFunc  
  26. call TrampolineDetour   ; load pWriteProcessMemory with &WPM()+5  
  27.   
  28. push 0  
  29. push 1h  
  30. push 0FFh  
  31. push offset start-1  
  32. push -1  
  33. trampCALL pWriteProcessMemory  ; use MACRO to invoke trampoline   
  34.   
  35. push 0  
  36. call ExitProcess  
  37.   
  38. TrampolineDetour PROC pszFunction:DWORD, pszLibrary:DWORD, dwAddr:DWORD  
  39.   
  40. Invoke LoadLibrary, pszLibrary  
  41. cmp eax, 0  
  42. je @error  
  43. Invoke GetProcAddress, eax, pszFunction  
  44. cmp eax, 0  
  45. je @error  
  46.    
  47. add eax, 5  
  48.   
  49. mov ebx, dwAddr  
  50. mov [ebx], eax  
  51. mov eax, 1  
  52. ret  
  53.   
  54.    @error:  
  55. mov eax, -1  
  56. ret  
  57.   
  58. TrampolineDetour ENDP  
  59.   
  60. end start  
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.
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