学习记录--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

posted @ 2018-03-17 16:26  WhiteLearner  阅读(340)  评论(0编辑  收藏  举报