diff --git a/Ghidra/Debug/Debugger-agent-x64dbg/src/main/py/src/ghidraxdbg/commands.py b/Ghidra/Debug/Debugger-agent-x64dbg/src/main/py/src/ghidraxdbg/commands.py index 5d3cdca851..6955bc7c9c 100644 --- a/Ghidra/Debug/Debugger-agent-x64dbg/src/main/py/src/ghidraxdbg/commands.py +++ b/Ghidra/Debug/Debugger-agent-x64dbg/src/main/py/src/ghidraxdbg/commands.py @@ -838,6 +838,8 @@ def ghidra_trace_disassemble(address: Union[str, int]) -> None: def compute_proc_state(nproc: Optional[int] = None) -> str: + if nproc is None: + return 'TERMINATED' try: if util.dbg.client.is_running(): return 'RUNNING' @@ -873,10 +875,12 @@ def put_processes(running: bool = False) -> None: def put_state(event_process: int) -> None: + state = compute_proc_state(event_process) + if event_process is None: + event_process = util.last_process ipath = PROCESS_PATTERN.format(procnum=event_process) trace = STATE.require_trace() procobj = trace.create_object(ipath) - state = compute_proc_state(event_process) procobj.set_value('State', state) procobj.insert() tnum = util.selected_thread() diff --git a/Ghidra/Debug/Debugger-agent-x64dbg/src/main/py/src/ghidraxdbg/hooks.py b/Ghidra/Debug/Debugger-agent-x64dbg/src/main/py/src/ghidraxdbg/hooks.py index df8f69d9dc..c8c53fd279 100644 --- a/Ghidra/Debug/Debugger-agent-x64dbg/src/main/py/src/ghidraxdbg/hooks.py +++ b/Ghidra/Debug/Debugger-agent-x64dbg/src/main/py/src/ghidraxdbg/hooks.py @@ -105,16 +105,14 @@ class ProcessState: except Exception: pass - def record_exited(self, description: Optional[str] = None, + def record_exited(self, exit_code: Optional[str] = None, time: Optional[Schedule] = None) -> None: - # print("RECORD_EXITED") trace = commands.STATE.require_trace() - if description is not None: - trace.snapshot(description, time=time) - proc = util.selected_process() - ipath = commands.PROCESS_PATTERN.format(procnum=proc) + if exit_code is not None: + trace.snapshot(f"Exited {exit_code}", time=time) + ipath = commands.PROCESS_PATTERN.format(procnum=util.last_process) procobj = trace.proxy_object_path(ipath) - #procobj.set_value('Exit Code', exit_code) + procobj.set_value('Exit Code', exit_code) procobj.set_value('State', 'TERMINATED') @@ -165,13 +163,15 @@ def on_state_changed(*args) -> None: ev_type = args[0].event_type # print(ev_type) proc = util.selected_process() - if proc not in PROC_STATE: - return - PROC_STATE[proc].waiting = False trace = commands.STATE.require_trace() with trace.client.batch(): with trace.open_tx("State changed proc {}".format(proc)): commands.put_state(proc) + if proc not in PROC_STATE: + if ev_type == EventType.EVENT_EXIT_PROCESS: + on_process_deleted(args) + return + PROC_STATE[proc].waiting = False try: if ev_type == EventType.EVENT_RESUME_DEBUG: on_cont() @@ -219,10 +219,9 @@ def on_process_selected() -> None: @log_errors def on_process_deleted(*args) -> None: - # print("ON_PROCESS_DELETED") - exit_code = args[0] + # print("PROCESS_DELETED: args={}".format(args)) proc = util.selected_process() - on_exited(proc) + on_exited(args) if proc in PROC_STATE: del PROC_STATE[proc] trace = commands.STATE.trace @@ -342,20 +341,17 @@ def on_stop(*args) -> None: commands.activate() -def on_exited(proc) -> None: +def on_exited(*args) -> None: # print("ON EXITED") - if proc not in PROC_STATE: - # print("not in state") - return trace = commands.STATE.trace if trace is None: return - state = PROC_STATE[proc] + state = PROC_STATE[util.last_process] state.visited.clear() - description = "Exited" with trace.client.batch(): with trace.open_tx("Exited"): - state.record_exited(description) + exit_code = args[0][0].event_data.dwExitCode + state.record_exited(exit_code) commands.activate() @@ -394,7 +390,12 @@ def install_hooks() -> None: dbg.watch_debug_event(EventType.EVENT_STEPPED, lambda x: on_state_changed(x)) dbg.watch_debug_event(EventType.EVENT_PAUSE_DEBUG, lambda x: on_state_changed(x)) dbg.watch_debug_event(EventType.EVENT_RESUME_DEBUG, lambda x: on_state_changed(x)) - #dbg.watch_debug_event(EventType.EVENT_DEBUG, lambda x: on_state_changed(x)) + dbg.watch_debug_event(EventType.EVENT_ATTACH, lambda x: on_state_changed(x)) + dbg.watch_debug_event(EventType.EVENT_DETACH, lambda x: on_state_changed(x)) + dbg.watch_debug_event(EventType.EVENT_INIT_DEBUG, lambda x: on_state_changed(x)) + dbg.watch_debug_event(EventType.EVENT_STOP_DEBUG, lambda x: on_state_changed(x)) + dbg.watch_debug_event(EventType.EVENT_CREATE_PROCESS, lambda x: on_state_changed(x)) + dbg.watch_debug_event(EventType.EVENT_EXIT_PROCESS, lambda x: on_state_changed(x)) def remove_hooks() -> None: @@ -410,6 +411,7 @@ def enable_current_process() -> None: def disable_current_process() -> None: + # print("Disable current process") proc = util.selected_process() if proc in PROC_STATE: # Silently ignore already disabled diff --git a/Ghidra/Debug/Debugger-agent-x64dbg/src/main/py/src/ghidraxdbg/util.py b/Ghidra/Debug/Debugger-agent-x64dbg/src/main/py/src/ghidraxdbg/util.py index f28b00c339..0cbfb89ff0 100644 --- a/Ghidra/Debug/Debugger-agent-x64dbg/src/main/py/src/ghidraxdbg/util.py +++ b/Ghidra/Debug/Debugger-agent-x64dbg/src/main/py/src/ghidraxdbg/util.py @@ -102,6 +102,7 @@ def compute_dbg_ver() -> DbgVersion: DBG_VERSION = compute_dbg_ver() +last_process = None def get_target(): @@ -124,12 +125,14 @@ def get_inst_sz(addr: int) -> int: def selected_process() -> int: + global last_process try: pid = dbg.client.debugee_pid() + if pid is not None: + last_process = pid return pid except: - # NB: we're intentionally returning 0 instead of None - return 0 + return None def selected_process_space() -> int: @@ -202,11 +205,13 @@ def process_list0(running: bool = False) -> Union[ Iterable[Tuple[int, str, int]], Iterable[Tuple[int]]]: """Get the list of all processes.""" nproc = selected_process() - proc = psutil.Process(nproc) sysids = [] names = [] + if nproc is None: + return zip(sysids) try: + proc = psutil.Process(nproc) sysids.append(nproc) names.append(proc.name()) return zip(sysids, names) diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/x64dbg/rmi/AbstractX64dbgTraceRmiTest.java b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/x64dbg/rmi/AbstractX64dbgTraceRmiTest.java index 61f7e00923..90d84781e4 100644 --- a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/x64dbg/rmi/AbstractX64dbgTraceRmiTest.java +++ b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/x64dbg/rmi/AbstractX64dbgTraceRmiTest.java @@ -71,6 +71,7 @@ public abstract class AbstractX64dbgTraceRmiTest extends AbstractGhidraHeadedDeb protected static boolean didSetupPython = false; public static final String NOTEPAD = "C:\\\\Windows\\\\notepad.exe"; + public static final String NETSTAT = "C:\\\\Windows\\\\System32\\\\netstat.exe"; public static final String INSTRUMENT_STATE = """ import sys from ghidraxdbg import commands diff --git a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/x64dbg/rmi/X64dbgMethodsTest.java b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/x64dbg/rmi/X64dbgMethodsTest.java index 8e1ce77221..6d62a3cc7a 100644 --- a/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/x64dbg/rmi/X64dbgMethodsTest.java +++ b/Ghidra/Test/DebuggerIntegrationTest/src/test.slow/java/agent/x64dbg/rmi/X64dbgMethodsTest.java @@ -391,7 +391,7 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { @Test public void testRemoveProcess() throws Exception { try (PythonAndConnection conn = startAndConnectPython()) { - start(conn, "netstat.exe"); + start(conn, NETSTAT); txPut(conn, "processes"); RemoteMethod removeProcess = conn.getMethod("remove_process"); @@ -404,7 +404,7 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { String out = conn.executeCapture("print(list(util.process_list0()))"); conn.execute("util.terminate_session()"); - assertThat(out, containsString("python.exe")); + assertEquals(out, "[]\n"); } } } @@ -465,7 +465,7 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { @Test public void testDetach() throws Exception { try (PythonAndConnection conn = startAndConnectPython()) { - start(conn, "netstat.exe"); + start(conn, NETSTAT); txPut(conn, "processes"); RemoteMethod detach = conn.getMethod("detach"); @@ -525,7 +525,7 @@ public class X64dbgMethodsTest extends AbstractX64dbgTraceRmiTest { String out = conn.executeCapture("print(list(util.process_list0()))"); conn.execute("util.terminate_session()"); - assertThat(out, containsString("python.exe")); + assertEquals(out, "[]\n"); } } }