Uninformed: Informative Information for the Uninformed

Vol 4» 2006.Jun

Patching non-exported, non-system-service kernel functions

KAV's kernel patching is not limited to just system services, however. One of the most dangerous hooks that KAV installs is one in the middle of the nt!SwapContext function, which is neither exported nor a system service (and thus has no reliable mechanism to be detected by driver code, other than code fingerprinting). nt!SwapContext is called by the kernel on every context switch in order to perform some internal bookkeeping tasks.

Patching such a critical, non-exported kernel function with a mechanism as unreliable as blind code fingerprinting is, in the author's opinion, not a particularly good idea. To make matters worse, KAV actually modifies code in the middle of nt!SwapContext instead of patching the start of the function, and as such makes assumptions about the internal register and stack usage of this kernel function.

kd> u nt!SwapContext
804db924 0ac9             or      cl,cl
804db926 26c6462d02       mov     byte ptr es:[esi+0x2d],0x2
804db92b 9c               pushfd
804db92c 8b0b             mov     ecx,[ebx]
804db92e e9dd69d677       jmp     klif!KavSwapContext (f8242310)

The unmodified nt!SwapContext has code that runs along the lines of this:

lkd> u nt!SwapContext
80540ab0 0ac9             or      cl,cl
80540ab2 26c6462d02       mov     byte ptr es:[esi+0x2d],0x2
80540ab7 9c               pushfd
80540ab8 8b0b             mov     ecx,[ebx]
80540aba 83bb9409000000   cmp     dword ptr [ebx+0x994],0x0
80540ac1 51               push    ecx
80540ac2 0f8535010000     jne     nt!SwapContext+0x14d (80540bfd)
80540ac8 833d0ca0558000 cmp dword ptr [nt!PPerfGlobalGroupMask (8055a00c)],0x0

This is an extremely dangerous patching operation to make, for several reasons:

  1. nt!SwapContext is a *very* hot code path, as it is called on every single context switch. Therefore, patching it at runtime without running a non-trivial risk of bringing down the system is very difficult, especially on multiprocessor systems. KAV attempts to solve the synchronization problems relating to patching this function on uniprocessor systems by disabling interrupts entirely, but this approach will not work reliably on multiprocessor systems. KAV makes no attempt to address this problem on multiprocessor systems and puts them at the risk of randomly failing on boot during KAV's patching.

  2. Reliably locating this function and making assumptions about the register and stack usage (and instruction layout) across all released and future Windows versions is a practical impossibility, and yet KAV attempts to do just this. This puts KAV customers at the mercy of the next Windows update, which may cause their systems to crash on boot because KAV's hooking code makes an assumption that has been invalidated about the context-switching process.

Additionally, in order to perform code patching on the kernel, KAV adjusts the page protections of kernel code to be writable by altering PTE attributes directly instead of using documented functions (which would have proper locking semantics for accessing internal memory management structures).

KAV nt!SwapContext patching:

