From 6715b08ed7f3216367999b559ddeeca86143761e Mon Sep 17 00:00:00 2001 From: Darius Houle Date: Mon, 20 Jan 2025 12:53:27 -0700 Subject: [PATCH] 2 test robustness improvements, helpful message on interpreter sandbox error --- tests/test_process.py | 23 ++++++++++++++++------- windows/injection.py | 7 +++++++ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/tests/test_process.py b/tests/test_process.py index 32ba6ef..2eb3f5d 100644 --- a/tests/test_process.py +++ b/tests/test_process.py @@ -4,9 +4,11 @@ import pytest import os import sys import time -import struct +import ctypes + import textwrap import shutil +import re import windows import windows.pipe @@ -26,8 +28,12 @@ class TestCurrentProcessWithCheckGarbage(object): return windows.current_process.peb def test_get_current_process_modules(self): - # Use sys.executable because executable can be a PyInstaller exe - assert os.path.basename(sys.executable) in windows.current_process.peb.modules[0].name + # Use module filename because this executable can be: + # 1. A PyInstaller exe + # 2. A Windows App execution alias (Microsoft Store builds) + current_proc_filename = ctypes.create_string_buffer(1000) + windows.winproxy.GetModuleFileNameA(None, current_proc_filename, 1000) + assert os.path.basename(current_proc_filename.value.decode()) in windows.current_process.peb.modules[0].name def test_get_current_process_exe(self): exe = windows.current_process.peb.exe @@ -38,10 +44,13 @@ class TestCurrentProcessWithCheckGarbage(object): def test_current_process_pe_imports(self): python_module = windows.current_process.peb.modules[0] imp = python_module.pe.imports - assert "kernel32.dll" in imp.keys(), 'Kernel32.dll not in python imports' - current_proc_id_iat = [f for f in imp["kernel32.dll"] if f.name == "GetCurrentProcessId"][0] - k32_base = windows.winproxy.LoadLibraryA(b"kernel32.dll") - assert windows.winproxy.GetProcAddress(k32_base, b"GetCurrentProcessId") == current_proc_id_iat.value + python_dll_regex = re.compile(r'python[0-9.]+dll', re.IGNORECASE) + python_dll_imp = next((i for i in imp.keys() if python_dll_regex.match(i)), None) + assert python_dll_imp is not None, 'Python dll not in python imports' + + imp_id_iat = imp[python_dll_imp][0] + mod_base = windows.winproxy.LoadLibraryA(python_dll_imp.encode()) + assert windows.winproxy.GetProcAddress(mod_base, imp_id_iat.name.encode()) == imp_id_iat.value def test_current_process_pe_exports(self): mods = [m for m in windows.current_process.peb.modules if m.name == "kernel32.dll"] diff --git a/windows/injection.py b/windows/injection.py index 0530a50..0546e78 100644 --- a/windows/injection.py +++ b/windows/injection.py @@ -389,6 +389,13 @@ def execute_python_code(process, code): # Cache the value ? py_dll_name = get_dll_name_from_python_version() pydll_path = find_python_dll_to_inject(process.bitness) + + # NB: Sandboxing on Windows Store apps prevents remote loading DLLs stored under "C:\Program Files*\WindowsApps\*" + # This is relevant for remote python execution, as the sandboxed python process is the _only_ one that can + # access its CRT and shared libraries. + if '\\windowsapps\\' in pydll_path.lower(): + raise ValueError('Cannot execute remote python code from a sandboxed python interpreter. Install python outside of the Microsoft Store to resolve.') + if sys.version_info.major == 3: # FOr py3, we may have a per-user install. # Meaning that the vcruntime140.dll will not be in the injected process path