API Copying/Trampolines by Irwin

On martes, 7 de septiembre de 2010 0 comentarios

API copying is an old yet effective technique which renders user-mode hooks useless, this is because most user-mode hooks rely on physically hooking the API through either a detour (inline on API or IAT) so by copying the API you render the hook useless. So here's quite an easy example of API copying & also a small trampoline:

Code:
.386
.model flat, stdcall

option casemap : none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib

.data

strUser32 db "user32.dll", 0
strSendInput db "SendInput", 0
strMouse_Event db "mouse_event", 0
strKeybd_Event db "keybd_event", 0

lpflOldProtect dd 0

.code

xSendInput proc ;; SendInput copy, this will have enough room for the copy. (you could allocate & copy more if it changes)
dd 090909090h
dd 090909090h
dd 090909090h
db 090h, 090h, 090h
;db 15d dup(?) ; I could have done this but it doesn't look nice when debugging :<
db 090h ; Gap for debugger cleanliness :P
xSendInput endp

CalcJump proc Source:DWORD, Destination:DWORD ;; Jump calculation function. dest - source - 5 = offset
mov ecx, dword ptr ds:[Destination]
sub ecx, dword ptr ds:[Source]
sub ecx, 5
pop ebp
retn 8
db 090h ; Gap for debugger cleanliness :P
CalcJump endp

WinMain proc
invoke LoadLibrary, addr strUser32 ; Load user32.dll
push eax ; Save the handle
invoke GetProcAddress, eax, addr strSendInput ; Get the address for user32.SendInput

mov ecx, 15d ; Move 15d into ECX (15 bytes to copy) for the rep prefix
mov esi, eax ; Specify source as EAX (user32.SendInput)
mov edi, offset xSendInput ; Specify destination as our empty SendInput buffer
rep movsb ; Copy 15 bytes from SendInput to empty xSendInput function

mov eax, dword ptr ds:[esp] ; Get the kernel32 handle from stack (saved earlier)
invoke GetProcAddress, eax, addr strMouse_Event ; Get user32.mouse_event's address
add eax, 36h ; Offset of SendInput call
push eax ; Store mouse_event address
inc eax ; Go to call offset location
invoke VirtualProtect, eax, 4, PAGE_EXECUTE_READWRITE, addr lpflOldProtect ; Allow us to write to it.
pop eax ; Restore mouse_event address
invoke CalcJump, eax, addr xSendInput ; Calculate jump offset
inc eax ; Point to offset address
mov dword ptr ds:[eax], ecx ; Move xSendInput offset to replace user32.SendInput's offset

mov eax, dword ptr ds:[esp] ; Get the kernel32 handle from stack (saved earlier)
invoke GetProcAddress, eax, addr strKeybd_Event ; Get user32.keybd_event's address
add eax, 37h ; Offset of SendInput call
push eax ; Store keybd_event address
inc eax ; Go to call offset location
invoke VirtualProtect, eax, 4, PAGE_EXECUTE_READWRITE, addr lpflOldProtect ; Allow us to write to it.
pop eax ; Restore mouse_event address
invoke CalcJump, eax, addr xSendInput ; Calculate call offset
inc eax ; Point to offset address
mov dword ptr ds:[eax], ecx ; Move xSendInput offset to replace user32.SendInput's offset

;;
;;
;; YOUR OWN CODE HERE.
;;
;;

invoke Sleep, INFINITE
WinMain endp

end WinMain



Now let's get onto trampolines, they're effectively jumping over the hook, so I won't get too in-detail about this but let's just say npggnt.des has a 5 byte inline hook on user32.PostMessage, we can either call our own function which calls the first 5 bytes that npggnt.des overwrites with it's hook then we can jump over npggnt.des' hook. So putting it simply...
Code:
TrampolinePostMessage proc
mov edi, edi ;;
push ebp ;; user32.PostMessage's first 5 bytes
mov ebp, esp ;;
jmp user32.PostMessage+5 ; Jump past the detour hook set by npggnt.des
TrampolinepostMessage endp


In most APIs (excluding services that directly hit the service dispatcher) the first 5 bytes will be as shown:
Code:
mov edi, edi ; Windows hotfix leeway
push ebp ; Save EBP (since it's used for parameters & local variables)
mov ebp, esp ; Prepare EBP for variable usage.


So we can just use those bytes then jump to GetProcAddress(handle, "whatever"); + 5.

Anyway, I'm lazy so I'll just abruptly end here.

P.S. The memory which you copy the API to must be writable.

Just another side-note: You could also just hook the IAT to point to your copied functions so you could integrate it with ACTools, etc.

0 comentarios:

Publicar un comentario