0x00 背景
Linux由于一切皆文件
,不管是文件、管道,还是socket,都可以轻易在父子进程间传递;而Windows上会复杂很多。最近有个需求,需要进行父子进程间的通信,常见的方案是在创建子进程时通过stdin
、stdout
、stderr
这三个句柄来传递管道句柄,从而达到父子进程间通信的目的。但这种方式最大的问题是:对子进程需要单独处理stdout和stderr,使用上有些限制。
经过调研之后,放弃了管道这种方式,因为匿名管道不支持异步读写,不符合我们的使用场景。然后,考虑将SOCKET句柄传递给子进程,进而进行通信。
0x01 复制句柄
Windows中有一个复制句柄的API:DuplicateHandle
。
BOOL DuplicateHandle(
[in] HANDLE hSourceProcessHandle,
[in] HANDLE hSourceHandle,
[in] HANDLE hTargetProcessHandle,
[out] LPHANDLE lpTargetHandle,
[in] DWORD dwDesiredAccess,
[in] BOOL bInheritHandle,
[in] DWORD dwOptions
);
参数含义如下:
- hSourceProcessHandle —— 源进程句柄
- hSourceHandle —— 源句柄
- hTargetProcessHandle —— 目标进程句柄
- lpTargetHandle —— 新句柄指针
- dwDesiredAccess —— 新句柄访问权限
- bInheritHandle —— 句柄是否可继承
- dwOptions —— 可选行为,取值为:
DUPLICATE_CLOSE_SOURCE
或DUPLICATE_SAME_ACCESS
使用这个函数,我们可以将当前进程的某个句柄复制到其它进程中,也可以将其它进程的某个句柄复制到当前进程中。因此,我们可以在父进程中创建一个socket对象,然后将句柄的id通过命令行参数传递给子进程;然后子进程将该句柄真正复制到当前进程,并转换成socket对象即可。
0x02 具体代码
父进程
代码语言:javascript复制import socket
import subprocess
sock = socket.create_connection(('www.qq.com', 80))
print(sock)
child_process = subprocess.Popen(
["python", "child.py", str(sock.fileno())],
)
child_process.wait()
子进程
代码语言:javascript复制import _winapi
import os
import socket
def steal_handle(source_pid, handle):
'''Steal a handle from process identified by source_pid.'''
source_process_handle = _winapi.OpenProcess(
_winapi.PROCESS_DUP_HANDLE, False, source_pid)
try:
return _winapi.DuplicateHandle(
source_process_handle, handle,
_winapi.GetCurrentProcess(), 0, False,
_winapi.DUPLICATE_SAME_ACCESS | _winapi.DUPLICATE_CLOSE_SOURCE)
finally:
_winapi.CloseHandle(source_process_handle)
handle = steal_handle(os.getppid(), int(sys.argv[1]))
print(sys.argv[1], "=>", handle)
socks = socket.fromfd(handle, socket.AF_INET, socket.SOCK_STREAM)
print(socks)
socks.send(b'GET / HTTP/1.1rnrn')
data = socks.recv(1024)
print("Received data:", data)
steal_handle
函数代码是从multiprocessing
模块中复制过来的,它也是用了类似的原理进行句柄的传递。
socket.fromfd
是Windows端python 3.5以上提供的内置方法,也可以直接用socks = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0, handle)
代替,差别只是没有再复制一个句柄出来。
0x03 总结
利用DuplicateHandle函数,可以实现一些特殊的效果