Visual Studio 2005 中的函数指针不正确,代码从 1 字节偏移量开始
有问题的代码挂接到explorer.exe,但在进入回调函数时崩溃:
Unhandled exception at 0x60055b50 (redacted.dll) in explorer.exe: 0xC0000005: Access violation writing location 0x548b0cca.
Callstack:
> redacted.dll!myCallWndProcRetCallback(int nCode=0x00000000, unsigned int wParam=0x00000000, long lParam=0x015afa58) Line 799 C++ user32.dll!_DispatchHookW@16() + 0x31 bytes user32.dll!_fnHkINLPCWPRETSTRUCTW@20() + 0x5e bytes user32.dll!___fnDWORD@4() + 0x24 bytes ntdll.dll!_KiUserCallbackDispatcher@12() + 0x13 bytes user32.dll!_NtUserMessageCall@28() + 0xc bytes user32.dll!_SendMessageW@16() + 0x49 bytes explorer.exe!CTaskBand::_FindIndexByHwnd() + 0x21 bytes explorer.exe!CTaskBand::_HandleShellHook() + 0x48 bytes explorer.exe!CTaskBand::v_WndProc() + 0x660 bytes explorer.exe!CImpWndProc::s_WndProc() + 0x3f bytes
Visual Studio 2005 给出以下反汇编:
--- c:\projects\redacted.cpp ------------------------- //------------------------------------------------------------------------------ LRESULT CALLBACK myCallWndProcRetCallback(int nCode, WPARAM wParam, LPARAM lParam) { 60055B50 inc dword ptr [ebx+548B0CC4h] 60055B56 and al,18h 60055B58 mov eax,dword ptr [g_callWndProcRetHook (600B9EE8h)] 60055B5D push esi
并且 0x548B0CC4 周围的内存全部是?????? 所以它不是映射内存,因此崩溃。
myCallWndProcRetCallback 开头的机器代码是这样的:
0x60055B50: ff 83 c4 0c 8b 54 24 18 a1 e8 9e 0b 60 56 52 57 50 ff 15 8c a6 09 60 5f 5e 83 c4 08 c2 0c 00 cc 8b 4c 24 04 8b 01 8b 50
但 Visual Studio 有时也会给出该函数的以下反汇编:
--- c:\projects\redacted.cpp ------------------------- 60055B51 add esp,0Ch if ( nCode == HC_ACTION && lParam != NULL) { 60055B54 mov edx,dword ptr [esp+18h] 60055B58 mov eax,dword ptr [g_callWndProcRetHook (600B9EE8h)] 60055B5D push esi
这看起来像是正确的反汇编,但它比上面的反汇编晚 1 个字节! 您可以看到从 0x60055B58 开始的指令是相同的。
因此,看起来链接器说函数位于 0x60055B50,但代码实际上从 0x60055B51 开始。 我已经确认前者是设置到 Windows 挂钩中的回调。 因此,当 Windows 回调该函数时,它会执行错误的代码。
我的问题是链接器怎么会出错? 我进行了重建,问题就消失了,看起来是随机的。 当时 /FORCE:MULTIPLE 链接器选项有效,但如果没有它,则不会为此回调报告链接错误。
最新补充:这是否与 DLL 的重定位或变基有关? 如果重定位偏移了 1 个字节,这可能会导致问题吗?
The code in question hooks into explorer.exe but was crashing on entry to the callback function:
Unhandled exception at 0x60055b50 (redacted.dll) in explorer.exe: 0xC0000005: Access violation writing location 0x548b0cca.
Callstack:
> redacted.dll!myCallWndProcRetCallback(int nCode=0x00000000, unsigned int wParam=0x00000000, long lParam=0x015afa58) Line 799 C++ user32.dll!_DispatchHookW@16() + 0x31 bytes user32.dll!_fnHkINLPCWPRETSTRUCTW@20() + 0x5e bytes user32.dll!___fnDWORD@4() + 0x24 bytes ntdll.dll!_KiUserCallbackDispatcher@12() + 0x13 bytes user32.dll!_NtUserMessageCall@28() + 0xc bytes user32.dll!_SendMessageW@16() + 0x49 bytes explorer.exe!CTaskBand::_FindIndexByHwnd() + 0x21 bytes explorer.exe!CTaskBand::_HandleShellHook() + 0x48 bytes explorer.exe!CTaskBand::v_WndProc() + 0x660 bytes explorer.exe!CImpWndProc::s_WndProc() + 0x3f bytes
Visual Studio 2005 gave the following disassembly:
--- c:\projects\redacted.cpp ------------------------- //------------------------------------------------------------------------------ LRESULT CALLBACK myCallWndProcRetCallback(int nCode, WPARAM wParam, LPARAM lParam) { 60055B50 inc dword ptr [ebx+548B0CC4h] 60055B56 and al,18h 60055B58 mov eax,dword ptr [g_callWndProcRetHook (600B9EE8h)] 60055B5D push esi
and the memory around 0x548B0CC4 is all ?????? so it is not mapped memory, hence the crash.
The machine code at the start of myCallWndProcRetCallback is this:
0x60055B50: ff 83 c4 0c 8b 54 24 18 a1 e8 9e 0b 60 56 52 57 50 ff 15 8c a6 09 60 5f 5e 83 c4 08 c2 0c 00 cc 8b 4c 24 04 8b 01 8b 50
But Visual Studio also sometimes gives the following disassembly for this function:
--- c:\projects\redacted.cpp ------------------------- 60055B51 add esp,0Ch if ( nCode == HC_ACTION && lParam != NULL) { 60055B54 mov edx,dword ptr [esp+18h] 60055B58 mov eax,dword ptr [g_callWndProcRetHook (600B9EE8h)] 60055B5D push esi
This looks like the correct disassembly but it is from 1 byte later than the disassembly above! You can see the instructions are the same from 0x60055B58 onward.
So, it looks like the linker says the function is at 0x60055B50 but the code actually starts at 0x60055B51. I have confirmed the former is the callback set into the Windows hook. So when Windows calls back into the function it executes bad code.
The question I have is how the linker could get this wrong? I did a rebuild and the problem went away, it seems random. At the time the /FORCE:MULTIPLE linker option was in effect but without it no link error is reported for this callback.
A late addition: Could this be related to the relocation or rebasing of a DLL? If the relocation was off by 1 byte, this could perhaps cause the problem?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
重定位几乎永远不会偏离 1 个字节; .dll 映像必须与 VirtualAlloc 返回的分配粒度对齐,在大多数计算机上,该粒度应为 64k。
这段代码有效了多久? 如果它是随机的,那么 /FORCE:MULTIPLE 可能值得怀疑。 或者您可以使用 Incredibuild...
Relocations will almost never be off by 1 byte; the .dll image has to be aligned to the granularity of allocations returned by VirtualAlloc which should be 64k on most machines.
How long has this code worked? If it's random then the /FORCE:MULTIPLE might be suspect. Or you could be using Incredibuild...