mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-31 09:50:55 +08:00
Merge remote-tracking branch 'origin/GP-3754_d-millar_traceRmi_dbgeng--SQUASHED'
This commit is contained in:
@@ -20,6 +20,7 @@ apply from: "$rootProject.projectDir/gradle/nativeProject.gradle"
|
|||||||
apply from: "$rootProject.projectDir/gradle/distributableGhidraModule.gradle"
|
apply from: "$rootProject.projectDir/gradle/distributableGhidraModule.gradle"
|
||||||
|
|
||||||
apply from: "$rootProject.projectDir/gradle/debugger/hasNodepJar.gradle"
|
apply from: "$rootProject.projectDir/gradle/debugger/hasNodepJar.gradle"
|
||||||
|
apply from: "$rootProject.projectDir/gradle/debugger/hasPythonPackage.gradle"
|
||||||
|
|
||||||
apply plugin: 'eclipse'
|
apply plugin: 'eclipse'
|
||||||
eclipse.project.name = 'Debug Debugger-agent-dbgeng'
|
eclipse.project.name = 'Debug Debugger-agent-dbgeng'
|
||||||
|
|||||||
@@ -3,3 +3,7 @@
|
|||||||
Module.manifest||GHIDRA||||END|
|
Module.manifest||GHIDRA||||END|
|
||||||
src/javaprovider/def/javaprovider.def||GHIDRA||||END|
|
src/javaprovider/def/javaprovider.def||GHIDRA||||END|
|
||||||
src/javaprovider/rc/javaprovider.rc||GHIDRA||||END|
|
src/javaprovider/rc/javaprovider.rc||GHIDRA||||END|
|
||||||
|
src/main/py/LICENSE||GHIDRA||||END|
|
||||||
|
src/main/py/README.md||GHIDRA||||END|
|
||||||
|
src/main/py/pyproject.toml||GHIDRA||||END|
|
||||||
|
src/main/py/src/ghidradbg/schema.xml||GHIDRA||||END|
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
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.
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# Ghidra Trace RMI
|
||||||
|
|
||||||
|
Package for connecting dbgeng to Ghidra via Trace RMI.
|
||||||
|
|
||||||
|
This connector requires Pybag 2.2.8 or better:
|
||||||
|
|
||||||
|
https://pypi.org/project/Pybag
|
||||||
|
https://github.com/dshikashio/Pybag
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
[build-system]
|
||||||
|
requires = ["hatchling"]
|
||||||
|
build-backend = "hatchling.build"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "ghidradbg"
|
||||||
|
version = "10.4"
|
||||||
|
authors = [
|
||||||
|
{ name="Ghidra Development Team" },
|
||||||
|
]
|
||||||
|
description = "Ghidra's Plugin for dbgeng"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.7"
|
||||||
|
classifiers = [
|
||||||
|
"Programming Language :: Python :: 3",
|
||||||
|
"License :: OSI Approved :: Apache Software License",
|
||||||
|
"Operating System :: OS Independent",
|
||||||
|
]
|
||||||
|
dependencies = [
|
||||||
|
"ghidratrace==10.4",
|
||||||
|
"pybag>=2.2.8"
|
||||||
|
]
|
||||||
|
|
||||||
|
[project.urls]
|
||||||
|
"Homepage" = "https://github.com/NationalSecurityAgency/ghidra"
|
||||||
|
"Bug Tracker" = "https://github.com/NationalSecurityAgency/ghidra/issues"
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
## ###
|
||||||
|
# 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
|
||||||
|
ctypes.windll.kernel32.SetErrorMode(0x0001|0x0002|0x8000)
|
||||||
|
|
||||||
|
from . import util, commands, methods, hooks
|
||||||
@@ -0,0 +1,240 @@
|
|||||||
|
## ###
|
||||||
|
# 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():
|
||||||
|
try:
|
||||||
|
type = util.get_debugger()._control.GetActualProcessorType()
|
||||||
|
except Exception:
|
||||||
|
return "Unknown"
|
||||||
|
if type is None:
|
||||||
|
return "x86_64"
|
||||||
|
if type == 0x8664:
|
||||||
|
return "x86_64"
|
||||||
|
if type == 0x014c:
|
||||||
|
return "x86"
|
||||||
|
if type == 0x01c0:
|
||||||
|
return "ARM"
|
||||||
|
if type == 0x0200:
|
||||||
|
return "Itanium"
|
||||||
|
if type == 0x0EBC:
|
||||||
|
return "EFI"
|
||||||
|
return "Unknown"
|
||||||
|
|
||||||
|
|
||||||
|
def get_endian():
|
||||||
|
parm = util.get_convenience_variable('endian')
|
||||||
|
if parm != 'auto':
|
||||||
|
return parm
|
||||||
|
return 'little'
|
||||||
|
|
||||||
|
|
||||||
|
def get_osabi():
|
||||||
|
parm = util.get_convenience_variable('osabi')
|
||||||
|
if not parm in ['auto', 'default']:
|
||||||
|
return parm
|
||||||
|
try:
|
||||||
|
os = util.get_debugger().cmd("vertarget")
|
||||||
|
if "Windows" not in os:
|
||||||
|
return "default"
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
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
@@ -0,0 +1,439 @@
|
|||||||
|
## ###
|
||||||
|
# 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')
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
def record(self, description=None):
|
||||||
|
first = self.first
|
||||||
|
self.first = False
|
||||||
|
if description is not None:
|
||||||
|
commands.STATE.trace.snapshot(description)
|
||||||
|
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", from_tty=False)
|
||||||
|
commands.putmem("$sp", "1", from_tty=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 or self.threads or self.modules:
|
||||||
|
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):
|
||||||
|
if description is not None:
|
||||||
|
commands.STATE.trace.snapshot(description)
|
||||||
|
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")
|
||||||
|
#print(args[0])
|
||||||
|
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:
|
||||||
|
if args[1] & DbgEng.DEBUG_STATUS_INSIDE_WAIT:
|
||||||
|
return DbgEng.DEBUG_STATUS_GO
|
||||||
|
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)):
|
||||||
|
PROC_STATE[nproc].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()
|
||||||
|
with commands.STATE.client.batch():
|
||||||
|
with trace.open_tx("Stopped"):
|
||||||
|
state.record("Stopped")
|
||||||
|
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.GetId())):
|
||||||
|
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(bpid):
|
||||||
|
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=bpid)
|
||||||
|
with commands.STATE.client.batch():
|
||||||
|
with trace.open_tx("Breakpoint {} deleted".format(bpid)):
|
||||||
|
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
|
||||||
|
|
||||||
|
events = dbg().events
|
||||||
|
|
||||||
|
events.engine_state(handler=on_state_changed)
|
||||||
|
events.debuggee_state(handler=on_debuggee_changed)
|
||||||
|
events.session_status(handler=on_session_status_changed)
|
||||||
|
events.symbol_state(handler=on_symbol_state_changed)
|
||||||
|
events.system_error(handler=on_system_error)
|
||||||
|
|
||||||
|
events.create_process(handler=on_new_process)
|
||||||
|
events.exit_process(handler=on_process_deleted)
|
||||||
|
events.create_thread(handler=on_threads_changed)
|
||||||
|
events.exit_thread(handler=on_threads_changed)
|
||||||
|
events.module_load(handler=on_modules_changed)
|
||||||
|
events.unload_module(handler=on_modules_changed)
|
||||||
|
|
||||||
|
#events.breakpoint(handler=on_breakpoint_hit)
|
||||||
|
#events.exception(handler=on_exception)
|
||||||
|
|
||||||
|
|
||||||
|
def remove_hooks():
|
||||||
|
if not HOOK_STATE.installed:
|
||||||
|
return
|
||||||
|
HOOK_STATE.installed = False
|
||||||
|
dbg()._reset_callbacks()
|
||||||
|
|
||||||
|
|
||||||
|
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()
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,434 @@
|
|||||||
|
<context>
|
||||||
|
<schema name="Session" elementResync="NEVER" attributeResync="NEVER">
|
||||||
|
<interface name="Access" />
|
||||||
|
<interface name="Attacher" />
|
||||||
|
<interface name="Interpreter" />
|
||||||
|
<interface name="Interruptible" />
|
||||||
|
<interface name="Launcher" />
|
||||||
|
<interface name="ActiveScope" />
|
||||||
|
<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="_accessible" schema="BOOL" required="yes" hidden="yes" />
|
||||||
|
<attribute name="_supported_attach_kinds" schema="SET_ATTACH_KIND" required="yes" hidden="yes" />
|
||||||
|
<attribute name="_prompt" schema="STRING" required="yes" hidden="yes" />
|
||||||
|
<attribute name="_parameters" schema="MAP_PARAMETERS" required="yes" hidden="yes" />
|
||||||
|
<attribute name="_event_thread" schema="OBJECT" hidden="yes" />
|
||||||
|
<attribute name="_focus" schema="Selectable" required="yes" hidden="yes" />
|
||||||
|
<attribute name="_value" schema="ANY" hidden="yes" />
|
||||||
|
<attribute name="_type" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_short_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_order" schema="INT" hidden="yes" />
|
||||||
|
<attribute name="_modified" schema="BOOL" hidden="yes" />
|
||||||
|
<attribute schema="VOID" />
|
||||||
|
</schema>
|
||||||
|
<schema name="Selectable" elementResync="NEVER" attributeResync="NEVER">
|
||||||
|
<element schema="OBJECT" />
|
||||||
|
<attribute name="_value" schema="ANY" hidden="yes" />
|
||||||
|
<attribute name="_type" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_short_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_order" schema="INT" hidden="yes" />
|
||||||
|
<attribute name="_modified" schema="BOOL" hidden="yes" />
|
||||||
|
<attribute schema="VOID" />
|
||||||
|
</schema>
|
||||||
|
<schema name="BreakpointContainer" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
|
||||||
|
<interface name="BreakpointLocationContainer" />
|
||||||
|
<interface name="BreakpointSpecContainer" />
|
||||||
|
<element schema="BreakpointSpec" />
|
||||||
|
<attribute name="_supported_breakpoint_kinds" schema="SET_BREAKPOINT_KIND" required="yes" hidden="yes" />
|
||||||
|
<attribute name="_value" schema="ANY" hidden="yes" />
|
||||||
|
<attribute name="_type" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_short_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_order" schema="INT" hidden="yes" />
|
||||||
|
<attribute name="_modified" schema="BOOL" hidden="yes" />
|
||||||
|
<attribute schema="VOID" />
|
||||||
|
</schema>
|
||||||
|
<schema name="AvailableContainer" canonical="yes" elementResync="ALWAYS" attributeResync="NEVER">
|
||||||
|
<interface name="Configurable" />
|
||||||
|
<element schema="Attachable" />
|
||||||
|
<attribute name="_value" schema="ANY" hidden="yes" />
|
||||||
|
<attribute name="_type" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_short_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_order" schema="INT" hidden="yes" />
|
||||||
|
<attribute name="_modified" schema="BOOL" hidden="yes" />
|
||||||
|
<attribute name="_base" schema="INT" />
|
||||||
|
<attribute schema="VOID" />
|
||||||
|
</schema>
|
||||||
|
<schema name="ProcessContainer" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
|
||||||
|
<interface name="Configurable" />
|
||||||
|
<element schema="Process" />
|
||||||
|
<attribute name="_value" schema="ANY" hidden="yes" />
|
||||||
|
<attribute name="_type" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_short_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_order" schema="INT" hidden="yes" />
|
||||||
|
<attribute name="_modified" schema="BOOL" hidden="yes" />
|
||||||
|
<attribute name="_base" schema="INT" />
|
||||||
|
<attribute schema="VOID" />
|
||||||
|
</schema>
|
||||||
|
<schema name="BreakpointSpec" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
|
||||||
|
<interface name="BreakpointSpec" />
|
||||||
|
<interface name="BreakpointLocation" />
|
||||||
|
<interface name="Deletable" />
|
||||||
|
<interface name="Togglable" />
|
||||||
|
<element schema="VOID" />
|
||||||
|
<attribute name="_container" schema="BreakpointContainer" required="yes" hidden="yes" />
|
||||||
|
<attribute name="_expression" schema="STRING" required="yes" hidden="yes" />
|
||||||
|
<attribute name="_kinds" schema="SET_BREAKPOINT_KIND" required="yes" hidden="yes" />
|
||||||
|
<attribute name="_value" schema="ANY" hidden="yes" />
|
||||||
|
<attribute name="_type" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_short_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_order" schema="INT" hidden="yes" />
|
||||||
|
<attribute name="_spec" schema="BreakpointSpec" />
|
||||||
|
<attribute name="_range" schema="RANGE" hidden="yes" />
|
||||||
|
<attribute name="_modified" schema="BOOL" hidden="yes" />
|
||||||
|
<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">
|
||||||
|
<interface name="Attachable" />
|
||||||
|
<element schema="VOID" />
|
||||||
|
<attribute name="_pid" schema="LONG" hidden="yes" />
|
||||||
|
<attribute name="_value" schema="ANY" hidden="yes" />
|
||||||
|
<attribute name="_type" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_short_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_order" schema="INT" hidden="yes" />
|
||||||
|
<attribute name="_modified" schema="BOOL" hidden="yes" />
|
||||||
|
<attribute schema="VOID" />
|
||||||
|
</schema>
|
||||||
|
<schema name="Process" elementResync="NEVER" attributeResync="NEVER">
|
||||||
|
<interface name="Process" />
|
||||||
|
<interface name="Aggregate" />
|
||||||
|
<interface name="ExecutionStateful" />
|
||||||
|
<interface name="Attacher" />
|
||||||
|
<interface name="Deletable" />
|
||||||
|
<interface name="Detachable" />
|
||||||
|
<interface name="Killable" />
|
||||||
|
<interface name="Launcher" />
|
||||||
|
<interface name="Resumable" />
|
||||||
|
<interface name="Steppable" />
|
||||||
|
<interface name="Interruptible" />
|
||||||
|
<element schema="VOID" />
|
||||||
|
<attribute name="Threads" schema="ThreadContainer" required="yes" fixed="yes" />
|
||||||
|
<attribute name="Breakpoints" schema="BreakpointContainer" required="yes" fixed="yes" />
|
||||||
|
<!-- attribute name="Breakpoints" schema="BreakpointLocationContainer" required="yes" fixed="yes" /-->
|
||||||
|
<attribute name="_exit_code" schema="LONG" />
|
||||||
|
<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 name="_state" schema="EXECUTION_STATE" required="yes" hidden="yes" />
|
||||||
|
<attribute name="_supported_attach_kinds" schema="SET_ATTACH_KIND" required="yes" hidden="yes" />
|
||||||
|
<attribute name="_parameters" schema="MAP_PARAMETERS" required="yes" hidden="yes" />
|
||||||
|
<attribute name="_supported_step_kinds" schema="SET_STEP_KIND" required="yes" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_value" schema="ANY" hidden="yes" />
|
||||||
|
<attribute name="_type" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_short_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_order" schema="INT" hidden="yes" />
|
||||||
|
<attribute name="_modified" schema="BOOL" hidden="yes" />
|
||||||
|
<attribute schema="VOID" />
|
||||||
|
</schema>
|
||||||
|
<schema name="Environment" elementResync="NEVER" attributeResync="NEVER">
|
||||||
|
<interface name="Environment" />
|
||||||
|
<element schema="VOID" />
|
||||||
|
<attribute name="arch" schema="STRING" />
|
||||||
|
<attribute name="os" schema="STRING" />
|
||||||
|
<attribute name="endian" schema="STRING" />
|
||||||
|
<attribute name="_arch" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_debugger" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_os" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_endian" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_value" schema="ANY" hidden="yes" />
|
||||||
|
<attribute name="_type" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_short_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_order" schema="INT" hidden="yes" />
|
||||||
|
<attribute name="_modified" schema="BOOL" hidden="yes" />
|
||||||
|
<attribute schema="VOID" />
|
||||||
|
</schema>
|
||||||
|
<schema name="ModuleContainer" canonical="yes" elementResync="ONCE" attributeResync="NEVER">
|
||||||
|
<interface name="ModuleContainer" />
|
||||||
|
<element schema="Module" />
|
||||||
|
<attribute name="_supports_synthetic_modules" schema="BOOL" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_value" schema="ANY" hidden="yes" />
|
||||||
|
<attribute name="_type" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_short_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_order" schema="INT" hidden="yes" />
|
||||||
|
<attribute name="_modified" schema="BOOL" hidden="yes" />
|
||||||
|
<attribute schema="VOID" />
|
||||||
|
</schema>
|
||||||
|
<schema name="Memory" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
|
||||||
|
<interface name="Memory" />
|
||||||
|
<element schema="MemoryRegion" />
|
||||||
|
<attribute name="_value" schema="ANY" hidden="yes" />
|
||||||
|
<attribute name="_type" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_short_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_order" schema="INT" hidden="yes" />
|
||||||
|
<attribute name="_modified" schema="BOOL" hidden="yes" />
|
||||||
|
<attribute schema="VOID" />
|
||||||
|
</schema>
|
||||||
|
<schema name="BreakpointLocation" elementResync="NEVER" attributeResync="NEVER">
|
||||||
|
<interface name="BreakpointLocation" />
|
||||||
|
<element schema="VOID" />
|
||||||
|
<attribute name="_range" schema="RANGE" hidden="yes" />
|
||||||
|
<attribute name="_spec" schema="BreakpointSpec" required="yes" hidden="yes" />
|
||||||
|
<attribute name="_value" schema="ANY" hidden="yes" />
|
||||||
|
<attribute name="_type" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_short_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_order" schema="INT" hidden="yes" />
|
||||||
|
<attribute name="_modified" schema="BOOL" hidden="yes" />
|
||||||
|
<attribute schema="VOID" />
|
||||||
|
</schema>
|
||||||
|
<schema name="BreakpointLocationContainer" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
|
||||||
|
<interface name="BreakpointLocationContainer" />
|
||||||
|
<element schema="BreakpointLocation" />
|
||||||
|
<attribute name="_value" schema="ANY" hidden="yes" />
|
||||||
|
<attribute name="_type" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_short_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_order" schema="INT" hidden="yes" />
|
||||||
|
<attribute name="_modified" schema="BOOL" hidden="yes" />
|
||||||
|
<attribute schema="VOID" />
|
||||||
|
</schema>
|
||||||
|
<schema name="ThreadContainer" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
|
||||||
|
<interface name="Configurable" />
|
||||||
|
<element schema="Thread" />
|
||||||
|
<attribute name="_value" schema="ANY" hidden="yes" />
|
||||||
|
<attribute name="_type" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_short_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_order" schema="INT" hidden="yes" />
|
||||||
|
<attribute name="_modified" schema="BOOL" hidden="yes" />
|
||||||
|
<attribute name="_base" schema="INT" />
|
||||||
|
<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="Thread" />
|
||||||
|
<interface name="ExecutionStateful" />
|
||||||
|
<interface name="Steppable" />
|
||||||
|
<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" hidden="yes" />
|
||||||
|
<attribute name="_state" schema="EXECUTION_STATE" required="yes" hidden="yes" />
|
||||||
|
<attribute name="_supported_step_kinds" schema="SET_STEP_KIND" required="yes" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_value" schema="ANY" hidden="yes" />
|
||||||
|
<attribute name="_type" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_short_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_order" schema="INT" hidden="yes" />
|
||||||
|
<attribute name="_modified" schema="BOOL" 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="module name" schema="STRING" />
|
||||||
|
<attribute name="_module_name" schema="STRING" required="yes" hidden="yes" />
|
||||||
|
<attribute name="_range" schema="RANGE" required="yes" hidden="yes" />
|
||||||
|
<attribute name="_value" schema="ANY" hidden="yes" />
|
||||||
|
<attribute name="_type" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_short_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_order" schema="INT" hidden="yes" />
|
||||||
|
<attribute name="_modified" schema="BOOL" hidden="yes" />
|
||||||
|
<attribute schema="VOID" />
|
||||||
|
</schema>
|
||||||
|
<schema name="MemoryRegion" elementResync="NEVER" attributeResync="NEVER">
|
||||||
|
<interface name="MemoryRegion" />
|
||||||
|
<element schema="VOID" />
|
||||||
|
<attribute name="_offset" schema="LONG" required="yes" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_objfile" schema="STRING" required="yes" fixed="yes" hidden="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 name="_memory" schema="Memory" required="yes" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_value" schema="ANY" hidden="yes" />
|
||||||
|
<attribute name="_type" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_short_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_order" schema="INT" hidden="yes" />
|
||||||
|
<attribute name="_modified" schema="BOOL" hidden="yes" />
|
||||||
|
<attribute schema="VOID" />
|
||||||
|
</schema>
|
||||||
|
<schema name="SectionContainer" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
|
||||||
|
<interface name="SectionContainer" />
|
||||||
|
<element schema="Section" />
|
||||||
|
<attribute name="_value" schema="ANY" hidden="yes" />
|
||||||
|
<attribute name="_type" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_short_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_order" schema="INT" hidden="yes" />
|
||||||
|
<attribute name="_modified" schema="BOOL" hidden="yes" />
|
||||||
|
<attribute schema="VOID" />
|
||||||
|
</schema>
|
||||||
|
<schema name="Stack" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
|
||||||
|
<interface name="Stack" />
|
||||||
|
<element schema="StackFrame" />
|
||||||
|
<attribute name="_value" schema="ANY" hidden="yes" />
|
||||||
|
<attribute name="_type" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_short_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_order" schema="INT" hidden="yes" />
|
||||||
|
<attribute name="_modified" schema="BOOL" hidden="yes" />
|
||||||
|
<attribute schema="VOID" />
|
||||||
|
</schema>
|
||||||
|
<schema name="SymbolContainer" canonical="yes" elementResync="ONCE" attributeResync="NEVER">
|
||||||
|
<interface name="SymbolNamespace" />
|
||||||
|
<element schema="Symbol" />
|
||||||
|
<attribute name="_value" schema="ANY" hidden="yes" />
|
||||||
|
<attribute name="_type" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_short_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_order" schema="INT" hidden="yes" />
|
||||||
|
<attribute name="_modified" schema="BOOL" hidden="yes" />
|
||||||
|
<attribute schema="VOID" />
|
||||||
|
</schema>
|
||||||
|
<schema name="Symbol" elementResync="NEVER" attributeResync="NEVER">
|
||||||
|
<interface name="Symbol" />
|
||||||
|
<element schema="VOID" />
|
||||||
|
<attribute name="_size" schema="LONG" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_namespace" schema="SymbolContainer" required="yes" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_data_type" schema="DATA_TYPE" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_value" schema="ADDRESS" hidden="yes" />
|
||||||
|
<attribute name="_type" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_short_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_order" schema="INT" hidden="yes" />
|
||||||
|
<attribute name="_modified" schema="BOOL" hidden="yes" />
|
||||||
|
<attribute name="_bpt" schema="STRING" />
|
||||||
|
<attribute schema="VOID" />
|
||||||
|
</schema>
|
||||||
|
<schema name="StackFrame" elementResync="NEVER" attributeResync="NEVER">
|
||||||
|
<interface name="StackFrame" />
|
||||||
|
<interface name="Aggregate" />
|
||||||
|
<element schema="VOID" />
|
||||||
|
<attribute name="_function" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="Registers" schema="RegisterValueContainer" required="yes" fixed="yes" />
|
||||||
|
<attribute name="_pc" schema="ADDRESS" required="yes" hidden="yes" />
|
||||||
|
<attribute name="_value" schema="ANY" hidden="yes" />
|
||||||
|
<attribute name="_type" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_short_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_order" schema="INT" hidden="yes" />
|
||||||
|
<attribute name="_modified" schema="BOOL" hidden="yes" />
|
||||||
|
<attribute schema="VOID" />
|
||||||
|
</schema>
|
||||||
|
<schema name="Section" elementResync="NEVER" attributeResync="NEVER">
|
||||||
|
<interface name="Section" />
|
||||||
|
<element schema="VOID" />
|
||||||
|
<attribute name="range" schema="RANGE" />
|
||||||
|
<attribute name="_module" schema="Module" required="yes" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_range" schema="RANGE" required="yes" fixed="yes" />
|
||||||
|
<attribute name="_offset" schema="INT" required="no" fixed="yes" />
|
||||||
|
<attribute name="_objfile" schema="STRING" required="no" fixed="yes" />
|
||||||
|
<attribute name="_value" schema="ANY" hidden="yes" />
|
||||||
|
<attribute name="_type" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_short_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_order" schema="INT" hidden="yes" />
|
||||||
|
<attribute name="_modified" schema="BOOL" hidden="yes" />
|
||||||
|
<attribute schema="VOID" />
|
||||||
|
</schema>
|
||||||
|
<schema name="RegisterValueContainer" canonical="yes" elementResync="ONCE" attributeResync="ONCE">
|
||||||
|
<interface name="RegisterContainer" />
|
||||||
|
<interface name="RegisterBank" />
|
||||||
|
<element schema="RegisterValue" />
|
||||||
|
<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="_descriptions" schema="RegisterValueContainer" />
|
||||||
|
<attribute name="_value" schema="ANY" hidden="yes" />
|
||||||
|
<attribute name="_type" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_short_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_order" schema="INT" hidden="yes" />
|
||||||
|
<attribute name="_modified" schema="BOOL" hidden="yes" />
|
||||||
|
<attribute schema="VOID" />
|
||||||
|
</schema>
|
||||||
|
<schema name="RegisterBank" canonical="yes" elementResync="ONCE" attributeResync="NEVER">
|
||||||
|
<interface name="RegisterBank" />
|
||||||
|
<element schema="RegisterValue" />
|
||||||
|
<attribute name="_value" schema="ANY" hidden="yes" />
|
||||||
|
<attribute name="_type" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_short_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_order" schema="INT" hidden="yes" />
|
||||||
|
<attribute name="_modified" schema="BOOL" hidden="yes" />
|
||||||
|
<attribute schema="VOID" />
|
||||||
|
</schema>
|
||||||
|
<schema name="RegisterValue" elementResync="NEVER" attributeResync="NEVER">
|
||||||
|
<interface name="Register" />
|
||||||
|
<element schema="VOID" />
|
||||||
|
<attribute name="_container" schema="OBJECT" required="yes" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_length" schema="INT" required="yes" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_value" schema="ANY" hidden="yes" />
|
||||||
|
<attribute name="_type" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_short_display" schema="STRING" hidden="yes" />
|
||||||
|
<attribute name="_kind" schema="STRING" fixed="yes" hidden="yes" />
|
||||||
|
<attribute name="_order" schema="INT" hidden="yes" />
|
||||||
|
<attribute name="_modified" schema="BOOL" hidden="yes" />
|
||||||
|
<attribute schema="VOID" />
|
||||||
|
</schema>
|
||||||
|
</context>
|
||||||
@@ -0,0 +1,260 @@
|
|||||||
|
## ###
|
||||||
|
# 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 pybag import pydbg
|
||||||
|
from pybag.dbgeng import core as DbgEng
|
||||||
|
from pybag.dbgeng import exception
|
||||||
|
from pybag.dbgeng import util as DbgUtil
|
||||||
|
|
||||||
|
base = pydbg.DebuggerBase()
|
||||||
|
DbgVersion = namedtuple('DbgVersion', ['full', 'major', 'minor'])
|
||||||
|
|
||||||
|
|
||||||
|
def _compute_pydbg_ver():
|
||||||
|
blurb = "" #base._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(addr, 15))
|
||||||
|
return str(ins)
|
||||||
|
|
||||||
|
def get_inst_sz(addr):
|
||||||
|
dbg = get_debugger()
|
||||||
|
ins = DbgUtil.disassemble_instruction(dbg.bitness(), addr, dbg.read(addr, 15))
|
||||||
|
return str(ins.size)
|
||||||
|
|
||||||
|
def get_breakpoints():
|
||||||
|
ids = [bpid for bpid in get_debugger().breakpoints]
|
||||||
|
offset_set = []
|
||||||
|
expr_set = []
|
||||||
|
prot_set = []
|
||||||
|
width_set = []
|
||||||
|
stat_set = []
|
||||||
|
for bpid in ids:
|
||||||
|
try:
|
||||||
|
bp = get_debugger()._control.GetBreakpointById(bpid)
|
||||||
|
except exception.E_NOINTERFACE_Error:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if bp.GetFlags() & DbgEng.DEBUG_BREAKPOINT_DEFERRED:
|
||||||
|
offset = "[Deferred]"
|
||||||
|
expr = bp.GetOffsetExpression()
|
||||||
|
else:
|
||||||
|
offset = "%016x" % bp.GetOffset()
|
||||||
|
expr = get_debugger().get_name_by_offset(bp.GetOffset())
|
||||||
|
|
||||||
|
if bp.GetType()[0] == DbgEng.DEBUG_BREAKPOINT_DATA:
|
||||||
|
width, prot = bp.GetDataParameters()
|
||||||
|
width = ' sz={}'.format(str(width))
|
||||||
|
prot = {4: 'type=x', 3: 'type=rw', 2: 'type=w', 1: 'type=r'}[prot]
|
||||||
|
else:
|
||||||
|
width = ''
|
||||||
|
prot = ''
|
||||||
|
|
||||||
|
if bp.GetFlags() & DbgEng.DEBUG_BREAKPOINT_ENABLED:
|
||||||
|
status = 'enabled'
|
||||||
|
else:
|
||||||
|
status = 'disabled'
|
||||||
|
|
||||||
|
offset_set.append(offset)
|
||||||
|
expr_set.append(expr)
|
||||||
|
prot_set.append(prot)
|
||||||
|
width_set.append(width)
|
||||||
|
stat_set.append(status)
|
||||||
|
return zip(offset_set, expr_set, prot_set, width_set, stat_set)
|
||||||
|
|
||||||
|
def selected_process():
|
||||||
|
try:
|
||||||
|
return get_debugger()._systems.GetCurrentProcessId()
|
||||||
|
#return current_process
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def selected_thread():
|
||||||
|
try:
|
||||||
|
return get_debugger()._systems.GetCurrentThreadId()
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def selected_frame():
|
||||||
|
return 0 #selected_thread().GetSelectedFrame()
|
||||||
|
|
||||||
|
def select_process(id: int):
|
||||||
|
return get_debugger()._systems.SetCurrentProcessId(id)
|
||||||
|
|
||||||
|
def select_thread(id: int):
|
||||||
|
return get_debugger()._systems.SetCurrentThreadId(id)
|
||||||
|
|
||||||
|
def select_frame(id: int):
|
||||||
|
#TODO: this needs to be fixed
|
||||||
|
return id
|
||||||
|
|
||||||
|
def parse_and_eval(expr):
|
||||||
|
regs = get_debugger().reg
|
||||||
|
if expr == "$pc":
|
||||||
|
return regs.get_pc()
|
||||||
|
if expr == "$sp":
|
||||||
|
return regs.get_sp()
|
||||||
|
return get_eval(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 GetProcessIdsByIndex(count=0):
|
||||||
|
if count == 0:
|
||||||
|
try :
|
||||||
|
count = get_debugger()._systems.GetNumberProcesses()
|
||||||
|
except Exception:
|
||||||
|
count = 0
|
||||||
|
ids = (c_ulong * count)()
|
||||||
|
sysids = (c_ulong * count)()
|
||||||
|
if count != 0:
|
||||||
|
hr = get_debugger()._systems._sys.GetProcessIdsByIndex(0, count, ids, sysids)
|
||||||
|
exception.check_err(hr)
|
||||||
|
return (tuple(ids), tuple(sysids))
|
||||||
|
|
||||||
|
|
||||||
|
def GetCurrentProcessExecutableName():
|
||||||
|
dbg = get_debugger()
|
||||||
|
size = c_ulong()
|
||||||
|
exesize = c_ulong()
|
||||||
|
hr = dbg._systems._sys.GetCurrentProcessExecutableName(None, size, byref(exesize))
|
||||||
|
exception.check_err(hr)
|
||||||
|
buffer = create_string_buffer(exesize.value)
|
||||||
|
size = exesize
|
||||||
|
hr = dbg._systems._sys.GetCurrentProcessExecutableName(buffer, size, None)
|
||||||
|
exception.check_err(hr)
|
||||||
|
buffer = buffer[:size.value]
|
||||||
|
buffer = buffer.rstrip(b'\x00')
|
||||||
|
return buffer
|
||||||
|
|
||||||
|
|
||||||
|
def GetCurrentProcessPeb():
|
||||||
|
dbg = get_debugger()
|
||||||
|
offset = c_ulonglong()
|
||||||
|
hr = dbg._systems._sys.GetCurrentProcessPeb(byref(offset))
|
||||||
|
exception.check_err(hr)
|
||||||
|
return offset.value
|
||||||
|
|
||||||
|
|
||||||
|
def GetExitCode():
|
||||||
|
exit_code = c_ulong()
|
||||||
|
hr = get_debugger()._client._cli.GetExitCode(byref(exit_code))
|
||||||
|
return exit_code.value
|
||||||
|
|
||||||
|
|
||||||
|
def process_list(running=False):
|
||||||
|
"""process_list() -> list of all processes"""
|
||||||
|
dbg = get_debugger()
|
||||||
|
ids, sysids = GetProcessIdsByIndex()
|
||||||
|
pebs = []
|
||||||
|
names = []
|
||||||
|
|
||||||
|
try :
|
||||||
|
curid = dbg._systems.GetCurrentProcessId()
|
||||||
|
if running == False:
|
||||||
|
for id in ids:
|
||||||
|
dbg._systems.SetCurrentProcessId(id)
|
||||||
|
names.append(GetCurrentProcessExecutableName())
|
||||||
|
pebs.append(GetCurrentProcessPeb())
|
||||||
|
if running == False:
|
||||||
|
dbg._systems.SetCurrentProcessId(curid)
|
||||||
|
return zip(sysids, names, pebs)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return zip(sysids)
|
||||||
|
|
||||||
|
def thread_list(running=False):
|
||||||
|
"""thread_list() -> list of all threads"""
|
||||||
|
dbg = get_debugger()
|
||||||
|
try :
|
||||||
|
ids, sysids = dbg._systems.GetThreadIdsByIndex()
|
||||||
|
except Exception:
|
||||||
|
return zip([])
|
||||||
|
tebs = []
|
||||||
|
syms = []
|
||||||
|
|
||||||
|
curid = dbg._systems.GetCurrentThreadId()
|
||||||
|
if running == False:
|
||||||
|
for id in ids:
|
||||||
|
dbg._systems.SetCurrentThreadId(id)
|
||||||
|
tebs.append(dbg._systems.GetCurrentThreadTeb())
|
||||||
|
addr = dbg.reg.get_pc()
|
||||||
|
syms.append(dbg.get_name_by_offset(addr))
|
||||||
|
if running == False:
|
||||||
|
dbg._systems.SetCurrentThreadId(curid)
|
||||||
|
return zip(sysids, tebs, syms)
|
||||||
|
return zip(sysids)
|
||||||
|
|
||||||
|
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
|
||||||
+1
-1
@@ -249,7 +249,7 @@ public class TraceRmiHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isClosed() {
|
public boolean isClosed() {
|
||||||
return socket.isClosed();
|
return socket.isClosed() && closed.isDone();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void waitClosed() throws InterruptedException, ExecutionException {
|
public void waitClosed() throws InterruptedException, ExecutionException {
|
||||||
|
|||||||
@@ -52,6 +52,12 @@ public class DummyProc implements AutoCloseable {
|
|||||||
if (platformExe.exists() && platformExe.getFile(false).canExecute()) {
|
if (platformExe.exists() && platformExe.getFile(false).canExecute()) {
|
||||||
return platformExe.getAbsolutePath();
|
return platformExe.getAbsolutePath();
|
||||||
}
|
}
|
||||||
|
platformExe = new ResourceFile(modRoot,
|
||||||
|
"build/exe/" + cmd + "/" + Platform.CURRENT_PLATFORM.getDirectoryName() + "/" +
|
||||||
|
cmd + ".exe");
|
||||||
|
if (platformExe.exists() && platformExe.getFile(false).canExecute()) {
|
||||||
|
return platformExe.getAbsolutePath();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
|
|||||||
@@ -82,4 +82,5 @@ integrationTest {
|
|||||||
dependsOn { project(':Debugger-rmi-trace').assemblePyPackage }
|
dependsOn { project(':Debugger-rmi-trace').assemblePyPackage }
|
||||||
dependsOn { project(':Debugger-agent-gdb').assemblePyPackage }
|
dependsOn { project(':Debugger-agent-gdb').assemblePyPackage }
|
||||||
dependsOn { project(':Debugger-agent-lldb').assemblePyPackage }
|
dependsOn { project(':Debugger-agent-lldb').assemblePyPackage }
|
||||||
|
dependsOn { project(':Debugger-agent-dbgeng').assemblePyPackage }
|
||||||
}
|
}
|
||||||
|
|||||||
+424
@@ -0,0 +1,424 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package agent.dbgeng.rmi;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.SocketAddress;
|
||||||
|
import java.net.SocketTimeoutException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||||
|
import org.junit.Before;
|
||||||
|
|
||||||
|
import ghidra.app.plugin.core.debug.gui.AbstractGhidraHeadedDebuggerGUITest;
|
||||||
|
import ghidra.app.plugin.core.debug.service.rmi.trace.RemoteAsyncResult;
|
||||||
|
import ghidra.app.plugin.core.debug.service.rmi.trace.RemoteMethod;
|
||||||
|
import ghidra.app.plugin.core.debug.service.rmi.trace.TraceRmiAcceptor;
|
||||||
|
import ghidra.app.plugin.core.debug.service.rmi.trace.TraceRmiHandler;
|
||||||
|
import ghidra.app.plugin.core.debug.service.rmi.trace.TraceRmiPlugin;
|
||||||
|
import ghidra.app.plugin.core.debug.utils.ManagedDomainObject;
|
||||||
|
import ghidra.app.services.TraceRmiService;
|
||||||
|
import ghidra.dbg.testutil.DummyProc;
|
||||||
|
import ghidra.framework.Application;
|
||||||
|
import ghidra.framework.OperatingSystem;
|
||||||
|
import ghidra.framework.TestApplicationUtils;
|
||||||
|
import ghidra.framework.main.ApplicationLevelOnlyPlugin;
|
||||||
|
import ghidra.framework.model.DomainFile;
|
||||||
|
import ghidra.framework.plugintool.Plugin;
|
||||||
|
import ghidra.framework.plugintool.PluginsConfiguration;
|
||||||
|
import ghidra.framework.plugintool.util.PluginDescription;
|
||||||
|
import ghidra.framework.plugintool.util.PluginException;
|
||||||
|
import ghidra.framework.plugintool.util.PluginPackage;
|
||||||
|
import ghidra.program.model.address.Address;
|
||||||
|
import ghidra.program.model.address.AddressRangeImpl;
|
||||||
|
import ghidra.trace.model.Lifespan;
|
||||||
|
import ghidra.trace.model.breakpoint.TraceBreakpointKind;
|
||||||
|
import ghidra.trace.model.breakpoint.TraceBreakpointKind.TraceBreakpointKindSet;
|
||||||
|
import ghidra.trace.model.target.TraceObject;
|
||||||
|
import ghidra.trace.model.target.TraceObjectValue;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.NumericUtilities;
|
||||||
|
|
||||||
|
public abstract class AbstractDbgEngTraceRmiTest extends AbstractGhidraHeadedDebuggerGUITest {
|
||||||
|
/**
|
||||||
|
* Some features have to be disabled to avoid permissions issues in the test container. Namely,
|
||||||
|
* don't try to disable ASLR.
|
||||||
|
*/
|
||||||
|
public static final String PREAMBLE = """
|
||||||
|
from ghidradbg.commands import *
|
||||||
|
""";
|
||||||
|
// Connecting should be the first thing the script does, so use a tight timeout.
|
||||||
|
protected static final int CONNECT_TIMEOUT_MS = 3000;
|
||||||
|
protected static final int TIMEOUT_SECONDS = 300;
|
||||||
|
protected static final int QUIT_TIMEOUT_MS = 1000;
|
||||||
|
|
||||||
|
protected TraceRmiService traceRmi;
|
||||||
|
private Path pythonPath;
|
||||||
|
private Path outFile;
|
||||||
|
private Path errFile;
|
||||||
|
|
||||||
|
//@BeforeClass
|
||||||
|
public static void setupPython() throws Throwable {
|
||||||
|
new ProcessBuilder("gradle", "Debugger-agent-dbgeng:assemblePyPackage")
|
||||||
|
.directory(TestApplicationUtils.getInstallationDirectory())
|
||||||
|
.inheritIO()
|
||||||
|
.start()
|
||||||
|
.waitFor();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setPythonPath(ProcessBuilder pb) throws IOException {
|
||||||
|
String sep =
|
||||||
|
OperatingSystem.CURRENT_OPERATING_SYSTEM == OperatingSystem.WINDOWS ? ";" : ":";
|
||||||
|
String rmiPyPkg = Application.getModuleSubDirectory("Debugger-rmi-trace",
|
||||||
|
"build/pypkg/src").getAbsolutePath();
|
||||||
|
String gdbPyPkg = Application.getModuleSubDirectory("Debugger-agent-dbgeng",
|
||||||
|
"build/pypkg/src").getAbsolutePath();
|
||||||
|
String add = rmiPyPkg + sep + gdbPyPkg;
|
||||||
|
pb.environment().compute("PYTHONPATH", (k, v) -> v == null ? add : (v + sep + add));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setupTraceRmi() throws Throwable {
|
||||||
|
traceRmi = addPlugin(tool, TraceRmiPlugin.class);
|
||||||
|
|
||||||
|
try {
|
||||||
|
pythonPath = Paths.get(DummyProc.which("python3"));
|
||||||
|
}
|
||||||
|
catch (RuntimeException e) {
|
||||||
|
pythonPath = Paths.get(DummyProc.which("python"));
|
||||||
|
}
|
||||||
|
outFile = Files.createTempFile("pydbgout", null);
|
||||||
|
errFile = Files.createTempFile("pydbgerr", null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void addAllDebuggerPlugins() throws PluginException {
|
||||||
|
PluginsConfiguration plugConf = new PluginsConfiguration() {
|
||||||
|
@Override
|
||||||
|
protected boolean accepts(Class<? extends Plugin> pluginClass) {
|
||||||
|
return !ApplicationLevelOnlyPlugin.class.isAssignableFrom(pluginClass);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (PluginDescription pd : plugConf
|
||||||
|
.getPluginDescriptions(PluginPackage.getPluginPackage("Debugger"))) {
|
||||||
|
addPlugin(tool, pd.getPluginClass());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static String addrToStringForPython(InetAddress address) {
|
||||||
|
if (address.isAnyLocalAddress()) {
|
||||||
|
return "127.0.0.1"; // Can't connect to 0.0.0.0 as such. Choose localhost.
|
||||||
|
}
|
||||||
|
return address.getHostAddress();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static String sockToStringForPython(SocketAddress address) {
|
||||||
|
if (address instanceof InetSocketAddress tcp) {
|
||||||
|
return addrToStringForPython(tcp.getAddress()) + ":" + tcp.getPort();
|
||||||
|
}
|
||||||
|
throw new AssertionError("Unhandled address type " + address);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected record PythonResult(boolean timedOut, int exitCode, String stdout, String stderr) {
|
||||||
|
protected String handle() {
|
||||||
|
if (stderr.contains("Error") || (0 != exitCode && 1 != exitCode && 143 != exitCode)) {
|
||||||
|
throw new PythonError(exitCode, stdout, stderr);
|
||||||
|
}
|
||||||
|
return stdout;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected record ExecInPython(Process python, CompletableFuture<PythonResult> future) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("resource") // Do not close stdin
|
||||||
|
protected ExecInPython execInPython(String script) throws IOException {
|
||||||
|
ProcessBuilder pb = new ProcessBuilder(pythonPath.toString(), "-i");
|
||||||
|
setPythonPath(pb);
|
||||||
|
|
||||||
|
// If commands come from file, Python will quit after EOF.
|
||||||
|
Msg.info(this, "outFile: " + outFile);
|
||||||
|
Msg.info(this, "errFile: " + errFile);
|
||||||
|
|
||||||
|
//pb.inheritIO();
|
||||||
|
pb.redirectInput(ProcessBuilder.Redirect.PIPE);
|
||||||
|
pb.redirectOutput(outFile.toFile());
|
||||||
|
pb.redirectError(errFile.toFile());
|
||||||
|
Process pyproc = pb.start();
|
||||||
|
OutputStream stdin = pyproc.getOutputStream();
|
||||||
|
stdin.write(script.getBytes());
|
||||||
|
stdin.flush();
|
||||||
|
//stdin.close();
|
||||||
|
return new ExecInPython(pyproc, CompletableFuture.supplyAsync(() -> {
|
||||||
|
try {
|
||||||
|
if (!pyproc.waitFor(TIMEOUT_SECONDS, TimeUnit.SECONDS)) {
|
||||||
|
Msg.error(this, "Timed out waiting for Python");
|
||||||
|
pyproc.destroyForcibly();
|
||||||
|
pyproc.waitFor(TIMEOUT_SECONDS, TimeUnit.SECONDS);
|
||||||
|
return new PythonResult(true, -1, Files.readString(outFile),
|
||||||
|
Files.readString(errFile));
|
||||||
|
}
|
||||||
|
Msg.info(this, "Python exited with code " + pyproc.exitValue());
|
||||||
|
return new PythonResult(false, pyproc.exitValue(), Files.readString(outFile),
|
||||||
|
Files.readString(errFile));
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
return ExceptionUtils.rethrow(e);
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
pyproc.destroyForcibly();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PythonError extends RuntimeException {
|
||||||
|
public final int exitCode;
|
||||||
|
public final String stdout;
|
||||||
|
public final String stderr;
|
||||||
|
|
||||||
|
public PythonError(int exitCode, String stdout, String stderr) {
|
||||||
|
super("""
|
||||||
|
exitCode=%d:
|
||||||
|
----stdout----
|
||||||
|
%s
|
||||||
|
----stderr----
|
||||||
|
%s
|
||||||
|
""".formatted(exitCode, stdout, stderr));
|
||||||
|
this.exitCode = exitCode;
|
||||||
|
this.stdout = stdout;
|
||||||
|
this.stderr = stderr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String runThrowError(String script) throws Exception {
|
||||||
|
CompletableFuture<PythonResult> result = execInPython(script).future;
|
||||||
|
return result.get(TIMEOUT_SECONDS, TimeUnit.SECONDS).handle();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected record PythonAndHandler(ExecInPython exec, TraceRmiHandler handler)
|
||||||
|
implements AutoCloseable {
|
||||||
|
protected RemoteMethod getMethod(String name) {
|
||||||
|
return Objects.requireNonNull(handler.getMethods().get(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void execute(String cmd) {
|
||||||
|
RemoteMethod execute = getMethod("execute");
|
||||||
|
execute.invoke(Map.of("cmd", cmd));
|
||||||
|
}
|
||||||
|
|
||||||
|
public RemoteAsyncResult executeAsync(String cmd) {
|
||||||
|
RemoteMethod execute = getMethod("execute");
|
||||||
|
return execute.invokeAsync(Map.of("cmd", cmd));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String executeCapture(String expr) {
|
||||||
|
RemoteMethod execute = getMethod("evaluate");
|
||||||
|
return (String) execute.invoke(Map.of("expr", expr));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws Exception {
|
||||||
|
Msg.info(this, "Cleaning up python");
|
||||||
|
exec.python().destroy();
|
||||||
|
try {
|
||||||
|
PythonResult r = exec.future.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
|
||||||
|
r.handle();
|
||||||
|
waitForPass(() -> assertTrue(handler.isClosed()));
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
exec.python.destroyForcibly();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected PythonAndHandler startAndConnectPython(Function<String, String> scriptSupplier)
|
||||||
|
throws Exception {
|
||||||
|
TraceRmiAcceptor acceptor = traceRmi.acceptOne(null);
|
||||||
|
ExecInPython exec =
|
||||||
|
execInPython(scriptSupplier.apply(sockToStringForPython(acceptor.getAddress())));
|
||||||
|
acceptor.setTimeout(CONNECT_TIMEOUT_MS);
|
||||||
|
try {
|
||||||
|
TraceRmiHandler handler = acceptor.accept();
|
||||||
|
return new PythonAndHandler(exec, handler);
|
||||||
|
}
|
||||||
|
catch (SocketTimeoutException e) {
|
||||||
|
exec.python.destroyForcibly();
|
||||||
|
exec.future.get(TIMEOUT_SECONDS, TimeUnit.SECONDS).handle();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected PythonAndHandler startAndConnectPython() throws Exception {
|
||||||
|
return startAndConnectPython(addr -> """
|
||||||
|
%s
|
||||||
|
ghidra_trace_connect('%s')
|
||||||
|
""".formatted(PREAMBLE, addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("resource")
|
||||||
|
protected String runThrowError(Function<String, String> scriptSupplier)
|
||||||
|
throws Exception {
|
||||||
|
PythonAndHandler conn = startAndConnectPython(scriptSupplier);
|
||||||
|
PythonResult r = conn.exec.future.get(TIMEOUT_SECONDS, TimeUnit.SECONDS);
|
||||||
|
String stdout = r.handle();
|
||||||
|
waitForPass(() -> assertTrue(conn.handler.isClosed()));
|
||||||
|
return stdout;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void waitStopped() {
|
||||||
|
TraceObject proc = Objects.requireNonNull(tb.objAny("Processes[]", Lifespan.at(0)));
|
||||||
|
waitForPass(() -> assertEquals("STOPPED", tb.objValue(proc, 0, "_state")));
|
||||||
|
waitTxDone();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void waitRunning() {
|
||||||
|
TraceObject proc = Objects.requireNonNull(tb.objAny("Processes[]", Lifespan.at(0)));
|
||||||
|
waitForPass(() -> assertEquals("RUNNING", tb.objValue(proc, 0, "_state")));
|
||||||
|
waitTxDone();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String extractOutSection(String out, String head) {
|
||||||
|
String[] split = out.split("\n");
|
||||||
|
String xout = "";
|
||||||
|
for (String s : split) {
|
||||||
|
if (!s.startsWith("(python)") && !s.equals("")) {
|
||||||
|
xout += s + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return xout.split(head)[1].split("---")[0].replace("(python)", "").trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
record MemDump(long address, byte[] data) {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected MemDump parseHexDump(String dump) throws IOException {
|
||||||
|
// First, get the address. Assume contiguous, so only need top line.
|
||||||
|
List<String> lines = List.of(dump.split("\n"));
|
||||||
|
List<String> toksLine0 = List.of(lines.get(0).split("\\s+"));
|
||||||
|
String addrstr = toksLine0.get(0);
|
||||||
|
if (addrstr.contains(":")) {
|
||||||
|
addrstr = addrstr.substring(0, addrstr.indexOf(":"));
|
||||||
|
}
|
||||||
|
long address = Long.parseLong(addrstr, 16);
|
||||||
|
|
||||||
|
ByteArrayOutputStream buf = new ByteArrayOutputStream();
|
||||||
|
for (String l : lines) {
|
||||||
|
List<String> parts = List.of(l.split(":"));
|
||||||
|
assertEquals(2, parts.size());
|
||||||
|
String hex = parts.get(1).substring(0, 48);
|
||||||
|
byte[] lineData = NumericUtilities.convertStringToBytes(hex);
|
||||||
|
assertNotNull("Converted to null: " + hex, parts.get(1));
|
||||||
|
buf.write(lineData);
|
||||||
|
}
|
||||||
|
return new MemDump(address, buf.toByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
record RegDump() {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected RegDump parseRegDump(String dump) {
|
||||||
|
return new RegDump();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ManagedDomainObject openDomainObject(String path) throws Exception {
|
||||||
|
DomainFile df = env.getProject().getProjectData().getFile(path);
|
||||||
|
assertNotNull(df);
|
||||||
|
return new ManagedDomainObject(df, false, false, monitor);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ManagedDomainObject waitDomainObject(String path) throws Exception {
|
||||||
|
DomainFile df;
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
while (true) {
|
||||||
|
df = env.getProject().getProjectData().getFile(path);
|
||||||
|
if (df != null) {
|
||||||
|
return new ManagedDomainObject(df, false, false, monitor);
|
||||||
|
}
|
||||||
|
Thread.sleep(1000);
|
||||||
|
if (System.currentTimeMillis() - start > 30000) {
|
||||||
|
throw new TimeoutException("30 seconds expired waiting for domain file");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void assertBreakLoc(TraceObjectValue locVal, String key, Address addr, int len,
|
||||||
|
Set<TraceBreakpointKind> kinds, String expression) throws Exception {
|
||||||
|
assertEquals(key, locVal.getEntryKey());
|
||||||
|
TraceObject loc = locVal.getChild();
|
||||||
|
TraceObject spec = loc;
|
||||||
|
assertEquals(new AddressRangeImpl(addr, len), loc.getValue(0, "_range").getValue());
|
||||||
|
assertEquals(TraceBreakpointKindSet.encode(kinds), spec.getValue(0, "_kinds").getValue());
|
||||||
|
assertTrue(spec.getValue(0, "_expression").getValue().toString().contains(expression));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void assertWatchLoc(TraceObjectValue locVal, String key, Address addr, int len,
|
||||||
|
Set<TraceBreakpointKind> kinds, String expression) throws Exception {
|
||||||
|
assertEquals(key, locVal.getEntryKey());
|
||||||
|
TraceObject loc = locVal.getChild();
|
||||||
|
assertEquals(new AddressRangeImpl(addr, len), loc.getValue(0, "_range").getValue());
|
||||||
|
assertEquals(TraceBreakpointKindSet.encode(kinds), loc.getValue(0, "_kinds").getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void waitTxDone() {
|
||||||
|
waitFor(() -> tb.trace.getCurrentTransactionInfo() == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void waitForPass(Runnable runnable, long timeoutMs, long retryDelayMs) {
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
AssertionError lastError = null;
|
||||||
|
while (System.currentTimeMillis() - start < timeoutMs) {
|
||||||
|
try {
|
||||||
|
runnable.run();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
catch (AssertionError e) {
|
||||||
|
lastError = e;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Thread.sleep(retryDelayMs);
|
||||||
|
}
|
||||||
|
catch (InterruptedException e) {
|
||||||
|
// Retry sooner, I guess.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lastError == null) {
|
||||||
|
throw new AssertionError("Timed out before first try?");
|
||||||
|
}
|
||||||
|
throw lastError;
|
||||||
|
}
|
||||||
|
}
|
||||||
+1223
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,380 @@
|
|||||||
|
/* ###
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
package agent.dbgeng.rmi;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.instanceOf;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import org.junit.Ignore;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ghidra.app.plugin.core.debug.service.rmi.trace.RemoteMethod;
|
||||||
|
import ghidra.app.plugin.core.debug.utils.ManagedDomainObject;
|
||||||
|
import ghidra.dbg.testutil.DummyProc;
|
||||||
|
import ghidra.dbg.util.PathPattern;
|
||||||
|
import ghidra.dbg.util.PathPredicates;
|
||||||
|
import ghidra.program.model.address.AddressSpace;
|
||||||
|
import ghidra.trace.database.ToyDBTraceBuilder;
|
||||||
|
import ghidra.trace.model.Lifespan;
|
||||||
|
import ghidra.trace.model.Trace;
|
||||||
|
import ghidra.trace.model.memory.TraceMemorySpace;
|
||||||
|
import ghidra.trace.model.target.TraceObject;
|
||||||
|
import ghidra.trace.model.thread.TraceThread;
|
||||||
|
import ghidra.trace.model.time.TraceSnapshot;
|
||||||
|
|
||||||
|
public class DbgEngHooksTest extends AbstractDbgEngTraceRmiTest {
|
||||||
|
private static final long RUN_TIMEOUT_MS = 20000;
|
||||||
|
private static final long RETRY_MS = 500;
|
||||||
|
|
||||||
|
record PythonAndTrace(PythonAndHandler conn, ManagedDomainObject mdo) implements AutoCloseable {
|
||||||
|
public void execute(String cmd) {
|
||||||
|
conn.execute(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String executeCapture(String cmd) {
|
||||||
|
return conn.executeCapture(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws Exception {
|
||||||
|
conn.close();
|
||||||
|
mdo.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("resource")
|
||||||
|
protected PythonAndTrace startAndSyncPython(String exec) throws Exception {
|
||||||
|
PythonAndHandler conn = startAndConnectPython();
|
||||||
|
try {
|
||||||
|
ManagedDomainObject mdo;
|
||||||
|
conn.execute("from ghidradbg.commands import *");
|
||||||
|
conn.execute(
|
||||||
|
"util.set_convenience_variable('ghidra-language', 'x86:LE:64:default')");
|
||||||
|
if (exec != null) {
|
||||||
|
start(conn, exec);
|
||||||
|
mdo = waitDomainObject("/New Traces/pydbg/"+exec);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
conn.execute("ghidra_trace_start()");
|
||||||
|
mdo = waitDomainObject("/New Traces/pydbg/noname");
|
||||||
|
}
|
||||||
|
tb = new ToyDBTraceBuilder((Trace) mdo.get());
|
||||||
|
return new PythonAndTrace(conn, mdo);
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
conn.close();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected long lastSnap(PythonAndTrace conn) {
|
||||||
|
return conn.conn.handler().getLastSnapshot(tb.trace);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // The 10s wait makes this a pretty expensive test
|
||||||
|
public void testOnNewThread() throws Exception {
|
||||||
|
try (PythonAndTrace conn = startAndSyncPython("notepad.exe")) {
|
||||||
|
conn.execute("from ghidradbg.commands import *");
|
||||||
|
txPut(conn, "processes");
|
||||||
|
|
||||||
|
waitForPass(() -> {
|
||||||
|
TraceObject proc = tb.objAny("Processes[]");
|
||||||
|
assertNotNull(proc);
|
||||||
|
assertEquals("STOPPED", tb.objValue(proc, lastSnap(conn), "_state"));
|
||||||
|
}, RUN_TIMEOUT_MS, RETRY_MS);
|
||||||
|
|
||||||
|
txPut(conn, "threads");
|
||||||
|
waitForPass(() -> assertEquals(4,
|
||||||
|
tb.objValues(lastSnap(conn), "Processes[].Threads[]").size()),
|
||||||
|
RUN_TIMEOUT_MS, RETRY_MS);
|
||||||
|
|
||||||
|
conn.execute("dbg().go(10)");
|
||||||
|
|
||||||
|
waitForPass(() -> assertTrue(tb.objValues(lastSnap(conn), "Processes[].Threads[]").size() > 4),
|
||||||
|
RUN_TIMEOUT_MS, RETRY_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOnThreadSelected() throws Exception {
|
||||||
|
try (PythonAndTrace conn = startAndSyncPython("notepad.exe")) {
|
||||||
|
txPut(conn, "processes");
|
||||||
|
|
||||||
|
waitForPass(() -> {
|
||||||
|
TraceObject inf = tb.objAny("Processes[]");
|
||||||
|
assertNotNull(inf);
|
||||||
|
assertEquals("STOPPED", tb.objValue(inf, lastSnap(conn), "_state"));
|
||||||
|
}, RUN_TIMEOUT_MS, RETRY_MS);
|
||||||
|
|
||||||
|
txPut(conn, "threads");
|
||||||
|
waitForPass(() -> assertEquals(4,
|
||||||
|
tb.objValues(lastSnap(conn), "Processes[].Threads[]").size()),
|
||||||
|
RUN_TIMEOUT_MS, RETRY_MS);
|
||||||
|
|
||||||
|
// Now the real test
|
||||||
|
conn.execute("util.select_thread(1)");
|
||||||
|
waitForPass(() -> {
|
||||||
|
String tnum = conn.executeCapture("util.selected_thread()");
|
||||||
|
assertTrue(tnum.contains("1"));
|
||||||
|
String threadIndex = threadIndex(traceManager.getCurrentObject());
|
||||||
|
assertTrue(tnum.contains(threadIndex));
|
||||||
|
}, RUN_TIMEOUT_MS, RETRY_MS);
|
||||||
|
|
||||||
|
conn.execute("util.select_thread(2)");
|
||||||
|
waitForPass(() -> {
|
||||||
|
String tnum = conn.executeCapture("util.selected_thread()");
|
||||||
|
assertTrue(tnum.contains("2"));
|
||||||
|
String threadIndex = threadIndex(traceManager.getCurrentObject());
|
||||||
|
assertTrue(tnum.contains(threadIndex));
|
||||||
|
}, RUN_TIMEOUT_MS, RETRY_MS);
|
||||||
|
|
||||||
|
conn.execute("util.select_thread(0)");
|
||||||
|
waitForPass(() -> {
|
||||||
|
String tnum = conn.executeCapture("util.selected_thread()");
|
||||||
|
assertTrue(tnum.contains("0"));
|
||||||
|
String threadIndex = threadIndex(traceManager.getCurrentObject());
|
||||||
|
assertTrue(tnum.contains(threadIndex));
|
||||||
|
}, RUN_TIMEOUT_MS, RETRY_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getIndex(TraceObject object, String pattern, int n) {
|
||||||
|
if (object == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
PathPattern pat = PathPredicates.parse(pattern).getSingletonPattern();
|
||||||
|
// if (pat.countWildcards() != 1) {
|
||||||
|
// throw new IllegalArgumentException("Exactly one wildcard required");
|
||||||
|
// }
|
||||||
|
List<String> path = object.getCanonicalPath().getKeyList();
|
||||||
|
if (path.size() < pat.asPath().size()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
List<String> matched = pat.matchKeys(path.subList(0, pat.asPath().size()));
|
||||||
|
if (matched == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (matched.size() <= n) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return matched.get(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String threadIndex(TraceObject object) {
|
||||||
|
return getIndex(object, "Processes[].Threads[]", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String frameIndex(TraceObject object) {
|
||||||
|
return getIndex(object, "Processes[].Threads[].Stack[]", 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Ignore
|
||||||
|
public void testOnSyscallMemory() throws Exception {
|
||||||
|
// TODO: Need a specimen
|
||||||
|
// FWIW, I've already seen this getting exercised in other tests.
|
||||||
|
}
|
||||||
|
|
||||||
|
//@Test - dbgeng has limited support via DEBUG_CDS_DATA,
|
||||||
|
// but expensive to implement anything here
|
||||||
|
public void testOnMemoryChanged() throws Exception {
|
||||||
|
try (PythonAndTrace conn = startAndSyncPython("notepad.exe")) {
|
||||||
|
|
||||||
|
conn.execute("ghidra_trace_txstart('Tx')");
|
||||||
|
conn.execute("ghidra_trace_putmem('$pc 10')");
|
||||||
|
conn.execute("ghidra_trace_txcommit()");
|
||||||
|
long address = getAddressAtOffset(conn, 0);
|
||||||
|
conn.execute("util.get_debugger().write("+address+", b'\\x7f')");
|
||||||
|
|
||||||
|
waitForPass(() -> {
|
||||||
|
ByteBuffer buf = ByteBuffer.allocate(10);
|
||||||
|
tb.trace.getMemoryManager().getBytes(lastSnap(conn), tb.addr(address), buf);
|
||||||
|
assertEquals(0x7f, buf.get(0));
|
||||||
|
}, RUN_TIMEOUT_MS, RETRY_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOnRegisterChanged() throws Exception {
|
||||||
|
try (PythonAndTrace conn = startAndSyncPython("notepad.exe")) {
|
||||||
|
|
||||||
|
conn.execute("ghidra_trace_txstart('Tx')");
|
||||||
|
conn.execute("ghidra_trace_putreg()");
|
||||||
|
conn.execute("ghidra_trace_txcommit()");
|
||||||
|
conn.execute("util.get_debugger().reg._set_register('rax', 0x1234)");
|
||||||
|
conn.execute("util.get_debugger().stepi()");
|
||||||
|
|
||||||
|
String path = "Processes[].Threads[].Registers";
|
||||||
|
TraceObject registers = Objects.requireNonNull(tb.objAny(path, Lifespan.at(0)));
|
||||||
|
AddressSpace space = tb.trace.getBaseAddressFactory()
|
||||||
|
.getAddressSpace(registers.getCanonicalPath().toString());
|
||||||
|
TraceMemorySpace regs = tb.trace.getMemoryManager().getMemorySpace(space, false);
|
||||||
|
waitForPass(() -> assertEquals("1234",
|
||||||
|
regs.getValue(lastSnap(conn), tb.reg("RAX")).getUnsignedValue().toString(16)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOnCont() throws Exception {
|
||||||
|
try (PythonAndTrace conn = startAndSyncPython("notepad.exe")) {
|
||||||
|
txPut(conn, "processes");
|
||||||
|
|
||||||
|
conn.execute("util.get_debugger()._control.SetExecutionStatus(DbgEng.DEBUG_STATUS_GO)");
|
||||||
|
waitRunning();
|
||||||
|
|
||||||
|
TraceObject proc = waitForValue(() -> tb.objAny("Processes[]"));
|
||||||
|
waitForPass(() -> {
|
||||||
|
assertEquals("RUNNING", tb.objValue(proc, lastSnap(conn), "_state"));
|
||||||
|
}, RUN_TIMEOUT_MS, RETRY_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOnStop() throws Exception {
|
||||||
|
try (PythonAndTrace conn = startAndSyncPython("notepad.exe")) {
|
||||||
|
txPut(conn, "processes");
|
||||||
|
|
||||||
|
TraceObject proc = waitForValue(() -> tb.objAny("Processes[]"));
|
||||||
|
waitForPass(() -> {
|
||||||
|
assertEquals("STOPPED", tb.objValue(proc, lastSnap(conn), "_state"));
|
||||||
|
}, RUN_TIMEOUT_MS, RETRY_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOnExited() throws Exception {
|
||||||
|
try (PythonAndTrace conn = startAndSyncPython("netstat.exe")) {
|
||||||
|
txPut(conn, "processes");
|
||||||
|
waitStopped();
|
||||||
|
|
||||||
|
conn.execute("util.get_debugger().go()");
|
||||||
|
|
||||||
|
waitForPass(() -> {
|
||||||
|
TraceSnapshot snapshot =
|
||||||
|
tb.trace.getTimeManager().getSnapshot(lastSnap(conn), false);
|
||||||
|
assertNotNull(snapshot);
|
||||||
|
assertEquals("Exited with code 0", snapshot.getDescription());
|
||||||
|
|
||||||
|
TraceObject proc = tb.objAny("Processes[]");
|
||||||
|
assertNotNull(proc);
|
||||||
|
Object val = tb.objValue(proc, lastSnap(conn), "_exit_code");
|
||||||
|
assertThat(val, instanceOf(Number.class));
|
||||||
|
assertEquals(0, ((Number) val).longValue());
|
||||||
|
}, RUN_TIMEOUT_MS, RETRY_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOnBreakpointCreated() throws Exception {
|
||||||
|
try (PythonAndTrace conn = startAndSyncPython("notepad.exe")) {
|
||||||
|
assertEquals(0, tb.objValues(lastSnap(conn), "Processes[].Breakpoints[]").size());
|
||||||
|
|
||||||
|
conn.execute("dbg = util.get_debugger()");
|
||||||
|
conn.execute("pc = dbg.reg.get_pc()");
|
||||||
|
conn.execute("dbg.bp(expr=pc)");
|
||||||
|
conn.execute("dbg.stepi()");
|
||||||
|
|
||||||
|
waitForPass(() -> {
|
||||||
|
List<Object> brks = tb.objValues(lastSnap(conn), "Processes[].Breakpoints[]");
|
||||||
|
assertEquals(1, brks.size());
|
||||||
|
return (TraceObject) brks.get(0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOnBreakpointModified() throws Exception {
|
||||||
|
try (PythonAndTrace conn = startAndSyncPython("notepad.exe")) {
|
||||||
|
assertEquals(0, tb.objValues(lastSnap(conn), "Processes[].Breakpoints[]").size());
|
||||||
|
|
||||||
|
conn.execute("dbg = util.get_debugger()");
|
||||||
|
conn.execute("pc = dbg.reg.get_pc()");
|
||||||
|
conn.execute("dbg.bp(expr=pc)");
|
||||||
|
conn.execute("dbg.stepi()");
|
||||||
|
|
||||||
|
TraceObject brk = waitForPass(() -> {
|
||||||
|
List<Object> brks = tb.objValues(lastSnap(conn), "Processes[].Breakpoints[]");
|
||||||
|
assertEquals(1, brks.size());
|
||||||
|
return (TraceObject) brks.get(0);
|
||||||
|
});
|
||||||
|
assertEquals(true, tb.objValue(brk, lastSnap(conn), "Enabled"));
|
||||||
|
conn.execute("dbg.bd(0)");
|
||||||
|
conn.execute("dbg.stepi()");
|
||||||
|
assertEquals(false, tb.objValue(brk, lastSnap(conn), "Enabled"));
|
||||||
|
|
||||||
|
/* Not currently enabled
|
||||||
|
assertEquals("", tb.objValue(brk, lastSnap(conn), "Command"));
|
||||||
|
conn.execute("dbg.bp(expr=pc, windbgcmd='bl')");
|
||||||
|
conn.execute("dbg.stepi()");
|
||||||
|
assertEquals("bl", tb.objValue(brk, lastSnap(conn), "Command"));
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOnBreakpointDeleted() throws Exception {
|
||||||
|
try (PythonAndTrace conn = startAndSyncPython("notepad.exe")) {
|
||||||
|
assertEquals(0, tb.objValues(lastSnap(conn), "Processes[].Breakpoints[]").size());
|
||||||
|
|
||||||
|
conn.execute("dbg = util.get_debugger()");
|
||||||
|
conn.execute("pc = dbg.reg.get_pc()");
|
||||||
|
conn.execute("dbg.bp(expr=pc)");
|
||||||
|
conn.execute("dbg.stepi()");
|
||||||
|
|
||||||
|
TraceObject brk = waitForPass(() -> {
|
||||||
|
List<Object> brks = tb.objValues(lastSnap(conn), "Processes[].Breakpoints[]");
|
||||||
|
assertEquals(1, brks.size());
|
||||||
|
return (TraceObject) brks.get(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
conn.execute("dbg.cmd('bc %s')".formatted(brk.getCanonicalPath().index()));
|
||||||
|
conn.execute("dbg.stepi()");
|
||||||
|
|
||||||
|
waitForPass(
|
||||||
|
() -> assertEquals(0,
|
||||||
|
tb.objValues(lastSnap(conn), "Processes[].Breakpoints[]").size()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void start(PythonAndHandler conn, String obj) {
|
||||||
|
conn.execute("from ghidradbg.commands import *");
|
||||||
|
if (obj != null)
|
||||||
|
conn.execute("ghidra_trace_create('"+obj+"')");
|
||||||
|
else
|
||||||
|
conn.execute("ghidra_trace_create()");
|
||||||
|
conn.execute("ghidra_trace_sync_enable()");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void txPut(PythonAndTrace conn, String obj) {
|
||||||
|
conn.execute("ghidra_trace_txstart('Tx" + obj + "')");
|
||||||
|
conn.execute("ghidra_trace_put_" + obj +"()");
|
||||||
|
conn.execute("ghidra_trace_txcommit()");
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getAddressAtOffset(PythonAndTrace conn, int offset) {
|
||||||
|
String inst = "util.get_inst(util.get_debugger().reg.get_pc()+"+offset+")";
|
||||||
|
String ret = conn.executeCapture(inst);
|
||||||
|
String[] split = ret.split("\\s+"); // get target
|
||||||
|
return Long.decode(split[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
+959
File diff suppressed because it is too large
Load Diff
@@ -137,7 +137,7 @@ public class LldbCommandsTest extends AbstractLldbTraceRmiTest {
|
|||||||
ghidra_trace_connect %s
|
ghidra_trace_connect %s
|
||||||
file bash
|
file bash
|
||||||
script ghidralldb.util.set_convenience_variable('ghidra-language','Toy:BE:64:default')
|
script ghidralldb.util.set_convenience_variable('ghidra-language','Toy:BE:64:default')
|
||||||
script ghidralldb.util.set_convenience_varaible('ghidra-compiler','default')
|
script ghidralldb.util.set_convenience_variable('ghidra-compiler','default')
|
||||||
ghidra_trace_start myToy
|
ghidra_trace_start myToy
|
||||||
quit
|
quit
|
||||||
"""
|
"""
|
||||||
|
|||||||
Reference in New Issue
Block a user