mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +02:00
GP-3754: post-review review
GP-3754: last pass GP-3754: most review issues address (ymm0/testSave not); tests pass GP-3754: review - eval/exec GP-3754: moved registers to thread GP-3754: review pass 7 - register w/o banks GP-3754: review pass 6 - basic clean-up items GP-3754: pass 5 - tests all pass (minus commented-out ones) GP-3754: review pass 4(?) GP-3754: review pass 3 GP-3754: review pass 2 GP-3754: review pass 1 GP-3754: ref Pybag GP-3754: clean-up GP-3754: testing post-changes in hooks GP-3754: fix for env GP-3754: convenience method for get_debugger GP-3754: tests all running successfully GP-3754: hook tests running GP-3754: hook tests: memory changed not wokring, bpt modified does nothing GP-3754: lot of work to get testExited to pass GP-3754: start on hooks tests; mods to methods not-runnable while running GP-3754: methods tests working GP-3754: bulk of bpt tests running GP-3754: whittling down the list GP-3754: more passing method tests GP-3754: at least a few method tests working (don't run batch) GP-3754: cmd tests pass w/o closing stdin GP-3754: command test basically running GP-3754: 3 cmd tests failing; 3 commented out GP-3754: ghidra_trace_set_values uses broken except for in testGetValues GP-3754: whittling down the command tests GP-3754: tests esp. SetValue GP-3754: testMinimal works but cannot 'execute' GP-3754: JUnits still don't run GP-3754: breakpoints in the list GP-3754: continued work on hooks; bpts not registering as bpts GP-3754: templates for hooks GP-3754: tests, first viable hook (module_load)' GP-3754: first pass at methods GP-3754: memory/regs working GP-3754: most of the puts done GP-3754: modules/regions working GP-3754: process/threads working GP-3754: added to manifest GP-3754: minimal shell: arch faked out / thru activate w/o push
This commit is contained in:
parent
f64c38ef7f
commit
abbc18f927
20 changed files with 6266 additions and 2 deletions
11
Ghidra/Debug/Debugger-agent-dbgeng/src/main/py/LICENSE
Normal file
11
Ghidra/Debug/Debugger-agent-dbgeng/src/main/py/LICENSE
Normal file
|
@ -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.
|
8
Ghidra/Debug/Debugger-agent-dbgeng/src/main/py/README.md
Normal file
8
Ghidra/Debug/Debugger-agent-dbgeng/src/main/py/README.md
Normal file
|
@ -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()
|
|
@ -0,0 +1,522 @@
|
|||
## ###
|
||||
# IP: GHIDRA
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
##
|
||||
from concurrent.futures import Future, ThreadPoolExecutor
|
||||
import re
|
||||
import sys
|
||||
|
||||
from ghidratrace import sch
|
||||
from ghidratrace.client import MethodRegistry, ParamDesc, Address, AddressRange
|
||||
|
||||
from pybag import pydbg
|
||||
from pybag.dbgeng import core as DbgEng
|
||||
|
||||
from . import util, commands
|
||||
from contextlib import redirect_stdout
|
||||
from io import StringIO
|
||||
|
||||
|
||||
REGISTRY = MethodRegistry(ThreadPoolExecutor(max_workers=1))
|
||||
|
||||
|
||||
def extre(base, ext):
|
||||
return re.compile(base.pattern + ext)
|
||||
|
||||
|
||||
AVAILABLE_PATTERN = re.compile('Available\[(?P<pid>\\d*)\]')
|
||||
WATCHPOINT_PATTERN = re.compile('Watchpoints\[(?P<watchnum>\\d*)\]')
|
||||
BREAKPOINT_PATTERN = re.compile('Breakpoints\[(?P<breaknum>\\d*)\]')
|
||||
BREAK_LOC_PATTERN = extre(BREAKPOINT_PATTERN, '\[(?P<locnum>\\d*)\]')
|
||||
PROCESS_PATTERN = re.compile('Processes\[(?P<procnum>\\d*)\]')
|
||||
PROC_BREAKS_PATTERN = extre(PROCESS_PATTERN, '\.Breakpoints')
|
||||
PROC_BREAKBPT_PATTERN = extre(PROC_BREAKS_PATTERN, '\[(?P<breaknum>\\d*)\]')
|
||||
ENV_PATTERN = extre(PROCESS_PATTERN, '\.Environment')
|
||||
THREADS_PATTERN = extre(PROCESS_PATTERN, '\.Threads')
|
||||
THREAD_PATTERN = extre(THREADS_PATTERN, '\[(?P<tnum>\\d*)\]')
|
||||
STACK_PATTERN = extre(THREAD_PATTERN, '\.Stack')
|
||||
FRAME_PATTERN = extre(STACK_PATTERN, '\[(?P<level>\\d*)\]')
|
||||
REGS_PATTERN0 = extre(THREAD_PATTERN, '.Registers')
|
||||
REGS_PATTERN = extre(FRAME_PATTERN, '.Registers')
|
||||
MEMORY_PATTERN = extre(PROCESS_PATTERN, '\.Memory')
|
||||
MODULES_PATTERN = extre(PROCESS_PATTERN, '\.Modules')
|
||||
|
||||
|
||||
def find_availpid_by_pattern(pattern, object, err_msg):
|
||||
mat = pattern.fullmatch(object.path)
|
||||
if mat is None:
|
||||
raise TypeError(f"{object} is not {err_msg}")
|
||||
pid = int(mat['pid'])
|
||||
return pid
|
||||
|
||||
|
||||
def find_availpid_by_obj(object):
|
||||
return find_availpid_by_pattern(AVAILABLE_PATTERN, object, "an Available")
|
||||
|
||||
|
||||
def find_proc_by_num(id):
|
||||
if id != util.selected_process():
|
||||
util.select_process(id)
|
||||
return util.selected_process()
|
||||
|
||||
|
||||
def find_proc_by_pattern(object, pattern, err_msg):
|
||||
mat = pattern.fullmatch(object.path)
|
||||
if mat is None:
|
||||
raise TypeError(f"{object} is not {err_msg}")
|
||||
procnum = int(mat['procnum'])
|
||||
return find_proc_by_num(procnum)
|
||||
|
||||
|
||||
def find_proc_by_obj(object):
|
||||
return find_proc_by_pattern(object, PROCESS_PATTERN, "an Process")
|
||||
|
||||
|
||||
def find_proc_by_procbreak_obj(object):
|
||||
return find_proc_by_pattern(object, PROC_BREAKS_PATTERN,
|
||||
"a BreakpointLocationContainer")
|
||||
|
||||
def find_proc_by_procwatch_obj(object):
|
||||
return find_proc_by_pattern(object, PROC_WATCHES_PATTERN,
|
||||
"a WatchpointContainer")
|
||||
|
||||
|
||||
def find_proc_by_env_obj(object):
|
||||
return find_proc_by_pattern(object, ENV_PATTERN, "an Environment")
|
||||
|
||||
|
||||
def find_proc_by_threads_obj(object):
|
||||
return find_proc_by_pattern(object, THREADS_PATTERN, "a ThreadContainer")
|
||||
|
||||
|
||||
def find_proc_by_mem_obj(object):
|
||||
return find_proc_by_pattern(object, MEMORY_PATTERN, "a Memory")
|
||||
|
||||
|
||||
def find_proc_by_modules_obj(object):
|
||||
return find_proc_by_pattern(object, MODULES_PATTERN, "a ModuleContainer")
|
||||
|
||||
|
||||
def find_thread_by_num(id):
|
||||
if id != util.selected_thread():
|
||||
util.select_thread(id)
|
||||
return util.selected_thread()
|
||||
|
||||
|
||||
def find_thread_by_pattern(pattern, object, err_msg):
|
||||
mat = pattern.fullmatch(object.path)
|
||||
if mat is None:
|
||||
raise TypeError(f"{object} is not {err_msg}")
|
||||
pnum = int(mat['procnum'])
|
||||
tnum = int(mat['tnum'])
|
||||
find_proc_by_num(pnum)
|
||||
return find_thread_by_num(tnum)
|
||||
|
||||
|
||||
def find_thread_by_obj(object):
|
||||
return find_thread_by_pattern(THREAD_PATTERN, object, "a Thread")
|
||||
|
||||
|
||||
def find_thread_by_stack_obj(object):
|
||||
return find_thread_by_pattern(STACK_PATTERN, object, "a Stack")
|
||||
|
||||
|
||||
def find_thread_by_regs_obj(object):
|
||||
return find_thread_by_pattern(REGS_PATTERN0, object, "a RegisterValueContainer")
|
||||
|
||||
|
||||
def find_frame_by_level(level):
|
||||
return dbg().backtrace_list()[level]
|
||||
|
||||
|
||||
def find_frame_by_pattern(pattern, object, err_msg):
|
||||
mat = pattern.fullmatch(object.path)
|
||||
if mat is None:
|
||||
raise TypeError(f"{object} is not {err_msg}")
|
||||
pnum = int(mat['procnum'])
|
||||
tnum = int(mat['tnum'])
|
||||
level = int(mat['level'])
|
||||
find_proc_by_num(pnum)
|
||||
find_thread_by_num(tnum)
|
||||
return find_frame_by_level(level)
|
||||
|
||||
|
||||
def find_frame_by_obj(object):
|
||||
return find_frame_by_pattern(FRAME_PATTERN, object, "a StackFrame")
|
||||
|
||||
|
||||
def find_bpt_by_number(breaknum):
|
||||
try:
|
||||
bp = dbg()._control.GetBreakpointById(breaknum)
|
||||
return bp
|
||||
except exception.E_NOINTERFACE_Error:
|
||||
raise KeyError(f"Breakpoints[{breaknum}] does not exist")
|
||||
|
||||
|
||||
def find_bpt_by_pattern(pattern, object, err_msg):
|
||||
mat = pattern.fullmatch(object.path)
|
||||
if mat is None:
|
||||
raise TypeError(f"{object} is not {err_msg}")
|
||||
breaknum = int(mat['breaknum'])
|
||||
return find_bpt_by_number(breaknum)
|
||||
|
||||
|
||||
def find_bpt_by_obj(object):
|
||||
return find_bpt_by_pattern(PROC_BREAKBPT_PATTERN, object, "a BreakpointSpec")
|
||||
|
||||
|
||||
shared_globals = dict()
|
||||
|
||||
@REGISTRY.method
|
||||
def execute(cmd: str, to_string: bool=False):
|
||||
"""Execute a CLI command."""
|
||||
#print("***{}***".format(cmd))
|
||||
#sys.stderr.flush()
|
||||
#sys.stdout.flush()
|
||||
if to_string:
|
||||
data = StringIO()
|
||||
with redirect_stdout(data):
|
||||
exec("{}".format(cmd), shared_globals)
|
||||
return data.getvalue()
|
||||
else:
|
||||
exec("{}".format(cmd), shared_globals)
|
||||
|
||||
|
||||
@REGISTRY.method
|
||||
def evaluate(expr: str):
|
||||
"""Execute a CLI command."""
|
||||
return str(eval("{}".format(expr), shared_globals))
|
||||
|
||||
|
||||
@REGISTRY.method(action='refresh')
|
||||
def refresh_available(node: sch.Schema('AvailableContainer')):
|
||||
"""List processes on pydbg's host system."""
|
||||
with commands.open_tracked_tx('Refresh Available'):
|
||||
commands.ghidra_trace_put_available()
|
||||
|
||||
|
||||
@REGISTRY.method(action='refresh')
|
||||
def refresh_breakpoints(node: sch.Schema('BreakpointContainer')):
|
||||
"""
|
||||
Refresh the list of breakpoints (including locations for the current
|
||||
process).
|
||||
"""
|
||||
with commands.open_tracked_tx('Refresh Breakpoints'):
|
||||
commands.ghidra_trace_put_breakpoints()
|
||||
|
||||
|
||||
@REGISTRY.method(action='refresh')
|
||||
def refresh_processes(node: sch.Schema('ProcessContainer')):
|
||||
"""Refresh the list of processes."""
|
||||
with commands.open_tracked_tx('Refresh Processes'):
|
||||
commands.ghidra_trace_put_threads()
|
||||
|
||||
|
||||
@REGISTRY.method(action='refresh')
|
||||
def refresh_proc_breakpoints(node: sch.Schema('BreakpointLocationContainer')):
|
||||
"""
|
||||
Refresh the breakpoint locations for the process.
|
||||
|
||||
In the course of refreshing the locations, the breakpoint list will also be
|
||||
refreshed.
|
||||
"""
|
||||
with commands.open_tracked_tx('Refresh Breakpoint Locations'):
|
||||
commands.ghidra_trace_put_breakpoints()
|
||||
|
||||
|
||||
@REGISTRY.method(action='refresh')
|
||||
def refresh_environment(node: sch.Schema('Environment')):
|
||||
"""Refresh the environment descriptors (arch, os, endian)."""
|
||||
with commands.open_tracked_tx('Refresh Environment'):
|
||||
commands.ghidra_trace_put_environment()
|
||||
|
||||
@REGISTRY.method(action='refresh')
|
||||
def refresh_threads(node: sch.Schema('ThreadContainer')):
|
||||
"""Refresh the list of threads in the process."""
|
||||
with commands.open_tracked_tx('Refresh Threads'):
|
||||
commands.ghidra_trace_put_threads()
|
||||
|
||||
|
||||
@REGISTRY.method(action='refresh')
|
||||
def refresh_stack(node: sch.Schema('Stack')):
|
||||
"""Refresh the backtrace for the thread."""
|
||||
tnum = find_thread_by_stack_obj(node)
|
||||
with commands.open_tracked_tx('Refresh Stack'):
|
||||
commands.ghidra_trace_put_frames()
|
||||
|
||||
|
||||
@REGISTRY.method(action='refresh')
|
||||
def refresh_registers(node: sch.Schema('RegisterValueContainer')):
|
||||
"""Refresh the register values for the frame."""
|
||||
tnum = find_thread_by_regs_obj(node)
|
||||
with commands.open_tracked_tx('Refresh Registers'):
|
||||
commands.ghidra_trace_putreg()
|
||||
|
||||
|
||||
@REGISTRY.method(action='refresh')
|
||||
def refresh_mappings(node: sch.Schema('Memory')):
|
||||
"""Refresh the list of memory regions for the process."""
|
||||
with commands.open_tracked_tx('Refresh Memory Regions'):
|
||||
commands.ghidra_trace_put_regions()
|
||||
|
||||
|
||||
@REGISTRY.method(action='refresh')
|
||||
def refresh_modules(node: sch.Schema('ModuleContainer')):
|
||||
"""
|
||||
Refresh the modules and sections list for the process.
|
||||
|
||||
This will refresh the sections for all modules, not just the selected one.
|
||||
"""
|
||||
with commands.open_tracked_tx('Refresh Modules'):
|
||||
commands.ghidra_trace_put_modules()
|
||||
|
||||
|
||||
@REGISTRY.method(action='activate')
|
||||
def activate_process(process: sch.Schema('Process')):
|
||||
"""Switch to the process."""
|
||||
find_proc_by_obj(process)
|
||||
|
||||
@REGISTRY.method(action='activate')
|
||||
def activate_thread(thread: sch.Schema('Thread')):
|
||||
"""Switch to the thread."""
|
||||
find_thread_by_obj(thread)
|
||||
|
||||
|
||||
@REGISTRY.method(action='activate')
|
||||
def activate_frame(frame: sch.Schema('StackFrame')):
|
||||
"""Select the frame."""
|
||||
find_frame_by_obj(frame)
|
||||
|
||||
|
||||
@REGISTRY.method(action='delete')
|
||||
def remove_process(process: sch.Schema('Process')):
|
||||
"""Remove the process."""
|
||||
find_proc_by_obj(process)
|
||||
dbg().detach()
|
||||
|
||||
|
||||
@REGISTRY.method(action='connect')
|
||||
def target(process: sch.Schema('Process'), spec: str):
|
||||
"""Connect to a target machine or process."""
|
||||
find_proc_by_obj(process)
|
||||
dbg().attach(spec)
|
||||
|
||||
|
||||
@REGISTRY.method(action='attach')
|
||||
def attach_obj(target: sch.Schema('Attachable')):
|
||||
"""Attach the process to the given target."""
|
||||
pid = find_availpid_by_obj(target)
|
||||
dbg().attach(pid)
|
||||
|
||||
@REGISTRY.method(action='attach')
|
||||
def attach_pid(pid: int):
|
||||
"""Attach the process to the given target."""
|
||||
dbg().attach(pid)
|
||||
|
||||
@REGISTRY.method(action='attach')
|
||||
def attach_name(process: sch.Schema('Process'), name: str):
|
||||
"""Attach the process to the given target."""
|
||||
dbg().atach(name)
|
||||
|
||||
|
||||
@REGISTRY.method
|
||||
def detach(process: sch.Schema('Process')):
|
||||
"""Detach the process's target."""
|
||||
dbg().detach()
|
||||
|
||||
|
||||
@REGISTRY.method(action='launch')
|
||||
def launch_loader(
|
||||
file: ParamDesc(str, display='File'),
|
||||
args: ParamDesc(str, display='Arguments')=''):
|
||||
"""
|
||||
Start a native process with the given command line, stopping at the ntdll initial breakpoint.
|
||||
"""
|
||||
command = file
|
||||
if args != None:
|
||||
command += " "+args
|
||||
commands.ghidra_trace_create(command=file, start_trace=False)
|
||||
|
||||
|
||||
@REGISTRY.method(action='launch')
|
||||
def launch(
|
||||
timeout: ParamDesc(int, display='Timeout'),
|
||||
file: ParamDesc(str, display='File'),
|
||||
args: ParamDesc(str, display='Arguments')=''):
|
||||
"""
|
||||
Run a native process with the given command line.
|
||||
"""
|
||||
command = file
|
||||
if args != None:
|
||||
command += " "+args
|
||||
commands.ghidra_trace_create(command, initial_break=False, timeout=timeout, start_trace=False)
|
||||
|
||||
|
||||
@REGISTRY.method
|
||||
def kill(process: sch.Schema('Process')):
|
||||
"""Kill execution of the process."""
|
||||
dbg().terminate()
|
||||
|
||||
|
||||
@REGISTRY.method(name='continue', action='resume')
|
||||
def _continue(process: sch.Schema('Process')):
|
||||
"""Continue execution of the process."""
|
||||
dbg().go()
|
||||
|
||||
|
||||
@REGISTRY.method
|
||||
def interrupt():
|
||||
"""Interrupt the execution of the debugged program."""
|
||||
dbg()._control.SetInterrupt(DbgEng.DEBUG_INTERRUPT_ACTIVE)
|
||||
|
||||
|
||||
@REGISTRY.method(action='step_into')
|
||||
def step_into(thread: sch.Schema('Thread'), n: ParamDesc(int, display='N')=1):
|
||||
"""Step on instruction exactly."""
|
||||
find_thread_by_obj(thread)
|
||||
dbg().stepi(n)
|
||||
|
||||
|
||||
@REGISTRY.method(action='step_over')
|
||||
def step_over(thread: sch.Schema('Thread'), n: ParamDesc(int, display='N')=1):
|
||||
"""Step one instruction, but proceed through subroutine calls."""
|
||||
find_thread_by_obj(thread)
|
||||
dbg().stepo(n)
|
||||
|
||||
|
||||
@REGISTRY.method(action='step_out')
|
||||
def step_out(thread: sch.Schema('Thread')):
|
||||
"""Execute until the current stack frame returns."""
|
||||
find_thread_by_obj(thread)
|
||||
dbg().stepout()
|
||||
|
||||
|
||||
@REGISTRY.method(action='step_to')
|
||||
def step_to(thread: sch.Schema('Thread'), address: Address, max=None):
|
||||
"""Continue execution up to the given address."""
|
||||
find_thread_by_obj(thread)
|
||||
return dbg().stepto(address.offset, max)
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_sw_execute')
|
||||
def break_address(process: sch.Schema('Process'), address: Address):
|
||||
"""Set a breakpoint."""
|
||||
find_proc_by_obj(process)
|
||||
dbg().bp(expr=address.offset)
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_sw_execute')
|
||||
def break_expression(expression: str):
|
||||
"""Set a breakpoint."""
|
||||
# TODO: Escape?
|
||||
dbg().bp(expr=expression)
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_hw_execute')
|
||||
def break_hw_address(process: sch.Schema('Process'), address: Address):
|
||||
"""Set a hardware-assisted breakpoint."""
|
||||
find_proc_by_obj(process)
|
||||
dbg().ba(expr=address.offset)
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_hw_execute')
|
||||
def break_hw_expression(expression: str):
|
||||
"""Set a hardware-assisted breakpoint."""
|
||||
dbg().ba(expr=expression)
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_read')
|
||||
def break_read_range(process: sch.Schema('Process'), range: AddressRange):
|
||||
"""Set a read watchpoint."""
|
||||
find_proc_by_obj(process)
|
||||
dbg().ba(expr=range.min, size=range.length(), access=DbgEng.DEBUG_BREAK_READ)
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_read')
|
||||
def break_read_expression(expression: str):
|
||||
"""Set a read watchpoint."""
|
||||
dbg().ba(expr=expression, access=DbgEng.DEBUG_BREAK_READ)
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_write')
|
||||
def break_write_range(process: sch.Schema('Process'), range: AddressRange):
|
||||
"""Set a watchpoint."""
|
||||
find_proc_by_obj(process)
|
||||
dbg().ba(expr=range.min, size=range.length(), access=DbgEng.DEBUG_BREAK_WRITE)
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_write')
|
||||
def break_write_expression(expression: str):
|
||||
"""Set a watchpoint."""
|
||||
dbg().ba(expr=expression, access=DbgEng.DEBUG_BREAK_WRITE)
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_access')
|
||||
def break_access_range(process: sch.Schema('Process'), range: AddressRange):
|
||||
"""Set an access watchpoint."""
|
||||
find_proc_by_obj(process)
|
||||
dbg().ba(expr=range.min, size=range.length(), access=DbgEng.DEBUG_BREAK_READ|DbgEng.DEBUG_BREAK_WRITE)
|
||||
|
||||
|
||||
@REGISTRY.method(action='break_access')
|
||||
def break_access_expression(expression: str):
|
||||
"""Set an access watchpoint."""
|
||||
dbg().ba(expr=expression, access=DbgEng.DEBUG_BREAK_READ|DbgEng.DEBUG_BREAK_WRITE)
|
||||
|
||||
|
||||
@REGISTRY.method(action='toggle')
|
||||
def toggle_breakpoint(breakpoint: sch.Schema('BreakpointSpec'), enabled: bool):
|
||||
"""Toggle a breakpoint."""
|
||||
bpt = find_bpt_by_obj(breakpoint)
|
||||
if enabled:
|
||||
dbg().be(bpt.GetId())
|
||||
else:
|
||||
dbg().bd(bpt.GetId())
|
||||
|
||||
|
||||
@REGISTRY.method(action='delete')
|
||||
def delete_breakpoint(breakpoint: sch.Schema('BreakpointSpec')):
|
||||
"""Delete a breakpoint."""
|
||||
bpt = find_bpt_by_obj(breakpoint)
|
||||
dbg().cmd("bc {}".format(bpt.GetId()))
|
||||
|
||||
|
||||
@REGISTRY.method
|
||||
def read_mem(process: sch.Schema('Process'), range: AddressRange):
|
||||
"""Read memory."""
|
||||
nproc = find_proc_by_obj(process)
|
||||
offset_start = process.trace.memory_mapper.map_back(
|
||||
nproc, Address(range.space, range.min))
|
||||
with commands.open_tracked_tx('Read Memory'):
|
||||
dbg().read(range.min, range.length())
|
||||
|
||||
|
||||
@REGISTRY.method
|
||||
def write_mem(process: sch.Schema('Process'), address: Address, data: bytes):
|
||||
"""Write memory."""
|
||||
nproc = find_proc_by_obj(process)
|
||||
offset = process.trace.memory_mapper.map_back(nproc, address)
|
||||
dbg().write(offset, data)
|
||||
|
||||
|
||||
@REGISTRY.method
|
||||
def write_reg(frame: sch.Schema('Frame'), name: str, value: bytes):
|
||||
"""Write a register."""
|
||||
util.select_frame()
|
||||
nproc = pydbg.selected_process()
|
||||
dbg().reg._set_register(name, value)
|
||||
|
||||
|
||||
def dbg():
|
||||
return util.get_debugger()
|
|
@ -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
|
Loading…
Add table
Add a link
Reference in a new issue