All About HackingBlackhat Hacking ToolsFree CoursesHacking

API hooking 2023

In this article we will learn about API hooking.

What is API hooking?

API hooking is a technique by which we can instrument and modify the behavior and flow of API calls. API hooking can be done using various methods in Windows. Techniques include memory breakpoint and insertion of .DEP and JMP instructions. We will briefly discuss trampoline insertion techniques.

Hooking can be used to introspect calls in a Windows application, or it can be used to capture some information related to API calls.

Let us consider the following application making some basic Win32 API calls.

[c]
/*************************************************

Simple WIN32 APP making some API calls

************************************************/

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>

int main(int argc, char **argv)
{
MessageBox(NULL, “Hello world”, “Hello World!”, MB_OK);
return EXIT_SUCCESS;
}
[/c]

Running this program will lead us to this message box:

Now let us consider the situation that we want to monitor the call to the message. The following diagram illustrates the procedure.

The code that is responsible for jumping into the linked dll is known as a trampoline. So the basic idea is to redirect calls to the base API function.

For injection-related purposes, we’ll create an injector. Following is the code for the injector exe which will be used to create a suspended process or attempt to inject into a running process.

[C]
int main(int argc, char *argv) { char pName = 0;
unsigned int type = 0, PID;
unsigned char psDLLname [MAX_PATH] = {0};
LPVOID pvMem = NULL;
LPDWORD rc;
PROCESS_INFORMATION ProcessInfo;

STARTUPINFO StartupInfo;

HANDLE hProcess, hProcess2, hThread;
if (argc < 3) { printf(“…[] Using %s / …”, argv[0]);
exit(0);
}

type = atoi(argv[2]);
EnableDebugPriv();

printf(“n []………. Type = %d , Process name = %sn”, type, argv[1]);

ZeroMemory(&StartupInfo, sizeof(StartupInfo));
StartupInfo.cb = sizeof StartupInfo ; //Required field only

if (type == 1) // Injection
{
PID = atoi(argv[1]);

hProcess = OpenProcess( PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, PID );

if (!hProcess)
{
printf(“Failed to open process..”);
}
}
otherwise // creation
{
pName = argv[1];
CreateProcess(pName, NULL, NULL,NULL,FALSE,CREATE_SUSPENDED,NULL,NULL,&StartupInfo,&ProcessInfo);
hProcess = OpenProcess( PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION |PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, ProcessInfo.dwProcessId );
}

GetModuleFileName(NULL, psDLLname, MAX_PATH);
sprintf(psDLLname, “%s.dll”, psDLLname);
pvMem = VirtualAllocEx( hProcess, 0, MAX_PATH, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
WriteProcessMemory( hProcess, pvMem, psDLLname, MAX_PATH, NULL);
hThread = CreateRemoteThread( hProcess, 0, 0, LoadLibrary, pvMem, 0, &rc );
ResumeThread(hThread);
ResumeThreads(ProcessInfo.dwProcessId, 1);

}
[/C]

To grant SE_DEBUG privileges to an application, we need to elevate those privileges to SE_DEBUG PRIVILEGES. We can use the following API call for this purpose.

[C]
void EnableDebugPriv( void )
{
HANDLE hToken;
LUID sedebugnameValue;
TOKEN_PRIVILEGES tkp;

if ( ! OpenProcessToken( GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken ) )
{
_debug();
return;
}

if ( ! LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &sedebugnameValue) )
{
_debug();
CloseHandle( hToken );
return;
}

tkp.PrivilegeCount = 1;
tkp.Privileges[0].Luid = sedebugnameValue;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

if ( ! AdjustTokenPrivileges( hToken, FALSE, &tkp, sizeof tkp, NULL, NULL) )
_debug();

CloseHandle( hToken );
}
[/C]

Similar code can be found here: http://msdn.microsoft.com/en-us/library/windows/desktop/aa446619(v=vs.85).aspx

Inside the dll we need to code a handler and a JMP replacement for the MessageBoxA API call.

The JMP instruction has the following OPCODE structure.

JMP = {0xe9} ADDRESS = {0x00, 0x00, 0x00, 0x00, 0x00}

where ADDRESS = jump destination – (EIP + SIZE (OPCODE))

which can be easily calculated using the following code:

[C]
unsigned char* JMP_OPCODE(unsigned int addr, unsigned int Hook)
{
static unsigned char OPCODE[0x06] = {0xe9, 0x00, 0x00, 0x00, 0x00, 0x00};

unsigned int Addr_RESX = addr – (Hook + 5);

memcpy(&OPCODE[1], &Addr_RESX, sizeof(int));
return OPCODE;
}

A similar thing can be done for OPCODE calls:

unsigned char* CALL_OPCODE(unsigned int addr, unsigned int Hook)
{
static unsigned char OPCODE[0x06] = {0xe8, 0x00, 0x00, 0x00, 0x00, 0x00};

unsigned int Addr_RESX = addr – (Hook + 5);

memcpy(&OPCODE[1], &Addr_RESX, sizeof(int));
return OPCODE;
}
[/C]

For example, if we want to get the JMP function opcode MessageBoxA, we will use this code in the following ways:

[C]
Void hook_function_MessageBoxA
{
….. Hook code

…. Jump back
}
unsigned char * trampoline = JMP_OPCODE(MessageBoxA, hook_function_MessageBoxA);
[/C]

This function returns a portion of five bytes of manipulated JMP opcode that can later be replaced in the function base.

[C]
Unsigned int org_buffer[5] = {0}; // This will store the original bytes for the function

buffer = getAddr(“MessageBoxA”, “user32.dll”);
VirtualProtect(buffer, 5, PAGE_EXECUTE_READWRITE, &x);

MessageBox(NULL, “Hello Word”!, “Hello World!”, MB_OK);
[/C]

We also need to save the original instructions and replace them again when our hook is called.

[C]
memcpy(org_buffer, API_CALL, 5);
[/C]

We also need to modify the Hooked function to replace the original bytes later:

[C]
Void hook_function_MessageBoxA
{
….. Hook code

memcmy(API_CALL, org_buffer, 5);
__asm
{
JMP [API_CALL];

}
[/C]

Related article:IPL Bootkits :Rovnix and Carberp-by Blackhat Pakistan 2023

Table of Contents

Leave a Reply

Your email address will not be published. Required fields are marked *