10

Windows 句柄可以设置为可继承或不可继承,以控制子进程是否会接收它们(当bInheritHandlesinCreateProcess为 TRUE 时)。但是,使用SetHandleInformation标记 SOCKET 不可继承并不总是有效。特别是,当安装了某些分层服务提供程序 (LSP) 时,子进程无论如何都会继承句柄。这特别有可能导致侦听套接字出现错误。(但是,由于另一个问题,如果孩子尝试使用套接字,它将无法做到!真正的 catch-22!)

重现步骤

  1. 创建例如一个监听套接字。使用 . 将其标记为不可继承SetHandleInformation
  2. 产生一个孩子,与bInheritHandles真实。
  3. 关闭父级中的套接字,并尝试重新绑定到端口。

当安装(非 IFS)LSP 时,例如。PCTools Internet Security,侦听套接字将在子进程中打开(在 中可见netstat),尽管SetHandleInformation在创建子进程之前调用套接字以禁用继承。

有关替代方案,请参阅KB2398202中的(简要)步骤。

有哪些解决方法?

4

1 回答 1

10

简短的回答

通常不可能将 SOCKET 句柄设置为不可继承。也就是说,当安装了某些(非 IFS)LSP 时,即使您将进程中的句柄特别标记为不可继承,也无法阻止子进程bInheritHandles=TRUE接收它们。

解释

防火墙或 A/V 产品通常使用 LSP 来过滤所有 TCP 连接。LSP 是由 WinSock 加载到您的进程中的 DLL,它处理所有 TCP 操作,通常通过执行一些过滤然后将调用直接传递到底层 WinSock 实现。LSP 通过为每个产生 WinSock 实现的实际 SOCKET 句柄创建一个虚拟句柄来工作:您对 WSASocket 的调用将为您提供虚拟句柄;当您使用虚拟句柄时,调用将发送到创建它的 LSP;然后 LSP 将 dummy 映射回实际句柄,并将操作(例如acceptor bind)传递给底层句柄。

因此,问题在于仅调用SetHandleInformation您创建的套接字是不够的:您从未见过的底层句柄(由 LSP 内部使用)仍然由子进程继承。

解决方法

  1. 永远不要调用CreateProcess允许从使用套接字的应用程序的任何继承。这是最可靠的解决方案。相反,要建立与孩子的通信,请创建一个具有适当权限的命名管道,在命令行上将其名称传递给孩子,然后在孩子中重新连接。然后手动传递您希望孩子继承的任何句柄。这是安全的,因为尽管其他用户可以读取命令行,但如果设置正确,则只有孩子的实际用户令牌才能连接到管道。
    如果您只想做一些简单的事情,例如重定向孩子的 stdio,这是非常不雅的,因为您必须控制孩子中的参数解析。要解决这个问题,请创建一个包装器二进制文件,它从命令行读取命名管道名称并连接,设置可继承的句柄,并使用重定向的 stdio 重新调用剩余的参数。从包装器继承句柄是安全的,因为进程中没有套接字。
  2. 或者,在 Vista(带有KB2398202)、Windows 7(带有 SP1)和更高版本上,该WSA_FLAG_NO_HANDLE_INHERIT标志被添加到WSASocket. (这是我可以从 Microsoft 找到的关于该问题的尽可能多的文档。创建修补程序几乎是承认,如果没有它来防止 Base Service Provider 句柄被继承,这是不可能的。虽然它没有很好地宣传!)
  3. 最后,在 Vista 上,还有允许查询基本服务提供者使用的句柄的 ioctl。然后可以将这些标记为不可继承。虽然这很痛苦,但仍然不能解决 XP 的问题。
于 2012-08-21T16:07:52.770 回答