diff --git a/Ghidra/Debug/Debugger-agent-gdb/data/debugger-launchers/remote-gdb.sh b/Ghidra/Debug/Debugger-agent-gdb/data/debugger-launchers/remote-gdb.sh index 05046d1853..7dd2a94142 100755 --- a/Ghidra/Debug/Debugger-agent-gdb/data/debugger-launchers/remote-gdb.sh +++ b/Ghidra/Debug/Debugger-agent-gdb/data/debugger-launchers/remote-gdb.sh @@ -1,4 +1,4 @@ -#!/usr/bin/bash +#!/usr/bin/env bash ## ### # IP: GHIDRA # @@ -30,9 +30,11 @@ #@menu-group remote #@icon icon.debugger #@help TraceRmiLauncherServicePlugin#gdb -#@arg :str "Image" "The target binary executable image on the remote system" +#@enum TargetType:str remote extended-remote +#@env OPT_TARGET_TYPE:TargetType="remote" "Target" "The type of remote target" #@env OPT_HOST:str="localhost" "Host" "The hostname of the target" #@env OPT_PORT:str="9999" "Port" "The host's listening port" +#@env OPT_ARCH:str="" "Architecture (optional)" "Target architecture override" #@env OPT_GDB_PATH:str="gdb" "Path to gdb" "The path to gdb on the local system. Omit the full path to resolve using the system PATH." if [ -d ${GHIDRA_HOME}/ghidra/.git ] @@ -48,13 +50,21 @@ else export PYTHONPATH=$GHIDRA_HOME/Ghidra/Debug/Debugger-rmi-trace/pypkg/src:$PYTHONPATH fi +if [ -z "$OPT_ARCH" ] +then + archcmd= +else + archcmd=-ex "set arch $OPT_ARCH" +fi + "$OPT_GDB_PATH" \ -q \ -ex "set pagination off" \ -ex "set confirm off" \ -ex "show version" \ -ex "python import ghidragdb" \ - -ex "target remote $OPT_HOST:$OPT_PORT" \ + $archcmd \ + -ex "target $OPT_TARGET_TYPE $OPT_HOST:$OPT_PORT" \ -ex "ghidra trace connect \"$GHIDRA_TRACE_RMI_ADDR\"" \ -ex "ghidra trace start" \ -ex "ghidra trace sync-enable" \ diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/commands.py b/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/commands.py index 206564e567..da22814244 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/commands.py +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/commands.py @@ -1524,7 +1524,7 @@ def ghidra_trace_sync_synth_stopped(*, is_mi, **kwargs): """ hooks.on_stop(object()) # Pass a fake event - + @cmd('ghidra util wait-stopped', '-ghidra-util-wait-stopped', gdb.COMMAND_NONE, False) def ghidra_util_wait_stopped(timeout='1', *, is_mi, **kwargs): diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/hooks.py b/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/hooks.py index df9db85bb2..8756f98195 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/hooks.py +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/hooks.py @@ -541,18 +541,18 @@ def install_hooks(): HOOK_STATE.mem_catchpoint.enabled = True else: breaks_before = set(gdb.breakpoints()) - gdb.execute(""" - catch syscall group:memory - commands - silent - hooks-ghidra event-memory - cont - end - """) - bpts = gdb.breakpoints() - # NB: this is unnecessary for gdb 11+ - if len(bpts) > 0: - HOOK_STATE.mem_catchpoint = (set(bpts) - breaks_before).pop() + try: + gdb.execute(""" + catch syscall group:memory + commands + silent + hooks-ghidra event-memory + cont + end + """) + HOOK_STATE.mem_catchpoint = (set(gdb.breakpoints()) - breaks_before).pop() + except Exception as e: + print(f"Error setting memory catchpoint: {e}") gdb.events.cont.connect(on_cont) gdb.events.stop.connect(on_stop) diff --git a/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/methods.py b/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/methods.py index 677bee4648..83e86d0178 100644 --- a/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/methods.py +++ b/Ghidra/Debug/Debugger-agent-gdb/src/main/py/src/ghidragdb/methods.py @@ -74,7 +74,7 @@ THREADS_PATTERN = extre(INFERIOR_PATTERN, '\.Threads') THREAD_PATTERN = extre(THREADS_PATTERN, '\[(?P\\d*)\]') STACK_PATTERN = extre(THREAD_PATTERN, '\.Stack') FRAME_PATTERN = extre(STACK_PATTERN, '\[(?P\\d*)\]') -REGS_PATTERN = extre(FRAME_PATTERN, '.Registers') +REGS_PATTERN = extre(FRAME_PATTERN, '\.Registers') MEMORY_PATTERN = extre(INFERIOR_PATTERN, '\.Memory') MODULES_PATTERN = extre(INFERIOR_PATTERN, '\.Modules') MODULE_PATTERN = extre(MODULES_PATTERN, '\[(?P.*)\]') diff --git a/Ghidra/Debug/Debugger-agent-lldb/data/debugger-launchers/remote-lldb.sh b/Ghidra/Debug/Debugger-agent-lldb/data/debugger-launchers/remote-lldb.sh new file mode 100755 index 0000000000..8ab749e8ea --- /dev/null +++ b/Ghidra/Debug/Debugger-agent-lldb/data/debugger-launchers/remote-lldb.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash +## ### +# IP: GHIDRA +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +## +#@title remote lldb +#@desc +#@desc

