绕过特定地址获取寄存器值[Windows上的x86汇编]

时间:2014-07-22 19:41:31

标签: c++ x86 hook inline-assembly

我要做的是截取x86 asm中给定地址的寄存器值并将其放入变量中。为此,我在我的程序中注入一个dll,这里是:

#include <Windows.h>
#include <stdio.h>

void test()
{
    int randomNum;
    DWORD address = 0x088AD6D; // return address (used only with JMP)

    __asm
    {
        //rewriting what I erased with my detour
        movsx eax, byte ptr ds:[esi+4]
        push ebp
        push eax

        // moving ecx to my variable
        mov randomNum, ecx
    }

    printf("number: %d\n", randomNum); // This correctly print the value I detoured


    // this part is only used if I use JMP instead of call
    __asm
    {
        JMP address // return to where the program should have been
    }

}


void DetourAddress(void* funcPtr, void* hook)
{

    // write jmp
    BYTE cmd[5] =
    { 
        0xE9, //jmp
        0x00, 0x00, 0x00, 0x00  //address
    };

    /*
    // write call
    BYTE cmd[5] =
    {
        0xE8, // call
        0x00, 0x00, 0x00, 0x00 // our function address
    };
    */

    DWORD dwProtect;

    VirtualProtect(funcPtr, 5, PAGE_EXECUTE_READWRITE, &dwProtect); // make memory writable
    DWORD offset = ((DWORD)hook - (DWORD)funcPtr - 5);  //((to)-(from)-5)
    memcpy(&cmd[1], &offset, 4); // write address into jmp
    WriteProcessMemory(GetCurrentProcess(), (LPVOID)funcPtr, cmd, 5, 0); // write jmp / call
    VirtualProtect(funcPtr, 5, dwProtect, NULL); // reprotect
}

BOOL APIENTRY DllMain( HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved  )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        AllocConsole();
        freopen("CONOUT$", "w", stdout);

        DetourAddress((void*)0x088AD68, (void*)&test);

        break;
    case DLL_PROCESS_DETACH:
        FreeConsole();
        break;
    }

    return TRUE;
}

DetourAddress函数是应该发生所有魔法的地方,我在我想要绕道我的test()函数的地址写一个jmp命令。测试函数复制已擦除的代码,并将ecx值移动到我的变量randomNum中。然后,如果我没有绕道它,我会回到代码本应处的位置。从理论上讲,它工作正常,但问题是在我的test()函数中,寄存器值发生了变化,当我回到原始代码时,不仅我的程序停止按预期工作,而且它也很快就崩溃了因为访问违规......

对于那些视觉上的人来说,这是ollydbg中发生的事情:

第1步(原始代码):

enter image description here

第2步,在我绕开代码之后的样子:

enter image description here

步骤3,ollydbg中的函数test():

enter image description here

步骤4,将jmp恢复为原始代码:

enter image description here

正如您所看到的,当我们回到原始代码时,寄存器值会完全改变,这会导致各种问题......我正在寻找一种方法来防止这种情况发生。

我发现这个问题(C++ mid-function hook: get register values and jump back [x86 assembly on windows])与我的相似(甚至相同......),但给出的答案对我没有帮助。我尝试使用call命令而不是jmp,不仅寄存器值保持不变,而且还会导致另外一个问题:当我从test()函数返回时,它返回非可读内存而不是原始函数...

请帮忙吗?

1 个答案:

答案 0 :(得分:2)

而是将调用挂了几个指令,这将允许您正确地保留寄存器,同时避免任何内联汇编。构建pass-though和capture会看起来像(假设它什么都不返回):

typedef void (__stdcall * hookfn)(const char* str, DWORD dw1, DWORD dw2, DWORD dw3, DWORD dw4);

void __stdcall Intercept(const char* str, DWORD dw1, DWORD dw2, DWORD dw3, DWORD dw4)
{
    randomNum = dw1;
    printf("%d\n",randomNum);

    hookfn fn = (hookfn)((DWORD)BaseOfTargetModule + RVAOfCall);
    fn(str,dw1,dw2,dw3,dw4);
}

您使用相同的修补机制覆盖相对的呼叫地址。使用Base + RVA而不是固定的虚拟地址来计算重定位也是一个好主意。

如果你仍然想要你的钩子,你需要保留所有的寄存器,这可以在输入时使用PUSHAD并在退出时使用POPAD轻松完成(这些保存/恢复所有寄存器都禁止标志)。此外,您应该只在执行跳转之前执行覆盖的指令(但在POPAD之后),否则它们可能会导致您的挂钩可能出现问题。