GP-5511: final review set

GP-5511: post-re-review
GP-5511: fix for require_tx bug
GP-5511: minor fixes
GP-5511: fixups using vscode - something broken
GP-5511: finsihing up
GP-5511: most things covered
GP-5511: commands (first pass)
GP-5511: arch
This commit is contained in:
d-millar 2025-03-26 15:32:11 -04:00
parent 8fcda62c5b
commit 5d4b56b7e4
6 changed files with 526 additions and 554 deletions

View file

@ -291,7 +291,7 @@ register_mappers = {
} }
def compute_register_mapper(lang): def compute_register_mapper(lang: str)-> DefaultRegisterMapper:
if not lang in register_mappers: if not lang in register_mappers:
if ':BE:' in lang: if ':BE:' in lang:
return DEFAULT_BE_REGISTER_MAPPER return DEFAULT_BE_REGISTER_MAPPER

View file

@ -13,6 +13,8 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
## ##
from typing import Dict, List, Literal, Optional, Tuple
from ghidratrace.client import Address, RegVal from ghidratrace.client import Address, RegVal
import drgn import drgn
@ -20,7 +22,7 @@ from . import util
# NOTE: This map is derived from the ldefs using a script # NOTE: This map is derived from the ldefs using a script
language_map = { language_map: Dict[str, List[str]] = {
'AARCH64': ['AARCH64:BE:64:v8A', 'AARCH64:LE:64:AppleSilicon', 'AARCH64:LE:64:v8A'], 'AARCH64': ['AARCH64:BE:64:v8A', 'AARCH64:LE:64:AppleSilicon', 'AARCH64:LE:64:v8A'],
'ARM': ['ARM:BE:32:v8', 'ARM:BE:32:v8T', 'ARM:LE:32:v8', 'ARM:LE:32:v8T'], 'ARM': ['ARM:BE:32:v8', 'ARM:BE:32:v8T', 'ARM:LE:32:v8', 'ARM:LE:32:v8T'],
'PPC64': ['PowerPC:BE:64:4xx', 'PowerPC:LE:64:4xx'], 'PPC64': ['PowerPC:BE:64:4xx', 'PowerPC:LE:64:4xx'],
@ -31,19 +33,19 @@ language_map = {
'UNKNOWN': ['DATA:LE:64:default', 'DATA:LE:64:default'], 'UNKNOWN': ['DATA:LE:64:default', 'DATA:LE:64:default'],
} }
data64_compiler_map = { data64_compiler_map: Dict[Optional[str], str] = {
None: 'pointer64', None: 'pointer64',
} }
default_compiler_map = { default_compiler_map: Dict[Optional[str], str] = {
'Language.C': 'default', 'Language.C': 'default',
} }
x86_compiler_map = { x86_compiler_map: Dict[Optional[str], str] = {
'Language.C': 'gcc', 'Language.C': 'gcc',
} }
compiler_map = { compiler_map: Dict[str, Dict[Optional[str], str]] = {
'DATA:BE:64:': data64_compiler_map, 'DATA:BE:64:': data64_compiler_map,
'DATA:LE:64:': data64_compiler_map, 'DATA:LE:64:': data64_compiler_map,
'x86:LE:32:': x86_compiler_map, 'x86:LE:32:': x86_compiler_map,
@ -56,12 +58,12 @@ compiler_map = {
} }
def get_arch(): def get_arch() -> str:
platform = drgn.host_platform platform = drgn.host_platform
return platform.arch.name return platform.arch.name
def get_endian(): def get_endian() -> Literal['little', 'big']:
parm = util.get_convenience_variable('endian') parm = util.get_convenience_variable('endian')
if parm != 'auto': if parm != 'auto':
return parm return parm
@ -73,7 +75,7 @@ def get_endian():
return 'big' return 'big'
def get_size(): def get_size() -> str:
parm = util.get_convenience_variable('size') parm = util.get_convenience_variable('size')
if parm != 'auto': if parm != 'auto':
return parm return parm
@ -85,11 +87,11 @@ def get_size():
return '32' return '32'
def get_osabi(): def get_osabi() -> str:
return "Language.C" return "Language.C"
def compute_ghidra_language(): def compute_ghidra_language() -> str:
# First, check if the parameter is set # First, check if the parameter is set
lang = util.get_convenience_variable('ghidra-language') lang = util.get_convenience_variable('ghidra-language')
if lang != 'auto': if lang != 'auto':
@ -115,7 +117,7 @@ def compute_ghidra_language():
return 'DATA' + lebe + sz + ':default' return 'DATA' + lebe + sz + ':default'
def compute_ghidra_compiler(lang): def compute_ghidra_compiler(lang: str) -> str:
# First, check if the parameter is set # First, check if the parameter is set
comp = util.get_convenience_variable('ghidra-compiler') comp = util.get_convenience_variable('ghidra-compiler')
if comp != 'auto': if comp != 'auto':
@ -145,7 +147,7 @@ def compute_ghidra_compiler(lang):
return 'default' return 'default'
def compute_ghidra_lcsp(): def compute_ghidra_lcsp() -> Tuple[str, str]:
lang = compute_ghidra_language() lang = compute_ghidra_language()
comp = compute_ghidra_compiler(lang) comp = compute_ghidra_compiler(lang)
return lang, comp return lang, comp
@ -153,14 +155,14 @@ def compute_ghidra_lcsp():
class DefaultMemoryMapper(object): class DefaultMemoryMapper(object):
def __init__(self, defaultSpace): def __init__(self, defaultSpace: str) -> None:
self.defaultSpace = defaultSpace self.defaultSpace = defaultSpace
def map(self, proc: drgn.Program, offset: int): def map(self, proc: int, offset: int) -> Tuple[str, Address]:
space = self.defaultSpace space = self.defaultSpace
return self.defaultSpace, Address(space, offset) return self.defaultSpace, Address(space, offset)
def map_back(self, proc: drgn.Program, address: Address) -> int: def map_back(self, proc: int, address: Address) -> int:
if address.space == self.defaultSpace: if address.space == self.defaultSpace:
return address.offset return address.offset
raise ValueError( raise ValueError(
@ -169,10 +171,10 @@ class DefaultMemoryMapper(object):
DEFAULT_MEMORY_MAPPER = DefaultMemoryMapper('ram') DEFAULT_MEMORY_MAPPER = DefaultMemoryMapper('ram')
memory_mappers = {} memory_mappers: Dict[str, DefaultMemoryMapper] = {}
def compute_memory_mapper(lang): def compute_memory_mapper(lang: str) -> DefaultMemoryMapper:
if not lang in memory_mappers: if not lang in memory_mappers:
return DEFAULT_MEMORY_MAPPER return DEFAULT_MEMORY_MAPPER
return memory_mappers[lang] return memory_mappers[lang]
@ -180,29 +182,29 @@ def compute_memory_mapper(lang):
class DefaultRegisterMapper(object): class DefaultRegisterMapper(object):
def __init__(self, byte_order): def __init__(self, byte_order: str) -> None:
if not byte_order in ['big', 'little']: if not byte_order in ['big', 'little']:
raise ValueError("Invalid byte_order: {}".format(byte_order)) raise ValueError("Invalid byte_order: {}".format(byte_order))
self.byte_order = byte_order self.byte_order = byte_order
self.union_winners = {}
def map_name(self, proc, name): def map_name(self, proc: int, name: str) -> str:
return name return name
def map_value(self, proc, name, value): def map_value(self, proc: int, name: str, value: bytes):
return RegVal(self.map_name(proc, name), value) return RegVal(self.map_name(proc, name), value)
def map_name_back(self, proc, name): def map_name_back(self, proc: int, name: str):
return name return name
def map_value_back(self, proc, name, value): def map_value_back(self, proc: int, name: str, value: bytes):
return RegVal(self.map_name_back(proc, name), value) return RegVal(self.map_name_back(proc, name), value)
DEFAULT_BE_REGISTER_MAPPER = DefaultRegisterMapper('big') DEFAULT_BE_REGISTER_MAPPER = DefaultRegisterMapper('big')
DEFAULT_LE_REGISTER_MAPPER = DefaultRegisterMapper('little') DEFAULT_LE_REGISTER_MAPPER = DefaultRegisterMapper('little')
def compute_register_mapper(lang):
def compute_register_mapper(lang: str) -> DefaultRegisterMapper:
if ':BE:' in lang: if ':BE:' in lang:
return DEFAULT_BE_REGISTER_MAPPER return DEFAULT_BE_REGISTER_MAPPER
else: else:

View file

@ -13,45 +13,43 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
## ##
from dataclasses import dataclass, field
import threading import threading
import time import time
import drgn import drgn
from typing import Any, Callable, Collection, Dict, Optional, TypeVar, cast
from . import commands, util from . import commands, util
ALL_EVENTS = 0xFFFF ALL_EVENTS = 0xFFFF
@dataclass(frozen=False)
class HookState(object): class HookState(object):
__slots__ = ('installed', 'mem_catchpoint') installed = False
def __init__(self):
self.installed = False
self.mem_catchpoint = None
@dataclass(frozen=False)
class ProcessState(object): class ProcessState(object):
__slots__ = ('first', 'regions', 'modules', 'threads', first = True
'breaks', 'watches', 'visited')
def __init__(self):
self.first = True
# For things we can detect changes to between stops # For things we can detect changes to between stops
self.regions = False regions = False
self.modules = False modules = False
self.threads = False threads = False
self.breaks = False breaks = False
self.watches = False watches = False
# For frames and threads that have already been synced since last stop # For frames and threads that have already been synced since last stop
self.visited = set() visited: set[Any] = field(default_factory=set)
def record(self, description=None): def record(self, description: Optional[str] = None) -> None:
first = self.first first = self.first
self.first = False self.first = False
trace = commands.STATE.require_trace()
if description is not None: if description is not None:
commands.STATE.trace.snapshot(description) trace.snapshot(description)
if first: if first:
commands.put_processes() commands.put_processes()
commands.put_environment() commands.put_environment()
@ -84,51 +82,53 @@ class ProcessState(object):
commands.put_modules() commands.put_modules()
self.modules = False self.modules = False
def record_continued(self): def record_continued(self) -> None:
commands.put_processes() commands.put_processes()
commands.put_threads() commands.put_threads()
def record_exited(self, exit_code): def record_exited(self, exit_code: int) -> None:
trace = commands.STATE.require_trace()
nproc = util.selected_process() nproc = util.selected_process()
ipath = commands.PROCESS_PATTERN.format(procnum=nproc) ipath = commands.PROCESS_PATTERN.format(procnum=nproc)
procobj = commands.STATE.trace.proxy_object_path(ipath) procobj = trace.proxy_object_path(ipath)
procobj.set_value('Exit Code', exit_code) procobj.set_value('Exit Code', exit_code)
procobj.set_value('State', 'TERMINATED') procobj.set_value('State', 'TERMINATED')
HOOK_STATE = HookState() HOOK_STATE = HookState()
PROC_STATE = {} PROC_STATE: Dict[int, ProcessState] = {}
def on_new_process(event):
def on_new_process(id: int) -> None:
trace = commands.STATE.trace trace = commands.STATE.trace
if trace is None: if trace is None:
return return
with commands.STATE.client.batch(): with trace.client.batch():
with trace.open_tx("New Process {}".format(event.process.num)): with trace.open_tx("New Process {}".format(id)):
commands.put_processes() # TODO: Could put just the one.... commands.put_processes() # TODO: Could put just the one....
def on_process_selected(): def on_process_selected() -> None:
nproc = util.selected_process() nproc = util.selected_process()
if nproc not in PROC_STATE: if nproc not in PROC_STATE:
return return
trace = commands.STATE.trace trace = commands.STATE.trace
if trace is None: if trace is None:
return return
with commands.STATE.client.batch(): with trace.client.batch():
with trace.open_tx("Process {} selected".format(nproc)): with trace.open_tx("Process {} selected".format(nproc)):
PROC_STATE[nproc].record() PROC_STATE[nproc].record()
commands.activate() commands.activate()
def on_new_thread(event): def on_new_thread() -> None:
nproc = util.selected_process() nproc = util.selected_process()
if nproc not in PROC_STATE: if nproc not in PROC_STATE:
return return
PROC_STATE[nproc].threads = True PROC_STATE[nproc].threads = True
def on_thread_selected(): def on_thread_selected() -> None:
nproc = util.selected_process() nproc = util.selected_process()
if nproc not in PROC_STATE: if nproc not in PROC_STATE:
return return
@ -136,14 +136,14 @@ def on_thread_selected():
if trace is None: if trace is None:
return return
nthrd = util.selected_thread() nthrd = util.selected_thread()
with commands.STATE.client.batch(): with trace.client.batch():
with trace.open_tx("Thread {}.{} selected".format(nproc, nthrd)): with trace.open_tx("Thread {}.{} selected".format(nproc, nthrd)):
PROC_STATE[nproc].record() PROC_STATE[nproc].record()
commands.put_threads() commands.put_threads()
commands.activate() commands.activate()
def on_frame_selected(): def on_frame_selected() -> None:
nproc = util.selected_process() nproc = util.selected_process()
if nproc not in PROC_STATE: if nproc not in PROC_STATE:
return return
@ -152,7 +152,7 @@ def on_frame_selected():
return return
nthrd = util.selected_thread() nthrd = util.selected_thread()
level = util.selected_frame() level = util.selected_frame()
with commands.STATE.client.batch(): with trace.client.batch():
with trace.open_tx("Frame {}.{}.{} selected".format(nproc, nthrd, level)): with trace.open_tx("Frame {}.{}.{} selected".format(nproc, nthrd, level)):
PROC_STATE[nproc].record() PROC_STATE[nproc].record()
commands.put_threads() commands.put_threads()
@ -160,32 +160,7 @@ def on_frame_selected():
commands.activate() commands.activate()
def on_memory_changed(event): def on_cont() -> None:
nproc = util.get_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("Memory *0x{:08x} changed".format(event.address)):
commands.put_bytes(event.address, event.address + event.length,
pages=False, is_mi=False, result=None)
def on_register_changed(event):
nproc = util.get_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("Register {} changed".format(event.regnum)):
commands.putreg()
def on_cont(event):
nproc = util.selected_process() nproc = util.selected_process()
if nproc not in PROC_STATE: if nproc not in PROC_STATE:
return return
@ -193,12 +168,12 @@ def on_cont(event):
if trace is None: if trace is None:
return return
state = PROC_STATE[nproc] state = PROC_STATE[nproc]
with commands.STATE.client.batch(): with trace.client.batch():
with trace.open_tx("Continued"): with trace.open_tx("Continued"):
state.record_continued() state.record_continued()
def on_stop(event): def on_stop() -> None:
nproc = util.selected_process() nproc = util.selected_process()
if nproc not in PROC_STATE: if nproc not in PROC_STATE:
PROC_STATE[nproc] = ProcessState() PROC_STATE[nproc] = ProcessState()
@ -208,7 +183,7 @@ def on_stop(event):
return return
state = PROC_STATE[nproc] state = PROC_STATE[nproc]
state.visited.clear() state.visited.clear()
with commands.STATE.client.batch(): with trace.client.batch():
with trace.open_tx("Stopped"): with trace.open_tx("Stopped"):
state.record("Stopped") state.record("Stopped")
commands.put_threads() commands.put_threads()
@ -216,34 +191,31 @@ def on_stop(event):
commands.activate() commands.activate()
def modules_changed(): def modules_changed() -> None:
nproc = util.selected_process() nproc = util.selected_process()
if nproc not in PROC_STATE: if nproc not in PROC_STATE:
return return
PROC_STATE[nproc].modules = True PROC_STATE[nproc].modules = True
def install_hooks(): def install_hooks() -> None:
if HOOK_STATE.installed: if HOOK_STATE.installed:
return return
HOOK_STATE.installed = True HOOK_STATE.installed = True
event_thread = EventThread()
event_thread.start()
def remove_hooks() -> None:
def remove_hooks():
if not HOOK_STATE.installed: if not HOOK_STATE.installed:
return return
HOOK_STATE.installed = False HOOK_STATE.installed = False
def enable_current_process(): def enable_current_process() -> None:
nproc = util.selected_process() nproc = util.selected_process()
PROC_STATE[nproc] = ProcessState() PROC_STATE[nproc] = ProcessState()
def disable_current_process(): def disable_current_process() -> None:
nproc = util.selected_process() nproc = util.selected_process()
if nproc in PROC_STATE: if nproc in PROC_STATE:
del PROC_STATE[nproc] del PROC_STATE[nproc]

View file

@ -19,10 +19,11 @@ from io import StringIO
import re import re
import sys import sys
import time import time
from typing import Annotated, Any, Dict, Optional from typing import Annotated, Any, Dict, Optional, Tuple
import drgn import drgn
import drgn.cli import drgn.cli
from drgn import Module, StackFrame # type: ignore
from ghidratrace import sch from ghidratrace import sch
from ghidratrace.client import ( from ghidratrace.client import (
@ -35,159 +36,22 @@ REGISTRY = MethodRegistry(ThreadPoolExecutor(
max_workers=1, thread_name_prefix='MethodRegistry')) max_workers=1, thread_name_prefix='MethodRegistry'))
def extre(base, ext): def extre(base, ext) -> re.Pattern:
return re.compile(base.pattern + ext) return re.compile(base.pattern + ext)
PROCESSES_PATTERN = re.compile('Processes') PROCESSES_PATTERN = re.compile('Processes')
PROCESS_PATTERN = extre(PROCESSES_PATTERN, '\[(?P<procnum>\\d*)\]') PROCESS_PATTERN = extre(PROCESSES_PATTERN, '\\[(?P<procnum>\\d*)\\]')
ENV_PATTERN = extre(PROCESS_PATTERN, '\.Environment') ENV_PATTERN = extre(PROCESS_PATTERN, '\\.Environment')
THREADS_PATTERN = extre(PROCESS_PATTERN, '\.Threads') THREADS_PATTERN = extre(PROCESS_PATTERN, '\\.Threads')
THREAD_PATTERN = extre(THREADS_PATTERN, '\[(?P<tnum>\\d*)\]') THREAD_PATTERN = extre(THREADS_PATTERN, '\\[(?P<tnum>\\d*)\\]')
STACK_PATTERN = extre(THREAD_PATTERN, '\.Stack') STACK_PATTERN = extre(THREAD_PATTERN, '\\.Stack')
FRAME_PATTERN = extre(STACK_PATTERN, '\[(?P<level>\\d*)\]') FRAME_PATTERN = extre(STACK_PATTERN, '\\[(?P<level>\\d*)\\]')
REGS_PATTERN = extre(FRAME_PATTERN, '.Registers') REGS_PATTERN = extre(FRAME_PATTERN, '.Registers')
LOCALS_PATTERN = extre(FRAME_PATTERN, '.Locals') LOCALS_PATTERN = extre(FRAME_PATTERN, '.Locals')
MEMORY_PATTERN = extre(PROCESS_PATTERN, '\.Memory') MEMORY_PATTERN = extre(PROCESS_PATTERN, '\\.Memory')
MODULES_PATTERN = extre(PROCESS_PATTERN, '\.Modules') MODULES_PATTERN = extre(PROCESS_PATTERN, '\\.Modules')
MODULE_PATTERN = extre(MODULES_PATTERN, '\[(?P<modbase>.*)\]') MODULE_PATTERN = extre(MODULES_PATTERN, '\\[(?P<modbase>.*)\\]')
def find_availpid_by_pattern(pattern, object, err_msg):
mat = pattern.fullmatch(object.path)
if mat is None:
raise TypeError(f"{object} is not {err_msg}")
pid = int(mat['pid'])
return pid
def find_availpid_by_obj(object):
return find_availpid_by_pattern(AVAILABLE_PATTERN, object, "an Available")
def find_proc_by_num(id):
if id != util.selected_process():
util.select_process(id)
return util.selected_process()
def find_proc_by_pattern(object, pattern, err_msg):
mat = pattern.fullmatch(object.path)
if mat is None:
raise TypeError(f"{object} is not {err_msg}")
procnum = int(mat['procnum'])
return find_proc_by_num(procnum)
def find_proc_by_obj(object):
return find_proc_by_pattern(object, PROCESS_PATTERN, "an Process")
def find_proc_by_env_obj(object):
return find_proc_by_pattern(object, ENV_PATTERN, "an Environment")
def find_proc_by_threads_obj(object):
return find_proc_by_pattern(object, THREADS_PATTERN, "a ThreadContainer")
def find_proc_by_mem_obj(object):
return find_proc_by_pattern(object, MEMORY_PATTERN, "a Memory")
def find_proc_by_modules_obj(object):
return find_proc_by_pattern(object, MODULES_PATTERN, "a ModuleContainer")
def find_thread_by_num(id):
if id != util.selected_thread():
util.select_thread(id)
return util.selected_thread()
def find_thread_by_pattern(pattern, object, err_msg):
mat = pattern.fullmatch(object.path)
if mat is None:
raise TypeError(f"{object} is not {err_msg}")
pnum = int(mat['procnum'])
tnum = int(mat['tnum'])
find_proc_by_num(pnum)
return find_thread_by_num(tnum)
def find_thread_by_obj(object):
return find_thread_by_pattern(THREAD_PATTERN, object, "a Thread")
def find_thread_by_stack_obj(object):
return find_thread_by_pattern(STACK_PATTERN, object, "a Stack")
def find_thread_by_regs_obj(object):
return find_thread_by_pattern(REGS_PATTERN, object, "a RegisterValueContainer")
def find_frame_by_level(level):
tnum = util.selected_thread()
thread = commands.prog.thread(tnum)
try:
frames = thread.stack_trace()
except Exception as e:
print(e)
return
for i, f in enumerate(frames):
if i == level:
if i != util.selected_frame():
util.select_frame(i)
return i, f
def find_frame_by_pattern(pattern, object, err_msg):
mat = pattern.fullmatch(object.path)
if mat is None:
raise TypeError(f"{object} is not {err_msg}")
pnum = int(mat['procnum'])
tnum = int(mat['tnum'])
level = int(mat['level'])
find_proc_by_num(pnum)
find_thread_by_num(tnum)
return find_frame_by_level(level)
def find_frame_by_obj(object):
return find_frame_by_pattern(FRAME_PATTERN, object, "a StackFrame")
def find_frame_by_regs_obj(object):
return find_frame_by_pattern(REGS_PATTERN, object, "a RegisterValueContainer")
def find_frame_by_locals_obj(object):
return find_frame_by_pattern(LOCALS_PATTERN, object, "a LocalsContainer")
def find_module_by_base(modbase):
for m in commands.prog.modules():
if modbase == str(hex(m.address_range[0])):
return m
def find_module_by_pattern(pattern, object, err_msg):
mat = pattern.fullmatch(object.path)
if mat is None:
raise TypeError(f"{object} is not {err_msg}")
pnum = int(mat['procnum'])
modbase = mat['modbase']
find_proc_by_num(pnum)
return find_module_by_base(modbase)
def find_module_by_obj(object):
return find_module_by_pattern(MODULE_PATTERN, object, "a Module")
shared_globals: Dict[str, Any] = dict()
class Environment(TraceObject): class Environment(TraceObject):
@ -222,10 +86,6 @@ class RegisterValueContainer(TraceObject):
pass pass
class StackFrame(TraceObject):
pass
class SymbolContainer(TraceObject): class SymbolContainer(TraceObject):
pass pass
@ -238,6 +98,136 @@ class ThreadContainer(TraceObject):
pass pass
def find_proc_by_num(id: int) -> int:
if id != util.selected_process():
util.select_process(id)
return util.selected_process()
def find_proc_by_pattern(object: TraceObject, pattern: re.Pattern,
err_msg: str) -> int:
mat = pattern.fullmatch(object.path)
if mat is None:
raise TypeError(f"{object} is not {err_msg}")
procnum = int(mat['procnum'])
return find_proc_by_num(procnum)
def find_proc_by_obj(object: TraceObject) -> int:
return find_proc_by_pattern(object, PROCESS_PATTERN, "an Process")
def find_proc_by_env_obj(object: TraceObject) -> int:
return find_proc_by_pattern(object, ENV_PATTERN, "an Environment")
def find_proc_by_threads_obj(object: TraceObject) -> int:
return find_proc_by_pattern(object, THREADS_PATTERN, "a ThreadContainer")
def find_proc_by_mem_obj(object: TraceObject) -> int:
return find_proc_by_pattern(object, MEMORY_PATTERN, "a Memory")
def find_proc_by_modules_obj(object: TraceObject) -> int:
return find_proc_by_pattern(object, MODULES_PATTERN, "a ModuleContainer")
def find_thread_by_num(id: int) -> Optional[int]:
if id != util.selected_thread():
util.select_thread(id)
return util.selected_thread()
def find_thread_by_pattern(pattern: re.Pattern, object: TraceObject,
err_msg: str) -> Optional[int]:
mat = pattern.fullmatch(object.path)
if mat is None:
raise TypeError(f"{object} is not {err_msg}")
pnum = int(mat['procnum'])
tnum = int(mat['tnum'])
find_proc_by_num(pnum)
return find_thread_by_num(tnum)
def find_thread_by_obj(object: TraceObject) -> Optional[int]:
return find_thread_by_pattern(THREAD_PATTERN, object, "a Thread")
def find_thread_by_stack_obj(object: TraceObject) -> Optional[int]:
return find_thread_by_pattern(STACK_PATTERN, object, "a Stack")
def find_thread_by_regs_obj(object: TraceObject) -> Optional[int]:
return find_thread_by_pattern(REGS_PATTERN, object, "a RegisterValueContainer")
def find_frame_by_level(level: int) -> Optional[Tuple[int, StackFrame]]:
tnum = util.selected_thread()
thread = commands.prog.thread(tnum)
try:
frames = thread.stack_trace()
except Exception as e:
print(e)
return None
for i, f in enumerate(frames):
if i == level:
if i != util.selected_frame():
util.select_frame(i)
return i, f
return None
def find_frame_by_pattern(pattern: re.Pattern, object: TraceObject,
err_msg: str) -> Optional[Tuple[int, StackFrame]]:
mat = pattern.fullmatch(object.path)
if mat is None:
raise TypeError(f"{object} is not {err_msg}")
pnum = int(mat['procnum'])
tnum = int(mat['tnum'])
level = int(mat['level'])
find_proc_by_num(pnum)
find_thread_by_num(tnum)
return find_frame_by_level(level)
def find_frame_by_obj(object: TraceObject) -> Optional[Tuple[int, StackFrame]]:
return find_frame_by_pattern(FRAME_PATTERN, object, "a StackFrame")
def find_frame_by_regs_obj(object: TraceObject) -> Optional[Tuple[int, StackFrame]]:
return find_frame_by_pattern(REGS_PATTERN, object, "a RegisterValueContainer")
def find_frame_by_locals_obj(object: TraceObject) -> Optional[Tuple[int, StackFrame]]:
return find_frame_by_pattern(LOCALS_PATTERN, object, "a LocalsContainer")
def find_module_by_base(modbase: TraceObject) -> Module:
for m in commands.prog.modules(): # type: ignore
if modbase == str(hex(m.address_range[0])):
return m
def find_module_by_pattern(pattern: re.Pattern, object: TraceObject,
err_msg: str) -> int:
mat = pattern.fullmatch(object.path)
if mat is None:
raise TypeError(f"{object} is not {err_msg}")
pnum = int(mat['procnum'])
modbase = mat['modbase']
find_proc_by_num(pnum)
return find_module_by_base(modbase)
def find_module_by_obj(object: TraceObject) -> int:
return find_module_by_pattern(MODULE_PATTERN, object, "a Module")
shared_globals: Dict[str, Any] = dict()
@REGISTRY.method() @REGISTRY.method()
def execute(cmd: str, to_string: bool = False) -> Optional[str]: def execute(cmd: str, to_string: bool = False) -> Optional[str]:
"""Execute a Python3 command or script.""" """Execute a Python3 command or script."""
@ -341,9 +331,12 @@ def activate_thread(thread: Thread) -> None:
@REGISTRY.method(action='activate') @REGISTRY.method(action='activate')
def activate_frame(frame: StackFrame) -> None: def activate_frame(frame: TraceObject) -> None:
"""Select the frame.""" """Select the frame."""
i, f = find_frame_by_obj(frame) res = find_frame_by_obj(frame)
if res is None:
return
i, f = res
util.select_frame(i) util.select_frame(i)
with commands.open_tracked_tx('Refresh Stack'): with commands.open_tracked_tx('Refresh Stack'):
commands.ghidra_trace_put_frames() commands.ghidra_trace_put_frames()
@ -411,7 +404,7 @@ def step_into(thread: Thread,
"""Step one instruction exactly.""" """Step one instruction exactly."""
find_thread_by_obj(thread) find_thread_by_obj(thread)
time.sleep(1) time.sleep(1)
hooks.on_stop(None) hooks.on_stop()
# @REGISTRY.method # @REGISTRY.method

View file

@ -14,9 +14,11 @@
# limitations under the License. # limitations under the License.
## ##
from collections import namedtuple from collections import namedtuple
from dataclasses import dataclass
import os import os
import re import re
import sys import sys
from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union
import drgn import drgn
import drgn.cli import drgn.cli
@ -29,7 +31,7 @@ selected_tid = 0
selected_level = 0 selected_level = 0
def _compute_drgn_ver(): def _compute_drgn_ver() -> DrgnVersion:
blurb = drgn.cli.version_header() blurb = drgn.cli.version_header()
top = blurb.split('\n')[0] top = blurb.split('\n')[0]
full = top.split()[1] # "drgn x.y.z" full = top.split()[1] # "drgn x.y.z"
@ -38,7 +40,17 @@ def _compute_drgn_ver():
DRGN_VERSION = _compute_drgn_ver() DRGN_VERSION = _compute_drgn_ver()
def full_mem(self):
@dataclass(frozen=True)
class Region:
start: int
end: int
offset: int
perms: Optional[str]
objfile: str
def full_mem(self) -> Region:
return Region(0, 1 << 64, 0, None, 'full memory') return Region(0, 1 << 64, 0, None, 'full memory')
@ -46,48 +58,40 @@ def get_debugger():
return drgn return drgn
def get_target(): def selected_process() -> int:
return commands.prog
def get_process(name):
return get_target()[name]
def selected_process():
return selected_pid return selected_pid
def selected_thread(): def selected_thread() -> int:
return selected_tid return selected_tid
def selected_frame(): def selected_frame() -> int:
return selected_level return selected_level
def select_process(id: int): def select_process(id: int) -> int:
global selected_pid global selected_pid
selected_pid = id selected_pid = id
return selected_pid return selected_pid
def select_thread(id: int): def select_thread(id: int) -> int:
global selected_tid global selected_tid
selected_tid = id selected_tid = id
return selected_tid return selected_tid
def select_frame(id: int): def select_frame(id: int) -> int:
global selected_level global selected_level
selected_level = id selected_level = id
return selected_level return selected_level
conv_map = {} conv_map: Dict[str, Any] = {}
def get_convenience_variable(id): def get_convenience_variable(id: str):
# val = get_target().GetEnvironment().Get(id) # val = get_target().GetEnvironment().Get(id)
if id not in conv_map: if id not in conv_map:
return "auto" return "auto"
@ -97,18 +101,18 @@ def get_convenience_variable(id):
return val return val
def set_convenience_variable(id, value): def set_convenience_variable(id: str, value: Any) -> None:
# env = get_target().GetEnvironment() # env = get_target().GetEnvironment()
# return env.Set(id, value, True) # return env.Set(id, value, True)
conv_map[id] = value conv_map[id] = value
def escape_ansi(line): def escape_ansi(line: str) -> str:
ansi_escape = re.compile(r'(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]') ansi_escape = re.compile(r'(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]')
return ansi_escape.sub('', line) return ansi_escape.sub('', line)
def debracket(init): def debracket(init: str) -> str:
val = init val = init
val = val.replace("[", "(") val = val.replace("[", "(")
val = val.replace("]", ")") val = val.replace("]", ")")