DEV Community

loading...
Cover image for Windows Hot Patching Mechanism Explained

Windows Hot Patching Mechanism Explained

pelock profile image PELock Updated on ・5 min read

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:

Hot patching bytes

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.

Discussion

pic
Editor guide