中转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));
        }
    }
}

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------


效果截图如下:


-------------------------------------------------------------------------------------------------------------------------------------------------

帖下我在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








评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

friendan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值