Uninformed: Informative Information for the Uninformed

Vol 4» 2006.Jun


Allowing User-mode Code to Access Kernel Memory

One of the most important principles of the kernel/user division that modern operating systems enforce is that user mode is not allowed to directly access kernel mode memory. This is necessary to enforce system stability, such as to prevent a buggy user mode program from corrupting the kernel and bringing down the whole system. Unfortunately, the KAV programmers appear to think that this distinction is not really so important after all.

One of the strangest of the unsafe practices implemented by KAV is to allow user mode to directly call some portions of their kernel driver (within kernel address space!) instead of just loading a user mode DLL (or otherwise loading user mode code in the target process).

This mechanism appears to be used to inspect DLLs as they are loaded - a task which would be much better accomplished with PsSetLoadImageNotifyRoutine.

KAV patches kernel32.dll as a new process is created, such that the export table points all of the DLL-loading routines (e.g. LoadLibraryA) to a thunk that calls portions of KAV's driver in kernel mode. Additionally, KAV modifies protections on parts of its code and data sections to allow user mode read access.

KAV sets a PsLoadImageNotifyRoutine hook to detect kernel32.dll being loaded in order to know when to patch kernel32's export table. The author wonders why KAV did not just do their work from within PsSetLoadImageNotifyRoutine directly instead of going through all the trouble to allow user mode to call kernel mode for a LoadLibrary hook.

The CheckInjectCodeForNewProcess function is called when a new process loads an image, and checks for kernel32 being loaded. If this is the case, it will queue an APC to the process that will perform patching.

.text:F82218B0 ; int __stdcall CheckInjectCodeForNewProcess(wchar_t *,PUCHAR ImageBase)
.text:F82218B0 CheckInjectCodeForNewProcess proc near  ; CODE XREF: KavLoadImageNotifyRoutine+B5p
.text:F82218B0               ; KavDoKernel32Check+41p
.text:F82218B0
.text:F82218B0 arg_0           = dword ptr  4
.text:F82218B0 ImageBase       = dword ptr  8
.text:F82218B0
.text:F82218B0    mov     al, byte_F82282F9
.text:F82218B5    push    esi
.text:F82218B6    test    al, al
.text:F82218B8    push    edi
.text:F82218B9    jz      short loc_F8221936
.text:F82218BB    mov     eax, [esp+8+arg_0]
.text:F82218BF    push    offset aKernel32_dll ; "kernel32.dll"
.text:F82218C4    push    eax             ; wchar_t *
.text:F82218C5    call    ds:_wcsicmp
.text:F82218CB    add     esp, 8
.text:F82218CE    test    eax, eax
.text:F82218D0    jnz     short loc_F8221936
.text:F82218D2    mov     al, g_FoundKernel32Exports
.text:F82218D7    mov     edi, [esp+8+ImageBase]
.text:F82218DB    test    al, al
.text:F82218DD    jnz     short KavInitializePatchApcLabel
.text:F82218DF    push    edi
.text:F82218E0    call    KavCheckFindKernel32Exports
.text:F82218E5    test    al, al
.text:F82218E7    jz      short loc_F8221936
.text:F82218E9
.text:F82218E9 KavInitializePatchApcLabel:             ; CODE XREF: CheckInjectCodeForNewProcess+2Dj
.text:F82218E9    push    '3SeB'          ; Tag
.text:F82218EE    push    30h             ; NumberOfBytes
.text:F82218F0    push    0               ; PoolType
.text:F82218F2    call    ds:ExAllocatePoolWithTag
.text:F82218F8    mov     esi, eax
.text:F82218FA    test    esi, esi
.text:F82218FC    jz      short loc_F8221936
.text:F82218FE    push    edi
.text:F82218FF    push    0
.text:F8221901    push    offset KavPatchNewProcessApcRoutine
.text:F8221906    push    offset loc_F82218A0
.text:F822190B    push    offset loc_F8221890
.text:F8221910    push    0
.text:F8221912    call    KeGetCurrentThread
.text:F8221917    push    eax
.text:F8221918    push    esi
.text:F8221919    call    KeInitializeApc
.text:F822191E    push    0
.text:F8221920    push    0
.text:F8221922    push    0
.text:F8221924    push    esi
.text:F8221925    call    KeInsertQueueApc
.text:F822192B    test    al, al
.text:F822192D    jnz     short loc_F822193D
.text:F822192F    push    esi             ; P
.text:F8221930    call    ds:ExFreePool
.text:F8221936
.text:F8221936 loc_F8221936:              ; CODE XREF: CheckInjectCodeForNewProcess+9j
.text:F8221936               ; CheckInjectCodeForNewProcess+20j ...
.text:F8221936    pop     edi
.text:F8221937    xor     al, al
.text:F8221939    pop     esi
.text:F822193A    retn    8
.text:F822193D ; ---------------------------------------------------------------------------
.text:F822193D
.text:F822193D loc_F822193D:              ; CODE XREF: CheckInjectCodeForNewProcess+7Dj
.text:F822193D    pop     edi
.text:F822193E    mov     al, 1
.text:F8221940    pop     esi
.text:F8221941    retn    8

The APC routine itself patches kernel32's export table (and generates the thunks to call kernel mode) and adjusts PTE attributes on KAV's driver image to allow user mode access.

