Skip to content

Instantly share code, notes, and snippets.

@zhangyoufu
Last active May 5, 2023 06:12
Show Gist options
  • Save zhangyoufu/be36035e94b8c0dcb1239a3c8b07a3b1 to your computer and use it in GitHub Desktop.
Save zhangyoufu/be36035e94b8c0dcb1239a3c8b07a3b1 to your computer and use it in GitHub Desktop.
force unbuffer stdout of Windows program
#!/usr/bin/env python3
from msvcrt import get_osfhandle
from ctypes.wintypes import *
from ctypes import *
import os
import struct
if sizeof(c_ulong) == sizeof(c_void_p):
ULONG_PTR = c_ulong
elif sizeof(c_ulonglong) == sizeof(c_void_p):
ULONG_PTR = c_ulonglong
DWORD_PTR = ULONG_PTR
SIZE_T = ULONG_PTR
PVOID = c_void_p
class PROC_THREAD_ATTRIBUTE_LIST(Structure):
pass
class SECURITY_ATTRIBUTES(Structure):
pass
class STARTUPINFOW(Structure):
_fields_ = [
('cb', DWORD),
('lpReserved', LPWSTR),
('lpDesktop', LPWSTR),
('lpTitle', LPWSTR),
('dwX', DWORD),
('dwY', DWORD),
('dwXSize', DWORD),
('dwYSize', DWORD),
('dwXCountChars', DWORD),
('dwYCountChars', DWORD),
('dwFillAttributes', DWORD),
('dwFlags', DWORD),
('wShowWindow', WORD),
('cbReserved2', WORD),
('lpReserved2', LPBYTE),
('hStdInput', HANDLE),
('hStdOutput', HANDLE),
('hStdError', HANDLE),
]
class STARTUPINFOEXW(Structure):
_fields_ = [
('StartupInfo', STARTUPINFOW),
('lpAttributeList', POINTER(PROC_THREAD_ATTRIBUTE_LIST)),
]
class PROCESS_INFORMATION(Structure):
_fields_ = [
('hProcess', HANDLE),
('hThread', HANDLE),
('dwProcessId', DWORD),
('dwThreadId', DWORD),
]
kernel32 = WinDLL('kernel32', use_last_error=True)
CreateProcessW = kernel32.CreateProcessW
CreateProcessW.argtypes = [LPCWSTR, LPWSTR, POINTER(SECURITY_ATTRIBUTES), POINTER(SECURITY_ATTRIBUTES), BOOL, DWORD, LPVOID, LPCWSTR, POINTER(STARTUPINFOW), POINTER(PROCESS_INFORMATION)]
CreateProcessW.restype = BOOL
CloseHandle = kernel32.CloseHandle
CloseHandle.argtypes = [HANDLE]
CloseHandle.restype = BOOL
InitializeProcThreadAttributeList = kernel32.InitializeProcThreadAttributeList
InitializeProcThreadAttributeList.argtypes = [POINTER(PROC_THREAD_ATTRIBUTE_LIST), DWORD, DWORD, POINTER(SIZE_T)]
InitializeProcThreadAttributeList.restype = BOOL
UpdateProcThreadAttribute = kernel32.UpdateProcThreadAttribute
UpdateProcThreadAttribute.argtypes = [POINTER(PROC_THREAD_ATTRIBUTE_LIST), DWORD, DWORD_PTR, PVOID, SIZE_T, PVOID, POINTER(SIZE_T)]
UpdateProcThreadAttribute.restype = BOOL
stdin_r, stdin = os.pipe()
stdout, stdout_w = os.pipe()
os.set_inheritable(stdin_r, True)
os.set_inheritable(stdout_w, True)
stdin_handle = get_osfhandle(stdin_r)
stdout_handle = get_osfhandle(stdout_w)
PROC_THREAD_ATTRIBUTE_HANDLE_LIST = 0x20002
size = SIZE_T()
InitializeProcThreadAttributeList(None, 1, 0, byref(size))
attr_list = cast(create_string_buffer(size.value), POINTER(PROC_THREAD_ATTRIBUTE_LIST))
handle_list = (HANDLE*2)(stdin_handle, stdout_handle)
UpdateProcThreadAttribute(attr_list, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST, handle_list, sizeof(handle_list), None, None)
FOPEN = 0x01
FDEV = 0x40
buf = struct.pack('I', 2) # cfi_len, number of handles
buf += 2 * struct.pack('B', FOPEN | FDEV) # file info
buf += struct.pack('PP', stdin_handle, stdout_handle) # os handle
startup_info_ex = STARTUPINFOEXW()
startup_info_ex.lpAttributeList = attr_list
startup_info = startup_info_ex.StartupInfo
startup_info.cb = sizeof(startup_info_ex)
startup_info.cbReserved2 = len(buf)
startup_info.lpReserved2 = cast(buf, LPBYTE)
EXTENDED_STARTUPINFO_PRESENT = 0x00080000
process_info = PROCESS_INFORMATION()
CreateProcessW(None, 'child.exe', None, None, True, EXTENDED_STARTUPINFO_PRESENT , None, None, byref(startup_info), byref(process_info))
CloseHandle(process_info.hThread)
CloseHandle(process_info.hProcess)
os.close(stdin_r)
os.close(stdout_w)
while 1:
chunk = os.read(stdout, 4096)
if not chunk: raise EOFError
print(repr(chunk))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment