mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 09:49:23 +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/kernel-dbgeng.bat||GHIDRA||||END|
|
||||||
data/debugger-launchers/local-dbgeng-attach.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-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-dbgeng.bat||GHIDRA||||END|
|
||||||
data/debugger-launchers/local-ttd.bat||GHIDRA||||END|
|
|
||||||
data/debugger-launchers/remote-dbgeng.bat||GHIDRA||||END|
|
data/debugger-launchers/remote-dbgeng.bat||GHIDRA||||END|
|
||||||
data/debugger-launchers/svrcx-dbgeng.bat||GHIDRA||||END|
|
data/debugger-launchers/svrcx-dbgeng.bat||GHIDRA||||END|
|
||||||
src/main/py/LICENSE||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/dbgmodel/DbgModel.idl||GHIDRA||||END|
|
||||||
src/main/py/src/ghidradbg/schema.xml||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/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,13 +24,14 @@ import time
|
||||||
|
|
||||||
from comtypes import c_ulong
|
from comtypes import c_ulong
|
||||||
from ghidratrace import sch
|
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 import pydbg, userdbg, kerneldbg
|
||||||
from pybag.dbgeng import core as DbgEng
|
from pybag.dbgeng import core as DbgEng
|
||||||
from pybag.dbgeng import exception
|
from pybag.dbgeng import exception
|
||||||
|
|
||||||
from . import util, arch, methods, hooks
|
from . import util, arch, methods, hooks
|
||||||
from .dbgmodel.imodelobject import ModelObjectKind
|
from .dbgmodel.imodelobject import ModelObjectKind
|
||||||
|
|
||||||
if util.is_exdi():
|
if util.is_exdi():
|
||||||
from .exdi import exdi_commands, exdi_methods
|
from .exdi import exdi_commands, exdi_methods
|
||||||
|
|
||||||
|
@ -65,6 +66,9 @@ SECTIONS_ADD_PATTERN = '.Sections'
|
||||||
SECTION_KEY_PATTERN = '[{secname}]'
|
SECTION_KEY_PATTERN = '[{secname}]'
|
||||||
SECTION_ADD_PATTERN = SECTIONS_ADD_PATTERN + SECTION_KEY_PATTERN
|
SECTION_ADD_PATTERN = SECTIONS_ADD_PATTERN + SECTION_KEY_PATTERN
|
||||||
GENERIC_KEY_PATTERN = '[{key}]'
|
GENERIC_KEY_PATTERN = '[{key}]'
|
||||||
|
TTD_PATTERN = 'State.DebuggerVariables.{var}.TTD'
|
||||||
|
|
||||||
|
DESCRIPTION_PATTERN = '[{major}] {type}'
|
||||||
|
|
||||||
# TODO: Symbols
|
# TODO: Symbols
|
||||||
|
|
||||||
|
@ -221,7 +225,8 @@ def start_trace(name):
|
||||||
variant = " (dbgmodel)" if using_dbgmodel else " (dbgeng)"
|
variant = " (dbgmodel)" if using_dbgmodel else " (dbgeng)"
|
||||||
with STATE.trace.open_tx("Create Root Object"):
|
with STATE.trace.open_tx("Create Root Object"):
|
||||||
root = STATE.trace.create_root_object(schema_xml, 'DbgRoot')
|
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:
|
if util.dbg.use_generics:
|
||||||
put_generic(root)
|
put_generic(root)
|
||||||
util.set_convenience_variable('_ghidra_tracing', "true")
|
util.set_convenience_variable('_ghidra_tracing', "true")
|
||||||
|
@ -294,7 +299,8 @@ def ghidra_trace_create_ext(command=None, initialDirectory='.', envVariables="\0
|
||||||
envVariables = None
|
envVariables = None
|
||||||
if envVariables is not None and envVariables.endswith("/0/0") is False:
|
if envVariables is not None and envVariables.endswith("/0/0") is False:
|
||||||
envVariables += "/0/0"
|
envVariables += "/0/0"
|
||||||
dbg._client.CreateProcess2(command, options, initialDirectory, envVariables)
|
dbg._client.CreateProcess2(
|
||||||
|
command, options, initialDirectory, envVariables)
|
||||||
dbg._control.AddEngineOptions(int(engine_options))
|
dbg._control.AddEngineOptions(int(engine_options))
|
||||||
if start_trace:
|
if start_trace:
|
||||||
ghidra_trace_start(command)
|
ghidra_trace_start(command)
|
||||||
|
@ -348,6 +354,19 @@ def ghidra_trace_connect_server(options=None):
|
||||||
dbg._client.ConnectProcessServer(enc_options)
|
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
|
@util.dbg.eng_thread
|
||||||
def ghidra_trace_kill():
|
def ghidra_trace_kill():
|
||||||
"""
|
"""
|
||||||
|
@ -905,7 +924,8 @@ def activate(path=None):
|
||||||
if frame is None:
|
if frame is None:
|
||||||
path = THREAD_PATTERN.format(procnum=nproc, tnum=nthrd)
|
path = THREAD_PATTERN.format(procnum=nproc, tnum=nthrd)
|
||||||
else:
|
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()
|
trace.proxy_object_path(path).activate()
|
||||||
|
|
||||||
|
|
||||||
|
@ -1201,8 +1221,8 @@ def put_regions():
|
||||||
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.set_value('_display', r.Name)
|
||||||
regobj.insert()
|
regobj.insert()
|
||||||
STATE.trace.proxy_object_path(
|
#STATE.trace.proxy_object_path(
|
||||||
MEMORY_PATTERN.format(procnum=nproc)).retain_values(keys)
|
# MEMORY_PATTERN.format(procnum=nproc)).retain_values(keys)
|
||||||
|
|
||||||
|
|
||||||
def ghidra_trace_put_regions():
|
def ghidra_trace_put_regions():
|
||||||
|
@ -1257,6 +1277,24 @@ def put_modules():
|
||||||
procnum=nproc)).retain_values(mod_keys)
|
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():
|
def ghidra_trace_put_modules():
|
||||||
"""
|
"""
|
||||||
Gather object files, if applicable, and write to the trace's 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)
|
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():
|
def ghidra_trace_put_threads():
|
||||||
"""
|
"""
|
||||||
Put the current process's threads into the Ghidra trace
|
Put the current process's threads into the Ghidra trace
|
||||||
|
@ -1402,35 +1458,50 @@ def ghidra_trace_put_frames():
|
||||||
put_frames()
|
put_frames()
|
||||||
|
|
||||||
|
|
||||||
def update_by_container(np, keyval, obj):
|
def update_key(np, keyval):
|
||||||
index = keyval[0]
|
"""
|
||||||
key = ''
|
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"):
|
if np.endswith("Processes") or np.endswith("Threads"):
|
||||||
istate = compute_proc_state(index)
|
istate = compute_proc_state(key)
|
||||||
obj.set_value('State', istate)
|
to.set_value('State', istate)
|
||||||
if np.endswith("Sessions"):
|
if np.endswith("Sessions"):
|
||||||
key = '[{:x}]'.format(index)
|
disp = '[{:x}]'.format(key)
|
||||||
if np.endswith("Processes"):
|
if np.endswith("Processes"):
|
||||||
create_generic(obj.path)
|
create_generic(to.path)
|
||||||
obj.set_value('PID', index)
|
to.set_value('PID', key)
|
||||||
create_generic(obj.path + ".Memory")
|
create_generic(to.path + ".Memory")
|
||||||
if util.is_kernel():
|
if util.is_kernel():
|
||||||
key = '[{:x}]'.format(index)
|
disp = '[{:x}]'.format(key)
|
||||||
else:
|
else:
|
||||||
id = util.get_proc_id(index)
|
id = util.get_proc_id(key)
|
||||||
key = '{:x} [{:x}]'.format(id, index)
|
disp = '{:x} [{:x}]'.format(id, key)
|
||||||
if np.endswith("Breakpoints"):
|
if np.endswith("Breakpoints"):
|
||||||
create_generic(obj.path)
|
create_generic(to.path)
|
||||||
if np.endswith("Threads"):
|
if np.endswith("Threads"):
|
||||||
create_generic(obj.path)
|
create_generic(to.path)
|
||||||
obj.set_value('TID', index)
|
to.set_value('TID', key)
|
||||||
if util.is_kernel():
|
if util.is_kernel():
|
||||||
key = '[{:x}]'.format(index)
|
disp = '[{:x}]'.format(key)
|
||||||
else:
|
else:
|
||||||
id = util.get_thread_id(index)
|
id = util.get_thread_id(key)
|
||||||
key = '{:x} [{:x}]'.format(id, index)
|
disp = '{:x} [{:x}]'.format(id, key)
|
||||||
if np.endswith("Frames"):
|
if np.endswith("Frames"):
|
||||||
mo = util.get_object(obj.path)
|
mo = util.get_object(to.path)
|
||||||
map = util.get_attributes(mo)
|
map = util.get_attributes(mo)
|
||||||
if 'Attributes' in map:
|
if 'Attributes' in map:
|
||||||
attr = map["Attributes"]
|
attr = map["Attributes"]
|
||||||
|
@ -1438,41 +1509,52 @@ def update_by_container(np, keyval, obj):
|
||||||
map = util.get_attributes(attr)
|
map = util.get_attributes(attr)
|
||||||
pc = util.get_value(map["InstructionOffset"])
|
pc = util.get_value(map["InstructionOffset"])
|
||||||
(pc_base, pc_addr) = map_address(pc)
|
(pc_base, pc_addr) = map_address(pc)
|
||||||
obj.set_value('Instruction Offset', pc_addr)
|
to.set_value('Instruction Offset', pc_addr)
|
||||||
key = '#{:x} 0x{:x}'.format(index, pc)
|
disp = '#{:x} 0x{:x}'.format(key, pc)
|
||||||
if np.endswith("Modules"):
|
if np.endswith("Modules"):
|
||||||
create_generic(obj.path)
|
modobjpath=np+'[{:d}]'.format(key)
|
||||||
mo = util.get_object(obj.path)
|
create_generic(to.path, modobjpath=modobjpath)
|
||||||
|
mo = util.get_object(modobjpath)
|
||||||
map = util.get_attributes(mo)
|
map = util.get_attributes(mo)
|
||||||
base = util.get_value(map["BaseAddress"])
|
base = util.get_value(map["BaseAddress"])
|
||||||
size = util.get_value(map["Size"])
|
size = util.get_value(map["Size"])
|
||||||
name = util.get_value(map["Name"])
|
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)
|
(base_base, base_addr) = map_address(base)
|
||||||
obj.set_value('Range', base_addr.extend(size))
|
to.set_value('Range', base_addr.extend(size))
|
||||||
key = '{:x} {:x} {}'.format(index, base, name)
|
disp = '{:x} {:x} {}'.format(key, base, name)
|
||||||
disp = util.to_display_string(keyval[1])
|
disp0 = util.to_display_string(keyval[1])
|
||||||
if disp is not None:
|
if disp0 is not None:
|
||||||
key += " " + disp
|
disp += " " + disp0
|
||||||
if key is not None and key != "":
|
if disp is not None and disp != "":
|
||||||
obj.set_value('_display', key)
|
to.set_value('_display', disp)
|
||||||
|
|
||||||
|
|
||||||
def create_generic(path):
|
def create_generic(path, modobjpath=None):
|
||||||
obj = STATE.trace.create_object(path)
|
obj = STATE.trace.create_object(path)
|
||||||
|
result = put_generic(obj, modobjpath)
|
||||||
obj.insert()
|
obj.insert()
|
||||||
result = put_generic(obj)
|
|
||||||
return result
|
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}")
|
# print(f"put_generic: {node}")
|
||||||
nproc = util.selected_process()
|
nproc = util.selected_process()
|
||||||
if nproc is None:
|
if nproc is None:
|
||||||
return
|
return
|
||||||
nthrd = util.selected_thread()
|
nthrd = util.selected_thread()
|
||||||
|
|
||||||
|
if modobjpath is None:
|
||||||
mo = util.get_object(node.path)
|
mo = util.get_object(node.path)
|
||||||
|
else:
|
||||||
|
mo = util.get_object(modobjpath)
|
||||||
mapper = STATE.trace.register_mapper
|
mapper = STATE.trace.register_mapper
|
||||||
|
|
||||||
attributes = util.get_attributes(mo)
|
attributes = util.get_attributes(mo)
|
||||||
|
@ -1508,8 +1590,8 @@ def put_generic(node):
|
||||||
keys = []
|
keys = []
|
||||||
if elements is not None:
|
if elements is not None:
|
||||||
for el in elements:
|
for el in elements:
|
||||||
index = el[0]
|
key = update_key(node.path, el)
|
||||||
key = GENERIC_KEY_PATTERN.format(key=index)
|
key = GENERIC_KEY_PATTERN.format(key=key)
|
||||||
lpath = node.path + key
|
lpath = node.path + key
|
||||||
lobj = STATE.trace.create_object(lpath)
|
lobj = STATE.trace.create_object(lpath)
|
||||||
update_by_container(node.path, el, lobj)
|
update_by_container(node.path, el, lobj)
|
||||||
|
@ -1557,7 +1639,132 @@ def ghidra_trace_put_generic(node):
|
||||||
|
|
||||||
STATE.require_tx()
|
STATE.require_tx()
|
||||||
with STATE.client.batch() as b:
|
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():
|
def ghidra_trace_put_all():
|
||||||
|
|
|
@ -26,6 +26,7 @@ from . import imodelobject as mo
|
||||||
class ModelIterator(object):
|
class ModelIterator(object):
|
||||||
def __init__(self, iter):
|
def __init__(self, iter):
|
||||||
self._iter = iter
|
self._iter = iter
|
||||||
|
self._index = 0
|
||||||
iter.AddRef()
|
iter.AddRef()
|
||||||
|
|
||||||
# ModelIterator
|
# ModelIterator
|
||||||
|
@ -39,10 +40,17 @@ class ModelIterator(object):
|
||||||
byref(indexer), byref(metadata))
|
byref(indexer), byref(metadata))
|
||||||
except COMError as ce:
|
except COMError as ce:
|
||||||
return None
|
return None
|
||||||
|
if "ptr=0x0" in str(indexer):
|
||||||
|
next = (self._index, mo.ModelObject(object))
|
||||||
|
self._index += 1
|
||||||
|
return next
|
||||||
|
|
||||||
index = mo.ModelObject(indexer)
|
index = mo.ModelObject(indexer)
|
||||||
ival = index.GetIntrinsicValue()
|
ival = index.GetIntrinsicValue()
|
||||||
if ival is None:
|
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))
|
return (ival.value, mo.ModelObject(object))
|
||||||
|
|
||||||
def Reset(self):
|
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)
|
||||||
|
|
|
@ -35,7 +35,7 @@ class RawEnumerator(object):
|
||||||
self._keys = None
|
self._keys = None
|
||||||
return cnt
|
return cnt
|
||||||
|
|
||||||
# KeyEnumerator
|
# RawEnumerator
|
||||||
|
|
||||||
def GetNext(self):
|
def GetNext(self):
|
||||||
key = BSTR()
|
key = BSTR()
|
||||||
|
|
|
@ -57,12 +57,12 @@ class ProcessState(object):
|
||||||
self.visited = set()
|
self.visited = set()
|
||||||
self.waiting = False
|
self.waiting = False
|
||||||
|
|
||||||
def record(self, description=None):
|
def record(self, description=None, snap=None):
|
||||||
# print("RECORDING")
|
# print("RECORDING")
|
||||||
first = self.first
|
first = self.first
|
||||||
self.first = False
|
self.first = False
|
||||||
if description is not None:
|
if description is not None:
|
||||||
commands.STATE.trace.snapshot(description)
|
commands.STATE.trace.snapshot(description, snap=snap)
|
||||||
if first:
|
if first:
|
||||||
if util.is_kernel():
|
if util.is_kernel():
|
||||||
commands.create_generic("Sessions")
|
commands.create_generic("Sessions")
|
||||||
|
@ -71,6 +71,9 @@ class ProcessState(object):
|
||||||
commands.put_processes()
|
commands.put_processes()
|
||||||
commands.put_environment()
|
commands.put_environment()
|
||||||
commands.put_threads()
|
commands.put_threads()
|
||||||
|
if util.is_trace():
|
||||||
|
commands.init_ttd()
|
||||||
|
#commands.put_events()
|
||||||
if self.threads:
|
if self.threads:
|
||||||
commands.put_threads()
|
commands.put_threads()
|
||||||
self.threads = False
|
self.threads = False
|
||||||
|
@ -106,10 +109,10 @@ class ProcessState(object):
|
||||||
commands.put_processes(running=True)
|
commands.put_processes(running=True)
|
||||||
commands.put_threads(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")
|
# print("RECORD_EXITED")
|
||||||
if description is not None:
|
if description is not None:
|
||||||
commands.STATE.trace.snapshot(description)
|
commands.STATE.trace.snapshot(description, snap=snap)
|
||||||
proc = util.selected_process()
|
proc = util.selected_process()
|
||||||
ipath = commands.PROCESS_PATTERN.format(procnum=proc)
|
ipath = commands.PROCESS_PATTERN.format(procnum=proc)
|
||||||
procobj = commands.STATE.trace.proxy_object_path(ipath)
|
procobj = commands.STATE.trace.proxy_object_path(ipath)
|
||||||
|
@ -381,13 +384,37 @@ def on_stop(*args):
|
||||||
return
|
return
|
||||||
state = PROC_STATE[proc]
|
state = PROC_STATE[proc]
|
||||||
state.visited.clear()
|
state.visited.clear()
|
||||||
|
snap = update_position()
|
||||||
with commands.STATE.client.batch():
|
with commands.STATE.client.batch():
|
||||||
with trace.open_tx("Stopped"):
|
with trace.open_tx("Stopped"):
|
||||||
state.record("Stopped")
|
state.record("Stopped", snap)
|
||||||
commands.put_event_thread()
|
commands.put_event_thread()
|
||||||
commands.activate()
|
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):
|
def on_exited(proc):
|
||||||
# print("ON EXITED")
|
# print("ON EXITED")
|
||||||
if proc not in PROC_STATE:
|
if proc not in PROC_STATE:
|
||||||
|
|
|
@ -26,7 +26,6 @@ from pybag.dbgeng import core as DbgEng, exception
|
||||||
|
|
||||||
from . import util, commands
|
from . import util, commands
|
||||||
|
|
||||||
|
|
||||||
REGISTRY = MethodRegistry(ThreadPoolExecutor(
|
REGISTRY = MethodRegistry(ThreadPoolExecutor(
|
||||||
max_workers=1, thread_name_prefix='MethodRegistry'))
|
max_workers=1, thread_name_prefix='MethodRegistry'))
|
||||||
|
|
||||||
|
@ -214,7 +213,7 @@ def evaluate(
|
||||||
|
|
||||||
@REGISTRY.method(action='refresh', display="Refresh", condition=util.dbg.use_generics)
|
@REGISTRY.method(action='refresh', display="Refresh", condition=util.dbg.use_generics)
|
||||||
def refresh_generic(node: sch.OBJECT):
|
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'):
|
with commands.open_tracked_tx('Refresh Generic'):
|
||||||
commands.ghidra_trace_put_generic(node)
|
commands.ghidra_trace_put_generic(node)
|
||||||
|
|
||||||
|
@ -294,6 +293,15 @@ def refresh_modules(node: sch.Schema('ModuleContainer')):
|
||||||
commands.ghidra_trace_put_modules()
|
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')
|
@REGISTRY.method(action='activate')
|
||||||
def activate_process(process: sch.Schema('Process')):
|
def activate_process(process: sch.Schema('Process')):
|
||||||
"""Switch to the process."""
|
"""Switch to the process."""
|
||||||
|
@ -411,6 +419,14 @@ def go(process: sch.Schema('Process')):
|
||||||
util.dbg.run_async(lambda: dbg().go())
|
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
|
@REGISTRY.method
|
||||||
def interrupt(process: sch.Schema('Process')):
|
def interrupt(process: sch.Schema('Process')):
|
||||||
"""Interrupt the execution of the debugged program."""
|
"""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))
|
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')
|
@REGISTRY.method(action='step_out')
|
||||||
def step_out(thread: sch.Schema('Thread')):
|
def step_out(thread: sch.Schema('Thread')):
|
||||||
"""Execute until the current stack frame returns."""
|
"""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))
|
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')
|
@REGISTRY.method(action='break_sw_execute')
|
||||||
@util.dbg.eng_thread
|
@util.dbg.eng_thread
|
||||||
def break_address(process: sch.Schema('Process'), address: Address):
|
def break_address(process: sch.Schema('Process'), address: Address):
|
||||||
|
@ -578,5 +618,14 @@ def write_reg(frame: sch.Schema('StackFrame'), name: str, value: bytes):
|
||||||
dbg().reg._set_register(name, value)
|
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():
|
def dbg():
|
||||||
return util.dbg._base
|
return util.dbg._base
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<schema name="DbgRoot" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
|
<schema name="DbgRoot" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
|
||||||
<attribute name="Sessions" schema="SessionContainer" required="yes" fixed="yes" />
|
<attribute name="Sessions" schema="SessionContainer" required="yes" fixed="yes" />
|
||||||
<attribute name="Settings" schema="ANY" />
|
<attribute name="Settings" schema="ANY" />
|
||||||
<attribute name="State" schema="ANY" />
|
<attribute name="State" schema="State" />
|
||||||
<attribute-alias from="_state" to="State" />
|
<attribute-alias from="_state" to="State" />
|
||||||
<attribute name="Utility" schema="ANY" />
|
<attribute name="Utility" schema="ANY" />
|
||||||
<attribute name="_display" schema="STRING" hidden="yes" />
|
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||||
|
@ -28,6 +28,11 @@
|
||||||
<attribute name="_order" schema="INT" hidden="yes" />
|
<attribute name="_order" schema="INT" hidden="yes" />
|
||||||
<attribute schema="ANY"/>
|
<attribute schema="ANY"/>
|
||||||
</schema>
|
</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">
|
<schema name="Selectable" elementResync="NEVER" attributeResync="NEVER">
|
||||||
<element schema="OBJECT" />
|
<element schema="OBJECT" />
|
||||||
<attribute name="_order" schema="INT" hidden="yes" />
|
<attribute name="_order" schema="INT" hidden="yes" />
|
||||||
|
|
|
@ -38,9 +38,9 @@ from pybag.dbgeng.callbacks import DbgEngCallbacks
|
||||||
from pybag.dbgeng.idebugclient import DebugClient
|
from pybag.dbgeng.idebugclient import DebugClient
|
||||||
|
|
||||||
from ghidradbg.dbgmodel.ihostdatamodelaccess import HostDataModelAccess
|
from ghidradbg.dbgmodel.ihostdatamodelaccess import HostDataModelAccess
|
||||||
|
from ghidradbg.dbgmodel.imodelmethod import ModelMethod
|
||||||
from _winapi import STILL_ACTIVE
|
from _winapi import STILL_ACTIVE
|
||||||
|
|
||||||
|
|
||||||
DbgVersion = namedtuple('DbgVersion', ['full', 'name', 'dotted', 'arch'])
|
DbgVersion = namedtuple('DbgVersion', ['full', 'name', 'dotted', 'arch'])
|
||||||
|
|
||||||
|
|
||||||
|
@ -81,6 +81,7 @@ class StdInputCallbacks(CoClass):
|
||||||
|
|
||||||
|
|
||||||
class _Worker(threading.Thread):
|
class _Worker(threading.Thread):
|
||||||
|
|
||||||
def __init__(self, new_base, work_queue, dispatch):
|
def __init__(self, new_base, work_queue, dispatch):
|
||||||
super().__init__(name='DbgWorker', daemon=True)
|
super().__init__(name='DbgWorker', daemon=True)
|
||||||
self.new_base = new_base
|
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
|
# https://github.com/python/cpython/blob/main/Lib/concurrent/futures/thread.py
|
||||||
# accessed 9 Jan 2024
|
# accessed 9 Jan 2024
|
||||||
class _WorkItem(object):
|
class _WorkItem(object):
|
||||||
|
|
||||||
def __init__(self, future, fn, args, kwargs):
|
def __init__(self, future, fn, args, kwargs):
|
||||||
self.future = future
|
self.future = future
|
||||||
self.fn = fn
|
self.fn = fn
|
||||||
|
@ -131,6 +133,7 @@ class DebuggeeRunningException(BaseException):
|
||||||
|
|
||||||
|
|
||||||
class DbgExecutor(object):
|
class DbgExecutor(object):
|
||||||
|
|
||||||
def __init__(self, ghidra_dbg):
|
def __init__(self, ghidra_dbg):
|
||||||
self._ghidra_dbg = ghidra_dbg
|
self._ghidra_dbg = ghidra_dbg
|
||||||
self._work_queue = queue.SimpleQueue()
|
self._work_queue = queue.SimpleQueue()
|
||||||
|
@ -199,6 +202,7 @@ class AllDbg(pydbg.DebuggerBase):
|
||||||
|
|
||||||
|
|
||||||
class GhidraDbg(object):
|
class GhidraDbg(object):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._queue = DbgExecutor(self)
|
self._queue = DbgExecutor(self)
|
||||||
self._thread = self._queue._thread
|
self._thread = self._queue._thread
|
||||||
|
@ -242,6 +246,7 @@ class GhidraDbg(object):
|
||||||
self.IS_KERNEL = False
|
self.IS_KERNEL = False
|
||||||
self.IS_EXDI = False
|
self.IS_EXDI = False
|
||||||
self.IS_REMOTE = os.getenv('OPT_CONNECT_STRING') is not None
|
self.IS_REMOTE = os.getenv('OPT_CONNECT_STRING') is not None
|
||||||
|
self.IS_TRACE = os.getenv('USE_TTD') == "true"
|
||||||
|
|
||||||
def _new_base(self):
|
def _new_base(self):
|
||||||
remote = os.getenv('OPT_CONNECT_STRING')
|
remote = os.getenv('OPT_CONNECT_STRING')
|
||||||
|
@ -252,7 +257,6 @@ class GhidraDbg(object):
|
||||||
else:
|
else:
|
||||||
self._protected_base = AllDbg()
|
self._protected_base = AllDbg()
|
||||||
|
|
||||||
|
|
||||||
def _generate_client(self, original):
|
def _generate_client(self, original):
|
||||||
cli = POINTER(DbgEng.IDebugClient)()
|
cli = POINTER(DbgEng.IDebugClient)()
|
||||||
cliptr = POINTER(POINTER(DbgEng.IDebugClient))(cli)
|
cliptr = POINTER(POINTER(DbgEng.IDebugClient))(cli)
|
||||||
|
@ -260,7 +264,6 @@ class GhidraDbg(object):
|
||||||
exception.check_err(hr)
|
exception.check_err(hr)
|
||||||
return DebugClient(client=cli)
|
return DebugClient(client=cli)
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def _base(self):
|
def _base(self):
|
||||||
if threading.current_thread() is not self._thread:
|
if threading.current_thread() is not self._thread:
|
||||||
|
@ -289,12 +292,14 @@ class GhidraDbg(object):
|
||||||
For methods inside of GhidraDbg, ensure it runs on the dbgeng
|
For methods inside of GhidraDbg, ensure it runs on the dbgeng
|
||||||
thread
|
thread
|
||||||
'''
|
'''
|
||||||
|
|
||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
def _func(self, *args, **kwargs):
|
def _func(self, *args, **kwargs):
|
||||||
if threading.current_thread() is self._thread:
|
if threading.current_thread() is self._thread:
|
||||||
return func(self, *args, **kwargs)
|
return func(self, *args, **kwargs)
|
||||||
else:
|
else:
|
||||||
return self.run(func, self, *args, **kwargs)
|
return self.run(func, self, *args, **kwargs)
|
||||||
|
|
||||||
return _func
|
return _func
|
||||||
|
|
||||||
def eng_thread(self, func):
|
def eng_thread(self, func):
|
||||||
|
@ -302,12 +307,14 @@ class GhidraDbg(object):
|
||||||
For methods and functions outside of GhidraDbg, ensure it
|
For methods and functions outside of GhidraDbg, ensure it
|
||||||
runs on this GhidraDbg's dbgeng thread
|
runs on this GhidraDbg's dbgeng thread
|
||||||
'''
|
'''
|
||||||
|
|
||||||
@functools.wraps(func)
|
@functools.wraps(func)
|
||||||
def _func(*args, **kwargs):
|
def _func(*args, **kwargs):
|
||||||
if threading.current_thread() is self._thread:
|
if threading.current_thread() is self._thread:
|
||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
else:
|
else:
|
||||||
return self.run(func, *args, **kwargs)
|
return self.run(func, *args, **kwargs)
|
||||||
|
|
||||||
return _func
|
return _func
|
||||||
|
|
||||||
def _ces_exec_status(self, argument):
|
def _ces_exec_status(self, argument):
|
||||||
|
@ -325,6 +332,7 @@ class GhidraDbg(object):
|
||||||
def _dispatch_events(self, timeout=DbgEng.WAIT_INFINITE):
|
def _dispatch_events(self, timeout=DbgEng.WAIT_INFINITE):
|
||||||
# NB: pybag's impl doesn't heed standalone
|
# NB: pybag's impl doesn't heed standalone
|
||||||
self._protected_base._client.DispatchCallbacks(timeout)
|
self._protected_base._client.DispatchCallbacks(timeout)
|
||||||
|
|
||||||
dispatch_events = check_thread(_dispatch_events)
|
dispatch_events = check_thread(_dispatch_events)
|
||||||
|
|
||||||
# no check_thread. Must allow reentry
|
# no check_thread. Must allow reentry
|
||||||
|
@ -393,7 +401,23 @@ class GhidraDbg(object):
|
||||||
return None
|
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()
|
dbg = GhidraDbg()
|
||||||
|
ttd = TTDState()
|
||||||
|
|
||||||
|
|
||||||
@dbg.eng_thread
|
@dbg.eng_thread
|
||||||
|
@ -750,18 +774,12 @@ def get_proc_id(pid):
|
||||||
|
|
||||||
|
|
||||||
def full_mem():
|
def full_mem():
|
||||||
sizeptr = 64; #int(gdb.parse_and_eval('sizeof(void*)')) * 8
|
info = DbgEng._MEMORY_BASIC_INFORMATION64()
|
||||||
infoLow = DbgEng._MEMORY_BASIC_INFORMATION64()
|
info.BaseAddress = 0
|
||||||
infoLow.BaseAddress = 0
|
info.RegionSize = (1 << 64) - 1
|
||||||
infoLow.RegionSize = (1 << (sizeptr-1))
|
info.Protect = 0xFFF
|
||||||
infoLow.Protect = 0xFFF
|
info.Name = "full memory"
|
||||||
infoLow.Name = "UMEM"
|
return [ info ]
|
||||||
infoHigh = DbgEng._MEMORY_BASIC_INFORMATION64()
|
|
||||||
infoHigh.BaseAddress = 1 << (sizeptr-1)
|
|
||||||
infoHigh.RegionSize = (1 << (sizeptr-1))
|
|
||||||
infoHigh.Protect = 0xFFF
|
|
||||||
infoHigh.Name = "KMEM"
|
|
||||||
return [ infoLow, infoHigh ]
|
|
||||||
|
|
||||||
|
|
||||||
@dbg.eng_thread
|
@dbg.eng_thread
|
||||||
|
@ -780,6 +798,16 @@ def get_thread_id(tid):
|
||||||
return None
|
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):
|
def split_path(pathString):
|
||||||
list = []
|
list = []
|
||||||
segs = pathString.split(".")
|
segs = pathString.split(".")
|
||||||
|
@ -800,6 +828,11 @@ def IHostDataModelAccess():
|
||||||
dbg._base._client._cli.QueryInterface(interface=DbgMod.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
|
@dbg.eng_thread
|
||||||
def get_object(relpath):
|
def get_object(relpath):
|
||||||
"""Get the list of all threads"""
|
"""Get the list of all threads"""
|
||||||
|
@ -816,6 +849,21 @@ def get_object(relpath):
|
||||||
return root.GetOffspring(path)
|
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
|
@dbg.eng_thread
|
||||||
def get_attributes(obj):
|
def get_attributes(obj):
|
||||||
"""Get the list of attributes"""
|
"""Get the list of attributes"""
|
||||||
|
@ -826,7 +874,7 @@ def get_attributes(obj):
|
||||||
|
|
||||||
@dbg.eng_thread
|
@dbg.eng_thread
|
||||||
def get_elements(obj):
|
def get_elements(obj):
|
||||||
"""Get the list of all threads"""
|
"""Get the list of elements"""
|
||||||
if obj is None:
|
if obj is None:
|
||||||
return None
|
return None
|
||||||
return obj.GetElements()
|
return obj.GetElements()
|
||||||
|
@ -834,7 +882,7 @@ def get_elements(obj):
|
||||||
|
|
||||||
@dbg.eng_thread
|
@dbg.eng_thread
|
||||||
def get_kind(obj):
|
def get_kind(obj):
|
||||||
"""Get the list of all threads"""
|
"""Get the kind"""
|
||||||
if obj is None:
|
if obj is None:
|
||||||
return None
|
return None
|
||||||
kind = obj.GetKind()
|
kind = obj.GetKind()
|
||||||
|
@ -845,7 +893,7 @@ def get_kind(obj):
|
||||||
|
|
||||||
@dbg.eng_thread
|
@dbg.eng_thread
|
||||||
def get_type(obj):
|
def get_type(obj):
|
||||||
"""Get the list of all threads"""
|
"""Get the type"""
|
||||||
if obj is None:
|
if obj is None:
|
||||||
return None
|
return None
|
||||||
return obj.GetTypeKind()
|
return obj.GetTypeKind()
|
||||||
|
@ -853,7 +901,7 @@ def get_type(obj):
|
||||||
|
|
||||||
@dbg.eng_thread
|
@dbg.eng_thread
|
||||||
def get_value(obj):
|
def get_value(obj):
|
||||||
"""Get the list of all threads"""
|
"""Get the value"""
|
||||||
if obj is None:
|
if obj is None:
|
||||||
return None
|
return None
|
||||||
return obj.GetValue()
|
return obj.GetValue()
|
||||||
|
@ -861,7 +909,7 @@ def get_value(obj):
|
||||||
|
|
||||||
@dbg.eng_thread
|
@dbg.eng_thread
|
||||||
def get_intrinsic_value(obj):
|
def get_intrinsic_value(obj):
|
||||||
"""Get the list of all threads"""
|
"""Get the intrinsic value"""
|
||||||
if obj is None:
|
if obj is None:
|
||||||
return None
|
return None
|
||||||
return obj.GetIntrinsicValue()
|
return obj.GetIntrinsicValue()
|
||||||
|
@ -869,7 +917,7 @@ def get_intrinsic_value(obj):
|
||||||
|
|
||||||
@dbg.eng_thread
|
@dbg.eng_thread
|
||||||
def get_target_info(obj):
|
def get_target_info(obj):
|
||||||
"""Get the list of all threads"""
|
"""Get the target info"""
|
||||||
if obj is None:
|
if obj is None:
|
||||||
return None
|
return None
|
||||||
return obj.GetTargetInfo()
|
return obj.GetTargetInfo()
|
||||||
|
@ -877,7 +925,7 @@ def get_target_info(obj):
|
||||||
|
|
||||||
@dbg.eng_thread
|
@dbg.eng_thread
|
||||||
def get_type_info(obj):
|
def get_type_info(obj):
|
||||||
"""Get the list of all threads"""
|
"""Get the type info"""
|
||||||
if obj is None:
|
if obj is None:
|
||||||
return None
|
return None
|
||||||
return obj.GetTypeInfo()
|
return obj.GetTypeInfo()
|
||||||
|
@ -885,7 +933,7 @@ def get_type_info(obj):
|
||||||
|
|
||||||
@dbg.eng_thread
|
@dbg.eng_thread
|
||||||
def get_name(obj):
|
def get_name(obj):
|
||||||
"""Get the list of all threads"""
|
"""Get the name"""
|
||||||
if obj is None:
|
if obj is None:
|
||||||
return None
|
return None
|
||||||
return obj.GetName().value
|
return obj.GetName().value
|
||||||
|
@ -893,7 +941,7 @@ def get_name(obj):
|
||||||
|
|
||||||
@dbg.eng_thread
|
@dbg.eng_thread
|
||||||
def to_display_string(obj):
|
def to_display_string(obj):
|
||||||
"""Get the list of all threads"""
|
"""Get the display string"""
|
||||||
if obj is None:
|
if obj is None:
|
||||||
return None
|
return None
|
||||||
return obj.ToDisplayString()
|
return obj.ToDisplayString()
|
||||||
|
@ -901,7 +949,7 @@ def to_display_string(obj):
|
||||||
|
|
||||||
@dbg.eng_thread
|
@dbg.eng_thread
|
||||||
def get_location(obj):
|
def get_location(obj):
|
||||||
"""Get the list of all threads"""
|
"""Get the location"""
|
||||||
if obj is None:
|
if obj is None:
|
||||||
return None
|
return None
|
||||||
try:
|
try:
|
||||||
|
@ -925,6 +973,45 @@ def get_convenience_variable(id):
|
||||||
return val
|
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):
|
def set_convenience_variable(id, value):
|
||||||
conv_map[id] = value
|
conv_map[id] = value
|
||||||
|
|
||||||
|
@ -953,3 +1040,10 @@ def is_remote():
|
||||||
return dbg.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)
|
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():
|
def ghidra_trace_put_all():
|
||||||
"""
|
"""
|
||||||
Put everything currently selected into the Ghidra trace
|
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
|
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>
|
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
|
<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
|
functions, but lacks full integration. In particular, Ghidra's concept of time is not
|
||||||
distribution, but is available in development environments. It also needs some additional
|
mapped directly to the TTD concept of time. TTD uses a major/minor scheme for ordering events,
|
||||||
protocol support, namely to integrate its notion of time travel with Ghidra's notion. For the
|
where the major index changes when TTD must record a change in state. Events, including thread
|
||||||
time being, we map our time specifications as follows. MS TTD uses a tuple for its time specs,
|
creation/termination, module loads/unloads, syscalls, and other asynchronous changes, merit
|
||||||
both displayed in hexadecimal, e.g., "B5:1A". The first is the "major," which we believe counts
|
new major indices. When you step forward or backward in a trace, the dbgeng API will increment
|
||||||
the events that warrant a snapshot. The second is the "minor," which we believe counts
|
and decrement correspondingly. Ghidra, on the other hand, will only increment.</P>
|
||||||
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>
|
|
||||||
|
|
||||||
<H4>Options</H4>
|
<H4>Options</H4>
|
||||||
|
|
||||||
<P>This launcher has the same options as the WinDbg launcher, except that the DLL path must
|
<P>This launcher has basically the same options as the WinDbg launcher, except that arguments
|
||||||
contain <TT>dbgmodel.dll</TT> and the scripts that implement TTD. These are most easily
|
are not included and the DLL path must contain <TT>TTDReplay.dll</TT>
|
||||||
obtained by installing WinDbg Preview or later.</P>
|
and the scripts that implement TTD. These are most easily obtained by installing WinDbg Preview or later.</P>
|
||||||
|
|
||||||
<H2>Stock Java Launchers</H2>
|
<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/register-marker.png||GHIDRA||||END|
|
||||||
src/main/resources/images/registers.png||GHIDRA||||END|
|
src/main/resources/images/registers.png||GHIDRA||||END|
|
||||||
src/main/resources/images/resume.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/seek-present.png||GHIDRA||||END|
|
||||||
src/main/resources/images/select-registers.png||GHIDRA||||END|
|
src/main/resources/images/select-registers.png||GHIDRA||||END|
|
||||||
src/main/resources/images/skipover.png||GHIDRA||||END|
|
src/main/resources/images/skipover.png||GHIDRA||||END|
|
||||||
src/main/resources/images/stepback.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/stepinto.png||GHIDRA||||END|
|
||||||
src/main/resources/images/steplast.png||GHIDRA||||END|
|
src/main/resources/images/steplast.png||GHIDRA||||END|
|
||||||
src/main/resources/images/stepout.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/register-marker.svg||GHIDRA||||END|
|
||||||
src/main/svg/registers.svg||GHIDRA||||END|
|
src/main/svg/registers.svg||GHIDRA||||END|
|
||||||
src/main/svg/resume.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/seek-present.svg||GHIDRA||||END|
|
||||||
src/main/svg/select-registers.svg||GHIDRA||||END|
|
src/main/svg/select-registers.svg||GHIDRA||||END|
|
||||||
src/main/svg/skipover.svg||GHIDRA||||END|
|
src/main/svg/skipover.svg||GHIDRA||||END|
|
||||||
src/main/svg/stack.svg||GHIDRA||||END|
|
src/main/svg/stack.svg||GHIDRA||||END|
|
||||||
src/main/svg/stepback.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/stepinto.svg||GHIDRA||||END|
|
||||||
src/main/svg/steplast.svg||GHIDRA||||END|
|
src/main/svg/steplast.svg||GHIDRA||||END|
|
||||||
src/main/svg/stepout.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.launch = launch.png
|
||||||
icon.debugger.attach = attach.png
|
icon.debugger.attach = attach.png
|
||||||
icon.debugger.resume = resume.png
|
icon.debugger.resume = resume.png
|
||||||
|
icon.debugger.resume.back = resumeback.png
|
||||||
icon.debugger.interrupt = interrupt.png
|
icon.debugger.interrupt = interrupt.png
|
||||||
icon.debugger.kill = kill.png
|
icon.debugger.kill = kill.png
|
||||||
icon.debugger.detach = detach.png
|
icon.debugger.detach = detach.png
|
||||||
|
@ -111,6 +112,8 @@ icon.debugger.record = record.png
|
||||||
icon.debugger.step.into = stepinto.png
|
icon.debugger.step.into = stepinto.png
|
||||||
icon.debugger.step.over = stepover.png
|
icon.debugger.step.over = stepover.png
|
||||||
icon.debugger.step.back = stepback.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.finish = stepout.png
|
||||||
icon.debugger.step.last = steplast.png
|
icon.debugger.step.last = steplast.png
|
||||||
icon.debugger.skip.over = skipover.png
|
icon.debugger.skip.over = skipover.png
|
||||||
|
|
|
@ -115,8 +115,11 @@ public class DebuggerMemviewTraceListener extends TraceDomainObjectListener {
|
||||||
|
|
||||||
TraceObject obj = objRegion.getObject();
|
TraceObject obj = objRegion.getObject();
|
||||||
obj.getOrderedValues(Lifespan.ALL, TraceObjectMemoryRegion.KEY_RANGE, true).forEach(v -> {
|
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()),
|
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);
|
updateList.add(box);
|
||||||
});
|
});
|
||||||
updateLabelDebouncer.contact(null);
|
updateLabelDebouncer.contact(null);
|
||||||
|
|
|
@ -38,8 +38,7 @@ class MemviewMapModel extends AbstractSortedTableModel<MemoryBox> {
|
||||||
private Map<String, MemoryBox> memMap = new HashMap<>();
|
private Map<String, MemoryBox> memMap = new HashMap<>();
|
||||||
private MemviewProvider provider;
|
private MemviewProvider provider;
|
||||||
|
|
||||||
private final static String COLUMN_NAMES[] =
|
private final static String COLUMN_NAMES[] = { NAME_COL, ASTART_COL, ASTOP_COL, TSTART_COL, TSTOP_COL };
|
||||||
{ NAME_COL, ASTART_COL, ASTOP_COL, TSTART_COL, TSTOP_COL };
|
|
||||||
|
|
||||||
public MemviewMapModel(MemviewProvider provider) {
|
public MemviewMapModel(MemviewProvider provider) {
|
||||||
super(ASTART);
|
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
|
* Convenience method for locating columns by name. Implementation is naive so
|
||||||
* overridden if this method is to be called often. This method is not in the TableModel
|
* this should be overridden if this method is to be called often. This method
|
||||||
* interface and is not used by the JTable.
|
* is not in the TableModel interface and is not used by the JTable.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int findColumn(String columnName) {
|
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
|
* Returns the number of records managed by the data source object. A
|
||||||
* method to determine how many rows it should create and display. This method should be quick,
|
* <B>JTable</B> uses this method to determine how many rows it should create
|
||||||
* as it is call by <B>JTable</B> quite frequently.
|
* 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
|
* @return the number or rows in the model
|
||||||
* @see #getColumnCount
|
* @see #getColumnCount
|
||||||
|
@ -165,8 +165,7 @@ class MemviewMapModel extends AbstractSortedTableModel<MemoryBox> {
|
||||||
MemoryBox box = memList.get(rowIndex);
|
MemoryBox box = memList.get(rowIndex);
|
||||||
try {
|
try {
|
||||||
box.getStart();
|
box.getStart();
|
||||||
}
|
} catch (ConcurrentModificationException e) {
|
||||||
catch (ConcurrentModificationException e) {
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
return memList.get(rowIndex);
|
return memList.get(rowIndex);
|
||||||
|
@ -187,18 +186,17 @@ class MemviewMapModel extends AbstractSortedTableModel<MemoryBox> {
|
||||||
case ASTOP:
|
case ASTOP:
|
||||||
return box.getRange().getMaxAddress();
|
return box.getRange().getMaxAddress();
|
||||||
case TSTART:
|
case TSTART:
|
||||||
return Long.toString(box.getStart());
|
return Long.toString(box.getStart(), 16);
|
||||||
case TSTOP:
|
case TSTOP:
|
||||||
long end = box.getEnd();
|
long end = box.getEnd();
|
||||||
if (end == Long.MAX_VALUE) {
|
if (end == Long.MAX_VALUE) {
|
||||||
return "+" + '\u221e' + '\u2025';
|
return "+" + '\u221e' + '\u2025';
|
||||||
}
|
}
|
||||||
return Long.toString(end);
|
return Long.toString(end, 16);
|
||||||
default:
|
default:
|
||||||
return "UNKNOWN";
|
return "UNKNOWN";
|
||||||
}
|
}
|
||||||
}
|
} catch (ConcurrentModificationException e) {
|
||||||
catch (ConcurrentModificationException e) {
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -487,7 +487,8 @@ public class MemviewPanel extends JPanel implements MouseListener, MouseMotionLi
|
||||||
public String getTagForTick(long tick) {
|
public String getTagForTick(long tick) {
|
||||||
String tval = "";
|
String tval = "";
|
||||||
if (0 <= tick && tick < timesArray.length) {
|
if (0 <= tick && tick < timesArray.length) {
|
||||||
tval = Long.toString(timesArray[(int) tick]);
|
Long time = timesArray[(int) tick];
|
||||||
|
tval = Long.toString(time, 16);
|
||||||
}
|
}
|
||||||
return tval;
|
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