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:
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:
Below screenshot shows it detecting the API hooks in firefox.exe:
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:
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:
Below screenshot shows it detecting the API hooks in firefox.exe: