6 June 2016 - batuhan
Windows Api Hooking with x86 Assembly
What is hooking?
In computer programming, the term hooking covers a range of techniques used to alter or augment the behavior of an operating system, of applications, or of other software components by intercepting or function calls or events passed between software components. Code that handles such intercepted function calls, events or messages is called a “hook”.
There are too many reasons for hooking function calls. Most important one is evading the detection mechanisms. We will talk about others a little bit later. Lets introduce Api hooking:
In our scenario, We have gained code execution on a target computer and we wanted to inject our code to the running process in it. There are 2 options on user mode. DLL injection or Process injection. We wont talk about these techniques but after injected our code to the one of process we would want to intercept function calls or modify them.
An Example api we want to hook: MessageBoxA function as shown. MessageBox function takes 4 arguments as Microsoft described,
int WINAPI MessageBox( _In_opt_ HWND hWnd, _In_opt_ LPCTSTR lpText, _In_opt_ LPCTSTR lpCaption, _In_ UINT uType );
The simplest way to hook this function is place a unconditional jmp instruction. Unconditional jmp is 5 byte in length and it will jump to our function which we had designed to run before original function.
HookedMessageBoxA PROC ARG1 :DWORD,
mov dword ptr [hooked], 6C697349h
mov dword ptr [hooked+4],00000000h
mov argptr, ecx
lea edx,hooked ;we wanted to change Text in 2. parameter.
mov ARG2,edx ; we have done it.
mov ebx, 6973696Ch ; our magic dword ‘isil’: we will use it for calling
push ARG4 ; the original function
mov esp, ebp
This is enough for testing I think. Now, imagine the original function in memory when we place the unconditional jmp.
This is the tricky part. We will overwrite the first 5 bytes of the code. First we need to know the length of the code part which we want to overwrite because an instruction can have a length between 1 byte and 16 bytes. LDE Engine is suitable for this operation. It returns the length of the opcode.
invoke GetProcAddress, h_Module, Api
invoke ReadProcessMemory,pHandle,eax,ecx,40h,ebx ; read to the local buffer.
lea ecx,LocalApi; the local buffer.
cmp eax , 5 ; length must be below or equal 5 byte.
So what if the original instructions has 6 bytes or more? Answer is simple. Now we can return the original function + length we calculated.
Second, unconditional jmp is a little bit different from call instruction. We need to calculate the relative offset. HookedMessageBoxA – MessageBoxA then, we subtract 5 because that’s the length of the jmp instruction which the CPU adds when executing this instruction.
Then we need to Allocate 2 memory region. First for executing original instructions overwritten before and jmp to original function plus length we calculated.
Second for our HookedMessageBoxA function. If you remember, We had a magic dword “6973696Ch” in HookedMessageBoxA function. We are changing it now for calling the first allocated memory region.
Search: ;0x6973696c is our magic DWORD
cmp dword ptr[esi + eax],6973696Ch
add eax, 1
This is the how our HookedMessageBoxA function looks like in the memory:
And the original code we are reconstructing now
push ecx ; its the length we had calculated before
lea eax, LocalApi
mov [OriginalCode + 0Fh], 0E9h ; unconditional jmp opcode
sub edx,edi ; calculated relative offset
Complete source code written in masm Visual Studio 2015 project : rayondesoleil.zip
There are too much options you can use instead of unconditional jmp. Like call and push then ret (ROP).