Hot Patching is a mechanism introduced in Windows XP if I remember correctly. It was created to solve the problems with hooking system library functions. For example to bugfix compatibility issues with older software running on a newer Windows version or applying security patches on the running machines.
How function hooking works at a low-level?
Normally hooking a function in DLL libraries or EXE files is about getting the memory address of the function and putting a so-called trampoline (a jump) to the hook handling routine in place of its first instructions. Sounds simple, right?
Let's take a look.
Sample function prologue (beginning) before setting the hook:
_function_x:
push ebp
mov ebp,esp
mov eax,ecx
mov edx,0400h
...
Function after the hook is set:
_function_x:
; transfer execution to the hooking handler
; the original instructions were removed from
; the function prolog to put the JMP instruction
; that needs 5 bytes for encoding E9 xx xx xx xx
jmp _hook_handler
_after_the_trampoline:
mov edx,0400h
...
_hook_handler:
; save original registers & CPU flags states
pushad
pushfd
...
; execute hooking code, manipulate original parameters etc.
...
popfd
popad
; execute the code that's been cut off
; from the original function prologue
...
push ebp
mov ebp,esp
mov eax,ecx
; return to the code after the trampoline jump
jmp _after_the_trampoline
This involves using a disassembler to get the correct size of the first instructions (different x86/x64 instructions have different sizes) and make sure to replace enough bytes (five to be exact) to fit the assembly jmp rel32
instruction (encoded as E9 xx xx xx xx
bytes).
Problems with hooking prologue code with random instructions
But what if the first instructions are something else, like a call
instruction? That would require rewriting the original call
instruction or emulating it in the hooking handler code (we need to execute it after all!). But it's just troublesome to rewrite or emulate all the possible x86 instructions.
Microsoft has met such a demand for hooking and created a hot patching mechanism. The system library functions have a special prologue that makes it easy to quickly set up and remove hooks without having to bother to rewrite random instructions.
Function prologue with hot patching design
System library USER32.dll in HIEW hex editor and a sample function with hot patching code structure:
Notice the difference. It looks like this:
; 5 nop instructions
db 90h, 90h, 90h, 90h, 90h
_function_x:
; mov edi,edi takes 2 bytes
mov edi,edi
push ebp
mov ebp,esp
mov eax,ecx
mov edx,0400h
...
In hot patching enabled binaries (you need to build it with appropriate compiler flags!) the first bytes of the functions are always the mov edi,edi
instruction (which does nothing by itself, it's like C++ version of 0=0;
statement).
Before it, there is also a window (byte array) with five nop
instructions encoded as 0x90, 0x90, 0x90, 0x90
bytes.
What's the deal here, huh?
First step. Putting a hook trampoline into such a constructed function consists of inserting the jmp short
(encoded as two bytes EB xx
opcode) instruction into this mov edi,edi
instruction place.
Second step. It jumps to the above 5x nop
window, where the call rel32
or jmp rel32
instructions are placed that jumps or calls the hook handler code.
So this time you don't have to worry about overwriting random function prologue instructions, because there is a standard construct that can be easily restored to the default value (unhooked).
The whole thing is trivially simple and allows you to quickly insert hooks without the need to use a library like Microsoft Detours.
Patching libraries using hot patching features
Below I present the code for MASM compiler, showing an example of setting a hook on SetWindowTextA
function within our own process using hot patching code characteristics.
.data
; library to set a hook
szLibUser db 'USER32.dll',0
; hooked function name
szSetWindowTextA db 'SetWindowTextA',0
; original function address (after mov edi,edi prologue)
lpOrgSetWindowTextA dd 0
; default short jump instruction into 5 bytes NOP window
; it's encoded as a relative short jump instruction
cHotPatchJmps db 0EBh,0F9h
; call _hook (E9 xx xx xx xx) instruction encoding
cHotPatchCall db 0E9h
dwHotPatchRel dd 0
szHookTest db 'https://www.pelock.com',0
.code
; sample hook handler code
; it replaced every call to:
; SetWindowTextA(hWindow, lpString);
; with
; SetWindowsTextA(hWindow, "https://www.pelock.com");
align 4
_hook_SetWindowTextA proc uses esi edi ebx, hWnd:dword, lpString:dword
; ignore the original lpString parameter
; and call the function with our bogus parameter
; push lpString <-- original parameter
push offset szHookTest
push hWnd
call lpOrgSetWindowTextA
ret
_hook_SetWindowTextA endp
align 4
_make_hook proc uses esi edi ebx, hProcess:dword, lpszLib:dword, lpszProc:dword, lpOrgPtr:dword, lpHookHandlerProc:dword
; check if the DLL is already loaded
push lpszLib
call GetModuleHandleA
test eax,eax
je _make_hook_exit
; get the function pointer
push lpszProc
push eax
call GetProcAddress
test eax,eax
je _make_hook_exit
; save the function pointer to the EDI register
mov edi,eax
; check if the first bytes of the function
; matches the bytes of the mov edi,edi instruction
cmp word ptr[eax],0FF8Bh
jne _make_hook_exit
; store the pointer to the code after the mov edi,edi
; instruction (where the real function starts)
add eax,2
mov edx,lpOrgPtr
mov dword ptr[edx],eax
; overwrite mov edi,edi instruction
; with a jump to the 5x NOP window
push offset dwWritten
push 2
push offset cHotPatchJmps
push edi
push hProcess
call WriteProcessMemory
; hook handler routine address
mov edx,lpHookHandlerProc
; let's build a call _hook_handler_proc instruction
; 5x NOP window memory address
lea eax,[edi-5]
; call instruction is built like this:
;
; E9 xx xx xx xx
;
; where xx xx xx xx is 4 bytes of the relative
; address of the destination call address, relative
; to the position of the call instruction itself
;
; if we were to call the instruction right after
; any call instruction, for example like this:
;
; call _next_instruction_label
; _next_instruction_label:
; nop
;
; it would have been encoded as:
;
; E9 00 00 00 00
; 90
;
; to calculate the relative destination address
; take the destination pointer, subtract the call
; instruction pointer and its size (5 bytes)
;
; rel32 = dst - src - 5
;
sub edx,eax
sub edx,5
mov dwHotPatchRel,edx
; patch library function memory, by overwriting
; the 5x NOP window with a call to the hooking
; handler routine
push offset dwWritten
push 5
push offset cHotPatchCall
push eax
push hProcess
call WriteProcessMemory
_make_hook_exit:
ret
_make_hook endp
align 4
_test_hooks proc uses esi edi ebx
; our process ID
call GetCurrentProcessId
; open our own process
push eax
push 1
push PROCESS_ALL_ACCESS
call OpenProcess
test eax,eax
je _exit
mov ebx,eax
; setup a hook
mov edi,offset _make_hook
push offset _hook_SetWindowTextA ; hook proc
push offset lpOrgSetWindowTextA ; &lpOrgProc
push offset szSetWindowTextA ; szApiName
push offset szLibUser ; szLibName
push ebx ; hProcess
call edi ;_make_hook
_exit:
ret
_test_hooks endp
As you can see, hot patching can be used not only by Windows, but we can use it too to our advantage.
More low-level and reverse engineering resources
If you are interested in similar security and low-level topics, I invite you to read my articles about reverse engineering, malware analysis & assembler.
Top comments (0)