mirror of
https://github.com/hakril/PythonForWindows
synced 2026-06-08 14:31:45 +00:00
Add Process.write_[dq]word + rewrite injection.py to use PEB parsing if needed
This commit is contained in:
@@ -16,6 +16,9 @@ TODO:
|
||||
- reprise sur erreur
|
||||
- reprise context modification
|
||||
|
||||
- Free all the virtual_alloc
|
||||
- Real API arround alloc/free memory in WinProcess..
|
||||
|
||||
FIXME:
|
||||
- WMI
|
||||
- COM initialisation when injected in another process
|
||||
|
||||
+103
-78
@@ -8,52 +8,101 @@ import windows.utils as utils
|
||||
from .native_exec import simple_x86 as x86
|
||||
from .native_exec import simple_x64 as x64
|
||||
|
||||
from windows.native_exec.nativeutils import GetProcAddress64
|
||||
|
||||
def get_loadlib_getproc(target):
|
||||
if windows.current_process.bitness == target.bitness:
|
||||
LoadLibraryA = utils.get_func_addr('kernel32', 'LoadLibraryA')
|
||||
GetProcAddress = utils.get_func_addr('kernel32', 'GetProcAddress')
|
||||
return LoadLibraryA, GetProcAddress
|
||||
else:
|
||||
k32 = [x for x in target.peb.modules if x.name == "kernel32.dll"][0]
|
||||
exp = k32.pe.exports
|
||||
return exp['LoadLibraryA'], exp['GetProcAddress']
|
||||
from windows.dbgprint import dbgprint
|
||||
|
||||
|
||||
def load_dll_in_remote_process(target, dll_name):
|
||||
rpeb = target.peb
|
||||
if rpeb.Ldr:
|
||||
# LDR est parcourable, ca va etre deja plus simple..
|
||||
modules = rpeb.modules
|
||||
if any(mod.name == dll_name for mod in modules):
|
||||
# DLL already loaded
|
||||
dbgprint("DLL already present in DLL", "DLLINJECT")
|
||||
return True
|
||||
k32 = [mod for mod in modules if mod.name.lower() == "kernel32.dll"]
|
||||
if k32:
|
||||
# We have kernel32 \o/
|
||||
k32 = k32[0]
|
||||
try:
|
||||
load_libraryA = k32.pe.exports["LoadLibraryA"]
|
||||
except KeyError:
|
||||
raise ValueError("Kernel32 have no export <LoadLibraryA> (wtf)")
|
||||
|
||||
addr = target.virtual_alloc(0x1000)
|
||||
target.write_memory(addr, dll_name + "\x00")
|
||||
t = target.create_thread(load_libraryA, addr)
|
||||
t.wait()
|
||||
windows.winproxy.VirtualFreeEx(target.handle, addr)
|
||||
dbgprint("DLL Injected via (LoadLibray)", "DLLINJECT")
|
||||
return True
|
||||
# Hardcore mode
|
||||
# We don't have k32 or PEB->Ldr
|
||||
# Go inject a GetProcAddress(LoadLib) + LoadLib shellcode :D
|
||||
if target.bitness == 32:
|
||||
raise NotImplementedError("Manuel GetProcAddress 32bits")
|
||||
|
||||
dll = "KERNEL32.DLL\x00".encode("utf-16-le")
|
||||
api = "LoadLibraryA\x00"
|
||||
dll_to_load = dll_name + "\x00"
|
||||
|
||||
RemoteManualLoadLibray = x64.MultipleInstr()
|
||||
code = RemoteManualLoadLibray
|
||||
code += x64.Mov("R15", "RCX")
|
||||
code += x64.Mov("RCX", x64.mem("[R15 + 0]"))
|
||||
code += x64.Mov("RDX", x64.mem("[R15 + 8]"))
|
||||
code += x64.Call(":FUNC_GETPROCADDRESS64")
|
||||
code += x64.Mov("RCX", x64.mem("[R15 + 0x10]"))
|
||||
code += x64.Push("RCX")
|
||||
code += x64.Push("RCX")
|
||||
code += x64.Push("RCX")
|
||||
code += x64.Call("RAX") # LoadLibrary
|
||||
code += x64.Pop("RCX")
|
||||
code += x64.Pop("RCX")
|
||||
code += x64.Pop("RCX")
|
||||
code += x64.Ret()
|
||||
|
||||
RemoteManualLoadLibray += GetProcAddress64
|
||||
|
||||
addr = target.virtual_alloc(0x1000)
|
||||
addr2 = addr + len(dll)
|
||||
addr3 = addr2 + len(api)
|
||||
addr4 = addr3 + len(dll_to_load)
|
||||
|
||||
target.write_memory(addr, dll)
|
||||
target.write_memory(addr2, api)
|
||||
target.write_memory(addr3, dll_to_load)
|
||||
target.write_qword(addr4, addr)
|
||||
target.write_qword(addr4 + 8, addr2)
|
||||
target.write_qword(addr4 + 0x10, addr3)
|
||||
|
||||
t = target.execute(RemoteManualLoadLibray.get_code(), addr4)
|
||||
t.wait()
|
||||
dbgprint("DLL Injected via manual GetProc(LoadLibray)", "DLLINJECT")
|
||||
return True
|
||||
|
||||
|
||||
# 32 to 32 injection
|
||||
def generate_python_exec_shellcode_32(target, PYDLL_addr, PyInit, PyRun, PYCODE_ADDR):
|
||||
LoadLibraryA, GetProcAddress = get_loadlib_getproc(target)
|
||||
def generate_python_exec_shellcode_32(target, PyInit, PyRun, PYCODE_ADDR):
|
||||
code = x86.MultipleInstr()
|
||||
# Load python27.dll
|
||||
code += x86.Push(PYDLL_addr)
|
||||
code += x86.Mov('EAX', LoadLibraryA)
|
||||
code += x86.Call('EAX')
|
||||
# Get PyInit function into pythondll
|
||||
code += x86.Push('EAX')
|
||||
code += x86.Pop('EDI')
|
||||
code += x86.Push(PyInit)
|
||||
code += x86.Push('EDI')
|
||||
code += x86.Mov('EBX', GetProcAddress)
|
||||
code += x86.Call('EBX')
|
||||
# Call PyInit
|
||||
|
||||
code += x86.Mov('EAX', PyInit)
|
||||
code += x86.Call('EAX')
|
||||
# Get PyRun function into pythondll
|
||||
code += x86.Push(PyRun)
|
||||
code += x86.Push('EDI')
|
||||
code += x86.Call('EBX')
|
||||
# Call PyRun with python code to exec
|
||||
code += x86.Push(PYCODE_ADDR)
|
||||
code += x86.Mov('EAX', PyRun)
|
||||
code += x86.Call('EAX')
|
||||
code += x86.Pop('EDI')
|
||||
code += x86.Pop("EDI")
|
||||
code += x86.Ret()
|
||||
return code.get_code()
|
||||
|
||||
|
||||
# 64 to 64 injection
|
||||
def generate_python_exec_shellcode_64(target, PYDLL_addr, PyInit, PyRun, PYCODE_ADDR):
|
||||
|
||||
LoadLibraryA, GetProcAddress = get_loadlib_getproc(target)
|
||||
|
||||
def generate_python_exec_shellcode_64(target, PyInit, PyRun, PYCODE_ADDR):
|
||||
Reserve_space_for_call = x64.MultipleInstr([x64.Push('RDI')] * 4)
|
||||
Clean_space_for_call = x64.MultipleInstr([x64.Pop('RDI')] * 4)
|
||||
|
||||
@@ -61,35 +110,16 @@ def generate_python_exec_shellcode_64(target, PYDLL_addr, PyInit, PyRun, PYCODE_
|
||||
# Do stack alignement
|
||||
code += x64.Push('RCX')
|
||||
# Load python27.dll
|
||||
code += x64.Mov('RCX', PYDLL_addr)
|
||||
code += x64.Mov('RAX', LoadLibraryA)
|
||||
code += Reserve_space_for_call
|
||||
code += x64.Call('RAX')
|
||||
code += Clean_space_for_call
|
||||
code += x64.Push('RAX')
|
||||
code += x64.Pop('RCX')
|
||||
# Save RCX
|
||||
code += x64.Push('RCX')
|
||||
# Align stack
|
||||
code += x64.Push('RDI')
|
||||
# Get PyInit function into pythondll
|
||||
code += Reserve_space_for_call
|
||||
code += x64.Mov('RDX', PyInit)
|
||||
code += x64.Mov('RBX', GetProcAddress)
|
||||
code += x64.Call('RBX')
|
||||
code += x64.Mov('RAX', PyInit)
|
||||
# Call PyInit
|
||||
code += x64.Call('RAX')
|
||||
code += Clean_space_for_call
|
||||
# Remove Stack align
|
||||
code += x64.Pop('RDI')
|
||||
# Restore pythondll base into rcx
|
||||
code += x64.Pop('RCX')
|
||||
# Get PyRun function into pythondll
|
||||
code += x64.Mov('RDX', PyRun)
|
||||
code += Reserve_space_for_call
|
||||
code += x64.Call('RBX')
|
||||
# Call PyInit with python code to exec
|
||||
code += x64.Mov('RAX', PyRun)
|
||||
code += x64.Mov('RCX', PYCODE_ADDR)
|
||||
# Call PyRun
|
||||
code += x64.Call('RAX')
|
||||
code += Clean_space_for_call
|
||||
# Remove stack alignement
|
||||
@@ -98,40 +128,32 @@ def generate_python_exec_shellcode_64(target, PYDLL_addr, PyInit, PyRun, PYCODE_
|
||||
return code.get_code()
|
||||
|
||||
|
||||
def inject_python_command(process, code_injected, PYDLL="python27.dll\x00"):
|
||||
PyInitT = "Py_Initialize\x00"
|
||||
def inject_python_command(target, code_injected, PYDLL):
|
||||
"""Postulate: PYDLL is already loaded in target process"""
|
||||
PyInit = "Py_Initialize\x00"
|
||||
Pyrun = "PyRun_SimpleString\x00"
|
||||
PYCODE = code_injected + "\x00"
|
||||
remote_addr_base = process.virtual_alloc(len(code_injected) + 0x100)
|
||||
remote_addr = remote_addr_base
|
||||
|
||||
PYDLL_addr = remote_addr
|
||||
process.write_memory(remote_addr, PYDLL)
|
||||
remote_addr += len(PYDLL)
|
||||
pymodule = [mod for mod in target.peb.modules if mod.name == PYDLL][0]
|
||||
Py_exports = pymodule.pe.exports
|
||||
PyInit = Py_exports["Py_Initialize"]
|
||||
Pyrun = Py_exports["PyRun_SimpleString"]
|
||||
|
||||
PyInitT_ADDR = remote_addr
|
||||
process.write_memory(remote_addr, PyInitT)
|
||||
remote_addr += len(PyInitT)
|
||||
remote_addr = target.virtual_alloc(len(PYCODE) + 0x100)
|
||||
target.write_memory(remote_addr, PYCODE)
|
||||
SHELLCODE_ADDR = remote_addr + len(PYCODE)
|
||||
|
||||
Pyrun_ADDR = remote_addr
|
||||
process.write_memory(remote_addr, Pyrun)
|
||||
remote_addr += len(Pyrun)
|
||||
|
||||
PYCODE_ADDR = remote_addr
|
||||
process.write_memory(remote_addr, PYCODE)
|
||||
remote_addr += len(PYCODE)
|
||||
|
||||
SHELLCODE_ADDR = remote_addr
|
||||
if process.bitness == 32:
|
||||
if target.bitness == 32:
|
||||
shellcode_generator = generate_python_exec_shellcode_32
|
||||
else:
|
||||
shellcode_generator = generate_python_exec_shellcode_64
|
||||
shellcode = shellcode_generator(process, PYDLL_addr, PyInitT_ADDR, Pyrun_ADDR, PYCODE_ADDR)
|
||||
process.write_memory(SHELLCODE_ADDR, shellcode)
|
||||
|
||||
shellcode = shellcode_generator(target, PyInit, Pyrun, remote_addr)
|
||||
target.write_memory(SHELLCODE_ADDR, shellcode)
|
||||
return SHELLCODE_ADDR
|
||||
|
||||
|
||||
def validate_python_dll_presence(process):
|
||||
def validate_python_dll_presence_on_disk(process):
|
||||
if windows.current_process.bitness == process.bitness:
|
||||
return True
|
||||
if windows.current_process.bitness == 32 and process.bitness == 64:
|
||||
@@ -146,9 +168,12 @@ def validate_python_dll_presence(process):
|
||||
raise NotImplementedError("Unknown bitness")
|
||||
|
||||
def execute_python_code(process, code):
|
||||
validate_python_dll_presence(process)
|
||||
shellcode_remote_addr = inject_python_command(process, code)
|
||||
return process.create_thread(shellcode_remote_addr, 0)
|
||||
validate_python_dll_presence_on_disk(process)
|
||||
load_dll_in_remote_process(process, "python27.dll")
|
||||
addr = inject_python_command(process, code, "python27.dll")
|
||||
t = process.create_thread(addr, 0)
|
||||
return t
|
||||
|
||||
|
||||
retrieve_exc = r"""
|
||||
import traceback
|
||||
|
||||
+78
-44
@@ -5,6 +5,8 @@ import time
|
||||
import struct
|
||||
import itertools
|
||||
|
||||
from contextlib import contextmanager
|
||||
|
||||
import windows
|
||||
import windows.network
|
||||
import windows.registry
|
||||
@@ -321,14 +323,22 @@ class Process(AutoHandle):
|
||||
"""
|
||||
return self.exit_code != STILL_ACTIVE
|
||||
|
||||
def execute(self, code):
|
||||
#@contextmanager
|
||||
#def allocated_memory(self, size):
|
||||
# addr = self.virtual_alloc(size)
|
||||
# try:
|
||||
# yield addr
|
||||
# finally:
|
||||
# windows.winproxy.VirtualFreeEx(self.handle, size)
|
||||
|
||||
def execute(self, code, parameter=0):
|
||||
"""Execute some native code in the context of the process
|
||||
|
||||
:return: The return value of the native code
|
||||
:rtype: :class:`int`"""
|
||||
x = self.virtual_alloc(len(code))
|
||||
self.write_memory(x, code)
|
||||
return self.create_thread(x, 0)
|
||||
return self.create_thread(x, parameter)
|
||||
|
||||
def query_memory(self, addr):
|
||||
"""Query the memory informations about page at ``addr``
|
||||
@@ -366,6 +376,60 @@ class Process(AutoHandle):
|
||||
return
|
||||
addr += x.RegionSize
|
||||
|
||||
def read_char(self, addr):
|
||||
sizeof_char = sizeof(CHAR)
|
||||
return struct.unpack("<B", self.read_memory(addr, sizeof_char))[0]
|
||||
|
||||
def read_dword(self, addr):
|
||||
sizeof_dword = sizeof(DWORD)
|
||||
return struct.unpack("<I", self.read_memory(addr, sizeof_dword))[0]
|
||||
|
||||
def read_qword(self, addr):
|
||||
sizeof_qword = sizeof(ULONG64)
|
||||
return struct.unpack("<Q", self.read_memory(addr, sizeof_qword))[0]
|
||||
|
||||
def read_ptr(self, addr):
|
||||
if self.bitness == 32:
|
||||
return self.read_dword(addr)
|
||||
return self.read_qword(addr)
|
||||
|
||||
def read_string(self, addr):
|
||||
res = []
|
||||
for i in itertools.count():
|
||||
x = self.read_memory(addr + (i * 0x100), 0x100)
|
||||
if "\x00" in x:
|
||||
res.append(x.split("\x00", 1)[0])
|
||||
break
|
||||
res.append(x)
|
||||
return "".join(res)
|
||||
|
||||
def read_wstring(self, addr):
|
||||
res = []
|
||||
for i in itertools.count():
|
||||
x = self.read_memory(addr + (i * 0x100), 0x100)
|
||||
utf16_chars = ["".join(c) for c in zip(*[iter(x)] * 2)]
|
||||
if "\x00\x00" in utf16_chars:
|
||||
res.extend(utf16_chars[:utf16_chars.index("\x00\x00")])
|
||||
break
|
||||
res.extend(x)
|
||||
return "".join(res).decode('utf16')
|
||||
|
||||
def write_byte(self, addr, byte):
|
||||
"""write a byte to virtual memory"""
|
||||
return self.write_memory(addr, struct.pack("<B", byte))
|
||||
|
||||
def write_word(self, addr, word):
|
||||
"""write a word to virtual memory"""
|
||||
return self.write_memory(addr, struct.pack("<H", word))
|
||||
|
||||
def write_dword(self, addr, dword):
|
||||
"""write a dword to virtual memory"""
|
||||
return self.write_memory(addr, struct.pack("<I", dword))
|
||||
|
||||
def write_qword(self, addr, qword):
|
||||
"""write a qword to virtual memory"""
|
||||
return self.write_memory(addr, struct.pack("<Q", qword))
|
||||
|
||||
@utils.fixedpropety
|
||||
def token(self):
|
||||
"""TODO: DOC"""
|
||||
@@ -587,44 +651,6 @@ class WinProcess(PROCESSENTRY32, Process):
|
||||
self.low_read_memory(addr, ctypes.byref(buffer), size)
|
||||
return buffer[:]
|
||||
|
||||
def read_char(self, addr):
|
||||
sizeof_char = sizeof(CHAR)
|
||||
return struct.unpack("<B", self.read_memory(addr, sizeof_char))[0]
|
||||
|
||||
def read_dword(self, addr):
|
||||
sizeof_dword = sizeof(DWORD)
|
||||
return struct.unpack("<I", self.read_memory(addr, sizeof_dword))[0]
|
||||
|
||||
def read_qword(self, addr):
|
||||
sizeof_qword = sizeof(ULONG64)
|
||||
return struct.unpack("<Q", self.read_memory(addr, sizeof_qword))[0]
|
||||
|
||||
def read_ptr(self, addr):
|
||||
if self.bitness == 32:
|
||||
return self.read_dword(addr)
|
||||
return self.read_qword(addr)
|
||||
|
||||
def read_string(self, addr):
|
||||
res = []
|
||||
for i in itertools.count():
|
||||
x = self.read_memory(addr + (i * 0x100), 0x100)
|
||||
if "\x00" in x:
|
||||
res.append(x.split("\x00", 1)[0])
|
||||
break
|
||||
res.append(x)
|
||||
return "".join(res)
|
||||
|
||||
def read_wstring(self, addr):
|
||||
res = []
|
||||
for i in itertools.count():
|
||||
x = self.read_memory(addr + (i * 0x100), 0x100)
|
||||
utf16_chars = ["".join(c) for c in zip(*[iter(x)] * 2)]
|
||||
if "\x00\x00" in utf16_chars:
|
||||
res.extend(utf16_chars[:utf16_chars.index("\x00\x00")])
|
||||
break
|
||||
res.extend(x)
|
||||
return "".join(res).decode('utf16')
|
||||
|
||||
# Simple cache test
|
||||
# real_read = read_memory
|
||||
#
|
||||
@@ -668,6 +694,12 @@ class WinProcess(PROCESSENTRY32, Process):
|
||||
LoadLibrary = utils.get_func_addr('kernel32', 'LoadLibraryA')
|
||||
return self.create_thread(LoadLibrary, x)
|
||||
|
||||
def tst_load_library(self, dll_path, target_addr):
|
||||
"""Load the library in remote process"""
|
||||
x = self.virtual_alloc(0x1000)
|
||||
self.write_memory(x, dll_path)
|
||||
return self.create_thread(target_addr, x)
|
||||
|
||||
def execute_python(self, pycode):
|
||||
"""Execute Python code into the remote process.
|
||||
|
||||
@@ -906,8 +938,9 @@ class RemotePEB(rctypes.RemoteStructure.from_structure(PEB)):
|
||||
:type: [:class:`LoadedModule`] -- List of loaded modules
|
||||
"""
|
||||
res = []
|
||||
if not self.Ldr.value:
|
||||
raise ValueError("PEB->Ldr is NULL: cannot walk the module list")
|
||||
list_entry_ptr = self.Ldr.contents.InMemoryOrderModuleList.Flink.raw_value
|
||||
|
||||
current_dll = self.ptr_flink_to_remote_module(list_entry_ptr)
|
||||
while current_dll.DllBase:
|
||||
res.append(current_dll)
|
||||
@@ -938,8 +971,9 @@ if CurrentProcess().bitness == 32:
|
||||
:type: [:class:`LoadedModule`] -- List of loaded modules
|
||||
"""
|
||||
res = []
|
||||
if not self.Ldr.value:
|
||||
raise ValueError("PEB->Ldr is NULL: cannot walk the module list")
|
||||
list_entry_ptr = self.Ldr.contents.InMemoryOrderModuleList.Flink.raw_value
|
||||
|
||||
current_dll = self.ptr_flink_to_remote_module(list_entry_ptr)
|
||||
while current_dll.DllBase:
|
||||
res.append(current_dll)
|
||||
@@ -970,9 +1004,9 @@ if CurrentProcess().bitness == 64:
|
||||
:type: [:class:`LoadedModule`] -- List of loaded modules
|
||||
"""
|
||||
res = []
|
||||
#import pdb;pdb.set_trace()
|
||||
if not self.Ldr.value:
|
||||
raise ValueError("PEB->Ldr is NULL: cannot walk the module list")
|
||||
list_entry_ptr = self.Ldr.contents.InMemoryOrderModuleList.Flink.raw_value
|
||||
|
||||
current_dll = self.ptr_flink_to_remote_module(list_entry_ptr)
|
||||
while current_dll.DllBase:
|
||||
res.append(current_dll)
|
||||
|
||||
Reference in New Issue
Block a user