功能挂钩实际上如何工作? WinAPI,C ++

时间:2017-04-19 10:15:00

标签: windows debugging hook code-injection

我将我的DLL注入进程,然后挂钩这样的函数:(recv)

BOOL HookFunction(LPCWSTR moduleName, LPCSTR funcName, LPVOID funcProxy, 
unsigned char* lpBackup)
{
     BYTE jmp[6] = { 0xe9,0x00,0x00,0x00,0x00,0xc3 };
     DWORD funcAddr = (DWORD)GetProcAddress(GetModuleHandle(moduleName), funcName);

     DWORD prev;
     VirtualProtect((LPVOID)funcAddr, 6, PAGE_EXECUTE_READWRITE, &prev);

     ReadProcessMemory(GetCurrentProcess(), (LPVOID)funcAddr, lpBackup, 6, NULL);

     DWORD proxy = ((DWORD)funcProxy - funcAddr) - 5;
     memcpy(&jmp[1], &proxy, 4);
     memcpy((LPVOID)funcAddr, jmp, 6);

     VirtualProtect((LPVOID)funcAddr, 6, prev, &prev);
     FlushInstructionCache(GetCurrentProcess(), NULL, NULL);

     return funcAddr;
}

// Hook
HookFunction(L"ws2_32.dll", "recv", (LPVOID*)nRecv, hookR);

然后我附加了一个调试器,结果如下:

挂钩前: enter image description here

挂钩后: enter image description here

我有一些事情我不明白,因为我仍然试图理解和可视化堆栈,堆等在调试器中如何一起工作。

BYTE jmp[6] = { 0xe9,0x00,0x00,0x00,0x00,0xc3 };

我是否在这里更换说明,例如," move,edi,edi" (recv)原始函数与0xe9?接下来的指令是0x00 ......或者究竟是如何工作的?

任何详细的解释将不胜感激。

1 个答案:

答案 0 :(得分:4)

BOOL HookFunction(LPCWSTR moduleName, LPCSTR funcName, LPVOID funcProxy, 
unsigned char* lpBackup)
{
     BYTE jmp[6] = 
     { 
        0xe9,0x00,0x00,0x00,0x00, /*JMP and 4 bytes of offset*/
        0xc3                      /*RET*/
     };

     /*
       JMP (e9) is relative, its 32-bit signed immediate operand encodes the 
       number of bytes to jump forward relative to the NEXT instruction.
     */

     /* Get the target address of the function to hook */
     DWORD funcAddr = (DWORD)GetProcAddress(GetModuleHandle(moduleName), funcName);

     /* Code is not necessarily mapped as writable, we remap it */
     DWORD prev;
     VirtualProtect((LPVOID)funcAddr, 6, PAGE_EXECUTE_READWRITE, &prev);

     /* Read the original 6 bytes we are going to overwrite */
     ReadProcessMemory(GetCurrentProcess(), (LPVOID)funcAddr, lpBackup, 6, NULL);

     /* 
        Compute the offset: target - source 
        target = funcProxy
        source = funcAddr + 5 (length of JMP)

        target - source = funcProxy - funcAddr - 5
     */
     DWORD proxy = ((DWORD)funcProxy - funcAddr) - 5;

     /*
        Create the JMP instruction: set the offset
     */
     memcpy(&jmp[1], &proxy, 4);

     /* Overwrite the first 6 bytes of the target function */
     memcpy((LPVOID)funcAddr, jmp, 6);

     /* Reset the memory protection to its original value*/
     VirtualProtect((LPVOID)funcAddr, 6, prev, &prev);

     /* Since we write to a code section with DS, flush the L1 I cache */
     FlushInstructionCache(GetCurrentProcess(), NULL, NULL);

     return funcAddr;
}

HookFunction在内存中创建一小段x86代码(trampoline

jmp <0>     ;e9 00 00 00 00
ret         ;c3

其中<0>被跳转的编码目标(参见代码中的注释)连续覆盖 - 钩子函数。
蹦床,一旦制作,变成

jmp funcProxy     ;e9 .. .. .. ..
ret               ;c3

然后直接在钩子函数的开头写入此代码,从而覆盖其原始代码。

代码是多语言 - 它适用于x86和x86-64。

将钩住函数的原始代码复制到lpBackup 这是再次调用原始函数所必需的,钩子函数在没有先恢复它的情况下无法调用它。

由于这是昂贵且不可重入的,因此更简洁的方法是修改Import Address Table - 此解决方案的有效性取决于您的要求。