From eb2d7fcc80dae8f23a0a8984dbcf893edc8e8554 Mon Sep 17 00:00:00 2001 From: d-millar <33498836+d-millar@users.noreply.github.com> Date: Thu, 29 Jan 2026 19:37:10 -0500 Subject: [PATCH] GP-6374: main logic --- .../src/main/py/src/ghidradbg/commands.py | 13 +++++ .../src/main/py/src/ghidradbg/hooks.py | 17 ++++++ .../src/main/py/src/ghidradbg/methods.py | 52 +++++++++++++++++++ .../src/main/py/src/ghidradbg/util.py | 11 ++++ 4 files changed, 93 insertions(+) diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/py/src/ghidradbg/commands.py b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/py/src/ghidradbg/commands.py index ba720d8170..812c2f725c 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/py/src/ghidradbg/commands.py +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/py/src/ghidradbg/commands.py @@ -1068,6 +1068,10 @@ def put_single_breakpoint(bp, ibobj, nproc: int, ikeys: List[str]) -> None: tid = "%04x" % tid except exception.E_NOINTERFACE_Error: tid = "****" + try: + handler = util.BPT_HANDLERS[bp.GetId()] + except Exception: + handler = "" if bp.GetType()[0] == DbgEng.DEBUG_BREAKPOINT_DATA: width, prot = bp.GetDataParameters() @@ -1103,6 +1107,8 @@ def put_single_breakpoint(bp, ibobj, nproc: int, ikeys: List[str]) -> None: brkobj.set_value('Flags', bp.GetFlags()) if tid != None: brkobj.set_value('Match TID', tid) + if handler != None: + brkobj.set_value('Handler', handler) brkobj.set_value('Command', bp.GetCommand()) brkobj.insert() @@ -1529,6 +1535,11 @@ def put_single_exception(obj: TraceObject, objpath: str, exc_cmd2 = util.GetExceptionFilterSecondCommand( offset + index, p.SecondCommandSize) exc_code = hex(p.ExceptionCode) + try: + util.EXC_CODES[index] = exc_code + handler = util.EXC_HANDLERS[exc_code] + except Exception: + handler = "" obj.set_value('Code', exc_code) trace = STATE.require_trace() contobj = trace.create_object(objpath+".Cont") @@ -1543,6 +1554,8 @@ def put_single_exception(obj: TraceObject, objpath: str, obj.set_value('Cmd', exc_cmd) if exc_cmd2 is not None: obj.set_value('Cmd2', exc_cmd2) + if handler is not None: + obj.set_value('Handler', handler) obj.set_value('_display', "{} {} [{}]".format(index, exc_name, exc_code)) obj.insert() diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/py/src/ghidradbg/hooks.py b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/py/src/ghidradbg/hooks.py index 94734f154c..409ceaf9e4 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/py/src/ghidradbg/hooks.py +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/py/src/ghidradbg/hooks.py @@ -382,6 +382,7 @@ def on_cont(*args) -> None: def on_stop(*args) -> None: + # print("ON STOP") proc = util.selected_process() if proc not in PROC_STATE: return @@ -505,12 +506,28 @@ def on_breakpoint_deleted(bpid) -> None: @log_errors def on_breakpoint_hit(*args) -> None: # print("ON_BREAKPOINT_HIT: args={}".format(args)) + id = util.get_breakpoint_id(args[0]) + if id in util.BPT_HANDLERS: + try: + handler = util.BPT_HANDLERS[id] + func = globals()[handler] + func(*args) + except Exception as e: + print(f"Error in breakpoint handler: {e}") return DbgEng.DEBUG_STATUS_NO_CHANGE @log_errors def on_exception(*args) -> None: # print("ON_EXCEPTION: args={}".format(args)) + id = str(hex(args[0][0].ExceptionCode)) + if id in util.EXC_HANDLERS: + try: + handler = util.EXC_HANDLERS[id] + func = globals()[handler] + func(*args) + except Exception as e: + print(f"Error in exception handler: {e}") return DbgEng.DEBUG_STATUS_NO_CHANGE diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/py/src/ghidradbg/methods.py b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/py/src/ghidradbg/methods.py index 27f18d316e..d8b5bbe708 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/py/src/ghidradbg/methods.py +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/py/src/ghidradbg/methods.py @@ -321,6 +321,14 @@ class ExceptionContainer(TraceObject): pass +class Event(TraceObject): + pass + + +class Exception(TraceObject): + pass + + class ContinueOption(TraceObject): pass @@ -865,5 +873,49 @@ def refresh_trace_events_custom(node: State, commands.ghidra_trace_put_trace_events_custom(prefix, cmd) +@REGISTRY.method(action='add_handler', display='Add Handler') +def add_handler_breakpoint(node: BreakpointSpec, handler: str) -> None: + """ + Add python handler + """ + mat = PROC_BREAKBPT_PATTERN.fullmatch(node.path) + if mat is None: + raise TypeError(f"{node} is not {err_msg}") + bptnum = int(mat['breaknum']) + with commands.open_tracked_tx('Add Exception Handler'): + util.BPT_HANDLERS[bptnum] = handler + commands.ghidra_trace_put_breakpoints() + + +@REGISTRY.method(action='add_handler', display='Add Handler') +def add_handler_exception(node: Exception, handler: str) -> None: + """ + Add python handler + """ + mat = PROC_EXCEPTION_PATTERN.fullmatch(node.path) + if mat is None: + raise TypeError(f"{node} is not {err_msg}") + excnum = int(mat['excnum']) + with commands.open_tracked_tx('Add Exception Handler'): + exc_code = util.EXC_CODES[excnum] + util.EXC_HANDLERS[exc_code] = handler + commands.ghidra_trace_put_exceptions() + + +# TODO? +# @REGISTRY.method(action='add_handler', display='Add Handler') +# def add_handler_event(node: Event, handler: str) -> None: +# """ +# Add python handler +# """ +# mat = PROC_EVENT_PATTERN.fullmatch(node.path) +# if mat is None: +# raise TypeError(f"{node} is not {err_msg}") +# evtnum = int(mat['eventnum']) +# with commands.open_tracked_tx('Add Event Handler'): +# util.EVT_HANDLERS[evtnum] = handler +# commands.ghidra_trace_put_events() + + def dbg(): return util.dbg._base diff --git a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/py/src/ghidradbg/util.py b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/py/src/ghidradbg/util.py index 631cc59e52..d8823c3bb7 100644 --- a/Ghidra/Debug/Debugger-agent-dbgeng/src/main/py/src/ghidradbg/util.py +++ b/Ghidra/Debug/Debugger-agent-dbgeng/src/main/py/src/ghidradbg/util.py @@ -44,12 +44,17 @@ from pybag.dbgeng import core as DbgEng # type: ignore from pybag.dbgeng import exception # type: ignore from pybag.dbgeng import util as DbgUtil # type: ignore from pybag.dbgeng.callbacks import DbgEngCallbacks # type: ignore +from pybag.dbgeng.idebugbreakpoint import DebugBreakpoint # type: ignore from pybag.dbgeng.idebugclient import DebugClient # type: ignore DESCRIPTION_PATTERN = '[{major:X}:{minor:X}] {type}' DbgVersion = namedtuple('DbgVersion', ['full', 'name', 'dotted', 'arch']) +BPT_HANDLERS: Dict[int, str] = {} +EXC_CODES: Dict[int, str] = {} +EXC_HANDLERS: Dict[str, str] = {} +EVT_HANDLERS: Dict[int, str] = {} class StdInputCallbacks(CoClass): # This is the UUID listed for IDebugInputCallbacks in DbgEng.h @@ -501,6 +506,12 @@ def get_breakpoints() -> Iterable[Tuple[str, str, str, str, str]]: return zip(offset_set, expr_set, prot_set, width_set, stat_set) +@dbg.eng_thread +def get_breakpoint_id(bp: DbgEng.IDebugBreakpoint) -> int: + bpt = DebugBreakpoint(bp) + return bpt.GetId() + + @dbg.eng_thread def selected_process() -> int: try: