Windows系统调用之Kifastcallenrty钩子

上一篇分析了浅析了一下Windows系统调用的过程,让我们看看如何在这个流程中做点手脚,达到我们的过滤的目的。

现在主流的杀软在X86下都是首先下钩Kifastcallentry,配合自己的重载内核,接管ServerTable,从而废除ServerTable钩子。

那么首先如何获得Kifastcallentry函数?

 

从看雪论坛的资料以及寒假逆向某安全软件模块发现现在主流的仍然是先SSDT HOOK然后调用Fake函数,然后在Fake函数里通过栈回溯找到Kifastcallentry,然后在恢复SSDT HOOK,最后暴力搜索下钩点,完成HOOK。

 

首先我们看看Kifastcallentry函数

 

nt!KiFastCallEntry:
8053e540 b923000000      mov     ecx,23h
8053e545 6a30            push    30h
8053e547 0fa1            pop     fs
8053e549 8ed9            mov     ds,cx
8053e54b 8ec1            mov     es,cx
8053e54d 8b0d40f0dfff    mov     ecx,dword ptr ds:[0FFDFF040h]
8053e553 8b6104          mov     esp,dword ptr [ecx+4]
8053e556 6a23            push    23h
8053e558 52              push    edx
8053e559 9c              pushfd
8053e55a 6a02            push    2
8053e55c 83c208          add     edx,8
8053e55f 9d              popfd
8053e560 804c240102      or      byte ptr [esp+1],2
8053e565 6a1b            push    1Bh
8053e567 ff350403dfff    push    dword ptr ds:[0FFDF0304h]
8053e56d 6a00            push    0
8053e56f 55              push    ebp
8053e570 53              push    ebx
8053e571 56              push    esi
8053e572 57              push    edi
8053e573 8b1d1cf0dfff    mov     ebx,dword ptr ds:[0FFDFF01Ch]
8053e579 6a3b            push    3Bh
8053e57b 8bb324010000    mov     esi,dword ptr [ebx+124h]
8053e581 ff33            push    dword ptr [ebx]
8053e583 c703ffffffff    mov     dword ptr [ebx],0FFFFFFFFh
8053e589 8b6e18          mov     ebp,dword ptr [esi+18h]
8053e58c 6a01            push    1
8053e58e 83ec48          sub     esp,48h
8053e591 81ed9c020000    sub     ebp,29Ch
8053e597 c6864001000001  mov     byte ptr [esi+140h],1
8053e59e 3bec            cmp     ebp,esp
8053e5a0 759a            jne     nt!KiFastCallEntry2+0x47 (8053e53c)
8053e5a2 83652c00        and     dword ptr [ebp+2Ch],0
8053e5a6 f6462cff        test    byte ptr [esi+2Ch],0FFh
8053e5aa 89ae34010000    mov     dword ptr [esi+134h],ebp
8053e5b0 0f854afeffff    jne     nt!Dr_FastCallDrSave (8053e400)
8053e5b6 8b5d60          mov     ebx,dword ptr [ebp+60h]
8053e5b9 8b7d68          mov     edi,dword ptr [ebp+68h]
8053e5bc 89550c          mov     dword ptr [ebp+0Ch],edx
8053e5bf c74508000ddbba  mov     dword ptr [ebp+8],0BADB0D00h
8053e5c6 895d00          mov     dword ptr [ebp],ebx
8053e5c9 897d04          mov     dword ptr [ebp+4],edi
8053e5cc fb              sti
8053e5cd 8bf8            mov     edi,eax
8053e5cf c1ef08          shr     edi,8
8053e5d2 83e730          and     edi,30h
8053e5d5 8bcf            mov     ecx,edi
8053e5d7 03bee0000000    add     edi,dword ptr [esi+0E0h]
8053e5dd 8bd8            mov     ebx,eax
8053e5df 25ff0f0000      and     eax,0FFFh
8053e5e4 3b4708          cmp     eax,dword ptr [edi+8]
8053e5e7 0f8345fdffff    jae     nt!KiBBTUnexpectedRange (8053e332)
8053e5ed 83f910          cmp     ecx,10h
8053e5f0 751a            jne     nt!KiFastCallEntry+0xcc (8053e60c)
8053e5f2 8b0d18f0dfff    mov     ecx,dword ptr ds:[0FFDFF018h]
8053e5f8 33db            xor     ebx,ebx
8053e5fa 0b99700f0000    or      ebx,dword ptr [ecx+0F70h]
8053e600 740a            je      nt!KiFastCallEntry+0xcc (8053e60c)
8053e602 52              push    edx
8053e603 50              push    eax
8053e604 ff15e43f5580    call    dword ptr [nt!KeGdiFlushUserBatch (80553fe4)]
8053e60a 58              pop     eax
8053e60b 5a              pop     edx
8053e60c ff0538f6dfff    inc     dword ptr ds:[0FFDFF638h]
8053e612 8bf2            mov     esi,edx
8053e614 8b5f0c          mov     ebx,dword ptr [edi+0Ch]
8053e617 33c9            xor     ecx,ecx
8053e619 8a0c18          mov     cl,byte ptr [eax+ebx]
8053e61c 8b3f            mov     edi,dword ptr [edi]
8053e61e 8b1c87          mov     ebx,dword ptr [edi+eax*4]
8053e621 2be1            sub     esp,ecx
8053e623 c1e902          shr     ecx,2
8053e626 8bfc            mov     edi,esp
8053e628 3b35d4995580    cmp     esi,dword ptr [nt!MmUserProbeAddress (805599d4)]
8053e62e 0f83a8010000    jae     nt!KiSystemCallExit2+0x9f (8053e7dc)
8053e634 f3a5            rep movs dword ptr es:[edi],dword ptr [esi]
8053e636 ffd3            call    ebx        // ebx 系统服务
8053e638 8be5            mov     esp,ebp
8053e63a 8b0d24f1dfff    mov     ecx,dword ptr ds:[0FFDFF124h]

 

