GP-4441: new option for target remote

GP-4441: post-review edits
GP-4441: first pass
GP-4441: working remote gdb/lldbGP-4441: updated via 4437GP-4441: embedded updatesGP-4441: set arch conditionally
This commit is contained in:
d-millar 2024-03-20 15:24:04 -04:00
parent f034eca003
commit 918d38c468
10 changed files with 204 additions and 30 deletions

View File

@ -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" \

View File

@ -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):

View File

@ -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)

View File

@ -74,7 +74,7 @@ THREADS_PATTERN = extre(INFERIOR_PATTERN, '\.Threads')
THREAD_PATTERN = extre(THREADS_PATTERN, '\[(?P<tnum>\\d*)\]')
STACK_PATTERN = extre(THREAD_PATTERN, '\.Stack')
FRAME_PATTERN = extre(STACK_PATTERN, '\[(?P<level>\\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<modname>.*)\]')

View File

@ -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 <html><body width="300px">
#@desc <h3>Launch with local <tt>lldb</tt> and connect to a stub (e.g., <tt>gdbserver</tt>)</h3>
#@desc <p>This will start <tt>lldb</tt> on the local system and then use it to connect to the remote system.
#@desc The actual command used is, e.g:</p>
#@desc <pre>gdb-remote host:port</pre>
#@desc <p>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 <tt>protobuf</tt> 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.</p>
#@desc </body></html>
#@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"

View File

@ -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]

View File

@ -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):
"""

View File

@ -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

View File

@ -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):

View File

@ -426,6 +426,9 @@
<attribute name="Floating Point Registers" schema="RegisterBank" />
<attribute name="Advanced Vector Extensions" schema="RegisterBank" />
<attribute name="Memory Protection Extensions" schema="RegisterBank" />
<attribute name="float" schema="RegisterBank" />
<attribute name="general" schema="RegisterBank" />
<attribute name="system" schema="RegisterBank" />
<attribute name="_descriptions" schema="RegisterValueContainer" />
<attribute name="_value" schema="ANY" hidden="yes" />
<attribute name="_type" schema="STRING" hidden="yes" />