mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
Merge remote-tracking branch 'origin/GP-3891_gdbWineLauncher--SQUASHED'
This commit is contained in:
commit
df29f50fa3
8 changed files with 255 additions and 15 deletions
|
@ -23,7 +23,7 @@
|
||||||
#@desc (you may install <tt>gdb-multiarch</tt>), and it must embed the Python 3 interpreter. You
|
#@desc (you may install <tt>gdb-multiarch</tt>), and it must embed the Python 3 interpreter. You
|
||||||
#@desc will also need <tt>protobuf</tt> installed for Python 3.
|
#@desc will also need <tt>protobuf</tt> installed for Python 3.
|
||||||
#@desc </body></html>
|
#@desc </body></html>
|
||||||
#@menu-group qemu
|
#@menu-group cross
|
||||||
#@icon icon.debugger
|
#@icon icon.debugger
|
||||||
#@help TraceRmiLauncherServicePlugin#gdb
|
#@help TraceRmiLauncherServicePlugin#gdb
|
||||||
#@enum StartCmd:str run start starti
|
#@enum StartCmd:str run start starti
|
||||||
|
|
75
Ghidra/Debug/Debugger-agent-gdb/data/debugger-launchers/wine-gdb.sh
Executable file
75
Ghidra/Debug/Debugger-agent-gdb/data/debugger-launchers/wine-gdb.sh
Executable file
|
@ -0,0 +1,75 @@
|
||||||
|
#!/usr/bin/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 wine + gdb
|
||||||
|
#@desc <html><body width="300px">
|
||||||
|
#@desc <h3>Launch with <tt>gdb</tt> and <tt>wine</tt></h3>
|
||||||
|
#@desc <p>This will launch the target on the local machine using <tt>gdb</tt> and <tt>wine</tt>.
|
||||||
|
#@desc GDB and Wine must already be installed on your system, and GDB must embed the Python 3
|
||||||
|
#@desc interpreter. You will also need <tt>protobuf</tt> and <tt>psutil</tt> installed for Python
|
||||||
|
#@desc 3.</p>
|
||||||
|
#@desc <p>This operates by starting GDB on the Wine executable and passing arguments to launch a
|
||||||
|
#@desc Windows target. This may prevent GDB from processing the object file, because it is a PE
|
||||||
|
#@desc file, and most copies of GDB for UNIX will support only ELF. Nevertheless, Ghidra should
|
||||||
|
#@desc recognize the target and map it, giving you symbols and debug info in the front end, even
|
||||||
|
#@desc if not in the GDB CLI.</p>
|
||||||
|
#@desc <p>You will need to locate the <tt>wine</tt> executable on your system, not the script. To
|
||||||
|
#@desc find it, either dissect the <tt>wine</tt> script or consult online documentation for your
|
||||||
|
#@desc distribution of Wine. There are often two executables, one for 32-bit targets and one for
|
||||||
|
#@desc 64-bit targets. You must select the correct one.</p>
|
||||||
|
#@desc </body></html>
|
||||||
|
#@menu-group cross
|
||||||
|
#@icon icon.debugger
|
||||||
|
#@help TraceRmiLauncherServicePlugin#gdb
|
||||||
|
#@arg :str "Image" "The target binary executable image"
|
||||||
|
#@args "Arguments" "Command-line arguments to pass to the target"
|
||||||
|
#@env OPT_WINE_PATH:str="/usr/lib/wine/wine64" "Path to wine binary" "The path to the wine executable for your target architecture."
|
||||||
|
#@env OPT_GDB_PATH:str="gdb" "Path to gdb" "The path to gdb. Omit the full path to resolve using the system PATH."
|
||||||
|
#@env OPT_EXTRA_TTY:bool=false "Inferior TTY" "Provide a separate terminal emulator for the target."
|
||||||
|
#@tty TTY_TARGET if env:OPT_EXTRA_TTY
|
||||||
|
|
||||||
|
if [ -d ${GHIDRA_HOME}/ghidra/.git ]
|
||||||
|
then
|
||||||
|
export PYTHONPATH=$GHIDRA_HOME/ghidra/Ghidra/Debug/Debugger-agent-gdb/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-gdb/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-gdb/pypkg/src:$PYTHONPATH
|
||||||
|
export PYTHONPATH=$GHIDRA_HOME/Ghidra/Debug/Debugger-rmi-trace/pypkg/src:$PYTHONPATH
|
||||||
|
fi
|
||||||
|
|
||||||
|
# NOTE: Ghidra will leave TTY_TARGET empty, which gdb takes for the same terminal.
|
||||||
|
|
||||||
|
"$OPT_GDB_PATH" \
|
||||||
|
-q \
|
||||||
|
-ex "set pagination off" \
|
||||||
|
-ex "set confirm off" \
|
||||||
|
-ex "show version" \
|
||||||
|
-ex "python import ghidragdb.wine" \
|
||||||
|
-ex "file \"$OPT_WINE_PATH\"" \
|
||||||
|
-ex "set args $@" \
|
||||||
|
-ex "set inferior-tty $TTY_TARGET" \
|
||||||
|
-ex "starti" \
|
||||||
|
-ex "ghidra wine run-to-image \"$1\"" \
|
||||||
|
-ex "ghidra trace connect \"$GHIDRA_TRACE_RMI_ADDR\"" \
|
||||||
|
-ex "ghidra trace start \"$1\"" \
|
||||||
|
-ex "ghidra trace sync-enable" \
|
||||||
|
-ex "ghidra trace sync-synth-stopped" \
|
||||||
|
-ex "set confirm on" \
|
||||||
|
-ex "set pagination on"
|
|
@ -18,6 +18,7 @@ from ghidratrace.client import Address, RegVal
|
||||||
import gdb
|
import gdb
|
||||||
|
|
||||||
# NOTE: This map is derived from the ldefs using a script
|
# NOTE: This map is derived from the ldefs using a script
|
||||||
|
# i386 is hand-patched
|
||||||
language_map = {
|
language_map = {
|
||||||
'aarch64': ['AARCH64:BE:64:v8A', 'AARCH64:LE:64:AppleSilicon', 'AARCH64:LE:64:v8A'],
|
'aarch64': ['AARCH64:BE:64:v8A', 'AARCH64:LE:64:AppleSilicon', 'AARCH64:LE:64:v8A'],
|
||||||
'aarch64:ilp32': ['AARCH64:BE:32:ilp32', 'AARCH64:LE:32:ilp32', 'AARCH64:LE:64:AppleSilicon'],
|
'aarch64:ilp32': ['AARCH64:BE:32:ilp32', 'AARCH64:LE:32:ilp32', 'AARCH64:LE:64:AppleSilicon'],
|
||||||
|
@ -48,6 +49,7 @@ language_map = {
|
||||||
'avr:51': ['avr8:LE:16:atmega256'],
|
'avr:51': ['avr8:LE:16:atmega256'],
|
||||||
'avr:6': ['avr8:LE:16:atmega256'],
|
'avr:6': ['avr8:LE:16:atmega256'],
|
||||||
'hppa2.0w': ['pa-risc:BE:32:default'],
|
'hppa2.0w': ['pa-risc:BE:32:default'],
|
||||||
|
'i386': ['x86:LE:32:default'],
|
||||||
'i386:intel': ['x86:LE:32:default'],
|
'i386:intel': ['x86:LE:32:default'],
|
||||||
'i386:x86-64': ['x86:LE:64:default'],
|
'i386:x86-64': ['x86:LE:64:default'],
|
||||||
'i386:x86-64:intel': ['x86:LE:64:default'],
|
'i386:x86-64:intel': ['x86:LE:64:default'],
|
||||||
|
|
|
@ -13,7 +13,9 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
##
|
##
|
||||||
|
import functools
|
||||||
import time
|
import time
|
||||||
|
import traceback
|
||||||
|
|
||||||
import gdb
|
import gdb
|
||||||
|
|
||||||
|
@ -144,6 +146,27 @@ BRK_STATE = BrkState()
|
||||||
INF_STATES = {}
|
INF_STATES = {}
|
||||||
|
|
||||||
|
|
||||||
|
def log_errors(func):
|
||||||
|
'''
|
||||||
|
Wrap a function in a try-except that prints and reraises the
|
||||||
|
exception.
|
||||||
|
|
||||||
|
This is needed because pybag and/or the COM wrappers do not print
|
||||||
|
exceptions that occur during event callbacks.
|
||||||
|
'''
|
||||||
|
|
||||||
|
@functools.wraps(func)
|
||||||
|
def _func(*args, **kwargs):
|
||||||
|
try:
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
except:
|
||||||
|
traceback.print_exc()
|
||||||
|
raise
|
||||||
|
|
||||||
|
return _func
|
||||||
|
|
||||||
|
|
||||||
|
@log_errors
|
||||||
def on_new_inferior(event):
|
def on_new_inferior(event):
|
||||||
trace = commands.STATE.trace
|
trace = commands.STATE.trace
|
||||||
if trace is None:
|
if trace is None:
|
||||||
|
@ -166,6 +189,7 @@ def on_inferior_selected():
|
||||||
commands.activate()
|
commands.activate()
|
||||||
|
|
||||||
|
|
||||||
|
@log_errors
|
||||||
def on_inferior_deleted(event):
|
def on_inferior_deleted(event):
|
||||||
trace = commands.STATE.trace
|
trace = commands.STATE.trace
|
||||||
if trace is None:
|
if trace is None:
|
||||||
|
@ -177,6 +201,7 @@ def on_inferior_deleted(event):
|
||||||
commands.put_inferiors() # TODO: Could just delete the one....
|
commands.put_inferiors() # TODO: Could just delete the one....
|
||||||
|
|
||||||
|
|
||||||
|
@log_errors
|
||||||
def on_new_thread(event):
|
def on_new_thread(event):
|
||||||
inf = gdb.selected_inferior()
|
inf = gdb.selected_inferior()
|
||||||
if inf.num not in INF_STATES:
|
if inf.num not in INF_STATES:
|
||||||
|
@ -214,6 +239,7 @@ def on_frame_selected():
|
||||||
commands.activate()
|
commands.activate()
|
||||||
|
|
||||||
|
|
||||||
|
@log_errors
|
||||||
def on_syscall_memory():
|
def on_syscall_memory():
|
||||||
inf = gdb.selected_inferior()
|
inf = gdb.selected_inferior()
|
||||||
if inf.num not in INF_STATES:
|
if inf.num not in INF_STATES:
|
||||||
|
@ -221,6 +247,7 @@ def on_syscall_memory():
|
||||||
INF_STATES[inf.num].regions = True
|
INF_STATES[inf.num].regions = True
|
||||||
|
|
||||||
|
|
||||||
|
@log_errors
|
||||||
def on_memory_changed(event):
|
def on_memory_changed(event):
|
||||||
inf = gdb.selected_inferior()
|
inf = gdb.selected_inferior()
|
||||||
if inf.num not in INF_STATES:
|
if inf.num not in INF_STATES:
|
||||||
|
@ -234,6 +261,7 @@ def on_memory_changed(event):
|
||||||
pages=False, is_mi=False, from_tty=False)
|
pages=False, is_mi=False, from_tty=False)
|
||||||
|
|
||||||
|
|
||||||
|
@log_errors
|
||||||
def on_register_changed(event):
|
def on_register_changed(event):
|
||||||
inf = gdb.selected_inferior()
|
inf = gdb.selected_inferior()
|
||||||
if inf.num not in INF_STATES:
|
if inf.num not in INF_STATES:
|
||||||
|
@ -249,6 +277,7 @@ def on_register_changed(event):
|
||||||
commands.putreg(event.frame, event.frame.architecture().registers())
|
commands.putreg(event.frame, event.frame.architecture().registers())
|
||||||
|
|
||||||
|
|
||||||
|
@log_errors
|
||||||
def on_cont(event):
|
def on_cont(event):
|
||||||
if (HOOK_STATE.check_skip_continue()):
|
if (HOOK_STATE.check_skip_continue()):
|
||||||
return
|
return
|
||||||
|
@ -264,6 +293,7 @@ def on_cont(event):
|
||||||
state.record_continued()
|
state.record_continued()
|
||||||
|
|
||||||
|
|
||||||
|
@log_errors
|
||||||
def on_stop(event):
|
def on_stop(event):
|
||||||
if hasattr(event, 'breakpoints') and HOOK_STATE.mem_catchpoint in event.breakpoints:
|
if hasattr(event, 'breakpoints') and HOOK_STATE.mem_catchpoint in event.breakpoints:
|
||||||
HOOK_STATE.skip_continue = True
|
HOOK_STATE.skip_continue = True
|
||||||
|
@ -284,6 +314,7 @@ def on_stop(event):
|
||||||
HOOK_STATE.end_batch()
|
HOOK_STATE.end_batch()
|
||||||
|
|
||||||
|
|
||||||
|
@log_errors
|
||||||
def on_exited(event):
|
def on_exited(event):
|
||||||
inf = gdb.selected_inferior()
|
inf = gdb.selected_inferior()
|
||||||
if inf.num not in INF_STATES:
|
if inf.num not in INF_STATES:
|
||||||
|
@ -320,18 +351,22 @@ def modules_changed():
|
||||||
INF_STATES[inf.num].modules = True
|
INF_STATES[inf.num].modules = True
|
||||||
|
|
||||||
|
|
||||||
|
@log_errors
|
||||||
def on_clear_objfiles(event):
|
def on_clear_objfiles(event):
|
||||||
modules_changed()
|
modules_changed()
|
||||||
|
|
||||||
|
|
||||||
|
@log_errors
|
||||||
def on_new_objfile(event):
|
def on_new_objfile(event):
|
||||||
modules_changed()
|
modules_changed()
|
||||||
|
|
||||||
|
|
||||||
|
@log_errors
|
||||||
def on_free_objfile(event):
|
def on_free_objfile(event):
|
||||||
modules_changed()
|
modules_changed()
|
||||||
|
|
||||||
|
|
||||||
|
@log_errors
|
||||||
def on_breakpoint_created(b):
|
def on_breakpoint_created(b):
|
||||||
inf = gdb.selected_inferior()
|
inf = gdb.selected_inferior()
|
||||||
notify_others_breaks(inf)
|
notify_others_breaks(inf)
|
||||||
|
@ -349,6 +384,7 @@ def on_breakpoint_created(b):
|
||||||
ibobj.insert()
|
ibobj.insert()
|
||||||
|
|
||||||
|
|
||||||
|
@log_errors
|
||||||
def on_breakpoint_modified(b):
|
def on_breakpoint_modified(b):
|
||||||
if b == HOOK_STATE.mem_catchpoint:
|
if b == HOOK_STATE.mem_catchpoint:
|
||||||
return
|
return
|
||||||
|
@ -369,10 +405,11 @@ def on_breakpoint_modified(b):
|
||||||
# NOTE: Location may not apply to inferior, but whatever.
|
# NOTE: Location may not apply to inferior, but whatever.
|
||||||
for i in range(new_count, old_count):
|
for i in range(new_count, old_count):
|
||||||
ikey = commands.INF_BREAK_KEY_PATTERN.format(
|
ikey = commands.INF_BREAK_KEY_PATTERN.format(
|
||||||
breaknum=b.number, locnum=i+1)
|
breaknum=b.number, locnum=i + 1)
|
||||||
ibobj.set_value(ikey, None)
|
ibobj.set_value(ikey, None)
|
||||||
|
|
||||||
|
|
||||||
|
@log_errors
|
||||||
def on_breakpoint_deleted(b):
|
def on_breakpoint_deleted(b):
|
||||||
inf = gdb.selected_inferior()
|
inf = gdb.selected_inferior()
|
||||||
notify_others_breaks(inf)
|
notify_others_breaks(inf)
|
||||||
|
@ -390,16 +427,18 @@ def on_breakpoint_deleted(b):
|
||||||
trace.proxy_object_path(bpath).remove(tree=True)
|
trace.proxy_object_path(bpath).remove(tree=True)
|
||||||
for i in range(old_count):
|
for i in range(old_count):
|
||||||
ikey = commands.INF_BREAK_KEY_PATTERN.format(
|
ikey = commands.INF_BREAK_KEY_PATTERN.format(
|
||||||
breaknum=b.number, locnum=i+1)
|
breaknum=b.number, locnum=i + 1)
|
||||||
ibobj.set_value(ikey, None)
|
ibobj.set_value(ikey, None)
|
||||||
|
|
||||||
|
|
||||||
|
@log_errors
|
||||||
def on_before_prompt():
|
def on_before_prompt():
|
||||||
HOOK_STATE.end_batch()
|
HOOK_STATE.end_batch()
|
||||||
|
|
||||||
|
|
||||||
# This will be called by a catchpoint
|
# This will be called by a catchpoint
|
||||||
class GhidraTraceEventMemoryCommand(gdb.Command):
|
class GhidraTraceEventMemoryCommand(gdb.Command):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__('hooks-ghidra event-memory', gdb.COMMAND_NONE)
|
super().__init__('hooks-ghidra event-memory', gdb.COMMAND_NONE)
|
||||||
|
|
||||||
|
@ -412,8 +451,11 @@ GhidraTraceEventMemoryCommand()
|
||||||
|
|
||||||
|
|
||||||
def cmd_hook(name):
|
def cmd_hook(name):
|
||||||
|
|
||||||
def _cmd_hook(func):
|
def _cmd_hook(func):
|
||||||
|
|
||||||
class _ActiveCommand(gdb.Command):
|
class _ActiveCommand(gdb.Command):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
# It seems we can't hook commands using the Python API....
|
# It seems we can't hook commands using the Python API....
|
||||||
super().__init__(f"hooks-ghidra def-{name}", gdb.COMMAND_USER)
|
super().__init__(f"hooks-ghidra def-{name}", gdb.COMMAND_USER)
|
||||||
|
@ -432,9 +474,11 @@ def cmd_hook(name):
|
||||||
define {name}
|
define {name}
|
||||||
end
|
end
|
||||||
""")
|
""")
|
||||||
|
|
||||||
func.hook = _ActiveCommand
|
func.hook = _ActiveCommand
|
||||||
func.unhook = _unhook_command
|
func.unhook = _unhook_command
|
||||||
return func
|
return func
|
||||||
|
|
||||||
return _cmd_hook
|
return _cmd_hook
|
||||||
|
|
||||||
|
|
||||||
|
@ -463,7 +507,7 @@ def hook_frame_down():
|
||||||
on_frame_selected()
|
on_frame_selected()
|
||||||
|
|
||||||
|
|
||||||
# TODO: Checks and workarounds for events missing in gdb 8
|
# TODO: Checks and workarounds for events missing in gdb 9
|
||||||
def install_hooks():
|
def install_hooks():
|
||||||
if HOOK_STATE.installed:
|
if HOOK_STATE.installed:
|
||||||
return
|
return
|
||||||
|
|
|
@ -289,7 +289,7 @@ class BreakpointLocationInfoReaderV8(object):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def get_locations(self, breakpoint):
|
def get_locations(self, breakpoint):
|
||||||
pass
|
return []
|
||||||
|
|
||||||
|
|
||||||
class BreakpointLocationInfoReaderV13(object):
|
class BreakpointLocationInfoReaderV13(object):
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
## ###
|
||||||
|
# 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.
|
||||||
|
##
|
||||||
|
import gdb
|
||||||
|
|
||||||
|
from . import util
|
||||||
|
from .commands import install, cmd
|
||||||
|
|
||||||
|
|
||||||
|
@install
|
||||||
|
class GhidraWinePrefix(gdb.Command):
|
||||||
|
"""Commands for tracing Wine processes"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__('ghidra wine', gdb.COMMAND_SUPPORT, prefix=True)
|
||||||
|
|
||||||
|
|
||||||
|
def is_mapped(pe_file):
|
||||||
|
return pe_file in gdb.execute("info proc mappings", to_string=True)
|
||||||
|
|
||||||
|
|
||||||
|
def set_break(command):
|
||||||
|
breaks_before = set(gdb.breakpoints())
|
||||||
|
gdb.execute(command)
|
||||||
|
return (set(gdb.breakpoints()) - breaks_before).pop()
|
||||||
|
|
||||||
|
|
||||||
|
@cmd('ghidra wine run-to-image', '-ghidra-wine-run-to-image', gdb.COMMAND_SUPPORT, False)
|
||||||
|
def ghidra_wine_run_to_image(pe_file, *, is_mi, **kwargs):
|
||||||
|
mprot_catchpoint = set_break("""
|
||||||
|
catch syscall mprotect
|
||||||
|
commands
|
||||||
|
silent
|
||||||
|
end
|
||||||
|
""".strip())
|
||||||
|
while not is_mapped(pe_file):
|
||||||
|
gdb.execute("continue")
|
||||||
|
mprot_catchpoint.delete()
|
||||||
|
|
||||||
|
|
||||||
|
ORIG_MODULE_INFO_READER = util.MODULE_INFO_READER
|
||||||
|
|
||||||
|
|
||||||
|
class Range(object):
|
||||||
|
|
||||||
|
def expand(self, region):
|
||||||
|
if not hasattr(self, 'min'):
|
||||||
|
self.min = region.start
|
||||||
|
self.max = region.end
|
||||||
|
else:
|
||||||
|
self.min = min(self.min, region.start)
|
||||||
|
self.max = max(self.max, region.end)
|
||||||
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
# There are more, but user can monkey patch this
|
||||||
|
MODULE_SUFFIXES = (".exe", ".dll")
|
||||||
|
|
||||||
|
|
||||||
|
class WineModuleInfoReader(object):
|
||||||
|
|
||||||
|
def get_modules(self):
|
||||||
|
modules = ORIG_MODULE_INFO_READER.get_modules()
|
||||||
|
ranges = dict()
|
||||||
|
for region in util.REGION_INFO_READER.get_regions():
|
||||||
|
if not region.objfile in ranges:
|
||||||
|
ranges[region.objfile] = Range().expand(region)
|
||||||
|
else:
|
||||||
|
ranges[region.objfile].expand(region)
|
||||||
|
for k, v in ranges.items():
|
||||||
|
if k in modules:
|
||||||
|
continue
|
||||||
|
if not k.lower().endswith(MODULE_SUFFIXES):
|
||||||
|
continue
|
||||||
|
modules[k] = util.Module(k, v.min, v.max, {})
|
||||||
|
return modules
|
||||||
|
|
||||||
|
|
||||||
|
util.MODULE_INFO_READER = WineModuleInfoReader()
|
|
@ -18,6 +18,7 @@ package ghidra.app.plugin.core.debug.gui.objects.components;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.beans.*;
|
import java.beans.*;
|
||||||
|
import java.math.BigInteger;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -44,6 +45,28 @@ import ghidra.util.layout.PairLayout;
|
||||||
|
|
||||||
public class DebuggerMethodInvocationDialog extends DialogComponentProvider
|
public class DebuggerMethodInvocationDialog extends DialogComponentProvider
|
||||||
implements PropertyChangeListener {
|
implements PropertyChangeListener {
|
||||||
|
|
||||||
|
public static class BigIntEditor extends PropertyEditorSupport {
|
||||||
|
@Override
|
||||||
|
public String getJavaInitializationString() {
|
||||||
|
Object value = getValue();
|
||||||
|
return value == null
|
||||||
|
? "null"
|
||||||
|
: "new BigInteger(\"%s\")".formatted(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAsText(String text) throws IllegalArgumentException {
|
||||||
|
setValue(text == null
|
||||||
|
? null
|
||||||
|
: new BigInteger(text));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
PropertyEditorManager.registerEditor(BigInteger.class, BigIntEditor.class);
|
||||||
|
}
|
||||||
|
|
||||||
private static final String KEY_MEMORIZED_ARGUMENTS = "memorizedArguments";
|
private static final String KEY_MEMORIZED_ARGUMENTS = "memorizedArguments";
|
||||||
|
|
||||||
static class ChoicesPropertyEditor implements PropertyEditor {
|
static class ChoicesPropertyEditor implements PropertyEditor {
|
||||||
|
|
|
@ -26,11 +26,12 @@ import ghidra.trace.model.Lifespan;
|
||||||
/**
|
/**
|
||||||
* Manages mappings from this trace into static images (Ghida {@link Program}s)
|
* Manages mappings from this trace into static images (Ghida {@link Program}s)
|
||||||
*
|
*
|
||||||
* Most commonly, this is used to map sections listed by a connected debugger to those same sections
|
* <p>
|
||||||
* of programs already imported into the same Ghidra project. It is vitally important that the image
|
* Most commonly, this is used to map modules listed by a connected debugger to programs already
|
||||||
* loaded by the target is an exact copy of the image imported by Ghidra, or else things may not be
|
* imported into the same Ghidra project. It is vitally important that the image loaded by the
|
||||||
* aligned.
|
* target is an exact copy of the image imported by Ghidra, or else things may not be aligned.
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* Note, to best handle mapping ranges to a variety of programs, and to validate the addition of new
|
* Note, to best handle mapping ranges to a variety of programs, and to validate the addition of new
|
||||||
* entries, it is unlikely a client should consume mapping entries directly. Instead, a service
|
* entries, it is unlikely a client should consume mapping entries directly. Instead, a service
|
||||||
* should track the mappings among all open traces and programs, permitting clients to mutate and
|
* should track the mappings among all open traces and programs, permitting clients to mutate and
|
||||||
|
@ -42,6 +43,7 @@ public interface TraceStaticMappingManager {
|
||||||
/**
|
/**
|
||||||
* Add a new mapping, if not already covered
|
* Add a new mapping, if not already covered
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* A new mapping may overlap an existing mapping, so long as they agree in address shift.
|
* A new mapping may overlap an existing mapping, so long as they agree in address shift.
|
||||||
* Furthermore, in such cases, the implementation may coalesce mappings to remove duplication.
|
* Furthermore, in such cases, the implementation may coalesce mappings to remove duplication.
|
||||||
*
|
*
|
||||||
|
@ -50,7 +52,7 @@ public interface TraceStaticMappingManager {
|
||||||
* @param toProgramURL the (Ghidra) URL of the static image ("to")
|
* @param toProgramURL the (Ghidra) URL of the static image ("to")
|
||||||
* @param toAddress the starting address (in string form) in the static image ("to")
|
* @param toAddress the starting address (in string form) in the static image ("to")
|
||||||
* @throws TraceConflictedMappingException if an existing mapping conflicts. See
|
* @throws TraceConflictedMappingException if an existing mapping conflicts. See
|
||||||
* {@link #isAnyConflicting(AddressRange, Lifespan, URL, String)}
|
* {@link #findAnyConflicting(AddressRange, Lifespan, URL, String)}
|
||||||
* @return the new entry, or any entry which subsumes the specified mapping
|
* @return the new entry, or any entry which subsumes the specified mapping
|
||||||
*/
|
*/
|
||||||
TraceStaticMapping add(AddressRange range, Lifespan lifespan, URL toProgramURL,
|
TraceStaticMapping add(AddressRange range, Lifespan lifespan, URL toProgramURL,
|
||||||
|
@ -75,14 +77,16 @@ public interface TraceStaticMappingManager {
|
||||||
/**
|
/**
|
||||||
* Check if another mapping would conflict with the given prospective mapping
|
* Check if another mapping would conflict with the given prospective mapping
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* Mappings are allowed to overlap, but they must agree on the destination program and address
|
* Mappings are allowed to overlap, but they must agree on the destination program and address
|
||||||
* throughout all overlapping portions.
|
* throughout all overlapping portions.
|
||||||
*
|
*
|
||||||
* TODO: It'd be nice if the manager automatically merged overlapping mappings in agreement or
|
* <p>
|
||||||
* provided a "deduplicate" method which optimized the entries in the database. This gets
|
* <b>TODO</b>: It'd be nice if the manager automatically merged overlapping mappings in
|
||||||
* complicated, since we're dealing with overlapping rectangles, not strict one-dimensional
|
* agreement or provided a "de-duplicate" method which optimized the entries in the database.
|
||||||
* ranges. Look into existing research for optimizing coverage of shapes by rectangles. The same
|
* This gets complicated, since we're dealing with overlapping rectangles, not strict
|
||||||
* is needed for property maps in 2 dimensions.
|
* one-dimensional ranges. Look into existing research for optimizing coverage of shapes by
|
||||||
|
* rectangles. The same is needed for property maps in 2 dimensions.
|
||||||
*
|
*
|
||||||
* @param range the range in the trace ("from")
|
* @param range the range in the trace ("from")
|
||||||
* @param lifespan the span of time in the trace
|
* @param lifespan the span of time in the trace
|
||||||
|
@ -96,6 +100,7 @@ public interface TraceStaticMappingManager {
|
||||||
/**
|
/**
|
||||||
* Find all mappings which overlap the given adddress range and span of time
|
* Find all mappings which overlap the given adddress range and span of time
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
* Note, this returns overlapping entries whether or not they conflict.
|
* Note, this returns overlapping entries whether or not they conflict.
|
||||||
*
|
*
|
||||||
* @param range the range in the trace ("from")
|
* @param range the range in the trace ("from")
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue