Function Hooking
Table of content
Definition
A hook is a callback added to a method that will redirect program execution flow. For example, EDR
can add hooks to sensitive functions to check the different parameters used.
The hook can be implemented easily with a JMP
added at the beginning of the function or through specific callback functions such as NtSetProcessInformation
.
Hook example
x86 : Add JMP patch
The following x86
code hooks the MessageBoxA
function and redirect the execution flow to another function:
#include <windows.h>
#include <stdio.h>
#define BYTES_REQUIRED 6
// Function where the execution flow will be redirected
int __stdcall HookedMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType){
printf("\n[ HOOKED MESSAGEBOXA ]\n");
printf("Arguments:\n");
printf(" 1. lpText: %s\n", lpText);
printf(" 2. lpCaption: %s\n", lpCaption);
printf(" 3. uType: %ld\n", uType);
return 1;
}
int main()
{
SIZE_T lpNumberOfBytesRead = 0;
HMODULE hModule = NULL;
FARPROC pMessageBoxAFunc = NULL;
char pMessageBoxABytes[BYTES_REQUIRED];
void* pHookedMessageBoxFunc = &HookedMessageBoxA;
hModule = LoadLibraryA("user32.dll");
if (!hModule){
return -1;
}
pMessageBoxAFunc = GetProcAddress(hModule, "MessageBoxA");
if (ReadProcessMemory(GetCurrentProcess(), pMessageBoxAFunc, pMessageBoxABytes, BYTES_REQUIRED, &lpNumberOfBytesRead) == FALSE){
printf("[!] ReadProcessMemory: %ld\n", GetLastError());
return -1;
}
// Add the hook :
// JMP HookedMessageBoxFunc
// RET
char patch[BYTES_REQUIRED] = { 0 };
// JMP OPCODE
memcpy_s(patch, 1, "\x68", 1);
// Function address where the execution flow will be redirect
memcpy_s(patch + 1, 4, &pHookedMessageBoxFunc, 4);
// RET OPCODE
memcpy_s(patch + 5, 1, "\xC3", 1);
// Write the patch
if (WriteProcessMemory(GetCurrentProcess(), (LPVOID)pMessageBoxAFunc, patch, sizeof(patch), &lpNumberOfBytesRead) == FALSE){
printf("[!] WriteProcessMemory: %ld\n", GetLastError());
return -1;
}
// Call the hooked function
MessageBoxA(NULL, "AAAAA", "BBBBB", MB_OK);
return 0;
}
/*
Result :
[ HOOKED MESSAGEBOXA ]
-> Arguments:
1. lpText: AAAAA
2. lpCaption: BBBBB
3. uType: 0
*/
For x64
the patch's OPCODE must be changed
x86 : NtSetProcessInformation - Hook syscall results
The NtSetProcessInformation
is called every time the kernel
sent back a value to the usermode
. When a syscall
is performed, the kernel
send back the result to the usermode
function and call the NtSetProcessInformation
function first.
The NtSetProcessInformation
takes as an argument a function pointer used as a callback function. Thus, this function will be executed each time the kernel
communicates with the usermode. It then can be used to intercept any system calls performed, any process or thread created etc...
Before calling the callback, the registers must be saved in the stack. Then the callback function is called and the registers are popped out from the stack. this this for more explanations.
R10
contains the last instruction pointer. When the callback is finished, just jump to R10
to continue the normal execution flow.
This is summarized in the following ASM
code :
include ksamd64.inc
; the callback function that will be called and defined in the C file
EXTERN hook:NEAR
.code
medium PROC
; https://docs.microsoft.com/en-us/cpp/build/caller-callee-saved-registers
push rax ; return value if system call
push rcx
push RBX
push RBP
push RDI
push RSI
push RSP
push R12
push R13
push R14
push R15
; allocate stack
sub rsp, 1000h
; set parameters for the hook function
mov rdx, rax
mov rcx, r10
; call the hook function
call hook
; erase the allocated stack
add rsp, 1000h
; repop registers
pop R15
pop R14
pop R13
pop R12
pop RSP
pop RSI
pop RDI
pop RBP
pop RBX
pop rcx
pop rax
; jump to the normal execution flow
jmp R10
medium ENDP
END
Resource
- Nirvana Hooking : https://www.youtube.com/watch?v=pHyWyH804xE