关于控制CreateProcess继承哪些句柄的补充说明
摘要
Raymond Chen介绍了一种使用辅助进程的技术,可以精确控制新进程继承哪些句柄,避免同一进程中其他组件意外继承句柄。
<p>不久前,我写了一篇关于如何使用<code>PROC_<wbr />THREAD_<wbr />ATTRIBUTE_<wbr />HANDLE_<wbr />LIST</code>精确限制新进程继承哪些句柄的<a title="通过程序控制Win32新进程继承哪些句柄" href="https://devblogs.microsoft.com/oldnewthing/20111216-00/?p=8873">文章</a>。这样,当你创建新进程时,就能精确控制哪些句柄被继承,避免意外继承进程中无关组件创建的句柄。</p>
<p>我的一位同事指出,你仍然面临相反的问题:由于句柄必须标记为可继承才能参与<code>PROC_<wbr />THREAD_<wbr />ATTRIBUTE_<wbr />HANDLE_<wbr />LIST</code>,如果另一个线程在未使用<code>PROC_<wbr />THREAD_<wbr />ATTRIBUTE_<wbr />HANDLE_<wbr />LIST</code>的情况下调用<code>CreateProcess</code>且<code>bInheritHandles</code> = <code>TRUE</code>,那么它们就会意外继承你所有的句柄。</p>
<p>如果<code>PROC_<wbr />THREAD_<wbr />ATTRIBUTE_<wbr />HANDLE_<wbr />LIST</code>允许包含不可继承的句柄,那么这个问题本可以避免——在这种情况下,它们对于普通的<code>CreateProcess</code>是不可继承的,但如果明确选择重新加入,则变为可继承。但可惜,设计并非如此。</p>
<p>相反,你可以创建一个辅助进程。这个辅助进程所做的就是等待主进程退出,然后自身退出。</p>
<pre>WaitForSingleObject(hMainProcess, INFINITE);
ExitProcess(0);
</pre>
<p>这个进程听起来没什么用处,实际上也确实没什么用。但它的用处不在于它做了什么,而在于对它做了什么。</p>
<p>主进程中的组件以不可继承的方式创建句柄。当它们想要创建一个继承特定句柄的进程时,会将所需句柄复制到辅助进程中(作为可继承句柄),然后构建一个<code>PROC_<wbr />THREAD_<wbr />ATTRIBUTE_<wbr />HANDLE_<wbr />LIST</code>,将这些复制的句柄列为要继承的句柄。它们还使用<code>PROC_<wbr />THREAD_<wbr />ATTRIBUTE_<wbr />PARENT_<wbr />PROCESS</code>指定辅助进程为句柄继承的父进程。然后将这些线程属性传递给<code>CreateProcess</code>,新进程将恰好继承这些句柄。最后,它们通过<code>DuplicateHandle</code>和<code>DUPLICATE_<wbr />CLOSE_<wbr />SOURCE</code>关闭辅助进程中的句柄进行清理。</p>
<p>请注意,多个线程可以同时以这种方式对辅助进程进行操作,因此你只需要一个辅助进程即可满足所有句柄继承控制的需求。</p>
<p>这避免了意外继承的问题,因为属于主进程组件的句柄仍然标记为不可继承,因此主进程中任何其他代码执行<code>CreateProcess</code>时都不会继承它们。</p>
<p>本文《<a href="https://devblogs.microsoft.com/oldnewthing/20260511-00/?p=112313">关于控制<CODE>Create­Process</CODE>继承哪些句柄的补充说明</a>》首发于<a href="https://devblogs.microsoft.com/oldnewthing">The Old New Thing</a>。</p>
查看缓存全文
缓存时间: 2026/05/16 03:31
# 关于控制 CreateProcess 继承哪些句柄的附加说明 - 旧事新知
来源:https://devblogs.microsoft.com/oldnewthing/20260511-00?p=112313
不久前,我写过一篇文章,介绍如何通过编程控制 Win32 中新进程继承哪些句柄(https://devblogs.microsoft.com/oldnewthing/20111216-00/?p=8873),即使用 `PROC\_THREAD\_ATTRIBUTE\_HANDLE\_LIST` 来精确限制继承的句柄。这样一来,创建新进程时,你就能精准控制哪些句柄被继承,避免意外继承进程中无关组件所创建的句柄。
我的一位同事指出,这仍然存在反向问题:由于句柄必须标记为可继承,才能参与 `PROC\_THREAD\_ATTRIBUTE\_HANDLE\_LIST`,如果另一个线程调用 `CreateProcess` 时 `bInheritHandles` = `TRUE`,但未使用 `PROC\_THREAD\_ATTRIBUTE\_HANDLE\_LIST`,那么它们就会意外继承你*所有*的句柄。
如果 `PROC\_THREAD\_ATTRIBUTE\_HANDLE\_LIST` 允许你包含不可继承的句柄,那么这些句柄在常规 `CreateProcess` 中不可继承,但一旦明确重新加入则可继承,这个问题本可以避免。但可惜,设计并非如此。
不过,你可以创建一个辅助进程。这个辅助进程只做一件事:等待主进程退出,然后自己也退出。
```
WaitForSingleObject(hMainProcess, INFINITE);
ExitProcess(0);
```
这个进程听起来没做什么有用的事,确实如此。但它之所以有用,不在于它做了什么,而在于对它做了什么。
主进程中的组件将其句柄标记为不可继承。当它们需要创建进程并指定继承某些句柄时,会将所需的句柄复制到辅助进程中(标记为可继承),然后构建一个 `PROC\_THREAD\_ATTRIBUTE\_HANDLE\_LIST`,将这些复制的句柄列为要继承的句柄。同时,它们使用 `PROC\_THREAD\_ATTRIBUTE\_PARENT\_PROCESS` 指定*辅助*进程作为句柄来源的父进程。然后,将这些线程属性传递给 `CreateProcess`,新进程将恰好继承这些句柄。最后,它们通过 `DuplicateHandle` 和 `DUPLICATE\_CLOSE\_SOURCE` 关闭辅助进程中的句柄,进行清理。
注意,多个线程可以同时对辅助进程进行此类操作,因此你只需要一个辅助进程就能满足所有句柄继承控制的需求。
这避免了意外继承问题,因为主进程中组件拥有的句柄仍然标记为不可继承,因此主进程中任何其他代码执行 `CreateProcess` 时都不会继承它们。
### 类别
### 主题
## 作者
Raymond Chen
Raymond 参与 Windows 的演进已超过 30 年。2003 年,他创办了一个名为“The Old New Thing”的网站,其受欢迎程度远超他最疯狂的想象,这一发展至今仍让他心有余悸。该网站还衍生出一本书,巧合地也名为《The Old New Thing》(Addison Wesley 2007)。他偶尔会出现在 Windows Dev Docs Twitter 账号上,讲述一些不传递任何有用信息的故事。
相似文章
System.Diagnostics.Process 的假设性重新设计,以避免对仅在你调用 Start 时才有效的属性产生混淆
一篇博客文章,提议重新设计 .NET 中的 System.Diagnostics.Process 类,将仅对已启动进程有效的属性分离到一个新类中,旨在减少 API 混淆。
理解在试图绕过规则时规则背后的原理
本文来自微软的《老东西》博客,解释了Windows内核回调函数最佳实践背后的原理,特别是为什么阻塞或等待工作项会违背其目的,并通过一个关于驱动程序导致系统挂起的警示故事来说明。
为何不根据链接的SDK来改变API行为?
本文以Windows的CoInitializeSecurity为例,探讨了根据链接的SDK版本改变API行为的陷阱。讨论了DLL版本不匹配和尾调用优化等问题使这种方法复杂化。
我是如何解决持续运行的Anthropic智能体循环中上下文窗口膨胀问题的(Opus + Sonnet架构)
一位开发者分享了一种架构模式,用于管理持续运行的Anthropic智能体循环中的上下文窗口膨胀问题,采用KV缓存、动态工具模式加载,以及通过Claude 3.5 Sonnet和Claude 3 Opus解耦执行器与顾问角色。
超越 fork() + exec()
一项向Linux内核添加spawn模板的提案,旨在通过缓存可执行文件信息来优化fork+exec模式,不过当前的补丁集不太可能被原样接受。