.text:F8221810 KavPatchNewProcessApcRoutine proc near  ; DATA XREF: CheckInjectCodeForNewProcess+51o
.text:F8221810
.text:F8221810 var_8           = dword ptr -8
.text:F8221810 var_4           = dword ptr -4
.text:F8221810 ImageBase       = dword ptr  8
.text:F8221810
.text:F8221810    push    ebp
.text:F8221811    mov     ebp, esp
.text:F8221813    sub     esp, 8
.text:F8221816    mov     eax, [ebp+ImageBase]
.text:F8221819    push    esi
.text:F822181A    push    eax             ; ImageBase
.text:F822181B    call    KavPatchImageForNewProcess
.text:F8221820    mov     esi, dword_F8230518
.text:F8221826    mov     eax, dword_F823051C
.text:F822182B    and     esi, 0FFFFF000h
.text:F8221831    cmp     esi, eax
.text:F8221833    mov     [ebp+ImageBase], esi
.text:F8221836    jnb     short loc_F8221883
.text:F8221838
.text:F8221838 loc_F8221838:              ; CODE XREF: KavPatchNewProcessApcRoutine+71j
.text:F8221838    push    esi
.text:F8221839    call    KavPageTranslation0
.text:F822183F    push    esi
.text:F8221840    mov     [ebp+var_8], eax
.text:F8221843    call    KavPageTranslation1
.text:F8221849    mov     [ebp+var_4], eax
.text:F822184C    mov     eax, [ebp+var_8]
.text:F822184F    lock or dword ptr [eax], 4
.text:F8221853    lock and dword ptr [eax], 0FFFFFEFFh
.text:F822185A    mov     eax, [ebp+var_4]
.text:F822185D    invlpg  byte ptr [eax]
.text:F8221860    lock or dword ptr [eax], 4
.text:F8221864    lock and dword ptr [eax], 0FFFFFEFDh
.text:F822186B    mov     eax, [ebp+ImageBase]
.text:F822186E    invlpg  byte ptr [eax]
.text:F8221871    mov     eax, dword_F823051C
.text:F8221876    add     esi, 1000h
.text:F822187C    cmp     esi, eax
.text:F822187E    mov     [ebp+ImageBase], esi
.text:F8221881    jb      short loc_F8221838
.text:F8221883
.text:F8221883 loc_F8221883:              ; CODE XREF: KavPatchNewProcessApcRoutine+26j
.text:F8221883    pop     esi
.text:F8221884    mov     esp, ebp
.text:F8221886    pop     ebp
.text:F8221887    retn    0Ch
.text:F8221887 KavPatchNewProcessApcRoutine endp

.text:F8221750 ; int __stdcall KavPatchImageForNewProcess(PUCHAR ImageBase)
.text:F8221750 KavPatchImageForNewProcess proc near    ; CODE XREF: KavPatchNewProcessApcRoutine+Bp
.text:F8221750
.text:F8221750 ImageBase       = dword ptr  8
.text:F8221750
.text:F8221750    push    ebx
.text:F8221751    call    ds:KeEnterCriticalRegion
.text:F8221757    mov     eax, dword_F82282F4
.text:F822175C    push    1               ; Wait
.text:F822175E    push    eax             ; Resource
.text:F822175F    call    ds:ExAcquireResourceExclusiveLite
.text:F8221765    push    1
.text:F8221767    call    KavSetPageAttributes1
.text:F822176C    mov     ecx, [esp+ImageBase]
.text:F8221770    push    ecx             ; ImageBase
.text:F8221771    call    KavPatchImage
.text:F8221776    push    0
.text:F8221778    mov     bl, al
.text:F822177A    call    KavSetPageAttributes1
.text:F822177F    mov     ecx, dword_F82282F4 ; Resource
.text:F8221785    call    ds:ExReleaseResourceLite
.text:F822178B    call    ds:KeLeaveCriticalRegion
.text:F8221791    mov     al, bl
.text:F8221793    pop     ebx
.text:F8221794    retn    4
.text:F8221794 KavPatchImageForNewProcess endp

The actual image patching reprotects the export table of kernel32, changes the export address table entries for the LoadLibrary* family of functions to point to a thunk that is written into spare space within the kernel32 image, and writes the actual thunk code out:

