gpt4 book ai didi

c++ - 如何安全地解除阻塞的 Win32 API?

转载 作者:行者123 更新时间:2023-11-28 05:30:04 25 4
gpt4 key购买 nike

我有一个 application它做了一堆 API Hook 到 Win32 目标应用程序(使用 ASIO)以观察目标处理的命名管道流量。它使用 ReadFile 或 WriteFile 调用为事务设置一个开始,如果调用是同步进行的,则获取该调用的结果。如果调用是异步的,它还会通过对 GetQueuedCompletionStatus 的 Hook 捕获该调用的结束。检索到的数据被发送到我自己的 ASIO 线程池,该线程池为 Wireshark 的命名管道连接提供服务。它实际上很甜。

一切正常,除非我必须将程序恢复到其原始状态。解除函数 Hook 工作正常,除了现有调用可以在 GetQueuedCompletionStatus 中无限期阻塞这一事实,除非我强制目标应用程序通过命名管道处理大量请求。如果我不等待这些线程解除阻塞,当 GetQueuedCompletionStatus 最终解除阻塞并返回到不再存在的代码洞穴时,我将导致 AV。

我尝试的另一件事是跟踪对 GetQueuedCompletionStatus 的每次调用,并让 Hook 函数在 GetQueuedCompletionStatus 的 LpOverlapped 参数匹配对 PostQueuedCompletionStatus 的相应调用时通知信号。这当然会解除所有阻塞,但它会严重扰乱调用 GetQueuedCompletionStatus 的代码,从而导致 AV。

有人知道处理这个问题的好方法吗?如果我可以执行以下操作之一,这将有效:

  • 调用 ASIO 将忽略的 PostQueuedCompletionStatus
  • 捕获由 PostQueuedCompletionStatus 发出的虚拟调用并覆盖堆栈中的返回值
  • 创建一个半永久性代码洞穴,其中包含一个用于 Hook 阻塞调用的蹦床,它将函数指针访问器传递给 Hook 调用。 trampoline 函数会发现,如果这个值不为空,它会改为调用该函数指针,而不是将执行返回给调用者。在解除阻塞调度后, Hook 代码可以将该函数指针设置为 GetQueuedCompletionStatus 的地址,然后我们就能够将正确的调用结果返回给调用者。

第一个选项很简单,只是能够将此应用程序概括为与其他目标一起使用会很好。第二个很容易,除了我想编写尽可能安全的代码这一事实。最后一个可能是可行的,我只是不想用代码洞穴污染外部内存空间。

最佳答案

我怀疑是否存在通用且安全的解决方案 - 除了简单地保持 DLL 加载之外。或者从一开始就避免使用所有这些钩子(Hook)......(对于主要剧透,请跳至答案末尾!)


论点与我在问题评论中所写的一致。

要释放 DLL,您必须确保当前没有人在 DLL 中执行代码,并且没有人打算在 DLL 中执行代码(除了我们正在讨论的拆卸线程之外)。

代码可能会在 DLL 中执行有两个原因,要么我们将调用它,要么我们将返回它(或返回将返回到 DLL 的代码,等等。 ).假设我们通过脱钩解决了第一个问题。我们还剩下第二个原因。也许我们已经上当了。

你可能会想到做这样的事情:

  1. 分配一个“代码洞穴”来做类似的事情

    PreHookWriteFile:
    LOCK INC [ref_count]
    POP R15
    CALL HookWriteFile
    PostHookWriteFile:
    LOCK DEC [ref_count]
    JMP R15
  2. 使用 JMP [PreHookWriteFile] Hook WriteFile

  3. 在解除 Hook WriteFile 的专用线程中执行释放并等待重新计数归零。然后它释放代码洞穴。

但这有两个问题。首先,我们真的没有地方可以存储原始返回地址。我们不能把它放在堆栈上,因为 HookWriteFile 不希望在那里看到它,我们不能将它存储在任何非 volatile 寄存器中,因为我们需要恢复它在 PostHookWriteFile 之前跳回,但问题是我们没有地方存储东西。

其次,释放线程可能会在跳转发生之前注意到减少并过早释放代码洞穴。

我认为我们尝试变得多么聪明(使用 __declspec(naked) 函数或通过修改原型(prototype))让 Hook 函数期望堆栈上的原始返回地址并不重要.第二个问题仍然存在——您在返回之前减少了引用计数。在您返回之前删除代码是不安全的,但是您无法通知您的返回,因为在您返回之后您将不再拥有控制权。这就是原因FreeLibraryAndExitThread已创建。

有人可能会想到一些疯狂的事情,比如挂起 DLL 中的所有线程并遍历它们的堆栈以确保其中没有来自 DLL 的代码,但这只是打开另一个蠕虫 jar 。


但也有好消息。

如果您真正想要的是监视命名管道通信,并且您愿意将自己限制在 Windows 8 和更新版本上,那么有一个解决方案更强大、有文档记录和受支持,代码行要少得多,而且不是侵入性的。 Koby Kahane写了一个filesystem mini-filter that does exactly that .

它的输出是您可以在 Microsoft Message Analyzer 中查看的 ETW 事件或使用来自基于 list 的提供程序的 ETW 事件的其他工具之一。如果您确实需要在 Wireshark 中查看它,您可以编写一个小型 ETW 使用者来使用事件并通过管道将它们发送回 Wireshark。它仍然比所有那些钩子(Hook)更容易,而且肯定更安全,也不会那么困惑。

关于c++ - 如何安全地解除阻塞的 Win32 API?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39758949/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com