• 学习记录--HooKSystemCall


    前言:

    这两天看了一个github上的项目,记录一下学习的心得。

    Win32 API大多数都要从Ring3层进入Ring0层,在内核中完成主要操作。这中间肯定要经过KiFastSystemCall这个过程,这个调用是Ring3层的。这些知识在《Windows内核安全与驱动开发》21章中。

    项目:

    HookSystemCall大致原理就是自己构建一个类似SSDT的东西,当有函数通过KiFastSystemCall进入内核时,就进入自己构建的SSDT中的函数。代码如下,dwHookSystemCall中保存的是自己的函数地址,dwOrigSystemCall中保存的是SSDT中原始的函数地址。Zw函数都是通过索引在SSDT中找相应的Nt函数,索引即在函数的第二个字节。

    //HOOK后跳入的函数地址
    DWORD dwHookSystemCall[0x4C8*10] = {0};
    
    //保存原始函数调用地址
    DWORD *dwOrigSystemCall[0x4C8*10] = {0};
    
    
    //调用函数GetDllFuncAddr以便获得服务号
    int HookSystemCall::GetSysCallIndex( PCHAR FuncName )
    {
        DWORD          FuncAddr;    //函数地址
        int            SysCallIndex;//服务号
        FuncAddr = (DWORD)GetProcAddress(GetModuleHandleA("ntdll.dll"),FuncName);
        SysCallIndex = *( (WORD*)(FuncAddr + 1) );
        return SysCallIndex;
    }

    接下来要Hook KiFastSystemCall,这个调用代码如下:

    77866190 >  8BD4             MOV EDX,ESP
    77866192    0F34             SYSENTER
    77866194 >  C3               RETN

    可以看到只有短短5个字节,所以要 Hook就要先进行短跳转,再来一发长跳转,如下:

    //    Hook KiFastSystemCall
        *(PWORD)ULongToPtr(dwKiFastSystemCall) = 0xF9EB; //即 EB F9 --> 向前跳5个字节
        *(PBYTE)ULongToPtr(dwKiFastSystemCall - 0x5) = 0xE9;
        *(PDWORD)ULongToPtr(dwKiFastSystemCall - 0x4) = PtrToUlong(KiFastSystemCall) - (dwKiFastSystemCall - 0x5) - 5;//跳到自己的KiFastSystemCall中

    自己的KiFastSystemCall如下:

    DWORD HookFunctionAdress;
    __declspec(naked) void KiFastSystemCall()
    {
        
        __asm{
            pushad
                pushfd
    
                cmp         dword ptr [eax*4+dwHookSystemCall],0;比较是否已经有Hook过的函数
    
                jz          Label
                popfd
                popad
                add         esp,4
                jmp         dword ptr [dwHookSystemCall+eax*4]
    
    Label:
    
            push eax
            call HookFunctionAdress
            popfd
            popad
            jmp         KiFastSystemCallEx
    
        }
    }

    对于KiFastSystemCall需要补充一点,Ring3层的堆栈空间如何被传入Ring0层代码中,这里用的方法是MOV EDX,ESP。在内核代码中直接MOV ESP,EDX就可以得到Ring3层的堆栈空间,完全不用拷贝就能直接得到所有的参数。而上面代码 add esp,4 是因为当调用Zw函数时进入Zw再进入KiFastSystemCall中已经发生一次call,esp向上抬了4个字节,需要加回来才能调用正确的参数。

    下面就是安装钩子的代码了:

    // 安装钩子
    BOOL HookSystemCall::InstallHook(PCHAR FunName,        //NTAPI
                                     DWORD pHookFunc,    //HOOK的函数
                                     DWORD * pOrigFunc)    //返回的函数
    {
        DWORD dwOldProtect;
        DWORD dwIndex = GetSysCallIndex(FunName);
        DWORD    dwFunadd =PtrToUlong(GetProcAddress(GetModuleHandleA("NTDLL.dll"), FunName));
        
        //保存各种信息
        dwHookSystemCall[dwIndex] = pHookFunc;
        dwOrigSystemCall[dwIndex] = (PDWORD)malloc(0x10);
        
        //将原本的ZW函数保存进去
        memcpy(dwOrigSystemCall[dwIndex],(PVOID)dwFunadd,0x10);
        
        //野猪改造开始
        VirtualProtect(ULongToPtr(dwOrigSystemCall[dwIndex]), 0x10, PAGE_EXECUTE_READWRITE, &dwOldProtect);
    
        *(PBYTE)((DWORD)dwOrigSystemCall[dwIndex]    + 5)    =     0xE8;
        *(PDWORD)((DWORD)dwOrigSystemCall[dwIndex] + 6)        =     (DWORD)KiFastSystemCallEx - (DWORD)dwOrigSystemCall[dwIndex] -0xA;//修改系统的KiFastSystemCall为自己的
        *(PWORD)((DWORD)dwOrigSystemCall[dwIndex]    + 10)    =     0x9090;
    
        * pOrigFunc = (DWORD)dwOrigSystemCall[dwIndex];
        
        return 0;
    }
    
    
    // Ex安装钩子
    BOOL HookSystemCall::InstallHookEx(DWORD dwIndex,        //NTAPI
                                       WORD dwRetIndex,    //返回堆栈平衡
                                       DWORD pHookFunc,        //HOOK的函数
                                       DWORD * pOrigFunc)    //返回的函数
    {
        DWORD dwOldProtect;
    
        //保存各种信息
        dwHookSystemCall[dwIndex] = pHookFunc;
        dwOrigSystemCall[dwIndex] = (PDWORD)malloc(0x10);
    
        //将原本的ZW函数保存进去
        memcpy(dwOrigSystemCall[dwIndex],(PVOID)SystemCall,0x10);
    
        //野猪改造开始
        VirtualProtect(ULongToPtr(dwOrigSystemCall[dwIndex]), 0x10, PAGE_EXECUTE_READWRITE, &dwOldProtect);
    
        *(PDWORD)((DWORD)dwOrigSystemCall[dwIndex] + 2)        =     (DWORD)dwIndex;
        *(PDWORD)((DWORD)dwOrigSystemCall[dwIndex] + 7)        =     (DWORD)KiFastSystemCallEx - (DWORD)dwOrigSystemCall[dwIndex] - 0xB;
        *(PWORD)((DWORD)dwOrigSystemCall[dwIndex]    + 12)    =     dwRetIndex;
    
        * pOrigFunc = (DWORD)dwOrigSystemCall[dwIndex];
    
        return 0;
    }

    EX这个主要是为了HOOK 一些不是NTDLL 导出的函数,比如NtUserFindWindowEx之类的函数,因为Shadow SSDT也是经由KiFastSystemCall进入内核,原理大概一致.
    不过因为没办法直接获取函数INDEX所以必须根据系统版本自己确定,而且也无法获取函数地址,所以使用我们之前构造的函数原形来处理.ret的平衡也只能手动了!

    最后附上源代码的地址:https://github.com/xiaomagexiao/GameDll/blob/master/HookSystemCall.cpp

  • 相关阅读:
    网络设备
    Linux常用操作
    工作常用笔记
    性能测试问题总结
    mysql性能分析
    Java8新特性学习笔记-CompletableFuture
    Java语言定义的线程状态分析
    Gatling-插件开发
    RabbitMQ记录
    Go常用库
  • 原文地址:https://www.cnblogs.com/Anony-WhiteLearner/p/8590637.html
Copyright © 2020-2023  润新知