mirror of
https://github.com/hakril/PythonForWindows
synced 2026-06-08 14:31:45 +00:00
Working / testing the debugger
This commit is contained in:
@@ -4,6 +4,11 @@ TODO:
|
||||
- Extend Registry feature (write)
|
||||
|
||||
- remove pe_parse.transform_ctypes_fields (use utils.transform_ctypes_fields)
|
||||
- DBG
|
||||
- Verif multiple bp at same place..
|
||||
- Verif multiple pending at same place
|
||||
- Clean / (rethink?) vectored_exception (+rename exception.py? context.py?)
|
||||
- Test !! (bp, BP_HX, bp on only on process, bp_hx on only one thread..)
|
||||
|
||||
FIXME:
|
||||
- WMI
|
||||
|
||||
+162
-59
@@ -9,6 +9,11 @@ import windows.native_exec.simple_x64 as x64
|
||||
from windows.generated_def.winstructs import *
|
||||
from .generated_def import windef
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
STANDARD_BP = "BP"
|
||||
HARDWARE_EXEC_BP = "HXBP"
|
||||
|
||||
class DEBUG_EVENT(DEBUG_EVENT):
|
||||
KNOWN_EVENT_CODE = dict((x,x) for x in [EXCEPTION_DEBUG_EVENT,
|
||||
@@ -33,9 +38,17 @@ class Debugger(object):
|
||||
self.current_process = None
|
||||
self.current_thread = None
|
||||
|
||||
# List of breakpoints
|
||||
self.breakpoints = {}
|
||||
self._pending_breakpoints = {} #Breakpoints to put in new process
|
||||
self._break_metadata = {}
|
||||
|
||||
self._pending_breakpoints = {} #Breakpoints to put in new process / threads
|
||||
|
||||
# Values rewritten by "\xcc"
|
||||
self._memory_save = defaultdict(dict)
|
||||
# Dict of {tid : {drx taken : BP}}
|
||||
self._hardware_breakpoint = defaultdict(dict)
|
||||
# Breakpoints to reput..
|
||||
self._breakpoint_to_reput = {}
|
||||
|
||||
|
||||
def _init_dispatch_handlers(self):
|
||||
@@ -73,43 +86,89 @@ class Debugger(object):
|
||||
|
||||
def _dispatch_breakpoint(self, exception, addr):
|
||||
bp = self.breakpoints[addr]
|
||||
return bp(self, exception)
|
||||
bp.trigger(self, exception)
|
||||
return bp
|
||||
|
||||
# Breakpoint stuff
|
||||
#def _setup_breakpoint(self, bp, target):
|
||||
# if bp.type != 0:
|
||||
# raise NotImplementedError("BP TYPE != 0 (TODO)")
|
||||
# if target is None:
|
||||
# targets = self.processes.items()
|
||||
# # Raise on multiple pending at same addr ?
|
||||
# # We will add the pending breakpoint to other new processes
|
||||
# self._pending_breakpoints[bp.addr] = (bp, target)
|
||||
# else:
|
||||
# targets = [(target.pid, target)]
|
||||
#
|
||||
# for pid, process in targets:
|
||||
# self._break_metadata[pid][bp.addr] = process.read_memory(bp.addr, 1)
|
||||
# #print("Write BP: {0} at {1}".format(process, addr))
|
||||
# process.write_memory(bp.addr, "\xcc")
|
||||
# return
|
||||
|
||||
|
||||
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 _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)
|
||||
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])
|
||||
#print("Empty DRx = {0}".format(empty_drx))
|
||||
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)
|
||||
|
||||
def _setup_breakpoint(self, addr, type, target):
|
||||
if type != 0:
|
||||
raise NotImplementedError("BP TYPE != 0 (TODO)")
|
||||
if target is None:
|
||||
targets = self.processes.items()
|
||||
# Raise on multiple pending ?
|
||||
self._pending_breakpoints[addr] = (addr, type, target)
|
||||
else:
|
||||
targets = [(target.pid, target)]
|
||||
for pid, process in targets:
|
||||
self._break_metadata[pid] = process.read_memory(addr, 1)
|
||||
print("Write BP: {0} at {1}".format(process, addr))
|
||||
process.write_memory(addr, "\xcc")
|
||||
return
|
||||
|
||||
def _setup_pending_breakpoints(self, target):
|
||||
for addr, (bp_info) in self._pending_breakpoints.items():
|
||||
# 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 ?)
|
||||
expected_target = bp_info[2]
|
||||
if expected_target is None or expected_target.pid == target.pid:
|
||||
self._setup_breakpoint(bp_info[0], bp_info[1], target)
|
||||
print("BP PLACED IN {0}".format(target))
|
||||
_setup_method = getattr(self, "_setup_breakpoint_" + bp.type)
|
||||
_setup_method(bp, [target])
|
||||
# TODO REMOVE PENDING HERE if target is not None..
|
||||
|
||||
def _activate_single_step(self, thread):
|
||||
raise NotImplementedError("TODO")
|
||||
regs = self.get_context(thread)
|
||||
|
||||
def _pass_breakpoint(self, addr):
|
||||
process = self.current_process
|
||||
thread = self.current_thread
|
||||
process.write_memory(addr, self._memory_save[process.pid][addr])
|
||||
regs = thread.context
|
||||
regs.EFlags |= (1 << 8)
|
||||
self.set_context(thread, regs)
|
||||
|
||||
def _desactivate_single_step(self, thread):
|
||||
regs = self.get_context(thread)
|
||||
raise NotImplementedError("TODO")
|
||||
regs.EFlags &= ~(1 << 8)
|
||||
self.set_context(thread, regs)
|
||||
regs.Eip -= 1
|
||||
thread.set_context(regs)
|
||||
self._breakpoint_to_reput[thread.tid] = addr #Register pending breakpoint for next single step
|
||||
|
||||
# debug event handlers
|
||||
def _handle_unknown_debug_event(self, debug_event):
|
||||
raise NotImplementedError("dwDebugEventCode = {0}".format(debug_event.dwDebugEventCode))
|
||||
|
||||
@@ -124,9 +183,25 @@ class Debugger(object):
|
||||
|
||||
excp_code = exception.ExceptionRecord.ExceptionCode
|
||||
excp_addr = exception.ExceptionRecord.ExceptionAddress
|
||||
print("Exception {0} at {1}".format(excp_code, hex(excp_addr)))
|
||||
if excp_code == EXCEPTION_BREAKPOINT and excp_addr in self.breakpoints:
|
||||
self._dispatch_breakpoint(exception, excp_addr)
|
||||
self._pass_breakpoint(excp_addr)
|
||||
return
|
||||
elif excp_code == EXCEPTION_SINGLE_STEP:
|
||||
if self.current_thread.tid in self._breakpoint_to_reput:
|
||||
addr = self._breakpoint_to_reput[self.current_thread.tid]
|
||||
del self._breakpoint_to_reput[self.current_thread.tid]
|
||||
# Re-put the breakpoint
|
||||
self.current_process.write_memory(addr, "\xcc")
|
||||
elif excp_addr in self.breakpoints:
|
||||
# Verif that's not a standard BP ?
|
||||
bp = self.breakpoints[excp_addr]
|
||||
bp.trigger(self, exception)
|
||||
ctx = self.current_thread.context
|
||||
ctx.EEFlags.RF = 1
|
||||
self.current_thread.set_context(ctx)
|
||||
else:
|
||||
self.on_exception(exception)
|
||||
else: # Do not trigger self.on_exception if breakpoint was registered
|
||||
self.on_exception(exception)
|
||||
|
||||
@@ -136,6 +211,7 @@ 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)
|
||||
self.on_create_thread(create_thread)
|
||||
|
||||
def _handle_create_process(self, debug_event):
|
||||
@@ -195,8 +271,45 @@ class Debugger(object):
|
||||
rip_info = debug_event.u.RipInfo
|
||||
self.on_rip(rip_info)
|
||||
|
||||
# Public callback
|
||||
# Public API
|
||||
def loop(self):
|
||||
for debug_event in self._debug_event_generator():
|
||||
self._dispatch_debug_event(debug_event)
|
||||
self._finish_debug_event(debug_event, windef.DBG_CONTINUE)
|
||||
if not self.processes:
|
||||
# No More process to debug
|
||||
break
|
||||
|
||||
def add_bp(self, bp, addr=None, type=None, target=None):
|
||||
"""TODO: use type for hardware breakpoints"""
|
||||
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
|
||||
_setup_method = getattr(self, "_setup_breakpoint_" + bp.type)
|
||||
_setup_method(bp, targets)
|
||||
return True
|
||||
|
||||
# Public callback
|
||||
def on_exception(self, exception):
|
||||
pass
|
||||
|
||||
@@ -224,36 +337,26 @@ class Debugger(object):
|
||||
def on_rip(self, rip_info):
|
||||
pass
|
||||
|
||||
# Public API
|
||||
def loop(self):
|
||||
for x, debug_event in enumerate(self._debug_event_generator()):
|
||||
self._dispatch_debug_event(debug_event)
|
||||
self._finish_debug_event(debug_event, windef.DBG_CONTINUE)
|
||||
if not self.processes:
|
||||
# No More process to debug
|
||||
break
|
||||
|
||||
def add_bp(self, bp, addr=None, target=None):
|
||||
"""TODO: use type for hardware breakpoints"""
|
||||
call_target = bp
|
||||
if getattr(bp, "addr", None) is not None:
|
||||
addr = bp.addr
|
||||
call_target = bp.trigger
|
||||
# if addr is not None: raise ?
|
||||
|
||||
# Non object breakpoint
|
||||
if addr is None:
|
||||
raise ValueError("No address: need a valid <bp.addr> or <addr> parameter")
|
||||
self.breakpoints[addr] = call_target
|
||||
self._setup_breakpoint(addr, bp.type, target)
|
||||
return True
|
||||
|
||||
|
||||
class Breakpoint(object):
|
||||
type = 0 # REAL BP
|
||||
type = "BP" # REAL BP
|
||||
def __init__(self, addr):
|
||||
self.addr = addr
|
||||
|
||||
def trigger(self, exception):
|
||||
def trigger(self, dbg, exception):
|
||||
pass
|
||||
|
||||
class ProxyBreakpoint(Breakpoint):
|
||||
def __init__(self, target, addr, type):
|
||||
self.target = target
|
||||
self.addr = addr
|
||||
self.type = type
|
||||
|
||||
def trigger(self, dbg, exception):
|
||||
return self.target(dbg, exception)
|
||||
|
||||
class HXBreakpoint(Breakpoint):
|
||||
type = HARDWARE_EXEC_BP
|
||||
|
||||
|
||||
|
||||
|
||||
+156
-9
@@ -8,9 +8,12 @@ from contextlib import contextmanager
|
||||
sys.path.append(".")
|
||||
import unittest
|
||||
import windows
|
||||
import windows.debug
|
||||
import windows.native_exec.simple_x86 as x86
|
||||
import windows.native_exec.simple_x64 as x64
|
||||
|
||||
from windows.generated_def.winstructs import *
|
||||
|
||||
is_process_32_bits = windows.current_process.bitness == 32
|
||||
is_process_64_bits = windows.current_process.bitness == 64
|
||||
|
||||
@@ -25,22 +28,22 @@ process_64bit_only = unittest.skipIf(not is_process_64_bits, "Test for 64bits pr
|
||||
|
||||
|
||||
if is_windows_32_bits:
|
||||
def pop_calc_32():
|
||||
return windows.utils.create_process(r"C:\Windows\system32\calc.exe", show_windows=True)
|
||||
def pop_calc_32(dwCreationFlags):
|
||||
return windows.utils.create_process(r"C:\Windows\system32\calc.exe", dwCreationFlags=dwCreationFlags, show_windows=True)
|
||||
|
||||
def pop_calc_64():
|
||||
def pop_calc_64(dwCreationFlags):
|
||||
raise WindowsError("Cannot create calc64 in 32bits system")
|
||||
else:
|
||||
def pop_calc_32():
|
||||
return windows.utils.create_process(r"C:\Windows\syswow64\calc.exe", show_windows=True)
|
||||
def pop_calc_32(dwCreationFlags):
|
||||
return windows.utils.create_process(r"C:\Windows\syswow64\calc.exe", dwCreationFlags=dwCreationFlags, show_windows=True)
|
||||
|
||||
if is_process_32_bits:
|
||||
def pop_calc_64():
|
||||
def pop_calc_64(dwCreationFlags):
|
||||
with windows.utils.DisableWow64FsRedirection():
|
||||
return windows.utils.create_process(r"C:\Windows\system32\calc.exe", show_windows=True)
|
||||
return windows.utils.create_process(r"C:\Windows\system32\calc.exe", dwCreationFlags=dwCreationFlags, show_windows=True)
|
||||
else:
|
||||
def pop_calc_64():
|
||||
return windows.utils.create_process(r"C:\Windows\system32\calc.exe", show_windows=True)
|
||||
def pop_calc_64(dwCreationFlags):
|
||||
return windows.utils.create_process(r"C:\Windows\system32\calc.exe", dwCreationFlags=dwCreationFlags, show_windows=True)
|
||||
|
||||
|
||||
@contextmanager
|
||||
@@ -298,10 +301,154 @@ class WindowsAPITestCase(unittest.TestCase):
|
||||
windows.winproxy.CreateFileA("NONEXISTFILE.FILE")
|
||||
|
||||
|
||||
class DebuggerTestCase(unittest.TestCase):
|
||||
|
||||
def debuggable_calc_32(self):
|
||||
return windows.utils.create_process(r"C:\python27\python.exe", dwCreationFlags=DEBUG_PROCESS | CREATE_NEW_CONSOLE, show_windows=True)
|
||||
|
||||
def test_init_breakpoint_callback(self):
|
||||
TEST_CASE = self
|
||||
class MyDbg(windows.debug.Debugger):
|
||||
def on_exception(self, exception):
|
||||
TEST_CASE.assertEqual(exception.ExceptionRecord.ExceptionCode, EXCEPTION_BREAKPOINT)
|
||||
self.current_process.exit()
|
||||
|
||||
calc = pop_calc_32(dwCreationFlags=DEBUG_PROCESS)
|
||||
d = MyDbg(calc)
|
||||
d.loop()
|
||||
|
||||
def test_simple_standard_breakpoint(self):
|
||||
TEST_CASE = self
|
||||
|
||||
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)
|
||||
d = windows.debug.Debugger(calc)
|
||||
d.add_bp(TSTBP(windows.current_process.peb.modules[1].pe.exports["LdrLoadDll"]))
|
||||
d.loop()
|
||||
|
||||
def test_simple_hwx_breakpoint(self):
|
||||
TEST_CASE = self
|
||||
|
||||
class TSTBP(windows.debug.HXBreakpoint):
|
||||
def trigger(self, dbg, exc):
|
||||
TEST_CASE.assertEqual(dbg.current_process.pid, calc.pid)
|
||||
TEST_CASE.assertEqual(dbg.current_thread.context.pc, self.addr)
|
||||
TEST_CASE.assertNotEqual(dbg.current_thread.context.Dr7, 0)
|
||||
d.current_process.exit()
|
||||
|
||||
calc = pop_calc_32(dwCreationFlags=DEBUG_PROCESS)
|
||||
d = windows.debug.Debugger(calc)
|
||||
d.add_bp(TSTBP(windows.current_process.peb.modules[1].pe.exports["LdrLoadDll"]))
|
||||
d.loop()
|
||||
|
||||
def test_multiple_hwx_breakpoint(self):
|
||||
TEST_CASE = self
|
||||
data = [0]
|
||||
class TSTBP(windows.debug.HXBreakpoint):
|
||||
def __init__(self, addr, expec_before):
|
||||
self.addr = addr
|
||||
self.expec_before = expec_before
|
||||
|
||||
def trigger(self, dbg, exc):
|
||||
TEST_CASE.assertEqual(dbg.current_process.pid, calc.pid)
|
||||
TEST_CASE.assertEqual(dbg.current_thread.context.pc, self.addr)
|
||||
TEST_CASE.assertNotEqual(dbg.current_thread.context.Dr7, 0)
|
||||
TEST_CASE.assertEqual(data[0], self.expec_before)
|
||||
TEST_CASE.assertNotEqual(dbg.current_process.read_memory(self.addr, 1), "\xcc")
|
||||
data[0] += 1
|
||||
if data[0] == 4:
|
||||
d.current_process.exit()
|
||||
|
||||
calc = pop_calc_32(dwCreationFlags=DEBUG_PROCESS)
|
||||
d = windows.debug.Debugger(calc)
|
||||
addr = calc.virtual_alloc(0x1000)
|
||||
calc.write_memory(addr, "\x90" * 8)
|
||||
d.add_bp(TSTBP(addr, 0))
|
||||
d.add_bp(TSTBP(addr + 1, 1))
|
||||
d.add_bp(TSTBP(addr + 2, 2))
|
||||
d.add_bp(TSTBP(addr + 3, 3))
|
||||
|
||||
calc.create_thread(addr, 0)
|
||||
d.loop()
|
||||
# Used to verif we actually called the Breakpoints
|
||||
TEST_CASE.assertEqual(data[0], 4)
|
||||
|
||||
def test_four_hwx_breakpoint_fail(self):
|
||||
TEST_CASE = self
|
||||
data = [0]
|
||||
|
||||
class TSTBP(windows.debug.HXBreakpoint):
|
||||
def __init__(self, addr, expec_before):
|
||||
self.addr = addr
|
||||
self.expec_before = expec_before
|
||||
|
||||
def trigger(self, dbg, exc):
|
||||
raise NotImplementedError("Should fail before")
|
||||
|
||||
calc = pop_calc_32(dwCreationFlags=DEBUG_PROCESS)
|
||||
d = windows.debug.Debugger(calc)
|
||||
addr = calc.virtual_alloc(0x1000)
|
||||
calc.write_memory(addr, "\x90" * 8)
|
||||
d.add_bp(TSTBP(addr, 0))
|
||||
d.add_bp(TSTBP(addr + 1, 1))
|
||||
d.add_bp(TSTBP(addr + 2, 2))
|
||||
d.add_bp(TSTBP(addr + 3, 3))
|
||||
d.add_bp(TSTBP(addr + 4, 4))
|
||||
|
||||
calc.create_thread(addr, 0)
|
||||
with self.assertRaises(ValueError) as e:
|
||||
d.loop()
|
||||
self.assertIn("DRx", e.exception.message)
|
||||
# Used to verif we actually NOT called the Breakpoints
|
||||
TEST_CASE.assertEqual(data[0], 0)
|
||||
|
||||
def test_hwx_breakpoint_are_on_all_thread(self):
|
||||
TEST_CASE = self
|
||||
data = [0]
|
||||
|
||||
class MyDbg(windows.debug.Debugger):
|
||||
def on_create_thread(self, exception):
|
||||
# Check that later created thread have their HWX breakpoint :)
|
||||
TEST_CASE.assertNotEqual(self.current_thread.context.Dr7, 0)
|
||||
|
||||
class TSTBP(windows.debug.HXBreakpoint):
|
||||
def __init__(self, addr, expec_before):
|
||||
self.addr = addr
|
||||
self.expec_before = expec_before
|
||||
|
||||
def trigger(self, dbg, exc):
|
||||
TEST_CASE.assertNotEqual(len(dbg.current_process.threads), 1)
|
||||
for t in dbg.current_process.threads:
|
||||
print(hex(t.context.Dr7))
|
||||
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)
|
||||
else:
|
||||
d.current_process.exit()
|
||||
|
||||
calc = pop_calc_32(dwCreationFlags=DEBUG_PROCESS)
|
||||
d = MyDbg(calc)
|
||||
addr = calc.virtual_alloc(0x1000)
|
||||
calc.write_memory(addr, "\x90" * 2)
|
||||
d.add_bp(TSTBP(addr, 0))
|
||||
calc.create_thread(addr, 0)
|
||||
d.loop()
|
||||
# Used to verif we actually called the Breakpoints
|
||||
TEST_CASE.assertEqual(data[0], 1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
alltests = unittest.TestSuite()
|
||||
alltests.addTest(unittest.makeSuite(WindowsTestCase))
|
||||
alltests.addTest(unittest.makeSuite(WindowsAPITestCase))
|
||||
alltests.addTest(unittest.makeSuite(DebuggerTestCase))
|
||||
alltests.debug()
|
||||
tester = unittest.TextTestRunner(verbosity=2)
|
||||
tester.run(alltests)
|
||||
|
||||
+129
-39
@@ -63,41 +63,92 @@ class EEXCEPTION_DEBUG_INFO32(ctypes.Structure):
|
||||
class EEXCEPTION_DEBUG_INFO64(ctypes.Structure):
|
||||
_fields_ = windows.utils.transform_ctypes_fields(EXCEPTION_DEBUG_INFO, {"ExceptionRecord": EnhancedEXCEPTION_RECORD64})
|
||||
|
||||
class Eflags(int):
|
||||
_flags_ = [("CF", 1),
|
||||
("RES_1", 1),
|
||||
("PF", 1),
|
||||
("RES_3", 1),
|
||||
("AF", 1),
|
||||
("RES_5", 1),
|
||||
("ZF", 1),
|
||||
("SF", 1),
|
||||
("TF", 1),
|
||||
("IF", 1),
|
||||
("DF", 1),
|
||||
("OF", 1),
|
||||
("IOPL_1", 1),
|
||||
("IOPL_2", 1),
|
||||
("NT", 1),
|
||||
("RES_15", 1),
|
||||
("RF", 1),
|
||||
("VM", 1),
|
||||
("AC", 1),
|
||||
("VIF", 1),
|
||||
("VIP", 1),
|
||||
("ID", 1),
|
||||
#class Eflags(int):
|
||||
# _flags_ = [("CF", 1),
|
||||
# ("RES_1", 1),
|
||||
# ("PF", 1),
|
||||
# ("RES_3", 1),
|
||||
# ("AF", 1),
|
||||
# ("RES_5", 1),
|
||||
# ("ZF", 1),
|
||||
# ("SF", 1),
|
||||
# ("TF", 1),
|
||||
# ("IF", 1),
|
||||
# ("DF", 1),
|
||||
# ("OF", 1),
|
||||
# ("IOPL_1", 1),
|
||||
# ("IOPL_2", 1),
|
||||
# ("NT", 1),
|
||||
# ("RES_15", 1),
|
||||
# ("RF", 1),
|
||||
# ("VM", 1),
|
||||
# ("AC", 1),
|
||||
# ("VIF", 1),
|
||||
# ("VIP", 1),
|
||||
# ("ID", 1),
|
||||
# ]
|
||||
#
|
||||
# _flag_mask_ = dict([(name, 1 << i) for i, (name, size) in enumerate(_flags_)])
|
||||
#
|
||||
# def __getattr__(self, name):
|
||||
# if name in self._flag_mask_:
|
||||
# return bool(self & self._flag_mask_[name])
|
||||
# return super(Eflags, self).__getattr_(name)
|
||||
#
|
||||
# def dump(self):
|
||||
# res = []
|
||||
# for name in self._flag_mask_:
|
||||
# if name.startswith("RES_"):
|
||||
# continue
|
||||
# if getattr(self, name):
|
||||
# res.append(name)
|
||||
# return "|".join(res)
|
||||
#
|
||||
# def __repr__(self):
|
||||
# return "{0}({1})".format(type(self).__name__, self.dump())
|
||||
#
|
||||
# __str__ = __repr__
|
||||
#
|
||||
# def __hex__(self):
|
||||
# return "{0}({1}:{2})".format(type(self).__name__, int.__hex__(self), self.dump())
|
||||
|
||||
class EEflags(ctypes.Structure):
|
||||
_fields_ = [("CF", DWORD, 1),
|
||||
("RES_1", DWORD, 1),
|
||||
("PF", DWORD, 1),
|
||||
("RES_3", DWORD, 1),
|
||||
("AF", DWORD, 1),
|
||||
("RES_5", DWORD, 1),
|
||||
("ZF", DWORD, 1),
|
||||
("SF", DWORD, 1),
|
||||
("TF", DWORD, 1),
|
||||
("IF", DWORD, 1),
|
||||
("DF", DWORD, 1),
|
||||
("OF", DWORD, 1),
|
||||
("IOPL_1", DWORD, 1),
|
||||
("IOPL_2", DWORD, 1),
|
||||
("NT", DWORD, 1),
|
||||
("RES_15", DWORD, 1),
|
||||
("RF", DWORD, 1),
|
||||
("VM", DWORD, 1),
|
||||
("AC", DWORD, 1),
|
||||
("VIF", DWORD, 1),
|
||||
("VIP", DWORD, 1),
|
||||
("ID", DWORD, 1),
|
||||
]
|
||||
|
||||
_flag_mask_ = dict([(name, 1 << i) for i, (name, size) in enumerate(_flags_)])
|
||||
def get_raw(self):
|
||||
x = DWORD.from_address(ctypes.addressof(self))
|
||||
return x.value
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name in self._flag_mask_:
|
||||
return bool(self & self._flag_mask_[name])
|
||||
return super(Eflags, self).__getattr_(name)
|
||||
def set_raw(self, value):
|
||||
x = DWORD.from_address(ctypes.addressof(self))
|
||||
x.value = value
|
||||
return None
|
||||
|
||||
def dump(self):
|
||||
res = []
|
||||
for name in self._flag_mask_:
|
||||
for name in [x[0] for x in self._fields_]:
|
||||
if name.startswith("RES_"):
|
||||
continue
|
||||
if getattr(self, name):
|
||||
@@ -105,12 +156,38 @@ class Eflags(int):
|
||||
return "|".join(res)
|
||||
|
||||
def __repr__(self):
|
||||
return "{0}({1})".format(type(self).__name__, self.dump())
|
||||
|
||||
__str__ = __repr__
|
||||
return hex(self)
|
||||
|
||||
def __hex__(self):
|
||||
return "{0}({1}:{2})".format(type(self).__name__, int.__hex__(self), self.dump())
|
||||
if self.raw == 0:
|
||||
return "{0}({1})".format(type(self).__name__, hex(self.raw))
|
||||
return "{0}({1}:{2})".format(type(self).__name__, hex(self.raw), self.dump())
|
||||
|
||||
raw = property(get_raw, set_raw)
|
||||
|
||||
class EDr7(ctypes.Structure):
|
||||
_fields_ = [("L0", DWORD, 1),
|
||||
("G0", DWORD, 1),
|
||||
("L1", DWORD, 1),
|
||||
("G1", DWORD, 1),
|
||||
("L2", DWORD, 1),
|
||||
("G2", DWORD, 1),
|
||||
("L3", DWORD, 1),
|
||||
("G3", DWORD, 1),
|
||||
("LE", DWORD, 1),
|
||||
("GE", DWORD, 1),
|
||||
("RES_1", DWORD, 3),
|
||||
("GD", DWORD, 1),
|
||||
("RES_1", DWORD, 2),
|
||||
("RW0", DWORD, 2),
|
||||
("LEN0", DWORD, 2),
|
||||
("RW1", DWORD, 2),
|
||||
("LEN1", DWORD, 2),
|
||||
("RW2", DWORD, 2),
|
||||
("LEN2", DWORD, 2),
|
||||
("RW3", DWORD, 2),
|
||||
("LEN3", DWORD, 2),
|
||||
]
|
||||
|
||||
class EnhancedCONTEXTBase():
|
||||
default_dump = ()
|
||||
@@ -142,23 +219,36 @@ class EnhancedCONTEXTBase():
|
||||
|
||||
pc = property(get_pc, set_pc, None, "Program Counter register (EIP or RIP)")
|
||||
|
||||
@property
|
||||
def EEFlags(self):
|
||||
off = type(self).EFlags.offset
|
||||
x = EEflags.from_address(ctypes.addressof(self) + off)
|
||||
x.self = self
|
||||
return x
|
||||
|
||||
class EnhancedCONTEXT32(EnhancedCONTEXTBase, CONTEXT32):
|
||||
@property
|
||||
def EDr7(self):
|
||||
off = type(self).Dr7.offset
|
||||
x = EDr7.from_address(ctypes.addressof(self) + off)
|
||||
x.self = self
|
||||
return x
|
||||
|
||||
class EnhancedCONTEXT32(EnhancedCONTEXTBase, (CONTEXT32)):
|
||||
default_dump = ('Eip', 'Esp', 'Eax', 'Ebx', 'Ecx', 'Edx', 'Ebp', 'Edi', 'Esi', 'EFlags')
|
||||
pc_reg = 'Eip'
|
||||
special_reg_type = {'EFlags': Eflags}
|
||||
#special_reg_type = {'EFlags': Eflags}
|
||||
|
||||
class EnhancedCONTEXTWOW64(EnhancedCONTEXTBase, WOW64_CONTEXT):
|
||||
class EnhancedCONTEXTWOW64(EnhancedCONTEXTBase, (WOW64_CONTEXT)):
|
||||
default_dump = ('Eip', 'Esp', 'Eax', 'Ebx', 'Ecx', 'Edx', 'Ebp', 'Edi', 'Esi', 'EFlags')
|
||||
pc_reg = 'Eip'
|
||||
special_reg_type = {'EFlags': Eflags}
|
||||
#special_reg_type = {'EFlags': Eflags}
|
||||
|
||||
|
||||
class EnhancedCONTEXT64(EnhancedCONTEXTBase, CONTEXT64):
|
||||
class EnhancedCONTEXT64(EnhancedCONTEXTBase, (CONTEXT64)):
|
||||
default_dump = ('Rip', 'Rsp', 'Rax', 'Rbx', 'Rcx', 'Rdx', 'Rbp', 'Rdi', 'Rsi',
|
||||
'R9', 'R10', 'R11', 'R12', 'R13', 'R14', 'R15', 'EFlags')
|
||||
pc_reg = 'Rip'
|
||||
special_reg_type = {'EFlags': Eflags}
|
||||
#special_reg_type = {'EFlags': Eflags}
|
||||
|
||||
@classmethod
|
||||
def new_aligned(cls):
|
||||
|
||||
+25
-6
@@ -3,6 +3,7 @@ import os
|
||||
import copy
|
||||
import time
|
||||
import struct
|
||||
import itertools
|
||||
|
||||
import windows
|
||||
import windows.network
|
||||
@@ -52,7 +53,6 @@ class AutoHandle(object):
|
||||
|
||||
def __del__(self):
|
||||
if hasattr(self, "_handle") and self._handle:
|
||||
print("Del HANDLE {0} ({1})".format((self), self._handle))
|
||||
self.CLOSE_FUNCTION(self._handle)
|
||||
|
||||
|
||||
@@ -147,13 +147,13 @@ class WinThread(THREADENTRY32, AutoHandle):
|
||||
if self.owner.bitness == 32 and windows.current_process.bitness == 64:
|
||||
# Wow64
|
||||
x = windows.vectored_exception.EnhancedCONTEXTWOW64()
|
||||
x.ContextFlags = CONTEXT_FULL
|
||||
x.ContextFlags = CONTEXT_ALL
|
||||
winproxy.Wow64GetThreadContext(self.handle, x)
|
||||
return x
|
||||
|
||||
if self.owner.bitness == 64 and windows.current_process.bitness == 32:
|
||||
x = windows.vectored_exception.EnhancedCONTEXT64.new_aligned()
|
||||
x.ContextFlags = CONTEXT_FULL
|
||||
x.ContextFlags = CONTEXT_ALL
|
||||
windows.syswow64.NtGetContextThread_32_to_64(self.handle, x)
|
||||
return x
|
||||
|
||||
@@ -161,7 +161,7 @@ class WinThread(THREADENTRY32, AutoHandle):
|
||||
x = windows.vectored_exception.EnhancedCONTEXT32()
|
||||
else:
|
||||
x = windows.vectored_exception.EnhancedCONTEXT64.new_aligned()
|
||||
x.ContextFlags = CONTEXT_FULL
|
||||
x.ContextFlags = CONTEXT_ALL
|
||||
winproxy.GetThreadContext(self.handle, x)
|
||||
return x
|
||||
|
||||
@@ -234,7 +234,6 @@ class WinThread(THREADENTRY32, AutoHandle):
|
||||
# Really useful ?
|
||||
thread = [t for t in System().threads if t.tid == tid][0]
|
||||
# set AutoHandle _handle
|
||||
print("New thread from handle {0}".format(handle))
|
||||
thread._handle = handle
|
||||
return thread
|
||||
except IndexError:
|
||||
@@ -519,7 +518,6 @@ class WinProcess(PROCESSENTRY32, Process):
|
||||
pid = winproxy.GetProcessId(handle)
|
||||
proc = [p for p in windows.system.processes if p.pid == pid][0]
|
||||
proc._handle = handle
|
||||
print("New Process from handle {0}".format(handle))
|
||||
return proc
|
||||
|
||||
|
||||
@@ -600,6 +598,27 @@ class WinProcess(PROCESSENTRY32, Process):
|
||||
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
|
||||
#
|
||||
|
||||
Reference in New Issue
Block a user