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

Type securityvulns
Reporter Securityvulns
Modified 2006-06-15T00:00:00


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 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 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 .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 .text:F822124B loc_F822124B: ; CODE XREF: KavSpinLockAcquire+8j .text:F822124B push eax .text:F822124C popf .text:F822124D .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 .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 .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 .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 .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 .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 .text:F8220448 loc_F8220448: ; DATA XREF: .text:F8212184o .text:F8220448 mov eax, 1 .text:F822044D retn .text:F822044E ; --------------------------------------------------------------------------- .text:F822044E .text:F822044E loc_F822044E: ; DATA XREF: .text:F8212188o .text:F822044E mov esp, [ebp-18h] .text:F8220451 .text:F8220451 loc_F8220451: ; CODE XREF: ModifyPteAttributes+54j .text:F8220451 ; ModifyPteAttributes+86j .text:F8220451 mov [ebp+var_4], 0FFFFFFFFh .text:F8220458 .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