.text:F82264EA    mov     eax, 90909090h  ; Build the code to be written to nt!SwapContext
.text:F82264EF    mov     [ebp+var_38], eax
.text:F82264F2    mov     [ebp+var_34], eax
.text:F82264F5    mov     [ebp+var_30], ax
.text:F82264F9    mov     byte ptr [ebp+var_38], 0E9h
.text:F82264FD    mov     ecx, offset KavSwapContext
.text:F8226502    sub     ecx, ebx
.text:F8226504    sub     ecx, 5
.text:F8226507    mov     [ebp+var_38+1], ecx
.text:F822650A    mov     ecx, [ebp+var_1C]
.text:F822650D    lea     edx, [ecx+ebx]
.text:F8226510    mov     dword_F8228338, edx
.text:F8226516    mov     esi, ebx
.text:F8226518    mov     edi, offset unk_F8227DBC
.text:F822651D    mov     eax, ecx
.text:F822651F    shr     ecx, 2
.text:F8226522    rep movsd
.text:F8226524    mov     ecx, eax
.text:F8226526    and     ecx, 3
.text:F8226529    rep movsb
.text:F822652B    lea     ecx, [ebp+var_48] ; Make nt!SwapContext writable by directly accessing
.text:F822652B               ; the PTEs.
.text:F822652E    push    ecx
.text:F822652F    push    1
.text:F8226531    push    ebx
.text:F8226532    call    ModifyPteAttributes
.text:F8226537    test    al, al
.text:F8226539    jz      short loc_F8226588
.text:F822653B    mov     ecx, offset KavInternalSpinLock
.text:F8226540    call    KavSpinLockAcquire ; Disable interrupts
.text:F8226545    mov     ecx, [ebp+var_1C] ; Write to kernel code
.text:F8226548    lea     esi, [ebp+var_38]
.text:F822654B    mov     edi, ebx
.text:F822654D    mov     edx, ecx
.text:F822654F    shr     ecx, 2
.text:F8226552    rep movsd
.text:F8226554    mov     ecx, edx
.text:F8226556    and     ecx, 3
.text:F8226559    rep movsb
.text:F822655B    mov     edx, eax
.text:F822655D    mov     ecx, offset KavInternalSpinLock
.text:F8226562    call    KavSpinLockRelease ; Reenable interrupts
.text:F8226567    lea     eax, [ebp+var_48] ; Restore the original PTE attributes.
.text:F822656A    push    eax
.text:F822656B    mov     ecx, [ebp+var_48]
.text:F822656E    push    ecx
.text:F822656F    push    ebx
.text:F8226570    call    ModifyPteAttributes
.text:F8226575    mov     al, 1
.text:F8226577    mov     ecx, [ebp+var_10]
.text:F822657A    mov     large fs:0, ecx
.text:F8226581    pop     edi
.text:F8226582    pop     esi
.text:F8226583    pop     ebx
.text:F8226584    mov     esp, ebp
.text:F8226586    pop     ebp
.text:F8226587    retn

KavSpinLockAcquire subroutine (disables interrupts):

.text:F8221240 KavSpinLockAcquire proc near            ; CODE XREF: sub_F8225690+D7p
.text:F8221240               ; sub_F8225D50+8Cp ...
.text:F8221240    pushf
.text:F8221241    pop     eax
.text:F8221242 loc_F8221242:              ; CODE XREF: KavSpinLockAcquire+13j
.text:F8221242    cli
.text:F8221243    lock bts dword ptr [ecx], 0
.text:F8221248    jb      short loc_F822124B
.text:F822124A    retn
.text:F822124B ; ---------------------------------------------------------------------------
.text:F822124B loc_F822124B:              ; CODE XREF: KavSpinLockAcquire+8j
.text:F822124B    push    eax
.text:F822124C    popf
.text:F822124D loc_F822124D:              ; CODE XREF: KavSpinLockAcquire+17j
.text:F822124D    test    dword ptr [ecx], 1
.text:F8221253    jz      short loc_F8221242
.text:F8221255    pause
.text:F8221257    jmp     short loc_F822124D
.text:F8221257 KavSpinLockAcquire endp

KavSpinLockRelease subroutine (reenables interrupts):

.text:F8221260 KavSpinLockRelease proc near            ; CODE XREF: sub_F8225690+F2p
.text:F8221260               ; sub_F8225D50+BAp ...
.text:F8221260    mov     dword ptr [ecx], 0
.text:F8221266    push    edx
.text:F8221267    popf
.text:F8221268    retn
.text:F8221268 KavSpinLockRelease endp

ModifyPteAttributes subroutine:

.text:F82203C0 ModifyPteAttributes proc near           ; CODE XREF: sub_F821A9D0+91p
.text:F82203C0                                         ; sub_F8220950+43p ...
.text:F82203C0 var_24          = dword ptr -24h
.text:F82203C0 var_20          = byte ptr -20h
.text:F82203C0 var_1C          = dword ptr -1Ch
.text:F82203C0 var_18          = dword ptr -18h
.text:F82203C0 var_10          = dword ptr -10h
.text:F82203C0 var_4           = dword ptr -4
.text:F82203C0 arg_0           = dword ptr  8
.text:F82203C0 arg_4           = byte ptr  0Ch
.text:F82203C0 arg_8           = dword ptr  10h
.text:F82203C0    push    ebp
.text:F82203C1    mov     ebp, esp
.text:F82203C3    push    0FFFFFFFFh
.text:F82203C5    push    offset dword_F8212180
.text:F82203CA    push    offset _except_handler3
.text:F82203CF    mov     eax, large fs:0
.text:F82203D5    push    eax
.text:F82203D6    mov     large fs:0, esp
.text:F82203DD    sub     esp, 14h
.text:F82203E0    push    ebx
.text:F82203E1    push    esi
.text:F82203E2    push    edi
.text:F82203E3    mov     [ebp+var_18], esp
.text:F82203E6    xor     ebx, ebx
.text:F82203E8    mov     [ebp+var_20], bl
.text:F82203EB    mov     esi, [ebp+arg_0]
.text:F82203EE    mov     ecx, esi
.text:F82203F0    call    KavGetEflags
.text:F82203F5    push    esi
.text:F82203F6    call    KavGetPte       ; This is a function pointer filled in at runtime,
.text:F82203F6               ; differing based on whether the system has PAE
.text:F82203F6               ; enabled or not.
.text:F82203FC    mov     edi, eax
.text:F82203FE    mov     [ebp+var_1C], edi
.text:F8220401    cmp     edi, 0FFFFFFFFh
.text:F8220404    jz      short loc_F8220458
.text:F8220406    mov     [ebp+var_4], ebx
.text:F8220409    mov     ecx, esi
.text:F822040B    call    KavGetEflags
.text:F8220410    mov     eax, [edi]
.text:F8220412    test    al, 1
.text:F8220414    jz      short loc_F8220451
.text:F8220416    mov     ecx, eax
.text:F8220418    mov     [ebp+var_24], ecx
.text:F822041B    cmp     [ebp+arg_4], bl
.text:F822041E    jz      short loc_F8220429
.text:F8220420    mov     eax, [ebp+var_1C]
.text:F8220423    lock or dword ptr [eax], 2
.text:F8220427    jmp     short loc_F8220430
.text:F8220429 ; ---------------------------------------------------------------------------
.text:F8220429 loc_F8220429:              ; CODE XREF: ModifyPteAttributes+5Ej
.text:F8220429    mov     eax, [ebp+var_1C]
.text:F822042C    lock and dword ptr [eax], 0FFFFFFFDh
.text:F8220430 loc_F8220430:              ; CODE XREF: ModifyPteAttributes+67j
.text:F8220430    mov     eax, [ebp+arg_8]
.text:F8220433    cmp     eax, ebx
.text:F8220435    jz      short loc_F822043C
.text:F8220437    and     ecx, 2
.text:F822043A    mov     [eax], cl
.text:F822043C loc_F822043C:              ; CODE XREF: ModifyPteAttributes+75j
.text:F822043C    mov     [ebp+var_20], 1
.text:F8220440    mov     eax, [ebp+arg_0]
.text:F8220443    invlpg  byte ptr [eax]
.text:F8220446    jmp     short loc_F8220451
.text:F8220448 ; ---------------------------------------------------------------------------
.text:F8220448 loc_F8220448:              ; DATA XREF: .text:F8212184o
.text:F8220448    mov     eax, 1
.text:F822044D    retn
.text:F822044E ; ---------------------------------------------------------------------------
.text:F822044E loc_F822044E:              ; DATA XREF: .text:F8212188o
.text:F822044E    mov     esp, [ebp-18h]
.text:F8220451 loc_F8220451:              ; CODE XREF: ModifyPteAttributes+54j
.text:F8220451               ; ModifyPteAttributes+86j
.text:F8220451    mov     [ebp+var_4], 0FFFFFFFFh
.text:F8220458 loc_F8220458:              ; CODE XREF: ModifyPteAttributes+44j
.text:F8220458    mov     al, [ebp+var_20]
.text:F822045B    mov     ecx, [ebp+var_10]
.text:F822045E    mov     large fs:0, ecx
.text:F8220465    pop     edi
.text:F8220466    pop     esi
.text:F8220467    pop     ebx
.text:F8220468    mov     esp, ebp
.text:F822046A    pop     ebp
.text:F822046B    retn    0Ch
.text:F822046B ModifyPteAttributes endp