.text:F8221680 ; int __stdcall KavPatchImage(PUCHAR ImageBase)
.text:F8221680 KavPatchImage   proc near               ; CODE XREF: KavPatchImageForNewProcess+21p
.text:F8221680
.text:F8221680 var_C           = dword ptr -0Ch
.text:F8221680 FunctionVa      = dword ptr -8
.text:F8221680 var_4           = dword ptr -4
.text:F8221680 ImageBase       = dword ptr  4
.text:F8221680
.text:F8221680    mov     eax, [esp+ImageBase]
.text:F8221684    sub     esp, 0Ch
.text:F8221687    push    ebp
.text:F8221688    push    3Ch
.text:F822168A    push    eax
.text:F822168B    call    KavReprotectExportTable
.text:F8221690    mov     ebp, eax
.text:F8221692    test    ebp, ebp
.text:F8221694    jnz     short loc_F822169F
.text:F8221696    xor     al, al
.text:F8221698    pop     ebp
.text:F8221699    add     esp, 0Ch
.text:F822169C    retn    4
.text:F822169F ; ---------------------------------------------------------------------------
.text:F822169F
.text:F822169F loc_F822169F:              ; CODE XREF: KavPatchImage+14j
.text:F822169F    push    ebx
.text:F82216A0    push    esi
.text:F82216A1    push    edi
.text:F82216A2    xor     ebx, ebx
.text:F82216A4    mov     edi, ebp
.text:F82216A6    mov     esi, offset ExportedFunctionsToCheckTable
.text:F82216AB
.text:F82216AB CheckNextFunctionInTable:               ; CODE XREF: KavPatchImage+B4j
.text:F82216AB    mov     edx, [esi+0Ch]
.text:F82216AE    mov     eax, [esp+1Ch+ImageBase]
.text:F82216B2    lea     ecx, [esp+1Ch+var_C]
.text:F82216B6    push    ecx
.text:F82216B7    push    edx
.text:F82216B8    push    eax
.text:F82216B9    call    LookupExportedFunction
.text:F82216BE    test    eax, eax
.text:F82216C0    mov     [esp+1Ch+FunctionVa], eax
.text:F82216C4    jz      short loc_F8221725
.text:F82216C6    mov     edx, [esp+1Ch+var_C]
.text:F82216CA    lea     ecx, [esp+1Ch+var_4]
.text:F82216CE    push    ecx
.text:F82216CF    push    40h
.text:F82216D1    push    4
.text:F82216D3    push    edx
.text:F82216D4    call    KavExecuteNtProtectVirtualMemoryInt2E
.text:F82216D9    test    al, al
.text:F82216DB    jz      short loc_F8221725
.text:F82216DD    cmp     dword ptr [esi], 0
.text:F82216E0    jnz     short loc_F82216EF
.text:F82216E2    mov     eax, [esp+1Ch+FunctionVa]
.text:F82216E6    mov     ecx, [esp+1Ch+var_C]
.text:F82216EA    mov     [esi], eax
.text:F82216EC    mov     [esi+8], ecx
.text:F82216EF
.text:F82216EF loc_F82216EF:              ; CODE XREF: KavPatchImage+60j
.text:F82216EF    mov     eax, edi
.text:F82216F1    mov     edx, 90909090h
.text:F82216F6    mov     [eax], edx
.text:F82216F8    mov     [eax+4], edx
.text:F82216FB    mov     [eax+8], edx
.text:F82216FE    mov     [eax+0Ch], dx
.text:F8221702    mov     [eax+0Eh], dl
.text:F8221705    mov     byte ptr [edi], 0E9h
.text:F8221708    mov     ecx, [esi+4]
.text:F822170B    mov     edx, ebx
.text:F822170D    sub     ecx, ebx
.text:F822170F    sub     ecx, ebp
.text:F8221711    sub     ecx, 5
.text:F8221714    mov     [edi+1], ecx
.text:F8221717    mov     ecx, [esp+1Ch+ImageBase]
.text:F822171B    mov     eax, [esp+1Ch+var_C]
.text:F822171F    sub     edx, ecx
.text:F8221721    add     edx, ebp
.text:F8221723    mov     [eax], edx      ;
.text:F8221723               ; Patching Export Table here
.text:F8221723               ; e.g. write to 7c802f58
.text:F8221723               ; (kernel32 EAT entry for LoadLibraryA)
.text:F8221723               ;
.text:F8221723               ;         578  241 00001D77 LoadLibraryA = _LoadLibraryA@4
.text:F8221723               ;         579  242 00001D4F LoadLibraryExA = _LoadLibraryExA@12
.text:F8221723               ;         580  243 00001AF1 LoadLibraryExW = _LoadLibraryExW@12
.text:F8221723               ;         581  244 0000ACD3 LoadLibraryW = _LoadLibraryW@4
.text:F8221723               ;
.text:F8221723               ; KAV writes a new RVA pointing to its hook code here.
.text:F8221725
.text:F8221725 loc_F8221725:              ; CODE XREF: KavPatchImage+44j
.text:F8221725               ; KavPatchImage+5Bj
.text:F8221725    add     esi, 10h
.text:F8221728    add     ebx, 0Fh
.text:F822172B    add     edi, 0Fh
.text:F822172E    cmp     esi, offset byte_F82357E0
.text:F8221734    jb      CheckNextFunctionInTable
.text:F822173A    pop     edi
.text:F822173B    pop     esi
.text:F822173C    pop     ebx
.text:F822173D    mov     al, 1
.text:F822173F    pop     ebp
.text:F8221740    add     esp, 0Ch
.text:F8221743    retn    4
.text:F8221743 KavPatchImage   endp

KAV's export table reprotecting code assumes that the user mode PE header is well-formed and does not contain offsets pointing to kernel mode addresses:

