Windows中如何在父子进程间传递SOCKET句柄

0x00 背景

Linux由于一切皆文件,不管是文件、管道,还是socket,都可以轻易在父子进程间传递;而Windows上会复杂很多。最近有个需求,需要进行父子进程间的通信,常见的方案是在创建子进程时通过stdinstdoutstderr这三个句柄来传递管道句柄,从而达到父子进程间通信的目的。但这种方式最大的问题是:对子进程需要单独处理stdout和stderr,使用上有些限制。

经过调研之后,放弃了管道这种方式,因为匿名管道不支持异步读写,不符合我们的使用场景。然后,考虑将SOCKET句柄传递给子进程,进而进行通信。

0x01 复制句柄

Windows中有一个复制句柄的API:DuplicateHandle

  1. BOOL DuplicateHandle(
  2. [in] HANDLE hSourceProcessHandle,
  3. [in] HANDLE hSourceHandle,
  4. [in] HANDLE hTargetProcessHandle,
  5. [out] LPHANDLE lpTargetHandle,
  6. [in] DWORD dwDesiredAccess,
  7. [in] BOOL bInheritHandle,
  8. [in] DWORD dwOptions
  9. );
COPY

参数含义如下:

  • hSourceProcessHandle —— 源进程句柄

  • hSourceHandle —— 源句柄

  • hTargetProcessHandle —— 目标进程句柄

  • lpTargetHandle —— 新句柄指针

  • dwDesiredAccess —— 新句柄访问权限

  • bInheritHandle —— 句柄是否可继承

  • dwOptions —— 可选行为,取值为:DUPLICATE_CLOSE_SOURCEDUPLICATE_SAME_ACCESS

使用这个函数,我们可以将当前进程的某个句柄复制到其它进程中,也可以将其它进程的某个句柄复制到当前进程中。因此,我们可以在父进程中创建一个socket对象,然后将句柄的id通过命令行参数传递给子进程;然后子进程将该句柄真正复制到当前进程,并转换成socket对象即可。

0x02 具体代码

父进程

  1. import socket
  2. import subprocess
  3. sock = socket.create_connection(('www.qq.com', 80))
  4. print(sock)
  5. child_process = subprocess.Popen(
  6. ["python", "child.py", str(sock.fileno())],
  7. )
  8. child_process.wait()
COPY

子进程

  1. import _winapi
  2. import os
  3. import socket
  4. def steal_handle(source_pid, handle):
  5. '''Steal a handle from process identified by source_pid.'''
  6. source_process_handle = _winapi.OpenProcess(
  7. _winapi.PROCESS_DUP_HANDLE, False, source_pid)
  8. try:
  9. return _winapi.DuplicateHandle(
  10. source_process_handle, handle,
  11. _winapi.GetCurrentProcess(), 0, False,
  12. _winapi.DUPLICATE_SAME_ACCESS | _winapi.DUPLICATE_CLOSE_SOURCE)
  13. finally:
  14. _winapi.CloseHandle(source_process_handle)
  15. handle = steal_handle(os.getppid(), int(sys.argv[1]))
  16. print(sys.argv[1], "=>", handle)
  17. socks = socket.fromfd(handle, socket.AF_INET, socket.SOCK_STREAM)
  18. print(socks)
  19. socks.send(b'GET / HTTP/1.1\r\n\r\n')
  20. data = socks.recv(1024)
  21. print("Received data:", data)
COPY

steal_handle函数代码是从multiprocessing模块中复制过来的,它也是用了类似的原理进行句柄的传递。

socket.fromfd是Windows端python 3.5以上提供的内置方法,也可以直接用socks = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0, handle)代替,差别只是没有再复制一个句柄出来。

0x03 总结

利用DuplicateHandle函数,可以实现一些特殊的效果,具体可以参考:Windows核心编程 第三章 内核对象

分享

Related Issues not found

Please contact @drunkdream to initialize the comment