diff --git a/tests/conftest.py b/tests/conftest.py index e99bd23..11c302d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -30,8 +30,11 @@ else: assert p.bitness == 64 return p else: + # Force creation of AMD64 process on arm system + # TODO: also pop an ARM64 process when code works better with it + machine = gdef.IMAGE_FILE_MACHINE_AMD64 if windows.system.architecture == gdef.PROCESSOR_ARCHITECTURE_ARM64 else None def pop_proc_64(dwCreationFlags=DEFAULT_CREATION_FLAGS): - p = windows.utils.create_process(r"C:\Windows\system32\{0}".format(test_binary_name).encode("ascii"), dwCreationFlags=dwCreationFlags, show_windows=True) + p = windows.utils.create_process(r"C:\Windows\system32\{0}".format(test_binary_name).encode("ascii"), dwCreationFlags=dwCreationFlags, show_windows=True, machine=machine) assert p.bitness == 64 return p diff --git a/tests/pfwtest.py b/tests/pfwtest.py index 578faac..cc353af 100644 --- a/tests/pfwtest.py +++ b/tests/pfwtest.py @@ -46,7 +46,11 @@ def cross_heaven_gates(tstfunc): check_for_gc_garbage = pytest.mark.usefixtures("check_for_gc_garbage") check_for_handle_leak = pytest.mark.usefixtures("check_for_handle_leak") -test_binary_name = "winver.exe" +# msiexec.exe is new best choice: +# - a real process (looking at calc.exe) +# - GUI and wait for a click to close when no param +# - Is ARM64CE on arm -> can be exec as AMD64 or ARM64 with `machine`` param +test_binary_name = "msiexec.exe" DEFAULT_CREATION_FLAGS = gdef.CREATE_NEW_CONSOLE @@ -89,8 +93,8 @@ def check_dll_injection_target_architecture(request): -dll_injection = pytest.mark.usefixtures("check_dll_injection_target_architecture") -python_injection = pytest.mark.usefixtures("check_dll_injection_target_architecture", "check_injected_python_installed") +dll_injection = pytest.mark.usefixtures("check_dll_injection_target_architecture", "check_cross_heaven_gate_arm64_xfail") +python_injection = pytest.mark.usefixtures("check_dll_injection_target_architecture", "check_injected_python_installed", "check_cross_heaven_gate_arm64_xfail") ## P2 VS PY3 diff --git a/tests/test_parse_pe.py b/tests/test_parse_pe.py index fa88f79..728c54d 100644 --- a/tests/test_parse_pe.py +++ b/tests/test_parse_pe.py @@ -9,9 +9,10 @@ from .pfwtest import * @pytest.fixture(params=[None, pop_proc_32, pop_proc_64], ids=["local-pe", "remote-pe32", "remote-pe64"]) def pe(request): - # Pe will be kernelbase.dll + # Pe will be kernelbase.dll or kernel32.dll + # Cannot hardcode peb.modules[2] as it may be xtajitX.dll on arm64 if request.param is None: - yield windows.current_process.peb.modules[2].pe + yield [mod for mod in windows.current_process.peb.modules if mod.name.lower().startswith("kernel")][0].pe return pop_proc = request.param @@ -20,7 +21,8 @@ def pe(request): for i in range(10): try: time.sleep(0.1) - yield proc.peb.modules[2].pe + # Pe will be kernelbase.dll or kernel32.dll + yield [mod for mod in windows.current_process.peb.modules if mod.name.lower().startswith("kernel")][0].pe break except ValueError: if i == 9: diff --git a/windows/utils/winutils.py b/windows/utils/winutils.py index 7121702..8b5c0ef 100644 --- a/windows/utils/winutils.py +++ b/windows/utils/winutils.py @@ -73,14 +73,35 @@ def create_console(): sys.stderr = console_stderr -def create_process(path, args=None, dwCreationFlags=0, show_windows=True): - """A convenient wrapper arround :func:`windows.winproxy.CreateProcessW`""" +def create_process(path, args=None, dwCreationFlags=0, show_windows=True, machine=None): + """A convenient wrapper arround :func:`windows.winproxy.CreateProcessW` + ..note: + The machine param only works starting at vista and should be used on arm64 computer + """ proc_info = PROCESS_INFORMATION() + StartupInfo = None lpStartupInfo = None + + if machine is not None: + buffer = ctypes.create_string_buffer(0x100) + size = gdef.DWORD64(len(buffer)) + machine = gdef.WORD(machine) + windows.winproxy.InitializeProcThreadAttributeList(buffer, 1, 0, size) + windows.winproxy.UpdateProcThreadAttribute(buffer, 0, gdef.PROC_THREAD_ATTRIBUTE_MACHINE_TYPE, ctypes.addressof(machine), ctypes.sizeof(machine), None, None) + + startup_infoex = gdef.STARTUPINFOEXW() + startup_infoex.StartupInfo.cb = ctypes.sizeof(gdef.STARTUPINFOEXW) + startup_infoex.lpAttributeList = ctypes.cast(buffer, gdef.PVOID) + StartupInfo = startup_infoex.StartupInfo + + dwCreationFlags |= gdef.EXTENDED_STARTUPINFO_PRESENT + if show_windows: - StartupInfo = STARTUPINFOW() - StartupInfo.cb = ctypes.sizeof(StartupInfo) + if StartupInfo is None: + StartupInfo = STARTUPINFOW() + StartupInfo.cb = ctypes.sizeof(StartupInfo) StartupInfo.dwFlags = 0 + if StartupInfo: lpStartupInfo = ctypes.byref(StartupInfo) lpCommandLine = None if isinstance(path, bytes): @@ -97,6 +118,8 @@ def create_process(path, args=None, dwCreationFlags=0, show_windows=True): dbgprint("CreateProcessW new thread handle {:#x}".format(proc_info.hThread), "HANDLE") dbgprint("Automatic close of thread handle {:#x}".format(proc_info.hThread), "HANDLE") windows.winproxy.CloseHandle(proc_info.hThread) # Give access to a WinThread in addition of the WinProcess ? + if machine: + windows.winproxy.DeleteProcThreadAttributeList(buffer) return windows.winobject.process.WinProcess(pid=proc_info.dwProcessId, handle=proc_info.hProcess)