中轉inline hook,不需要恢複首字節的hook

注:我實驗的環境:win7 x64

經驗證XP會有稍微區別,主要是我本次實驗HOOK的API, 從XP到WIN7微軟有了些許改變。

—————————————————————————————————————————————-

最近在看驅動教程時,從前人的HOOK得到啓發,才有了本篇文章,在此甚是感激。

文章估計有些長,當是我的學習筆記了,適合有一定HOOK基礎的盆友,

我寫這篇文章的目的就是爲了提高HOOK的效率。

本次我以HOOK GetProcAddress爲例。

例子VS2010工程HookGetProcAddressTest.rar下載地址:

http://download.csdn.net/detail/friendan/8454859

——————————————————————————————————————————–

問題的提出:

以前我們HOOK時,第一步就是定義我們自己假的API了,如:

FARPROC WINAPI MyGetProcAddress(
    __in HMODULE hModule,
    __in LPCSTR lpProcName
    );

在我們假的API中,一般做法如下:

FARPROC WINAPI MyGetProcAddress(__in HMODULE hModule, __in LPCSTR lpProcName)
{
    // 1.記錄參數
    // 2.修改參數
    // 3.修改返回值
    // 4.恢複HOOK,調用原函數,然後再HOOK
    // ...
}

 

問題就出在第4步了,如果是多線程的環境,在恢複HOOK時,我們可能會漏HOOK

—————————————————————————————————————————————————–

問題的解決:

爲了不恢複HOOK,也能調用原API,我加了一個中轉函數,該函數沒有參數,也沒有返回值,

沒有參數和沒有返回值是爲了保持堆棧的平衡,該函數聲明如下:

void GetProcAddressProxy(void);

實現如下:

__declspec(naked) void GetProcAddressProxy(void)
{
    // 先執行原來的代碼,再跳到原地址+5處執行
    __asm
    {
        // 原函數入口代碼
        mov edi,edi
        push ebp
        mov ebp,esp

        // 調用我們的過濾函數
        push [ebp+0xC]  // 參數lpProcName
        push [ebp+0x8]  // 參數hModule
        call MyGetProcAddress
        
        // 跳到原函數首地址+5處執行,因爲其前面的5字節已經被我們修改了
        mov eax, g_iOldGetProcAddress
        add eax, 5
        jmp eax
    }
}

可以看出在中轉函數中調用了我們的過濾函數即假API了,本次實驗,我的假API主要功能爲替換參數和替換返回值,

主要代碼如下:

FARPROC WINAPI MyGetProcAddress(__in HMODULE hModule, __in LPCSTR lpProcName)
{
    printf("MyGetProcAddress: hModule=0x%X, lpProcName=%s \n", hModule, lpProcName);

    // 如果是Sleep,我們將其替換成不存在的一個函數
    if (strcmp(lpProcName, "Sleep") == 0)
    {
        DisabeMemoryProtect((int)lpProcName, strlen(lpProcName));
        strcpy((char*)lpProcName, "Slee8"); // 函數名不能比原來的長哈
        EnableMemoryProtect((int)lpProcName, strlen(lpProcName));
    }

    // 如果是要過濾的函數
    if (strcmp(lpProcName, "LoadLibraryW") == 0)
    {
        // 獲取KernelBa.GetProcAddress入口地址
        // 7564122F  - FF25 280B6475   jmp dword ptr ds:[<&API-MS-Win-Core-LibraryLoader-L1-1-0.GetProcAd>; KernelBa.GetProcAddress
        // win7 64
        if (*(byte*)(g_iOldGetProcAddress+6) == 0xEB)
        {
            g_iGetProcAddressHookAddr = g_iOldGetProcAddress + 15;
            g_iGetProcAddressHookAddr = *(int*)g_iGetProcAddressHookAddr;
            g_iGetProcAddressHookAddr = *(int*)g_iGetProcAddressHookAddr;
        }
        // xp
        if (*(byte*)(g_iOldGetProcAddress+6) == 0x51)
        {
             g_iGetProcAddressHookAddr = g_iOldGetProcAddress;
        }

        // 從函數入口尋找特征碼,找到要替換的起始地址
       /*76121E83   8B45 0C         mov eax,dword ptr ss:[ebp+0xC]	;獲取到的函數地址 
        76121E86    5F              pop edi
        76121E87    5B              pop ebx
        76121E88    C9              leave
        76121E89 >  C2 0800         retn 0x8 ; 返回用戶層
        76121E8C    CC              int3
        76121E8D    CC              int3
        76121E8E    CC              int3
        76121E8F    CC              int3
        76121E90    CC              int3*/
        byte bSpecialCode[9] = {0x8B, 0x45, 0x0C, 0x5F, 0x5B, 0xC9, 0xC2, 0x08, 0x00};
        g_iGetProcAddressHookAddr = FindSpecialCode(g_iGetProcAddressHookAddr, bSpecialCode, sizeof(bSpecialCode));
        if (g_iGetProcAddressHookAddr <= 0)
        {
            return 0;
        }
        //ShowMemoryData(g_iGetProcAddressHookAddr, 14);

        // 舊函數的尾部代碼
        byte bOldCode[14] = {0x8B, 0x45, 0x0C, 0x5F, 0x5B, 0xC9, 0xC2, 0x08, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC};

        // 替換原來的0x8B, 0x45, 0x0C,即替換mov eax,dword ptr ss:[ebp+0xC]
        // 76071242    B8 78563412     mov eax,0x12345678
        byte bNewCode[11] = {0xB8, 0x90, 0x90, 0x90, 0x90, 0x5F, 0x5B, 0xC9, 0xC2, 0x08, 0x00}; 

        DisabeMemoryProtect(g_iGetProcAddressHookAddr, sizeof(bNewCode));
        memcpy((void*)g_iGetProcAddressHookAddr, bNewCode, sizeof(bNewCode));
        *(int*)(g_iGetProcAddressHookAddr + 1) = (int)LoadLibraryI;     // 將4個0x90替換成我們的函數地址
        g_bIsModifyGetProcAddressHookAddr = true;
        EnableMemoryProtect(g_iGetProcAddressHookAddr, sizeof(bNewCode));
       // ShowMemoryData(g_iGetProcAddressHookAddr, 14);
    }
    else
    {
        // 恢複被我們修改過的指令
        // 舊函數的尾部代碼
        byte bOldCode[14] = {0x8B, 0x45, 0x0C, 0x5F, 0x5B, 0xC9, 0xC2, 0x08, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC};
        if (g_bIsModifyGetProcAddressHookAddr)
        {
            DisabeMemoryProtect(g_iGetProcAddressHookAddr, sizeof(bOldCode));
            memcpy((void*)(g_iGetProcAddressHookAddr), bOldCode, sizeof(bOldCode));
            g_bIsModifyGetProcAddressHookAddr = false;
            EnableMemoryProtect(g_iGetProcAddressHookAddr, sizeof(bOldCode));
        }
    }
    
    return 0;
}

 

———————————————————————————————————————————————————————

在HOOK時,我們讓原API跳到我們的中轉函數即可,我HOOK的實現代碼如下:

void HookGetProcAddress(bool bIsHook)
{
    // 爲方便OD調試,加入的特征碼
    __asm
    {
        mov eax, eax
        mov ebx, ebx
        mov ecx, ecx
    }

    // HOOK GetProcAddress
    if (bIsHook)
    {
        // 取GetProcAddress地址
        g_iOldGetProcAddress = (int)GetProcAddress(LoadLibrary(_T("Kernel32.dll")), "GetProcAddress");
        if (g_iOldGetProcAddress <= 0)
        {
            return;
        }

        // 修改函數前5個字節,使其跳到我們的函數GetProcAddressProxy
        DisabeMemoryProtect(g_iOldGetProcAddress, 5);
        int iJmpAddr = (int)GetProcAddressProxy - g_iOldGetProcAddress - 5;
        *(byte *)g_iOldGetProcAddress = 0xE9; // jmp
        *(int *)(g_iOldGetProcAddress + 1) = iJmpAddr; 
        EnableMemoryProtect(g_iOldGetProcAddress, 5);
    }

    // UNHOOK GetProcAddress
    if (!bIsHook && g_iOldGetProcAddress > 0)
    {
        // 恢複函數前5個字節
        // 8B FF 55 8B EC
        byte bBeginCode[5] = {0x8B,0xFF, 0x55, 0x8B, 0xEC};
        DisabeMemoryProtect(g_iOldGetProcAddress, sizeof(bBeginCode));
        memcpy((void*)g_iOldGetProcAddress, bBeginCode, sizeof(bBeginCode));
        EnableMemoryProtect(g_iOldGetProcAddress, sizeof(bBeginCode));

        // 恢複被我們修改過的指令
        // 舊函數的尾部代碼
        byte bOldCode[14] = {0x8B, 0x45, 0x0C, 0x5F, 0x5B, 0xC9, 0xC2, 0x08, 0x00, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC};
        if (g_bIsModifyGetProcAddressHookAddr)
        {
            DisabeMemoryProtect(g_iGetProcAddressHookAddr, sizeof(bOldCode));
            memcpy((void*)(g_iGetProcAddressHookAddr), bOldCode, sizeof(bOldCode));
            g_bIsModifyGetProcAddressHookAddr = false;
            EnableMemoryProtect(g_iGetProcAddressHookAddr, sizeof(bOldCode));
        }
    }
}

