简短的回答
通常不可能将 SOCKET 句柄设置为不可继承。也就是说,当安装了某些(非 IFS)LSP 时,即使您将进程中的句柄特别标记为不可继承,也无法阻止子进程bInheritHandles=TRUE
接收它们。
解释
防火墙或 A/V 产品通常使用 LSP 来过滤所有 TCP 连接。LSP 是由 WinSock 加载到您的进程中的 DLL,它处理所有 TCP 操作,通常通过执行一些过滤然后将调用直接传递到底层 WinSock 实现。LSP 通过为每个产生 WinSock 实现的实际 SOCKET 句柄创建一个虚拟句柄来工作:您对 WSASocket 的调用将为您提供虚拟句柄;当您使用虚拟句柄时,调用将发送到创建它的 LSP;然后 LSP 将 dummy 映射回实际句柄,并将操作(例如accept
or bind
)传递给底层句柄。
因此,问题在于仅调用SetHandleInformation
您创建的套接字是不够的:您从未见过的底层句柄(由 LSP 内部使用)仍然由子进程继承。
解决方法
- 永远不要调用
CreateProcess
允许从使用套接字的应用程序的任何继承。这是最可靠的解决方案。相反,要建立与孩子的通信,请创建一个具有适当权限的命名管道,在命令行上将其名称传递给孩子,然后在孩子中重新连接。然后手动传递您希望孩子继承的任何句柄。这是安全的,因为尽管其他用户可以读取命令行,但如果设置正确,则只有孩子的实际用户令牌才能连接到管道。
如果您只想做一些简单的事情,例如重定向孩子的 stdio,这是非常不雅的,因为您必须控制孩子中的参数解析。要解决这个问题,请创建一个包装器二进制文件,它从命令行读取命名管道名称并连接,设置可继承的句柄,并使用重定向的 stdio 重新调用剩余的参数。从包装器继承句柄是安全的,因为进程中没有套接字。
- 或者,在 Vista(带有KB2398202)、Windows 7(带有 SP1)和更高版本上,该
WSA_FLAG_NO_HANDLE_INHERIT
标志被添加到WSASocket
. (这是我可以从 Microsoft 找到的关于该问题的尽可能多的文档。创建修补程序几乎是承认,如果没有它来防止 Base Service Provider 句柄被继承,这是不可能的。虽然它没有很好地宣传!)
- 最后,在 Vista 上,还有允许查询基本服务提供者使用的句柄的 ioctl。然后可以将这些标记为不可继承。虽然这很痛苦,但仍然不能解决 XP 的问题。