API Hooking in Assembly
Sunday, November 24, 2013 | Author: Deep Flash
I wanted to write an article which discusses in depth the method used for hooking the entry point of an API. This method is often used in malwares to alter the behavior of some APIs. Usually the Networking APIs imported from ws2_32.dll, wininet.dll are hooked in this way.

As an example I have taken the Win32/Gepys virus family. The code examples are in assembly. This will help in understanding clearly the method used for hooking.

In order to hook the entry point of the API we need the following:

1. API Address. This can be retrieved by calling GetProcAddress() on the API.
2. Buffer: This buffer will be used to store the first few opcodes of the API along with the jump trampoline. You can get this buffer by calling VirtualAlloc().
3. Malicious Subroutine: This is the subroutine which we want to execute before executing the main API. It will be invoked each time the main API is called from the program.

Now, let's call the API hooking routine:

We need to call VirtualProtect() on both the API and the buffer to mark these regions of memory as PAGE_EXECUTE_READWRITE. We will be executing the code from the buffer as well.

VirtualProtect(buffer, 0x10, 0x40, &oldProtect);
VirtualProtect(api, 0x10, 0x40, &oldProtect);

Now comes the main code for hooking the API. I have explained it with comments:

ESI - Function Pointer
EDI - Buffer
00C816F9 803E E9 CMP BYTE PTR DS:[ESI],0E9 ; check if the first instruction of API is a jump instruction
00C816FC 75 09 JNZ SHORT 00C81707
00C816FE 8B46 01 MOV EAX,DWORD PTR DS:[ESI+1]
00C81701 8D4430 05 LEA EAX,DWORD PTR DS:[EAX+ESI+5]
00C81705 EB 12 JMP SHORT 00C81719
00C81707 8D46 05 LEA EAX,DWORD PTR DS:[ESI+5] ; point eax to the 5th byte of the function
00C8170A A5 MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI] ; store 5 bytes from the function into the buffer
00C8170B A4 MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
00C8170C 8B7D 0C MOV EDI,DWORD PTR SS:[EBP+C] ; edi = buffer
00C8170F 8B75 08 MOV ESI,DWORD PTR SS:[EBP+8] ; esi = function pointer
00C81712 C745 F8 05000000 MOV DWORD PTR SS:[EBP-8],5
00C81719 8B55 F8 MOV EDX,DWORD PTR SS:[EBP-8]
00C8171C 2BC2 SUB EAX,EDX
00C8171E 2BC7 SUB EAX,EDI
00C81720 83E8 05 SUB EAX,5 ; eax = function pointer - buffer - 0x5
00C81723 8D0C3A LEA ECX,DWORD PTR DS:[EDX+EDI] ; buffer = buffer + 0x5
00C81726 8941 01 MOV DWORD PTR DS:[ECX+1],EAX ; write above calculated value of eax in the buffer
00C81729 8B45 10 MOV EAX,DWORD PTR SS:[EBP+10] ; malicious subroutine
00C8172C 2BC6 SUB EAX,ESI
00C8172E 83E8 05 SUB EAX,5 ; hooked api = hooked api - function pointer - 0x5
00C81731 C601 E9 MOV BYTE PTR DS:[ECX],0E9 ; write jump opcode to buffer
00C81734 8946 01 MOV DWORD PTR DS:[ESI+1],EAX ; write the above calculated hooked api value to the function pointer + 1
00C81737 8D45 F4 LEA EAX,DWORD PTR SS:[EBP-C]
00C8173A 50 PUSH EAX
00C8173B FF75 F4 PUSH DWORD PTR SS:[EBP-C]
00C8173E C606 E9 MOV BYTE PTR DS:[ESI],0E9 ; write jump opcode to function pointer
view raw hookapi.asm hosted with ❤ by GitHub


Once we are done with it, we again mark these regions of memory as: PAGE_EXECUTE_READ.

VirtualProtect(api, 0x10, 0x20, &oldProtect)
VirtualProtect(buffer, 0x10, 0x20, &newProtect)

So, the buffer format is:

[first 5 bytes of the API][E9 - opcode for jump][function pointer - buffer - 0x5]

and the first 5 bytes of the API are calculated as:

E9 - jump opcode
Address = malicious subroutine address - function pointer - 0x5

As an example, if we are hooking the API, ws2_32.gethostbyname with the following details:

buffer = 00D90010
api = 71AB5355 (gethostbyname)
malicious subroutine: 00C8159B

This is how the first 3 instructions of the API look before hooking:

71AB5355 > 8BFF                 MOV EDI,EDI
71AB5357   55                      PUSH EBP
71AB5358   8BEC                  MOV EBP,ESP

Using the above format of the buffer, we know the buffer should look like this for hooking:

buffer = 8b ff 55 8b ec e9 45 53 d2 70

The opcodes in the above buffer correspond to:

00D90010   8BFF                  MOV EDI,EDI
00D90012   55                       PUSH EBP
00D90013   8BEC                 MOV EBP,ESP
00D90015  -E9 4053D270    JMP WS2_32.71AB535A

This jump instruction will redirect the execution to the 4th instruction of the API, ws2_32.gethostbyname.

Also, the first 5 bytes of the function pointer can be calculated using the above method as:

jump opcode: e9
address: 8F1CC241

API after hooking:

71AB5355 >-E9 41C21C8F      JMP 00C8159B    ; malicious subroutine
71AB535A   81EC 14020000        SUB ESP,214

malicious subroutine:

00C8159B   6A 00                    PUSH 0
00C8159D   FF7424 08            PUSH DWORD PTR SS:[ESP+8]
00C815A1   E8 23FDFFFF        CALL 00C812C9
00C815A6   59                         POP ECX
00C815A7   59                         POP ECX
00C815A8   50                         PUSH EAX
00C815A9   FF15 0030C900     CALL DWORD PTR DS:[C93000]
00C815AF   C2 0400               RETN 4

at address, 0xC93000 we have the address of the buffer.

So, the instruction, call dword ptr ds:[buffer] will redirect the execution to the buffer which has the opcodes for the first 3 instructions of the API and then redirects execution to the 4th instruction of the API.

Now, that we have understood this method of API hooking. Let us see how we can detect it.

In the case of Win32/Gepys virus family, it will add the full path of the malicious DLL to the Registry Entry: AppInit_DLL. This will allow the DLL to be loaded into the address space of any new process on the system (it should be linked with user32.dll).

Also, it performs the API hooking only when it is loaded in the address space of a Browser like firefox.exe, chrome.exe, iexplore.exe, opera.exe and so on.

So, to detect this method of API hooking, we will check the calls to VirtualProtect(). Since in API hooking we are writing our jump trampolines to the API, we will check specifically for calls to VirtualProtect() that mark the regions of memory as: PAGE_EXECUTE_READWRITE.

Also, we are interested in those VirtualProtect() calls which are invoked on the API addresses.

I wrote the following Pintool which can help automate this:

/*
Pintool to detect API hooks in a process
c0d3inj3cT
*/
#include <stdio.h>
#include <iostream>
#include "pin.H"
int i=0;
void VirtualProtectHandler(void *address, int newProtect)
{
if(newProtect == 0x40)
{
PIN_LockClient();
RTN lrtn = RTN_FindByAddress((ADDRINT) address);
if(RTN_Valid(lrtn))
{
i++;
string symbolName = RTN_Name(lrtn);
symbolName = PIN_UndecorateSymbolName(symbolName, UNDECORATION_COMPLETE);
printf("VirtualProtect(%p) ==> %s\n", address, symbolName.c_str());
}
PIN_UnlockClient();
}
}
void Image(IMG img, void *v)
{
RTN rtn = RTN_FindByName(img, "VirtualProtect");
if(RTN_Valid(rtn))
{
RTN_Open(rtn);
RTN_InsertCall(rtn, IPOINT_BEFORE, (AFUNPTR) VirtualProtectHandler, IARG_FUNCARG_ENTRYPOINT_VALUE, 0, IARG_FUNCARG_ENTRYPOINT_VALUE, 2, IARG_END);
RTN_Close(rtn);
}
}
void Fini(INT32 code, void *v)
{
printf("There are %d functions hooked\n", i);
}
INT32 Usage()
{
printf("There was an error\n");
return -1;
}
int main(int argc, char *argv[])
{
PIN_InitSymbols();
if( PIN_Init(argc,argv) )
{
return Usage();
}
IMG_AddInstrumentFunction(Image, 0);
PIN_AddFiniFunction(Fini, 0);
PIN_StartProgram();
return 0;
}
view raw hookdetect.c hosted with ❤ by GitHub


Below screenshot shows it detecting the API hooks in firefox.exe:


|
This entry was posted on Sunday, November 24, 2013 and is filed under . You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

0 comments: