通过绕行特定地址获取寄存器值[Windows上的x86程序集]
Posted
技术标签:
【中文标题】通过绕行特定地址获取寄存器值[Windows上的x86程序集]【英文标题】:Get register value by detouring specific address [x86 assembly on Windows] 【发布时间】:2014-07-22 19:41:30 【问题描述】:我要做的是在 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步(原代码):
第二步,我绕行代码后的样子:
第三步,ollydbg中的test()函数:
第四步,jmp回到原代码:
如您所见,当我们返回到原始代码时,寄存器的值会完全改变,这会导致各种问题......我正在寻找一种方法来防止这种情况发生。
我发现这个问题 (C++ mid-function hook: get register values and jump back [x86 assembly on windows]) 与我的相似(甚至相同...),但给出的答案对我没有帮助。我尝试使用 call 命令而不是 jmp 不仅寄存器值不断变化,而且还导致另一个问题:当我从 test() 函数返回时,它返回到非可读内存而不是原始函数...
请帮忙?
【问题讨论】:
所以问题似乎源于您在绕道途中调用 printf 的事实。调用函数时,有些寄存器必须被调用函数保留,有些可以被覆盖。这些规则因使用的调用约定而异。也许您可以对值执行其他操作(将其写入缓冲区?)或推送/弹出在 printf 之前/之后更改的寄存器。 【参考方案1】:而是将调用挂起一些指令,这将允许您正确保留寄存器,同时避免任何内联汇编。构建传递和捕获看起来像(假设它什么都不返回):
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
之后),否则它们可能会导致您的钩子可能正在执行的任何问题。
【讨论】:
以上是关于通过绕行特定地址获取寄存器值[Windows上的x86程序集]的主要内容,如果未能解决你的问题,请参考以下文章
如何从 windows phone 中的纬度和经度值获取地址?
Windows 逆向OD 调试器工具 ( 分析 OD 硬件断点处的关键代码 | 添加硬件断点 | 关键代码 | MOV 指令 | EAX 寄存器值分析 | 使用命令查看 esi+0cc 地址 )(代码