【发布时间】:2016-09-28 23:05:18
【问题描述】:
我有一个application,它执行一堆 API 挂钩到一个 Win32 目标应用程序(它使用 ASIO)来观察目标处理的命名管道流量。它使用 ReadFile 或 WriteFile 调用设置事务的开始,如果调用是同步进行的,则获取该调用的结果。如果调用是异步的,它还会通过到 GetQueuedCompletionStatus 的挂钩来捕获该调用的结束。检索到的数据被发送到我自己的 ASIO 线程池,该线程池为与 Wireshark 的命名管道连接提供服务。其实挺甜的。
一切都很好,除非我必须让程序恢复到原来的状态。取消挂钩功能可以正常工作,除非我强制目标应用程序通过命名管道处理大量请求,否则可以在 GetQueuedCompletionStatus 中无限期阻止现有调用。如果我不等待这些线程解除阻塞,当 GetQueuedCompletionStatus 最终解除阻塞并返回到不再存在的代码洞穴时,我将导致 AV。
我尝试的另一件事是跟踪对 GetQueuedCompletionStatus 的每次调用,并让挂钩函数在 GetQueuedCompletionStatus 的 LpOverlapped 参数与对 PostQueuedCompletionStatus 的相应调用匹配时通知信号。当然,这会解除对所有内容的阻塞,但会严重破坏调用 GetQueuedCompletionStatus 的代码,从而导致 AV。
有谁知道处理这个问题的好方法吗?如果我可以执行以下操作之一,这将起作用:
- 使用 ASIO 将忽略的 PostQueuedCompletionStatus 进行调用
- 捕获 PostQueuedCompletionStatus 进行的虚拟调用并覆盖堆栈中的返回值
- 创建一个半永久性代码洞穴,其中包含一个用于挂钩阻塞调用的蹦床,它将函数指针访问器传递给挂钩调用。 trampoline 函数将看到,如果该值不为 null,它将改为调用该函数指针,而不是将执行返回给调用者。在解除阻塞调度后,挂钩代码可以将该函数指针设置为 GetQueuedCompletionStatus 的地址,然后我们就能够将正确的调用结果返回给调用者。
第一个选项很简单,但如果能够将此应用程序概括为与其他目标一起使用会很好。第二个很容易,除了我想尽可能安全地编写代码。最后一个可能是可行的,我只是不想用代码洞穴污染外部内存空间。
【问题讨论】:
-
钩子函数可以自己解钩吗?如果是这样,它可以跟踪它被输入了多少次,并响应一个全局的“unhook”标志或信号,当入口计数下降到 0 时将它自己解开。很难让它完全线程安全不过。
-
OP:首先,您的标题和第二段错误且具有误导性。脱钩没有问题。您的问题是“我如何确定何时可以安全地卸载函数用作挂钩的 DLL”或类似的问题。请编辑它。要取消挂钩当前被阻止的功能,您只需取消挂钩即可。对于这个问题,最简单、最安全的解决方案可能就是保持 DLL 处于加载状态。
-
@conio:这很公平。
标签: c++ winapi boost-asio api-hook