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:
d-millar
2025-03-04 13:02:21 -05:00
parent 136a944796
commit a2e42f5fe2
32 changed files with 829 additions and 3419 deletions
@@ -6,8 +6,8 @@ README.md||GHIDRA||||END|
data/debugger-launchers/kernel-dbgeng.bat||GHIDRA||||END|
data/debugger-launchers/local-dbgeng-attach.bat||GHIDRA||||END|
data/debugger-launchers/local-dbgeng-ext.bat||GHIDRA||||END|
data/debugger-launchers/local-dbgeng-trace.bat||GHIDRA||||END|
data/debugger-launchers/local-dbgeng.bat||GHIDRA||||END|
data/debugger-launchers/local-ttd.bat||GHIDRA||||END|
data/debugger-launchers/remote-dbgeng.bat||GHIDRA||||END|
data/debugger-launchers/svrcx-dbgeng.bat||GHIDRA||||END|
src/main/py/LICENSE||GHIDRA||||END|
@@ -17,4 +17,3 @@ src/main/py/pyproject.toml||GHIDRA||||END|
src/main/py/src/ghidradbg/dbgmodel/DbgModel.idl||GHIDRA||||END|
src/main/py/src/ghidradbg/schema.xml||GHIDRA||||END|
src/main/py/src/ghidradbg/schema_exdi.xml||GHIDRA||||END|
src/main/py/src/ghidrattd/schema.xml||GHIDRA||||END|
@@ -0,0 +1,21 @@
::@title dbgeng-trace
::@desc <html><body width="300px">
::@desc <h3>Open trace with <tt>dbgeng</tt> (in a Python interpreter)</h3>
::@desc <p>
::@desc This will open a WinDbg TTD trace of the target on the local machine using <tt>dbgeng.dll</tt>.
::@desc For setup instructions, press <b>F1</b>.
::@desc </p>
::@desc </body></html>
::@menu-group local
::@icon icon.debugger
::@help TraceRmiLauncherServicePlugin#dbgeng_trace
::@env OPT_PYTHON_EXE:file!="python" "Python command" "The path to the Python 3 interpreter. Omit the full path to resolve using the system PATH."
:: Use env instead of args, because "all args except first" is terrible to implement in batch
::@env OPT_TARGET_TRACE:file="" "Trace (.run)" "The target trace image"
::@env OPT_USE_DBGMODEL:bool=true "Use dbgmodel" "Load and use dbgmodel.dll if it is available."
::@env WINDBG_DIR:dir="" "Path to dbgeng.dll directory" "Path containing dbgeng and associated DLLS (if not Windows Kits)."
@echo off
set USE_TTD=true
"%OPT_PYTHON_EXE%" -i ..\support\local-dbgeng-trace.py
@@ -1,21 +0,0 @@
::@title ttd
::@desc <html><body width="300px">
::@desc <h3>Launch with <tt>ttd</tt> (in a Python interpreter)</h3>
::@desc <p>
::@desc This will launch the target on the local machine for time-travel debugging.
::@desc For setup instructions, press <b>F1</b>.
::@desc </p>
::@desc </body></html>
::@menu-group local
::@icon icon.debugger
::@help TraceRmiLauncherServicePlugin#dbgeng_ttd
::@env OPT_PYTHON_EXE:file!="python" "Python command" "The path to the Python 3 interpreter. Omit the full path to resolve using the system PATH."
:: Use env instead of args, because "all args except first" is terrible to implement in batch
::@env OPT_TARGET_IMG:file!="" "Trace (.run)" "A trace associated with the target binary executable"
::@env OPT_TARGET_ARGS:str="" "Arguments" "Command-line arguments to pass to the target"
::@env OPT_USE_DBGMODEL:bool=true "Use dbgmodel" "Load and use dbgmodel.dll if it is available."
::@env OPT_DBGMODEL_PATH:dir="" "Path to dbgeng.dll & \\ttd" "Path containing dbgeng and associated DLLS (if not Windows Kits)."
@echo off
"%OPT_PYTHON_EXE%" -i ..\support\local-ttd.py
@@ -0,0 +1,73 @@
## ###
# IP: GHIDRA
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
import os
import sys
home = os.getenv('GHIDRA_HOME')
if os.path.isdir(f'{home}\\ghidra\\.git'):
sys.path.append(
f'{home}\\ghidra\\Ghidra\\Debug\\Debugger-agent-dbgeng\\build\\pypkg\\src')
sys.path.append(
f'{home}\\ghidra\\Ghidra\\Debug\\Debugger-rmi-trace\\build\\pypkg\\src')
elif os.path.isdir(f'{home}\\.git'):
sys.path.append(
f'{home}\\Ghidra\\Debug\\Debugger-agent-dbgeng\\build\\pypkg\\src')
sys.path.append(
f'{home}\\Ghidra\\Debug\\Debugger-rmi-trace\\build\\pypkg\\src')
else:
sys.path.append(
f'{home}\\Ghidra\\Debug\\Debugger-agent-dbgeng\\pypkg\\src')
sys.path.append(f'{home}\\Ghidra\\Debug\\Debugger-rmi-trace\\pypkg\\src')
def main():
# Delay these imports until sys.path is patched
from ghidradbg import commands as cmd
from pybag.dbgeng import core as DbgEng
from ghidradbg.hooks import on_state_changed
from ghidradbg.util import dbg
# So that the user can re-enter by typing repl()
global repl
repl = cmd.repl
cmd.ghidra_trace_connect(os.getenv('GHIDRA_TRACE_RMI_ADDR'))
target = os.getenv('OPT_TARGET_TRACE')
if target is None or target == "":
print("dbgeng requires a target trace - please try again.")
cmd.ghidra_trace_disconnect()
return
cmd.ghidra_trace_open(target, start_trace=False)
# TODO: HACK
try:
dbg.wait()
except KeyboardInterrupt as ki:
dbg.interrupt()
cmd.ghidra_trace_start(target)
cmd.ghidra_trace_sync_enable()
on_state_changed(DbgEng.DEBUG_CES_EXECUTION_STATUS, DbgEng.DEBUG_STATUS_BREAK)
cmd.repl()
if __name__ == '__main__':
main()
@@ -1,58 +0,0 @@
## ###
# IP: GHIDRA
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
import os
import sys
home = os.getenv('GHIDRA_HOME')
if os.path.isdir(f'{home}\\ghidra\\.git'):
sys.path.append(
f'{home}\\ghidra\\Ghidra\\Debug\\Debugger-agent-dbgeng\\build\\pypkg\\src')
sys.path.append(
f'{home}\\ghidra\\Ghidra\\Debug\\Debugger-rmi-trace\\build\\pypkg\\src')
elif os.path.isdir(f'{home}\\.git'):
sys.path.append(
f'{home}\\Ghidra\\Debug\\Debugger-agent-dbgeng\\build\\pypkg\\src')
sys.path.append(
f'{home}\\Ghidra\\Debug\\Debugger-rmi-trace\\build\\pypkg\\src')
else:
sys.path.append(
f'{home}\\Ghidra\\Debug\\Debugger-agent-dbgeng\\pypkg\\src')
sys.path.append(f'{home}\\Ghidra\\Debug\\Debugger-rmi-trace\\pypkg\\src')
def main():
# Delay these imports until sys.path is patched
from ghidrattd import commands as cmd
from ghidrattd import hooks
###from ghidrattd.util import dbg
cmd.ghidra_trace_connect(os.getenv('GHIDRA_TRACE_RMI_ADDR'))
args = os.getenv('OPT_TARGET_ARGS')
if args:
args = ' ' + args
cmd.ghidra_trace_create(
os.getenv('OPT_TARGET_IMG') + args, start_trace=True)
cmd.ghidra_trace_sync_enable()
hooks.on_stop()
cmd.repl()
if __name__ == '__main__':
main()
File diff suppressed because it is too large Load Diff
@@ -1,17 +1,17 @@
## ###
# IP: GHIDRA
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# IP: GHIDRA
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from ctypes import *
@@ -26,6 +26,7 @@ from . import imodelobject as mo
class ModelIterator(object):
def __init__(self, iter):
self._iter = iter
self._index = 0
iter.AddRef()
# ModelIterator
@@ -39,10 +40,17 @@ class ModelIterator(object):
byref(indexer), byref(metadata))
except COMError as ce:
return None
if "ptr=0x0" in str(indexer):
next = (self._index, mo.ModelObject(object))
self._index += 1
return next
index = mo.ModelObject(indexer)
ival = index.GetIntrinsicValue()
if ival is None:
return (0, mo.ModelObject(object))
next = (self._index, mo.ModelObject(object))
self._index += 1
return next
return (ival.value, mo.ModelObject(object))
def Reset(self):
@@ -0,0 +1,45 @@
## ###
# IP: GHIDRA
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from ctypes import *
from comtypes import COMError
from comtypes.gen import DbgMod
from comtypes.hresult import S_OK, S_FALSE
from pybag.dbgeng import exception
from . import imodelobject as mo
class ModelMethod(object):
def __init__(self, method):
self._method = method
method.AddRef()
# ModelMethod
def Call(self, object, argcount=0, arguments=None):
if argcount == 0:
arguments = POINTER(DbgMod.IModelObject)()
result = POINTER(DbgMod.IModelObject)()
metadata = POINTER(DbgMod.IKeyStore)()
try:
self._method.Call(byref(object), argcount, byref(arguments),
byref(result), byref(metadata))
except COMError as ce:
return None
return mo.ModelObject(result)
@@ -1,17 +1,17 @@
## ###
# IP: GHIDRA
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# IP: GHIDRA
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from ctypes import *
@@ -35,7 +35,7 @@ class RawEnumerator(object):
self._keys = None
return cnt
# KeyEnumerator
# RawEnumerator
def GetNext(self):
key = BSTR()
@@ -57,12 +57,12 @@ class ProcessState(object):
self.visited = set()
self.waiting = False
def record(self, description=None):
def record(self, description=None, snap=None):
# print("RECORDING")
first = self.first
self.first = False
if description is not None:
commands.STATE.trace.snapshot(description)
commands.STATE.trace.snapshot(description, snap=snap)
if first:
if util.is_kernel():
commands.create_generic("Sessions")
@@ -71,6 +71,9 @@ class ProcessState(object):
commands.put_processes()
commands.put_environment()
commands.put_threads()
if util.is_trace():
commands.init_ttd()
#commands.put_events()
if self.threads:
commands.put_threads()
self.threads = False
@@ -106,10 +109,10 @@ class ProcessState(object):
commands.put_processes(running=True)
commands.put_threads(running=True)
def record_exited(self, exit_code, description=None):
def record_exited(self, exit_code, description=None, snap=None):
# print("RECORD_EXITED")
if description is not None:
commands.STATE.trace.snapshot(description)
commands.STATE.trace.snapshot(description, snap=snap)
proc = util.selected_process()
ipath = commands.PROCESS_PATTERN.format(procnum=proc)
procobj = commands.STATE.trace.proxy_object_path(ipath)
@@ -381,13 +384,37 @@ def on_stop(*args):
return
state = PROC_STATE[proc]
state.visited.clear()
snap = update_position()
with commands.STATE.client.batch():
with trace.open_tx("Stopped"):
state.record("Stopped")
state.record("Stopped", snap)
commands.put_event_thread()
commands.activate()
def update_position():
"""Update the position"""
cursor = util.get_cursor()
if cursor is None:
return None
pos = cursor.get_position()
lpos = util.get_last_position()
rng = range(pos.major, lpos.major)
if pos.major > lpos.major:
rng = range(lpos.major, pos.major)
for i in rng:
type = util.get_event_type(i)
if type == "modload" or type == "modunload":
on_modules_changed()
break
for i in rng:
type = util.get_event_type(i)
if type == "threadcreated" or type == "threadterm":
on_threads_changed()
util.set_last_position(pos)
return util.pos2snap(pos)
def on_exited(proc):
# print("ON EXITED")
if proc not in PROC_STATE:
@@ -26,7 +26,6 @@ from pybag.dbgeng import core as DbgEng, exception
from . import util, commands
REGISTRY = MethodRegistry(ThreadPoolExecutor(
max_workers=1, thread_name_prefix='MethodRegistry'))
@@ -214,7 +213,7 @@ def evaluate(
@REGISTRY.method(action='refresh', display="Refresh", condition=util.dbg.use_generics)
def refresh_generic(node: sch.OBJECT):
"""List processes on pydbg's host system."""
"""List the children for a generic node."""
with commands.open_tracked_tx('Refresh Generic'):
commands.ghidra_trace_put_generic(node)
@@ -294,6 +293,15 @@ def refresh_modules(node: sch.Schema('ModuleContainer')):
commands.ghidra_trace_put_modules()
@REGISTRY.method(action='refresh', display='Refresh Events')
def refresh_events(node: sch.Schema('State')):
"""
Refresh the events list for a trace.
"""
with commands.open_tracked_tx('Refresh Events'):
commands.ghidra_trace_put_events(node)
@REGISTRY.method(action='activate')
def activate_process(process: sch.Schema('Process')):
"""Switch to the process."""
@@ -377,7 +385,7 @@ def launch_loader(
"""
command = file
if args != None:
command += " "+args
command += " " + args
commands.ghidra_trace_create(command=file, start_trace=False)
@@ -393,7 +401,7 @@ def launch(
"""
command = file
if args != None:
command += " "+args
command += " " + args
commands.ghidra_trace_create(
command, initial_break=initial_break, timeout=timeout, start_trace=False)
@@ -411,6 +419,14 @@ def go(process: sch.Schema('Process')):
util.dbg.run_async(lambda: dbg().go())
@REGISTRY.method(action='step_ext', display='Go (backwards)', icon='icon.debugger.resume.back', condition=util.dbg.IS_TRACE)
@util.dbg.eng_thread
def go_back(thread: sch.Schema('Process')):
"""Continue execution of the process backwards."""
dbg().cmd("g-")
dbg().wait()
@REGISTRY.method
def interrupt(process: sch.Schema('Process')):
"""Interrupt the execution of the debugged program."""
@@ -433,6 +449,22 @@ def step_over(thread: sch.Schema('Thread'), n: ParamDesc(int, display='N')=1):
util.dbg.run_async(lambda: dbg().stepo(n))
@REGISTRY.method(action='step_ext', display='Step Into (backwards)', icon='icon.debugger.step.back.into', condition=util.dbg.IS_TRACE)
@util.dbg.eng_thread
def step_back_into(thread: sch.Schema('Thread'), n: ParamDesc(int, display='N')=1):
"""Step one instruction backward exactly."""
dbg().cmd("t- " + str(n))
dbg().wait()
@REGISTRY.method(action='step_ext', display='Step Over (backwards)', icon='icon.debugger.step.back.over', condition=util.dbg.IS_TRACE)
@util.dbg.eng_thread
def step_back_over(thread: sch.Schema('Thread'), n: ParamDesc(int, display='N')=1):
"""Step one instruction backward, but proceed through subroutine calls."""
dbg().cmd("p- " + str(n))
dbg().wait()
@REGISTRY.method(action='step_out')
def step_out(thread: sch.Schema('Thread')):
"""Execute until the current stack frame returns."""
@@ -448,6 +480,14 @@ def step_to(thread: sch.Schema('Thread'), address: Address, max=None):
util.dbg.run_async(lambda: dbg().stepto(address.offset, max))
@REGISTRY.method(action='go_to_time', display='Go To (event)', condition=util.dbg.IS_TRACE)
@util.dbg.eng_thread
def go_to_time(node: sch.Schema('State'), evt: ParamDesc(str, display='Event')):
"""Reset the trace to a specific time."""
dbg().cmd("!tt " + evt)
dbg().wait()
@REGISTRY.method(action='break_sw_execute')
@util.dbg.eng_thread
def break_address(process: sch.Schema('Process'), address: Address):
@@ -557,7 +597,7 @@ def read_mem(process: sch.Schema('Process'), range: AddressRange):
offset_start, offset_start + range.length() - 1, pages=True, display_result=False)
if result['count'] == 0:
commands.putmem_state(
offset_start, offset_start+range.length() - 1, 'error')
offset_start, offset_start + range.length() - 1, 'error')
@REGISTRY.method
@@ -578,5 +618,14 @@ def write_reg(frame: sch.Schema('StackFrame'), name: str, value: bytes):
dbg().reg._set_register(name, value)
@REGISTRY.method(display='Refresh Events (custom)', condition=util.dbg.IS_TRACE)
@util.dbg.eng_thread
def refresh_events_custom(node: sch.Schema('State'), cmd: ParamDesc(str, display='Cmd'),
prefix: ParamDesc(str, display='Prefix')="dx -r2 @$cursession.TTD"):
"""Parse TTD objects generated from a LINQ command."""
with commands.open_tracked_tx('Put Events (custom)'):
commands.ghidra_trace_put_events_custom(prefix, cmd)
def dbg():
return util.dbg._base
@@ -2,7 +2,7 @@
<schema name="DbgRoot" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
<attribute name="Sessions" schema="SessionContainer" required="yes" fixed="yes" />
<attribute name="Settings" schema="ANY" />
<attribute name="State" schema="ANY" />
<attribute name="State" schema="State" />
<attribute-alias from="_state" to="State" />
<attribute name="Utility" schema="ANY" />
<attribute name="_display" schema="STRING" hidden="yes" />
@@ -28,6 +28,11 @@
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="ANY"/>
</schema>
<schema name="State" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
<element schema="VOID" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="ANY"/>
</schema>
<schema name="Selectable" elementResync="NEVER" attributeResync="NEVER">
<element schema="OBJECT" />
<attribute name="_order" schema="INT" hidden="yes" />
@@ -38,9 +38,9 @@ from pybag.dbgeng.callbacks import DbgEngCallbacks
from pybag.dbgeng.idebugclient import DebugClient
from ghidradbg.dbgmodel.ihostdatamodelaccess import HostDataModelAccess
from ghidradbg.dbgmodel.imodelmethod import ModelMethod
from _winapi import STILL_ACTIVE
DbgVersion = namedtuple('DbgVersion', ['full', 'name', 'dotted', 'arch'])
@@ -81,6 +81,7 @@ class StdInputCallbacks(CoClass):
class _Worker(threading.Thread):
def __init__(self, new_base, work_queue, dispatch):
super().__init__(name='DbgWorker', daemon=True)
self.new_base = new_base
@@ -109,6 +110,7 @@ class _Worker(threading.Thread):
# https://github.com/python/cpython/blob/main/Lib/concurrent/futures/thread.py
# accessed 9 Jan 2024
class _WorkItem(object):
def __init__(self, future, fn, args, kwargs):
self.future = future
self.fn = fn
@@ -131,6 +133,7 @@ class DebuggeeRunningException(BaseException):
class DbgExecutor(object):
def __init__(self, ghidra_dbg):
self._ghidra_dbg = ghidra_dbg
self._work_queue = queue.SimpleQueue()
@@ -139,12 +142,12 @@ class DbgExecutor(object):
self._thread.start()
self._executing = False
def submit(self, fn, /, *args, **kwargs):
def submit(self, fn, / , *args, **kwargs):
f = self._submit_no_exit(fn, *args, **kwargs)
self._ghidra_dbg.exit_dispatch()
return f
def _submit_no_exit(self, fn, /, *args, **kwargs):
def _submit_no_exit(self, fn, / , *args, **kwargs):
f = Future()
if self._executing and self._ghidra_dbg.IS_REMOTE == False:
f.set_exception(DebuggeeRunningException("Debuggee is Running"))
@@ -199,6 +202,7 @@ class AllDbg(pydbg.DebuggerBase):
class GhidraDbg(object):
def __init__(self):
self._queue = DbgExecutor(self)
self._thread = self._queue._thread
@@ -239,10 +243,11 @@ class GhidraDbg(object):
'load_dump'
]:
setattr(self, name, self.eng_thread(getattr(base, name)))
self.IS_KERNEL = False
self.IS_EXDI = False
self.IS_REMOTE = os.getenv('OPT_CONNECT_STRING') is not None
self.IS_KERNEL = False
self.IS_EXDI = False
self.IS_REMOTE = os.getenv('OPT_CONNECT_STRING') is not None
self.IS_TRACE = os.getenv('USE_TTD') == "true"
def _new_base(self):
remote = os.getenv('OPT_CONNECT_STRING')
if remote is not None:
@@ -252,14 +257,12 @@ class GhidraDbg(object):
else:
self._protected_base = AllDbg()
def _generate_client(self, original):
cli = POINTER(DbgEng.IDebugClient)()
cliptr = POINTER(POINTER(DbgEng.IDebugClient))(cli)
hr = original.CreateClient(cliptr)
exception.check_err(hr)
return DebugClient(client=cli)
@property
def _base(self):
@@ -289,12 +292,14 @@ class GhidraDbg(object):
For methods inside of GhidraDbg, ensure it runs on the dbgeng
thread
'''
@functools.wraps(func)
def _func(self, *args, **kwargs):
if threading.current_thread() is self._thread:
return func(self, *args, **kwargs)
else:
return self.run(func, self, *args, **kwargs)
return _func
def eng_thread(self, func):
@@ -302,12 +307,14 @@ class GhidraDbg(object):
For methods and functions outside of GhidraDbg, ensure it
runs on this GhidraDbg's dbgeng thread
'''
@functools.wraps(func)
def _func(*args, **kwargs):
if threading.current_thread() is self._thread:
return func(*args, **kwargs)
else:
return self.run(func, *args, **kwargs)
return _func
def _ces_exec_status(self, argument):
@@ -325,6 +332,7 @@ class GhidraDbg(object):
def _dispatch_events(self, timeout=DbgEng.WAIT_INFINITE):
# NB: pybag's impl doesn't heed standalone
self._protected_base._client.DispatchCallbacks(timeout)
dispatch_events = check_thread(_dispatch_events)
# no check_thread. Must allow reentry
@@ -393,7 +401,23 @@ class GhidraDbg(object):
return None
class TTDState(object):
def __init__(self):
self._cursor = None
self._first = None
self._last = None
self._lastmajor = None
self._lastpos = None
self.breakpoints = []
self.events = {}
self.evttypes = {}
self.starts = {}
self.stops = {}
dbg = GhidraDbg()
ttd = TTDState()
@dbg.eng_thread
@@ -750,18 +774,12 @@ def get_proc_id(pid):
def full_mem():
sizeptr = 64; #int(gdb.parse_and_eval('sizeof(void*)')) * 8
infoLow = DbgEng._MEMORY_BASIC_INFORMATION64()
infoLow.BaseAddress = 0
infoLow.RegionSize = (1 << (sizeptr-1))
infoLow.Protect = 0xFFF
infoLow.Name = "UMEM"
infoHigh = DbgEng._MEMORY_BASIC_INFORMATION64()
infoHigh.BaseAddress = 1 << (sizeptr-1)
infoHigh.RegionSize = (1 << (sizeptr-1))
infoHigh.Protect = 0xFFF
infoHigh.Name = "KMEM"
return [ infoLow, infoHigh ]
info = DbgEng._MEMORY_BASIC_INFORMATION64()
info.BaseAddress = 0
info.RegionSize = (1 << 64) - 1
info.Protect = 0xFFF
info.Name = "full memory"
return [ info ]
@dbg.eng_thread
@@ -780,6 +798,16 @@ def get_thread_id(tid):
return None
@dbg.eng_thread
def open_trace_or_dump(filename):
"""Open a trace or dump file"""
_cli = dbg._base._client._cli
if isinstance(filename, str):
filename = filename.encode()
hr = _cli.OpenDumpFile(filename)
exception.check_err(hr)
def split_path(pathString):
list = []
segs = pathString.split(".")
@@ -800,6 +828,11 @@ def IHostDataModelAccess():
dbg._base._client._cli.QueryInterface(interface=DbgMod.IHostDataModelAccess))
def IModelMethod(method_ptr):
return ModelMethod(
method_ptr.GetIntrinsicValue().value.QueryInterface(interface=DbgMod.IModelMethod))
@dbg.eng_thread
def get_object(relpath):
"""Get the list of all threads"""
@@ -810,12 +843,27 @@ def get_object(relpath):
root = mgr.GetRootNamespace()
pathstr = "Debugger"
if relpath != '':
pathstr += "."+relpath
pathstr += "." + relpath
path = split_path(pathstr)
# print(f"PATH: {pathstr}")
return root.GetOffspring(path)
@dbg.eng_thread
def get_method(context_path, method_name):
"""Get the list of all threads"""
obj = get_object(context_path)
keys = obj.EnumerateKeys()
(k, v) = keys.GetNext()
while k is not None:
if k.value == method_name:
break
(k, v) = keys.GetNext()
if k is None:
return None
return IModelMethod(v)
@dbg.eng_thread
def get_attributes(obj):
"""Get the list of attributes"""
@@ -826,7 +874,7 @@ def get_attributes(obj):
@dbg.eng_thread
def get_elements(obj):
"""Get the list of all threads"""
"""Get the list of elements"""
if obj is None:
return None
return obj.GetElements()
@@ -834,7 +882,7 @@ def get_elements(obj):
@dbg.eng_thread
def get_kind(obj):
"""Get the list of all threads"""
"""Get the kind"""
if obj is None:
return None
kind = obj.GetKind()
@@ -845,7 +893,7 @@ def get_kind(obj):
@dbg.eng_thread
def get_type(obj):
"""Get the list of all threads"""
"""Get the type"""
if obj is None:
return None
return obj.GetTypeKind()
@@ -853,7 +901,7 @@ def get_type(obj):
@dbg.eng_thread
def get_value(obj):
"""Get the list of all threads"""
"""Get the value"""
if obj is None:
return None
return obj.GetValue()
@@ -861,7 +909,7 @@ def get_value(obj):
@dbg.eng_thread
def get_intrinsic_value(obj):
"""Get the list of all threads"""
"""Get the intrinsic value"""
if obj is None:
return None
return obj.GetIntrinsicValue()
@@ -869,7 +917,7 @@ def get_intrinsic_value(obj):
@dbg.eng_thread
def get_target_info(obj):
"""Get the list of all threads"""
"""Get the target info"""
if obj is None:
return None
return obj.GetTargetInfo()
@@ -877,7 +925,7 @@ def get_target_info(obj):
@dbg.eng_thread
def get_type_info(obj):
"""Get the list of all threads"""
"""Get the type info"""
if obj is None:
return None
return obj.GetTypeInfo()
@@ -885,7 +933,7 @@ def get_type_info(obj):
@dbg.eng_thread
def get_name(obj):
"""Get the list of all threads"""
"""Get the name"""
if obj is None:
return None
return obj.GetName().value
@@ -893,7 +941,7 @@ def get_name(obj):
@dbg.eng_thread
def to_display_string(obj):
"""Get the list of all threads"""
"""Get the display string"""
if obj is None:
return None
return obj.ToDisplayString()
@@ -901,7 +949,7 @@ def to_display_string(obj):
@dbg.eng_thread
def get_location(obj):
"""Get the list of all threads"""
"""Get the location"""
if obj is None:
return None
try:
@@ -925,6 +973,45 @@ def get_convenience_variable(id):
return val
def get_cursor():
return ttd._cursor
def get_last_position():
return ttd._lastpos
def set_last_position(pos):
ttd._lastpos = pos
def get_event_type(rng):
if ttd.evttypes.__contains__(rng):
return ttd.evttypes[rng]
def pos2snap(pos):
pmap = get_attributes(pos)
major = get_value(pmap["Sequence"])
minor = get_value(pmap["Steps"])
return mm2snap(major, minor)
def mm2snap(major, minor):
index = int(major)
if index < 0 or index >= ttd.MAX_STEP:
return int(ttd._lastmajor) # << 32
snap = index # << 32 + int(minor)
return snap
def pos2split(pos):
pmap = get_attributes(pos)
major = get_value(pmap["Sequence"])
minor = get_value(pmap["Steps"])
return (major, minor)
def set_convenience_variable(id, value):
conv_map[id] = value
@@ -952,4 +1039,11 @@ def set_remote(value):
def is_remote():
return dbg.IS_REMOTE
def set_trace(value):
dbg.IS_TRACE = value
def is_trace():
return dbg.IS_TRACE
@@ -1,19 +0,0 @@
## ###
# IP: GHIDRA
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
# NOTE: libraries must precede EVERYTHING, esp pybag and DbgMod
from . import libraries, util, commands, methods, hooks
@@ -1,212 +0,0 @@
## ###
# IP: GHIDRA
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from ghidratrace.client import Address, RegVal
from pybag import pydbg
from . import util
language_map = {
'ARM': ['AARCH64:BE:64:v8A', 'AARCH64:LE:64:AppleSilicon', 'AARCH64:LE:64:v8A', 'ARM:BE:64:v8', 'ARM:LE:64:v8'],
'Itanium': [],
'x86': ['x86:LE:32:default'],
'x86_64': ['x86:LE:64:default'],
'EFI': ['x86:LE:64:default'],
}
data64_compiler_map = {
None: 'pointer64',
}
x86_compiler_map = {
'windows': 'windows',
'Cygwin': 'windows',
}
arm_compiler_map = {
'windows': 'windows',
}
compiler_map = {
'DATA:BE:64:default': data64_compiler_map,
'DATA:LE:64:default': data64_compiler_map,
'x86:LE:32:default': x86_compiler_map,
'x86:LE:64:default': x86_compiler_map,
'AARCH64:BE:64:v8A': arm_compiler_map,
'AARCH64:LE:64:AppleSilicon': arm_compiler_map,
'AARCH64:LE:64:v8A': arm_compiler_map,
'ARM:BE:64:v8': arm_compiler_map,
'ARM:LE:64:v8': arm_compiler_map,
}
def get_arch():
return "x86_64"
def get_endian():
return 'little'
def get_osabi():
return "windows"
def compute_ghidra_language():
# First, check if the parameter is set
lang = util.get_convenience_variable('ghidra-language')
if lang != 'auto':
return lang
# Get the list of possible languages for the arch. We'll need to sift
# through them by endian and probably prefer default/simpler variants. The
# heuristic for "simpler" will be 'default' then shortest variant id.
arch = get_arch()
endian = get_endian()
lebe = ':BE:' if endian == 'big' else ':LE:'
if not arch in language_map:
return 'DATA' + lebe + '64:default'
langs = language_map[arch]
matched_endian = sorted(
(l for l in langs if lebe in l),
key=lambda l: 0 if l.endswith(':default') else len(l)
)
if len(matched_endian) > 0:
return matched_endian[0]
# NOTE: I'm disinclined to fall back to a language match with wrong endian.
return 'DATA' + lebe + '64:default'
def compute_ghidra_compiler(lang):
# First, check if the parameter is set
comp = util.get_convenience_variable('ghidra-compiler')
if comp != 'auto':
return comp
# Check if the selected lang has specific compiler recommendations
if not lang in compiler_map:
return 'default'
comp_map = compiler_map[lang]
osabi = get_osabi()
if osabi in comp_map:
return comp_map[osabi]
if None in comp_map:
return comp_map[None]
return 'default'
def compute_ghidra_lcsp():
lang = compute_ghidra_language()
comp = compute_ghidra_compiler(lang)
return lang, comp
class DefaultMemoryMapper(object):
def __init__(self, defaultSpace):
self.defaultSpace = defaultSpace
def map(self, proc: int, offset: int):
space = self.defaultSpace
return self.defaultSpace, Address(space, offset)
def map_back(self, proc: int, address: Address) -> int:
if address.space == self.defaultSpace:
return address.offset
raise ValueError(f"Address {address} is not in process {proc.GetProcessID()}")
DEFAULT_MEMORY_MAPPER = DefaultMemoryMapper('ram')
memory_mappers = {}
def compute_memory_mapper(lang):
if not lang in memory_mappers:
return DEFAULT_MEMORY_MAPPER
return memory_mappers[lang]
class DefaultRegisterMapper(object):
def __init__(self, byte_order):
if not byte_order in ['big', 'little']:
raise ValueError("Invalid byte_order: {}".format(byte_order))
self.byte_order = byte_order
self.union_winners = {}
def map_name(self, proc, name):
return name
def map_value(self, proc, name, value):
try:
### TODO: this seems half-baked
av = value.to_bytes(8, "big")
except Exception:
raise ValueError("Cannot convert {}'s value: '{}', type: '{}'"
.format(name, value, type(value)))
return RegVal(self.map_name(proc, name), av)
def map_name_back(self, proc, name):
return name
def map_value_back(self, proc, name, value):
return RegVal(self.map_name_back(proc, name), value)
class Intel_x86_64_RegisterMapper(DefaultRegisterMapper):
def __init__(self):
super().__init__('little')
def map_name(self, proc, name):
if name is None:
return 'UNKNOWN'
if name == 'efl':
return 'rflags'
if name.startswith('zmm'):
# Ghidra only goes up to ymm, right now
return 'ymm' + name[3:]
return super().map_name(proc, name)
def map_value(self, proc, name, value):
rv = super().map_value(proc, name, value)
if rv.name.startswith('ymm') and len(rv.value) > 32:
return RegVal(rv.name, rv.value[-32:])
return rv
def map_name_back(self, proc, name):
if name == 'rflags':
return 'eflags'
DEFAULT_BE_REGISTER_MAPPER = DefaultRegisterMapper('big')
DEFAULT_LE_REGISTER_MAPPER = DefaultRegisterMapper('little')
register_mappers = {
'x86:LE:64:default': Intel_x86_64_RegisterMapper()
}
def compute_register_mapper(lang):
if not lang in register_mappers:
if ':BE:' in lang:
return DEFAULT_BE_REGISTER_MAPPER
if ':LE:' in lang:
return DEFAULT_LE_REGISTER_MAPPER
return register_mappers[lang]
File diff suppressed because it is too large Load Diff
@@ -1,441 +0,0 @@
## ###
# IP: GHIDRA
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
import sys
import time
import threading
from pybag import pydbg
from pybag.dbgeng.callbacks import EventHandler
from pybag.dbgeng import core as DbgEng
from pybag.dbgeng import exception
from pybag.dbgeng.idebugbreakpoint import DebugBreakpoint
from . import commands, util
ALL_EVENTS = 0xFFFF
class HookState(object):
__slots__ = ('installed', 'mem_catchpoint')
def __init__(self):
self.installed = False
self.mem_catchpoint = None
class ProcessState(object):
__slots__ = ('first', 'regions', 'modules', 'threads', 'breaks', 'watches', 'visited', 'waiting')
def __init__(self):
self.first = True
# For things we can detect changes to between stops
self.regions = False
self.modules = False
self.threads = False
self.breaks = False
self.watches = False
# For frames and threads that have already been synced since last stop
self.visited = set()
self.waiting = True
def record(self, description=None, snap=None):
first = self.first
self.first = False
if description is not None:
commands.STATE.trace.snapshot(description, snap=snap)
if first:
commands.put_processes()
commands.put_environment()
if self.threads:
commands.put_threads()
self.threads = False
thread = util.selected_thread()
if thread is not None:
if first or thread not in self.visited:
commands.putreg()
commands.putmem("$pc", "1", display_result=False)
commands.putmem("$sp", "1", display_result=False)
#commands.put_frames()
self.visited.add(thread)
#frame = util.selected_frame()
#hashable_frame = (thread, frame)
#if first or hashable_frame not in self.visited:
# self.visited.add(hashable_frame)
if first or self.regions:
commands.put_regions()
self.regions = False
if first or self.modules:
commands.put_modules()
self.modules = False
if first or self.breaks:
commands.put_breakpoints()
self.breaks = False
def record_continued(self):
commands.put_processes(running=True)
commands.put_threads(running=True)
def record_exited(self, exit_code, description=None, snap=None):
if description is not None:
commands.STATE.trace.snapshot(description, snap)
proc = util.selected_process()
ipath = commands.PROCESS_PATTERN.format(procnum=proc)
commands.STATE.trace.proxy_object_path(
ipath).set_value('Exit Code', exit_code)
class BrkState(object):
__slots__ = ('break_loc_counts',)
def __init__(self):
self.break_loc_counts = {}
def update_brkloc_count(self, b, count):
self.break_loc_counts[b.GetID()] = count
def get_brkloc_count(self, b):
return self.break_loc_counts.get(b.GetID(), 0)
def del_brkloc_count(self, b):
if b not in self.break_loc_counts:
return 0 # TODO: Print a warning?
count = self.break_loc_counts[b.GetID()]
del self.break_loc_counts[b.GetID()]
return count
HOOK_STATE = HookState()
BRK_STATE = BrkState()
PROC_STATE = {}
def on_state_changed(*args):
#print("ON_STATE_CHANGED")
if args[0] == DbgEng.DEBUG_CES_CURRENT_THREAD:
return on_thread_selected(args)
elif args[0] == DbgEng.DEBUG_CES_BREAKPOINTS:
return on_breakpoint_modified(args)
elif args[0] == DbgEng.DEBUG_CES_RADIX:
util.set_convenience_variable('output-radix', args[1])
return DbgEng.DEBUG_STATUS_GO
elif args[0] == DbgEng.DEBUG_CES_EXECUTION_STATUS:
proc = util.selected_process()
if args[1] & DbgEng.DEBUG_STATUS_INSIDE_WAIT:
PROC_STATE[proc].waiting = True
return DbgEng.DEBUG_STATUS_GO
PROC_STATE[proc].waiting = False
commands.put_state(proc)
if args[1] == DbgEng.DEBUG_STATUS_BREAK:
return on_stop(args)
else:
return on_cont(args)
return DbgEng.DEBUG_STATUS_GO
def on_debuggee_changed(*args):
#print("ON_DEBUGGEE_CHANGED")
trace = commands.STATE.trace
if trace is None:
return
if args[1] == DbgEng.DEBUG_CDS_REGISTERS:
on_register_changed(args[0][1])
#if args[1] == DbgEng.DEBUG_CDS_DATA:
# on_memory_changed(args[0][1])
return DbgEng.DEBUG_STATUS_GO
def on_session_status_changed(*args):
#print("ON_STATUS_CHANGED")
trace = commands.STATE.trace
if trace is None:
return
if args[0] == DbgEng.DEBUG_SESSION_ACTIVE or args[0] == DbgEng.DEBUG_SSESION_REBOOT:
with commands.STATE.client.batch():
with trace.open_tx("New Process {}".format(util.selected_process())):
commands.put_processes()
return DbgEng.DEBUG_STATUS_GO
def on_symbol_state_changed(*args):
#print("ON_SYMBOL_STATE_CHANGED")
trace = commands.STATE.trace
if trace is None:
return
if args[0] == 1 or args[0] == 2:
PROC_STATE[proc].modules = True
return DbgEng.DEBUG_STATUS_GO
def on_system_error(*args):
print("ON_SYSTEM_ERROR")
print(hex(args[0]))
trace = commands.STATE.trace
if trace is None:
return
with commands.STATE.client.batch():
with trace.open_tx("New Process {}".format(util.selected_process())):
commands.put_processes()
return DbgEng.DEBUG_STATUS_BREAK
def on_new_process(*args):
#print("ON_NEW_PROCESS")
trace = commands.STATE.trace
if trace is None:
return
with commands.STATE.client.batch():
with trace.open_tx("New Process {}".format(util.selected_process())):
commands.put_processes()
return DbgEng.DEBUG_STATUS_BREAK
def on_process_selected():
#print("PROCESS_SELECTED")
proc = util.selected_process()
if proc not in PROC_STATE:
return
trace = commands.STATE.trace
if trace is None:
return
with commands.STATE.client.batch():
with trace.open_tx("Process {} selected".format(proc)):
PROC_STATE[proc].record()
commands.activate()
def on_process_deleted(*args):
#print("ON_PROCESS_DELETED")
proc = args[0]
on_exited(proc)
if proc in PROC_STATE:
del PROC_STATE[proc]
trace = commands.STATE.trace
if trace is None:
return
with commands.STATE.client.batch():
with trace.open_tx("Process {} deleted".format(proc)):
commands.put_processes() # TODO: Could just delete the one....
return DbgEng.DEBUG_STATUS_BREAK
def on_threads_changed(*args):
#print("ON_THREADS_CHANGED")
proc = util.selected_process()
if proc not in PROC_STATE:
return DbgEng.DEBUG_STATUS_GO
PROC_STATE[proc].threads = True
return DbgEng.DEBUG_STATUS_GO
def on_thread_selected(*args):
#print("THREAD_SELECTED")
nthrd = args[0][1]
nproc = util.selected_process()
if nproc not in PROC_STATE:
return
trace = commands.STATE.trace
if trace is None:
return
with commands.STATE.client.batch():
with trace.open_tx("Thread {}.{} selected".format(nproc, nthrd)):
commands.put_state(nproc)
state = PROC_STATE[nproc]
if state.waiting:
state.record_continued()
else:
state.record()
commands.activate()
def on_register_changed(regnum):
#print("REGISTER_CHANGED")
proc = util.selected_process()
if proc not in PROC_STATE:
return
trace = commands.STATE.trace
if trace is None:
return
with commands.STATE.client.batch():
with trace.open_tx("Register {} changed".format(regnum)):
commands.putreg()
commands.activate()
def on_cont(*args):
proc = util.selected_process()
if proc not in PROC_STATE:
return
trace = commands.STATE.trace
if trace is None:
return
state = PROC_STATE[proc]
with commands.STATE.client.batch():
with trace.open_tx("Continued"):
state.record_continued()
return DbgEng.DEBUG_STATUS_GO
def on_stop(*args):
proc = util.selected_process()
if proc not in PROC_STATE:
print("not in state")
return
trace = commands.STATE.trace
if trace is None:
print("no trace")
return
state = PROC_STATE[proc]
state.visited.clear()
pos = dbg().get_position()
rng = range(pos.major, util.lastpos.major)
if pos.major > util.lastpos.major:
rng = range(util.lastpos.major, pos.major)
for i in rng:
if util.evttypes.__contains__(i):
type = util.evttypes[i]
if type == "modload" or type == "modunload":
on_modules_changed()
if type == "threadcreated" or type == "threadterm":
on_threads_changed()
util.lastpos = pos
with commands.STATE.client.batch():
with trace.open_tx("Stopped"):
state.record("Stopped", util.pos2snap(pos))
commands.put_state(proc)
commands.put_event_thread()
commands.activate()
def on_exited(proc):
if proc not in PROC_STATE:
print("not in state")
return
trace = commands.STATE.trace
if trace is None:
return
state = PROC_STATE[proc]
state.visited.clear()
exit_code = util.GetExitCode()
description = "Exited with code {}".format(exit_code)
with commands.STATE.client.batch():
with trace.open_tx(description):
state.record_exited(exit_code, description)
commands.activate()
def on_modules_changed(*args):
#print("ON_MODULES_CHANGED")
proc = util.selected_process()
if proc not in PROC_STATE:
return DbgEng.DEBUG_STATUS_GO
PROC_STATE[proc].modules = True
return DbgEng.DEBUG_STATUS_GO
def on_breakpoint_created(bp):
proc = util.selected_process()
if proc not in PROC_STATE:
return
PROC_STATE[proc].breaks = True
trace = commands.STATE.trace
if trace is None:
return
ibpath = commands.PROC_BREAKS_PATTERN.format(procnum=proc)
with commands.STATE.client.batch():
with trace.open_tx("Breakpoint {} created".format(bp.id)):
ibobj = trace.create_object(ibpath)
# Do not use retain_values or it'll remove other locs
commands.put_single_breakpoint(bp, ibobj, proc, [])
ibobj.insert()
def on_breakpoint_modified(*args):
#print("BREAKPOINT_MODIFIED")
proc = util.selected_process()
if proc not in PROC_STATE:
return
PROC_STATE[proc].breaks = True
trace = commands.STATE.trace
if trace is None:
return
ibpath = commands.PROC_BREAKS_PATTERN.format(procnum=proc)
ibobj = trace.create_object(ibpath)
bpid = args[0][1]
try:
bp = dbg()._control.GetBreakpointById(bpid)
except exception.E_NOINTERFACE_Error:
dbg().breakpoints._remove_stale(bpid)
return on_breakpoint_deleted(bpid)
return on_breakpoint_created(bp)
def on_breakpoint_deleted(bpt):
proc = util.selected_process()
if proc not in PROC_STATE:
return
PROC_STATE[proc].breaks = True
trace = commands.STATE.trace
if trace is None:
return
bpath = commands.PROC_BREAK_PATTERN.format(procnum=proc, breaknum=bpt.id)
with commands.STATE.client.batch():
with trace.open_tx("Breakpoint {} deleted".format(bpt.id)):
trace.proxy_object_path(bpath).remove(tree=True)
def on_breakpoint_hit(*args):
trace = commands.STATE.trace
if trace is None:
return
with commands.STATE.client.batch():
with trace.open_tx("New Process {}".format(util.selected_process())):
commands.put_processes()
return DbgEng.DEBUG_STATUS_GO
def on_exception(*args):
trace = commands.STATE.trace
if trace is None:
return
with commands.STATE.client.batch():
with trace.open_tx("New Process {}".format(util.selected_process())):
commands.put_processes()
return DbgEng.DEBUG_STATUS_GO
def install_hooks():
if HOOK_STATE.installed:
return
HOOK_STATE.installed = True
def remove_hooks():
if not HOOK_STATE.installed:
return
HOOK_STATE.installed = False
def enable_current_process():
proc = util.selected_process()
PROC_STATE[proc] = ProcessState()
def disable_current_process():
proc = util.selected_process()
if proc in PROC_STATE:
# Silently ignore already disabled
del PROC_STATE[proc]
def dbg():
return util.get_debugger()
@@ -1,78 +0,0 @@
## ###
# IP: GHIDRA
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
import ctypes
import os
import platform
import comtypes
import comtypes.client
ctypes.windll.kernel32.SetErrorMode(0x0001 | 0x0002 | 0x8000)
if platform.architecture()[0] == '64bit':
dbgdirs = [os.getenv('OPT_DBGMODEL_PATH'),
r'C:\Program Files\Windows Kits\10\Debuggers\x64',
r'C:\Program Files (x86)\Windows Kits\10\Debuggers\x64']
else:
dbgdirs = [os.getenv('OPT_DBGMODEL_PATH'),
r'C:\Program Files\Windows Kits\10\Debuggers\x86',
r'C:\Program Files (x86)\Windows Kits\10\Debuggers\x86']
dbgdir = None
for _dir in dbgdirs:
if _dir is not None and os.path.exists(_dir):
dbgdir = _dir
break
if not dbgdir:
raise RuntimeError("Windbg install directory not found!")
print(f"Loading dbgeng and friends from {dbgdir}")
# preload these to get correct DLLs loaded
try:
ctypes.windll.LoadLibrary(os.path.join(dbgdir, 'dbghelp.dll'))
except Exception as exc:
print(fr"LoadLibrary failed: {dbgdir}\dbghelp.dll {exc}")
pass
try:
ctypes.windll.LoadLibrary(os.path.join(dbgdir, 'dbgeng.dll'))
except Exception as exc:
print(fr"LoadLibrary failed: {dbgdir}\dbgeng.dll {exc}")
pass
try:
ctypes.windll.LoadLibrary(os.path.join(dbgdir, 'DbgModel.dll'))
except Exception as exc:
print(fr"LoadLibrary failed: {dbgdir}\dbgmodel.dll {exc}")
pass
try:
ctypes.windll.LoadLibrary(os.path.join(dbgdir, 'ttd/TTDReplay.dll'))
except Exception as exc:
print(fr"LoadLibrary failed: {dbgdir}\ttd\TTDReplay.dll {exc}")
pass
try:
ctypes.windll.LoadLibrary(os.path.join(dbgdir, 'ttd/TTDReplayCPU.dll'))
except Exception as exc:
print(fr"LoadLibrary failed: {dbgdir}\ttd\TTDReplayCPU.dll {exc}")
pass
try:
from comtypes.gen import DbgMod
except:
tlb = os.path.join(dbgmodel.module_locator(), 'tlb', 'dbgmodel.tlb')
print(f"Loading TLB: {tlb}")
comtypes.client.GetModule(tlb)
from comtypes.gen import DbgMod
File diff suppressed because it is too large Load Diff
@@ -1,233 +0,0 @@
<context>
<schema name="TTDSession" elementResync="NEVER" attributeResync="NEVER">
<interface name="EventScope" />
<interface name="FocusScope" />
<interface name="Aggregate" />
<element schema="VOID" />
<attribute name="Processes" schema="ProcessContainer" required="yes" fixed="yes" />
<attribute name="Available" schema="AvailableContainer" required="yes" fixed="yes" />
<attribute name="_event_thread" schema="OBJECT" hidden="yes" />
<attribute name="_focus" schema="Selectable" required="yes" hidden="yes" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="VOID" />
</schema>
<schema name="Selectable" elementResync="NEVER" attributeResync="NEVER">
<element schema="OBJECT" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="VOID" />
</schema>
<schema name="BreakpointContainer" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
<element schema="BreakpointSpec" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="VOID" />
</schema>
<schema name="AvailableContainer" canonical="yes" elementResync="ALWAYS" attributeResync="NEVER">
<element schema="Attachable" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="VOID" />
</schema>
<schema name="ProcessContainer" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
<element schema="Process" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="VOID" />
</schema>
<schema name="BreakpointSpec" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
<interface name="BreakpointSpec" />
<interface name="BreakpointLocation" />
<interface name="Togglable" />
<element schema="VOID" />
<attribute name="Expression" schema="STRING" required="yes" hidden="yes" />
<attribute-alias from="_expression" to="Expression" />
<attribute name="Kinds" schema="STRING" required="yes" hidden="yes" />
<attribute-alias from="_kinds" to="Kinds" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute name="Range" schema="RANGE" />
<attribute-alias from="_range" to="Range" />
<attribute name="_enabled" schema="BOOL" required="yes" hidden="yes" />
<attribute name="Commands" schema="STRING" />
<attribute name="Condition" schema="STRING" />
<attribute name="Hit Count" schema="INT" />
<attribute name="Ignore Count" schema="INT" />
<attribute name="Pending" schema="BOOL" />
<attribute name="Silent" schema="BOOL" />
<attribute name="Temporary" schema="BOOL" />
<attribute schema="VOID" />
</schema>
<schema name="Attachable" elementResync="NEVER" attributeResync="NEVER">
<element schema="VOID" />
<attribute name="PID" schema="LONG" />
<attribute-alias from="_pid" to="PID" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="VOID" />
</schema>
<schema name="Process" elementResync="NEVER" attributeResync="NEVER">
<interface name="Activatable" />
<interface name="Process" />
<interface name="Aggregate" />
<interface name="ExecutionStateful" />
<element schema="VOID" />
<attribute name="Threads" schema="ThreadContainer" required="yes" fixed="yes" />
<attribute name="Breakpoints" schema="BreakpointContainer" required="yes" fixed="yes" />
<attribute name="Exit Code" schema="LONG" />
<attribute-alias from="_exit_code" to="Exit Code" />
<attribute name="Environment" schema="Environment" required="yes" fixed="yes" />
<attribute name="Memory" schema="Memory" required="yes" fixed="yes" />
<attribute name="Modules" schema="ModuleContainer" required="yes" fixed="yes" />
<attribute name="PID" schema="LONG" hidden="yes" />
<attribute-alias from="_pid" to="PID" />
<attribute name="State" schema="EXECUTION_STATE" required="yes" hidden="yes" />
<attribute-alias from="_state" to="State" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_short_display" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="VOID" />
</schema>
<schema name="Environment" elementResync="NEVER" attributeResync="NEVER">
<interface name="Environment" />
<element schema="VOID" />
<attribute name="OS" schema="STRING" />
<attribute name="Arch" schema="STRING" />
<attribute name="Endian" schema="STRING" />
<attribute name="Debugger" schema="STRING" />
<attribute-alias from="_os" to="OS" />
<attribute-alias from="_arch" to="Arch" />
<attribute-alias from="_endian" to="Endian" />
<attribute-alias from="_debugger" to="Debugger" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="VOID" />
</schema>
<schema name="ModuleContainer" canonical="yes" elementResync="ONCE" attributeResync="NEVER">
<element schema="Module" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="VOID" />
</schema>
<schema name="Memory" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
<interface name="Memory" />
<element schema="MemoryRegion" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="VOID" />
</schema>
<schema name="ThreadContainer" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
<element schema="Thread" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="VOID" />
</schema>
<schema name="Method" elementResync="NEVER" attributeResync="NEVER">
<interface name="Method" />
<element schema="VOID" />
<attribute name="_display" schema="STRING" required="yes" fixed="yes" hidden="yes" />
<attribute name="_return_type" schema="TYPE" required="yes" fixed="yes" hidden="yes" />
<attribute name="_parameters" schema="MAP_PARAMETERS" required="yes" fixed="yes" hidden="yes" />
<attribute schema="VOID" fixed="yes" hidden="yes" />
</schema>
<schema name="Thread" elementResync="NEVER" attributeResync="NEVER">
<interface name="Activatable" />
<interface name="Thread" />
<interface name="ExecutionStateful" />
<interface name="Aggregate" />
<element schema="VOID" />
<attribute name="Stack" schema="Stack" required="yes" fixed="yes" />
<attribute name="Registers" schema="RegisterValueContainer" required="yes" fixed="yes" />
<attribute name="TID" schema="LONG" />
<attribute-alias from="_tid" to="TID" />
<attribute name="State" schema="EXECUTION_STATE" required="yes" hidden="yes" />
<attribute-alias from="_state" to="State" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_short_display" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute name="Advance" schema="Method" required="yes" fixed="yes" hidden="yes" />
<attribute schema="VOID" />
</schema>
<schema name="Module" elementResync="NEVER" attributeResync="NEVER">
<interface name="Module" />
<element schema="VOID" />
<attribute name="Sections" schema="SectionContainer" required="yes" fixed="yes" />
<attribute name="Symbols" schema="SymbolContainer" required="yes" fixed="yes" />
<attribute name="Range" schema="RANGE" />
<attribute name="Name" schema="STRING" />
<attribute-alias from="_module_name" to="Name" />
<attribute-alias from="_range" to="Range" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="VOID" />
</schema>
<schema name="MemoryRegion" elementResync="NEVER" attributeResync="NEVER">
<interface name="MemoryRegion" />
<element schema="VOID" />
<attribute name="Base" schema="LONG" required="yes" fixed="yes" />
<attribute name="Object File" schema="STRING" fixed="yes" />
<attribute name="_readable" schema="BOOL" required="yes" hidden="yes" />
<attribute name="_writable" schema="BOOL" required="yes" hidden="yes" />
<attribute name="_executable" schema="BOOL" required="yes" hidden="yes" />
<attribute name="Range" schema="RANGE" required="yes" hidden="yes" />
<attribute-alias from="_range" to="Range" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="VOID" />
</schema>
<schema name="SectionContainer" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
<element schema="Section" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="VOID" />
</schema>
<schema name="Stack" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
<interface name="Stack" />
<element schema="StackFrame" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="VOID" />
</schema>
<schema name="SymbolContainer" canonical="yes" elementResync="ONCE" attributeResync="NEVER">
<element schema="Symbol" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="VOID" />
</schema>
<schema name="Symbol" elementResync="NEVER" attributeResync="NEVER">
<element schema="VOID" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="VOID" />
</schema>
<schema name="StackFrame" elementResync="NEVER" attributeResync="NEVER">
<interface name="Activatable" />
<interface name="StackFrame" />
<interface name="Aggregate" />
<element schema="VOID" />
<attribute name="Function" schema="STRING" hidden="yes" />
<attribute-alias from="_function" to="Function" />
<attribute name="Registers" schema="RegisterValueContainer" required="yes" fixed="yes" />
<attribute name="Instruction Offset" schema="ADDRESS" required="yes" />
<attribute-alias from="_pc" to="Instruction Offset" />
<attribute name="Stack Offset" schema="ADDRESS" />
<attribute name="Return Offset" schema="ADDRESS" />
<attribute name="Frame Offset" schema="ADDRESS" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="ANY" />
</schema>
<schema name="Section" elementResync="NEVER" attributeResync="NEVER">
<interface name="Section" />
<element schema="VOID" />
<attribute name="Range" schema="RANGE" />
<attribute-alias from="_range" to="Range" />
<attribute name="Offset" schema="STRING" fixed="yes" />
<attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="VOID" />
</schema>
<schema name="RegisterValueContainer" attributeResync="ONCE">
<interface name="RegisterContainer" />
<attribute name="General Purpose Registers" schema="RegisterBank" />
<attribute name="Floating Point Registers" schema="RegisterBank" />
<attribute name="Advanced Vector Extensions" schema="RegisterBank" />
<attribute name="Memory Protection Extensions" schema="RegisterBank" />
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="VOID" />
</schema>
<schema name="RegisterBank" canonical="yes" elementResync="ONCE" attributeResync="NEVER">
<attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="VOID" />
</schema>
</context>
@@ -1,204 +0,0 @@
## ###
# IP: GHIDRA
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from collections import namedtuple
import os
import re
import sys
from ctypes import *
from pyttd import pyTTD
#from pybag import pydbg
#from pybag.dbgeng import core as DbgEng
#from pybag.dbgeng import exception
#from pybag.dbgeng import util as DbgUtil
base = False
eng = False
first = False
last = False
breakpoints = []
events = {}
evttypes = {}
starts = {}
stops = {}
lastpos = False
DbgVersion = namedtuple('DbgVersion', ['full', 'major', 'minor'])
class Watchpoint(object):
def __init__(self, addr, size, flags, id, bp):
self.addr = addr
self.size = size
self.flags = flags
self.id = id
self.bp = bp
self.expr = None
def _compute_pydbg_ver():
blurb = "" #get_debugger()._control.GetActualProcessorType()
full = ""
major = 0
minor = 0
return DbgVersion(full, int(major), int(minor))
DBG_VERSION = _compute_pydbg_ver()
def get_debugger():
return base
def get_target():
return 0 # get_debugger()._systems.GetCurrentSystemId()
def get_inst(addr):
dbg = get_debugger()
ins = DbgUtil.disassemble_instruction(
dbg.bitness(), addr, dbg.read_mem(addr, 15))
return str(ins)
def get_inst_sz(addr):
dbg = get_debugger()
ins = DbgUtil.disassemble_instruction(
dbg.bitness(), addr, dbg.read_mem(addr, 15))
return str(ins.size)
def get_breakpoints():
return None
def selected_process():
try:
return 0
# return current_process
except Exception:
return None
def selected_thread():
try:
dbg = get_debugger()
current = dbg.get_thread_info()
return current.threadid
except Exception:
return None
def selected_frame():
return 0 # selected_thread().GetSelectedFrame()
def select_process(id: int):
return None
def select_thread(id: int):
return None
def select_frame(id: int):
# TODO: this needs to be fixed
return None
def parse_and_eval(expr):
dbg = get_debugger()
if expr == "$pc":
return dbg.get_program_counter()
if expr == "$sp":
return dbg.get_context_x86_64().rsp
return int(expr)
def get_eval(expr, type=None):
ctrl = get_debugger()._control._ctrl
ctrl.SetExpressionSyntax(1)
value = DbgEng._DEBUG_VALUE()
index = c_ulong()
if type == None:
type = DbgEng.DEBUG_VALUE_INT64
hr = ctrl.Evaluate(Expression="{}".format(expr).encode(
), DesiredType=type, Value=byref(value), RemainderIndex=byref(index))
exception.check_err(hr)
if type == DbgEng.DEBUG_VALUE_INT8:
return value.u.I8
if type == DbgEng.DEBUG_VALUE_INT16:
return value.u.I16
if type == DbgEng.DEBUG_VALUE_INT32:
return value.u.I32
if type == DbgEng.DEBUG_VALUE_INT64:
return value.u.I64.I64
if type == DbgEng.DEBUG_VALUE_FLOAT32:
return value.u.F32
if type == DbgEng.DEBUG_VALUE_FLOAT64:
return value.u.F64
if type == DbgEng.DEBUG_VALUE_FLOAT80:
return value.u.F80Bytes
if type == DbgEng.DEBUG_VALUE_FLOAT82:
return value.u.F82Bytes
if type == DbgEng.DEBUG_VALUE_FLOAT128:
return value.u.F128Bytes
def process_list(running=False):
"""process_list() -> list of all processes"""
sysids = [0]
return sysids
def thread_list():
"""thread_list() -> list of all threads"""
dbg = get_debugger()
return dbg.get_thread_list()
def module_list():
"""thread_list() -> list of all threads"""
dbg = get_debugger()
return dbg.get_module_list()
conv_map = {}
def get_convenience_variable(id):
#val = get_target().GetEnvironment().Get(id)
if id not in conv_map:
return "auto"
val = conv_map[id]
if val is None:
return "auto"
return val
def set_convenience_variable(id, value):
#env = get_target().GetEnvironment()
# return env.Set(id, value, True)
conv_map[id] = value
def pos2snap(pos: int):
index = int(pos.major)
if index < 0 or index >= pyTTD.MAX_STEP:
return int(last.major)*1000
return index*1000+int(pos.minor)
@@ -1296,16 +1296,6 @@ def map_address(address):
return (base, addr)
# def ghidra_trace_put_generic(node):
# """
# Put the current thread's frames into the Ghidra trace
# """
#
# STATE.require_tx()
# with STATE.client.batch() as b:
# put_generic(node)
def ghidra_trace_put_all():
"""
Put everything currently selected into the Ghidra trace
@@ -955,38 +955,21 @@ exdi:CLSID={29f9906e-9dbe-4d4b-b0fb-6acf7fb6d014},Kd=Guess,DataBreaks=Exdi
values are detected. If anyone understand how to extend this search (or knows how to set the
base address to sidestep the scan), we would really love some guidance.</P>
<H3><A name="dbgeng_ttd"></A>TTD (Time-Travel Debugging)</H3>
<H3><A name="dbgeng_trace"></A>TTD (Time-Travel Debugging)</H3>
<P>This is a nascent extension to our launcher for the Windows Debugger. The launcher itself
functions well, but lacks full integration. It is not yet properly packaged for the Ghidra
distribution, but is available in development environments. It also needs some additional
protocol support, namely to integrate its notion of time travel with Ghidra's notion. For the
time being, we map our time specifications as follows. MS TTD uses a tuple for its time specs,
both displayed in hexadecimal, e.g., "B5:1A". The first is the "major," which we believe counts
the events that warrant a snapshot. The second is the "minor," which we believe counts
instructions executed since the major event. Thus, we would like to map the major to a Ghidra
trace snapshot and the minor to steps of p-code emulation. For example, the "B5:1A" notation
would map to "181:26". It should be no surprise the notations are similar, since both MS TTD
and Ghidra (as well as several other "timeless" debuggers) use a snapshot-replay strategy to
recover past machine states. However, we have not yet worked out how to have Ghidra cooperate
with a back end in the replay part of this strategy. Currently, Ghidra will always apply p-code
emulation, despite having a perfectly good and performant back end available. So, for the time
being, we multiply the major number by 1000, thus reserving that many Ghidra snapshots between
major events for MS TTD minor steps. Thus, the notation "B5:1A" will actually map to "181026",
that is snapshot 181026 with no steps of p-code emulation. This hack will fall short if you
visit a time where the minor number exceeds 999.</P>
<P>Furthermore, if you use the Ghidra Debugger UI to visit a past snapshot, the back end is not
yet informed of your intent. You will probably only see stale records of the machine state.
Instead, please use the kd commands from the Terminal to navigate through time. The back end
(MS TTD) will perform the replay, record the snapshot, and command Ghidra to navigate
there.</P>
functions, but lacks full integration. In particular, Ghidra's concept of time is not
mapped directly to the TTD concept of time. TTD uses a major/minor scheme for ordering events,
where the major index changes when TTD must record a change in state. Events, including thread
creation/termination, module loads/unloads, syscalls, and other asynchronous changes, merit
new major indices. When you step forward or backward in a trace, the dbgeng API will increment
and decrement correspondingly. Ghidra, on the other hand, will only increment.</P>
<H4>Options</H4>
<P>This launcher has the same options as the WinDbg launcher, except that the DLL path must
contain <TT>dbgmodel.dll</TT> and the scripts that implement TTD. These are most easily
obtained by installing WinDbg Preview or later.</P>
<P>This launcher has basically the same options as the WinDbg launcher, except that arguments
are not included and the DLL path must contain <TT>TTDReplay.dll</TT>
and the scripts that implement TTD. These are most easily obtained by installing WinDbg Preview or later.</P>
<H2>Stock Java Launchers</H2>
@@ -110,10 +110,12 @@ src/main/resources/images/record.png||GHIDRA||||END|
src/main/resources/images/register-marker.png||GHIDRA||||END|
src/main/resources/images/registers.png||GHIDRA||||END|
src/main/resources/images/resume.png||GHIDRA||||END|
src/main/resources/images/resumeback.png||GHIDRA||||END|
src/main/resources/images/seek-present.png||GHIDRA||||END|
src/main/resources/images/select-registers.png||GHIDRA||||END|
src/main/resources/images/skipover.png||GHIDRA||||END|
src/main/resources/images/stepback.png||GHIDRA||||END|
src/main/resources/images/stepbackinto.png||GHIDRA||||END|
src/main/resources/images/stepinto.png||GHIDRA||||END|
src/main/resources/images/steplast.png||GHIDRA||||END|
src/main/resources/images/stepout.png||GHIDRA||||END|
@@ -163,11 +165,13 @@ src/main/svg/record.svg||GHIDRA||||END|
src/main/svg/register-marker.svg||GHIDRA||||END|
src/main/svg/registers.svg||GHIDRA||||END|
src/main/svg/resume.svg||GHIDRA||||END|
src/main/svg/resumeback.svg||GHIDRA||||END|
src/main/svg/seek-present.svg||GHIDRA||||END|
src/main/svg/select-registers.svg||GHIDRA||||END|
src/main/svg/skipover.svg||GHIDRA||||END|
src/main/svg/stack.svg||GHIDRA||||END|
src/main/svg/stepback.svg||GHIDRA||||END|
src/main/svg/stepbackinto.svg||GHIDRA||||END|
src/main/svg/stepinto.svg||GHIDRA||||END|
src/main/svg/steplast.svg||GHIDRA||||END|
src/main/svg/stepout.svg||GHIDRA||||END|
@@ -104,6 +104,7 @@ icon.debugger.processor = memory16.gif // TODO this icon was missing 'kcmprocess
icon.debugger.launch = launch.png
icon.debugger.attach = attach.png
icon.debugger.resume = resume.png
icon.debugger.resume.back = resumeback.png
icon.debugger.interrupt = interrupt.png
icon.debugger.kill = kill.png
icon.debugger.detach = detach.png
@@ -111,6 +112,8 @@ icon.debugger.record = record.png
icon.debugger.step.into = stepinto.png
icon.debugger.step.over = stepover.png
icon.debugger.step.back = stepback.png
icon.debugger.step.back.into = stepbackinto.png
icon.debugger.step.back.over = stepback.png
icon.debugger.step.finish = stepout.png
icon.debugger.step.last = steplast.png
icon.debugger.skip.over = skipover.png
@@ -112,11 +112,14 @@ public class DebuggerMemviewTraceListener extends TraceDomainObjectListener {
!(region instanceof TraceObjectMemoryRegion objRegion)) {
return;
}
TraceObject obj = objRegion.getObject();
obj.getOrderedValues(Lifespan.ALL, TraceObjectMemoryRegion.KEY_RANGE, true).forEach(v -> {
if (region.getName(v.getMinSnap()).equals("full memory")) {
return;
}
MemoryBox box = new MemoryBox("Region " + region.getName(v.getMinSnap()),
MemviewBoxType.VIRTUAL_ALLOC, v.castValue(), v.getLifespan());
MemviewBoxType.REGION, v.castValue(), v.getLifespan());
updateList.add(box);
});
updateLabelDebouncer.contact(null);
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -38,8 +38,7 @@ class MemviewMapModel extends AbstractSortedTableModel<MemoryBox> {
private Map<String, MemoryBox> memMap = new HashMap<>();
private MemviewProvider provider;
private final static String COLUMN_NAMES[] =
{ NAME_COL, ASTART_COL, ASTOP_COL, TSTART_COL, TSTOP_COL };
private final static String COLUMN_NAMES[] = { NAME_COL, ASTART_COL, ASTOP_COL, TSTART_COL, TSTOP_COL };
public MemviewMapModel(MemviewProvider provider) {
super(ASTART);
@@ -109,9 +108,9 @@ class MemviewMapModel extends AbstractSortedTableModel<MemoryBox> {
}
/**
* Convenience method for locating columns by name. Implementation is naive so this should be
* overridden if this method is to be called often. This method is not in the TableModel
* interface and is not used by the JTable.
* Convenience method for locating columns by name. Implementation is naive so
* this should be overridden if this method is to be called often. This method
* is not in the TableModel interface and is not used by the JTable.
*/
@Override
public int findColumn(String columnName) {
@@ -143,9 +142,10 @@ class MemviewMapModel extends AbstractSortedTableModel<MemoryBox> {
}
/**
* Returns the number of records managed by the data source object. A <B>JTable</B> uses this
* method to determine how many rows it should create and display. This method should be quick,
* as it is call by <B>JTable</B> quite frequently.
* Returns the number of records managed by the data source object. A
* <B>JTable</B> uses this method to determine how many rows it should create
* and display. This method should be quick, as it is call by <B>JTable</B>
* quite frequently.
*
* @return the number or rows in the model
* @see #getColumnCount
@@ -165,8 +165,7 @@ class MemviewMapModel extends AbstractSortedTableModel<MemoryBox> {
MemoryBox box = memList.get(rowIndex);
try {
box.getStart();
}
catch (ConcurrentModificationException e) {
} catch (ConcurrentModificationException e) {
update();
}
return memList.get(rowIndex);
@@ -180,25 +179,24 @@ class MemviewMapModel extends AbstractSortedTableModel<MemoryBox> {
public Object getColumnValueForRow(MemoryBox box, int columnIndex) {
try {
switch (columnIndex) {
case NAME:
return box.getId();
case ASTART:
return box.getRange().getMinAddress();
case ASTOP:
return box.getRange().getMaxAddress();
case TSTART:
return Long.toString(box.getStart());
case TSTOP:
long end = box.getEnd();
if (end == Long.MAX_VALUE) {
return "+" + '\u221e' + '\u2025';
}
return Long.toString(end);
default:
return "UNKNOWN";
case NAME:
return box.getId();
case ASTART:
return box.getRange().getMinAddress();
case ASTOP:
return box.getRange().getMaxAddress();
case TSTART:
return Long.toString(box.getStart(), 16);
case TSTOP:
long end = box.getEnd();
if (end == Long.MAX_VALUE) {
return "+" + '\u221e' + '\u2025';
}
return Long.toString(end, 16);
default:
return "UNKNOWN";
}
}
catch (ConcurrentModificationException e) {
} catch (ConcurrentModificationException e) {
update();
}
return null;
@@ -225,18 +223,18 @@ class MemviewMapModel extends AbstractSortedTableModel<MemoryBox> {
public int compare(MemoryBox b1, MemoryBox b2) {
switch (sortColumn) {
case NAME:
return b1.getId().compareToIgnoreCase(b2.getId());
case ASTART:
return (int) (b1.getStartAddress() - b2.getStartAddress());
case ASTOP:
return (int) (b1.getStopAddress() - b2.getStopAddress());
case TSTART:
return (int) (b1.getStartTime() - b2.getStartTime());
case TSTOP:
return (int) (b1.getStopTime() - b2.getStopTime());
default:
return 0;
case NAME:
return b1.getId().compareToIgnoreCase(b2.getId());
case ASTART:
return (int) (b1.getStartAddress() - b2.getStartAddress());
case ASTOP:
return (int) (b1.getStopAddress() - b2.getStopAddress());
case TSTART:
return (int) (b1.getStartTime() - b2.getStartTime());
case TSTOP:
return (int) (b1.getStopTime() - b2.getStopTime());
default:
return 0;
}
}
}
@@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -348,7 +348,7 @@ public class MemviewPanel extends JPanel implements MouseListener, MouseMotionLi
for (MemoryBox memoryBox : boxes) {
aval = memoryBox.getId();
}
return vertical ? tval + ":" + aval : aval + ":" + tval;
return vertical ? tval + " : " + aval : aval + " : " + tval;
}
private void parseBoxes(Collection<MemoryBox> boxes) {
@@ -487,7 +487,8 @@ public class MemviewPanel extends JPanel implements MouseListener, MouseMotionLi
public String getTagForTick(long tick) {
String tval = "";
if (0 <= tick && tick < timesArray.length) {
tval = Long.toString(timesArray[(int) tick]);
Long time = timesArray[(int) tick];
tval = Long.toString(time, 16);
}
return tval;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 354 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 367 B

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

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