我要做的是截取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步(原始代码):
第2步,在我绕开代码之后的样子:
步骤3,ollydbg中的函数test():
步骤4,将jmp恢复为原始代码:
正如您所看到的,当我们回到原始代码时,寄存器值会完全改变,这会导致各种问题......我正在寻找一种方法来防止这种情况发生。
我发现这个问题(C++ mid-function hook: get register values and jump back [x86 assembly on windows])与我的相似(甚至相同......),但给出的答案对我没有帮助。我尝试使用call命令而不是jmp,不仅寄存器值保持不变,而且还会导致另外一个问题:当我从test()函数返回时,它返回非可读内存而不是原始函数...
请帮忙吗?
答案 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
之后),否则它们可能会导致您的挂钩可能出现问题。