.text:F8221360 KavReprotectExportTable proc near       ; CODE XREF: KavPatchImage+Bp
.text:F8221360
.text:F8221360 var_10          = dword ptr -10h
.text:F8221360 var_C           = dword ptr -0Ch
.text:F8221360 var_8           = dword ptr -8
.text:F8221360 var_4           = dword ptr -4
.text:F8221360 arg_0           = dword ptr  4
.text:F8221360 arg_4           = dword ptr  8
.text:F8221360
.text:F8221360    mov     eax, [esp+arg_0]
.text:F8221364    sub     esp, 10h
.text:F8221367    cmp     word ptr [eax], 'ZM'
.text:F822136C    push    ebx
.text:F822136D    push    ebp
.text:F822136E    push    esi
.text:F822136F    push    edi
.text:F8221370    jnz     loc_F8221442
.text:F8221376    mov     esi, [eax+3Ch]
.text:F8221379    add     esi, eax
.text:F822137B    mov     [esp+20h+var_C], esi
.text:F822137F    cmp     dword ptr [esi], 'EP'
.text:F8221385    jnz     loc_F8221442
.text:F822138B    lea     eax, [esp+20h+var_8]
.text:F822138F    xor     edx, edx
.text:F8221391    mov     dx, [esi+14h]
.text:F8221395    push    eax
.text:F8221396    xor     eax, eax
.text:F8221398    push    40h
.text:F822139A    mov     ax, [esi+6]
.text:F822139E    lea     ecx, [eax+eax*4]
.text:F82213A1    lea     eax, [edx+ecx*8+18h]
.text:F82213A5    push    eax
.text:F82213A6    push    esi
.text:F82213A7    call    KavExecuteNtProtectVirtualMemoryInt2E ; NtProtectVirtualMemory
.text:F82213AC    test    al, al
.text:F82213AE    jz      loc_F8221442
.text:F82213B4    mov     ecx, [esi+8]
.text:F82213B7    mov     [esp+20h+var_10], 0
.text:F82213BF    inc     ecx
.text:F82213C0    mov     [esi+8], ecx
.text:F82213C3    xor     ecx, ecx
.text:F82213C5    mov     cx, [esi+14h]
.text:F82213C9    cmp     word ptr [esi+6], 0
.text:F82213CE    lea     edi, [ecx+esi+18h]
.text:F82213D2    jbe     short loc_F8221442
.text:F82213D4    mov     ebp, [esp+20h+arg_4]
.text:F82213D8
.text:F82213D8 loc_F82213D8:              ; CODE XREF: KavReprotectExportTable+E0j
.text:F82213D8    mov     ebx, [edi+10h]
.text:F82213DB    test    ebx, 0FFFh
.text:F82213E1    jz      short loc_F82213EA
.text:F82213E3    or      ebx, 0FFFh
.text:F82213E9    inc     ebx
.text:F82213EA
.text:F82213EA loc_F82213EA:              ; CODE XREF: KavReprotectExportTable+81j
.text:F82213EA    mov     ecx, [edi+8]
.text:F82213ED    mov     edx, ebx
.text:F82213EF    sub     edx, ecx
.text:F82213F1    cmp     edx, ebp
.text:F82213F3    jle     short loc_F822142C
.text:F82213F5    mov     esi, [edi+0Ch]
.text:F82213F8    mov     ecx, [esp+20h+arg_0]
.text:F82213FC    sub     esi, ebp
.text:F82213FE    push    ebp
.text:F82213FF    add     esi, ebx
.text:F8221401    add     esi, ecx
.text:F8221403    push    esi
.text:F8221404    call    KavFindSectionName
.text:F8221409    test    al, al
.text:F822140B    jz      short loc_F8221428
.text:F822140D    cmp     dword ptr [edi+1], 'TINI'
.text:F8221414    jz      short loc_F8221428
.text:F8221416    lea     eax, [esp+20h+var_4]
.text:F822141A    push    eax
.text:F822141B    push    40h
.text:F822141D    push    ebp
.text:F822141E    push    esi
.text:F822141F    call    KavExecuteNtProtectVirtualMemoryInt2E ; NtProtectVirtualMemory
.text:F8221424    test    al, al
.text:F8221426    jnz     short loc_F822144E
.text:F8221428
.text:F8221428 loc_F8221428:              ; CODE XREF: KavReprotectExportTable+ABj
.text:F8221428               ; KavReprotectExportTable+B4j
.text:F8221428    mov     esi, [esp+20h+var_C]
.text:F822142C
.text:F822142C loc_F822142C:              ; CODE XREF: KavReprotectExportTable+93j
.text:F822142C    mov     eax, [esp+20h+var_10]
.text:F8221430    xor     ecx, ecx
.text:F8221432    mov     cx, [esi+6]
.text:F8221436    add     edi, 28h
.text:F8221439    inc     eax
.text:F822143A    cmp     eax, ecx
.text:F822143C    mov     [esp+20h+var_10], eax
.text:F8221440    jb      short loc_F82213D8
.text:F8221442
.text:F8221442 loc_F8221442:              ; CODE XREF: KavReprotectExportTable+10j
.text:F8221442               ; KavReprotectExportTable+25j ...
.text:F8221442    pop     edi
.text:F8221443    pop     esi
.text:F8221444    pop     ebp
.text:F8221445    xor     eax, eax
.text:F8221447    pop     ebx
.text:F8221448    add     esp, 10h
.text:F822144B    retn    8
.text:F822144E ; ---------------------------------------------------------------------------
.text:F822144E
.text:F822144E loc_F822144E:              ; CODE XREF: KavReprotectExportTable+C6j
.text:F822144E    mov     eax, [edi+8]
.text:F8221451    mov     [edi+10h], ebx
.text:F8221454    add     eax, ebp
.text:F8221456    mov     [edi+8], eax
.text:F8221459    mov     eax, esi
.text:F822145B    pop     edi
.text:F822145C    pop     esi
.text:F822145D    pop     ebp
.text:F822145E    pop     ebx
.text:F822145F    add     esp, 10h
.text:F8221462    retn    8
.text:F8221462 KavReprotectExportTable endp

The mechanism that KAV uses to reprotect user mode code is much of a hack as well. KAV dynamically determines the system call ordinal of the NtProtectVirtualMemory system service and uses its own int 2e thunk to call the service.

.text:F8221320 KavExecuteNtProtectVirtualMemoryInt2E proc near
.text:F8221320               ; CODE XREF: KavReprotectExportTable+47p
.text:F8221320               ; KavReprotectExportTable+BFp ...
.text:F8221320
.text:F8221320 arg_0           = dword ptr  4
.text:F8221320 arg_4           = dword ptr  8
.text:F8221320 arg_8           = dword ptr  0Ch
.text:F8221320 arg_C           = dword ptr  10h
.text:F8221320
.text:F8221320    mov     eax, [esp+arg_0]
.text:F8221324    mov     ecx, [esp+arg_C]
.text:F8221328    mov     edx, [esp+arg_8]
.text:F822132C    push    ebx
.text:F822132D    mov     [esp+4+arg_0], eax
.text:F8221331    push    ecx
.text:F8221332    lea     eax, [esp+8+arg_4]
.text:F8221336    push    edx
.text:F8221337    mov     edx, NtProtectVirtualMemoryOrdinal
.text:F822133D    lea     ecx, [esp+0Ch+arg_0]
.text:F8221341    push    eax
.text:F8221342    push    ecx
.text:F8221343    push    0FFFFFFFFh
.text:F8221345    push    edx
.text:F8221346    xor     bl, bl
.text:F8221348    call    KavInt2E
.text:F822134D    test    eax, eax
.text:F822134F    mov     al, 1
.text:F8221351    jge     short loc_F8221355
.text:F8221353    mov     al, bl
.text:F8221355
.text:F8221355 loc_F8221355:              ; CODE XREF: KavExecuteNtProtectVirtualMemoryInt2E+31j
.text:F8221355    pop     ebx
.text:F8221356    retn    10h
.text:F8221356 KavExecuteNtProtectVirtualMemoryInt2E endp


.user:F8231090 KavInt2E        proc near               ; CODE XREF: KavExecuteNtProtectVirtualMemoryInt2E+28p
.user:F8231090
.user:F8231090 arg_0           = dword ptr  8
.user:F8231090 arg_4           = dword ptr  0Ch
.user:F8231090
.user:F8231090    push    ebp
.user:F8231091    mov     ebp, esp
.user:F8231093    mov     eax, [ebp+arg_0]
.user:F8231096    lea     edx, [ebp+arg_4]
.user:F823109C    int     2Eh             
.user:F823109C               
.user:F823109E    pop     ebp
.user:F823109F    retn    18h
.user:F823109F KavInt2E        endp
.user:F823109F

KAV's export lookup code does not correctly validate offsets garnered from the PE header before using them:

.text:F8220CA0 LookupExportedFunction proc near        ; CODE XREF: sub_F8217A60+C9p
.text:F8220CA0               ; sub_F82181D0+Dp ...
.text:F8220CA0
.text:F8220CA0 var_20          = dword ptr -20h
.text:F8220CA0 var_1C          = dword ptr -1Ch
.text:F8220CA0 var_18          = dword ptr -18h
.text:F8220CA0 var_14          = dword ptr -14h
.text:F8220CA0 var_10          = dword ptr -10h
.text:F8220CA0 var_C           = dword ptr -0Ch
.text:F8220CA0 var_8           = dword ptr -8
.text:F8220CA0 var_4           = dword ptr -4
.text:F8220CA0 arg_0           = dword ptr  4
.text:F8220CA0 arg_4           = dword ptr  8
.text:F8220CA0 arg_8           = dword ptr  0Ch
.text:F8220CA0
.text:F8220CA0    mov     edx, [esp+arg_0]
.text:F8220CA4    sub     esp, 20h
.text:F8220CA7    cmp     word ptr [edx], 'ZM'
.text:F8220CAC    push    ebx
.text:F8220CAD    push    ebp
.text:F8220CAE    push    esi
.text:F8220CAF    push    edi
.text:F8220CB0    jnz     loc_F8220DE1
.text:F8220CB6    mov     eax, [edx+3Ch]
.text:F8220CB9    add     eax, edx
.text:F8220CBB    cmp     dword ptr [eax], 'EP'
.text:F8220CC1    jnz     loc_F8220DE1
.text:F8220CC7    mov     eax, [eax+78h]
.text:F8220CCA    mov     edi, [esp+30h+arg_4]
.text:F8220CCE    add     eax, edx
.text:F8220CD0    mov     [esp+30h+var_14], eax
.text:F8220CD4    mov     esi, [eax+1Ch]
.text:F8220CD7    mov     ebx, [eax+24h]
.text:F8220CDA    mov     ecx, [eax+20h]
.text:F8220CDD    add     esi, edx
.text:F8220CDF    add     ebx, edx
.text:F8220CE1    add     ecx, edx
.text:F8220CE3    cmp     edi, 1000h
.text:F8220CE9    mov     [esp+30h+var_4], esi
.text:F8220CED    mov     [esp+30h+var_C], ebx
.text:F8220CF1    mov     [esp+30h+var_18], ecx
.text:F8220CF5    jnb     short loc_F8220D27
.text:F8220CF7    mov     ecx, [eax+10h]
.text:F8220CFA    mov     eax, edi
.text:F8220CFC    sub     eax, ecx
.text:F8220CFE    mov     eax, [esi+eax*4]
.text:F8220D01    add     eax, edx
.text:F8220D03    mov     edx, [esp+30h+arg_8]
.text:F8220D07    test    edx, edx
.text:F8220D09    jz      loc_F8220DE3
.text:F8220D0F    mov     ebx, ecx
.text:F8220D11    shl     ebx, 1Eh
.text:F8220D14    sub     ebx, ecx
.text:F8220D16    add     ebx, edi
.text:F8220D18    pop     edi
.text:F8220D19    lea     ecx, [esi+ebx*4]
.text:F8220D1C    pop     esi
.text:F8220D1D    pop     ebp
.text:F8220D1E    mov     [edx], ecx
.text:F8220D20    pop     ebx
.text:F8220D21    add     esp, 20h
.text:F8220D24    retn    0Ch
.text:F8220D27 ; ---------------------------------------------------------------------------
.text:F8220D27
.text:F8220D27 loc_F8220D27:              ; CODE XREF: LookupExportedFunction+55j
.text:F8220D27    mov     edi, [eax+14h]
.text:F8220D2A    mov     [esp+30h+arg_0], 0
.text:F8220D32    test    edi, edi
.text:F8220D34    mov     [esp+30h+var_8], edi
.text:F8220D38    jbe     loc_F8220DE1
.text:F8220D3E    mov     [esp+30h+var_1C], esi
.text:F8220D42
.text:F8220D42 loc_F8220D42:              ; CODE XREF: LookupExportedFunction+13Bj
.text:F8220D42    cmp     dword ptr [esi], 0
.text:F8220D45    jz      short loc_F8220DC5
.text:F8220D47    mov     ecx, [eax+18h]
.text:F8220D4A    xor     ebp, ebp
.text:F8220D4C    test    ecx, ecx
.text:F8220D4E    mov     [esp+30h+var_10], ecx
.text:F8220D52    jbe     short loc_F8220DC5
.text:F8220D54    mov     edi, [esp+30h+var_18]
.text:F8220D58    mov     [esp+30h+var_20], ebx
.text:F8220D5C
.text:F8220D5C loc_F8220D5C:              ; CODE XREF: LookupExportedFunction+11Bj
.text:F8220D5C    mov     ebx, [esp+30h+var_20]
.text:F8220D60    xor     esi, esi
.text:F8220D62    mov     si, [ebx]
.text:F8220D65    mov     ebx, [esp+30h+arg_0]
.text:F8220D69    cmp     esi, ebx
.text:F8220D6B    jnz     short loc_F8220DAA
.text:F8220D6D    mov     eax, [edi]
.text:F8220D6F    mov     esi, [esp+30h+arg_4]
.text:F8220D73    add     eax, edx
.text:F8220D75
.text:F8220D75 loc_F8220D75:              ; CODE XREF: LookupExportedFunction+F3j
.text:F8220D75    mov     bl, [eax]
.text:F8220D77    mov     cl, bl
.text:F8220D79    cmp     bl, [esi]
.text:F8220D7B    jnz     short loc_F8220D99
.text:F8220D7D    test    cl, cl
.text:F8220D7F    jz      short loc_F8220D95
.text:F8220D81    mov     bl, [eax+1]
.text:F8220D84    mov     cl, bl
.text:F8220D86    cmp     bl, [esi+1]
.text:F8220D89    jnz     short loc_F8220D99
.text:F8220D8B    add     eax, 2
.text:F8220D8E    add     esi, 2
.text:F8220D91    test    cl, cl
.text:F8220D93    jnz     short loc_F8220D75
.text:F8220D95
.text:F8220D95 loc_F8220D95:              ; CODE XREF: LookupExportedFunction+DFj
.text:F8220D95    xor     eax, eax
.text:F8220D97    jmp     short loc_F8220D9E
.text:F8220D99 ; ---------------------------------------------------------------------------
.text:F8220D99
.text:F8220D99 loc_F8220D99:              ; CODE XREF: LookupExportedFunction+DBj
.text:F8220D99               ; LookupExportedFunction+E9j
.text:F8220D99    sbb     eax, eax
.text:F8220D9B    sbb     eax, 0FFFFFFFFh
.text:F8220D9E
.text:F8220D9E loc_F8220D9E:              ; CODE XREF: LookupExportedFunction+F7j
.text:F8220D9E    test    eax, eax
.text:F8220DA0    jz      short loc_F8220DED
.text:F8220DA2    mov     eax, [esp+30h+var_14]
.text:F8220DA6    mov     ecx, [esp+30h+var_10]
.text:F8220DAA
.text:F8220DAA loc_F8220DAA:              ; CODE XREF: LookupExportedFunction+CBj
.text:F8220DAA    mov     esi, [esp+30h+var_20]
.text:F8220DAE    inc     ebp
.text:F8220DAF    add     esi, 2
.text:F8220DB2    add     edi, 4
.text:F8220DB5    cmp     ebp, ecx
.text:F8220DB7    mov     [esp+30h+var_20], esi
.text:F8220DBB    jb      short loc_F8220D5C
.text:F8220DBD    mov     ebx, [esp+30h+var_C]
.text:F8220DC1    mov     edi, [esp+30h+var_8]
.text:F8220DC5
.text:F8220DC5 loc_F8220DC5:              ; CODE XREF: LookupExportedFunction+A5j
.text:F8220DC5               ; LookupExportedFunction+B2j
.text:F8220DC5    mov     ecx, [esp+30h+arg_0]
.text:F8220DC9    mov     esi, [esp+30h+var_1C]
.text:F8220DCD    inc     ecx
.text:F8220DCE    add     esi, 4
.text:F8220DD1    cmp     ecx, edi
.text:F8220DD3    mov     [esp+30h+arg_0], ecx
.text:F8220DD7    mov     [esp+30h+var_1C], esi
.text:F8220DDB    jb      loc_F8220D42
.text:F8220DE1
.text:F8220DE1 loc_F8220DE1:              ; CODE XREF: LookupExportedFunction+10j
.text:F8220DE1               ; LookupExportedFunction+21j ...
.text:F8220DE1    xor     eax, eax
.text:F8220DE3
.text:F8220DE3 loc_F8220DE3:              ; CODE XREF: LookupExportedFunction+69j
.text:F8220DE3               ; LookupExportedFunction+162j
.text:F8220DE3    pop     edi
.text:F8220DE4    pop     esi
.text:F8220DE5    pop     ebp
.text:F8220DE6    pop     ebx
.text:F8220DE7    add     esp, 20h
.text:F8220DEA    retn    0Ch
.text:F8220DED ; ---------------------------------------------------------------------------
.text:F8220DED
.text:F8220DED loc_F8220DED:              ; CODE XREF: LookupExportedFunction+100j
.text:F8220DED    mov     eax, [esp+30h+var_4]
.text:F8220DF1    mov     ecx, [esp+30h+arg_0]
.text:F8220DF5    lea     ecx, [eax+ecx*4]
.text:F8220DF8    mov     eax, [ecx]
.text:F8220DFA    add     eax, edx
.text:F8220DFC    mov     edx, [esp+30h+arg_8]
.text:F8220E00    test    edx, edx
.text:F8220E02    jz      short loc_F8220DE3
.text:F8220E04    pop     edi
.text:F8220E05    pop     esi
.text:F8220E06    pop     ebp
.text:F8220E07    mov     [edx], ecx
.text:F8220E09    pop     ebx
.text:F8220E0A    add     esp, 20h
.text:F8220E0D    retn    0Ch
.text:F8220E0D LookupExportedFunction endp