然后我们看看如何通过栈回溯找到Kifastcall函数

 

 1     uIndex = SYSCALL_INDEX(ZwSetEvent);   // 获得SetEvent的系统索引号
 2 
 3     Irql = WPOFF();
 4     HOOK_SYSCALL(uIndex,HookZwSetEvent,OrgZwSetEvent);         //SSDT HOOK
 5     WPON(Irql);
 6 
 7     ZwSetEvent(&hEvent,NULL);            // 调用一次Fake函数
 8     
 9     Irql = WPOFF();
10     UNHOOK_SYSCALL(uIndex,OrgZwSetEvent);    //恢复hook
11     WPON(Irql);
12 

 

接下来我们看看FakeSetEvent中做了些什么

 

 1 NTSTATUS
 2 HookZwSetEvent(
 3               IN PHANDLE EventHandle,
 4               OUT PLONG PreviousState OPTIONAL)
 5 {
 6 
 7 
 8     if (*EventHandle!=(HANDLE)0x12345678||ExGetPreviousMode()!=KernelMode)  
 9     {
10         OrgZwSetEvent(EventHandle,PreviousState);
11     }
12 
13     else
14     {
15 
16         __asm
17         {
18             mov eax,dword ptr[ebp + 4]   //得到EIP  
19             mov KiFastCallEntryRetAddress,eax
20             
21         }
22 
23         DbgPrint("%x\r\n",KiFastCallEntryRetAddress);   //测试
24     }
25 
26 
27     return STATUS_SUCCESS;
28 
29 }

调试可以看到KiFastCallEntryRetAddress值为0x8053e638

其实就是进入函数后被push的eip,可以回头看看kifasycallentry函数

