【发布时间】:2019-01-24 02:40:55
【问题描述】:
我基本上是在我的 C++ 应用程序中使用 LoadLibrary() 调用 C++ DLL。应用程序导致随机 0xc0000005(访问冲突) 错误。我对 DLL 有自己的堆及其问题进行了大量研究。
到目前为止我确定要做的事情:
在 DLL 中:
- 所有分配均以 C++ 标准完成。 (不使用 malloc 或 calloc)
- 所有新的都具有可访问的等效删除。
- 在主机 exe 中释放的 DLL 内没有分配内存,反之亦然。
- 两者之间的数据传输是通过 POD(特别是 char*)完成的。没有 STL。
- 所有导出函数的调用约定为 __stdcall
- 构建 DLL 时指定 extern "C" 和一个 DEF 文件。
在宿主程序中:
- 使用 HeapAlloc() 和 GetProcessHeap() 分配内存
- 指针被传递给 DLL,该 DLL 使用 memcpy()
- DLL 函数 typedef 是正确的。
- DLL 和 exe 的编译器是相同的。(内置于 VS2010)。
崩溃发生在随机位置:
- 在调试时我观察到,当我们在 DLL 中越过“}”函数结束大括号时,就会发生异常。
- 从 DLL 调用成功返回后。崩溃随机发生。
所有事件日志都显示 “故障模块名称” 为 DLL。
考虑到我之前所说的所有要点,如果有人指导我在哪里寻找异常原因,我将不胜感激。
我发送给 DLL 的指针是否也被解析为 memcpy() 中的正确 HEAP?但是主机exe中的数据是正确的。 GetProcessHeaps() 返回 4 个 HEAPS。
编辑 由于政策,无法发布完整代码。 (再次注意,我已经解释了大多数常见错误)。
发生错误的函数(DLL)
extern "C" void __stdcall BuildApplicationsList();
exe中的类型定义
typedef void(__stdcall *buildAppsList)(void);
更新
回应@RalfFriedl。你是对的!。程序在此位置崩溃。
}
5822593F mov byte ptr [esp+7A0h],7
58225947 cmp dword ptr [esp+0A0h],0
5822594F jne BuildApplicationsList+1CE2h (58225992h)
58225951 mov eax,dword ptr [esp+74h]
58225955 test eax,eax
58225957 je BuildApplicationsList+1CB1h (58225961h)
58225959 mov ecx,dword ptr [eax]
5822595B mov edx,dword ptr [ecx+8] // Crash Occurs here.
5822595E push eax
5822595F call edx
58225961 mov eax,dword ptr [esp+70h]
58225965 test eax,eax
58225967 je BuildApplicationsList+1CC1h (58225971h)
58225969 mov ecx,dword ptr [eax]
5822596B mov edx,dword ptr [ecx+8]
5822596E push eax
5822596F call edx
58225971 mov eax,dword ptr [esp+6Ch]
58225975 test eax,eax
58225977 je BuildApplicationsList+1CD1h (58225981h)
58225979 mov ecx,dword ptr [eax]
5822597B mov edx,dword ptr [ecx+8]
5822597E push eax
5822597F call edx
58225981 call dword ptr [__imp__CoUninitialize@0 (5823F2C8h)]
edx 和 ecx 为 0,显然访问 0x00000008 是违规行为。 下一步去哪里?
【问题讨论】:
-
Windows“堆”是同一内存空间的一部分——不需要“解析”指针。这些堆与人们在 C++ 中称为“堆”的东西无关。
-
下一个要看的地方是
BuildApplicationsList。 (顺便说一下,DLL 中的CoUninitialize看起来有点奇怪。) -
mov ecx,dword ptr [eax]:从eax的对象中获取vtable指针;mov edx,dword ptr [ecx+8]:获取虚表中偏移8处的函数指针;push eax:添加this参数;call edx:调用函数。eax不是零,但edx是,所以 vtable 指针必须为空。 -
mov ecx, dword ptr [eax]检索地址eax处的值,而不是eax的值。 (也就是说,它正在取消引用一个指针。) -
刚刚意识到我在解释虚函数调用的评论中写了
edx,而我的意思是ecx。编辑太晚了,但我不妨自己挑剔。