Skip to content

Instantly share code, notes, and snippets.

@lambdalisue
Last active February 14, 2020 12:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lambdalisue/ce420370e7e24619db8e32e826fb1f86 to your computer and use it in GitHub Desktop.
Save lambdalisue/ce420370e7e24619db8e32e826fb1f86 to your computer and use it in GitHub Desktop.
Spoof process parent in Windows via Win32 APIs
import ctypes
from contextlib import contextmanager, ExitStack
from ctypes import byref, sizeof, Structure, POINTER
from ctypes import wintypes
# Alias
windll = ctypes.windll # type: ignore
WinError = ctypes.WinError # type: ignore
SIZE_T = ctypes.c_size_t
TRUE = 1
FALSE = 0
# https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfow
class STARTUPINFOW(Structure):
_fields_ = [
('cb', wintypes.DWORD),
('lpReserved', wintypes.LPWSTR),
('lpDesktop', wintypes.LPWSTR),
('lpTitle', wintypes.LPWSTR),
('dwX', wintypes.DWORD),
('dwY', wintypes.DWORD),
('dwXSize', wintypes.DWORD),
('dwYSize', wintypes.DWORD),
('dwXCountChars', wintypes.DWORD),
('dwYCountChars', wintypes.DWORD),
('dwFillAttribute', wintypes.DWORD),
('dwFlags', wintypes.DWORD),
('wShowWindow', wintypes.WORD),
('cbReserved2', wintypes.WORD),
('lpReserved2', wintypes.LPVOID), # LPBYTE
('hStdInput', wintypes.HANDLE),
('hStdOutput', wintypes.HANDLE),
('hStdError', wintypes.HANDLE),
]
# https://docs.microsoft.com/ja-jp/windows/win32/api/winbase/ns-winbase-startupinfoexw
class STARTUPINFOEXW(Structure):
_fields_ = [
('StartupInfo', STARTUPINFOW),
('lpAttributeList', wintypes.LPVOID), # LPPROC_THREAD_ATTRIBUTE_LIST
]
# https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-process_information
class PROCESS_INFORMATION(Structure):
_fields_ = [
('hProcess', wintypes.HANDLE),
('hThread', wintypes.HANDLE),
('dwProcessId', wintypes.DWORD),
('dwThreadId', wintypes.DWORD),
]
def errcheck(result, func, args):
if not result:
raise WinError()
return args
# https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getshellwindow
GetShellWindow = windll.user32.GetShellWindow
GetShellWindow.argtypes = ()
GetShellWindow.restype = wintypes.HWND
# https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowthreadprocessid
GetWindowThreadProcessId = windll.user32.GetWindowThreadProcessId
GetWindowThreadProcessId.argtypes = (wintypes.HWND, wintypes.LPDWORD)
GetWindowThreadProcessId.restype = wintypes.DWORD
# https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess
OpenProcess = windll.kernel32.OpenProcess
OpenProcess.argtypes = (wintypes.DWORD, wintypes.BOOL, wintypes.DWORD)
OpenProcess.restype = wintypes.HANDLE
# https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-initializeprocthreadattributelist?redirectedfrom=MSDN
InitializeProcThreadAttributeList = windll.kernel32.InitializeProcThreadAttributeList
InitializeProcThreadAttributeList.argtypes = (
wintypes.LPVOID, # LPPROC_THREAD_ATTRIBUTE_LIST
wintypes.DWORD,
wintypes.DWORD,
POINTER(SIZE_T),
)
InitializeProcThreadAttributeList.restype = wintypes.BOOL
# https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-updateprocthreadattribute?redirectedfrom=MSDN
UpdateProcThreadAttribute = windll.kernel32.UpdateProcThreadAttribute
UpdateProcThreadAttribute.argtypes = (
wintypes.LPVOID, # LPPROC_THREAD_ATTRIBUTE_LIST
wintypes.DWORD,
SIZE_T, # DWORD_PTR
wintypes.LPVOID,
SIZE_T,
wintypes.LPVOID,
POINTER(SIZE_T),
)
UpdateProcThreadAttribute.restype = wintypes.BOOL
UpdateProcThreadAttribute.errcheck = errcheck
# https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-deleteprocthreadattributelist
DeleteProcThreadAttributeList = windll.kernel32.DeleteProcThreadAttributeList
DeleteProcThreadAttributeList.argtypes = (wintypes.LPVOID,)
DeleteProcThreadAttributeList.restype = None
# https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw
CreateProcessW = windll.kernel32.CreateProcessW
CreateProcessW.argtypes = (
wintypes.LPCWSTR,
wintypes.LPWSTR,
wintypes.LPVOID, # LPSECURITY_ATTRIBUTES
wintypes.LPVOID, # LPSECURITY_ATTRIBUTES
wintypes.BOOL,
wintypes.DWORD,
wintypes.LPVOID,
wintypes.LPCWSTR,
wintypes.LPVOID,
POINTER(PROCESS_INFORMATION),
)
CreateProcessW.restype = wintypes.BOOL
CreateProcessW.errcheck = errcheck
# https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitforsingleobject
WaitForSingleObject = windll.kernel32.WaitForSingleObject
WaitForSingleObject.argtypes = (wintypes.HANDLE, wintypes.DWORD)
WaitForSingleObject.restype = wintypes.DWORD
# https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle
CloseHandle = windll.kernel32.CloseHandle
CloseHandle.argtypes = (wintypes.HANDLE,)
CloseHandle.restype = wintypes.BOOL
CloseHandle.errcheck = errcheck
@contextmanager
def get_shell_window_process():
hWnd = GetShellWindow()
if hWnd is None:
raise RuntimeError("No shell process exist")
lpdwProcessId = wintypes.DWORD()
GetWindowThreadProcessId(hWnd, byref(lpdwProcessId))
# NOTE:
# PROCESS_CREATE_PROCESS is required by PROC_THREAD_ATTRIBUTE_PARENT_PROCESS
PROCESS_CREATE_PROCESS = 0x0080
hProcess = OpenProcess(
PROCESS_CREATE_PROCESS,
FALSE,
lpdwProcessId.value,
)
if not hProcess:
raise WinError()
try:
yield hProcess
finally:
CloseHandle(hProcess)
@contextmanager
def create_attribute_list(parent: int):
lpSize = SIZE_T()
InitializeProcThreadAttributeList(None, 1, 0, byref(lpSize))
AttributeList = (wintypes.BYTE * lpSize.value)()
ret = InitializeProcThreadAttributeList(
byref(AttributeList), 1, 0, byref(lpSize)
)
if not ret:
raise WinError()
hProcess = wintypes.HANDLE(parent)
ProcThreadAttributeParentProcess = 0
PROC_THREAD_ATTRIBUTE_INPUT = 0x00020000
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS = ProcThreadAttributeParentProcess | PROC_THREAD_ATTRIBUTE_INPUT
UpdateProcThreadAttribute(
byref(AttributeList),
0,
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
byref(hProcess),
sizeof(wintypes.HANDLE),
None,
None,
)
try:
yield AttributeList
finally:
DeleteProcThreadAttributeList(byref(AttributeList))
@contextmanager
def create_process(cmd: str, attribute_list: ctypes.Array):
StartupInfo = STARTUPINFOEXW()
StartupInfo.lpAttributeList = ctypes.cast(
byref(attribute_list),
wintypes.LPVOID,
)
StartupInfo.StartupInfo.cb = sizeof(StartupInfo)
DETACHED_PROCESS = 0x00000008
CREATE_NEW_PROCESS_GROUP = 0x00000200
EXTENDED_STARTUPINFO_PRESENT = 0x00080000
lpCommandLine = wintypes.LPWSTR(cmd)
ProcessInformation = PROCESS_INFORMATION()
CreateProcessW(
None,
lpCommandLine,
None,
None,
TRUE,
DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP | EXTENDED_STARTUPINFO_PRESENT,
None,
None,
byref(StartupInfo),
byref(ProcessInformation),
)
# we don't need thread handle
CloseHandle(ProcessInformation.hThread)
# yield process handle
try:
yield ProcessInformation.hProcess
finally:
CloseHandle(ProcessInformation.hProcess)
def main():
import sys
with ExitStack() as stack:
parent = stack.enter_context(get_shell_window_process())
attribute_list = stack.enter_context(create_attribute_list(parent))
process_handle = stack.enter_context(create_process(
f"{sys.executable} -c \"import time; time.sleep(10)\"",
attribute_list,
))
INFINITE = -1
ret = WaitForSingleObject(process_handle, INFINITE)
if ret:
raise WinError()
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment