Working on debugger (str address + rewrite + test)

This commit is contained in:
Clement Rouault
2016-02-10 11:53:13 +01:00
parent 6cf4a9cae4
commit 401db9c347
3 changed files with 448 additions and 94 deletions
+318 -79
View File
@@ -1,3 +1,5 @@
import os.path
import windows
import windows.winproxy as winproxy
@@ -26,13 +28,16 @@ class DEBUG_EVENT(DEBUG_EVENT):
return self.KNOWN_EVENT_CODE.get(self.dwDebugEventCode, self.dwDebugEventCode)
class Debugger(object):
"""A debugger based on standard Win32 API. Handle standard (int3) and Hardware-Exec Breakpoints"""
def __init__(self, target, already_debuggable=False):
"""``target`` must be a WinProcess.
def __init__(self, target):
# Todo: accept PID / String / WinProcess
``already_debuggable`` must be set to ``True`` if process is already expecting a debugger (created with DEBUG_PROCESS)"""
self._init_dispatch_handlers()
self.target = target
self.is_target_launched = False
#winproxy.DebugActiveProcess(target.pid)
if not already_debuggable:
winproxy.DebugActiveProcess(target.pid)
self.processes = {}
self.threads = {}
self.current_process = None
@@ -40,6 +45,7 @@ class Debugger(object):
# List of breakpoints
self.breakpoints = {}
self._pending_breakpoints = {} #Breakpoints to put in new process / threads
self._pending_address = {} # Breakpoints that address have not been resolved yet
# Values rewritten by "\xcc"
self._memory_save = defaultdict(dict)
# Dict of {tid : {drx taken : BP}}
@@ -47,6 +53,16 @@ class Debugger(object):
# Breakpoints to reput..
self._breakpoint_to_reput = {}
self._module_by_process = {}
#TODO: remove this: THIS IS A TEST
self._breakpoints_new_targets = {}
self._breakpoint_resolvable_address = {}
self._pending_breakpoints_new = {}
self._pending_breakpoints_new = defaultdict(list)
def _init_dispatch_handlers(self):
dbg_evt_dispatch = {}
@@ -60,7 +76,6 @@ class Debugger(object):
dbg_evt_dispatch[RIP_EVENT] = self._handle_rip
dbg_evt_dispatch[OUTPUT_DEBUG_STRING_EVENT] = self._handle_output_debug_string
self._DebugEventCode_dispatch = dbg_evt_dispatch
# TODO: breakpoint type dispatch
def _debug_event_generator(self):
while True:
@@ -78,68 +93,136 @@ class Debugger(object):
self.current_thread = self.threads[debug_event.dwThreadId]
def _dispatch_debug_event(self, debug_event):
#print("DISPATCH {0}".format(DEBUG_EVENT.KNOWN_EVENT_CODE.get(debug_event.dwDebugEventCode)))
handler = self._DebugEventCode_dispatch.get(debug_event.dwDebugEventCode, self._handle_unknown_debug_event)
return handler(debug_event)
def _dispatch_breakpoint(self, exception, addr):
bp = self.breakpoints[addr]
bp = self.breakpoints[self.current_process.pid][addr]
x = bp.trigger(self, exception)
return x
def _setup_breakpoint_BP(self, bp, targets):
for target in targets:
if not isinstance(target, WinProcess):
raise ValueError("Cannot put standard breakpoint on target {0} (not a process)".format(target))
self._memory_save[target.pid][bp.addr] = target.read_memory(bp.addr, 1)
#print("Write BP: {0} at {1}".format(process, addr))
target.write_memory(bp.addr, "\xcc")
def _resolve(self, addr, target):
if not isinstance(addr, basestring):
return addr
dll, api = addr.split("!")
dll = dll.lower()
modules = self._module_by_process[target.pid]
mod = None
if dll in modules:
mod = [modules[dll]]
if not mod:
return None
# TODO: optim exports are the same for whole system (32 vs 64 bits)
# I don't have to reparse the exports each time..
exports = mod[0].exports
if api not in exports:
raise ValueError("Unknown API <{0}> in DLL {1}".format(api, dll))
return exports[api]
def _setup_breakpoint_HXBP(self, bp, targets):
all_threads = []
for target in targets:
if isinstance(target, WinProcess):
for t in target.threads:
all_threads.append(t)
elif isinstance(target, WinThread):
all_threads.append(target)
def add_pending_breakpoint(self, bp, target):
self._pending_breakpoints_new[target].append(bp)
def _setup_breakpoint(self, bp, targets):
_setup_method = getattr(self, "_setup_breakpoint_" + bp.type)
if targets is None:
if bp.type == STANDARD_BP: #TODO: better..
targets = self.processes
else:
raise ValueError("Unknow HXBP target type for <{0}>".format(target))
for target_thread in all_threads:
x = self._hardware_breakpoint[target_thread.tid]
if all(pos in x for pos in range(4)):
raise ValueError("Cannot put {0} in {1} (DRx full)".format(bp, target_thread))
empty_drx = str([pos for pos in range(4) if pos not in x][0])
ctx = target_thread.context
ctx.EDr7.GE = 1
ctx.EDr7.LE = 1
setattr(ctx.EDr7, "L" + empty_drx, 1)
setattr(ctx, "Dr" + empty_drx, bp.addr)
x[int(empty_drx)] = bp
target_thread.set_context(ctx)
targets = self.threads
for target in targets:
return _setup_method(bp, target)
def _setup_pending_breakpoints(self, target):
# TODO: good format of data ? (dict and we just use values)
# TODO: need to handle threads ?
# TODO: handle target/expected_target is a thread :)
# Can it happen ?
pending_todo = list(self._pending_breakpoints.values())
for bp, expected_target in pending_todo:
# 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
if bp in x.values():
continue
def _setup_breakpoint_BP(self, bp, target):
if not isinstance(target, WinProcess):
raise ValueError("SETUP STANDARD_BP on {0}".format(target))
addr = self._resolve(bp.addr, target)
if addr is None:
return False
self._memory_save[target.pid][addr] = target.read_memory(addr, 1)
self.breakpoints[target.pid][addr] = bp
target.write_memory(addr, "\xcc")
return True
def _setup_breakpoint_HXBP(self, bp, target):
if not isinstance(target, WinThread):
raise ValueError("SETUP HXBP_BP on {0}".format(target))
# Todo: opti, not reparse exports for all thread of the same process..
addr = self._resolve(bp.addr, target.owner)
if addr is None:
return False
x = self._hardware_breakpoint[target.tid]
if all(pos in x for pos in range(4)):
raise ValueError("Cannot put {0} in {1} (DRx full)".format(bp, target))
empty_drx = str([pos for pos in range(4) if pos not in x][0])
ctx = target.context
ctx.EDr7.GE = 1
ctx.EDr7.LE = 1
setattr(ctx.EDr7, "L" + empty_drx, 1)
setattr(ctx, "Dr" + empty_drx, addr)
x[int(empty_drx)] = bp
target.set_context(ctx)
self.breakpoints[target.owner.pid][addr] = bp
return True
def _setup_pending_breakpoints_new_process(self, new_process):
for bp in self._pending_breakpoints_new[None]:
if bp.apply_to_target(new_process): #BP for thread or process ?
_setup_method = getattr(self, "_setup_breakpoint_" + bp.type)
_setup_method(bp, [target])
# TODO REMOVE PENDING HERE if target is not None..
_setup_method(bp, new_process)
for bp in list(self._pending_breakpoints_new[new_process.pid]):
if bp.apply_to_target(new_process):
_setup_method = getattr(self, "_setup_breakpoint_" + bp.type)
if _setup_method(bp, new_process):
self._pending_breakpoints_new[new_process.pid].remove(bp)
def _setup_pending_breakpoints_new_thread(self, new_thread):
for bp in self._pending_breakpoints_new[None]:
if bp.apply_to_target(new_thread): #BP for thread or process ?
_setup_method = getattr(self, "_setup_breakpoint_" + bp.type)
_setup_method(bp, new_thread)
for bp in self._pending_breakpoints_new[new_thread.owner.pid]:
if bp.apply_to_target(new_thread):
_setup_method = getattr(self, "_setup_breakpoint_" + bp.type)
_setup_method(bp, new_thread)
for bp in list(self._pending_breakpoints_new[new_thread.tid]):
_setup_method = getattr(self, "_setup_breakpoint_" + bp.type)
if _setup_method(bp, new_thread):
self._pending_breakpoints_new[new_thread.tid].remove(bp)
def _setup_pending_breakpoints_load_dll(self, dll_name):
for bp in self._pending_breakpoints_new[None]:
if isinstance(bp.addr, basestring):
target_dll = bp.addr.split("!")[0]
if target_dll == dll_name:
_setup_method = getattr(self, "_setup_breakpoint_" + bp.type)
if bp.apply_to_target(self.current_process):
_setup_method(bp, self.current_process)
else:
for t in self.current_process.threads:
_setup_method(bp, t)
for bp in self._pending_breakpoints_new[self.current_process.pid]:
if isinstance(bp.addr, basestring):
target_dll = bp.addr.split("!")[0]
if target_dll == dll_name:
_setup_method = getattr(self, "_setup_breakpoint_" + bp.type)
_setup_method(bp, self.current_process)
for thread in self.current_process.threads:
for bp in self._pending_breakpoints_new[thread.tid]:
if isinstance(bp.addr, basestring):
target_dll = bp.addr.split("!")[0]
if target_dll == dll_name:
_setup_method = getattr(self, "_setup_breakpoint_" + bp.type)
_setup_method(bp, self.thread)
def _pass_breakpoint(self, addr):
process = self.current_process
@@ -147,7 +230,7 @@ class Debugger(object):
process.write_memory(addr, self._memory_save[process.pid][addr])
regs = thread.context
regs.EFlags |= (1 << 8)
regs.Eip -= 1
regs.pc -= 1
thread.set_context(regs)
self._breakpoint_to_reput[thread.tid] = addr #Register pending breakpoint for next single step
@@ -161,14 +244,13 @@ class Debugger(object):
self._update_debugger_state(debug_event)
if windows.current_process.bitness == 32:
exception.__class__ = windows.vectored_exception.EEXCEPTION_DEBUG_INFO32
exception.__class__ = windows.exception.EEXCEPTION_DEBUG_INFO32
else:
exception.__class__ = windows.vectored_exception.EEXCEPTION_DEBUG_INFO64
exception.__class__ = windows.exception.EEXCEPTION_DEBUG_INFO64
excp_code = exception.ExceptionRecord.ExceptionCode
excp_addr = exception.ExceptionRecord.ExceptionAddress
if excp_code in [EXCEPTION_BREAKPOINT, STATUS_WX86_BREAKPOINT] and excp_addr in self.breakpoints:
if excp_code in [EXCEPTION_BREAKPOINT, STATUS_WX86_BREAKPOINT] and excp_addr in self.breakpoints[self.current_process.pid]:
continue_flag = self._dispatch_breakpoint(exception, excp_addr)
self._pass_breakpoint(excp_addr)
return continue_flag
@@ -179,9 +261,10 @@ class Debugger(object):
# Re-put the breakpoint
self.current_process.write_memory(addr, "\xcc")
return DBG_CONTINUE
elif excp_addr in self.breakpoints:
elif excp_addr in self.breakpoints[self.current_process.pid]:
# Verif that's not a standard BP ?
bp = self.breakpoints[excp_addr]
bp = self.breakpoints[self.current_process.pid][excp_addr]
#import pdb;pdb.set_trace()
bp.trigger(self, exception)
ctx = self.current_thread.context
ctx.EEFlags.RF = 1
@@ -193,6 +276,23 @@ class Debugger(object):
return self.on_exception(exception)
def _get_loaded_dll(self, load_dll):
if not load_dll.lpImageName:
pe = windows.pe_parse.GetPEFile(load_dll.lpBaseOfDll, self.current_process)
return pe.export_name
try:
addr = self.current_process.read_ptr(load_dll.lpImageName)
except:
addr = None
if not addr:
pe = windows.pe_parse.GetPEFile(load_dll.lpBaseOfDll, self.current_process)
return pe.export_name
if load_dll.fUnicode:
return self.current_process.read_wstring(addr)
return self.current_process.read_string(addr)
def _handle_create_process(self, debug_event):
"""Handle CREATE_PROCESS_DEBUG_EVENT"""
create_process = debug_event.u.CreateProcessInfo
@@ -201,8 +301,11 @@ class Debugger(object):
self.current_thread = WinThread._from_handle(create_process.hThread)
self.threads[self.current_thread.tid] = self.current_thread
self.processes[self.current_process.pid] = self.current_process
self.breakpoints[self.current_process.pid] = {}
self._module_by_process[self.current_process.pid] = {}
self._update_debugger_state(debug_event)
self._setup_pending_breakpoints(self.current_process)
self._setup_pending_breakpoints_new_process(self.current_process)
self._setup_pending_breakpoints_new_thread(self.current_thread)
return self.on_create_process(create_process)
# TODO: clode hFile
@@ -225,7 +328,8 @@ class Debugger(object):
create_thread = debug_event.u.CreateThread
self.current_thread = WinThread._from_handle(create_thread.hThread)
self.threads[self.current_thread.tid] = self.current_thread
self._setup_pending_breakpoints(self.current_thread)
#import pdb;pdb.set_trace()
self._setup_pending_breakpoints_new_thread(self.current_thread)
return self.on_create_thread(create_thread)
@@ -245,6 +349,10 @@ class Debugger(object):
"""Handle LOAD_DLL_DEBUG_EVENT"""
self._update_debugger_state(debug_event)
load_dll = debug_event.u.LoadDll
dll = self._get_loaded_dll(load_dll)
dll_name = os.path.basename(dll).lower()
self._module_by_process[self.current_process.pid][dll_name] = windows.pe_parse.GetPEFile(load_dll.lpBaseOfDll, self.current_process)
self._setup_pending_breakpoints_load_dll(dll_name)
return self.on_load_dll(load_dll)
def _handle_unload_dll(self, debug_event):
@@ -267,6 +375,7 @@ class Debugger(object):
# Public API
def loop(self):
"""Debugging loop: handle event / dispatch to breakpoint. Returns when all targets are dead"""
for debug_event in self._debug_event_generator():
dbg_continue_flag = self._dispatch_debug_event(debug_event)
if dbg_continue_flag is None:
@@ -275,8 +384,63 @@ class Debugger(object):
if not self.processes:
break
#def add_bp(self, bp, addr=None, type=None, target=None):
# """Add a breakpoint, bp can be:
#
# * a :class:`Breakpoint` (addr and type must be None)
# * any callable (addr and type must NOT be None)
#
# If the ``bp`` type is ``STANDARD_BP``, target can be None (all targets) or a process.
#
# If the ``bp`` type is ``HARDWARE_EXEC_BP``, target can be None (all targets), a process or a thread.
# """
# if getattr(bp, "addr", None) is None:
# if addr is None or type is None:
# raise ValueError("SUCK YOUR NONE")
# bp = ProxyBreakpoint(bp, addr, type)
# else:
# if addr is not None or type is not None:
# raise ValueError("Given <addr|type> by parameters but BP object have them")
# del addr
# del type
# if target is None:
# # Raise on multiple pending at same addr ?
# # We will add the pending breakpoint to other new processes
# if bp.addr in self._pending_breakpoints:
# raise ValueError("Pending breakpoint already at {0}".format(hex(bp.addr)))
# self._pending_breakpoints[bp.addr] = (bp, target)
# targets = self.processes.values()
# if targets is None:
# return
# else:
# targets = [target]
# if bp.addr in self.breakpoints:
# raise ValueError("Breakpoint already at {0}".format(hex(bp.addr)))
#
# #self.breakpoints[bp.addr] = bp
#
# if isinstance(bp.addr, basestring):
# dll, api = bp.addr.split("!")
# dll = dll.lower()
# if dll not in self._pending_address: #TODO: default dict
# self._pending_address[dll] = []
# self._pending_address[dll].append((api, bp))
#
# _setup_method = getattr(self, "_setup_breakpoint_" + bp.type)
# _setup_method(bp, targets)
# return True
def add_bp(self, bp, addr=None, type=None, target=None):
"""TODO: use type for hardware breakpoints"""
"""Add a breakpoint, bp can be:
* a :class:`Breakpoint` (addr and type must be None)
* any callable (addr and type must NOT be None)
If the ``bp`` type is ``STANDARD_BP``, target can be None (all targets) or a process.
If the ``bp`` type is ``HARDWARE_EXEC_BP``, target can be None (all targets), a process or a thread.
"""
if getattr(bp, "addr", None) is None:
if addr is None or type is None:
raise ValueError("SUCK YOUR NONE")
@@ -286,59 +450,68 @@ class Debugger(object):
raise ValueError("Given <addr|type> by parameters but BP object have them")
del addr
del type
if target is None:
# Raise on multiple pending at same addr ?
# We will add the pending breakpoint to other new processes
if bp.addr in self._pending_breakpoints:
raise ValueError("Pending breakpoint already at {0}".format(hex(bp.addr)))
self._pending_breakpoints[bp.addr] = (bp, target)
targets = self.processes.values()
if targets is None:
return
else:
targets = [target]
if bp.addr in self.breakpoints:
raise ValueError("Breakpoint already at {0}".format(hex(bp.addr)))
self.breakpoints[bp.addr] = bp
_setup_method = getattr(self, "_setup_breakpoint_" + bp.type)
_setup_method(bp, targets)
return True
# Need to add it to all other breakpoint
self.add_pending_breakpoint(bp, None)
elif target is not None:
# Check that targets are accepted
if target not in self.processes + self.threads:
if target == self.target: # Original target (that have not been lauched yet)
return self.add_pending_breakpoint(bp, target)
else:
raise ValueError("Unknown target {0}".format(target))
return self._setup_breakpoint(bp, target)
# Public callback
def on_exception(self, exception):
"""Called on exception event other that known breakpoint"""
pass
def on_create_process(self, create_process):
"""Called on create_process event"""
pass
def on_exit_process(self, exit_process):
"""Called on exit_process event"""
pass
def on_create_thread(self, create_thread):
"""Called on create_thread event"""
pass
def on_exit_thread(self, exit_thread):
"""Called on exit_thread event"""
pass
def on_load_dll(self, load_dll):
"""Called on load_dll event"""
pass
def on_unload_dll(self, unload_dll):
"""Called on unload_dll event"""
pass
def on_output_debug_string(self, debug_string):
"""Called on debug_string event"""
pass
def on_rip(self, rip_info):
"""Called on rip_info event"""
pass
class Breakpoint(object):
"""An standard (Int3) breakpoint (type == ``STANDARD_BP``)"""
type = STANDARD_BP # REAL BP
def __init__(self, addr):
self.addr = addr
def apply_to_target(self, target):
return isinstance(target, WinProcess)
def trigger(self, dbg, exception):
"""Called when breakpoint is hit"""
pass
class ProxyBreakpoint(Breakpoint):
@@ -351,7 +524,73 @@ class ProxyBreakpoint(Breakpoint):
return self.target(dbg, exception)
class HXBreakpoint(Breakpoint):
"""An hardware-execution breakpoint (type == ``HARDWARE_EXEC_BP``)"""
type = HARDWARE_EXEC_BP
def apply_to_target(self, target):
return isinstance(target, WinThread)
## Test a fun little thing
from windows.exception import VectoredException
import ctypes
class LocalDebugger(object):
def __init__(self):
self.breakpoints = {}
self._memory_save = {}
self._reput_breakpoint = {}
self.callback_vectored = VectoredException(self.callback)
windows.winproxy.AddVectoredExceptionHandler(0, self.callback_vectored)
def get_exception_code(self):
return self.current_exception[0].ExceptionRecord[0].ExceptionCode
def get_exception_context(self):
return self.current_exception[0].ContextRecord[0]
def single_step(self):
self.get_exception_context().EEFlags.TF = 1
return windef.EXCEPTION_CONTINUE_EXECUTION
def _pass_breakpoint(self, addr, single_step):
with windows.utils.VirtualProtected(addr, 1, PAGE_EXECUTE_READWRITE):
windows.current_process.write_memory(addr, self._memory_save[addr])
self._reput_breakpoint[windows.current_thread.tid] = self.breakpoints[addr], single_step
return self.single_step()
def callback(self, exc):
self.current_exception = exc
exp_code = self.get_exception_code()
exp_addr = self.get_exception_context().get_pc()
if exp_code == EXCEPTION_BREAKPOINT and exp_addr in self.breakpoints:
continue_value = self.breakpoints[exp_addr].trigger(self, exc)
single_step = self.get_exception_context().EEFlags.TF # single step activated by breakpoint
return self._pass_breakpoint(exp_addr, single_step)
if exp_code == EXCEPTION_SINGLE_STEP and windows.current_thread.tid in self._reput_breakpoint:
bp, single_step = self._reput_breakpoint[windows.current_thread.tid]
self._memory_save[bp.addr] = windows.current_process.read_memory(bp.addr, 1)
with windows.utils.VirtualProtected(bp.addr, 1, PAGE_EXECUTE_READWRITE):
windows.current_process.write_memory(bp.addr, "\xcc")
del self._reput_breakpoint[windows.current_thread.tid]
if single_step:
return self.on_exception(exc)
return windef.EXCEPTION_CONTINUE_EXECUTION
return self.on_exception(exc)
def on_exception(self, exc):
return windef.EXCEPTION_CONTINUE_EXECUTION
def add_bp(self, bp):
if bp.type != STANDARD_BP:
raise NotImplementedError("Add non standard-BP in LocalKernelDebugger")
self.breakpoints[bp.addr] = bp
self._memory_save[bp.addr] = windows.current_process.read_memory(bp.addr, 1)
with windows.utils.VirtualProtected(bp.addr, 1, PAGE_EXECUTE_READWRITE):
windows.current_process.write_memory(bp.addr, "\xcc")
return
+19 -6
View File
@@ -197,10 +197,15 @@ def GetPEFile(baseaddr, target=None):
export_directory_addr = baseaddr + export_directory_rva
return create_structure_at(self._IMAGE_EXPORT_DIRECTORY, export_directory_addr)
class PESection(ctypes_structure_transformer(IMAGE_SECTION_HEADER)):
@utils.fixedpropety
def name(self):
return ctypes.c_char_p(ctypes.addressof(self.Name)).value
class PESection((IMAGE_SECTION_HEADER)):
if target is None:
@property
def name(self):
return ctypes.c_char_p(self.Name).value.decode()
else:
@property
def name(self):
return create_structure_at(ctypes.c_char_p, self._base_addr).value.decode()
def __repr__(self):
return "<PESection \"{0}\">".format(self.name)
@@ -209,8 +214,11 @@ def GetPEFile(baseaddr, target=None):
def sections(self):
nt_header = self.get_NT_HEADER()
nb_section = nt_header.FileHeader.NumberOfSections
base_section = ctypes.addressof(nt_header) + ctypes.sizeof(nt_header)
sections_array = create_structure_at(self.PESection * nb_section, base_section)
if target is None:
base_section = ctypes.addressof(nt_header) + ctypes.sizeof(nt_header)
else:
base_section = nt_header._base_addr + ctypes.sizeof(nt_header)
sections_array = create_structure_at((self.PESection * nb_section), base_section)
return list(sections_array)
@utils.fixedpropety
@@ -230,6 +238,11 @@ def GetPEFile(baseaddr, target=None):
res[rva_name.str] = rva_addr.addr
return res
@utils.fixedpropety
def export_name(self):
"""The Name attribute of the ``EXPORT_DIRECTORY``"""
return self.get_EXPORT_DIRECTORY().Name.str
# TODO: get imports by parsing other modules exports if no INT
@utils.fixedpropety
def imports(self):
+111 -9
View File
@@ -16,6 +16,7 @@ import windows.native_exec.nativeutils as nativeutils
from windows.generated_def.winstructs import *
from windows.native_exec.nativeutils import GetProcAddress64, GetProcAddress32
is_process_32_bits = windows.current_process.bitness == 32
@@ -195,6 +196,7 @@ class WindowsTestCase(unittest.TestCase):
mods = [m for m in calc.peb.modules if m.name == "kernel32.dll"]
self.assertTrue(mods, 'Could not find "kernel32.dll" in calc32')
k32 = mods[0]
mods[0].pe.sections # Just see if it's parse
get_current_proc_id = k32.pe.exports['GetCurrentProcessId']
# TODO: check get_current_proc_id value (but we cannot do 64->32 injection for now)
#if is_process_64_bits:
@@ -219,6 +221,7 @@ class WindowsTestCase(unittest.TestCase):
mods = [m for m in calc.peb.modules if m.name == "kernel32.dll"]
self.assertTrue(mods, 'Could not find "kernel32.dll" in calc32')
k32 = mods[0]
mods[0].pe.sections
get_current_proc_id = k32.pe.exports['GetCurrentProcessId']
data = calc.virtual_alloc(0x1000)
remote_python_code = """
@@ -456,7 +459,7 @@ class DebuggerTestCase(unittest.TestCase):
self.current_process.exit()
calc = pop_calc_32(dwCreationFlags=DEBUG_PROCESS)
d = MyDbg(calc)
d = MyDbg(calc, already_debuggable=True)
d.loop()
def test_simple_standard_breakpoint(self):
@@ -478,7 +481,7 @@ class DebuggerTestCase(unittest.TestCase):
LdrLoadDll32 = calcref.peb.modules[1].pe.exports["LdrLoadDll"]
calcref.exit()
d = windows.debug.Debugger(calc)
d = windows.debug.Debugger(calc, already_debuggable=True)
d.add_bp(TSTBP(LdrLoadDll32))
d.loop()
@@ -491,6 +494,7 @@ class DebuggerTestCase(unittest.TestCase):
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)
data[0] += 1
d.current_process.exit()
calc = pop_calc_32(dwCreationFlags=DEBUG_PROCESS)
@@ -502,7 +506,7 @@ class DebuggerTestCase(unittest.TestCase):
LdrLoadDll32 = calcref.peb.modules[1].pe.exports["LdrLoadDll"]
calcref.exit()
d = windows.debug.Debugger(calc)
d = windows.debug.Debugger(calc, already_debuggable=True)
calc.execute("\xc3")
calc.execute("\xc3")
calc.execute("\xc3")
@@ -528,7 +532,7 @@ class DebuggerTestCase(unittest.TestCase):
LdrLoadDll32 = calcref.peb.modules[1].pe.exports["LdrLoadDll"]
calcref.exit()
d = windows.debug.Debugger(calc)
d = windows.debug.Debugger(calc, already_debuggable=True)
d.add_bp(TSTBP(LdrLoadDll32))
d.loop()
@@ -551,7 +555,7 @@ class DebuggerTestCase(unittest.TestCase):
d.current_process.exit()
calc = pop_calc_32(dwCreationFlags=DEBUG_PROCESS)
d = windows.debug.Debugger(calc)
d = windows.debug.Debugger(calc, already_debuggable=True)
addr = calc.virtual_alloc(0x1000)
calc.write_memory(addr, "\x90" * 8)
d.add_bp(TSTBP(addr, 0))
@@ -577,7 +581,7 @@ class DebuggerTestCase(unittest.TestCase):
raise NotImplementedError("Should fail before")
calc = pop_calc_32(dwCreationFlags=DEBUG_PROCESS)
d = windows.debug.Debugger(calc)
d = windows.debug.Debugger(calc, already_debuggable=True)
addr = calc.virtual_alloc(0x1000)
calc.write_memory(addr, "\x90" * 8 + "\xc3")
d.add_bp(TSTBP(addr, 0))
@@ -609,8 +613,8 @@ class DebuggerTestCase(unittest.TestCase):
def trigger(self, dbg, exc):
TEST_CASE.assertNotEqual(len(dbg.current_process.threads), 1)
for t in dbg.current_process.threads:
TEST_CASE.assertNotEqual(t.context.Dr7, 0)
#for t in dbg.current_process.threads:
# TEST_CASE.assertNotEqual(t.context.Dr7, 0)
if data[0] == 0: #First time we got it ! create new thread
data[0] = 1
calc.create_thread(addr, 0)
@@ -618,7 +622,7 @@ class DebuggerTestCase(unittest.TestCase):
d.current_process.exit()
calc = pop_calc_32(dwCreationFlags=DEBUG_PROCESS)
d = MyDbg(calc)
d = MyDbg(calc, already_debuggable=True)
addr = calc.virtual_alloc(0x1000)
calc.write_memory(addr, "\x90" * 2 + "\xc3")
d.add_bp(TSTBP(addr, 0))
@@ -627,6 +631,104 @@ class DebuggerTestCase(unittest.TestCase):
# Used to verif we actually called the Breakpoints
TEST_CASE.assertEqual(data[0], 1)
def test_simple_breakpoint_name_addr(self):
TEST_CASE = self
data = [0]
class TSTBP(windows.debug.Breakpoint):
def trigger(self, dbg, exc):
addr = exc.ExceptionRecord.ExceptionAddress
TEST_CASE.assertEqual(dbg.current_process.pid, calc.pid)
TEST_CASE.assertEqual(dbg.current_process.read_memory(addr, 1), "\xcc")
TEST_CASE.assertEqual(dbg.current_thread.context.pc - 1, addr)
data[0] += 1
d.current_process.exit()
calc = pop_calc_32(dwCreationFlags=DEBUG_PROCESS)
d = windows.debug.Debugger(calc, already_debuggable=True)
d.add_bp(TSTBP("ntdll.dll!LdrLoadDll"))
d.loop()
TEST_CASE.assertEqual(data[0], 1)
def test_simple_hardware_breakpoint_name_addr(self):
TEST_CASE = self
data = [0]
class TSTBP(windows.debug.HXBreakpoint):
def trigger(self, dbg, exc):
addr = exc.ExceptionRecord.ExceptionAddress
TEST_CASE.assertEqual(dbg.current_process.pid, calc.pid)
TEST_CASE.assertEqual(dbg.current_thread.context.pc, dbg._resolve(self.addr, dbg.current_process))
TEST_CASE.assertNotEqual(dbg.current_thread.context.Dr7, 0)
TEST_CASE.assertNotEqual(dbg.current_process.read_memory(addr, 1), "\xcc")
data[0] += 1
d.current_process.exit()
calc = pop_calc_32(dwCreationFlags=DEBUG_PROCESS)
d = windows.debug.Debugger(calc, already_debuggable=True)
d.add_bp(TSTBP("ntdll.dll!LdrLoadDll"))
d.loop()
TEST_CASE.assertEqual(data[0], 1)
def perform_manual_getproc_loadlib_32_yolo(self, target, dll_name):
dll = "KERNEL32.DLL\x00".encode("utf-16-le")
api = "LoadLibraryA\x00"
dll_to_load = dll_name + "\x00"
RemoteManualLoadLibray = x86.MultipleInstr()
code = RemoteManualLoadLibray
code += x86.Mov("ECX", x86.mem("[ESP + 4]"))
code += x86.Push(x86.mem("[ECX + 4]"))
code += x86.Push(x86.mem("[ECX]"))
code += x86.Call(":FUNC_GETPROCADDRESS32")
code += x86.Push(x86.mem("[ECX + 8]"))
code += x86.Call("EAX") # LoadLibrary
code += x86.Pop("ECX")
code += x86.Pop("ECX")
code += x86.Ret()
RemoteManualLoadLibray += GetProcAddress32
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 + 4, addr2)
target.write_qword(addr4 + 0x8, addr3)
t = target.execute(RemoteManualLoadLibray.get_code(), addr4)
return t
def test_hardware_breakpoint_name_addr(self):
TEST_CASE = self
data = [0]
class TSTBP(windows.debug.HXBreakpoint):
def trigger(self, dbg, exc):
addr = exc.ExceptionRecord.ExceptionAddress
TEST_CASE.assertEqual(dbg.current_process.pid, calc.pid)
TEST_CASE.assertEqual(dbg.current_thread.context.pc, dbg._resolve(self.addr, dbg.current_process))
TEST_CASE.assertNotEqual(dbg.current_thread.context.Dr7, 0)
TEST_CASE.assertNotEqual(dbg.current_process.read_memory(addr, 1), "\xcc")
data[0] += 1
if data[0] == 1:
# Perform a loaddll in a new thread :)
# See if it's trigger a bp
t = TEST_CASE.perform_manual_getproc_loadlib_32_yolo(dbg.current_process, "wintrust.dll")
self.new_thread = t
if hasattr(self, "new_thread") and dbg.current_thread.tid == self.new_thread.tid:
for t in dbg.current_process.threads:
TEST_CASE.assertNotEqual(t.context.Dr7, 0)
d.current_process.exit()
calc = pop_calc_32(dwCreationFlags=DEBUG_PROCESS)
d = windows.debug.Debugger(calc, already_debuggable=True)
d.add_bp(TSTBP("ntdll.dll!LdrLoadDll"))
# Code that will load wintrust !
d.loop()
#TEST_CASE.assertEqual(data[0], 1)
if __name__ == '__main__':
alltests = unittest.TestSuite()