mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-23 05:45:51 +08: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:
@@ -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
+22
-14
@@ -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)
|
||||
|
||||
+14
-14
@@ -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
|
||||
|
||||
+10
-27
@@ -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
|
||||
|
||||
+5
-2
@@ -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);
|
||||
|
||||
+40
-42
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-4
@@ -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 |
Reference in New Issue
Block a user