我一直在阅读了很多有关Windows API钩子不同的技术(技术,我特别着迷和喜欢的),似乎在实现的一个重大问题realiable钩子函数是确保挂钩的方式书写时那是线程安全的。当然,有一些技术已经解决了这个问题或者可以简单地解决这个问题,例如热补丁Windows API,但热补丁并不能保证对所有的 win32 或第三方 API 函数都有效,以及支持挂钩它们的技术通常不是线程安全的。
一个很常见的技术是有问题的多线程是一个内联钩子,它用一个跳转指令代替普通的函数序言代码到钩子过程,然后根据需要通过蹦床调用原始函数。
内联钩子技术有几个固有的问题,这使它成为一种非常复杂的使用和调试方法。正如我所提到的,一个主要问题是,它在现实世界中的正统多线程环境中并不安全。这是因为在改变函数的字节时,你不能保证指令指针不会在你新注入的代码中间,这可能会导致目标应用程序因执行旧的无效混合而崩溃操作码与您插入的操作码混合。
这个问题有一些解决方案,一种是暂停进程中的所有线程,然后检查每个线程中的指令指针,以确保当前没有线程正在执行您要替换的目标指令。然后,如果碰巧有一两个线程执行该特定功能,那么您可以通过执行诸如执行堆栈跟踪以在返回地址处放置断点,恢复线程,然后在线程出现异常时处理异常来做出相应的响应已从目标函数返回。
当然,这种方法仍然不安全,因为CreateThread()
在你可以挂起进程中所有正在运行的线程之前,没有什么可以阻止一个偷偷摸摸的线程的使用(我电脑上的一些应用程序同时运行 40 多个线程)。甚至可能有一个相关进程CreateRemoteThread()
在您的目标应用程序中使用,然后在它安全之前调用您正在挂钩的函数。
该问题的解决方案可能是尝试调试进程并接收进程何时创建新线程的通知,然后通过挂起该线程来响应。当然,Windows API 或第三方 API 提供的许多事件通知系统不会实时发送,这可能允许该线程在挂起之前执行不安全的操作。
另一种解决方案可能是在进程启动之前使用钩子函数静态修补可执行文件,大概是通过钩子 EAT/IAT。这对我来说不是一个选择,因为我需要一个可以在整个进程范围内工作的实现,无论函数是如何解析的,或者在新的未挂钩模块调用该函数的情况下。
使用内联钩子技术还有许多其他问题需要克服,我没有提到。这让我再次回到我的问题:
什么方法可以用于在程序执行期间原子地更改代码流?
我很好奇是否有更强大的解决方案可以克服我在本文中介绍的方法的一些缺点。
请不要为挂钩函数提供第三方库建议。我想实现我自己的教育利益。
我更喜欢使用 C 编程语言的挂钩技术文档和示例。
我的处理器是兼容 x86-x64 的 AMD Athlon II X2 250,我的操作系统是 Windows 7。