我从C ++ exe导出函数[使用_declspec(dllexport)]。当exe本身调用时,该函数工作正常。我正在使用静态链接从另一个exe [测试项目的exe - 我将调用此exe2]加载此exe(让我们调用此exe1),即我在编译exe2时使用exe1的.lib文件,exe2在启动时将其加载到内存中任何dll。这会导致函数执行失败。
在函数中的switch case语句的反汇编中显示了确切的问题。
exe1调用函数时的汇编代码
switch (dwType)
0040FF84 mov eax,dword ptr [dwType]
0040FF87 mov dword ptr [ebp-4],eax
0040FF8A cmp dword ptr [ebp-4],0Bh
0040FF8E ja $LN2+7 (40FFD2h)
0040FF90 mov ecx,dword ptr [ebp-4]
0040FF93 jmp dword ptr (40FFE0h)[ecx*4]
考虑最后两条指令。 mov将传入的参数移动到ecx中。在40EFF0h,我们有相应案例陈述的各种指令的地址。因此,jmp会将我们带到相关的案例指令
exe2调用函数时的汇编代码
switch (dwType)
0037FF84 mov eax,dword ptr [dwType]
0037FF87 mov dword ptr [ebp-4],eax
0037FF8A cmp dword ptr [ebp-4],0Bh
0037FF8E ja $LN2+7 (37FFD2h)
0037FF90 mov ecx,dword ptr [ebp-4]
0037FF93 jmp dword ptr [ecx*4+40FFE0h]
现货出了什么问题? 指令地址。代码现在已加载到内存中的不同位置。编译exe1时,编译器假定我们将始终启动它,因此它将始终加载到0x0040000 [与所有Windows exes的情况一样]。因此它将40FFE0h等一些值硬编码到指令中。只有在第二种情况下,40FFE0和垃圾内存一样好,因为我们要查找的指令地址表不在那里。
如何在不将exe1转换为dll的情况下解决这个问题?
答案 0 :(得分:1)
就是不要这样做。这不值得打扰。
我尝试过你刚才做的事情。您可以通过更改“Linker-> Advenced->固定基址”下的属性窗口中的选项来解决不可重定位的exe问题,但是您会遇到其他问题。
最终让我意识到浪费时间的事情就是意识到EXE没有DllMain()
功能。这意味着CRT库没有被初始化,并且各种各样的东西都没有按照你期望的方式工作。
答案 1 :(得分:1)
您是否考虑过另一种方法?例如,当你想将第二个.exe用作可执行文件时,将第二个.exe转换为.dll并使用rundll32调用它?
否则: 生成的组件很好。问题是Win32可移植可执行文件有一个基地址(在这种情况下为0x0040000)和一个包含详细地址位置的部分,以便在需要时可以重新定位。
所以有两件事情发生了: - 编译器在构建.exe时不包括IMAGE_BASE_RELOCATION记录。 - 或者,当动态加载.exe时,运行时没有执行基本重定位 - (可能两者)
如果.exe确实包含重定位记录,您可以自己读取它们并执行基本重定位。你必须跳过箍,比如确保你有对内存的写入权限(VirtualAlloc等),但它在概念上非常简单。
如果.exe不包含您填充的重定位记录 - 要么找到编译器选项以强制包含它们,要么找到另一种方法来执行您正在执行的操作。
编辑:正如shoosh指出的那样,一旦解决了这个问题,你可能会遇到其他问题。