Launch with local lldb and connect to a stub (e.g., gdbserver)

+#@desc

This will start lldb on the local system and then use it to connect to the remote system. +#@desc The actual command used is, e.g:

+#@desc
gdb-remote host:port
+#@desc

It may be worth testing this manually to ensure everything is configured correctly. +#@desc LLDB must be installed on your local system, it must be compatible with the remote system, +#@desc and it must embed the Python 3 interpreter. You will also need protobuf installed +#@desc for Python 3 on the local system. There are no Python requirements for the remote system. +#@desc Please ensure that Ghidra's current program and the target's image match. Otherwise, the +#@desc modules may not map.

+#@desc +#@menu-group remote +#@icon icon.debugger +#@help TraceRmiLauncherServicePlugin#lldb +#@env OPT_HOST:str="localhost" "Host" "The hostname of the target" +#@env OPT_PORT:str="9999" "Port" "The host's listening port" +#@env OPT_ARCH:str="" "Architecture" "Target architecture override" +#@env OPT_LLDB_PATH:str="lldb" "Path to lldb" "The path to lldb on the local system. Omit the full path to resolve using the system PATH." + +if [ -d ${GHIDRA_HOME}/ghidra/.git ] +then + export PYTHONPATH=$GHIDRA_HOME/ghidra/Ghidra/Debug/Debugger-agent-lldb/build/pypkg/src:$PYTHONPATH + export PYTHONPATH=$GHIDRA_HOME/ghidra/Ghidra/Debug/Debugger-rmi-trace/build/pypkg/src:$PYTHONPATH +elif [ -d ${GHIDRA_HOME}/.git ] +then + export PYTHONPATH=$GHIDRA_HOME/Ghidra/Debug/Debugger-agent-lldb/build/pypkg/src:$PYTHONPATH + export PYTHONPATH=$GHIDRA_HOME/Ghidra/Debug/Debugger-rmi-trace/build/pypkg/src:$PYTHONPATH +else + export PYTHONPATH=$GHIDRA_HOME/Ghidra/Debug/Debugger-agent-lldb/pypkg/src:$PYTHONPATH + export PYTHONPATH=$GHIDRA_HOME/Ghidra/Debug/Debugger-rmi-trace/pypkg/src:$PYTHONPATH +fi + +if [ -z "$OPT_ARCH" ] +then + archcmd= +else + archcmd=-o "settings set target.default-arch $OPT_ARCH" +fi + +"$OPT_LLDB_PATH" \ + -o "version" \ + -o "script import ghidralldb" \ + $archcmd \ + -o "gdb-remote $OPT_HOST:$OPT_PORT" \ + -o "ghidra trace connect \"$GHIDRA_TRACE_RMI_ADDR\"" \ + -o "ghidra trace start" \ + -o "ghidra trace sync-enable" \ + -o "ghidra trace sync-synth-stopped" + diff --git a/Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/arch.py b/Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/arch.py index e1eeb193ce..42a3198bc8 100644 --- a/Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/arch.py +++ b/Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/arch.py @@ -22,18 +22,93 @@ from . import util # NOTE: This map is derived from the ldefs using a script language_map = { 'aarch64': ['AARCH64:BE:64:v8A', 'AARCH64:LE:64:AppleSilicon', 'AARCH64:LE:64:v8A'], + 'arm': ['ARM:BE:32:v8', 'ARM:BE:32:v8T', 'ARM:LE:32:v8', 'ARM:LE:32:v8T'], + 'armv4': ['ARM:BE:32:v4', 'ARM:LE:32:v4'], + 'armv4t': ['ARM:BE:32:v4t', 'ARM:LE:32:v4t'], + 'armv5': ['ARM:BE:32:v5', 'ARM:LE:32:v5'], + 'armv5e': ['ARM:BE:32:v5t', 'ARM:LE:32:v5t'], + 'armv5t': ['ARM:BE:32:v5t', 'ARM:LE:32:v5t'], + 'armv6': ['ARM:BE:32:v6', 'ARM:LE:32:v6'], + 'armv6m': ['ARM:BE:32:Cortex', 'ARM:LE:32:Cortex'], 'armv7': ['ARM:BE:32:v7', 'ARM:LE:32:v7'], - 'armv7k': ['ARM:BE:32:v7', 'ARM:LE:32:v7'], + 'armv7l': ['ARM:BE:32:v7', 'ARM:LE:32:v7'], + 'armv7f': ['ARM:BE:32:v7', 'ARM:LE:32:v7'], 'armv7s': ['ARM:BE:32:v7', 'ARM:LE:32:v7'], - 'arm64': ['AARCH64:BE:64:v8A', 'AARCH64:LE:64:v8A'], - 'arm64_32': ['ARM:BE:32:v8', 'ARM:LE:32:v8'], - 'arm64e': ['AARCH64:BE:64:v8A', 'AARCH64:LE:64:v8A'], - 'i386': ['x86:LE:32:default'], + 'armv7k': ['ARM:BE:32:v7', 'ARM:LE:32:v7'], + 'armv7m': ['ARM:BE:32:v7', 'ARM:LE:32:v7'], + 'armv7em': ['ARM:BE:32:Cortex', 'ARM:LE:32:Cortex'], + 'xscale': ['ARM:BE:32:v6', 'ARM:LE:32:v6'], + 'thumbv5': ['ARM:BE:32:v5', 'ARM:LE:32:v5'], + 'thumbv5e': ['ARM:BE:32:v5', 'ARM:LE:32:v5'], + 'thumbv6': ['ARM:BE:32:v6', 'ARM:LE:32:v6'], + 'thumbv6m': ['ARM:BE:32:Cortex', 'ARM:LE:32:Cortex'], 'thumbv7': ['ARM:BE:32:v7', 'ARM:LE:32:v7'], - 'thumbv7k': ['ARM:BE:32:v7', 'ARM:LE:32:v7'], + 'thumbv7f': ['ARM:BE:32:v7', 'ARM:LE:32:v7'], 'thumbv7s': ['ARM:BE:32:v7', 'ARM:LE:32:v7'], + 'thumbv7k': ['ARM:BE:32:v7', 'ARM:LE:32:v7'], + 'thumbv7m': ['ARM:BE:32:v7', 'ARM:LE:32:v7'], + 'thumbv7em': ['ARM:BE:32:Cortex', 'ARM:LE:32:Cortex'], + 'armv8': ['ARM:BE:32:v8', 'ARM:LE:32:v8'], + 'armv8l': ['ARM:BE:32:v8', 'ARM:LE:32:v8'], + 'arm64': ['ARM:BE:64:v8', 'ARM:LE:64:v8'], + 'arm64e': ['ARM:BE:64:v8', 'ARM:LE:64:v8'], + 'arm64_32': ['ARM:BE:32:v8', 'ARM:LE:32:v8'], + 'mips': ['MIPS:BE:32:default', 'MIPS:LE:32:default'], + 'mipsr2': ['MIPS:BE:32:default', 'MIPS:LE:32:default'], + 'mipsr3': ['MIPS:BE:32:default', 'MIPS:LE:32:default'], + 'mipsr5': ['MIPS:BE:32:default', 'MIPS:LE:32:default'], + 'mipsr6': ['MIPS:BE:32:default', 'MIPS:LE:32:default'], + 'mipsel': ['MIPS:BE:32:default', 'MIPS:LE:32:default'], + 'mipsr2el': ['MIPS:BE:32:default', 'MIPS:LE:32:default'], + 'mipsr3el': ['MIPS:BE:32:default', 'MIPS:LE:32:default'], + 'mipsr5el': ['MIPS:BE:32:default', 'MIPS:LE:32:default'], + 'mipsr6el': ['MIPS:BE:32:default', 'MIPS:LE:32:default'], + 'mips64': ['MIPS:BE:3264:default', 'MIPS:LE:64:default'], + 'mips64r2': ['MIPS:BE:64:default', 'MIPS:LE:64:default'], + 'mips64r3': ['MIPS:BE:64:default', 'MIPS:LE:64:default'], + 'mips64r5': ['MIPS:BE:64:default', 'MIPS:LE:64:default'], + 'mips64r6': ['MIPS:BE:64:default', 'MIPS:LE:64:default'], + 'mips64el': ['MIPS:BE:64:default', 'MIPS:LE:64:default'], + 'mips64r2el': ['MIPS:BE:64:default', 'MIPS:LE:64:default'], + 'mips64r3el': ['MIPS:BE:64:default', 'MIPS:LE:64:default'], + 'mips64r5el': ['MIPS:BE:64:default', 'MIPS:LE:64:default'], + 'mips64r6el': ['MIPS:BE:64:default', 'MIPS:LE:64:default'], + 'msp:430X': ['TI_MSP430:LE:16:default'], + 'powerpc': ['PowerPC:BE:32:4xx', 'PowerPC:LE:32:4xx'], + 'ppc601': ['PowerPC:BE:32:4xx', 'PowerPC:LE:32:4xx'], + 'ppc602': ['PowerPC:BE:32:4xx', 'PowerPC:LE:32:4xx'], + 'ppc603': ['PowerPC:BE:32:4xx', 'PowerPC:LE:32:4xx'], + 'ppc603e': ['PowerPC:BE:32:4xx', 'PowerPC:LE:32:4xx'], + 'ppc603ev': ['PowerPC:BE:32:4xx', 'PowerPC:LE:32:4xx'], + 'ppc604': ['PowerPC:BE:32:4xx', 'PowerPC:LE:32:4xx'], + 'ppc604e': ['PowerPC:BE:32:4xx', 'PowerPC:LE:32:4xx'], + 'ppc620': ['PowerPC:BE:32:4xx', 'PowerPC:LE:32:4xx'], + 'ppc750': ['PowerPC:BE:32:4xx', 'PowerPC:LE:32:4xx'], + 'ppc7400': ['PowerPC:BE:32:4xx', 'PowerPC:LE:32:4xx'], + 'ppc7450': ['PowerPC:BE:32:4xx', 'PowerPC:LE:32:4xx'], + 'ppc970': ['PowerPC:BE:32:4xx', 'PowerPC:LE:32:4xx'], + 'powerpc64': ['PowerPC:BE:64:4xx', 'PowerPC:LE:64:4xx'], + 'powerpc64le': ['PowerPC:BE:64:4xx', 'PowerPC:LE:64:4xx'], + 'ppc970-64': ['PowerPC:BE:64:4xx', 'PowerPC:LE:64:4xx'], + 's390x': [], + 'sparc': ['sparc:BE:32:default', 'sparc:BE:64:default'], + 'sparcv9': ['sparc:BE:32:default', 'sparc:BE:64:default'], + 'i386': ['x86:LE:32:default'], + 'i486': ['x86:LE:32:default'], + 'i486sx': ['x86:LE:32:default'], + 'i686': ['x86:LE:64:default'], 'x86_64': ['x86:LE:64:default'], - 'wasm32': ['x86:LE:64:default'], + 'x86_64h': ['x86:LE:64:default'], + 'hexagon': [], + 'hexagonv4': [], + 'hexagonv5': [], + 'riscv32': ['RISCV:LE:32:RV32G', 'RISCV:LE:32:RV32GC', 'RISCV:LE:32:RV32I', 'RISCV:LE:32:RV32IC', 'RISCV:LE:32:RV32IMC', 'RISCV:LE:32:default'], + 'riscv64': ['RISCV:LE:64:RV64G', 'RISCV:LE:64:RV64GC', 'RISCV:LE:64:RV64I', 'RISCV:LE:64:RV64IC', 'RISCV:LE:64:default'], + 'unknown-mach-32': ['DATA:LE:32:default', 'DATA:LE:32:default'], + 'unknown-mach-64': ['DATA:LE:64:default', 'DATA:LE:64:default'], + 'arc': [], + 'avr': ['avr8:LE:24:xmega'], + 'wasm32': ['x86:LE:32:default'], } data64_compiler_map = { @@ -52,6 +127,8 @@ default_compiler_map = { 'windows': 'Visual Studio', # This may seem wrong, but Ghidra cspecs really describe the ABI 'Cygwin': 'Visual Studio', + 'default': 'default', + 'unknown': 'gcc', } compiler_map = { @@ -97,8 +174,8 @@ def get_osabi(): # thing on that line. ("auto" may appear earlier on the line.) triple = util.get_target().triple # this is an unfortunate feature of the tests - if triple is None: - return "linux" + if triple is None or '-' not in triple: + return "default" return triple.split('-')[2] diff --git a/Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/commands.py b/Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/commands.py index ade0786407..f48e541160 100644 --- a/Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/commands.py +++ b/Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/commands.py @@ -220,6 +220,8 @@ elif lldb.debugger: 'command script add -f ghidralldb.commands.ghidra_trace_sync_enable ghidra trace sync-enable') lldb.debugger.HandleCommand( 'command script add -f ghidralldb.commands.ghidra_trace_sync_disable ghidra trace sync-disable') + lldb.debugger.HandleCommand( + 'command script add -f ghidralldb.commands.ghidra_trace_sync_synth_stopped ghidra trace sync-synth-stopped') lldb.debugger.HandleCommand( 'command script add -f ghidralldb.commands.ghidra_util_mark _mark_') #lldb.debugger.HandleCommand('target stop-hook add -P ghidralldb.hooks.StopHook') @@ -1581,6 +1583,18 @@ def ghidra_trace_sync_disable(debugger, command, result, internal_dict): hooks.disable_current_process() +@convert_errors +def ghidra_trace_sync_synth_stopped(debugger, command, result, internal_dict): + """ + Act as though the target has just stopped. + + This may need to be invoked immediately after 'ghidra trace sync-enable', + to ensure the first snapshot displays the initial/current target state. + """ + + hooks.on_stop(None) # Pass a fake event + + @convert_errors def ghidra_util_wait_stopped(debugger, command, result, internal_dict): """ diff --git a/Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/hooks.py b/Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/hooks.py index 786a6a7fda..7b58444401 100644 --- a/Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/hooks.py +++ b/Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/hooks.py @@ -67,8 +67,11 @@ class ProcessState(object): hashable_frame = (thread.GetThreadID(), frame.GetFrameID()) if first or hashable_frame not in self.visited: banks = frame.GetRegisters() - commands.putreg(frame, banks.GetFirstValueByName( - commands.DEFAULT_REGISTER_BANK)) + primary = banks.GetFirstValueByName(commands.DEFAULT_REGISTER_BANK) + if primary.value is None: + primary = banks[0] + commands.DEFAULT_REGISTER_BANK = primary.name + commands.putreg(frame, primary) commands.putmem("$pc", "1", result=None) commands.putmem("$sp", "1", result=None) self.visited.add(hashable_frame) @@ -473,7 +476,7 @@ def on_cont(event): def on_stop(event): - proc = lldb.SBProcess.GetProcessFromEvent(event) + proc = lldb.SBProcess.GetProcessFromEvent(event) if event is not None else util.get_process() if proc.GetProcessID() not in PROC_STATE: print("not in state") return diff --git a/Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/methods.py b/Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/methods.py index a59babcf0c..14db747329 100644 --- a/Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/methods.py +++ b/Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/methods.py @@ -73,7 +73,7 @@ def find_proc_by_pattern(object, pattern, err_msg): def find_proc_by_obj(object): - return find_proc_by_pattern(object, PROCESS_PATTERN, "an Process") + return find_proc_by_pattern(object, PROCESS_PATTERN, "a Process") def find_proc_by_procbreak_obj(object): diff --git a/Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/schema.xml b/Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/schema.xml index b3e28bb963..f6b8385720 100644 --- a/Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/schema.xml +++ b/Ghidra/Debug/Debugger-agent-lldb/src/main/py/src/ghidralldb/schema.xml @@ -426,6 +426,9 @@ + + +