Add test / fix Winprocess.load_library / free some of the memory

This commit is contained in:
Clement Rouault
2016-02-03 17:47:07 +01:00
parent 9fbba331ca
commit 6cf4a9cae4
6 changed files with 1905 additions and 59 deletions
-6
View File
@@ -10,11 +10,6 @@ TODO:
- Clean / (rethink?) vectored_exception (+rename exception.py? context.py?)
- Test !! (bp, BP_HX, bp on only on process, bp_hx on only one thread..)
- Injection with suspended process ?
- FIX/add test (inject suspended 32 -> 64)
- Free all the virtual_alloc
- Real API arround alloc/free memory in WinProcess..
- Threading
- Quid IAT hook stub ? just einit threads and remove this ?
@@ -24,7 +19,6 @@ TODO:
Just need to passe the address of the python string as argument
FIXME:
- WMI
- COM initialisation when injected in another process
File diff suppressed because it is too large Load Diff
+3 -1
View File
@@ -129,6 +129,8 @@ class Debugger(object):
# Valid addr ? (in non-loaded module: raise / pass ?)
if expected_target is None or expected_target.pid == target.pid:
if isinstance(target, WinThread):
if bp.type == STANDARD_BP:
continue # Standard BP are set on wide process, nothing to do on a thread
x = self._hardware_breakpoint[target.tid]
# Ignore BP on thread_create that have already been
# put by the process_create event
@@ -332,7 +334,7 @@ class Debugger(object):
class Breakpoint(object):
type = "BP" # REAL BP
type = STANDARD_BP # REAL BP
def __init__(self, addr):
self.addr = addr
+33 -35
View File
@@ -32,20 +32,20 @@ def perform_manual_getproc_loadlib_32(target, dll_name):
RemoteManualLoadLibray += GetProcAddress32
addr = target.virtual_alloc(0x1000)
addr2 = addr + len(dll)
addr3 = addr2 + len(api)
addr4 = addr3 + len(dll_to_load)
with target.allocated_memory(0x1000) as addr:
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 + 4, addr2)
target.write_qword(addr4 + 0x8, addr3)
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 + 4, addr2)
target.write_qword(addr4 + 0x8, addr3)
t = target.execute(RemoteManualLoadLibray.get_code(), addr4)
t.wait()
t = target.execute(RemoteManualLoadLibray.get_code(), addr4)
t.wait()
return True
def perform_manual_getproc_loadlib_64(target, dll_name):
@@ -71,20 +71,20 @@ def perform_manual_getproc_loadlib_64(target, dll_name):
RemoteManualLoadLibray += GetProcAddress64
addr = target.virtual_alloc(0x1000)
addr2 = addr + len(dll)
addr3 = addr2 + len(api)
addr4 = addr3 + len(dll_to_load)
with target.allocated_memory(0x1000) as addr:
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)
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()
t = target.execute(RemoteManualLoadLibray.get_code(), addr4)
t.wait()
return True
@@ -106,11 +106,10 @@ def load_dll_in_remote_process(target, dll_name):
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)
with target.allocated_memory(0x1000) as addr:
target.write_memory(addr, dll_name + "\x00")
t = target.create_thread(load_libraryA, addr)
t.wait()
dbgprint("DLL Injected via LoadLibray", "DLLINJECT")
return True
# Hardcore mode
@@ -244,7 +243,7 @@ def generate_python_exec_shellcode_64(target, PYCODE_ADDR, PyDll):
def inject_python_command(target, code_injected, PYDLL):
"""Postulate: PYDLL is already loaded in target process"""
PYCODE = code_injected + "\x00"
# TODO: free this (how ? when ?)
remote_addr = target.virtual_alloc(len(PYCODE) + 0x100)
target.write_memory(remote_addr, PYCODE)
SHELLCODE_ADDR = remote_addr + len(PYCODE)
@@ -295,11 +294,10 @@ buff[:] = txt
"""
def retrieve_last_exception_data(process):
# TODO : FREE THIS
mem = process.virtual_alloc(0x1000)
execute_python_code(process, retrieve_exc.format(mem))
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)
with process.allocated_memory(0x1000) as mem:
execute_python_code(process, retrieve_exc.format(mem))
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
class RemotePythonError(Exception):
+71 -4
View File
@@ -51,17 +51,17 @@ else:
@contextmanager
def Calc64(exit_code=0):
def Calc64(dwCreationFlags=0, exit_code=0):
try:
calc = pop_calc_64()
calc = pop_calc_64(dwCreationFlags)
yield calc
finally:
calc.exit(exit_code)
@contextmanager
def Calc32(exit_code=0):
def Calc32(dwCreationFlags=0, exit_code=0):
try:
calc = pop_calc_32()
calc = pop_calc_32(dwCreationFlags)
yield calc
finally:
calc.exit(exit_code)
@@ -147,6 +147,32 @@ class WindowsTestCase(unittest.TestCase):
dword = struct.unpack("<I", calc.read_memory(data, 4))[0]
self.assertEqual(dword, 0x42424242)
def test_execute_python_to_32_suspended(self):
with Calc32(dwCreationFlags=CREATE_SUSPENDED) as calc:
data = calc.virtual_alloc(0x1000)
calc.execute_python('import ctypes; ctypes.c_uint.from_address({0}).value = 0x42424242'.format(data))
dword = struct.unpack("<I", calc.read_memory(data, 4))[0]
self.assertEqual(dword, 0x42424242)
# Check calc32 is still suspended:
# 1 thread
# suspend count == 1
self.assertEqual(len(calc.threads), 1)
self.assertEqual(calc.threads[0].suspend(), 1)
@windows_64bit_only
def test_execute_python_to_64_suspended(self):
with Calc64(dwCreationFlags=CREATE_SUSPENDED) as calc:
data = calc.virtual_alloc(0x1000)
calc.execute_python('import ctypes; ctypes.c_uint.from_address({0}).value = 0x42424242'.format(data))
dword = struct.unpack("<I", calc.read_memory(data, 4))[0]
self.assertEqual(dword, 0x42424242)
# Check calc32 is still suspended:
# 1 thread
# suspend count == 1
self.assertEqual(len(calc.threads), 1)
self.assertEqual(calc.threads[0].suspend(), 1)
def test_parse_remote_32_peb(self):
with Calc32() as calc:
# Wait for PEB initialization
@@ -340,6 +366,20 @@ class WindowsTestCase(unittest.TestCase):
time.sleep(0.1)
self.assertEqual(t.exit_code, 0x11223344)
def test_load_library_32(self):
DLL = "wintrust.dll"
with Calc32() as calc:
calc.load_library(DLL)
self.assertIn(DLL, [m.name for m in calc.peb.modules])
@windows_64bit_only
def test_load_library_64(self):
DLL = "wintrust.dll"
with Calc64() as calc:
calc.load_library(DLL)
self.assertIn(DLL, [m.name for m in calc.peb.modules])
class WindowsAPITestCase(unittest.TestCase):
def test_createfileA_fail(self):
with self.assertRaises(WindowsError) as ar:
@@ -442,6 +482,33 @@ class DebuggerTestCase(unittest.TestCase):
d.add_bp(TSTBP(LdrLoadDll32))
d.loop()
def test_standard_breakpoint_multiple_threads(self):
TEST_CASE = self
data = [0]
class TSTBP(windows.debug.Breakpoint):
def trigger(self, dbg, exc):
TEST_CASE.assertEqual(dbg.current_process.pid, calc.pid)
TEST_CASE.assertEqual(dbg.current_process.read_memory(self.addr, 1), "\xcc")
TEST_CASE.assertEqual(dbg.current_thread.context.pc - 1, self.addr)
d.current_process.exit()
calc = pop_calc_32(dwCreationFlags=DEBUG_PROCESS)
if windows.current_process.bitness == 32:
LdrLoadDll32 = windows.current_process.peb.modules[1].pe.exports["LdrLoadDll"]
else:
calcref = pop_calc_32()
LdrLoadDll32 = calcref.peb.modules[1].pe.exports["LdrLoadDll"]
calcref.exit()
d = windows.debug.Debugger(calc)
calc.execute("\xc3")
calc.execute("\xc3")
calc.execute("\xc3")
d.add_bp(TSTBP(LdrLoadDll32))
d.loop()
def test_simple_hwx_breakpoint(self):
TEST_CASE = self
+4 -13
View File
@@ -32,7 +32,7 @@ import windows.pe_parse as pe_parse
class AutoHandle(object):
"""An abstract class that allow easy handle creation/destruction/wait"""
# Big bypass to prevent missing reference at programm close..
CLOSE_FUNCTION = ctypes.WinDLL("kernel32").CloseHandle
_close_function = ctypes.WinDLL("kernel32").CloseHandle
def _get_handle(self):
raise NotImplementedError("{0} is abstract".format(type(self).__name__))
@@ -58,7 +58,7 @@ class AutoHandle(object):
def __del__(self):
if hasattr(self, "_handle") and self._handle:
dbgprint("Closing Handle {0} for {1}".format(hex(self._handle), self), "HANDLE")
self.CLOSE_FUNCTION(self._handle)
self._close_function(self._handle)
class System(object):
@@ -343,7 +343,7 @@ class Process(AutoHandle):
:return: The return value of the native code
:rtype: :class:`int`"""
x = self.virtual_alloc(len(code))
x = self.virtual_alloc(len(code)) #Todo: free this ? when ? how ? reuse ?
self.write_memory(x, code)
return self.create_thread(x, parameter)
@@ -704,16 +704,7 @@ class WinProcess(PROCESSENTRY32, Process):
def load_library(self, dll_path):
"""Load the library in remote process"""
x = self.virtual_alloc(0x1000)
self.write_memory(x, dll_path)
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)
return windows.injection.load_dll_in_remote_process(self, dll_path)
def execute_python(self, pycode):
"""Execute Python code into the remote process.