替换或修改外部应用程序中的 API 调用(GetOpenFileName、GetSaveFileName)

Fer*_*hat 5 c++ crash hook winapi .net-assembly

我有一个旧的应用程序,它调用 GetOpenFileNameA 和 GetSaveFileNameA。两个调用都是错误的。应用程序崩溃了!我使用 OllyDbg 和 API Monitor 来读取 OPENFILENAME 结构中存储的大小。该结构的大小为 76 字节(使用 Windows 7 x64 进行测试)。调用 GetOpenFileNameA 或 GetSaveFileNameA 时出现访问冲突异常。我假设在运行时 Windows 尝试读取 88 字节而不是 76 字节。看看这个: http: //dotnetbutchering.blogspot.de/2007/10/vc-60-getting-0xc0000005-access.html 和这个 http://www.asmcommunity.net/board/index.php?topic =5768.15

我做了一些研究,同时我发现了以下行为:运行 Microsoft Spy++ 时,应用程序不会崩溃!我单步执行调试器,发现访问冲突异常仍然发生,但不知何故,异常被吞没了。该应用程序运行良好!我可以加载和保存文件。

我有以下想法。你觉得他们怎么样?

  1. 写某事。就像 Loader.exe 一样,它的作用与 Spy++ 相同。调用两个 API 时吞掉访问冲突异常。

  2. 使用 DLL 注入和 API 挂钩。我可以将 GetOpenFileName 和 GetSaveFileName 与自定义 DLL 中的自定义实现挂钩。我的实现将修复该结构并将更正后的结构传递给原始 API 调用。

  3. 使用 SetWindowsHook 挂钩窗口消息?!?!?!

  4. 修补二进制文件。是否可以通过使用十六进制编辑器修补来解决此结构大小问题?

哪一个有效?您有更好的主意我该如何解决这个问题吗?

我无法获取这个旧应用程序的源代码。我必须使用现有的二进制文件来修复它。我的解决方案必须至少在 Windows XP 和 Windows 7(x86、x64)上运行

PEiD 工具向我显示了有关旧应用程序的以下信息:链接器信息:2.55 MS Visual C++ 4.0

0xC*_*22L 1

该结构的大小为 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)
Run Code Online (Sandbox Code Playgroud)

在 32 位程序(VC++ 4 不支持 64 位目标)中,这正好转换为 12 个字节的差异。只要lStructSize调用者正确设置,这根本不应该是问题。可能值得使用procdumpMicrosoft/Sysinternals 来获取确切状态的小型转储(或附加调试器并进行调查)。您遇到的异常不一定是由于struct大小造成的。如果是这样,那么微软在向后兼容该功能时很可能失败了。显然OPENFILENAME::lStructSize是为了版本控制struct并确保您遇到的情况不会发生。但是,我们谈论的是使用 Windows 2000 之前的编译器/链接器构建的程序。

写某事。就像 Loader.exe 一样,它的作用与 Spy++ 相同。调用两个 API 时吞掉访问冲突异常。这是一个公平的观点。如果您在顶层插入异常处理,您可以做您想做的事情,但它可能会导致副作用,具体取决于导致异常的具体原因(即哪个确切的内存被覆盖)。

使用 DLL 注入和 API 挂钩。我可以将 GetOpenFileName 和 GetSaveFileName 与自定义 DLL 中的自定义实现挂钩。我的实现将修复该结构并将更正后的结构传递给原始 API 调用。这与第一个有很大关系。我认为这将是最简单和最安全的,因为这样你就可以纠正行为而不会受到太多干扰。请进一步阅读下文。另外,请查看 NInjectLib。

使用 SetWindowsHook 挂钩窗口消息?!?!?!除了促进 DLL 的注入(对于 1. 和 2.)之外,我看不出这有什么帮助。

修补二进制文件。是否可以通过使用十六进制编辑器修补来解决此结构大小问题?这可能是最棘手的,具体取决于它OPENFILENAME是在二进制文件(初始化数据)内还是在堆栈上,或者是否在堆上分配(那么很容易)。


1. 和 2. 的一种可能的混合方法是:

  1. 添加一个HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options以您正在执行的程序命名的子项(例如foo.exe
  2. 在新创建的子项中创建一个REG_SZ命名的值Debugger,并将该值设置为我现在将尝试简要描述的程序。

这有效地为您的旧应用程序设置了一个调试器,这意味着我们要编写的调试器将接收您应用程序的命令行作为参数。它很方便,因为它对最终用户是透明的,您可以调整它以满足您的需要。

您需要编写一个调试器。这个任务并不像乍看起来那样令人厌烦,因为您可以使用 Win32 提供的调试助手。要点在于调试器循环。通常,您可以使用CreateProcess传递适当的标志来自己创建目标进程,以便能够对其进行调试。使用WaitForDebugEventContinueDebugEvent控制执行。出于所有实际目的,您甚至可能根本不需要调试器循环,因为您可以创建挂起的目标应用程序的主线程(传递给CREATE_SUSPENDEDCreateProcess,然后将CONTEXT主线程的 指向您自己的代码,然后调用ResumeThread(pi.hThread)。这样你就可以在主线程开始之前完成。但是,这可能会由于该方式kernel32.dllCreateThread工作方式而导致问题(其中涉及向 Win32 子系统(又名)注册新线程csrss.exe)。因此,建议在内存或类似内容中修补目标的 IAT。毕竟您只对两个功能感兴趣。

请查看此处此处的两篇文章,以更详细地了解该主题。

我更喜欢基于 PaiMei 编写调试器PyDbg我承认我没有尝试在Image File Execution Options.