User mode calling KAV kernel code directly without a ring 0 transition:

kd> bp f824d820
kd> g
Breakpoint 0 hit
klif!sub_F8231820:
001b:f824d820 83ec08      sub     esp,0x8
kd> kv
ChildEBP RetAddr  Args to Child              
WARNING: Stack unwind information not available. Following frames may be wrong.
0006f4ec 7432f69c 74320000 00000001 00000000 klif!sub_F8231820
0006f50c 7c9011a7 74320000 00000001 00000000 0x7432f69c
0006f52c 7c91cbab 7432f659 74320000 00000001 ntdll!LdrpCallInitRoutine+0x14
0006f634 7c916178 00000000 c0150008 00000000 ntdll!LdrpRunInitializeRoutines+0x344 (FPO: [Non-Fpo])
0006f8e0 7c9162da 00000000 0007ced0 0006fbd4 ntdll!LdrpLoadDll+0x3e5 (FPO: [Non-Fpo])
0006fb88 7c801bb9 0007ced0 0006fbd4 0006fbb4 ntdll!LdrLoadDll+0x230 (FPO: [Non-Fpo])
0006fc20 f824d749 0106c0f0 0000000e 0107348c 0x7c801bb9
0006fd14 7c918dfa 7c90d625 7c90eacf 00000000 klif!loc_F823173D+0xc
0006fe00 7c910551 000712e8 00000044 0006ff0c ntdll!_LdrpInitialize+0x246 (FPO: [Non-Fpo])
0006fecc 00000000 00072368 00000000 00078c48 ntdll!RtlFreeHeap+0x1e9 (FPO: [Non-Fpo])
kd> t
klif!sub_F8231820+0x3:
001b:f824d823 53          push    ebx
kd> r
eax=0006f3cc ebx=00000000 ecx=00005734 edx=0006f3ea esi=7c882fd3 edi=7432f608
eip=f824d823 esp=0006ef00 ebp=0006f4ec iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
klif!sub_F8231820+0x3:
001b:f824d823 53          push    ebx
kd> dg 1b
                                  P Si Gr Pr Lo
Sel    Base     Limit     Type    l ze an es ng Flags
---- -------- -------- ---------- - -- -- -- -- --------
001B 00000000 ffffffff Code RE    3 Bg Pg P  Nl 00000cfa
kd> !pte eip
               VA f824d823
PDE at   C0300F80        PTE at C03E0934
contains 01010067      contains 06B78065
pfn 1010 ---DA--UWEV    pfn 6b78 ---DA--UREV

KAV crashing the system when stepping through its kernel mode code when called from user mode (apparently not that reliable after all!):

Breakpoint 0 hit
klif!sub_F8231820:
001b:f824d820 83ec08      sub     esp,0x8
kd> u eip
klif!sub_F8231820:
f824d820 ebfe             jmp     klif!sub_F8231820 (f824d820)
f824d822 085355           or      [ebx+0x55],dl
f824d825 56               push    esi
f824d826 57               push    edi
f824d827 33ed             xor     ebp,ebp
f824d829 6820d824f8       push    0xf824d820
f824d82e 896c2418         mov     [esp+0x18],ebp
f824d832 896c2414         mov     [esp+0x14],ebp
kd> g
Breakpoint 0 hit
klif!sub_F8231820:
001b:f824d820 ebfe        jmp     klif!sub_F8231820 (f824d820)
kd> g
Breakpoint 0 hit
klif!sub_F8231820:
001b:f824d820 ebfe        jmp     klif!sub_F8231820 (f824d820)
kd> bd 0
kd> g
Break instruction exception - code 80000003 (first chance)
*******************************************************************************
*                                                                             *
*   You are seeing this message because you pressed either                    *
*       CTRL+C (if you run kd.exe) or,                                        *
*       CTRL+BREAK (if you run WinDBG),                                       *
*   on your debugger machine's keyboard.                                      *
*                                                                             *
*                   THIS IS NOT A BUG OR A SYSTEM CRASH                       *
*                                                                             *
* If you did not intend to break into the debugger, press the "g" key, then   *
* press the "Enter" key now.  This message might immediately reappear.  If it *
* does, press "g" and "Enter" again.                                          *
*                                                                             *
*******************************************************************************
nt!RtlpBreakWithStatusInstruction:
804e3592 cc               int     3
kd> gu

*** Fatal System Error: 0x000000d1
                       (0x00003592,0x0000001C,0x00000000,0x00003592)

