You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

逆向获取函数指针后,从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");
}

最后验证步骤

  1. 用PE工具(比如PEview、LordPE)查看目标程序的ImageBase,计算出正确的函数偏移;
  2. 在逆向工具里确认目标函数的调用约定;
  3. 用调试器(比如x64dbg)附加到目标进程,在DLL代码里下断点,查看ofunct的实际地址是否正确,调用时是否触发异常。

内容的提问来源于stack exchange,提问作者imnotdax

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 10:42:41