结构的大小为 76 字节(使用 Windows 7 x64 进行测试)。调用 GetOpenFileNameA 或 GetSaveFileNameA 时出现访问冲突异常。我假设在运行时 Windows 尝试读取 88 字节而不是 76 字节。
如果你看一下OPENFILENAME
struct
你会注意到:
#if (_WIN32_WINNT >= 0x0500)
void * pvReserved;
DWORD dwReserved;
DWORD FlagsEx;
#endif // (_WIN32_WINNT >= 0x0500)
这在 32 位程序(VC++ 4 不支持 64 位目标)中转换为正好 12 个字节的差异。只要lStructSize
调用者正确设置,这根本不应该是一个问题。procdump
从 Microsoft/Sysinternals 获取确切状态的小型转储(或附加调试器并进行调查)可能是值得的。您遇到的异常不一定是由于struct
大小。如果是这样,微软更有可能在此功能的向后兼容性方面失手。显然OPENFILENAME::lStructSize
有版本控制struct
和确保你遇到的事情不会发生。但是,我们谈论的是使用 Windows 2000 之前的编译器/链接器构建的程序。
写。就像一个 Loader.exe,它和 Spy++ 一样。调用两个 API 时吞下访问冲突异常。这是一个公平的观点。如果您要在顶层插入异常处理,您可以做您想做的事情,但它可能会导致副作用,具体取决于导致异常的确切原因(即覆盖了确切的内存)。
使用 DLL 注入和 API 挂钩。我可以将 GetOpenFileName 和 GetSaveFileName 与自定义 DLL 中的自定义实现挂钩。我的实现将修复结构并将更正后的结构传递给原始 API 调用。这和第一个有很大关系。我认为这将是最简单和最安全的,因为这样你就可以在没有太多干扰的情况下纠正行为。请进一步阅读下文。另外,请查看 NInjectLib。
使用 SetWindowsHook 挂钩窗口消息?!?!?!除了促进 DLL 的注入(对于 1. 和 2.)之外,我看不出这有什么帮助。
修补二进制文件。是否可以通过使用 HEX 编辑器进行修补来解决此结构大小问题?这可能是最棘手的,取决于它OPENFILENAME
是在二进制文件中(初始化数据)还是在堆栈中,或者它是否在堆上分配(那么容易)。
1. 和 2. 的一种可能的混合方法是:
- 添加一个以
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options
您正在执行的程序命名的子键(例如foo.exe
)
- 在新创建的子键中创建一个
REG_SZ
名为Debugger
的值,并将该值设置为我现在将尝试简要描述的程序。
这有效地为您的这个旧应用程序设置了一个调试器,这意味着我们要编写的调试器将接收您应用程序的命令行作为参数。它很方便,因为它对最终用户是透明的,您可以根据自己的需要进行调整。
您需要编写一个调试器。这个任务并不像一开始看起来那样令人讨厌,因为您可以使用 Win32 提供的调试助手。要点在调试器循环中。通常,您使用CreateProcess
传递适当的标志来自己创建目标进程以便能够对其进行调试。使用WaitForDebugEvent
和ContinueDebugEvent
控制执行。出于所有实际目的,您甚至可能根本不需要调试器循环,因为您可以创建挂起的目标应用程序的主线程(传递CREATE_SUSPENDED
给CreateProcess
),然后CONTEXT
一开始就将主线程指向您自己的代码,然后调用ResumeThread(pi.hThread)
. 这样,您将在主线程启动之前完成。但是,这可能会导致问题由于方式kernel32.dll
的CreateThread
工作(这涉及使用 Win32 子系统 aka 注册新线程csrss.exe
)。因此,建议改为在内存中修补目标的 IAT 或类似的东西。毕竟你只对两个功能感兴趣。
查看此处和此处的两篇文章,以更详细地了解该主题。
我更喜欢基于 PaiMei 编写我的调试器PyDbg
,但我承认我没有尝试在Image File Execution Options
.