mirror of
https://github.com/hakril/PythonForWindows
synced 2026-06-08 14:31:45 +00:00
Adapt sample + docs for microsoft-store-python-injection
This commit is contained in:
@@ -69,6 +69,20 @@ Output
|
||||
|
||||
.. _token_sample:
|
||||
|
||||
|
||||
Microsoft Store Python Injection
|
||||
''''''''''''''''''''''''''''''''
|
||||
|
||||
Python execution in remote process fails with Microsoft Store builds of pythons (`mspython`), as the interpreter DLLs do not grant execute to Users.
|
||||
This sample shows a workaround by user https://github.com/dariushoule by copying needed mspython files to a temporary directory and injecting those instead.
|
||||
|
||||
.. literalinclude:: ..\..\samples\process\msstore_interpreter_remote_python.py
|
||||
|
||||
Output
|
||||
|
||||
.. literalinclude:: samples_output\process_msstore_interpreter_remote_python.txt
|
||||
|
||||
|
||||
Token
|
||||
"""""
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
PS C:\Users\hakril\PythonForWindows> py .\samples\process\msstore_interpreter_remote_python.py
|
||||
Executable is: C:\Users\hakril\AppData\Local\Microsoft\WindowsApps\PythonSoftwareFoundation.Python.3.13_qbz5n2kfra8p0\python.exe
|
||||
Trying normal execute_python()
|
||||
Exception during proc1.execute_python():
|
||||
InjectionFailedError('Injection of <c:\\program files\\windowsapps\\pythonsoftwarefoundation.python.3.13_3.13.496.0_x64__qbz5n2kfra8p0\\vcruntime140.dll> failed')
|
||||
Trying mspython workaround:
|
||||
Executing python code!
|
||||
Injecting: C:\Users\hakril\AppData\Local\Temp\pfw_dllcache\vcruntime140.dll
|
||||
Injecting: C:\Users\hakril\AppData\Local\Temp\pfw_dllcache\python313.dll
|
||||
Executing more python code!
|
||||
Executing an error python code!
|
||||
Expected error during safe_execute_python
|
||||
b'Traceback (most recent call last):\n File "<string>", line 1, in <module>\nNameError: name \'BAD_VARIABLE\' is not defined\n'
|
||||
Sleeping a little
|
||||
Killing target process !
|
||||
@@ -1,7 +1,7 @@
|
||||
# Some python interpreters run in environments with restrictive ACLs (no Users/* execute) on bundled DLLs.
|
||||
# The Microsoft Store version of python is the prime example of this.
|
||||
#
|
||||
# Remote execution of python is still possible by creating a minimal set of the dependencies outside of the restricted directory.
|
||||
# Some python interpreters run in environments with restrictive ACLs (no Users/* execute) on bundled DLLs.
|
||||
# The Microsoft Store version of python is the prime example of this.
|
||||
#
|
||||
# Remote execution of python is still possible by creating a minimal set of the dependencies outside of the restricted directory.
|
||||
#
|
||||
# This can be very helpful when operating PFW in environments with restrive GPOs / AppLocker.
|
||||
|
||||
@@ -12,15 +12,19 @@ import os
|
||||
import shutil
|
||||
import tempfile
|
||||
import time
|
||||
import sys
|
||||
import struct
|
||||
|
||||
import windows
|
||||
from windows.generated_def.ntstatus import STATUS_THREAD_IS_TERMINATING
|
||||
from windows.generated_def.windef import CREATE_SUSPENDED
|
||||
from windows.generated_def.winstructs import PROCESS_INFORMATION, STARTUPINFOW
|
||||
from windows.injection import RemotePythonError, \
|
||||
find_python_dll_to_inject, get_dll_name_from_python_version, inject_python_command, load_dll_in_remote_process, retrieve_last_exception_data
|
||||
find_python_dll_to_inject, get_dll_name_from_python_version, inject_python_command, load_dll_in_remote_process, retrieve_exc
|
||||
|
||||
|
||||
print("Executable is: {0}".format(sys.executable))
|
||||
|
||||
CACHE_DIR = os.path.join(tempfile.gettempdir(), 'pfw_dllcache')
|
||||
INTERPRETER_DIR = os.path.dirname(find_python_dll_to_inject(64)) # Tailor bitness to your needs
|
||||
|
||||
@@ -28,8 +32,8 @@ INTERPRETER_DIR = os.path.dirname(find_python_dll_to_inject(64)) # Tailor bitnes
|
||||
def mspython_acl_workaround(target, pydll_path):
|
||||
"""
|
||||
Works around mspython ACL restrictions on mspython interpreters
|
||||
by copying the critical DLLs to a TEMP dir and orienting the interpreter
|
||||
against that TEMP dir.
|
||||
by copying the critical DLLs to a TEMP dir and orienting the interpreter
|
||||
against that TEMP dir.
|
||||
"""
|
||||
|
||||
if not os.path.exists(CACHE_DIR):
|
||||
@@ -40,10 +44,12 @@ def mspython_acl_workaround(target, pydll_path):
|
||||
try:
|
||||
# Creates a copy of the DLL without bringing over restrictive ACLs
|
||||
shutil.copyfile(dll, cache_dll_path)
|
||||
except:
|
||||
except Exception as e:
|
||||
# If its not writeable good chance these DLLs are just already loaded somewhere
|
||||
pass
|
||||
print(e)
|
||||
|
||||
# Preloading python DLL and vcruntime so they don't get loaded from the path tree with restrictive ACLs
|
||||
print("Injecting: {0}".format(cache_dll_path))
|
||||
load_dll_in_remote_process(target, cache_dll_path)
|
||||
|
||||
for dll in glob.glob(os.path.join(INTERPRETER_DIR, 'dlls', '*')):
|
||||
@@ -51,16 +57,19 @@ def mspython_acl_workaround(target, pydll_path):
|
||||
try:
|
||||
# Dynamic lib DLLs with restrictive ACLs copied to unrestricted parent
|
||||
shutil.copyfile(dll, cache_dll_path)
|
||||
except:
|
||||
pass
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
target._workaround_applied = True
|
||||
|
||||
|
||||
# Adapted from windows\winobject\process.py
|
||||
def execute_python_code(process, code):
|
||||
py_dll_name = get_dll_name_from_python_version()
|
||||
pydll_path = find_python_dll_to_inject(process.bitness)
|
||||
|
||||
mspython_acl_workaround(process, pydll_path)
|
||||
|
||||
if not getattr(process, "_workaround_applied", None):
|
||||
mspython_acl_workaround(process, pydll_path)
|
||||
shellcode, pythoncode = inject_python_command(process, code, py_dll_name)
|
||||
t = process.create_thread(shellcode, pythoncode)
|
||||
return t
|
||||
@@ -78,23 +87,41 @@ def safe_execute_python(process, code):
|
||||
data = retrieve_last_exception_data(process)
|
||||
raise RemotePythonError(data)
|
||||
|
||||
# Adapted from windows\injection.py
|
||||
def retrieve_last_exception_data(process):
|
||||
with process.allocated_memory(0x1000) as mem:
|
||||
execute_python_code(process, retrieve_exc.format(mem)).wait()
|
||||
size = struct.unpack("<I", process.read_memory(mem, ctypes.sizeof(ctypes.c_uint)))[0]
|
||||
data = process.read_memory(mem + ctypes.sizeof(ctypes.c_uint), size)
|
||||
return data
|
||||
|
||||
print("Starting target")
|
||||
# First: show what happen when injecting mspython normally
|
||||
print("Trying normal execute_python()")
|
||||
proc1 = windows.utils.create_process(r"C:\Windows\system32\winver.exe")
|
||||
try:
|
||||
proc1.execute_python("2 + 2 == 5")
|
||||
except Exception as e:
|
||||
print(" Exception during proc1.execute_python():")
|
||||
print(" {0}".format(repr(e)))
|
||||
proc1.exit()
|
||||
|
||||
print("Trying mspython workaround:")
|
||||
proc_info = PROCESS_INFORMATION()
|
||||
StartupInfo = STARTUPINFOW()
|
||||
StartupInfo.cb = ctypes.sizeof(StartupInfo)
|
||||
windows.winproxy.CreateProcessW(
|
||||
r"C:\Windows\system32\winver.exe",
|
||||
dwCreationFlags=CREATE_SUSPENDED,
|
||||
r"C:\Windows\system32\winver.exe",
|
||||
dwCreationFlags=CREATE_SUSPENDED,
|
||||
# Point PYTHONHOME to the interpreter dir so non-DLL libs can load
|
||||
# Point PYTHONPATH to the newly created cache directory so DLL libs are loaded from there
|
||||
lpEnvironment=('\0'.join('{}={}'.format(e, v) for e, v in os.environ.items()) + \
|
||||
'\0PYTHONHOME={}\0PYTHONPATH={}\0\0'.format(INTERPRETER_DIR, CACHE_DIR)).encode(),
|
||||
lpProcessInformation=ctypes.byref(proc_info),
|
||||
lpProcessInformation=ctypes.byref(proc_info),
|
||||
lpStartupInfo=ctypes.byref(StartupInfo))
|
||||
|
||||
process = windows.winobject.process.WinProcess(pid=proc_info.dwProcessId, handle=proc_info.hProcess)
|
||||
|
||||
print("Executing python code!")
|
||||
print(" Executing python code!")
|
||||
safe_execute_python(process, """
|
||||
import windows
|
||||
windows.utils.create_console()
|
||||
@@ -103,9 +130,19 @@ print('hello from inside the suspended process!', flush=True)
|
||||
|
||||
process.threads[0].resume()
|
||||
|
||||
print("Executing more python code!")
|
||||
print(" Executing more python code!")
|
||||
safe_execute_python(process, """
|
||||
print('hello from inside the resumed process!', flush=True)
|
||||
""")
|
||||
|
||||
process.wait()
|
||||
print(" Executing an error python code!")
|
||||
try:
|
||||
safe_execute_python(process, """BAD_VARIABLE""")
|
||||
except RemotePythonError as e:
|
||||
print(" Expected error during safe_execute_python")
|
||||
print(" {0}".format(e))
|
||||
|
||||
print(" Sleeping a little")
|
||||
time.sleep(5)
|
||||
print(" Killing target process !")
|
||||
process.exit()
|
||||
@@ -1134,7 +1134,7 @@ class WinProcess(Process):
|
||||
|
||||
.. note::
|
||||
This method is incompatible with Microsoft Store builds of python, as the interpreter DLLs do not grant execute to Users.
|
||||
See workaround: https://github.com/hakril/PythonForWindows/tree/master/samples/process/msstore_interpreter_remote_python.py
|
||||
See workaround: https://hakril.github.io/PythonForWindows/build/html/sample.html#microsoft-store-python-injection
|
||||
"""
|
||||
return injection.safe_execute_python(self, pycode)
|
||||
|
||||
@@ -1145,7 +1145,7 @@ class WinProcess(Process):
|
||||
|
||||
.. note::
|
||||
This method is incompatible with Microsoft Store builds of python, as the interpreter DLLs do not grant execute to Users.
|
||||
See workaround: https://github.com/hakril/PythonForWindows/tree/master/samples/process/msstore_interpreter_remote_python.py
|
||||
See workaround: https://hakril.github.io/PythonForWindows/build/html/sample.html#microsoft-store-python-injection
|
||||
"""
|
||||
return injection.execute_python_code(self, pycode)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user