—————————————————————————————————————————————————————————-

效果截圖如下:

中轉inline hook,不需要恢複首字節的hook

————————————————————————————————————————————————-

帖下我在WIN7 X64使用OD得出GetProcAddress的反彙編代碼:

// bp Kernel32.GetProcAddress
75641222 >  8BFF            mov edi,edi     ; GetProcAddresss入口
75641224    55              push ebp
75641225    8BEC            mov ebp,esp
75641227    5D              pop ebp
75641228    EB 05           jmp X<jmp.&API-MS-Win-Core-LibraryLoader-L1-1-0.GetProcAddress>
7564122A    90              nop
7564122B    90              nop
7564122C    90              nop
7564122D    90              nop
7564122E    90              nop
7564122F  – FF25 280B6475   jmp dword ptr ds:[<&API-MS-Win-Core-LibraryLoader-L1-1-0.GetProcAd>; KernelBa.GetProcAddress
75641235    90              nop
75641236    90              nop
76121E15 >  8BFF            mov edi,edi ; KernelBa.GetProcAddress入口地址
76121E17    55              push ebp
76121E18    8BEC            mov ebp,esp
76121E1A    51              push ecx
76121E1B    51              push ecx
76121E1C    53              push ebx
76121E1D    57              push edi
76121E1E    8B7D 0C         mov edi,dword ptr ss:[ebp+0xC] 76121E21    BB FFFF0000     mov ebx,0xFFFF
76121E26    3BFB            cmp edi,ebx
76121E28    76 17           jbe XKernelBa.76121E41
76121E2A    57              push edi
76121E2B    8D45 F8         lea eax,dword ptr ss:[ebp-0x8] 76121E2E    50              push eax
76121E2F    FF15 AC121176   call dword ptr ds:[<&ntdll.RtlInitString>]                         ; ntdll_1a.RtlInitString
76121E35    8D45 0C         lea eax,dword ptr ss:[ebp+0xC] 76121E38    50              push eax
76121E39    6A 00           push 0x0
76121E3B    8D45 F8         lea eax,dword ptr ss:[ebp-0x8] 76121E3E    50              push eax
76121E3F    EB 07           jmp XKernelBa.76121E48
76121E41    8D45 0C         lea eax,dword ptr ss:[ebp+0xC] 76121E44    50              push eax
76121E45    57              push edi
76121E46    6A 00           push 0x0
76121E48    6A 00           push 0x0
76121E4A    FF75 08         push dword ptr ss:[ebp+0x8] 76121E4D    E8 C05E0200     call KernelBa.76147D12
76121E52    50              push eax
76121E53    FF15 A8121176   call dword ptr ds:[<&ntdll.LdrGetProcedureAddress>]                ; ntdll_1a.LdrGetProcedureAddress
76121E59    85C0            test eax,eax
76121E5B    7D 0A           jge XKernelBa.76121E67
76121E5D    50              push eax
76121E5E    E8 1F590200     call KernelBa.76147782
76121E63    33C0            xor eax,eax
76121E65    EB 1F           jmp XKernelBa.76121E86
76121E67    6A 00           push 0x0
76121E69    FF75 08         push dword ptr ss:[ebp+0x8] 76121E6C    E8 A15E0200     call KernelBa.76147D12
76121E71    3945 0C         cmp dword ptr ss:[ebp+0xC],eax
76121E74    75 0D           jnz XKernelBa.76121E83
76121E76    3BDF            cmp ebx,edi
76121E78    1BC0            sbb eax,eax
76121E7A    F7D8            neg eax
76121E7C    05 380100C0     add eax,0xC0000138
76121E81  ^ EB DA           jmp XKernelBa.76121E5D
76121E83    8B45 0C         mov eax,dword ptr ss:[ebp+0xC];獲取到的函數地址
76121E86    5F              pop edi
76121E87    5B              pop ebx
76121E88    C9              leave
76121E89 >  C2 0800         retn 0x8 ; 返回用戶層
76121E8C    CC              int3
76121E8D    CC              int3
76121E8E    CC              int3
76121E8F    CC              int3
76121E90    CC              int3
———————————————————————————————————————————————

帖下我在XP使用OD得出GetProcAddress的反彙編代碼:
7C80AE40 >  8BFF            MOV     EDI, EDI                         ; kernel32.7C800000
7C80AE42    55              PUSH    EBP
7C80AE43    8BEC            MOV     EBP, ESP
7C80AE45    51              PUSH    ECX
7C80AE46    51              PUSH    ECX
7C80AE47    53              PUSH    EBX
7C80AE48    57              PUSH    EDI
7C80AE49    8B7D 0C         MOV     EDI, DWORD PTR SS:[EBP+0xC] 7C80AE4C    BB FFFF0000     MOV     EBX, 0xFFFF
7C80AE51    3BFB            CMP     EDI, EBX
7C80AE53  ^ 0F86 D1F2FFFF   JBE     kernel32.7C80A12A
7C80AE59    57              PUSH    EDI
7C80AE5A    8D45 F8         LEA     EAX, DWORD PTR SS:[EBP-0x8] 7C80AE5D    50              PUSH    EAX
7C80AE5E    FF15 9412807C   CALL    NEAR DWORD PTR DS:[<&ntdll.RtlIn>; ntdll.RtlInitString
7C80AE64    8D45 0C         LEA     EAX, DWORD PTR SS:[EBP+0xC] 7C80AE67    50              PUSH    EAX
7C80AE68    6A 00           PUSH    0x0
7C80AE6A    8D45 F8         LEA     EAX, DWORD PTR SS:[EBP-0x8] 7C80AE6D    50              PUSH    EAX
7C80AE6E    6A 00           PUSH    0x0
7C80AE70    FF75 08         PUSH    DWORD PTR SS:[EBP+0x8] 7C80AE73    E8 1CEBFFFF     CALL    kernel32.7C809994
7C80AE78    50              PUSH    EAX
7C80AE79    E8 B7FFFFFF     CALL    <JMP.&ntdll.LdrGetProcedureAddre>
7C80AE7E    85C0            TEST    EAX, EAX
7C80AE80    0F8C F6840000   JL      kernel32.7C81337C
7C80AE86    6A 00           PUSH    0x0
7C80AE88    FF75 08         PUSH    DWORD PTR SS:[EBP+0x8] 7C80AE8B    E8 04EBFFFF     CALL    kernel32.7C809994
7C80AE90    3945 0C         CMP     DWORD PTR SS:[EBP+0xC], EAX
7C80AE93    0F84 B65F0300   JE      kernel32.7C840E4F
7C80AE99    8B45 0C         MOV     EAX, DWORD PTR SS:[EBP+0xC] 7C80AE9C    5F              POP     EDI
7C80AE9D    5B              POP     EBX
7C80AE9E    C9              LEAVE
7C80AE9F    C2 0800         RETN    0x8; 函數之後的數據和WIN7不一樣
7C80AEA2    837D 10 00      CMP     DWORD PTR SS:[EBP+0x10], 0x0
7C80AEA6  ^ 0F85 07E5FFFF   JNZ     kernel32.7C8093B3
7C80AEAC    33FF            XOR     EDI, EDI
7C80AEAE  ^ E9 07E5FFFF     JMP     kernel32.7C8093BA