mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 01:39:21 +02:00
GP-5407: from review
GP-5407: from review GP-5407: rebase GP-5407: minor fix GP-5407: new launcherGP-5407: new launcherGP-5407: working (?) open traceGP-5407: modules sort of worksGP-5407: mostly sane (threads+modules)GP-5407: start on methodsGP-5407: refresh fixGP-5407: update on refreshGP-5407: need a better fix for displaysGP-5407: backwards methodsGP-5407: add actionGP-5407: add actionGP-5407: working back buttonGP-5407: experimentingGP-5407: events workingGP-5407: clearer optionsGP-5407: minorGP-5407: actions->methods (step_ext)GP-5407: iconsGP-5407: iconsGP-5407: icons pt.2GP-5407: fix for KMEM/UMEMGP-5407: deprecate pyttdGP-5407: deprecate pyttdGP-5407: launchers updateGP-5407: ??
This commit is contained in:
parent
136a944796
commit
a2e42f5fe2
32 changed files with 829 additions and 3419 deletions
|
@ -6,8 +6,8 @@ README.md||GHIDRA||||END|
|
|||
data/debugger-launchers/kernel-dbgeng.bat||GHIDRA||||END|
|
||||
data/debugger-launchers/local-dbgeng-attach.bat||GHIDRA||||END|
|
||||
data/debugger-launchers/local-dbgeng-ext.bat||GHIDRA||||END|
|
||||
data/debugger-launchers/local-dbgeng-trace.bat||GHIDRA||||END|
|
||||
data/debugger-launchers/local-dbgeng.bat||GHIDRA||||END|
|
||||
data/debugger-launchers/local-ttd.bat||GHIDRA||||END|
|
||||
data/debugger-launchers/remote-dbgeng.bat||GHIDRA||||END|
|
||||
data/debugger-launchers/svrcx-dbgeng.bat||GHIDRA||||END|
|
||||
src/main/py/LICENSE||GHIDRA||||END|
|
||||
|
@ -17,4 +17,3 @@ src/main/py/pyproject.toml||GHIDRA||||END|
|
|||
src/main/py/src/ghidradbg/dbgmodel/DbgModel.idl||GHIDRA||||END|
|
||||
src/main/py/src/ghidradbg/schema.xml||GHIDRA||||END|
|
||||
src/main/py/src/ghidradbg/schema_exdi.xml||GHIDRA||||END|
|
||||
src/main/py/src/ghidrattd/schema.xml||GHIDRA||||END|
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
::@title dbgeng-trace
|
||||
::@desc <html><body width="300px">
|
||||
::@desc <h3>Open trace with <tt>dbgeng</tt> (in a Python interpreter)</h3>
|
||||
::@desc <p>
|
||||
::@desc This will open a WinDbg TTD trace of the target on the local machine using <tt>dbgeng.dll</tt>.
|
||||
::@desc For setup instructions, press <b>F1</b>.
|
||||
::@desc </p>
|
||||
::@desc </body></html>
|
||||
::@menu-group local
|
||||
::@icon icon.debugger
|
||||
::@help TraceRmiLauncherServicePlugin#dbgeng_trace
|
||||
::@env OPT_PYTHON_EXE:file!="python" "Python command" "The path to the Python 3 interpreter. Omit the full path to resolve using the system PATH."
|
||||
:: Use env instead of args, because "all args except first" is terrible to implement in batch
|
||||
::@env OPT_TARGET_TRACE:file="" "Trace (.run)" "The target trace image"
|
||||
::@env OPT_USE_DBGMODEL:bool=true "Use dbgmodel" "Load and use dbgmodel.dll if it is available."
|
||||
::@env WINDBG_DIR:dir="" "Path to dbgeng.dll directory" "Path containing dbgeng and associated DLLS (if not Windows Kits)."
|
||||
|
||||
@echo off
|
||||
|
||||
set USE_TTD=true
|
||||
"%OPT_PYTHON_EXE%" -i ..\support\local-dbgeng-trace.py
|
|
@ -1,21 +0,0 @@
|
|||
::@title ttd
|
||||
::@desc <html><body width="300px">
|
||||
::@desc <h3>Launch with <tt>ttd</tt> (in a Python interpreter)</h3>
|
||||
::@desc <p>
|
||||
::@desc This will launch the target on the local machine for time-travel debugging.
|
||||
::@desc For setup instructions, press <b>F1</b>.
|
||||
::@desc </p>
|
||||
::@desc </body></html>
|
||||
::@menu-group local
|
||||
::@icon icon.debugger
|
||||
::@help TraceRmiLauncherServicePlugin#dbgeng_ttd
|
||||
::@env OPT_PYTHON_EXE:file!="python" "Python command" "The path to the Python 3 interpreter. Omit the full path to resolve using the system PATH."
|
||||
:: Use env instead of args, because "all args except first" is terrible to implement in batch
|
||||
::@env OPT_TARGET_IMG:file!="" "Trace (.run)" "A trace associated with the target binary executable"
|
||||
::@env OPT_TARGET_ARGS:str="" "Arguments" "Command-line arguments to pass to the target"
|
||||
::@env OPT_USE_DBGMODEL:bool=true "Use dbgmodel" "Load and use dbgmodel.dll if it is available."
|
||||
::@env OPT_DBGMODEL_PATH:dir="" "Path to dbgeng.dll & \\ttd" "Path containing dbgeng and associated DLLS (if not Windows Kits)."
|
||||
|
||||
@echo off
|
||||
|
||||
"%OPT_PYTHON_EXE%" -i ..\support\local-ttd.py
|
|
@ -0,0 +1,73 @@
|
|||
## ###
|
||||
# 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 os
|
||||
import sys
|
||||
|
||||
|
||||
home = os.getenv('GHIDRA_HOME')
|
||||
|
||||
if os.path.isdir(f'{home}\\ghidra\\.git'):
|
||||
sys.path.append(
|
||||
f'{home}\\ghidra\\Ghidra\\Debug\\Debugger-agent-dbgeng\\build\\pypkg\\src')
|
||||
sys.path.append(
|
||||
f'{home}\\ghidra\\Ghidra\\Debug\\Debugger-rmi-trace\\build\\pypkg\\src')
|
||||
elif os.path.isdir(f'{home}\\.git'):
|
||||
sys.path.append(
|
||||
f'{home}\\Ghidra\\Debug\\Debugger-agent-dbgeng\\build\\pypkg\\src')
|
||||
sys.path.append(
|
||||
f'{home}\\Ghidra\\Debug\\Debugger-rmi-trace\\build\\pypkg\\src')
|
||||
else:
|
||||
sys.path.append(
|
||||
f'{home}\\Ghidra\\Debug\\Debugger-agent-dbgeng\\pypkg\\src')
|
||||
sys.path.append(f'{home}\\Ghidra\\Debug\\Debugger-rmi-trace\\pypkg\\src')
|
||||
|
||||
|
||||
def main():
|
||||
# Delay these imports until sys.path is patched
|
||||
from ghidradbg import commands as cmd
|
||||
from pybag.dbgeng import core as DbgEng
|
||||
from ghidradbg.hooks import on_state_changed
|
||||
from ghidradbg.util import dbg
|
||||
|
||||
# So that the user can re-enter by typing repl()
|
||||
global repl
|
||||
repl = cmd.repl
|
||||
|
||||
cmd.ghidra_trace_connect(os.getenv('GHIDRA_TRACE_RMI_ADDR'))
|
||||
target = os.getenv('OPT_TARGET_TRACE')
|
||||
if target is None or target == "":
|
||||
print("dbgeng requires a target trace - please try again.")
|
||||
cmd.ghidra_trace_disconnect()
|
||||
return
|
||||
|
||||
cmd.ghidra_trace_open(target, start_trace=False)
|
||||
|
||||
# TODO: HACK
|
||||
try:
|
||||
dbg.wait()
|
||||
except KeyboardInterrupt as ki:
|
||||
dbg.interrupt()
|
||||
|
||||
cmd.ghidra_trace_start(target)
|
||||
cmd.ghidra_trace_sync_enable()
|
||||
|
||||
on_state_changed(DbgEng.DEBUG_CES_EXECUTION_STATUS, DbgEng.DEBUG_STATUS_BREAK)
|
||||
cmd.repl()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -1,58 +0,0 @@
|
|||
## ###
|
||||
# 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 os
|
||||
import sys
|
||||
|
||||
|
||||
home = os.getenv('GHIDRA_HOME')
|
||||
|
||||
if os.path.isdir(f'{home}\\ghidra\\.git'):
|
||||
sys.path.append(
|
||||
f'{home}\\ghidra\\Ghidra\\Debug\\Debugger-agent-dbgeng\\build\\pypkg\\src')
|
||||
sys.path.append(
|
||||
f'{home}\\ghidra\\Ghidra\\Debug\\Debugger-rmi-trace\\build\\pypkg\\src')
|
||||
elif os.path.isdir(f'{home}\\.git'):
|
||||
sys.path.append(
|
||||
f'{home}\\Ghidra\\Debug\\Debugger-agent-dbgeng\\build\\pypkg\\src')
|
||||
sys.path.append(
|
||||
f'{home}\\Ghidra\\Debug\\Debugger-rmi-trace\\build\\pypkg\\src')
|
||||
else:
|
||||
sys.path.append(
|
||||
f'{home}\\Ghidra\\Debug\\Debugger-agent-dbgeng\\pypkg\\src')
|
||||
sys.path.append(f'{home}\\Ghidra\\Debug\\Debugger-rmi-trace\\pypkg\\src')
|
||||
|
||||
|
||||
def main():
|
||||
# Delay these imports until sys.path is patched
|
||||
from ghidrattd import commands as cmd
|
||||
from ghidrattd import hooks
|
||||
###from ghidrattd.util import dbg
|
||||
|
||||
cmd.ghidra_trace_connect(os.getenv('GHIDRA_TRACE_RMI_ADDR'))
|
||||
args = os.getenv('OPT_TARGET_ARGS')
|
||||
if args:
|
||||
args = ' ' + args
|
||||
cmd.ghidra_trace_create(
|
||||
os.getenv('OPT_TARGET_IMG') + args, start_trace=True)
|
||||
cmd.ghidra_trace_sync_enable()
|
||||
hooks.on_stop()
|
||||
|
||||
cmd.repl()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -24,15 +24,16 @@ import time
|
|||
|
||||
from comtypes import c_ulong
|
||||
from ghidratrace import sch
|
||||
from ghidratrace.client import Client, Address, AddressRange, TraceObject
|
||||
from ghidratrace.client import Client, Address, AddressRange, Lifespan, TraceObject
|
||||
from pybag import pydbg, userdbg, kerneldbg
|
||||
from pybag.dbgeng import core as DbgEng
|
||||
from pybag.dbgeng import exception
|
||||
|
||||
from . import util, arch, methods, hooks
|
||||
from .dbgmodel.imodelobject import ModelObjectKind
|
||||
|
||||
if util.is_exdi():
|
||||
from .exdi import exdi_commands, exdi_methods
|
||||
from .exdi import exdi_commands, exdi_methods
|
||||
|
||||
STILL_ACTIVE = 259
|
||||
PAGE_SIZE = 4096
|
||||
|
@ -65,6 +66,9 @@ SECTIONS_ADD_PATTERN = '.Sections'
|
|||
SECTION_KEY_PATTERN = '[{secname}]'
|
||||
SECTION_ADD_PATTERN = SECTIONS_ADD_PATTERN + SECTION_KEY_PATTERN
|
||||
GENERIC_KEY_PATTERN = '[{key}]'
|
||||
TTD_PATTERN = 'State.DebuggerVariables.{var}.TTD'
|
||||
|
||||
DESCRIPTION_PATTERN = '[{major}] {type}'
|
||||
|
||||
# TODO: Symbols
|
||||
|
||||
|
@ -221,7 +225,8 @@ def start_trace(name):
|
|||
variant = " (dbgmodel)" if using_dbgmodel else " (dbgeng)"
|
||||
with STATE.trace.open_tx("Create Root Object"):
|
||||
root = STATE.trace.create_root_object(schema_xml, 'DbgRoot')
|
||||
root.set_value('_display', util.DBG_VERSION.full + ' via pybag' + variant)
|
||||
root.set_value('_display', util.DBG_VERSION.full +
|
||||
' via pybag' + variant)
|
||||
if util.dbg.use_generics:
|
||||
put_generic(root)
|
||||
util.set_convenience_variable('_ghidra_tracing', "true")
|
||||
|
@ -294,7 +299,8 @@ def ghidra_trace_create_ext(command=None, initialDirectory='.', envVariables="\0
|
|||
envVariables = None
|
||||
if envVariables is not None and envVariables.endswith("/0/0") is False:
|
||||
envVariables += "/0/0"
|
||||
dbg._client.CreateProcess2(command, options, initialDirectory, envVariables)
|
||||
dbg._client.CreateProcess2(
|
||||
command, options, initialDirectory, envVariables)
|
||||
dbg._control.AddEngineOptions(int(engine_options))
|
||||
if start_trace:
|
||||
ghidra_trace_start(command)
|
||||
|
@ -312,9 +318,9 @@ def ghidra_trace_attach(pid=None, attach_flags='0', initial_break=True, timeout=
|
|||
if attach_flags == None:
|
||||
attach_flags = '0'
|
||||
if pid != None:
|
||||
dbg._client.AttachProcess(int(pid,0), int(attach_flags,0))
|
||||
dbg._client.AttachProcess(int(pid, 0), int(attach_flags, 0))
|
||||
if start_trace:
|
||||
ghidra_trace_start("pid_"+pid)
|
||||
ghidra_trace_start("pid_" + pid)
|
||||
|
||||
|
||||
@util.dbg.eng_thread
|
||||
|
@ -348,6 +354,19 @@ def ghidra_trace_connect_server(options=None):
|
|||
dbg._client.ConnectProcessServer(enc_options)
|
||||
|
||||
|
||||
@util.dbg.eng_thread
|
||||
def ghidra_trace_open(command=None, initial_break=True, timeout=DbgEng.WAIT_INFINITE, start_trace=True):
|
||||
"""
|
||||
Create a session.
|
||||
"""
|
||||
|
||||
dbg = util.dbg._base
|
||||
if command != None:
|
||||
util.open_trace_or_dump(command)
|
||||
if start_trace:
|
||||
ghidra_trace_start(command)
|
||||
|
||||
|
||||
@util.dbg.eng_thread
|
||||
def ghidra_trace_kill():
|
||||
"""
|
||||
|
@ -370,7 +389,7 @@ def ghidra_trace_info():
|
|||
print("Not connected to Ghidra")
|
||||
return
|
||||
host, port = STATE.client.s.getpeername()
|
||||
print(f"Connected to {STATE.client.description} at {host}:{port}")
|
||||
print(f"Connected to {STATE.client.description} at {host}: {port}")
|
||||
if STATE.trace is None:
|
||||
print("No trace")
|
||||
return
|
||||
|
@ -585,7 +604,7 @@ def putreg():
|
|||
nframe = util.selected_frame()
|
||||
# NB: We're going to update the Register View for non-zero stack frames
|
||||
if nframe == 0:
|
||||
return {'missing': STATE.trace.put_registers(rpath, values)}
|
||||
return {'missing': STATE.trace.put_registers(rpath, values)}
|
||||
|
||||
nproc = util.selected_process()
|
||||
if nproc < 0:
|
||||
|
@ -601,13 +620,13 @@ def putreg():
|
|||
for i in range(0, len(regs)):
|
||||
name = regs._reg.GetDescription(i)[0]
|
||||
try:
|
||||
value = regs._get_register_by_index(i)
|
||||
value = regs._get_register_by_index(i)
|
||||
except Exception:
|
||||
value = 0
|
||||
value = 0
|
||||
try:
|
||||
values.append(mapper.map_value(nproc, name, value))
|
||||
if util.dbg.use_generics is False:
|
||||
robj.set_value(name, hex(value))
|
||||
robj.set_value(name, hex(value))
|
||||
except Exception:
|
||||
pass
|
||||
return {'missing': STATE.trace.put_registers(space, values)}
|
||||
|
@ -903,9 +922,10 @@ def activate(path=None):
|
|||
else:
|
||||
frame = util.selected_frame()
|
||||
if frame is None:
|
||||
path = THREAD_PATTERN.format(procnum=nproc, tnum=nthrd)
|
||||
path = THREAD_PATTERN.format(procnum=nproc, tnum=nthrd)
|
||||
else:
|
||||
path = FRAME_PATTERN.format(procnum=nproc, tnum=nthrd, level=frame)
|
||||
path = FRAME_PATTERN.format(
|
||||
procnum=nproc, tnum=nthrd, level=frame)
|
||||
trace.proxy_object_path(path).activate()
|
||||
|
||||
|
||||
|
@ -1198,11 +1218,11 @@ def put_regions():
|
|||
regobj.set_value('AllocationBase', hex(r.AllocationBase))
|
||||
regobj.set_value('Protect', hex(r.Protect))
|
||||
regobj.set_value('Type', hex(r.Type))
|
||||
if hasattr(r, 'Name') and r.Name is not None:
|
||||
if hasattr(r, 'Name') and r.Name is not None:
|
||||
regobj.set_value('_display', r.Name)
|
||||
regobj.insert()
|
||||
STATE.trace.proxy_object_path(
|
||||
MEMORY_PATTERN.format(procnum=nproc)).retain_values(keys)
|
||||
#STATE.trace.proxy_object_path(
|
||||
# MEMORY_PATTERN.format(procnum=nproc)).retain_values(keys)
|
||||
|
||||
|
||||
def ghidra_trace_put_regions():
|
||||
|
@ -1257,6 +1277,24 @@ def put_modules():
|
|||
procnum=nproc)).retain_values(mod_keys)
|
||||
|
||||
|
||||
def get_module(key, mod):
|
||||
nproc = util.selected_process()
|
||||
modmap = util.get_attributes(mod)
|
||||
base = util.get_value(modmap["Address"])
|
||||
size = util.get_value(modmap["Size"])
|
||||
name = util.get_value(modmap["Name"])
|
||||
mpath = MODULE_PATTERN.format(procnum=nproc, modpath=hex(base))
|
||||
modobj = STATE.trace.create_object(mpath)
|
||||
mapper = STATE.trace.memory_mapper
|
||||
base_base, base_addr = mapper.map(nproc, base)
|
||||
if base_base != base_addr.space:
|
||||
STATE.trace.create_overlay_space(base_base, base_addr.space)
|
||||
modobj.set_value('Range', base_addr.extend(size))
|
||||
modobj.set_value('Name', name)
|
||||
modobj.set_value('_display','{} {:x} {}'.format(key, base, name))
|
||||
return modobj
|
||||
|
||||
|
||||
def ghidra_trace_put_modules():
|
||||
"""
|
||||
Gather object files, if applicable, and write to the trace's Modules
|
||||
|
@ -1334,6 +1372,24 @@ def put_event_thread(nthrd=None):
|
|||
STATE.trace.proxy_object_path('').set_value('_event_thread', tobj)
|
||||
|
||||
|
||||
def get_thread(key, thread):
|
||||
pid = util.selected_process()
|
||||
tmap = util.get_attributes(thread)
|
||||
tid = int(key[1:len(key)-1])
|
||||
radix = util.get_convenience_variable('output-radix')
|
||||
if radix == 'auto':
|
||||
radix = 16
|
||||
tpath = THREAD_PATTERN.format(procnum=pid, tnum=tid)
|
||||
tobj = STATE.trace.create_object(tpath)
|
||||
tobj.set_value('TID', tid, span=Lifespan(0))
|
||||
tidstr = ('0x{:x}' if radix == 16 else '0{:o}' if radix ==
|
||||
8 else '{}').format(tid)
|
||||
tobj.set_value('_short_display', '[{}:{}]'.format(
|
||||
pid, tidstr), span=Lifespan(0))
|
||||
tobj.set_value('_display', '[{}]'.format(tidstr), span=Lifespan(0))
|
||||
return tobj
|
||||
|
||||
|
||||
def ghidra_trace_put_threads():
|
||||
"""
|
||||
Put the current process's threads into the Ghidra trace
|
||||
|
@ -1358,7 +1414,7 @@ def put_frames():
|
|||
(values, keys) = create_generic(path)
|
||||
STATE.trace.proxy_object_path(path).retain_values(keys)
|
||||
# NB: some flavors of dbgmodel lack Attributes, so we grab Instruction Offset regardless
|
||||
#return
|
||||
# return
|
||||
|
||||
mapper = STATE.trace.memory_mapper
|
||||
keys = []
|
||||
|
@ -1373,18 +1429,18 @@ def put_frames():
|
|||
STATE.trace.create_overlay_space(base, offset_inst.space)
|
||||
fobj.set_value('Instruction Offset', offset_inst)
|
||||
if not util.dbg.use_generics:
|
||||
base, offset_stack = mapper.map(nproc, f.StackOffset)
|
||||
if base != offset_stack.space:
|
||||
STATE.trace.create_overlay_space(base, offset_stack.space)
|
||||
base, offset_ret = mapper.map(nproc, f.ReturnOffset)
|
||||
if base != offset_ret.space:
|
||||
STATE.trace.create_overlay_space(base, offset_ret.space)
|
||||
base, offset_frame = mapper.map(nproc, f.FrameOffset)
|
||||
if base != offset_frame.space:
|
||||
STATE.trace.create_overlay_space(base, offset_frame.space)
|
||||
fobj.set_value('Stack Offset', offset_stack)
|
||||
fobj.set_value('Return Offset', offset_ret)
|
||||
fobj.set_value('Frame Offset', offset_frame)
|
||||
base, offset_stack = mapper.map(nproc, f.StackOffset)
|
||||
if base != offset_stack.space:
|
||||
STATE.trace.create_overlay_space(base, offset_stack.space)
|
||||
base, offset_ret = mapper.map(nproc, f.ReturnOffset)
|
||||
if base != offset_ret.space:
|
||||
STATE.trace.create_overlay_space(base, offset_ret.space)
|
||||
base, offset_frame = mapper.map(nproc, f.FrameOffset)
|
||||
if base != offset_frame.space:
|
||||
STATE.trace.create_overlay_space(base, offset_frame.space)
|
||||
fobj.set_value('Stack Offset', offset_stack)
|
||||
fobj.set_value('Return Offset', offset_ret)
|
||||
fobj.set_value('Frame Offset', offset_frame)
|
||||
fobj.set_value('_display', "#{} {}".format(
|
||||
f.FrameNumber, offset_inst.offset))
|
||||
fobj.insert()
|
||||
|
@ -1402,79 +1458,105 @@ def ghidra_trace_put_frames():
|
|||
put_frames()
|
||||
|
||||
|
||||
def update_by_container(np, keyval, obj):
|
||||
index = keyval[0]
|
||||
key = ''
|
||||
def update_key(np, keyval):
|
||||
"""
|
||||
This should set the modified key
|
||||
"""
|
||||
key = keyval[0]
|
||||
if np.endswith("Modules"):
|
||||
key = '[{:d}]'.format(key)
|
||||
mo = util.get_object(np+key+".BaseAddress")
|
||||
key = hex(util.get_value(mo))
|
||||
return key
|
||||
|
||||
|
||||
def update_by_container(np, keyval, to):
|
||||
"""
|
||||
Sets non-generic variables by container
|
||||
"""
|
||||
key = keyval[0]
|
||||
disp = ''
|
||||
if np.endswith("Processes") or np.endswith("Threads"):
|
||||
istate = compute_proc_state(index)
|
||||
obj.set_value('State', istate)
|
||||
istate = compute_proc_state(key)
|
||||
to.set_value('State', istate)
|
||||
if np.endswith("Sessions"):
|
||||
key = '[{:x}]'.format(index)
|
||||
disp = '[{:x}]'.format(key)
|
||||
if np.endswith("Processes"):
|
||||
create_generic(obj.path)
|
||||
obj.set_value('PID', index)
|
||||
create_generic(obj.path + ".Memory")
|
||||
create_generic(to.path)
|
||||
to.set_value('PID', key)
|
||||
create_generic(to.path + ".Memory")
|
||||
if util.is_kernel():
|
||||
key = '[{:x}]'.format(index)
|
||||
disp = '[{:x}]'.format(key)
|
||||
else:
|
||||
id = util.get_proc_id(index)
|
||||
key = '{:x} [{:x}]'.format(id, index)
|
||||
id = util.get_proc_id(key)
|
||||
disp = '{:x} [{:x}]'.format(id, key)
|
||||
if np.endswith("Breakpoints"):
|
||||
create_generic(obj.path)
|
||||
create_generic(to.path)
|
||||
if np.endswith("Threads"):
|
||||
create_generic(obj.path)
|
||||
obj.set_value('TID', index)
|
||||
create_generic(to.path)
|
||||
to.set_value('TID', key)
|
||||
if util.is_kernel():
|
||||
key = '[{:x}]'.format(index)
|
||||
disp = '[{:x}]'.format(key)
|
||||
else:
|
||||
id = util.get_thread_id(index)
|
||||
key = '{:x} [{:x}]'.format(id, index)
|
||||
id = util.get_thread_id(key)
|
||||
disp = '{:x} [{:x}]'.format(id, key)
|
||||
if np.endswith("Frames"):
|
||||
mo = util.get_object(obj.path)
|
||||
mo = util.get_object(to.path)
|
||||
map = util.get_attributes(mo)
|
||||
if 'Attributes' in map:
|
||||
attr = map["Attributes"]
|
||||
if attr is not None:
|
||||
map = util.get_attributes(attr)
|
||||
map = util.get_attributes(attr)
|
||||
pc = util.get_value(map["InstructionOffset"])
|
||||
(pc_base, pc_addr) = map_address(pc)
|
||||
obj.set_value('Instruction Offset', pc_addr)
|
||||
key = '#{:x} 0x{:x}'.format(index, pc)
|
||||
to.set_value('Instruction Offset', pc_addr)
|
||||
disp = '#{:x} 0x{:x}'.format(key, pc)
|
||||
if np.endswith("Modules"):
|
||||
create_generic(obj.path)
|
||||
mo = util.get_object(obj.path)
|
||||
modobjpath=np+'[{:d}]'.format(key)
|
||||
create_generic(to.path, modobjpath=modobjpath)
|
||||
mo = util.get_object(modobjpath)
|
||||
map = util.get_attributes(mo)
|
||||
base = util.get_value(map["BaseAddress"])
|
||||
size = util.get_value(map["Size"])
|
||||
name = util.get_value(map["Name"])
|
||||
obj.set_value('Name', '{}'.format(name))
|
||||
to.set_value('Name', '{}'.format(name))
|
||||
(base_base, base_addr) = map_address(base)
|
||||
obj.set_value('Range', base_addr.extend(size))
|
||||
key = '{:x} {:x} {}'.format(index, base, name)
|
||||
disp = util.to_display_string(keyval[1])
|
||||
if disp is not None:
|
||||
key += " " + disp
|
||||
if key is not None and key != "":
|
||||
obj.set_value('_display', key)
|
||||
to.set_value('Range', base_addr.extend(size))
|
||||
disp = '{:x} {:x} {}'.format(key, base, name)
|
||||
disp0 = util.to_display_string(keyval[1])
|
||||
if disp0 is not None:
|
||||
disp += " " + disp0
|
||||
if disp is not None and disp != "":
|
||||
to.set_value('_display', disp)
|
||||
|
||||
|
||||
def create_generic(path):
|
||||
def create_generic(path, modobjpath=None):
|
||||
obj = STATE.trace.create_object(path)
|
||||
result = put_generic(obj, modobjpath)
|
||||
obj.insert()
|
||||
result = put_generic(obj)
|
||||
return result
|
||||
|
||||
|
||||
def put_generic(node):
|
||||
def put_generic_from_node(node):
|
||||
obj = STATE.trace.create_object(node.path)
|
||||
result = put_generic(obj, None)
|
||||
obj.insert()
|
||||
return result
|
||||
|
||||
|
||||
def put_generic(node, modobjpath=None):
|
||||
# print(f"put_generic: {node}")
|
||||
nproc = util.selected_process()
|
||||
if nproc is None:
|
||||
return
|
||||
nthrd = util.selected_thread()
|
||||
|
||||
mo = util.get_object(node.path)
|
||||
if modobjpath is None:
|
||||
mo = util.get_object(node.path)
|
||||
else:
|
||||
mo = util.get_object(modobjpath)
|
||||
mapper = STATE.trace.register_mapper
|
||||
|
||||
|
||||
attributes = util.get_attributes(mo)
|
||||
# print(f"ATTR={attributes}")
|
||||
values = []
|
||||
|
@ -1508,8 +1590,8 @@ def put_generic(node):
|
|||
keys = []
|
||||
if elements is not None:
|
||||
for el in elements:
|
||||
index = el[0]
|
||||
key = GENERIC_KEY_PATTERN.format(key=index)
|
||||
key = update_key(node.path, el)
|
||||
key = GENERIC_KEY_PATTERN.format(key=key)
|
||||
lpath = node.path + key
|
||||
lobj = STATE.trace.create_object(lpath)
|
||||
update_by_container(node.path, el, lobj)
|
||||
|
@ -1534,7 +1616,7 @@ def set_display(key, value, obj):
|
|||
if hloc is not None:
|
||||
key += " @ " + str(hloc)
|
||||
obj.set_value('_display', key)
|
||||
(hloc_base, hloc_addr) = map_address(int(hloc,0))
|
||||
(hloc_base, hloc_addr) = map_address(int(hloc, 0))
|
||||
obj.set_value('_address', hloc_addr, schema=Address)
|
||||
if vstr is not None:
|
||||
key += " : " + str(vstr)
|
||||
|
@ -1557,9 +1639,134 @@ def ghidra_trace_put_generic(node):
|
|||
|
||||
STATE.require_tx()
|
||||
with STATE.client.batch() as b:
|
||||
put_generic(node)
|
||||
put_generic_from_node(node)
|
||||
|
||||
|
||||
def init_ttd():
|
||||
# print(f"put_events: {node}")
|
||||
with open_tracked_tx('Init TTDState'):
|
||||
ttd = util.ttd
|
||||
nproc = util.selected_process()
|
||||
path = TTD_PATTERN.format(var="curprocess")+".Lifetime"
|
||||
(values, keys) = create_generic(path)
|
||||
lifetime = util.get_object(path)
|
||||
map = util.get_attributes(lifetime)
|
||||
ttd._first = map["MinPosition"]
|
||||
ttd._last = map["MaxPosition"]
|
||||
ttd._lastmajor = util.pos2split(ttd._last)[0]
|
||||
ttd._lastpos = ttd._first
|
||||
ttd.MAX_STEP = 0xFFFFFFFFFFFFFFFE
|
||||
ghidra_trace_set_snap(util.pos2snap(ttd._first))
|
||||
|
||||
|
||||
def put_events():
|
||||
ttd = util.ttd
|
||||
nproc = util.selected_process()
|
||||
path = TTD_PATTERN.format(var="curprocess")+".Events"
|
||||
(values, keys) = create_generic(path)
|
||||
for k in keys:
|
||||
event = util.get_object(path+k)
|
||||
map = util.get_attributes(event)
|
||||
type = util.get_value(map["Type"])
|
||||
pos = map["Position"]
|
||||
(major, minor) = util.pos2split(pos)
|
||||
ttd.events[major] = event
|
||||
ttd.evttypes[major] = type
|
||||
with open_tracked_tx('Populate events'):
|
||||
index = util.pos2snap(pos)
|
||||
STATE.trace.snapshot(DESCRIPTION_PATTERN.format(major=major, type=type), snap=index)
|
||||
if type == "ModuleLoaded" or type == "ModuleUnloaded":
|
||||
mod = map["Module"]
|
||||
mobj = get_module(k, mod)
|
||||
if type == "ModuleLoaded":
|
||||
mobj.insert(span=Lifespan(index))
|
||||
else:
|
||||
mobj.remove(span=Lifespan(index))
|
||||
if type == "ThreadCreated" or type == "ThreadTerminated":
|
||||
t = map["Thread"]
|
||||
tobj = get_thread(k, t)
|
||||
if type == "ThreadCreated":
|
||||
tobj.insert(span=Lifespan(index))
|
||||
else:
|
||||
tobj.remove(span=Lifespan(index))
|
||||
hooks.on_stop()
|
||||
|
||||
|
||||
def ghidra_trace_put_events(node):
|
||||
"""
|
||||
Put the event set the Ghidra trace
|
||||
"""
|
||||
|
||||
STATE.require_tx()
|
||||
with STATE.client.batch() as b:
|
||||
put_events()
|
||||
|
||||
|
||||
def put_events_custom(prefix, cmd):
|
||||
result = util.dbg.cmd("{prefix}.{cmd}".format(prefix=prefix, cmd=cmd))
|
||||
if result.startswith("Error"):
|
||||
print(result)
|
||||
return
|
||||
nproc = util.selected_process()
|
||||
mapper = STATE.trace.memory_mapper
|
||||
path = TTD_PATTERN.format(var="cursession")+".CustomEvents"
|
||||
obj = STATE.trace.create_object(path)
|
||||
index = 0
|
||||
addr = size = start = stop = None
|
||||
attrs = {}
|
||||
keys = []
|
||||
for l in result.split('\n'):
|
||||
split = l.split(":")
|
||||
id = split[0].strip()
|
||||
if id == "Address":
|
||||
addr = int(split[1].strip(),16)
|
||||
elif id == "Size":
|
||||
size = int(split[1].strip(),16)
|
||||
elif id == "TimeStart":
|
||||
start = util.mm2snap(int(split[1],16), int(split[2],16))
|
||||
elif id == "TimeEnd":
|
||||
stop = util.mm2snap(int(split[1],16), int(split[2],16))
|
||||
elif " : " in l:
|
||||
attrs[id] = l[l.index(":"):].strip()
|
||||
if addr is not None and size is not None and start is not None and stop is not None:
|
||||
with open_tracked_tx('Populate events'):
|
||||
key = "[{:x}]".format(addr)
|
||||
STATE.trace.snapshot("[{:x}] EventCreated {} ".format(start, key), snap=start)
|
||||
if start > stop:
|
||||
print(f"ERROR: {start}:{stop}")
|
||||
continue
|
||||
span=Lifespan(start, stop)
|
||||
rpath = REGION_PATTERN.format(procnum=nproc, start=addr)
|
||||
keys.append(REGION_KEY_PATTERN.format(start=addr))
|
||||
regobj = STATE.trace.create_object(rpath)
|
||||
(start_base, start_addr) = map_address(addr)
|
||||
rng = start_addr.extend(size)
|
||||
regobj.set_value('Range', rng, span=span)
|
||||
regobj.set_value('_range', rng, span=span)
|
||||
regobj.set_value('_display', hex(addr), span=span)
|
||||
regobj.set_value('_cmd', cmd)
|
||||
for (k,v) in attrs.items():
|
||||
regobj.set_value(k, v, span=span)
|
||||
regobj.insert(span=span)
|
||||
keys.append(key)
|
||||
index += 1
|
||||
addr = size = start = stop = None
|
||||
attrs = {}
|
||||
obj.insert()
|
||||
STATE.trace.proxy_object_path(TTD_PATTERN.format(var="cursession")).retain_values(keys)
|
||||
hooks.on_stop()
|
||||
|
||||
|
||||
def ghidra_trace_put_events_custom(prefix, cmd):
|
||||
"""
|
||||
Generate events by cmd and put them into the Ghidra trace
|
||||
"""
|
||||
|
||||
STATE.require_tx()
|
||||
with STATE.client.batch() as b:
|
||||
put_events_custom(prefix, cmd)
|
||||
|
||||
|
||||
def ghidra_trace_put_all():
|
||||
"""
|
||||
Put everything currently selected into the Ghidra trace
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
## ###
|
||||
# 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.
|
||||
# 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.
|
||||
##
|
||||
from ctypes import *
|
||||
|
||||
|
@ -26,6 +26,7 @@ from . import imodelobject as mo
|
|||
class ModelIterator(object):
|
||||
def __init__(self, iter):
|
||||
self._iter = iter
|
||||
self._index = 0
|
||||
iter.AddRef()
|
||||
|
||||
# ModelIterator
|
||||
|
@ -39,10 +40,17 @@ class ModelIterator(object):
|
|||
byref(indexer), byref(metadata))
|
||||
except COMError as ce:
|
||||
return None
|
||||
if "ptr=0x0" in str(indexer):
|
||||
next = (self._index, mo.ModelObject(object))
|
||||
self._index += 1
|
||||
return next
|
||||
|
||||
index = mo.ModelObject(indexer)
|
||||
ival = index.GetIntrinsicValue()
|
||||
if ival is None:
|
||||
return (0, mo.ModelObject(object))
|
||||
next = (self._index, mo.ModelObject(object))
|
||||
self._index += 1
|
||||
return next
|
||||
return (ival.value, mo.ModelObject(object))
|
||||
|
||||
def Reset(self):
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
## ###
|
||||
# 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.
|
||||
##
|
||||
from ctypes import *
|
||||
|
||||
from comtypes import COMError
|
||||
from comtypes.gen import DbgMod
|
||||
from comtypes.hresult import S_OK, S_FALSE
|
||||
from pybag.dbgeng import exception
|
||||
|
||||
from . import imodelobject as mo
|
||||
|
||||
|
||||
class ModelMethod(object):
|
||||
def __init__(self, method):
|
||||
self._method = method
|
||||
method.AddRef()
|
||||
|
||||
# ModelMethod
|
||||
|
||||
def Call(self, object, argcount=0, arguments=None):
|
||||
if argcount == 0:
|
||||
arguments = POINTER(DbgMod.IModelObject)()
|
||||
result = POINTER(DbgMod.IModelObject)()
|
||||
metadata = POINTER(DbgMod.IKeyStore)()
|
||||
try:
|
||||
self._method.Call(byref(object), argcount, byref(arguments),
|
||||
byref(result), byref(metadata))
|
||||
except COMError as ce:
|
||||
return None
|
||||
|
||||
return mo.ModelObject(result)
|
||||
|
|
@ -1,17 +1,17 @@
|
|||
## ###
|
||||
# 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.
|
||||
# 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.
|
||||
##
|
||||
from ctypes import *
|
||||
|
||||
|
@ -35,7 +35,7 @@ class RawEnumerator(object):
|
|||
self._keys = None
|
||||
return cnt
|
||||
|
||||
# KeyEnumerator
|
||||
# RawEnumerator
|
||||
|
||||
def GetNext(self):
|
||||
key = BSTR()
|
||||
|
|
|
@ -57,12 +57,12 @@ class ProcessState(object):
|
|||
self.visited = set()
|
||||
self.waiting = False
|
||||
|
||||
def record(self, description=None):
|
||||
def record(self, description=None, snap=None):
|
||||
# print("RECORDING")
|
||||
first = self.first
|
||||
self.first = False
|
||||
if description is not None:
|
||||
commands.STATE.trace.snapshot(description)
|
||||
commands.STATE.trace.snapshot(description, snap=snap)
|
||||
if first:
|
||||
if util.is_kernel():
|
||||
commands.create_generic("Sessions")
|
||||
|
@ -71,6 +71,9 @@ class ProcessState(object):
|
|||
commands.put_processes()
|
||||
commands.put_environment()
|
||||
commands.put_threads()
|
||||
if util.is_trace():
|
||||
commands.init_ttd()
|
||||
#commands.put_events()
|
||||
if self.threads:
|
||||
commands.put_threads()
|
||||
self.threads = False
|
||||
|
@ -106,10 +109,10 @@ class ProcessState(object):
|
|||
commands.put_processes(running=True)
|
||||
commands.put_threads(running=True)
|
||||
|
||||
def record_exited(self, exit_code, description=None):
|
||||
def record_exited(self, exit_code, description=None, snap=None):
|
||||
# print("RECORD_EXITED")
|
||||
if description is not None:
|
||||
commands.STATE.trace.snapshot(description)
|
||||
commands.STATE.trace.snapshot(description, snap=snap)
|
||||
proc = util.selected_process()
|
||||
ipath = commands.PROCESS_PATTERN.format(procnum=proc)
|
||||
procobj = commands.STATE.trace.proxy_object_path(ipath)
|
||||
|
@ -381,13 +384,37 @@ def on_stop(*args):
|
|||
return
|
||||
state = PROC_STATE[proc]
|
||||
state.visited.clear()
|
||||
snap = update_position()
|
||||
with commands.STATE.client.batch():
|
||||
with trace.open_tx("Stopped"):
|
||||
state.record("Stopped")
|
||||
state.record("Stopped", snap)
|
||||
commands.put_event_thread()
|
||||
commands.activate()
|
||||
|
||||
|
||||
def update_position():
|
||||
"""Update the position"""
|
||||
cursor = util.get_cursor()
|
||||
if cursor is None:
|
||||
return None
|
||||
pos = cursor.get_position()
|
||||
lpos = util.get_last_position()
|
||||
rng = range(pos.major, lpos.major)
|
||||
if pos.major > lpos.major:
|
||||
rng = range(lpos.major, pos.major)
|
||||
for i in rng:
|
||||
type = util.get_event_type(i)
|
||||
if type == "modload" or type == "modunload":
|
||||
on_modules_changed()
|
||||
break
|
||||
for i in rng:
|
||||
type = util.get_event_type(i)
|
||||
if type == "threadcreated" or type == "threadterm":
|
||||
on_threads_changed()
|
||||
util.set_last_position(pos)
|
||||
return util.pos2snap(pos)
|
||||
|
||||
|
||||
def on_exited(proc):
|
||||
# print("ON EXITED")
|
||||
if proc not in PROC_STATE:
|
||||
|
|
|
@ -26,7 +26,6 @@ from pybag.dbgeng import core as DbgEng, exception
|
|||
|
||||
from . import util, commands
|
||||
|
||||
|
||||
REGISTRY = MethodRegistry(ThreadPoolExecutor(
|
||||
max_workers=1, thread_name_prefix='MethodRegistry'))
|
||||
|
||||
|
@ -214,7 +213,7 @@ def evaluate(
|
|||
|
||||
@REGISTRY.method(action='refresh', display="Refresh", condition=util.dbg.use_generics)
|
||||
def refresh_generic(node: sch.OBJECT):
|
||||
"""List processes on pydbg's host system."""
|
||||
"""List the children for a generic node."""
|
||||
with commands.open_tracked_tx('Refresh Generic'):
|
||||
commands.ghidra_trace_put_generic(node)
|
||||
|
||||
|
@ -294,6 +293,15 @@ def refresh_modules(node: sch.Schema('ModuleContainer')):
|
|||
commands.ghidra_trace_put_modules()
|
||||
|
||||
|
||||
@REGISTRY.method(action='refresh', display='Refresh Events')
|
||||
def refresh_events(node: sch.Schema('State')):
|
||||
"""
|
||||
Refresh the events list for a trace.
|
||||
"""
|
||||
with commands.open_tracked_tx('Refresh Events'):
|
||||
commands.ghidra_trace_put_events(node)
|
||||
|
||||
|
||||
@REGISTRY.method(action='activate')
|
||||
def activate_process(process: sch.Schema('Process')):
|
||||
"""Switch to the process."""
|
||||
|
@ -377,7 +385,7 @@ def launch_loader(
|
|||
"""
|
||||
command = file
|
||||
if args != None:
|
||||
command += " "+args
|
||||
command += " " + args
|
||||
commands.ghidra_trace_create(command=file, start_trace=False)
|
||||
|
||||
|
||||
|
@ -393,7 +401,7 @@ def launch(
|
|||
"""
|
||||
command = file
|
||||
if args != None:
|
||||
command += " "+args
|
||||
command += " " + args
|
||||
commands.ghidra_trace_create(
|
||||
command, initial_break=initial_break, timeout=timeout, start_trace=False)
|
||||
|
||||
|
@ -411,6 +419,14 @@ def go(process: sch.Schema('Process')):
|
|||
util.dbg.run_async(lambda: dbg().go())
|
||||
|
||||
|
||||
@REGISTRY.method(action='step_ext', display='Go (backwards)', icon='icon.debugger.resume.back', condition=util.dbg.IS_TRACE)
|
||||
@util.dbg.eng_thread
|
||||
def go_back(thread: sch.Schema('Process')):
|
||||
"""Continue execution of the process backwards."""
|
||||
dbg().cmd("g-")
|
||||
dbg().wait()
|
||||
|
||||
|
||||
@REGISTRY.method
|
||||
def interrupt(process: sch.Schema('Process')):
|
||||
"""Interrupt the execution of the debugged program."""
|
||||
|
@ -433,6 +449,22 @@ def step_over(thread: sch.Schema('Thread'), n: ParamDesc(int, display='N')=1):
|
|||
util.dbg.run_async(lambda: dbg().stepo(n))
|
||||
|
||||
|
||||
@REGISTRY.method(action='step_ext', display='Step Into (backwards)', icon='icon.debugger.step.back.into', condition=util.dbg.IS_TRACE)
|
||||
@util.dbg.eng_thread
|
||||
def step_back_into(thread: sch.Schema('Thread'), n: ParamDesc(int, display='N')=1):
|
||||
"""Step one instruction backward exactly."""
|
||||
dbg().cmd("t- " + str(n))
|
||||
dbg().wait()
|
||||
|
||||
|
||||
@REGISTRY.method(action='step_ext', display='Step Over (backwards)', icon='icon.debugger.step.back.over', condition=util.dbg.IS_TRACE)
|
||||
@util.dbg.eng_thread
|
||||
def step_back_over(thread: sch.Schema('Thread'), n: ParamDesc(int, display='N')=1):
|
||||
"""Step one instruction backward, but proceed through subroutine calls."""
|
||||
dbg().cmd("p- " + str(n))
|
||||
dbg().wait()
|
||||
|
||||
|
||||
@REGISTRY.method(action='step_out')
|
||||
def step_out(thread: sch.Schema('Thread')):
|
||||
"""Execute until the current stack frame returns."""
|
||||
|
@ -448,6 +480,14 @@ def step_to(thread: sch.Schema('Thread'), address: Address, max=None):
|
|||
util.dbg.run_async(lambda: dbg().stepto(address.offset, max))
|
||||
|
||||
|
||||
@REGISTRY.method(action='go_to_time', display='Go To (event)', condition=util.dbg.IS_TRACE)
|
||||
@util.dbg.eng_thread
|
||||
def go_to_time(node: sch.Schema('State'), evt: ParamDesc(str, display='Event')):
|
||||
"""Reset the trace to a specific time."""
|
||||
dbg().cmd("!tt " + evt)
|
||||
dbg().wait()
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_sw_execute')
|
||||
@util.dbg.eng_thread
|
||||
def break_address(process: sch.Schema('Process'), address: Address):
|
||||
|
@ -557,7 +597,7 @@ def read_mem(process: sch.Schema('Process'), range: AddressRange):
|
|||
offset_start, offset_start + range.length() - 1, pages=True, display_result=False)
|
||||
if result['count'] == 0:
|
||||
commands.putmem_state(
|
||||
offset_start, offset_start+range.length() - 1, 'error')
|
||||
offset_start, offset_start + range.length() - 1, 'error')
|
||||
|
||||
|
||||
@REGISTRY.method
|
||||
|
@ -578,5 +618,14 @@ def write_reg(frame: sch.Schema('StackFrame'), name: str, value: bytes):
|
|||
dbg().reg._set_register(name, value)
|
||||
|
||||
|
||||
@REGISTRY.method(display='Refresh Events (custom)', condition=util.dbg.IS_TRACE)
|
||||
@util.dbg.eng_thread
|
||||
def refresh_events_custom(node: sch.Schema('State'), cmd: ParamDesc(str, display='Cmd'),
|
||||
prefix: ParamDesc(str, display='Prefix')="dx -r2 @$cursession.TTD"):
|
||||
"""Parse TTD objects generated from a LINQ command."""
|
||||
with commands.open_tracked_tx('Put Events (custom)'):
|
||||
commands.ghidra_trace_put_events_custom(prefix, cmd)
|
||||
|
||||
|
||||
def dbg():
|
||||
return util.dbg._base
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<schema name="DbgRoot" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
|
||||
<attribute name="Sessions" schema="SessionContainer" required="yes" fixed="yes" />
|
||||
<attribute name="Settings" schema="ANY" />
|
||||
<attribute name="State" schema="ANY" />
|
||||
<attribute name="State" schema="State" />
|
||||
<attribute-alias from="_state" to="State" />
|
||||
<attribute name="Utility" schema="ANY" />
|
||||
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||
|
@ -28,6 +28,11 @@
|
|||
<attribute name="_order" schema="INT" hidden="yes" />
|
||||
<attribute schema="ANY"/>
|
||||
</schema>
|
||||
<schema name="State" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
|
||||
<element schema="VOID" />
|
||||
<attribute name="_order" schema="INT" hidden="yes" />
|
||||
<attribute schema="ANY"/>
|
||||
</schema>
|
||||
<schema name="Selectable" elementResync="NEVER" attributeResync="NEVER">
|
||||
<element schema="OBJECT" />
|
||||
<attribute name="_order" schema="INT" hidden="yes" />
|
||||
|
|
|
@ -38,9 +38,9 @@ from pybag.dbgeng.callbacks import DbgEngCallbacks
|
|||
from pybag.dbgeng.idebugclient import DebugClient
|
||||
|
||||
from ghidradbg.dbgmodel.ihostdatamodelaccess import HostDataModelAccess
|
||||
from ghidradbg.dbgmodel.imodelmethod import ModelMethod
|
||||
from _winapi import STILL_ACTIVE
|
||||
|
||||
|
||||
DbgVersion = namedtuple('DbgVersion', ['full', 'name', 'dotted', 'arch'])
|
||||
|
||||
|
||||
|
@ -81,6 +81,7 @@ class StdInputCallbacks(CoClass):
|
|||
|
||||
|
||||
class _Worker(threading.Thread):
|
||||
|
||||
def __init__(self, new_base, work_queue, dispatch):
|
||||
super().__init__(name='DbgWorker', daemon=True)
|
||||
self.new_base = new_base
|
||||
|
@ -109,6 +110,7 @@ class _Worker(threading.Thread):
|
|||
# https://github.com/python/cpython/blob/main/Lib/concurrent/futures/thread.py
|
||||
# accessed 9 Jan 2024
|
||||
class _WorkItem(object):
|
||||
|
||||
def __init__(self, future, fn, args, kwargs):
|
||||
self.future = future
|
||||
self.fn = fn
|
||||
|
@ -131,6 +133,7 @@ class DebuggeeRunningException(BaseException):
|
|||
|
||||
|
||||
class DbgExecutor(object):
|
||||
|
||||
def __init__(self, ghidra_dbg):
|
||||
self._ghidra_dbg = ghidra_dbg
|
||||
self._work_queue = queue.SimpleQueue()
|
||||
|
@ -139,12 +142,12 @@ class DbgExecutor(object):
|
|||
self._thread.start()
|
||||
self._executing = False
|
||||
|
||||
def submit(self, fn, /, *args, **kwargs):
|
||||
def submit(self, fn, / , *args, **kwargs):
|
||||
f = self._submit_no_exit(fn, *args, **kwargs)
|
||||
self._ghidra_dbg.exit_dispatch()
|
||||
return f
|
||||
|
||||
def _submit_no_exit(self, fn, /, *args, **kwargs):
|
||||
def _submit_no_exit(self, fn, / , *args, **kwargs):
|
||||
f = Future()
|
||||
if self._executing and self._ghidra_dbg.IS_REMOTE == False:
|
||||
f.set_exception(DebuggeeRunningException("Debuggee is Running"))
|
||||
|
@ -199,6 +202,7 @@ class AllDbg(pydbg.DebuggerBase):
|
|||
|
||||
|
||||
class GhidraDbg(object):
|
||||
|
||||
def __init__(self):
|
||||
self._queue = DbgExecutor(self)
|
||||
self._thread = self._queue._thread
|
||||
|
@ -239,10 +243,11 @@ class GhidraDbg(object):
|
|||
'load_dump'
|
||||
]:
|
||||
setattr(self, name, self.eng_thread(getattr(base, name)))
|
||||
self.IS_KERNEL = False
|
||||
self.IS_EXDI = False
|
||||
self.IS_REMOTE = os.getenv('OPT_CONNECT_STRING') is not None
|
||||
|
||||
self.IS_KERNEL = False
|
||||
self.IS_EXDI = False
|
||||
self.IS_REMOTE = os.getenv('OPT_CONNECT_STRING') is not None
|
||||
self.IS_TRACE = os.getenv('USE_TTD') == "true"
|
||||
|
||||
def _new_base(self):
|
||||
remote = os.getenv('OPT_CONNECT_STRING')
|
||||
if remote is not None:
|
||||
|
@ -252,14 +257,12 @@ class GhidraDbg(object):
|
|||
else:
|
||||
self._protected_base = AllDbg()
|
||||
|
||||
|
||||
def _generate_client(self, original):
|
||||
cli = POINTER(DbgEng.IDebugClient)()
|
||||
cliptr = POINTER(POINTER(DbgEng.IDebugClient))(cli)
|
||||
hr = original.CreateClient(cliptr)
|
||||
exception.check_err(hr)
|
||||
return DebugClient(client=cli)
|
||||
|
||||
|
||||
@property
|
||||
def _base(self):
|
||||
|
@ -289,12 +292,14 @@ class GhidraDbg(object):
|
|||
For methods inside of GhidraDbg, ensure it runs on the dbgeng
|
||||
thread
|
||||
'''
|
||||
|
||||
@functools.wraps(func)
|
||||
def _func(self, *args, **kwargs):
|
||||
if threading.current_thread() is self._thread:
|
||||
return func(self, *args, **kwargs)
|
||||
else:
|
||||
return self.run(func, self, *args, **kwargs)
|
||||
|
||||
return _func
|
||||
|
||||
def eng_thread(self, func):
|
||||
|
@ -302,12 +307,14 @@ class GhidraDbg(object):
|
|||
For methods and functions outside of GhidraDbg, ensure it
|
||||
runs on this GhidraDbg's dbgeng thread
|
||||
'''
|
||||
|
||||
@functools.wraps(func)
|
||||
def _func(*args, **kwargs):
|
||||
if threading.current_thread() is self._thread:
|
||||
return func(*args, **kwargs)
|
||||
else:
|
||||
return self.run(func, *args, **kwargs)
|
||||
|
||||
return _func
|
||||
|
||||
def _ces_exec_status(self, argument):
|
||||
|
@ -325,6 +332,7 @@ class GhidraDbg(object):
|
|||
def _dispatch_events(self, timeout=DbgEng.WAIT_INFINITE):
|
||||
# NB: pybag's impl doesn't heed standalone
|
||||
self._protected_base._client.DispatchCallbacks(timeout)
|
||||
|
||||
dispatch_events = check_thread(_dispatch_events)
|
||||
|
||||
# no check_thread. Must allow reentry
|
||||
|
@ -393,7 +401,23 @@ class GhidraDbg(object):
|
|||
return None
|
||||
|
||||
|
||||
class TTDState(object):
|
||||
|
||||
def __init__(self):
|
||||
self._cursor = None
|
||||
self._first = None
|
||||
self._last = None
|
||||
self._lastmajor = None
|
||||
self._lastpos = None
|
||||
self.breakpoints = []
|
||||
self.events = {}
|
||||
self.evttypes = {}
|
||||
self.starts = {}
|
||||
self.stops = {}
|
||||
|
||||
|
||||
dbg = GhidraDbg()
|
||||
ttd = TTDState()
|
||||
|
||||
|
||||
@dbg.eng_thread
|
||||
|
@ -750,18 +774,12 @@ def get_proc_id(pid):
|
|||
|
||||
|
||||
def full_mem():
|
||||
sizeptr = 64; #int(gdb.parse_and_eval('sizeof(void*)')) * 8
|
||||
infoLow = DbgEng._MEMORY_BASIC_INFORMATION64()
|
||||
infoLow.BaseAddress = 0
|
||||
infoLow.RegionSize = (1 << (sizeptr-1))
|
||||
infoLow.Protect = 0xFFF
|
||||
infoLow.Name = "UMEM"
|
||||
infoHigh = DbgEng._MEMORY_BASIC_INFORMATION64()
|
||||
infoHigh.BaseAddress = 1 << (sizeptr-1)
|
||||
infoHigh.RegionSize = (1 << (sizeptr-1))
|
||||
infoHigh.Protect = 0xFFF
|
||||
infoHigh.Name = "KMEM"
|
||||
return [ infoLow, infoHigh ]
|
||||
info = DbgEng._MEMORY_BASIC_INFORMATION64()
|
||||
info.BaseAddress = 0
|
||||
info.RegionSize = (1 << 64) - 1
|
||||
info.Protect = 0xFFF
|
||||
info.Name = "full memory"
|
||||
return [ info ]
|
||||
|
||||
|
||||
@dbg.eng_thread
|
||||
|
@ -780,6 +798,16 @@ def get_thread_id(tid):
|
|||
return None
|
||||
|
||||
|
||||
@dbg.eng_thread
|
||||
def open_trace_or_dump(filename):
|
||||
"""Open a trace or dump file"""
|
||||
_cli = dbg._base._client._cli
|
||||
if isinstance(filename, str):
|
||||
filename = filename.encode()
|
||||
hr = _cli.OpenDumpFile(filename)
|
||||
exception.check_err(hr)
|
||||
|
||||
|
||||
def split_path(pathString):
|
||||
list = []
|
||||
segs = pathString.split(".")
|
||||
|
@ -800,6 +828,11 @@ def IHostDataModelAccess():
|
|||
dbg._base._client._cli.QueryInterface(interface=DbgMod.IHostDataModelAccess))
|
||||
|
||||
|
||||
def IModelMethod(method_ptr):
|
||||
return ModelMethod(
|
||||
method_ptr.GetIntrinsicValue().value.QueryInterface(interface=DbgMod.IModelMethod))
|
||||
|
||||
|
||||
@dbg.eng_thread
|
||||
def get_object(relpath):
|
||||
"""Get the list of all threads"""
|
||||
|
@ -810,12 +843,27 @@ def get_object(relpath):
|
|||
root = mgr.GetRootNamespace()
|
||||
pathstr = "Debugger"
|
||||
if relpath != '':
|
||||
pathstr += "."+relpath
|
||||
pathstr += "." + relpath
|
||||
path = split_path(pathstr)
|
||||
# print(f"PATH: {pathstr}")
|
||||
return root.GetOffspring(path)
|
||||
|
||||
|
||||
@dbg.eng_thread
|
||||
def get_method(context_path, method_name):
|
||||
"""Get the list of all threads"""
|
||||
obj = get_object(context_path)
|
||||
keys = obj.EnumerateKeys()
|
||||
(k, v) = keys.GetNext()
|
||||
while k is not None:
|
||||
if k.value == method_name:
|
||||
break
|
||||
(k, v) = keys.GetNext()
|
||||
if k is None:
|
||||
return None
|
||||
return IModelMethod(v)
|
||||
|
||||
|
||||
@dbg.eng_thread
|
||||
def get_attributes(obj):
|
||||
"""Get the list of attributes"""
|
||||
|
@ -826,7 +874,7 @@ def get_attributes(obj):
|
|||
|
||||
@dbg.eng_thread
|
||||
def get_elements(obj):
|
||||
"""Get the list of all threads"""
|
||||
"""Get the list of elements"""
|
||||
if obj is None:
|
||||
return None
|
||||
return obj.GetElements()
|
||||
|
@ -834,7 +882,7 @@ def get_elements(obj):
|
|||
|
||||
@dbg.eng_thread
|
||||
def get_kind(obj):
|
||||
"""Get the list of all threads"""
|
||||
"""Get the kind"""
|
||||
if obj is None:
|
||||
return None
|
||||
kind = obj.GetKind()
|
||||
|
@ -845,7 +893,7 @@ def get_kind(obj):
|
|||
|
||||
@dbg.eng_thread
|
||||
def get_type(obj):
|
||||
"""Get the list of all threads"""
|
||||
"""Get the type"""
|
||||
if obj is None:
|
||||
return None
|
||||
return obj.GetTypeKind()
|
||||
|
@ -853,7 +901,7 @@ def get_type(obj):
|
|||
|
||||
@dbg.eng_thread
|
||||
def get_value(obj):
|
||||
"""Get the list of all threads"""
|
||||
"""Get the value"""
|
||||
if obj is None:
|
||||
return None
|
||||
return obj.GetValue()
|
||||
|
@ -861,7 +909,7 @@ def get_value(obj):
|
|||
|
||||
@dbg.eng_thread
|
||||
def get_intrinsic_value(obj):
|
||||
"""Get the list of all threads"""
|
||||
"""Get the intrinsic value"""
|
||||
if obj is None:
|
||||
return None
|
||||
return obj.GetIntrinsicValue()
|
||||
|
@ -869,7 +917,7 @@ def get_intrinsic_value(obj):
|
|||
|
||||
@dbg.eng_thread
|
||||
def get_target_info(obj):
|
||||
"""Get the list of all threads"""
|
||||
"""Get the target info"""
|
||||
if obj is None:
|
||||
return None
|
||||
return obj.GetTargetInfo()
|
||||
|
@ -877,7 +925,7 @@ def get_target_info(obj):
|
|||
|
||||
@dbg.eng_thread
|
||||
def get_type_info(obj):
|
||||
"""Get the list of all threads"""
|
||||
"""Get the type info"""
|
||||
if obj is None:
|
||||
return None
|
||||
return obj.GetTypeInfo()
|
||||
|
@ -885,7 +933,7 @@ def get_type_info(obj):
|
|||
|
||||
@dbg.eng_thread
|
||||
def get_name(obj):
|
||||
"""Get the list of all threads"""
|
||||
"""Get the name"""
|
||||
if obj is None:
|
||||
return None
|
||||
return obj.GetName().value
|
||||
|
@ -893,7 +941,7 @@ def get_name(obj):
|
|||
|
||||
@dbg.eng_thread
|
||||
def to_display_string(obj):
|
||||
"""Get the list of all threads"""
|
||||
"""Get the display string"""
|
||||
if obj is None:
|
||||
return None
|
||||
return obj.ToDisplayString()
|
||||
|
@ -901,7 +949,7 @@ def to_display_string(obj):
|
|||
|
||||
@dbg.eng_thread
|
||||
def get_location(obj):
|
||||
"""Get the list of all threads"""
|
||||
"""Get the location"""
|
||||
if obj is None:
|
||||
return None
|
||||
try:
|
||||
|
@ -925,6 +973,45 @@ def get_convenience_variable(id):
|
|||
return val
|
||||
|
||||
|
||||
def get_cursor():
|
||||
return ttd._cursor
|
||||
|
||||
|
||||
def get_last_position():
|
||||
return ttd._lastpos
|
||||
|
||||
|
||||
def set_last_position(pos):
|
||||
ttd._lastpos = pos
|
||||
|
||||
|
||||
def get_event_type(rng):
|
||||
if ttd.evttypes.__contains__(rng):
|
||||
return ttd.evttypes[rng]
|
||||
|
||||
|
||||
def pos2snap(pos):
|
||||
pmap = get_attributes(pos)
|
||||
major = get_value(pmap["Sequence"])
|
||||
minor = get_value(pmap["Steps"])
|
||||
return mm2snap(major, minor)
|
||||
|
||||
|
||||
def mm2snap(major, minor):
|
||||
index = int(major)
|
||||
if index < 0 or index >= ttd.MAX_STEP:
|
||||
return int(ttd._lastmajor) # << 32
|
||||
snap = index # << 32 + int(minor)
|
||||
return snap
|
||||
|
||||
|
||||
def pos2split(pos):
|
||||
pmap = get_attributes(pos)
|
||||
major = get_value(pmap["Sequence"])
|
||||
minor = get_value(pmap["Steps"])
|
||||
return (major, minor)
|
||||
|
||||
|
||||
def set_convenience_variable(id, value):
|
||||
conv_map[id] = value
|
||||
|
||||
|
@ -952,4 +1039,11 @@ def set_remote(value):
|
|||
def is_remote():
|
||||
return dbg.IS_REMOTE
|
||||
|
||||
|
||||
def set_trace(value):
|
||||
dbg.IS_TRACE = value
|
||||
|
||||
|
||||
def is_trace():
|
||||
return dbg.IS_TRACE
|
||||
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
## ###
|
||||
# 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.
|
||||
##
|
||||
|
||||
# NOTE: libraries must precede EVERYTHING, esp pybag and DbgMod
|
||||
|
||||
from . import libraries, util, commands, methods, hooks
|
|
@ -1,212 +0,0 @@
|
|||
## ###
|
||||
# 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.
|
||||
##
|
||||
from ghidratrace.client import Address, RegVal
|
||||
|
||||
from pybag import pydbg
|
||||
|
||||
from . import util
|
||||
|
||||
language_map = {
|
||||
'ARM': ['AARCH64:BE:64:v8A', 'AARCH64:LE:64:AppleSilicon', 'AARCH64:LE:64:v8A', 'ARM:BE:64:v8', 'ARM:LE:64:v8'],
|
||||
'Itanium': [],
|
||||
'x86': ['x86:LE:32:default'],
|
||||
'x86_64': ['x86:LE:64:default'],
|
||||
'EFI': ['x86:LE:64:default'],
|
||||
}
|
||||
|
||||
data64_compiler_map = {
|
||||
None: 'pointer64',
|
||||
}
|
||||
|
||||
x86_compiler_map = {
|
||||
'windows': 'windows',
|
||||
'Cygwin': 'windows',
|
||||
}
|
||||
|
||||
arm_compiler_map = {
|
||||
'windows': 'windows',
|
||||
}
|
||||
|
||||
compiler_map = {
|
||||
'DATA:BE:64:default': data64_compiler_map,
|
||||
'DATA:LE:64:default': data64_compiler_map,
|
||||
'x86:LE:32:default': x86_compiler_map,
|
||||
'x86:LE:64:default': x86_compiler_map,
|
||||
'AARCH64:BE:64:v8A': arm_compiler_map,
|
||||
'AARCH64:LE:64:AppleSilicon': arm_compiler_map,
|
||||
'AARCH64:LE:64:v8A': arm_compiler_map,
|
||||
'ARM:BE:64:v8': arm_compiler_map,
|
||||
'ARM:LE:64:v8': arm_compiler_map,
|
||||
}
|
||||
|
||||
|
||||
def get_arch():
|
||||
return "x86_64"
|
||||
|
||||
|
||||
def get_endian():
|
||||
return 'little'
|
||||
|
||||
|
||||
def get_osabi():
|
||||
return "windows"
|
||||
|
||||
|
||||
def compute_ghidra_language():
|
||||
# First, check if the parameter is set
|
||||
lang = util.get_convenience_variable('ghidra-language')
|
||||
if lang != 'auto':
|
||||
return lang
|
||||
|
||||
# Get the list of possible languages for the arch. We'll need to sift
|
||||
# through them by endian and probably prefer default/simpler variants. The
|
||||
# heuristic for "simpler" will be 'default' then shortest variant id.
|
||||
arch = get_arch()
|
||||
endian = get_endian()
|
||||
lebe = ':BE:' if endian == 'big' else ':LE:'
|
||||
if not arch in language_map:
|
||||
return 'DATA' + lebe + '64:default'
|
||||
langs = language_map[arch]
|
||||
matched_endian = sorted(
|
||||
(l for l in langs if lebe in l),
|
||||
key=lambda l: 0 if l.endswith(':default') else len(l)
|
||||
)
|
||||
if len(matched_endian) > 0:
|
||||
return matched_endian[0]
|
||||
# NOTE: I'm disinclined to fall back to a language match with wrong endian.
|
||||
return 'DATA' + lebe + '64:default'
|
||||
|
||||
|
||||
def compute_ghidra_compiler(lang):
|
||||
# First, check if the parameter is set
|
||||
comp = util.get_convenience_variable('ghidra-compiler')
|
||||
if comp != 'auto':
|
||||
return comp
|
||||
|
||||
# Check if the selected lang has specific compiler recommendations
|
||||
if not lang in compiler_map:
|
||||
return 'default'
|
||||
comp_map = compiler_map[lang]
|
||||
osabi = get_osabi()
|
||||
if osabi in comp_map:
|
||||
return comp_map[osabi]
|
||||
if None in comp_map:
|
||||
return comp_map[None]
|
||||
return 'default'
|
||||
|
||||
|
||||
def compute_ghidra_lcsp():
|
||||
lang = compute_ghidra_language()
|
||||
comp = compute_ghidra_compiler(lang)
|
||||
return lang, comp
|
||||
|
||||
|
||||
class DefaultMemoryMapper(object):
|
||||
|
||||
def __init__(self, defaultSpace):
|
||||
self.defaultSpace = defaultSpace
|
||||
|
||||
def map(self, proc: int, offset: int):
|
||||
space = self.defaultSpace
|
||||
return self.defaultSpace, Address(space, offset)
|
||||
|
||||
def map_back(self, proc: int, address: Address) -> int:
|
||||
if address.space == self.defaultSpace:
|
||||
return address.offset
|
||||
raise ValueError(f"Address {address} is not in process {proc.GetProcessID()}")
|
||||
|
||||
|
||||
DEFAULT_MEMORY_MAPPER = DefaultMemoryMapper('ram')
|
||||
|
||||
memory_mappers = {}
|
||||
|
||||
|
||||
def compute_memory_mapper(lang):
|
||||
if not lang in memory_mappers:
|
||||
return DEFAULT_MEMORY_MAPPER
|
||||
return memory_mappers[lang]
|
||||
|
||||
|
||||
class DefaultRegisterMapper(object):
|
||||
|
||||
def __init__(self, byte_order):
|
||||
if not byte_order in ['big', 'little']:
|
||||
raise ValueError("Invalid byte_order: {}".format(byte_order))
|
||||
self.byte_order = byte_order
|
||||
self.union_winners = {}
|
||||
|
||||
def map_name(self, proc, name):
|
||||
return name
|
||||
|
||||
|
||||
def map_value(self, proc, name, value):
|
||||
try:
|
||||
### TODO: this seems half-baked
|
||||
av = value.to_bytes(8, "big")
|
||||
except Exception:
|
||||
raise ValueError("Cannot convert {}'s value: '{}', type: '{}'"
|
||||
.format(name, value, type(value)))
|
||||
return RegVal(self.map_name(proc, name), av)
|
||||
|
||||
def map_name_back(self, proc, name):
|
||||
return name
|
||||
|
||||
def map_value_back(self, proc, name, value):
|
||||
return RegVal(self.map_name_back(proc, name), value)
|
||||
|
||||
|
||||
class Intel_x86_64_RegisterMapper(DefaultRegisterMapper):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__('little')
|
||||
|
||||
def map_name(self, proc, name):
|
||||
if name is None:
|
||||
return 'UNKNOWN'
|
||||
if name == 'efl':
|
||||
return 'rflags'
|
||||
if name.startswith('zmm'):
|
||||
# Ghidra only goes up to ymm, right now
|
||||
return 'ymm' + name[3:]
|
||||
return super().map_name(proc, name)
|
||||
|
||||
def map_value(self, proc, name, value):
|
||||
rv = super().map_value(proc, name, value)
|
||||
if rv.name.startswith('ymm') and len(rv.value) > 32:
|
||||
return RegVal(rv.name, rv.value[-32:])
|
||||
return rv
|
||||
|
||||
def map_name_back(self, proc, name):
|
||||
if name == 'rflags':
|
||||
return 'eflags'
|
||||
|
||||
|
||||
DEFAULT_BE_REGISTER_MAPPER = DefaultRegisterMapper('big')
|
||||
DEFAULT_LE_REGISTER_MAPPER = DefaultRegisterMapper('little')
|
||||
|
||||
register_mappers = {
|
||||
'x86:LE:64:default': Intel_x86_64_RegisterMapper()
|
||||
}
|
||||
|
||||
|
||||
def compute_register_mapper(lang):
|
||||
if not lang in register_mappers:
|
||||
if ':BE:' in lang:
|
||||
return DEFAULT_BE_REGISTER_MAPPER
|
||||
if ':LE:' in lang:
|
||||
return DEFAULT_LE_REGISTER_MAPPER
|
||||
return register_mappers[lang]
|
||||
|
File diff suppressed because it is too large
Load diff
|
@ -1,441 +0,0 @@
|
|||
## ###
|
||||
# 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 sys
|
||||
import time
|
||||
import threading
|
||||
|
||||
from pybag import pydbg
|
||||
from pybag.dbgeng.callbacks import EventHandler
|
||||
from pybag.dbgeng import core as DbgEng
|
||||
from pybag.dbgeng import exception
|
||||
from pybag.dbgeng.idebugbreakpoint import DebugBreakpoint
|
||||
|
||||
from . import commands, util
|
||||
|
||||
ALL_EVENTS = 0xFFFF
|
||||
|
||||
class HookState(object):
|
||||
__slots__ = ('installed', 'mem_catchpoint')
|
||||
|
||||
def __init__(self):
|
||||
self.installed = False
|
||||
self.mem_catchpoint = None
|
||||
|
||||
|
||||
class ProcessState(object):
|
||||
__slots__ = ('first', 'regions', 'modules', 'threads', 'breaks', 'watches', 'visited', 'waiting')
|
||||
|
||||
def __init__(self):
|
||||
self.first = True
|
||||
# For things we can detect changes to between stops
|
||||
self.regions = False
|
||||
self.modules = False
|
||||
self.threads = False
|
||||
self.breaks = False
|
||||
self.watches = False
|
||||
# For frames and threads that have already been synced since last stop
|
||||
self.visited = set()
|
||||
self.waiting = True
|
||||
|
||||
def record(self, description=None, snap=None):
|
||||
first = self.first
|
||||
self.first = False
|
||||
if description is not None:
|
||||
commands.STATE.trace.snapshot(description, snap=snap)
|
||||
if first:
|
||||
commands.put_processes()
|
||||
commands.put_environment()
|
||||
if self.threads:
|
||||
commands.put_threads()
|
||||
self.threads = False
|
||||
thread = util.selected_thread()
|
||||
if thread is not None:
|
||||
if first or thread not in self.visited:
|
||||
commands.putreg()
|
||||
commands.putmem("$pc", "1", display_result=False)
|
||||
commands.putmem("$sp", "1", display_result=False)
|
||||
#commands.put_frames()
|
||||
self.visited.add(thread)
|
||||
#frame = util.selected_frame()
|
||||
#hashable_frame = (thread, frame)
|
||||
#if first or hashable_frame not in self.visited:
|
||||
# self.visited.add(hashable_frame)
|
||||
if first or self.regions:
|
||||
commands.put_regions()
|
||||
self.regions = False
|
||||
if first or self.modules:
|
||||
commands.put_modules()
|
||||
self.modules = False
|
||||
if first or self.breaks:
|
||||
commands.put_breakpoints()
|
||||
self.breaks = False
|
||||
|
||||
def record_continued(self):
|
||||
commands.put_processes(running=True)
|
||||
commands.put_threads(running=True)
|
||||
|
||||
def record_exited(self, exit_code, description=None, snap=None):
|
||||
if description is not None:
|
||||
commands.STATE.trace.snapshot(description, snap)
|
||||
proc = util.selected_process()
|
||||
ipath = commands.PROCESS_PATTERN.format(procnum=proc)
|
||||
commands.STATE.trace.proxy_object_path(
|
||||
ipath).set_value('Exit Code', exit_code)
|
||||
|
||||
|
||||
class BrkState(object):
|
||||
__slots__ = ('break_loc_counts',)
|
||||
|
||||
def __init__(self):
|
||||
self.break_loc_counts = {}
|
||||
|
||||
def update_brkloc_count(self, b, count):
|
||||
self.break_loc_counts[b.GetID()] = count
|
||||
|
||||
def get_brkloc_count(self, b):
|
||||
return self.break_loc_counts.get(b.GetID(), 0)
|
||||
|
||||
def del_brkloc_count(self, b):
|
||||
if b not in self.break_loc_counts:
|
||||
return 0 # TODO: Print a warning?
|
||||
count = self.break_loc_counts[b.GetID()]
|
||||
del self.break_loc_counts[b.GetID()]
|
||||
return count
|
||||
|
||||
|
||||
HOOK_STATE = HookState()
|
||||
BRK_STATE = BrkState()
|
||||
PROC_STATE = {}
|
||||
|
||||
|
||||
def on_state_changed(*args):
|
||||
#print("ON_STATE_CHANGED")
|
||||
if args[0] == DbgEng.DEBUG_CES_CURRENT_THREAD:
|
||||
return on_thread_selected(args)
|
||||
elif args[0] == DbgEng.DEBUG_CES_BREAKPOINTS:
|
||||
return on_breakpoint_modified(args)
|
||||
elif args[0] == DbgEng.DEBUG_CES_RADIX:
|
||||
util.set_convenience_variable('output-radix', args[1])
|
||||
return DbgEng.DEBUG_STATUS_GO
|
||||
elif args[0] == DbgEng.DEBUG_CES_EXECUTION_STATUS:
|
||||
proc = util.selected_process()
|
||||
if args[1] & DbgEng.DEBUG_STATUS_INSIDE_WAIT:
|
||||
PROC_STATE[proc].waiting = True
|
||||
return DbgEng.DEBUG_STATUS_GO
|
||||
PROC_STATE[proc].waiting = False
|
||||
commands.put_state(proc)
|
||||
if args[1] == DbgEng.DEBUG_STATUS_BREAK:
|
||||
return on_stop(args)
|
||||
else:
|
||||
return on_cont(args)
|
||||
return DbgEng.DEBUG_STATUS_GO
|
||||
|
||||
|
||||
def on_debuggee_changed(*args):
|
||||
#print("ON_DEBUGGEE_CHANGED")
|
||||
trace = commands.STATE.trace
|
||||
if trace is None:
|
||||
return
|
||||
if args[1] == DbgEng.DEBUG_CDS_REGISTERS:
|
||||
on_register_changed(args[0][1])
|
||||
#if args[1] == DbgEng.DEBUG_CDS_DATA:
|
||||
# on_memory_changed(args[0][1])
|
||||
return DbgEng.DEBUG_STATUS_GO
|
||||
|
||||
|
||||
def on_session_status_changed(*args):
|
||||
#print("ON_STATUS_CHANGED")
|
||||
trace = commands.STATE.trace
|
||||
if trace is None:
|
||||
return
|
||||
if args[0] == DbgEng.DEBUG_SESSION_ACTIVE or args[0] == DbgEng.DEBUG_SSESION_REBOOT:
|
||||
with commands.STATE.client.batch():
|
||||
with trace.open_tx("New Process {}".format(util.selected_process())):
|
||||
commands.put_processes()
|
||||
return DbgEng.DEBUG_STATUS_GO
|
||||
|
||||
|
||||
def on_symbol_state_changed(*args):
|
||||
#print("ON_SYMBOL_STATE_CHANGED")
|
||||
trace = commands.STATE.trace
|
||||
if trace is None:
|
||||
return
|
||||
if args[0] == 1 or args[0] == 2:
|
||||
PROC_STATE[proc].modules = True
|
||||
return DbgEng.DEBUG_STATUS_GO
|
||||
|
||||
|
||||
def on_system_error(*args):
|
||||
print("ON_SYSTEM_ERROR")
|
||||
print(hex(args[0]))
|
||||
trace = commands.STATE.trace
|
||||
if trace is None:
|
||||
return
|
||||
with commands.STATE.client.batch():
|
||||
with trace.open_tx("New Process {}".format(util.selected_process())):
|
||||
commands.put_processes()
|
||||
return DbgEng.DEBUG_STATUS_BREAK
|
||||
|
||||
|
||||
def on_new_process(*args):
|
||||
#print("ON_NEW_PROCESS")
|
||||
trace = commands.STATE.trace
|
||||
if trace is None:
|
||||
return
|
||||
with commands.STATE.client.batch():
|
||||
with trace.open_tx("New Process {}".format(util.selected_process())):
|
||||
commands.put_processes()
|
||||
return DbgEng.DEBUG_STATUS_BREAK
|
||||
|
||||
|
||||
def on_process_selected():
|
||||
#print("PROCESS_SELECTED")
|
||||
proc = util.selected_process()
|
||||
if proc not in PROC_STATE:
|
||||
return
|
||||
trace = commands.STATE.trace
|
||||
if trace is None:
|
||||
return
|
||||
with commands.STATE.client.batch():
|
||||
with trace.open_tx("Process {} selected".format(proc)):
|
||||
PROC_STATE[proc].record()
|
||||
commands.activate()
|
||||
|
||||
|
||||
def on_process_deleted(*args):
|
||||
#print("ON_PROCESS_DELETED")
|
||||
proc = args[0]
|
||||
on_exited(proc)
|
||||
if proc in PROC_STATE:
|
||||
del PROC_STATE[proc]
|
||||
trace = commands.STATE.trace
|
||||
if trace is None:
|
||||
return
|
||||
with commands.STATE.client.batch():
|
||||
with trace.open_tx("Process {} deleted".format(proc)):
|
||||
commands.put_processes() # TODO: Could just delete the one....
|
||||
return DbgEng.DEBUG_STATUS_BREAK
|
||||
|
||||
|
||||
def on_threads_changed(*args):
|
||||
#print("ON_THREADS_CHANGED")
|
||||
proc = util.selected_process()
|
||||
if proc not in PROC_STATE:
|
||||
return DbgEng.DEBUG_STATUS_GO
|
||||
PROC_STATE[proc].threads = True
|
||||
return DbgEng.DEBUG_STATUS_GO
|
||||
|
||||
|
||||
def on_thread_selected(*args):
|
||||
#print("THREAD_SELECTED")
|
||||
nthrd = args[0][1]
|
||||
nproc = util.selected_process()
|
||||
if nproc not in PROC_STATE:
|
||||
return
|
||||
trace = commands.STATE.trace
|
||||
if trace is None:
|
||||
return
|
||||
with commands.STATE.client.batch():
|
||||
with trace.open_tx("Thread {}.{} selected".format(nproc, nthrd)):
|
||||
commands.put_state(nproc)
|
||||
state = PROC_STATE[nproc]
|
||||
if state.waiting:
|
||||
state.record_continued()
|
||||
else:
|
||||
state.record()
|
||||
commands.activate()
|
||||
|
||||
|
||||
def on_register_changed(regnum):
|
||||
#print("REGISTER_CHANGED")
|
||||
proc = util.selected_process()
|
||||
if proc not in PROC_STATE:
|
||||
return
|
||||
trace = commands.STATE.trace
|
||||
if trace is None:
|
||||
return
|
||||
with commands.STATE.client.batch():
|
||||
with trace.open_tx("Register {} changed".format(regnum)):
|
||||
commands.putreg()
|
||||
commands.activate()
|
||||
|
||||
|
||||
def on_cont(*args):
|
||||
proc = util.selected_process()
|
||||
if proc not in PROC_STATE:
|
||||
return
|
||||
trace = commands.STATE.trace
|
||||
if trace is None:
|
||||
return
|
||||
state = PROC_STATE[proc]
|
||||
with commands.STATE.client.batch():
|
||||
with trace.open_tx("Continued"):
|
||||
state.record_continued()
|
||||
return DbgEng.DEBUG_STATUS_GO
|
||||
|
||||
|
||||
def on_stop(*args):
|
||||
proc = util.selected_process()
|
||||
if proc not in PROC_STATE:
|
||||
print("not in state")
|
||||
return
|
||||
trace = commands.STATE.trace
|
||||
if trace is None:
|
||||
print("no trace")
|
||||
return
|
||||
state = PROC_STATE[proc]
|
||||
state.visited.clear()
|
||||
pos = dbg().get_position()
|
||||
rng = range(pos.major, util.lastpos.major)
|
||||
if pos.major > util.lastpos.major:
|
||||
rng = range(util.lastpos.major, pos.major)
|
||||
for i in rng:
|
||||
if util.evttypes.__contains__(i):
|
||||
type = util.evttypes[i]
|
||||
if type == "modload" or type == "modunload":
|
||||
on_modules_changed()
|
||||
if type == "threadcreated" or type == "threadterm":
|
||||
on_threads_changed()
|
||||
util.lastpos = pos
|
||||
with commands.STATE.client.batch():
|
||||
with trace.open_tx("Stopped"):
|
||||
state.record("Stopped", util.pos2snap(pos))
|
||||
commands.put_state(proc)
|
||||
commands.put_event_thread()
|
||||
commands.activate()
|
||||
|
||||
|
||||
def on_exited(proc):
|
||||
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.visited.clear()
|
||||
exit_code = util.GetExitCode()
|
||||
description = "Exited with code {}".format(exit_code)
|
||||
with commands.STATE.client.batch():
|
||||
with trace.open_tx(description):
|
||||
state.record_exited(exit_code, description)
|
||||
commands.activate()
|
||||
|
||||
|
||||
def on_modules_changed(*args):
|
||||
#print("ON_MODULES_CHANGED")
|
||||
proc = util.selected_process()
|
||||
if proc not in PROC_STATE:
|
||||
return DbgEng.DEBUG_STATUS_GO
|
||||
PROC_STATE[proc].modules = True
|
||||
return DbgEng.DEBUG_STATUS_GO
|
||||
|
||||
|
||||
def on_breakpoint_created(bp):
|
||||
proc = util.selected_process()
|
||||
if proc not in PROC_STATE:
|
||||
return
|
||||
PROC_STATE[proc].breaks = True
|
||||
trace = commands.STATE.trace
|
||||
if trace is None:
|
||||
return
|
||||
ibpath = commands.PROC_BREAKS_PATTERN.format(procnum=proc)
|
||||
with commands.STATE.client.batch():
|
||||
with trace.open_tx("Breakpoint {} created".format(bp.id)):
|
||||
ibobj = trace.create_object(ibpath)
|
||||
# Do not use retain_values or it'll remove other locs
|
||||
commands.put_single_breakpoint(bp, ibobj, proc, [])
|
||||
ibobj.insert()
|
||||
|
||||
|
||||
def on_breakpoint_modified(*args):
|
||||
#print("BREAKPOINT_MODIFIED")
|
||||
proc = util.selected_process()
|
||||
if proc not in PROC_STATE:
|
||||
return
|
||||
PROC_STATE[proc].breaks = True
|
||||
trace = commands.STATE.trace
|
||||
if trace is None:
|
||||
return
|
||||
ibpath = commands.PROC_BREAKS_PATTERN.format(procnum=proc)
|
||||
ibobj = trace.create_object(ibpath)
|
||||
bpid = args[0][1]
|
||||
try:
|
||||
bp = dbg()._control.GetBreakpointById(bpid)
|
||||
except exception.E_NOINTERFACE_Error:
|
||||
dbg().breakpoints._remove_stale(bpid)
|
||||
return on_breakpoint_deleted(bpid)
|
||||
return on_breakpoint_created(bp)
|
||||
|
||||
|
||||
def on_breakpoint_deleted(bpt):
|
||||
proc = util.selected_process()
|
||||
if proc not in PROC_STATE:
|
||||
return
|
||||
PROC_STATE[proc].breaks = True
|
||||
trace = commands.STATE.trace
|
||||
if trace is None:
|
||||
return
|
||||
bpath = commands.PROC_BREAK_PATTERN.format(procnum=proc, breaknum=bpt.id)
|
||||
with commands.STATE.client.batch():
|
||||
with trace.open_tx("Breakpoint {} deleted".format(bpt.id)):
|
||||
trace.proxy_object_path(bpath).remove(tree=True)
|
||||
|
||||
|
||||
def on_breakpoint_hit(*args):
|
||||
trace = commands.STATE.trace
|
||||
if trace is None:
|
||||
return
|
||||
with commands.STATE.client.batch():
|
||||
with trace.open_tx("New Process {}".format(util.selected_process())):
|
||||
commands.put_processes()
|
||||
return DbgEng.DEBUG_STATUS_GO
|
||||
|
||||
|
||||
def on_exception(*args):
|
||||
trace = commands.STATE.trace
|
||||
if trace is None:
|
||||
return
|
||||
with commands.STATE.client.batch():
|
||||
with trace.open_tx("New Process {}".format(util.selected_process())):
|
||||
commands.put_processes()
|
||||
return DbgEng.DEBUG_STATUS_GO
|
||||
|
||||
|
||||
def install_hooks():
|
||||
if HOOK_STATE.installed:
|
||||
return
|
||||
HOOK_STATE.installed = True
|
||||
|
||||
def remove_hooks():
|
||||
if not HOOK_STATE.installed:
|
||||
return
|
||||
HOOK_STATE.installed = False
|
||||
|
||||
|
||||
def enable_current_process():
|
||||
proc = util.selected_process()
|
||||
PROC_STATE[proc] = ProcessState()
|
||||
|
||||
|
||||
def disable_current_process():
|
||||
proc = util.selected_process()
|
||||
if proc in PROC_STATE:
|
||||
# Silently ignore already disabled
|
||||
del PROC_STATE[proc]
|
||||
|
||||
def dbg():
|
||||
return util.get_debugger()
|
|
@ -1,78 +0,0 @@
|
|||
## ###
|
||||
# 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 ctypes
|
||||
import os
|
||||
import platform
|
||||
|
||||
import comtypes
|
||||
import comtypes.client
|
||||
|
||||
|
||||
ctypes.windll.kernel32.SetErrorMode(0x0001 | 0x0002 | 0x8000)
|
||||
|
||||
if platform.architecture()[0] == '64bit':
|
||||
dbgdirs = [os.getenv('OPT_DBGMODEL_PATH'),
|
||||
r'C:\Program Files\Windows Kits\10\Debuggers\x64',
|
||||
r'C:\Program Files (x86)\Windows Kits\10\Debuggers\x64']
|
||||
else:
|
||||
dbgdirs = [os.getenv('OPT_DBGMODEL_PATH'),
|
||||
r'C:\Program Files\Windows Kits\10\Debuggers\x86',
|
||||
r'C:\Program Files (x86)\Windows Kits\10\Debuggers\x86']
|
||||
dbgdir = None
|
||||
for _dir in dbgdirs:
|
||||
if _dir is not None and os.path.exists(_dir):
|
||||
dbgdir = _dir
|
||||
break
|
||||
|
||||
if not dbgdir:
|
||||
raise RuntimeError("Windbg install directory not found!")
|
||||
|
||||
print(f"Loading dbgeng and friends from {dbgdir}")
|
||||
|
||||
# preload these to get correct DLLs loaded
|
||||
try:
|
||||
ctypes.windll.LoadLibrary(os.path.join(dbgdir, 'dbghelp.dll'))
|
||||
except Exception as exc:
|
||||
print(fr"LoadLibrary failed: {dbgdir}\dbghelp.dll {exc}")
|
||||
pass
|
||||
try:
|
||||
ctypes.windll.LoadLibrary(os.path.join(dbgdir, 'dbgeng.dll'))
|
||||
except Exception as exc:
|
||||
print(fr"LoadLibrary failed: {dbgdir}\dbgeng.dll {exc}")
|
||||
pass
|
||||
try:
|
||||
ctypes.windll.LoadLibrary(os.path.join(dbgdir, 'DbgModel.dll'))
|
||||
except Exception as exc:
|
||||
print(fr"LoadLibrary failed: {dbgdir}\dbgmodel.dll {exc}")
|
||||
pass
|
||||
try:
|
||||
ctypes.windll.LoadLibrary(os.path.join(dbgdir, 'ttd/TTDReplay.dll'))
|
||||
except Exception as exc:
|
||||
print(fr"LoadLibrary failed: {dbgdir}\ttd\TTDReplay.dll {exc}")
|
||||
pass
|
||||
try:
|
||||
ctypes.windll.LoadLibrary(os.path.join(dbgdir, 'ttd/TTDReplayCPU.dll'))
|
||||
except Exception as exc:
|
||||
print(fr"LoadLibrary failed: {dbgdir}\ttd\TTDReplayCPU.dll {exc}")
|
||||
pass
|
||||
|
||||
try:
|
||||
from comtypes.gen import DbgMod
|
||||
except:
|
||||
tlb = os.path.join(dbgmodel.module_locator(), 'tlb', 'dbgmodel.tlb')
|
||||
print(f"Loading TLB: {tlb}")
|
||||
comtypes.client.GetModule(tlb)
|
||||
from comtypes.gen import DbgMod
|
|
@ -1,538 +0,0 @@
|
|||
## ###
|
||||
# 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.
|
||||
##
|
||||
from concurrent.futures import Future, ThreadPoolExecutor
|
||||
import re
|
||||
import sys
|
||||
|
||||
from ghidratrace import sch
|
||||
from ghidratrace.client import MethodRegistry, ParamDesc, Address, AddressRange
|
||||
|
||||
from pyttd import pyTTD
|
||||
#from pybag import pydbg
|
||||
#from pybag.dbgeng import core as DbgEng
|
||||
|
||||
from . import util, commands, hooks
|
||||
from contextlib import redirect_stdout
|
||||
from io import StringIO
|
||||
|
||||
|
||||
REGISTRY = MethodRegistry(ThreadPoolExecutor(max_workers=1))
|
||||
|
||||
|
||||
def extre(base, ext):
|
||||
return re.compile(base.pattern + ext)
|
||||
|
||||
|
||||
AVAILABLE_PATTERN = re.compile('Available\[(?P<pid>\\d*)\]')
|
||||
WATCHPOINT_PATTERN = re.compile('Watchpoints\[(?P<watchnum>\\d*)\]')
|
||||
BREAKPOINT_PATTERN = re.compile('Breakpoints\[(?P<breaknum>\\d*)\]')
|
||||
BREAK_LOC_PATTERN = extre(BREAKPOINT_PATTERN, '\[(?P<locnum>\\d*)\]')
|
||||
PROCESS_PATTERN = re.compile('Processes\[(?P<procnum>\\d*)\]')
|
||||
PROC_BREAKS_PATTERN = extre(PROCESS_PATTERN, '\.Breakpoints')
|
||||
PROC_BREAKBPT_PATTERN = extre(PROC_BREAKS_PATTERN, '\[(?P<breaknum>\\d*)\]')
|
||||
ENV_PATTERN = extre(PROCESS_PATTERN, '\.Environment')
|
||||
THREADS_PATTERN = extre(PROCESS_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_PATTERN0 = extre(THREAD_PATTERN, '.Registers')
|
||||
REGS_PATTERN = extre(FRAME_PATTERN, '.Registers')
|
||||
MEMORY_PATTERN = extre(PROCESS_PATTERN, '\.Memory')
|
||||
MODULES_PATTERN = extre(PROCESS_PATTERN, '\.Modules')
|
||||
|
||||
|
||||
def find_availpid_by_pattern(pattern, object, err_msg):
|
||||
mat = pattern.fullmatch(object.path)
|
||||
if mat is None:
|
||||
raise TypeError(f"{object} is not {err_msg}")
|
||||
pid = int(mat['pid'])
|
||||
return pid
|
||||
|
||||
|
||||
def find_availpid_by_obj(object):
|
||||
return find_availpid_by_pattern(AVAILABLE_PATTERN, object, "an Available")
|
||||
|
||||
|
||||
def find_proc_by_num(id):
|
||||
if id != util.selected_process():
|
||||
util.select_process(id)
|
||||
return util.selected_process()
|
||||
|
||||
|
||||
def find_proc_by_pattern(object, pattern, err_msg):
|
||||
mat = pattern.fullmatch(object.path)
|
||||
if mat is None:
|
||||
raise TypeError(f"{object} is not {err_msg}")
|
||||
procnum = int(mat['procnum'])
|
||||
return find_proc_by_num(procnum)
|
||||
|
||||
|
||||
def find_proc_by_obj(object):
|
||||
return find_proc_by_pattern(object, PROCESS_PATTERN, "an Process")
|
||||
|
||||
|
||||
def find_proc_by_procbreak_obj(object):
|
||||
return find_proc_by_pattern(object, PROC_BREAKS_PATTERN,
|
||||
"a BreakpointLocationContainer")
|
||||
|
||||
|
||||
def find_proc_by_procwatch_obj(object):
|
||||
return find_proc_by_pattern(object, PROC_WATCHES_PATTERN,
|
||||
"a WatchpointContainer")
|
||||
|
||||
|
||||
def find_proc_by_env_obj(object):
|
||||
return find_proc_by_pattern(object, ENV_PATTERN, "an Environment")
|
||||
|
||||
|
||||
def find_proc_by_threads_obj(object):
|
||||
return find_proc_by_pattern(object, THREADS_PATTERN, "a ThreadContainer")
|
||||
|
||||
|
||||
def find_proc_by_mem_obj(object):
|
||||
return find_proc_by_pattern(object, MEMORY_PATTERN, "a Memory")
|
||||
|
||||
|
||||
def find_proc_by_modules_obj(object):
|
||||
return find_proc_by_pattern(object, MODULES_PATTERN, "a ModuleContainer")
|
||||
|
||||
|
||||
def find_thread_by_num(id):
|
||||
if id != util.selected_thread():
|
||||
util.select_thread(id)
|
||||
return util.selected_thread()
|
||||
|
||||
|
||||
def find_thread_by_pattern(pattern, object, err_msg):
|
||||
mat = pattern.fullmatch(object.path)
|
||||
if mat is None:
|
||||
raise TypeError(f"{object} is not {err_msg}")
|
||||
pnum = int(mat['procnum'])
|
||||
tnum = int(mat['tnum'])
|
||||
find_proc_by_num(pnum)
|
||||
return find_thread_by_num(tnum)
|
||||
|
||||
|
||||
def find_thread_by_obj(object):
|
||||
return find_thread_by_pattern(THREAD_PATTERN, object, "a Thread")
|
||||
|
||||
|
||||
def find_thread_by_stack_obj(object):
|
||||
return find_thread_by_pattern(STACK_PATTERN, object, "a Stack")
|
||||
|
||||
|
||||
def find_thread_by_regs_obj(object):
|
||||
return find_thread_by_pattern(REGS_PATTERN0, object, "a RegisterValueContainer")
|
||||
|
||||
|
||||
def find_frame_by_level(level):
|
||||
return dbg().backtrace_list()[level]
|
||||
|
||||
|
||||
def find_frame_by_pattern(pattern, object, err_msg):
|
||||
mat = pattern.fullmatch(object.path)
|
||||
if mat is None:
|
||||
raise TypeError(f"{object} is not {err_msg}")
|
||||
pnum = int(mat['procnum'])
|
||||
tnum = int(mat['tnum'])
|
||||
level = int(mat['level'])
|
||||
find_proc_by_num(pnum)
|
||||
find_thread_by_num(tnum)
|
||||
return find_frame_by_level(level)
|
||||
|
||||
|
||||
def find_frame_by_obj(object):
|
||||
return find_frame_by_pattern(FRAME_PATTERN, object, "a StackFrame")
|
||||
|
||||
|
||||
def find_bpt_by_number(breaknum):
|
||||
try:
|
||||
bp = util.breakpoints[breaknum]
|
||||
return bp
|
||||
except exception.E_NOINTERFACE_Error:
|
||||
raise KeyError(f"Breakpoints[{breaknum}] does not exist")
|
||||
|
||||
|
||||
def find_bpt_by_pattern(pattern, object, err_msg):
|
||||
mat = pattern.fullmatch(object.path)
|
||||
if mat is None:
|
||||
raise TypeError(f"{object} is not {err_msg}")
|
||||
breaknum = int(mat['breaknum'])
|
||||
return find_bpt_by_number(breaknum)
|
||||
|
||||
|
||||
def find_bpt_by_obj(object):
|
||||
return find_bpt_by_pattern(PROC_BREAKBPT_PATTERN, object, "a BreakpointSpec")
|
||||
|
||||
|
||||
shared_globals = dict()
|
||||
|
||||
|
||||
@REGISTRY.method
|
||||
def execute(cmd: str, to_string: bool=False):
|
||||
"""Execute a CLI command."""
|
||||
# print("***{}***".format(cmd))
|
||||
# sys.stderr.flush()
|
||||
# sys.stdout.flush()
|
||||
if to_string:
|
||||
data = StringIO()
|
||||
with redirect_stdout(data):
|
||||
exec("{}".format(cmd), shared_globals)
|
||||
return data.getvalue()
|
||||
else:
|
||||
exec("{}".format(cmd), shared_globals)
|
||||
|
||||
|
||||
@REGISTRY.method(action='evaluate', display='Evaluate')
|
||||
def evaluate(
|
||||
session: sch.Schema('Session'),
|
||||
expr: ParamDesc(str, display='Expr')):
|
||||
"""Execute a CLI command."""
|
||||
return str(eval("{}".format(expr), shared_globals))
|
||||
|
||||
|
||||
@REGISTRY.method(action='refresh')
|
||||
def refresh_available(node: sch.Schema('AvailableContainer')):
|
||||
"""List processes on pydbg's host system."""
|
||||
with commands.open_tracked_tx('Refresh Available'):
|
||||
commands.ghidra_trace_put_available()
|
||||
|
||||
|
||||
@REGISTRY.method(action='refresh')
|
||||
def refresh_breakpoints(node: sch.Schema('BreakpointContainer')):
|
||||
"""
|
||||
Refresh the list of breakpoints (including locations for the current
|
||||
process).
|
||||
"""
|
||||
with commands.open_tracked_tx('Refresh Breakpoints'):
|
||||
commands.ghidra_trace_put_breakpoints()
|
||||
|
||||
|
||||
@REGISTRY.method(action='refresh')
|
||||
def refresh_processes(node: sch.Schema('ProcessContainer')):
|
||||
"""Refresh the list of processes."""
|
||||
with commands.open_tracked_tx('Refresh Processes'):
|
||||
commands.ghidra_trace_put_threads()
|
||||
|
||||
|
||||
def refresh_environment(node: sch.Schema('Environment')):
|
||||
"""Refresh the environment descriptors (arch, os, endian)."""
|
||||
with commands.open_tracked_tx('Refresh Environment'):
|
||||
commands.ghidra_trace_put_environment()
|
||||
|
||||
|
||||
@REGISTRY.method(action='refresh')
|
||||
def refresh_threads(node: sch.Schema('ThreadContainer')):
|
||||
"""Refresh the list of threads in the process."""
|
||||
with commands.open_tracked_tx('Refresh Threads'):
|
||||
commands.ghidra_trace_put_threads()
|
||||
|
||||
|
||||
@REGISTRY.method(action='refresh')
|
||||
def refresh_stack(node: sch.Schema('Stack')):
|
||||
"""Refresh the backtrace for the thread."""
|
||||
tnum = find_thread_by_stack_obj(node)
|
||||
with commands.open_tracked_tx('Refresh Stack'):
|
||||
commands.ghidra_trace_put_frames()
|
||||
|
||||
|
||||
@REGISTRY.method(action='refresh')
|
||||
def refresh_registers(node: sch.Schema('RegisterValueContainer')):
|
||||
"""Refresh the register values for the frame."""
|
||||
tnum = find_thread_by_regs_obj(node)
|
||||
with commands.open_tracked_tx('Refresh Registers'):
|
||||
commands.ghidra_trace_putreg()
|
||||
|
||||
|
||||
@REGISTRY.method(action='refresh')
|
||||
def refresh_mappings(node: sch.Schema('Memory')):
|
||||
"""Refresh the list of memory regions for the process."""
|
||||
with commands.open_tracked_tx('Refresh Memory Regions'):
|
||||
commands.ghidra_trace_put_regions()
|
||||
|
||||
|
||||
@REGISTRY.method(action='refresh')
|
||||
def refresh_modules(node: sch.Schema('ModuleContainer')):
|
||||
"""
|
||||
Refresh the modules and sections list for the process.
|
||||
|
||||
This will refresh the sections for all modules, not just the selected one.
|
||||
"""
|
||||
with commands.open_tracked_tx('Refresh Modules'):
|
||||
commands.ghidra_trace_put_modules()
|
||||
|
||||
|
||||
@REGISTRY.method(action='activate')
|
||||
def activate_process(process: sch.Schema('Process')):
|
||||
"""Switch to the process."""
|
||||
find_proc_by_obj(process)
|
||||
|
||||
|
||||
@REGISTRY.method(action='activate')
|
||||
def activate_thread(thread: sch.Schema('Thread')):
|
||||
"""Switch to the thread."""
|
||||
find_thread_by_obj(thread)
|
||||
|
||||
|
||||
@REGISTRY.method(action='activate')
|
||||
def activate_frame(frame: sch.Schema('StackFrame')):
|
||||
"""Select the frame."""
|
||||
find_frame_by_obj(frame)
|
||||
|
||||
|
||||
@REGISTRY.method(action='delete')
|
||||
def remove_process(process: sch.Schema('Process')):
|
||||
"""Remove the process."""
|
||||
find_proc_by_obj(process)
|
||||
dbg().detach()
|
||||
|
||||
|
||||
@REGISTRY.method(action='connect', display='Connect')
|
||||
@util.dbg.eng_thread
|
||||
def target(
|
||||
session: sch.Schema('Session'),
|
||||
cmd: ParamDesc(str, display='Command')):
|
||||
"""Connect to a target machine or process."""
|
||||
dbg().attach_kernel(cmd)
|
||||
|
||||
|
||||
@REGISTRY.method(action='attach', display='Attach')
|
||||
@util.dbg.eng_thread
|
||||
def attach_obj(target: sch.Schema('Attachable')):
|
||||
"""Attach the process to the given target."""
|
||||
pid = find_availpid_by_obj(target)
|
||||
dbg().attach(pid)
|
||||
|
||||
|
||||
@REGISTRY.method(action='attach', display='Attach by pid')
|
||||
@util.dbg.eng_thread
|
||||
def attach_pid(
|
||||
session: sch.Schema('Session'),
|
||||
pid: ParamDesc(str, display='PID')):
|
||||
"""Attach the process to the given target."""
|
||||
dbg().attach_proc(int(pid))
|
||||
|
||||
|
||||
@REGISTRY.method(action='attach', display='Attach by name')
|
||||
@util.dbg.eng_thread
|
||||
def attach_name(
|
||||
session: sch.Schema('Session'),
|
||||
name: ParamDesc(str, display='Name')):
|
||||
"""Attach the process to the given target."""
|
||||
dbg().attach_proc(name)
|
||||
|
||||
|
||||
@REGISTRY.method(action='detach', display='Detach')
|
||||
@util.dbg.eng_thread
|
||||
def detach(process: sch.Schema('Process')):
|
||||
"""Detach the process's target."""
|
||||
dbg().detach()
|
||||
|
||||
|
||||
@REGISTRY.method(action='launch', display='Launch')
|
||||
def launch_loader(
|
||||
session: sch.Schema('Session'),
|
||||
file: ParamDesc(str, display='File'),
|
||||
args: ParamDesc(str, display='Arguments')=''):
|
||||
"""
|
||||
Start a native process with the given command line, stopping at the ntdll initial breakpoint.
|
||||
"""
|
||||
command = file
|
||||
if args != None:
|
||||
command += " "+args
|
||||
commands.ghidra_trace_create(command=file, start_trace=False)
|
||||
|
||||
|
||||
@REGISTRY.method(action='launch', display='LaunchEx')
|
||||
def launch(
|
||||
session: sch.Schema('Session'),
|
||||
file: ParamDesc(str, display='File'),
|
||||
args: ParamDesc(str, display='Arguments')='',
|
||||
initial_break: ParamDesc(bool, display='Initial Break')=True,
|
||||
timeout: ParamDesc(int, display='Timeout')=-1):
|
||||
"""
|
||||
Run a native process with the given command line.
|
||||
"""
|
||||
command = file
|
||||
if args != None:
|
||||
command += " "+args
|
||||
commands.ghidra_trace_create(
|
||||
command, initial_break=initial_break, timeout=timeout, start_trace=False)
|
||||
|
||||
|
||||
@REGISTRY.method
|
||||
def kill(process: sch.Schema('Process')):
|
||||
"""Kill execution of the process."""
|
||||
dbg().terminate()
|
||||
|
||||
|
||||
@REGISTRY.method(name='continue', action='resume')
|
||||
def _continue(process: sch.Schema('Process')):
|
||||
"""Continue execution of the process."""
|
||||
dbg().replay_forward(pyTTD.MAX_STEP, util.last)
|
||||
hooks.on_stop()
|
||||
|
||||
|
||||
@REGISTRY.method
|
||||
def interrupt(process: sch.Schema('Process')):
|
||||
"""Interrupt the execution of the debugged program."""
|
||||
print("'interrupt' is unsupported for TTD")
|
||||
|
||||
|
||||
@REGISTRY.method(action='step_into')
|
||||
def step_into(thread: sch.Schema('Thread'), n: ParamDesc(int, display='N')=1):
|
||||
"""Step one instruction exactly."""
|
||||
# find_thread_by_obj(thread)
|
||||
dbg().replay_forward(n, util.last)
|
||||
hooks.on_stop()
|
||||
|
||||
|
||||
@REGISTRY.method(action='step_over')
|
||||
def step_over(thread: sch.Schema('Thread'), n: ParamDesc(int, display='N')=1):
|
||||
"""Step one instruction, but proceed through subroutine calls."""
|
||||
# find_thread_by_obj(thread)
|
||||
dbg().replay_backward(n, util.first)
|
||||
hooks.on_stop()
|
||||
|
||||
|
||||
@REGISTRY.method(action='step_out')
|
||||
def step_out(thread: sch.Schema('Thread')):
|
||||
"""Execute until the current stack frame returns."""
|
||||
dbg().replay_backward(pyTTD.MAX_STEP, util.first)
|
||||
hooks.on_stop()
|
||||
|
||||
|
||||
@REGISTRY.method(action='step_to', display='Step To')
|
||||
def step_to(thread: sch.Schema('Thread'), address: Address, max=None):
|
||||
"""Continue execution up to the given address."""
|
||||
find_thread_by_obj(thread)
|
||||
return dbg().stepto(address.offset, max)
|
||||
|
||||
|
||||
def gen_bpt(offset: int, size: int, flags: int):
|
||||
bp = pyTTD.MemoryWatchpointData(addr=offset, size=size, flags=flags)
|
||||
dbg().add_memory_watchpoint(bp)
|
||||
bpt = util.Watchpoint(offset, size, flags, len(util.breakpoints), bp)
|
||||
util.breakpoints.append(bpt)
|
||||
hooks.on_breakpoint_created(bpt)
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_sw_execute')
|
||||
def break_address(process: sch.Schema('Process'), address: Address):
|
||||
"""Set a breakpoint."""
|
||||
gen_bpt(offset=address.offset, size=4, flags=pyTTD.BP_FLAGS.EXEC)
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_sw_execute')
|
||||
def break_expression(expression: str):
|
||||
"""Set a breakpoint."""
|
||||
# TODO: Escape?
|
||||
dbg().bp(expr=expression)
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_hw_execute')
|
||||
def break_hw_address(process: sch.Schema('Process'), address: Address):
|
||||
"""Set a hardware-assisted breakpoint."""
|
||||
gen_bpt(offset=address.offset, size=4, flags=pyTTD.BP_FLAGS.EXEC)
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_hw_execute')
|
||||
def break_hw_expression(expression: str):
|
||||
"""Set a hardware-assisted breakpoint."""
|
||||
dbg().ba(expr=expression)
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_read')
|
||||
def break_read_range(process: sch.Schema('Process'), range: AddressRange):
|
||||
"""Set a read watchpoint."""
|
||||
gen_bpt(offset=range.min, size=range.length(), flags=pyTTD.BP_FLAGS.READ)
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_read')
|
||||
def break_read_expression(expression: str):
|
||||
"""Set a read watchpoint."""
|
||||
dbg().ba(expr=expression, access=DbgEng.DEBUG_BREAK_READ)
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_write')
|
||||
def break_write_range(process: sch.Schema('Process'), range: AddressRange):
|
||||
"""Set a watchpoint."""
|
||||
gen_bpt(offset=range.min, size=range.length(), flags=pyTTD.BP_FLAGS.WRITE)
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_write')
|
||||
def break_write_expression(expression: str):
|
||||
"""Set a watchpoint."""
|
||||
dbg().ba(expr=expression, access=DbgEng.DEBUG_BREAK_WRITE)
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_access')
|
||||
def break_access_range(process: sch.Schema('Process'), range: AddressRange):
|
||||
"""Set an access watchpoint."""
|
||||
find_proc_by_obj(process)
|
||||
break_read_range(process, range)
|
||||
break_write_range(process, range)
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_access')
|
||||
def break_access_expression(expression: str):
|
||||
"""Set an access watchpoint."""
|
||||
dbg().ba(expr=expression, access=DbgEng.DEBUG_BREAK_READ | DbgEng.DEBUG_BREAK_WRITE)
|
||||
|
||||
|
||||
@REGISTRY.method(action='toggle')
|
||||
def toggle_breakpoint(breakpoint: sch.Schema('BreakpointSpec'), enabled: bool):
|
||||
"""Toggle a breakpoint."""
|
||||
bpt = find_bpt_by_obj(breakpoint)
|
||||
if enabled:
|
||||
dbg().be(bpt.GetId())
|
||||
else:
|
||||
dbg().bd(bpt.GetId())
|
||||
|
||||
|
||||
@REGISTRY.method(action='delete')
|
||||
def delete_breakpoint(breakpoint: sch.Schema('BreakpointSpec')):
|
||||
"""Delete a breakpoint."""
|
||||
bpt = find_bpt_by_obj(breakpoint)
|
||||
dbg().remove_memory_watchpoint(bpt.bp)
|
||||
util.breakpoints.remove(bpt)
|
||||
hooks.on_breakpoint_deleted(bpt)
|
||||
|
||||
|
||||
@REGISTRY.method
|
||||
def read_mem(process: sch.Schema('Process'), range: AddressRange):
|
||||
"""Read memory."""
|
||||
nproc = find_proc_by_obj(process)
|
||||
offset_start = process.trace.memory_mapper.map_back(
|
||||
nproc, Address(range.space, range.min))
|
||||
with commands.open_tracked_tx('Read Memory'):
|
||||
dbg().read_mem(range.min, range.length())
|
||||
|
||||
|
||||
@REGISTRY.method
|
||||
def write_mem(process: sch.Schema('Process'), address: Address, data: bytes):
|
||||
"""Write memory."""
|
||||
print("'write_mem' is unsupported for TTD")
|
||||
|
||||
|
||||
@REGISTRY.method
|
||||
def write_reg(frame: sch.Schema('StackFrame'), name: str, value: bytes):
|
||||
"""Write a register."""
|
||||
print("'write_reg' is unsupported for TTD")
|
||||
|
||||
|
||||
def dbg():
|
||||
return util.get_debugger()
|
|
@ -1,233 +0,0 @@
|
|||
<context>
|
||||
<schema name="TTDSession" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="EventScope" />
|
||||
<interface name="FocusScope" />
|
||||
<interface name="Aggregate" />
|
||||
<element schema="VOID" />
|
||||
<attribute name="Processes" schema="ProcessContainer" required="yes" fixed="yes" />
|
||||
<attribute name="Available" schema="AvailableContainer" required="yes" fixed="yes" />
|
||||
<attribute name="_event_thread" schema="OBJECT" hidden="yes" />
|
||||
<attribute name="_focus" schema="Selectable" required="yes" hidden="yes" />
|
||||
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||
<attribute name="_order" schema="INT" hidden="yes" />
|
||||
<attribute schema="VOID" />
|
||||
</schema>
|
||||
<schema name="Selectable" elementResync="NEVER" attributeResync="NEVER">
|
||||
<element schema="OBJECT" />
|
||||
<attribute name="_order" schema="INT" hidden="yes" />
|
||||
<attribute schema="VOID" />
|
||||
</schema>
|
||||
<schema name="BreakpointContainer" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
|
||||
<element schema="BreakpointSpec" />
|
||||
<attribute name="_order" schema="INT" hidden="yes" />
|
||||
<attribute schema="VOID" />
|
||||
</schema>
|
||||
<schema name="AvailableContainer" canonical="yes" elementResync="ALWAYS" attributeResync="NEVER">
|
||||
<element schema="Attachable" />
|
||||
<attribute name="_order" schema="INT" hidden="yes" />
|
||||
<attribute schema="VOID" />
|
||||
</schema>
|
||||
<schema name="ProcessContainer" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
|
||||
<element schema="Process" />
|
||||
<attribute name="_order" schema="INT" hidden="yes" />
|
||||
<attribute schema="VOID" />
|
||||
</schema>
|
||||
<schema name="BreakpointSpec" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="BreakpointSpec" />
|
||||
<interface name="BreakpointLocation" />
|
||||
<interface name="Togglable" />
|
||||
<element schema="VOID" />
|
||||
<attribute name="Expression" schema="STRING" required="yes" hidden="yes" />
|
||||
<attribute-alias from="_expression" to="Expression" />
|
||||
<attribute name="Kinds" schema="STRING" required="yes" hidden="yes" />
|
||||
<attribute-alias from="_kinds" to="Kinds" />
|
||||
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||
<attribute name="_order" schema="INT" hidden="yes" />
|
||||
<attribute name="Range" schema="RANGE" />
|
||||
<attribute-alias from="_range" to="Range" />
|
||||
<attribute name="_enabled" schema="BOOL" required="yes" hidden="yes" />
|
||||
<attribute name="Commands" schema="STRING" />
|
||||
<attribute name="Condition" schema="STRING" />
|
||||
<attribute name="Hit Count" schema="INT" />
|
||||
<attribute name="Ignore Count" schema="INT" />
|
||||
<attribute name="Pending" schema="BOOL" />
|
||||
<attribute name="Silent" schema="BOOL" />
|
||||
<attribute name="Temporary" schema="BOOL" />
|
||||
<attribute schema="VOID" />
|
||||
</schema>
|
||||
<schema name="Attachable" elementResync="NEVER" attributeResync="NEVER">
|
||||
<element schema="VOID" />
|
||||
<attribute name="PID" schema="LONG" />
|
||||
<attribute-alias from="_pid" to="PID" />
|
||||
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||
<attribute name="_order" schema="INT" hidden="yes" />
|
||||
<attribute schema="VOID" />
|
||||
</schema>
|
||||
<schema name="Process" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="Activatable" />
|
||||
<interface name="Process" />
|
||||
<interface name="Aggregate" />
|
||||
<interface name="ExecutionStateful" />
|
||||
<element schema="VOID" />
|
||||
<attribute name="Threads" schema="ThreadContainer" required="yes" fixed="yes" />
|
||||
<attribute name="Breakpoints" schema="BreakpointContainer" required="yes" fixed="yes" />
|
||||
<attribute name="Exit Code" schema="LONG" />
|
||||
<attribute-alias from="_exit_code" to="Exit Code" />
|
||||
<attribute name="Environment" schema="Environment" required="yes" fixed="yes" />
|
||||
<attribute name="Memory" schema="Memory" required="yes" fixed="yes" />
|
||||
<attribute name="Modules" schema="ModuleContainer" required="yes" fixed="yes" />
|
||||
<attribute name="PID" schema="LONG" hidden="yes" />
|
||||
<attribute-alias from="_pid" to="PID" />
|
||||
<attribute name="State" schema="EXECUTION_STATE" required="yes" hidden="yes" />
|
||||
<attribute-alias from="_state" to="State" />
|
||||
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||
<attribute name="_short_display" schema="STRING" hidden="yes" />
|
||||
<attribute name="_order" schema="INT" hidden="yes" />
|
||||
<attribute schema="VOID" />
|
||||
</schema>
|
||||
<schema name="Environment" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="Environment" />
|
||||
<element schema="VOID" />
|
||||
<attribute name="OS" schema="STRING" />
|
||||
<attribute name="Arch" schema="STRING" />
|
||||
<attribute name="Endian" schema="STRING" />
|
||||
<attribute name="Debugger" schema="STRING" />
|
||||
<attribute-alias from="_os" to="OS" />
|
||||
<attribute-alias from="_arch" to="Arch" />
|
||||
<attribute-alias from="_endian" to="Endian" />
|
||||
<attribute-alias from="_debugger" to="Debugger" />
|
||||
<attribute name="_order" schema="INT" hidden="yes" />
|
||||
<attribute schema="VOID" />
|
||||
</schema>
|
||||
<schema name="ModuleContainer" canonical="yes" elementResync="ONCE" attributeResync="NEVER">
|
||||
<element schema="Module" />
|
||||
<attribute name="_order" schema="INT" hidden="yes" />
|
||||
<attribute schema="VOID" />
|
||||
</schema>
|
||||
<schema name="Memory" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="Memory" />
|
||||
<element schema="MemoryRegion" />
|
||||
<attribute name="_order" schema="INT" hidden="yes" />
|
||||
<attribute schema="VOID" />
|
||||
</schema>
|
||||
<schema name="ThreadContainer" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
|
||||
<element schema="Thread" />
|
||||
<attribute name="_order" schema="INT" hidden="yes" />
|
||||
<attribute schema="VOID" />
|
||||
</schema>
|
||||
<schema name="Method" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="Method" />
|
||||
<element schema="VOID" />
|
||||
<attribute name="_display" schema="STRING" required="yes" fixed="yes" hidden="yes" />
|
||||
<attribute name="_return_type" schema="TYPE" required="yes" fixed="yes" hidden="yes" />
|
||||
<attribute name="_parameters" schema="MAP_PARAMETERS" required="yes" fixed="yes" hidden="yes" />
|
||||
<attribute schema="VOID" fixed="yes" hidden="yes" />
|
||||
</schema>
|
||||
<schema name="Thread" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="Activatable" />
|
||||
<interface name="Thread" />
|
||||
<interface name="ExecutionStateful" />
|
||||
<interface name="Aggregate" />
|
||||
<element schema="VOID" />
|
||||
<attribute name="Stack" schema="Stack" required="yes" fixed="yes" />
|
||||
<attribute name="Registers" schema="RegisterValueContainer" required="yes" fixed="yes" />
|
||||
<attribute name="TID" schema="LONG" />
|
||||
<attribute-alias from="_tid" to="TID" />
|
||||
<attribute name="State" schema="EXECUTION_STATE" required="yes" hidden="yes" />
|
||||
<attribute-alias from="_state" to="State" />
|
||||
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||
<attribute name="_short_display" schema="STRING" hidden="yes" />
|
||||
<attribute name="_order" schema="INT" hidden="yes" />
|
||||
<attribute name="Advance" schema="Method" required="yes" fixed="yes" hidden="yes" />
|
||||
<attribute schema="VOID" />
|
||||
</schema>
|
||||
<schema name="Module" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="Module" />
|
||||
<element schema="VOID" />
|
||||
<attribute name="Sections" schema="SectionContainer" required="yes" fixed="yes" />
|
||||
<attribute name="Symbols" schema="SymbolContainer" required="yes" fixed="yes" />
|
||||
<attribute name="Range" schema="RANGE" />
|
||||
<attribute name="Name" schema="STRING" />
|
||||
<attribute-alias from="_module_name" to="Name" />
|
||||
<attribute-alias from="_range" to="Range" />
|
||||
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||
<attribute name="_order" schema="INT" hidden="yes" />
|
||||
<attribute schema="VOID" />
|
||||
</schema>
|
||||
<schema name="MemoryRegion" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="MemoryRegion" />
|
||||
<element schema="VOID" />
|
||||
<attribute name="Base" schema="LONG" required="yes" fixed="yes" />
|
||||
<attribute name="Object File" schema="STRING" fixed="yes" />
|
||||
<attribute name="_readable" schema="BOOL" required="yes" hidden="yes" />
|
||||
<attribute name="_writable" schema="BOOL" required="yes" hidden="yes" />
|
||||
<attribute name="_executable" schema="BOOL" required="yes" hidden="yes" />
|
||||
<attribute name="Range" schema="RANGE" required="yes" hidden="yes" />
|
||||
<attribute-alias from="_range" to="Range" />
|
||||
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||
<attribute name="_order" schema="INT" hidden="yes" />
|
||||
<attribute schema="VOID" />
|
||||
</schema>
|
||||
<schema name="SectionContainer" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
|
||||
<element schema="Section" />
|
||||
<attribute name="_order" schema="INT" hidden="yes" />
|
||||
<attribute schema="VOID" />
|
||||
</schema>
|
||||
<schema name="Stack" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="Stack" />
|
||||
<element schema="StackFrame" />
|
||||
<attribute name="_order" schema="INT" hidden="yes" />
|
||||
<attribute schema="VOID" />
|
||||
</schema>
|
||||
<schema name="SymbolContainer" canonical="yes" elementResync="ONCE" attributeResync="NEVER">
|
||||
<element schema="Symbol" />
|
||||
<attribute name="_order" schema="INT" hidden="yes" />
|
||||
<attribute schema="VOID" />
|
||||
</schema>
|
||||
<schema name="Symbol" elementResync="NEVER" attributeResync="NEVER">
|
||||
<element schema="VOID" />
|
||||
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||
<attribute name="_order" schema="INT" hidden="yes" />
|
||||
<attribute schema="VOID" />
|
||||
</schema>
|
||||
<schema name="StackFrame" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="Activatable" />
|
||||
<interface name="StackFrame" />
|
||||
<interface name="Aggregate" />
|
||||
<element schema="VOID" />
|
||||
<attribute name="Function" schema="STRING" hidden="yes" />
|
||||
<attribute-alias from="_function" to="Function" />
|
||||
<attribute name="Registers" schema="RegisterValueContainer" required="yes" fixed="yes" />
|
||||
<attribute name="Instruction Offset" schema="ADDRESS" required="yes" />
|
||||
<attribute-alias from="_pc" to="Instruction Offset" />
|
||||
<attribute name="Stack Offset" schema="ADDRESS" />
|
||||
<attribute name="Return Offset" schema="ADDRESS" />
|
||||
<attribute name="Frame Offset" schema="ADDRESS" />
|
||||
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||
<attribute name="_order" schema="INT" hidden="yes" />
|
||||
<attribute schema="ANY" />
|
||||
</schema>
|
||||
<schema name="Section" elementResync="NEVER" attributeResync="NEVER">
|
||||
<interface name="Section" />
|
||||
<element schema="VOID" />
|
||||
<attribute name="Range" schema="RANGE" />
|
||||
<attribute-alias from="_range" to="Range" />
|
||||
<attribute name="Offset" schema="STRING" fixed="yes" />
|
||||
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||
<attribute name="_order" schema="INT" hidden="yes" />
|
||||
<attribute schema="VOID" />
|
||||
</schema>
|
||||
<schema name="RegisterValueContainer" attributeResync="ONCE">
|
||||
<interface name="RegisterContainer" />
|
||||
<attribute name="General Purpose Registers" schema="RegisterBank" />
|
||||
<attribute name="Floating Point Registers" schema="RegisterBank" />
|
||||
<attribute name="Advanced Vector Extensions" schema="RegisterBank" />
|
||||
<attribute name="Memory Protection Extensions" schema="RegisterBank" />
|
||||
<attribute name="_order" schema="INT" hidden="yes" />
|
||||
<attribute schema="VOID" />
|
||||
</schema>
|
||||
<schema name="RegisterBank" canonical="yes" elementResync="ONCE" attributeResync="NEVER">
|
||||
<attribute name="_order" schema="INT" hidden="yes" />
|
||||
<attribute schema="VOID" />
|
||||
</schema>
|
||||
</context>
|
|
@ -1,204 +0,0 @@
|
|||
## ###
|
||||
# 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.
|
||||
##
|
||||
from collections import namedtuple
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
from ctypes import *
|
||||
|
||||
from pyttd import pyTTD
|
||||
|
||||
#from pybag import pydbg
|
||||
#from pybag.dbgeng import core as DbgEng
|
||||
#from pybag.dbgeng import exception
|
||||
#from pybag.dbgeng import util as DbgUtil
|
||||
|
||||
base = False
|
||||
eng = False
|
||||
first = False
|
||||
last = False
|
||||
breakpoints = []
|
||||
events = {}
|
||||
evttypes = {}
|
||||
starts = {}
|
||||
stops = {}
|
||||
lastpos = False
|
||||
DbgVersion = namedtuple('DbgVersion', ['full', 'major', 'minor'])
|
||||
|
||||
|
||||
class Watchpoint(object):
|
||||
def __init__(self, addr, size, flags, id, bp):
|
||||
self.addr = addr
|
||||
self.size = size
|
||||
self.flags = flags
|
||||
self.id = id
|
||||
self.bp = bp
|
||||
self.expr = None
|
||||
|
||||
|
||||
def _compute_pydbg_ver():
|
||||
blurb = "" #get_debugger()._control.GetActualProcessorType()
|
||||
full = ""
|
||||
major = 0
|
||||
minor = 0
|
||||
return DbgVersion(full, int(major), int(minor))
|
||||
|
||||
|
||||
DBG_VERSION = _compute_pydbg_ver()
|
||||
|
||||
|
||||
def get_debugger():
|
||||
return base
|
||||
|
||||
|
||||
def get_target():
|
||||
return 0 # get_debugger()._systems.GetCurrentSystemId()
|
||||
|
||||
|
||||
def get_inst(addr):
|
||||
dbg = get_debugger()
|
||||
ins = DbgUtil.disassemble_instruction(
|
||||
dbg.bitness(), addr, dbg.read_mem(addr, 15))
|
||||
return str(ins)
|
||||
|
||||
|
||||
def get_inst_sz(addr):
|
||||
dbg = get_debugger()
|
||||
ins = DbgUtil.disassemble_instruction(
|
||||
dbg.bitness(), addr, dbg.read_mem(addr, 15))
|
||||
return str(ins.size)
|
||||
|
||||
|
||||
def get_breakpoints():
|
||||
return None
|
||||
|
||||
|
||||
def selected_process():
|
||||
try:
|
||||
return 0
|
||||
# return current_process
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def selected_thread():
|
||||
try:
|
||||
dbg = get_debugger()
|
||||
current = dbg.get_thread_info()
|
||||
return current.threadid
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def selected_frame():
|
||||
return 0 # selected_thread().GetSelectedFrame()
|
||||
|
||||
|
||||
def select_process(id: int):
|
||||
return None
|
||||
|
||||
|
||||
def select_thread(id: int):
|
||||
return None
|
||||
|
||||
|
||||
def select_frame(id: int):
|
||||
# TODO: this needs to be fixed
|
||||
return None
|
||||
|
||||
|
||||
def parse_and_eval(expr):
|
||||
dbg = get_debugger()
|
||||
if expr == "$pc":
|
||||
return dbg.get_program_counter()
|
||||
if expr == "$sp":
|
||||
return dbg.get_context_x86_64().rsp
|
||||
return int(expr)
|
||||
|
||||
|
||||
def get_eval(expr, type=None):
|
||||
ctrl = get_debugger()._control._ctrl
|
||||
ctrl.SetExpressionSyntax(1)
|
||||
value = DbgEng._DEBUG_VALUE()
|
||||
index = c_ulong()
|
||||
if type == None:
|
||||
type = DbgEng.DEBUG_VALUE_INT64
|
||||
hr = ctrl.Evaluate(Expression="{}".format(expr).encode(
|
||||
), DesiredType=type, Value=byref(value), RemainderIndex=byref(index))
|
||||
exception.check_err(hr)
|
||||
if type == DbgEng.DEBUG_VALUE_INT8:
|
||||
return value.u.I8
|
||||
if type == DbgEng.DEBUG_VALUE_INT16:
|
||||
return value.u.I16
|
||||
if type == DbgEng.DEBUG_VALUE_INT32:
|
||||
return value.u.I32
|
||||
if type == DbgEng.DEBUG_VALUE_INT64:
|
||||
return value.u.I64.I64
|
||||
if type == DbgEng.DEBUG_VALUE_FLOAT32:
|
||||
return value.u.F32
|
||||
if type == DbgEng.DEBUG_VALUE_FLOAT64:
|
||||
return value.u.F64
|
||||
if type == DbgEng.DEBUG_VALUE_FLOAT80:
|
||||
return value.u.F80Bytes
|
||||
if type == DbgEng.DEBUG_VALUE_FLOAT82:
|
||||
return value.u.F82Bytes
|
||||
if type == DbgEng.DEBUG_VALUE_FLOAT128:
|
||||
return value.u.F128Bytes
|
||||
|
||||
|
||||
def process_list(running=False):
|
||||
"""process_list() -> list of all processes"""
|
||||
sysids = [0]
|
||||
return sysids
|
||||
|
||||
|
||||
def thread_list():
|
||||
"""thread_list() -> list of all threads"""
|
||||
dbg = get_debugger()
|
||||
return dbg.get_thread_list()
|
||||
|
||||
|
||||
def module_list():
|
||||
"""thread_list() -> list of all threads"""
|
||||
dbg = get_debugger()
|
||||
return dbg.get_module_list()
|
||||
|
||||
|
||||
conv_map = {}
|
||||
|
||||
|
||||
def get_convenience_variable(id):
|
||||
#val = get_target().GetEnvironment().Get(id)
|
||||
if id not in conv_map:
|
||||
return "auto"
|
||||
val = conv_map[id]
|
||||
if val is None:
|
||||
return "auto"
|
||||
return val
|
||||
|
||||
|
||||
def set_convenience_variable(id, value):
|
||||
#env = get_target().GetEnvironment()
|
||||
# return env.Set(id, value, True)
|
||||
conv_map[id] = value
|
||||
|
||||
|
||||
def pos2snap(pos: int):
|
||||
index = int(pos.major)
|
||||
if index < 0 or index >= pyTTD.MAX_STEP:
|
||||
return int(last.major)*1000
|
||||
return index*1000+int(pos.minor)
|
|
@ -1296,16 +1296,6 @@ def map_address(address):
|
|||
return (base, addr)
|
||||
|
||||
|
||||
# def ghidra_trace_put_generic(node):
|
||||
# """
|
||||
# Put the current thread's frames into the Ghidra trace
|
||||
# """
|
||||
#
|
||||
# STATE.require_tx()
|
||||
# with STATE.client.batch() as b:
|
||||
# put_generic(node)
|
||||
|
||||
|
||||
def ghidra_trace_put_all():
|
||||
"""
|
||||
Put everything currently selected into the Ghidra trace
|
||||
|
|
|
@ -955,38 +955,21 @@ exdi:CLSID={29f9906e-9dbe-4d4b-b0fb-6acf7fb6d014},Kd=Guess,DataBreaks=Exdi
|
|||
values are detected. If anyone understand how to extend this search (or knows how to set the
|
||||
base address to sidestep the scan), we would really love some guidance.</P>
|
||||
|
||||
<H3><A name="dbgeng_ttd"></A>TTD (Time-Travel Debugging)</H3>
|
||||
<H3><A name="dbgeng_trace"></A>TTD (Time-Travel Debugging)</H3>
|
||||
|
||||
<P>This is a nascent extension to our launcher for the Windows Debugger. The launcher itself
|
||||
functions well, but lacks full integration. It is not yet properly packaged for the Ghidra
|
||||
distribution, but is available in development environments. It also needs some additional
|
||||
protocol support, namely to integrate its notion of time travel with Ghidra's notion. For the
|
||||
time being, we map our time specifications as follows. MS TTD uses a tuple for its time specs,
|
||||
both displayed in hexadecimal, e.g., "B5:1A". The first is the "major," which we believe counts
|
||||
the events that warrant a snapshot. The second is the "minor," which we believe counts
|
||||
instructions executed since the major event. Thus, we would like to map the major to a Ghidra
|
||||
trace snapshot and the minor to steps of p-code emulation. For example, the "B5:1A" notation
|
||||
would map to "181:26". It should be no surprise the notations are similar, since both MS TTD
|
||||
and Ghidra (as well as several other "timeless" debuggers) use a snapshot-replay strategy to
|
||||
recover past machine states. However, we have not yet worked out how to have Ghidra cooperate
|
||||
with a back end in the replay part of this strategy. Currently, Ghidra will always apply p-code
|
||||
emulation, despite having a perfectly good and performant back end available. So, for the time
|
||||
being, we multiply the major number by 1000, thus reserving that many Ghidra snapshots between
|
||||
major events for MS TTD minor steps. Thus, the notation "B5:1A" will actually map to "181026",
|
||||
that is snapshot 181026 with no steps of p-code emulation. This hack will fall short if you
|
||||
visit a time where the minor number exceeds 999.</P>
|
||||
|
||||
<P>Furthermore, if you use the Ghidra Debugger UI to visit a past snapshot, the back end is not
|
||||
yet informed of your intent. You will probably only see stale records of the machine state.
|
||||
Instead, please use the kd commands from the Terminal to navigate through time. The back end
|
||||
(MS TTD) will perform the replay, record the snapshot, and command Ghidra to navigate
|
||||
there.</P>
|
||||
functions, but lacks full integration. In particular, Ghidra's concept of time is not
|
||||
mapped directly to the TTD concept of time. TTD uses a major/minor scheme for ordering events,
|
||||
where the major index changes when TTD must record a change in state. Events, including thread
|
||||
creation/termination, module loads/unloads, syscalls, and other asynchronous changes, merit
|
||||
new major indices. When you step forward or backward in a trace, the dbgeng API will increment
|
||||
and decrement correspondingly. Ghidra, on the other hand, will only increment.</P>
|
||||
|
||||
<H4>Options</H4>
|
||||
|
||||
<P>This launcher has the same options as the WinDbg launcher, except that the DLL path must
|
||||
contain <TT>dbgmodel.dll</TT> and the scripts that implement TTD. These are most easily
|
||||
obtained by installing WinDbg Preview or later.</P>
|
||||
<P>This launcher has basically the same options as the WinDbg launcher, except that arguments
|
||||
are not included and the DLL path must contain <TT>TTDReplay.dll</TT>
|
||||
and the scripts that implement TTD. These are most easily obtained by installing WinDbg Preview or later.</P>
|
||||
|
||||
<H2>Stock Java Launchers</H2>
|
||||
|
||||
|
|
|
@ -110,10 +110,12 @@ src/main/resources/images/record.png||GHIDRA||||END|
|
|||
src/main/resources/images/register-marker.png||GHIDRA||||END|
|
||||
src/main/resources/images/registers.png||GHIDRA||||END|
|
||||
src/main/resources/images/resume.png||GHIDRA||||END|
|
||||
src/main/resources/images/resumeback.png||GHIDRA||||END|
|
||||
src/main/resources/images/seek-present.png||GHIDRA||||END|
|
||||
src/main/resources/images/select-registers.png||GHIDRA||||END|
|
||||
src/main/resources/images/skipover.png||GHIDRA||||END|
|
||||
src/main/resources/images/stepback.png||GHIDRA||||END|
|
||||
src/main/resources/images/stepbackinto.png||GHIDRA||||END|
|
||||
src/main/resources/images/stepinto.png||GHIDRA||||END|
|
||||
src/main/resources/images/steplast.png||GHIDRA||||END|
|
||||
src/main/resources/images/stepout.png||GHIDRA||||END|
|
||||
|
@ -163,11 +165,13 @@ src/main/svg/record.svg||GHIDRA||||END|
|
|||
src/main/svg/register-marker.svg||GHIDRA||||END|
|
||||
src/main/svg/registers.svg||GHIDRA||||END|
|
||||
src/main/svg/resume.svg||GHIDRA||||END|
|
||||
src/main/svg/resumeback.svg||GHIDRA||||END|
|
||||
src/main/svg/seek-present.svg||GHIDRA||||END|
|
||||
src/main/svg/select-registers.svg||GHIDRA||||END|
|
||||
src/main/svg/skipover.svg||GHIDRA||||END|
|
||||
src/main/svg/stack.svg||GHIDRA||||END|
|
||||
src/main/svg/stepback.svg||GHIDRA||||END|
|
||||
src/main/svg/stepbackinto.svg||GHIDRA||||END|
|
||||
src/main/svg/stepinto.svg||GHIDRA||||END|
|
||||
src/main/svg/steplast.svg||GHIDRA||||END|
|
||||
src/main/svg/stepout.svg||GHIDRA||||END|
|
||||
|
|
|
@ -104,6 +104,7 @@ icon.debugger.processor = memory16.gif // TODO this icon was missing 'kcmprocess
|
|||
icon.debugger.launch = launch.png
|
||||
icon.debugger.attach = attach.png
|
||||
icon.debugger.resume = resume.png
|
||||
icon.debugger.resume.back = resumeback.png
|
||||
icon.debugger.interrupt = interrupt.png
|
||||
icon.debugger.kill = kill.png
|
||||
icon.debugger.detach = detach.png
|
||||
|
@ -111,6 +112,8 @@ icon.debugger.record = record.png
|
|||
icon.debugger.step.into = stepinto.png
|
||||
icon.debugger.step.over = stepover.png
|
||||
icon.debugger.step.back = stepback.png
|
||||
icon.debugger.step.back.into = stepbackinto.png
|
||||
icon.debugger.step.back.over = stepback.png
|
||||
icon.debugger.step.finish = stepout.png
|
||||
icon.debugger.step.last = steplast.png
|
||||
icon.debugger.skip.over = skipover.png
|
||||
|
|
|
@ -112,11 +112,14 @@ public class DebuggerMemviewTraceListener extends TraceDomainObjectListener {
|
|||
!(region instanceof TraceObjectMemoryRegion objRegion)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
TraceObject obj = objRegion.getObject();
|
||||
obj.getOrderedValues(Lifespan.ALL, TraceObjectMemoryRegion.KEY_RANGE, true).forEach(v -> {
|
||||
if (region.getName(v.getMinSnap()).equals("full memory")) {
|
||||
return;
|
||||
}
|
||||
MemoryBox box = new MemoryBox("Region " + region.getName(v.getMinSnap()),
|
||||
MemviewBoxType.VIRTUAL_ALLOC, v.castValue(), v.getLifespan());
|
||||
MemviewBoxType.REGION, v.castValue(), v.getLifespan());
|
||||
updateList.add(box);
|
||||
});
|
||||
updateLabelDebouncer.contact(null);
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* 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.
|
||||
|
@ -38,8 +38,7 @@ class MemviewMapModel extends AbstractSortedTableModel<MemoryBox> {
|
|||
private Map<String, MemoryBox> memMap = new HashMap<>();
|
||||
private MemviewProvider provider;
|
||||
|
||||
private final static String COLUMN_NAMES[] =
|
||||
{ NAME_COL, ASTART_COL, ASTOP_COL, TSTART_COL, TSTOP_COL };
|
||||
private final static String COLUMN_NAMES[] = { NAME_COL, ASTART_COL, ASTOP_COL, TSTART_COL, TSTOP_COL };
|
||||
|
||||
public MemviewMapModel(MemviewProvider provider) {
|
||||
super(ASTART);
|
||||
|
@ -109,9 +108,9 @@ class MemviewMapModel extends AbstractSortedTableModel<MemoryBox> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Convenience method for locating columns by name. Implementation is naive so this should be
|
||||
* overridden if this method is to be called often. This method is not in the TableModel
|
||||
* interface and is not used by the JTable.
|
||||
* Convenience method for locating columns by name. Implementation is naive so
|
||||
* this should be overridden if this method is to be called often. This method
|
||||
* is not in the TableModel interface and is not used by the JTable.
|
||||
*/
|
||||
@Override
|
||||
public int findColumn(String columnName) {
|
||||
|
@ -143,9 +142,10 @@ class MemviewMapModel extends AbstractSortedTableModel<MemoryBox> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the number of records managed by the data source object. A <B>JTable</B> uses this
|
||||
* method to determine how many rows it should create and display. This method should be quick,
|
||||
* as it is call by <B>JTable</B> quite frequently.
|
||||
* Returns the number of records managed by the data source object. A
|
||||
* <B>JTable</B> uses this method to determine how many rows it should create
|
||||
* and display. This method should be quick, as it is call by <B>JTable</B>
|
||||
* quite frequently.
|
||||
*
|
||||
* @return the number or rows in the model
|
||||
* @see #getColumnCount
|
||||
|
@ -165,8 +165,7 @@ class MemviewMapModel extends AbstractSortedTableModel<MemoryBox> {
|
|||
MemoryBox box = memList.get(rowIndex);
|
||||
try {
|
||||
box.getStart();
|
||||
}
|
||||
catch (ConcurrentModificationException e) {
|
||||
} catch (ConcurrentModificationException e) {
|
||||
update();
|
||||
}
|
||||
return memList.get(rowIndex);
|
||||
|
@ -180,25 +179,24 @@ class MemviewMapModel extends AbstractSortedTableModel<MemoryBox> {
|
|||
public Object getColumnValueForRow(MemoryBox box, int columnIndex) {
|
||||
try {
|
||||
switch (columnIndex) {
|
||||
case NAME:
|
||||
return box.getId();
|
||||
case ASTART:
|
||||
return box.getRange().getMinAddress();
|
||||
case ASTOP:
|
||||
return box.getRange().getMaxAddress();
|
||||
case TSTART:
|
||||
return Long.toString(box.getStart());
|
||||
case TSTOP:
|
||||
long end = box.getEnd();
|
||||
if (end == Long.MAX_VALUE) {
|
||||
return "+" + '\u221e' + '\u2025';
|
||||
}
|
||||
return Long.toString(end);
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
case NAME:
|
||||
return box.getId();
|
||||
case ASTART:
|
||||
return box.getRange().getMinAddress();
|
||||
case ASTOP:
|
||||
return box.getRange().getMaxAddress();
|
||||
case TSTART:
|
||||
return Long.toString(box.getStart(), 16);
|
||||
case TSTOP:
|
||||
long end = box.getEnd();
|
||||
if (end == Long.MAX_VALUE) {
|
||||
return "+" + '\u221e' + '\u2025';
|
||||
}
|
||||
return Long.toString(end, 16);
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
catch (ConcurrentModificationException e) {
|
||||
} catch (ConcurrentModificationException e) {
|
||||
update();
|
||||
}
|
||||
return null;
|
||||
|
@ -225,18 +223,18 @@ class MemviewMapModel extends AbstractSortedTableModel<MemoryBox> {
|
|||
public int compare(MemoryBox b1, MemoryBox b2) {
|
||||
|
||||
switch (sortColumn) {
|
||||
case NAME:
|
||||
return b1.getId().compareToIgnoreCase(b2.getId());
|
||||
case ASTART:
|
||||
return (int) (b1.getStartAddress() - b2.getStartAddress());
|
||||
case ASTOP:
|
||||
return (int) (b1.getStopAddress() - b2.getStopAddress());
|
||||
case TSTART:
|
||||
return (int) (b1.getStartTime() - b2.getStartTime());
|
||||
case TSTOP:
|
||||
return (int) (b1.getStopTime() - b2.getStopTime());
|
||||
default:
|
||||
return 0;
|
||||
case NAME:
|
||||
return b1.getId().compareToIgnoreCase(b2.getId());
|
||||
case ASTART:
|
||||
return (int) (b1.getStartAddress() - b2.getStartAddress());
|
||||
case ASTOP:
|
||||
return (int) (b1.getStopAddress() - b2.getStopAddress());
|
||||
case TSTART:
|
||||
return (int) (b1.getStartTime() - b2.getStartTime());
|
||||
case TSTOP:
|
||||
return (int) (b1.getStopTime() - b2.getStopTime());
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* 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.
|
||||
|
@ -348,7 +348,7 @@ public class MemviewPanel extends JPanel implements MouseListener, MouseMotionLi
|
|||
for (MemoryBox memoryBox : boxes) {
|
||||
aval = memoryBox.getId();
|
||||
}
|
||||
return vertical ? tval + ":" + aval : aval + ":" + tval;
|
||||
return vertical ? tval + " : " + aval : aval + " : " + tval;
|
||||
}
|
||||
|
||||
private void parseBoxes(Collection<MemoryBox> boxes) {
|
||||
|
@ -487,7 +487,8 @@ public class MemviewPanel extends JPanel implements MouseListener, MouseMotionLi
|
|||
public String getTagForTick(long tick) {
|
||||
String tval = "";
|
||||
if (0 <= tick && tick < timesArray.length) {
|
||||
tval = Long.toString(timesArray[(int) tick]);
|
||||
Long time = timesArray[(int) tick];
|
||||
tval = Long.toString(time, 16);
|
||||
}
|
||||
return tval;
|
||||
}
|
||||
|
|
BIN
Ghidra/Debug/Debugger/src/main/resources/images/resumeback.png
Normal file
BIN
Ghidra/Debug/Debugger/src/main/resources/images/resumeback.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 354 B |
BIN
Ghidra/Debug/Debugger/src/main/resources/images/stepbackinto.png
Normal file
BIN
Ghidra/Debug/Debugger/src/main/resources/images/stepbackinto.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 367 B |
34
Ghidra/Debug/Debugger/src/main/svg/resumeback.svg
Normal file
34
Ghidra/Debug/Debugger/src/main/svg/resumeback.svg
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="0 0 16 16"
|
||||
version="1.1"
|
||||
id="svg4819"
|
||||
height="16"
|
||||
width="16"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<defs
|
||||
id="defs4821" />
|
||||
<metadata
|
||||
id="metadata4824">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<path
|
||||
id="path842"
|
||||
d="M 12.5,2.1503906 V 13.849609 L 1.970703,8 Z"
|
||||
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#fffffe;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
|
||||
<path
|
||||
id="path856"
|
||||
d="M 12,3 V 13 L 3,8 Z"
|
||||
style="opacity:1;fill:#008000;fill-opacity:1;stroke:none;stroke-width:1.76383;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
57
Ghidra/Debug/Debugger/src/main/svg/stepbackinto.svg
Normal file
57
Ghidra/Debug/Debugger/src/main/svg/stepbackinto.svg
Normal file
|
@ -0,0 +1,57 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
height="16"
|
||||
width="16"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<metadata
|
||||
id="metadata8">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs6" />
|
||||
<rect
|
||||
y="10"
|
||||
x="-15"
|
||||
height="5"
|
||||
width="14"
|
||||
id="rect815"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.191062;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
transform="scale(-1,1)" />
|
||||
<path
|
||||
style="opacity:1;vector-effect:none;fill:#d4aa00;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
|
||||
d="M 5,9.0004758 H 7 V 5.5 C 7,3 7,3 9.5,3 H 15 V 5 H 9.5 C 9,5 9,5 9,5.5 v 3.5004758 l 2,-4.759e-4 -3,3.0004761 z"
|
||||
id="path834" />
|
||||
<rect
|
||||
y="11"
|
||||
x="-14"
|
||||
height="3"
|
||||
width="3"
|
||||
id="rect819"
|
||||
style="fill:#0166a9;fill-opacity:1;stroke:none;stroke-width:2.25;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
transform="scale(-1,1)" />
|
||||
<rect
|
||||
y="11"
|
||||
x="-5"
|
||||
height="3"
|
||||
width="3"
|
||||
id="rect821"
|
||||
style="fill:#0166a9;fill-opacity:1;stroke:none;stroke-width:2.25;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
transform="scale(-1,1)" />
|
||||
<path
|
||||
id="rect4163"
|
||||
d="M 5,9.0004758 H 7 V 5.5 C 7,3 7,3 9.5,3 H 15 V 5 H 9.5 C 9,5 9,5 9,5.5 v 3.5004758 l 2,-4.759e-4 -3,3.0004761 z"
|
||||
style="opacity:1;vector-effect:none;fill:#d4aa00;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
Loading…
Add table
Add a link
Reference in a new issue