【问题标题】:Resume thread of process created suspended, having lost the thread handle恢复创建的进程的线程已挂起,丢失了线程句柄
【发布时间】:2015-12-31 20:44:40
【问题描述】:

在 Windows 上,Python (2) 的标准库例程 subprocess.Popen 允许您为 CreateProcess 指定任意标志,并且您可以从以下位置访问新创建进程的 process 句柄Popen 返回的对象。但是,新创建进程的初始线程的 thread 句柄在 Popen 返回之前被库关闭。

现在,我需要创建一个暂停的进程(CREATE_SUSPENDED 在创建标志中),以便我可以在它有机会执行任何代码之前对其进行操作(特别是,将其附加到作业对象)。但是,这意味着我需要线程句柄才能从暂停中释放进程(使用ResumeThread)。我能找到恢复线程句柄的唯一方法是使用“工具帮助”库来遍历整个系统上的所有线程(例如,参见this question and answer)。这工作,但我不喜欢它。具体来说,我担心每次我需要创建一个进程时对系统上的所有线程进行快照会太昂贵。 (较大的应用程序是一个测试套件,使用进程进行隔离;它以每秒数十到数百个的速度创建和销毁进程。)

所以,问题是:如果您只有进程句柄和Python 2 标准库(包括 ctypes,但 不是 winapi 附加组件)? Vista 和更高版本的技术是可以接受的,但最好与 XP 兼容。

【问题讨论】:

  • 你可以让你的新线程首先等待一个全局(或命名)事件对象;然后只需调用SetEvent 释放它。这可能会给你类似的结果。
  • @JonathanPotter 唉,我不控制正在执行的程序的代码。
  • 似乎不太可能有任何其他明智的方法来找到主线程,但我认为您会发现与启动进程的成本相比,所涉及的开销并不显着。有没有办法自己调用 CreateProcess 而不是使用 .POpen?
  • @HarryJohnston 没有办法将 Popen 对象包装在手动调用 CreateProcess 的结果周围,并且调用代码需要一个 Popen 对象(或者,更准确地说,我不想重新发明所有子流程模块中的底层管道相关知识)。
  • 好的。鉴于所有的限制,我认为答案必须是“不”,没有任何更有效的方法可以做到这一点。如果我们放宽约束,您可以,哦,挂钩 CreateProcess,或使用 Python 的自定义构建,或类似的东西。但我真的认为,如果您进行测量,您会发现流程快照不会增加任何重大开销。如果它甚至比启动流程的成本高出 1%,我会感到惊讶。

标签: python winapi python-2.x


【解决方案1】:

我找到了一种更快的方法;不幸的是,它依赖于一个未记录的 API,NtResumeProcess。这完全符合听起来的样子 - 接受一个进程句柄并将 ResumeThread 的等效项应用于进程中的每个线程。使用它的 Python/ctypes 代码看起来像

import ctypes
from ctypes.wintypes import HANDLE, LONG, ULONG

ntdll = ctypes.WinDLL("ntdll.dll")
RtlNtStatusToDosError = ntdll.RtlNtStatusToDosError
NtResumeProcess = ntdll.NtResumeProcess

def errcheck_ntstatus(status, *etc):
   if status < 0: raise ctypes.WinError(RtlNtStatusToDosError(status))
   return status

RtlNtStatusToDosError.argtypes = (LONG,)
RtlNtStatusToDosError.restype  = ULONG
# RtlNtStatusToDosError cannot fail

NtResumeProcess.argtypes = (HANDLE,)
NtResumeProcess.restype  = LONG
NtResumeProcess.errcheck = errcheck_ntstatus

def resume_subprocess(proc):
    NtResumeProcess(int(proc._handle))

在原本空闲的 Windows 7 虚拟机上,使用这种技术比使用 Toolhelp 减少了大约 20% 的进程设置开销。正如预期的那样,考虑到 Toolhelp 的工作原理,系统上存在的线程越多,性能差异就会越大——无论它们与相关程序是否有任何关系。

鉴于NtResumeProcess 及其对应物NtSuspendProcess 的明显通用性,我想知道为什么它们从未被记录并给出kernel32 包装器。它们被少数核心系统 DLL 和 EXE 使用,所有这些 AFAICT 都是 Windows 错误报告机制的一部分(faultrep.dllwerui.dllwerfault.exedwwin.exe 等)并且不会出现以记录的名称重新公开功能。这些函数似乎不太可能在不更改名称的情况下更改它们的语义,但一个防御性编码的程序可能应该准备好让它们消失(我想回退到工具帮助)。

【讨论】:

    【解决方案2】:

    我在这里发布这个,因为我发现了一些可以解决这个问题的东西。我自己正在研究这个问题,我相信我已经找到了解决方案。

    我不能给你摘录或摘要,因为它太多了,而且我在两个小时前才找到它。我在这里为所有像我一样寻求“轻松”在 Windows 中生成适当子进程但想要执行 cuckoo 的其他人发布此信息。 ;)

    整个第二章都很重要,但细节从第 12 页开始。

    http://lsd-pl.net/winasm.pdf

    我希望它能帮助其他人,就像它希望能帮助我一样。

    编辑:

    我想我可以添加更多内容。根据我收集到的信息,本文档是否解释了如何生成一个永远不会执行的休眠进程。这样我们就可以运行正确设置的 Windows 进程。然后解释了通过使用win32api函数VirtualAllocExWriteProcessMemory,我们可以轻松地分配可执行页面并将机器码注入其他进程。

    然后——我认为最好的部分——可以改变进程的寄存器,允许程序员改变指令指针指向 cuckoo

    太棒了!

    【讨论】:

    • 这很可爱,但它不是我所问问题的答案。我不想在子进程中注入代码,我只是想稍微调整一下它的状态,然后正常运行。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-02
    • 2012-05-20
    • 2011-07-09
    • 2014-06-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多