Break instruction exception - code 80000003 (first chance)
*******************************************************************************
*                                                                             *
*   You are seeing this message because you pressed either                    *
*       CTRL+C (if you run kd.exe) or,                                        *
*       CTRL+BREAK (if you run WinDBG),                                       *
*   on your debugger machine's keyboard.                                      *
*                                                                             *
*                   THIS IS NOT A BUG OR A SYSTEM CRASH                       *
*                                                                             *
* If you did not intend to break into the debugger, press the "g" key, then   *
* press the "Enter" key now.  This message might immediately reappear.  If it *
* does, press "g" and "Enter" again.                                          *
*                                                                             *
*******************************************************************************
nt!RtlpBreakWithStatusInstruction:
804e3592 cc               int     3
kd> g
Break instruction exception - code 80000003 (first chance)

A fatal system error has occurred.
Debugger entered on first try; Bugcheck callbacks have not been invoked.

A fatal system error has occurred.

Connected to Windows XP 2600 x86 compatible target, ptr64 FALSE
Loading Kernel Symbols
..........................
Loading User Symbols
................................
Loading unloaded module list
............
*******************************************************************************
*                                                                             *
*                        Bugcheck Analysis                                    *
*                                                                             *
*******************************************************************************

Use !analyze -v to get detailed debugging information.

BugCheck D1, {3592, 1c, 0, 3592}

*** ERROR: Module load completed but symbols could not be loaded for klif.sys
Probably caused by : hardware

Followup: MachineOwner
---------
 *** Possible invalid call from 804e331f ( nt!KeUpdateSystemTime+0x160 )
 *** Expected target 804e358e ( nt!DbgBreakPointWithStatus+0x0 )

nt!RtlpBreakWithStatusInstruction:
804e3592 cc               int     3
kd> !analyze -v
*******************************************************************************
*                                                                             *
*                        Bugcheck Analysis                                    *
*                                                                             *
*******************************************************************************

DRIVER_IRQL_NOT_LESS_OR_EQUAL (d1)
An attempt was made to access a pageable (or completely invalid) address at an
interrupt request level (IRQL) that is too high.  This is usually
caused by drivers using improper addresses.
If kernel debugger is available get stack backtrace.
Arguments:
Arg1: 00003592, memory referenced
Arg2: 0000001c, IRQL
Arg3: 00000000, value 0 = read operation, 1 = write operation
Arg4: 00003592, address which referenced memory

Debugging Details:
------------------


READ_ADDRESS:  00003592 

CURRENT_IRQL:  1c

FAULTING_IP: 
+3592
00003592 ??               ???

PROCESS_NAME:  winlogon.exe

DEFAULT_BUCKET_ID:  INTEL_CPU_MICROCODE_ZERO

BUGCHECK_STR:  0xD1

LAST_CONTROL_TRANSFER:  from 804e3324 to 00003592

FAILED_INSTRUCTION_ADDRESS: 
+3592
00003592 ??               ???

POSSIBLE_INVALID_CONTROL_TRANSFER:  from 804e331f to 804e358e

TRAP_FRAME:  f7872ce0 -- (.trap fffffffff7872ce0)
ErrCode = 00000000
eax=00000001 ebx=000275fc ecx=8055122c edx=000003f8 esi=00000005 edi=ddfff298
eip=00003592 esp=f7872d54 ebp=f7872d64 iopl=0         nv up ei pl nz na pe nc
cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00010202
00003592 ??               ???
Resetting default scope

STACK_TEXT:  
WARNING: Frame IP not in any known module. Following frames may be wrong.
f7872d50 804e3324 00000001 f7872d00 000000d1 0x3592
f7872d50 f824d820 00000001 f7872d00 000000d1 nt!KeUpdateSystemTime+0x165
0006f4ec 7432f69c 74320000 00000001 00000000 klif+0x22820
0006f50c 7c9011a7 74320000 00000001 00000000 ODBC32!_DllMainCRTStartup+0x52
0006f52c 7c91cbab 7432f659 74320000 00000001 ntdll!LdrpCallInitRoutine+0x14
0006f634 7c916178 00000000 c0150008 00000000 ntdll!LdrpRunInitializeRoutines+0x344
0006f8e0 7c9162da 00000000 0007ced0 0006fbd4 ntdll!LdrpLoadDll+0x3e5
0006fb88 7c801bb9 0007ced0 0006fbd4 0006fbb4 ntdll!LdrLoadDll+0x230
0006fbf0 7c801d6e 7ffddc00 00000000 00000000 kernel32!LoadLibraryExW+0x18e
0006fc04 7c801da4 0106c0f0 00000000 00000000 kernel32!LoadLibraryExA+0x1f
0006fc20 f824d749 0106c0f0 0000000e 0107348c kernel32!LoadLibraryA+0x94
00000000 00000000 00000000 00000000 00000000 klif+0x22749


STACK_COMMAND:  .trap 0xfffffffff7872ce0 ; kb

FOLLOWUP_NAME:  MachineOwner

MODULE_NAME:  hardware

IMAGE_NAME:  hardware

DEBUG_FLR_IMAGE_TIMESTAMP:  0

BUCKET_ID:  CPU_CALL_ERROR

Followup: MachineOwner
---------
 *** Possible invalid call from 804e331f ( nt!KeUpdateSystemTime+0x160 )
 *** Expected target 804e358e ( nt!DbgBreakPointWithStatus+0x0 )

kd> u 804e331f 
nt!KeUpdateSystemTime+0x160:
804e331f e86a020000       call    nt!DbgBreakPointWithStatus (804e358e)
804e3324 ebb4             jmp     nt!KeUpdateSystemTime+0x11b (804e32da)
804e3326 90               nop
804e3327 fb               sti
804e3328 8d09             lea     ecx,[ecx]
nt!KeUpdateRunTime:
804e332a a11cf0dfff       mov     eax,[ffdff01c]
804e332f 53               push    ebx
804e3330 ff80c4050000     inc     dword ptr [eax+0x5c4]