逆向获取函数指针后,从DLL调用外部程序函数失败求助
问题分析与解决方案
看起来你遇到的核心问题是函数地址计算错误,加上可能的调用约定不匹配,导致调用函数时程序直接崩溃(所以done!完全没机会打印)。咱们一步步拆解问题,逐个解决:
1. 最致命的错误:偏移计算完全搞反了
你在修改后的代码里直接把IDA/Ghidra显示的虚拟地址(VA)0x00401000当成偏移加到基地址上,这是完全错误的逻辑。
逆向工具里显示的地址是程序原始基址(ImageBase)下的虚拟地址,正确的函数偏移应该是:
函数实际偏移 = IDA/Ghidra显示的VA - 目标程序PE头里的原始ImageBase
举个实际例子:如果你的目标程序是32位PE,默认ImageBase是0x00400000,IDA里函数的VA是0x00401000,那实际偏移就是0x00401000 - 0x00400000 = 0x1000。
你之前写的baseAddress + 0x00401000会直接指向完全错误的内存区域,调用必然触发崩溃,程序自然走不到printf("done!")这一步。
2. 调用约定不匹配的隐形坑
你定义函数指针时用了__cdecl,但目标函数的调用约定可能是__stdcall(Windows系统函数常用)或者其他类型。调用约定不匹配会直接破坏栈平衡,触发崩溃。
怎么验证?
- 看IDA/Ghidra的函数标注:IDA会在函数名后缀标注
cdecl/stdcall,Ghidra也会在函数详情里显示调用约定; - 看汇编结尾:
stdcall函数会用ret n(n是参数占用的栈大小)自动清理栈,而cdecl是由调用者负责清理栈。
如果目标函数是__stdcall,你的函数指针要改成:
void(*ofunct)() = (void(__stdcall *)())(baseAddress + 正确偏移);
3. 其他需要排查的细节
- 目标函数是否带参数?:你定义的是无参函数,但如果目标打印函数实际需要参数(比如
printf("test")是带字符串参数的),调用无参指针会导致栈参数混乱,触发崩溃; - 内存权限检查:确保目标地址所在的内存页有
EXECUTE权限,可以用VirtualQuery查看权限,如果没有,需要调用VirtualProtect添加执行权限; - ASLR适配:虽然你用
GetModuleHandle(0)获取了基地址,但如果目标程序开启了ASLR,要确认逆向工具是否加载了正确的动态基址(IDA里按Ctrl+S查看段基址,Ghidra里可以手动调整加载地址)。
修正后的示例代码
假设目标函数的正确偏移是0x1000,调用约定为__cdecl,修正后的coolRoutine代码如下:
void coolRoutine() { printf("starting...\n"); uintptr_t baseAddress = (uintptr_t)GetModuleHandle(NULL); std::cout << "Base Address: " << std::hex << baseAddress << std::endl; // 替换成你实际计算的偏移:IDA显示的VA - 目标程序的ImageBase uintptr_t funcOffset = 0x1000; void(*ofunct)() = (void(__cdecl *)())(baseAddress + funcOffset); printf("got funct!\n"); ofunct(); printf("done!\n"); }
最后验证步骤
- 用PE工具(比如PEview、LordPE)查看目标程序的
ImageBase,计算出正确的函数偏移; - 在逆向工具里确认目标函数的调用约定;
- 用调试器(比如x64dbg)附加到目标进程,在DLL代码里下断点,查看
ofunct的实际地址是否正确,调用时是否触发异常。
内容的提问来源于stack exchange,提问作者imnotdax
相关产品推荐
相关产品推荐