8053e634 f3a5            rep movs dword ptr es:[edi],dword ptr [esi]
8053e636 ffd3            call    ebx        // ebx 系统服务
8053e638 8be5            mov     esp,ebp

 这样我们就进入Kifastcallentry函数了,剩下的就是搜索下钩点进行InlineHook了

 

    CHAR SearchCode[5] = {0x2b,0xe1,0xc1,0xe9,0x02};

    SearchAddress = KiFastCallEntryRetAddress;
            
    while (1)
    {
        if (memcmp((PVOID)SearchAddress,SearchCode,5) == 0)
        {
            PatchAddress = SearchAddress;
            PatchAddress1 = SearchAddress + 5;   //PatchAddress1 == 8053e626
            *(PULONG)&JmpCode[1] = (ULONG)FakeKiFastCallEntry - (PatchAddress + 5);
            memcpy((PVOID)PatchAddress,JmpCode,5);
            break;
        }
            
            SearchAddress --;      //栈回溯    
    }
    WPON(Irql);    

 

让我们看看挂钩后的kifastcallentry函数部分

 

 1 8053e619 8a0c18          mov     cl,byte ptr [eax+ebx]
 2 8053e61c 8b3f            mov     edi,dword ptr [edi]
 3 8053e61e 8b1c87          mov     ebx,dword ptr [edi+eax*4]
 4 8053e621 e9ea194077      jmp     KifastcallentryHook!FakeKiFastCallEntry (f7940010)
 5 8053e626 8bfc            mov     edi,esp
 6 8053e628 3b35d4995580    cmp     esi,dword ptr [nt!MmUserProbeAddress (805599d4)]
 7 8053e62e 0f83a8010000    jae     nt!KiSystemCallExit2+0x9f (8053e7dc)
 8 8053e634 f3a5            rep movs dword ptr es:[edi],dword ptr [esi]
 9 8053e636 ffd3            call    ebx
10 8053e638 8be5            mov     esp,ebp
11 8053e63a 8b0d24f1dfff    mov     ecx,dword ptr ds:[0FFDFF124h]
12 8053e640 8b553c          mov     edx,dword ptr [ebp+3Ch]

 

我们现在已经挂钩Kifastcallentry函数了,并且已经把返回地址保存进了PatchAddress1,我们接下里看看FakeKifastcallentry函数做了什么

 

 1 _declspec(naked) VOID FakeKiFastCallEntry()
 2 {
 3     __asm
 4     {
 5             mov edi,edi    //对齐内存
 6             pushfd
 7             pushad
 8             push edi
 9             push ebx
10             push eax
11             call SysCallfilter
12             mov  dword ptr [esp+10h],eax   // esb+10h == ebx   eax == SysCallfilter的返回值
13             popad
14             popfd
15             sub esp, ecx
16             shr ecx, 2
17             push PatchAddress1;   //  相当于  PatchAddress1 所保存的值当成了EIP  
18             ret                   //   ret == pop eip
19     }
20 }

 

FakeKifastcallentry函数里就是保存下现场,Push edi,push ebx,push eax,call SysCallfilter  最后把返回结果保存进了 esp+10 (其实就是真正的系统函数服务ebx里),其中eax是系统服务索引号,ebx 真正的系统服务服务地址,edi ServiceBase。

其他的注释也已经写的很清楚了。

最后我们看看 SysCallfilter 过滤函数

 1 ULONG    SysCallfilter(ULONG SysCallNum,ULONG FunAddr,ULONG ServiceBase)
 2 {
 3     
 4     //得到Index,判断Index是不是NtCreateFile,如果是,返回一个新的函数地址,而不是SSDT原先的函数地址
 5     ULONG Index = SYSCALL_INDEX(ZwCreateFile);
 6     if (Index == SysCallNum)
 7     {
 8         return (ULONG)HookNtCreateFile;
 9     }
10     
11     return FunAddr;
12 }

 

好了,到此基本分析完了,至于HookNtCreateFile函数,自然是想怎么过滤就怎么过滤了,过滤完后记着再把原函数调用一边,不然容易发生蓝屏。

这只是一个小Demo,对于大型的hook框架可以参考看雪论坛的Fypher大神Hook Kifastcallentry监视系统调用 源码,或者逆向分析下主流杀软的hook框架,定获益匪浅。

你可能感兴趣的:(Windows系统调用之Kifastcallenrty钩子)