GP-4686: more edits

GP-4686: formatting
GP-4686: post-review fixes
GP-4686: post-review fixes
GP-4686: remote options
GP-4686: remote options
GP-4686: remote options
GP-4686: post-review / readmem logic
GP-4686: extended launch
GP-4686: better desc for kernel
GP-4686: aisle 9
GP-4686: basically working
GP-4686: better display
GP-4686: unnecessary?
GP-4686: better attr display logic
GP-4686: temp
GP-4686: addresses for synthetics
GP-4686: cleanup, minor errors, start on CreateProcess2
GP-4686: adding names & addresses
GP-4686: print hell, but fixes TARGET_OBJECT
GP-4686: first pass kernel stuff
This commit is contained in:
d-millar 2024-07-03 12:24:35 -04:00
parent 715a8ba335
commit 1fab470a5b
18 changed files with 845 additions and 106 deletions

View file

@ -2,8 +2,11 @@
##MODULE IP: Apache License 2.0 ##MODULE IP: Apache License 2.0
##MODULE IP: MIT ##MODULE IP: MIT
Module.manifest||GHIDRA||||END| Module.manifest||GHIDRA||||END|
data/debugger-launchers/kernel-dbgeng.bat||GHIDRA||||END|
data/debugger-launchers/local-dbgeng-ext.bat||GHIDRA||||END|
data/debugger-launchers/local-dbgeng.bat||GHIDRA||||END| data/debugger-launchers/local-dbgeng.bat||GHIDRA||||END|
data/debugger-launchers/local-ttd.bat||GHIDRA||||END| data/debugger-launchers/local-ttd.bat||GHIDRA||||END|
data/debugger-launchers/remote-dbgeng.bat||GHIDRA||||END|
src/main/py/LICENSE||GHIDRA||||END| src/main/py/LICENSE||GHIDRA||||END|
src/main/py/MANIFEST.in||GHIDRA||||END| src/main/py/MANIFEST.in||GHIDRA||||END|
src/main/py/README.md||GHIDRA||||END| src/main/py/README.md||GHIDRA||||END|

View file

@ -0,0 +1,20 @@
::@title dbgeng-kernel
::@desc <html><body width="300px">
::@desc <h3>Kernel debugging using <tt>dbgeng</tt> (in a Python interpreter)</h3>
::@desc <p>
::@desc This will connect the kernel debugger to a remote machine using <tt>dbgeng.dll</tt>.
::@desc For setup instructions, press <b>F1</b>.
::@desc </p>
::@desc </body></html>
::@menu-group local
::@icon icon.debugger
::@help TraceRmiLauncherServicePlugin#dbgeng_kernel
::@env OPT_PYTHON_EXE:file="python" "Python command" "The path to the Python 3 interpreter. Omit the full path to resolve using the system PATH."
:: Use env instead of args, because "all args except first" is terrible to implement in batch
::@env OPT_TARGET_ARGS:str="" "Arguments" "Connection-string arguments (a la .server)"
::@env OPT_USE_DBGMODEL:bool=true "Use dbgmodel" "Load and use dbgmodel.dll if it is available."
::@env WINDBG_DIR:dir="" "Path to dbgeng.dll directory" "Path containing dbgeng and associated DLLS (if not Windows Kits)."
@echo off
"%OPT_PYTHON_EXE%" -i ..\support\kernel-dbgeng.py

View file

@ -0,0 +1,26 @@
::@title dbgeng-ext
::@desc <html><body width="300px">
::@desc <h3>Launch with <tt>dbgeng</tt> (in a Python interpreter)</h3>
::@desc <p>
::@desc This will launch the target on the local machine using <tt>dbgeng.dll</tt>.
::@desc For setup instructions, press <b>F1</b>.
::@desc </p>
::@desc </body></html>
::@menu-group local
::@icon icon.debugger
::@help TraceRmiLauncherServicePlugin#dbgeng_ext
::@env OPT_PYTHON_EXE:file="python" "Python command" "The path to the Python 3 interpreter. Omit the full path to resolve using the system PATH."
:: Use env instead of args, because "all args except first" is terrible to implement in batch
::@env OPT_TARGET_IMG:file="" "Image" "The target binary executable image"
::@env OPT_TARGET_ARGS:str="" "Arguments" "Command-line arguments to pass to the target"
::@env OPT_USE_DBGMODEL:bool=true "Use dbgmodel" "Load and use dbgmodel.dll if it is available."
::@env WINDBG_DIR:dir="" "Path to dbgeng.dll directory" "Path containing dbgeng and associated DLLS (if not Windows Kits)."
::@env OPT_TARGET_DIR:str="" "Dir" "Initial directory"
::@env OPT_TARGET_ENV:str="" "Env" "Environment variables (sep=/0)"
::@env OPT_CREATE_FLAGS:str="1" "Create flags" "Creation flags"
::@env OPT_CREATE_ENGFLAGS:str="0" "Create flags (Engine)" "Engine-specific creation flags"
::@env OPT_VERIFIER_FLAGS:str="0" "Verifier flags" "Verifier flags"
@echo off
"%OPT_PYTHON_EXE%" -i ..\support\local-dbgeng-ext.py

View file

@ -0,0 +1,22 @@
::@title dbgeng-remote
::@desc <html><body width="300px">
::@desc <h3>Launch with <tt>dbgeng</tt> remotely (in a Python interpreter)</h3>
::@desc <p>
::@desc This will launch the target on a remote machine using <tt>dbgeng.dll</tt>.
::@desc For setup instructions, press <b>F1</b>.
::@desc </p>
::@desc </body></html>
::@menu-group local
::@icon icon.debugger
::@help TraceRmiLauncherServicePlugin#dbgeng_remote
::@env OPT_PYTHON_EXE:file="python" "Python command" "The path to the Python 3 interpreter. Omit the full path to resolve using the system PATH."
:: Use env instead of args, because "all args except first" is terrible to implement in batch
::@env OPT_TARGET_IMG:file="" "Image" "The target binary executable image"
::@env OPT_TARGET_ARGS:str="" "Arguments" "Command-line arguments to pass to the target"
::@env OPT_CONNECT_STRING:str="" "Connection" "Connection-string arguments (a la dbgsrv args)"
::@env OPT_USE_DBGMODEL:bool=true "Use dbgmodel" "Load and use dbgmodel.dll if it is available."
::@env WINDBG_DIR:dir="" "Path to dbgeng.dll directory" "Path containing dbgeng and associated DLLS (if not Windows Kits)."
@echo off
"%OPT_PYTHON_EXE%" -i ..\support\remote-dbgeng.py

View file

@ -0,0 +1,67 @@
## ###
# IP: GHIDRA
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
import os
import sys
home = os.getenv('GHIDRA_HOME')
if os.path.isdir(f'{home}\\ghidra\\.git'):
sys.path.append(
f'{home}\\ghidra\\Ghidra\\Debug\\Debugger-agent-dbgeng\\build\\pypkg\\src')
sys.path.append(
f'{home}\\ghidra\\Ghidra\\Debug\\Debugger-rmi-trace\\build\\pypkg\\src')
elif os.path.isdir(f'{home}\\.git'):
sys.path.append(
f'{home}\\Ghidra\\Debug\\Debugger-agent-dbgeng\\build\\pypkg\\src')
sys.path.append(
f'{home}\\Ghidra\\Debug\\Debugger-rmi-trace\\build\\pypkg\\src')
else:
sys.path.append(
f'{home}\\Ghidra\\Debug\\Debugger-agent-dbgeng\\pypkg\\src')
sys.path.append(f'{home}\\Ghidra\\Debug\\Debugger-rmi-trace\\pypkg\\src')
def main():
# Delay these imports until sys.path is patched
from ghidradbg import commands as cmd
from pybag.dbgeng import core as DbgEng
from ghidradbg.hooks import on_state_changed
from ghidradbg.util import dbg
# So that the user can re-enter by typing repl()
global repl
repl = cmd.repl
cmd.ghidra_trace_connect(os.getenv('GHIDRA_TRACE_RMI_ADDR'))
args = os.getenv('OPT_TARGET_ARGS')
cmd.ghidra_trace_attach_kernel(args, start_trace=False)
# TODO: HACK
try:
dbg.wait()
except KeyboardInterrupt as ki:
dbg.interrupt()
cmd.ghidra_trace_start(os.getenv('OPT_TARGET_IMG'))
cmd.ghidra_trace_sync_enable()
on_state_changed(DbgEng.DEBUG_CES_EXECUTION_STATUS, DbgEng.DEBUG_STATUS_BREAK)
cmd.repl()
if __name__ == '__main__':
main()

View file

@ -0,0 +1,77 @@
## ###
# IP: GHIDRA
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
import os
import sys
home = os.getenv('GHIDRA_HOME')
if os.path.isdir(f'{home}\\ghidra\\.git'):
sys.path.append(
f'{home}\\ghidra\\Ghidra\\Debug\\Debugger-agent-dbgeng\\build\\pypkg\\src')
sys.path.append(
f'{home}\\ghidra\\Ghidra\\Debug\\Debugger-rmi-trace\\build\\pypkg\\src')
elif os.path.isdir(f'{home}\\.git'):
sys.path.append(
f'{home}\\Ghidra\\Debug\\Debugger-agent-dbgeng\\build\\pypkg\\src')
sys.path.append(
f'{home}\\Ghidra\\Debug\\Debugger-rmi-trace\\build\\pypkg\\src')
else:
sys.path.append(
f'{home}\\Ghidra\\Debug\\Debugger-agent-dbgeng\\pypkg\\src')
sys.path.append(f'{home}\\Ghidra\\Debug\\Debugger-rmi-trace\\pypkg\\src')
def main():
# Delay these imports until sys.path is patched
from ghidradbg import commands as cmd
from pybag.dbgeng import core as DbgEng
from ghidradbg.hooks import on_state_changed
from ghidradbg.util import dbg
# So that the user can re-enter by typing repl()
global repl
repl = cmd.repl
cmd.ghidra_trace_connect(os.getenv('GHIDRA_TRACE_RMI_ADDR'))
args = os.getenv('OPT_TARGET_ARGS')
if args:
args = ' ' + args
cmd.ghidra_trace_create_ext(
os.getenv('OPT_TARGET_IMG') + args,
os.getenv('OPT_TARGET_DIR'),
os.getenv('OPT_TARGET_ENV'),
os.getenv('OPT_CREATE_FLAGS'),
os.getenv('OPT_CREATE_ENGFLAGS'),
os.getenv('OPT_VERIFIER_FLAGS'),
start_trace=False)
# TODO: HACK
try:
dbg.wait()
except KeyboardInterrupt as ki:
dbg.interrupt()
cmd.ghidra_trace_start(os.getenv('OPT_TARGET_IMG'))
cmd.ghidra_trace_sync_enable()
on_state_changed(DbgEng.DEBUG_CES_EXECUTION_STATUS, DbgEng.DEBUG_STATUS_BREAK)
cmd.repl()
if __name__ == '__main__':
main()

View file

@ -0,0 +1,73 @@
## ###
# IP: GHIDRA
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
import os
import sys
home = os.getenv('GHIDRA_HOME')
if os.path.isdir(f'{home}\\ghidra\\.git'):
sys.path.append(
f'{home}\\ghidra\\Ghidra\\Debug\\Debugger-agent-dbgeng\\build\\pypkg\\src')
sys.path.append(
f'{home}\\ghidra\\Ghidra\\Debug\\Debugger-rmi-trace\\build\\pypkg\\src')
elif os.path.isdir(f'{home}\\.git'):
sys.path.append(
f'{home}\\Ghidra\\Debug\\Debugger-agent-dbgeng\\build\\pypkg\\src')
sys.path.append(
f'{home}\\Ghidra\\Debug\\Debugger-rmi-trace\\build\\pypkg\\src')
else:
sys.path.append(
f'{home}\\Ghidra\\Debug\\Debugger-agent-dbgeng\\pypkg\\src')
sys.path.append(f'{home}\\Ghidra\\Debug\\Debugger-rmi-trace\\pypkg\\src')
def main():
# Delay these imports until sys.path is patched
from ghidradbg import commands as cmd
from pybag.dbgeng import core as DbgEng
from ghidradbg.hooks import on_state_changed
from ghidradbg.util import dbg
# So that the user can re-enter by typing repl()
global repl
repl = cmd.repl
cmd.ghidra_trace_connect(os.getenv('GHIDRA_TRACE_RMI_ADDR'))
args = os.getenv('OPT_TARGET_ARGS')
if args:
args = ' ' + args
cmd.ghidra_trace_connect_server(os.getenv('OPT_CONNECT_STRING'))
img = os.getenv('OPT_TARGET_IMG')
if img is not None and img != "":
cmd.ghidra_trace_create(img + args, start_trace=False)
# TODO: HACK
try:
dbg.wait()
except KeyboardInterrupt as ki:
dbg.interrupt()
cmd.ghidra_trace_start(os.getenv('OPT_TARGET_IMG'))
cmd.ghidra_trace_sync_enable()
on_state_changed(DbgEng.DEBUG_CES_EXECUTION_STATUS, DbgEng.DEBUG_STATUS_BREAK)
cmd.repl()
if __name__ == '__main__':
main()

View file

@ -204,14 +204,18 @@ class DefaultMemoryMapper(object):
self.defaultSpace = defaultSpace self.defaultSpace = defaultSpace
def map(self, proc: int, offset: int): def map(self, proc: int, offset: int):
if proc == 0:
space = self.defaultSpace space = self.defaultSpace
else:
space = f'{self.defaultSpace}{proc}'
return self.defaultSpace, Address(space, offset) return self.defaultSpace, Address(space, offset)
def map_back(self, proc: int, address: Address) -> int: def map_back(self, proc: int, address: Address) -> int:
if address.space == self.defaultSpace: if address.space == self.defaultSpace and proc == 0:
return address.offset return address.offset
raise ValueError( if address.space == f'{self.defaultSpace}{proc}':
f"Address {address} is not in process {proc.GetProcessID()}") return address.offset
raise ValueError(f"Address {address} is not in process {proc}")
DEFAULT_MEMORY_MAPPER = DefaultMemoryMapper('ram') DEFAULT_MEMORY_MAPPER = DefaultMemoryMapper('ram')

View file

@ -22,6 +22,7 @@ import socket
import sys import sys
import time import time
from comtypes import c_ulong
from ghidratrace import sch from ghidratrace import sch
from ghidratrace.client import Client, Address, AddressRange, TraceObject from ghidratrace.client import Client, Address, AddressRange, TraceObject
from pybag import pydbg, userdbg, kerneldbg from pybag import pydbg, userdbg, kerneldbg
@ -31,7 +32,7 @@ from pybag.dbgeng.win32.kernel32 import STILL_ACTIVE
from . import util, arch, methods, hooks from . import util, arch, methods, hooks
from .dbgmodel.imodelobject import ModelObjectKind from .dbgmodel.imodelobject import ModelObjectKind
from .dbgeng.idebugclient5 import *
PAGE_SIZE = 4096 PAGE_SIZE = 4096
@ -68,6 +69,7 @@ GENERIC_KEY_PATTERN = '[{key}]'
class ErrorWithCode(Exception): class ErrorWithCode(Exception):
def __init__(self, code): def __init__(self, code):
self.code = code self.code = code
@ -216,6 +218,8 @@ def start_trace(name):
with STATE.trace.open_tx("Create Root Object"): with STATE.trace.open_tx("Create Root Object"):
root = STATE.trace.create_root_object(schema_xml, 'DbgRoot') root = STATE.trace.create_root_object(schema_xml, 'DbgRoot')
root.set_value('_display', util.DBG_VERSION.full + ' via pybag' + variant) root.set_value('_display', util.DBG_VERSION.full + ' via pybag' + variant)
if util.dbg.use_generics:
put_generic(root)
util.set_convenience_variable('_ghidra_tracing', "true") util.set_convenience_variable('_ghidra_tracing', "true")
@ -261,6 +265,67 @@ def ghidra_trace_create(command=None, initial_break=True, timeout=DbgEng.WAIT_IN
ghidra_trace_start(command) ghidra_trace_start(command)
@util.dbg.eng_thread
def ghidra_trace_create_ext(command=None, initialDirectory='.', envVariables="\0\0", create_flags=1, create_flags_eng=0, verifier_flags=0, initial_break=True, timeout=DbgEng.WAIT_INFINITE, start_trace=True):
"""
Create a session.
"""
dbg = util.dbg._base
if command != None:
if create_flags == "":
create_flags = 1
if create_flags_eng == "":
create_flags_eng = 0
if verifier_flags == "":
verifier_flags = 0
options = DbgEng._DEBUG_CREATE_PROCESS_OPTIONS()
options.CreateFlags = c_ulong(int(create_flags))
options.EngCreateFlags = c_ulong(int(create_flags_eng))
options.VerifierFlags = c_ulong(int(verifier_flags))
options.Reserved = c_ulong(int(0))
if initialDirectory == "":
initialDirectory = None
if envVariables == "":
envVariables = None
if envVariables is not None and envVariables.endswith("/0/0") is False:
envVariables += "/0/0"
dbg._client.CreateProcess2(command, options, initialDirectory, envVariables)
if initial_break:
dbg._control.AddEngineOptions(DbgEng.DEBUG_ENGINITIAL_BREAK)
if start_trace:
ghidra_trace_start(command)
@util.dbg.eng_thread
def ghidra_trace_attach_kernel(command=None, initial_break=True, timeout=DbgEng.WAIT_INFINITE, start_trace=True):
"""
Create a session.
"""
dbg = util.dbg._base
util.set_kernel(True)
if initial_break:
dbg._control.AddEngineOptions(DbgEng.DEBUG_ENGINITIAL_BREAK)
if command != None:
dbg._client.AttachKernel(command)
if start_trace:
ghidra_trace_start(command)
@util.dbg.eng_thread
def ghidra_trace_connect_server(options=None):
"""
Connect to a process server session.
"""
dbg = util.dbg._base
if options != None:
if isinstance(options, str):
enc_options = options.encode()
dbg._client.ConnectProcessServer(enc_options)
@util.dbg.eng_thread @util.dbg.eng_thread
def ghidra_trace_kill(): def ghidra_trace_kill():
""" """
@ -453,7 +518,7 @@ def putmem_state(address, length, state, pages=True):
nproc = util.selected_process() nproc = util.selected_process()
base, addr = STATE.trace.memory_mapper.map(nproc, start) base, addr = STATE.trace.memory_mapper.map(nproc, start)
if base != addr.space and state != 'unknown': if base != addr.space and state != 'unknown':
trace.create_overlay_space(base, addr.space) STATE.trace.create_overlay_space(base, addr.space)
STATE.trace.set_memory_state(addr.extend(end - start), state) STATE.trace.set_memory_state(addr.extend(end - start), state)
@ -718,6 +783,7 @@ def ghidra_trace_get_obj(path):
class TableColumn(object): class TableColumn(object):
def __init__(self, head): def __init__(self, head):
self.head = head self.head = head
self.contents = [head] self.contents = [head]
@ -735,6 +801,7 @@ class TableColumn(object):
class Tabular(object): class Tabular(object):
def __init__(self, heads): def __init__(self, heads):
self.columns = [TableColumn(h) for h in heads] self.columns = [TableColumn(h) for h in heads]
self.columns[-1].is_last = True self.columns[-1].is_last = True
@ -1077,6 +1144,8 @@ def put_regions():
regions = util.dbg._base.memory_list() regions = util.dbg._base.memory_list()
except Exception: except Exception:
regions = [] regions = []
if len(regions) == 0:
regions = util.full_mem()
mapper = STATE.trace.memory_mapper mapper = STATE.trace.memory_mapper
keys = [] keys = []
@ -1096,6 +1165,8 @@ def put_regions():
regobj.set_value('AllocationBase', hex(r.AllocationBase)) regobj.set_value('AllocationBase', hex(r.AllocationBase))
regobj.set_value('Protect', hex(r.Protect)) regobj.set_value('Protect', hex(r.Protect))
regobj.set_value('Type', hex(r.Type)) regobj.set_value('Type', hex(r.Type))
if hasattr(r, 'Name') and r.Name is not None:
regobj.set_value('_display', r.Name)
regobj.insert() regobj.insert()
STATE.trace.proxy_object_path( STATE.trace.proxy_object_path(
MEMORY_PATTERN.format(procnum=nproc)).retain_values(keys) MEMORY_PATTERN.format(procnum=nproc)).retain_values(keys)
@ -1296,37 +1367,44 @@ def ghidra_trace_put_frames():
put_frames() put_frames()
def update_by_container(np, index, obj): def update_by_container(np, keyval, obj):
index = keyval[0]
key = ''
if np.endswith("Processes") or np.endswith("Threads"): if np.endswith("Processes") or np.endswith("Threads"):
istate = compute_proc_state(index) istate = compute_proc_state(index)
obj.set_value('State', istate) obj.set_value('State', istate)
if np.endswith("Sessions"):
key = '[{:x}]'.format(index)
if np.endswith("Processes"): if np.endswith("Processes"):
create_generic(obj.path) create_generic(obj.path)
id = util.get_proc_id(index)
obj.set_value('PID', index) obj.set_value('PID', index)
obj.set_value('_display', '{:x} {:x}'.format(id, index)) create_generic(obj.path + ".Memory")
if util.is_kernel():
key = '[{:x}]'.format(index)
else:
id = util.get_proc_id(index)
key = '{:x} [{:x}]'.format(id, index)
if np.endswith("Breakpoints"): if np.endswith("Breakpoints"):
create_generic(obj.path) create_generic(obj.path)
#id = util.get_thread_id(index)
#obj.set_value('TID', index)
#obj.set_value('_display','{:x} {:x}'.format(id, index))
if np.endswith("Threads"): if np.endswith("Threads"):
create_generic(obj.path) create_generic(obj.path)
id = util.get_thread_id(index)
obj.set_value('TID', index) obj.set_value('TID', index)
obj.set_value('_display', '{:x} {:x}'.format(id, index)) if util.is_kernel():
key = '[{:x}]'.format(index)
else:
id = util.get_thread_id(index)
key = '{:x} [{:x}]'.format(id, index)
if np.endswith("Frames"): if np.endswith("Frames"):
mo = util.get_object(obj.path) mo = util.get_object(obj.path)
map = util.get_attributes(mo) map = util.get_attributes(mo)
attr = map["Attributes"] attr = map["Attributes"]
if attr is None: if attr is None:
return return
create_generic(obj.path+".Attributes")
map = util.get_attributes(attr) map = util.get_attributes(attr)
pc = util.get_value(map["InstructionOffset"]) pc = util.get_value(map["InstructionOffset"])
(pc_base, pc_addr) = map_address(pc) (pc_base, pc_addr) = map_address(pc)
obj.set_value('Instruction Offset', pc_addr) obj.set_value('Instruction Offset', pc_addr)
obj.set_value('_display', '#{:x} 0x{:x}'.format(index, pc)) key = '#{:x} 0x{:x}'.format(index, pc)
if np.endswith("Modules"): if np.endswith("Modules"):
create_generic(obj.path) create_generic(obj.path)
mo = util.get_object(obj.path) mo = util.get_object(obj.path)
@ -1337,7 +1415,12 @@ def update_by_container(np, index, obj):
obj.set_value('Name', '{}'.format(name)) obj.set_value('Name', '{}'.format(name))
(base_base, base_addr) = map_address(base) (base_base, base_addr) = map_address(base)
obj.set_value('Range', base_addr.extend(size)) obj.set_value('Range', base_addr.extend(size))
obj.set_value('_display', '{:x} {:x} {}'.format(index, base, name)) key = '{:x} {:x} {}'.format(index, base, name)
disp = util.to_display_string(keyval[1])
if disp is not None:
key += " " + disp
if key is not None and key != "":
obj.set_value('_display', key)
def create_generic(path): def create_generic(path):
@ -1354,52 +1437,75 @@ def put_generic(node):
return return
nthrd = util.selected_thread() nthrd = util.selected_thread()
mapper = STATE.trace.memory_mapper
mo = util.get_object(node.path) mo = util.get_object(node.path)
kind = util.get_kind(mo) mapper = STATE.trace.register_mapper
type = util.get_type(mo)
vstr = util.get_value(mo)
# print(f"MO={mo}")
attributes = util.get_attributes(mo) attributes = util.get_attributes(mo)
# print(f"ATTR={attributes}") # print(f"ATTR={attributes}")
mapper = STATE.trace.register_mapper
values = [] values = []
if attributes is not None:
for key, value in attributes.items(): for key, value in attributes.items():
if value is None:
continue
kind = util.get_kind(value) kind = util.get_kind(value)
vstr = util.get_value(value) if kind == ModelObjectKind.METHOD.value:
#print(f"key={key} kind={kind} value={vstr} type={type}") continue
if kind == ModelObjectKind.PROPERTY_ACCESSOR.value or \ # print(f"key={key} kind={kind}")
kind == ModelObjectKind.SYNTHETIC.value or \ if kind != ModelObjectKind.INTRINSIC.value:
kind == ModelObjectKind.METHOD.value:
if vstr is not None:
key += " : " + vstr
apath = node.path + '.' + key apath = node.path + '.' + key
aobj = STATE.trace.create_object(apath) aobj = STATE.trace.create_object(apath)
set_display(key, value, aobj)
aobj.insert() aobj.insert()
else: else:
val = util.get_value(value)
try: try:
if node.path.endswith('.User'): if node.path.endswith('.User'):
values.append(mapper.map_value(nproc, key, vstr)) # print(f"PUT_REG: {key} {val}")
node.set_value(key, hex(vstr)) values.append(mapper.map_value(nproc, key, val))
node.set_value(key, hex(val))
elif isinstance(val, int):
(v_base, v_addr) = map_address(val)
node.set_value(key, v_addr, schema="ADDRESS")
else:
node.set_value(key, val)
except Exception as e: except Exception as e:
pass # Error is printed by another mechanism print(f"Attribute exception for {key} {type(val)}: {e}")
elements = util.get_elements(mo) elements = util.get_elements(mo)
# print(f"ELEM={elements}") # print(f"ELEM={elements}")
keys = [] keys = []
if elements is not None:
for el in elements: for el in elements:
index = el[0] index = el[0]
key = GENERIC_KEY_PATTERN.format(key=index) key = GENERIC_KEY_PATTERN.format(key=index)
lpath = node.path + key lpath = node.path + key
lobj = STATE.trace.create_object(lpath) lobj = STATE.trace.create_object(lpath)
update_by_container(node.path, index, lobj) update_by_container(node.path, el, lobj)
lobj.insert() lobj.insert()
keys.append(key) keys.append(key)
node.retain_values(keys) node.retain_values(keys)
return (values, keys) return (values, keys)
def set_display(key, value, obj):
kind = util.get_kind(value)
vstr = util.get_value(value)
# istr = util.get_intrinsic_value(value)
if kind == ModelObjectKind.TARGET_OBJECT.value:
hloc = util.get_location(value)
ti = util.get_type_info(value)
if ti is not None:
name = util.get_name(ti)
if name is not None:
key += " : " + name
obj.set_value('_display', key)
if hloc is not None:
key += " @ " + str(hloc)
obj.set_value('_display', key)
(hloc_base, hloc_addr) = map_address(int(hloc,0))
obj.set_value('_address', hloc_addr, schema=Address)
if vstr is not None:
key += " : " + str(vstr)
obj.set_value('_display', key)
def map_address(address): def map_address(address):
nproc = util.selected_process() nproc = util.selected_process()
mapper = STATE.trace.memory_mapper mapper = STATE.trace.memory_mapper

View file

@ -155,6 +155,13 @@ library DbgMod
DWORD NotSupported; DWORD NotSupported;
}; };
typedef struct _DEBUG_CREATE_PROCESS_OPTIONS {
ULONG CreateFlags;
ULONG EngCreateFlags;
ULONG VerfierFlags;
ULONG Reserved;
} DEBUG_CREATE_PROCESS_OPTIONS, *PDEBUG_CREATE_PROCESS_OPTIONS;
/* /*
struct _MEMORY_BASIC_INFORMATION64 { struct _MEMORY_BASIC_INFORMATION64 {
ULONGLONG BaseAddress; ULONGLONG BaseAddress;

View file

@ -40,8 +40,10 @@ class ModelIterator(object):
except COMError as ce: except COMError as ce:
return None return None
index = mo.ModelObject(indexer) index = mo.ModelObject(indexer)
id = index.GetIntrinsicValue().value ival = index.GetIntrinsicValue()
return (id, mo.ModelObject(object)) if ival is None:
return (0, mo.ModelObject(object))
return (ival.value, mo.ModelObject(object))
def Reset(self): def Reset(self):
hr = self._keys.Reset() hr = self._keys.Reset()

View file

@ -22,10 +22,13 @@ from comtypes.gen import DbgMod
from comtypes.hresult import S_OK, S_FALSE from comtypes.hresult import S_OK, S_FALSE
from pybag.dbgeng import exception from pybag.dbgeng import exception
from comtypes import BSTR
from comtypes.gen.DbgMod import * from comtypes.gen.DbgMod import *
from .iiterableconcept import IterableConcept from .iiterableconcept import IterableConcept
from .istringdisplayableconcept import StringDisplayableConcept
from .ikeyenumerator import KeyEnumerator from .ikeyenumerator import KeyEnumerator
from .irawenumerator import RawEnumerator
class ModelObjectKind(Enum): class ModelObjectKind(Enum):
@ -45,6 +48,7 @@ class ModelObject(object):
def __init__(self, obj): def __init__(self, obj):
self._obj = obj self._obj = obj
self.concept = None self.concept = None
self.dconcept = None
exception.wrap_comclass(self._obj) exception.wrap_comclass(self._obj)
def Release(self): def Release(self):
@ -96,12 +100,16 @@ class ModelObject(object):
return RawEnumerator(keys, kind) return RawEnumerator(keys, kind)
def GetConcept(self, ref): def GetConcept(self, ref):
try:
ifc = POINTER(IUnknown)() ifc = POINTER(IUnknown)()
metadata = POINTER(DbgMod.IKeyStore)() metadata = POINTER(DbgMod.IKeyStore)()
hr = self._obj.GetConcept(ref._iid_, byref(ifc), byref(metadata)) hr = self._obj.GetConcept(ref._iid_, byref(ifc), byref(metadata))
if hr != S_OK: if hr != S_OK:
return None return None
return cast(ifc, POINTER(ref)) return cast(ifc, POINTER(ref))
except Exception as e:
print(f"GetConcept exception: {e}")
return None
def GetContext(self, context): def GetContext(self, context):
raise exception.E_NOTIMPL_Error raise exception.E_NOTIMPL_Error
@ -111,10 +119,14 @@ class ModelObject(object):
def GetIntrinsicValue(self): def GetIntrinsicValue(self):
var = VARIANT() var = VARIANT()
try:
hr = self._obj.GetIntrinsicValue(var) hr = self._obj.GetIntrinsicValue(var)
if hr != S_OK: if hr != S_OK:
return None return None
return var return var
except Exception as e:
print(f"GetIntrinsicValue exception: {e}")
return None
def GetIntrinsicValueAs(self, vt): def GetIntrinsicValueAs(self, vt):
raise exception.E_NOTIMPL_Error raise exception.E_NOTIMPL_Error
@ -140,8 +152,31 @@ class ModelObject(object):
exception.check_err(hr) exception.check_err(hr)
return kind return kind
def GetLocation(self, location): # DOESN"T WORK YET
raise exception.E_NOTIMPL_Error # def GetTypeKind(self):
# typeKind = None
# modelKind = self.GetKind()
# if modelKind.value == ModelObjectKind.TARGET_OBJECT.value:
# targetInfo = self.GetTargetInfo()
# if targetInfo is not None:
# typeKind = DbgMod.tagTYPEKIND()
# hr = targetInfo._obj.GetTypeKind(byref(typeKind))
# if hr != S_OK:
# return None
# if modelKind.value == ModelObjectKind.INTRINSIC.value:
# typeInfo = self.GetTypeInfo()
# if typeInfo is not None:
# typeKind = DbgMod.tagTYPEKIND()
# hr = typeInfo._obj.GetTypeKind(byref(typeKind))
# if hr != S_OK:
# return None
# return typeKind
def GetLocation(self):
loc = DbgMod._Location()
hr = self._obj.GetLocation(loc)
exception.check_err(hr)
return loc
def GetNumberOfParentModels(self, numModels): def GetNumberOfParentModels(self, numModels):
raise exception.E_NOTIMPL_Error raise exception.E_NOTIMPL_Error
@ -152,18 +187,55 @@ class ModelObject(object):
def GetRawReference(self, kind, name, searchFlags, object): def GetRawReference(self, kind, name, searchFlags, object):
raise exception.E_NOTIMPL_Error raise exception.E_NOTIMPL_Error
def GetRawValue(self, kind, name, searchFlags, object): def GetRawValue(self, kind, name, searchFlags):
raise exception.E_NOTIMPL_Error kbuf = cast(c_wchar_p(name), POINTER(c_ushort))
value = POINTER(DbgMod.IModelObject)()
hr = self._obj.GetRawValue(kind, kbuf, searchFlags, byref(value))
if hr != S_OK:
return None
return ModelObject(value)
def GetTargetInfo(self): def GetTargetInfo(self):
location = POINTER(DbgMod._Location)() location = DbgMod._Location()
type = POINTER(DbgMod.IDebugHostType)() type = POINTER(DbgMod.IDebugHostType)()
hr = self._obj.GetTargetInfo(location, byref(type)) hr = self._obj.GetTargetInfo(location, byref(type))
exception.check_err(hr) exception.check_err(hr)
return type return ModelObject(type)
def GetTypeInfo(self, type): def GetTypeInfo(self):
raise exception.E_NOTIMPL_Error type = POINTER(DbgMod.IDebugHostType)()
hr = self._obj.GetTypeInfo(byref(type))
exception.check_err(hr)
return ModelObject(type)
def GetName(self):
name = BSTR()
hr = self._obj.GetName(name)
exception.check_err(hr)
return name
def ToDisplayString(self):
if self.dconcept is None:
dconcept = self.GetConcept(DbgMod.IStringDisplayableConcept)
if dconcept is None:
return None
self.dconcept = StringDisplayableConcept(dconcept)
return self.dconcept.ToDisplayString(self)
# This does NOT work - returns a null pointer for value. Why?
# One possibility: casting is not a valid way to obtain an IModelMethod
#
# def ToDisplayString0(self):
# map = self.GetAttributes()
# method = map["ToDisplayString"]
# mm = cast(method._obj, POINTER(DbgMod.IModelMethod))
# context = self._obj
# args = POINTER(DbgMod.IModelObject)()
# value = POINTER(DbgMod.IModelObject)()
# meta = POINTER(DbgMod.IKeyStore)()
# hr = mm.Call(context, c_ulonglong(0), args, byref(value), byref(meta))
# exception.check_err(hr)
# return ModelObject(value)
def IsEqualTo(self, other, equal): def IsEqualTo(self, other, equal):
raise exception.E_NOTIMPL_Error raise exception.E_NOTIMPL_Error
@ -198,23 +270,28 @@ class ModelObject(object):
return map return map
def GetRawValueMap(self): def GetRawValueMap(self):
# print(f"GetRawValueMap: {self}")
map = {} map = {}
kind = self.GetKind() kind = self.GetKind()
keys = self.EnumerateRawValues(kind, c_long(0)) # TODO: forcing kind to 0 because we can't GetTypeKind
keys = self.EnumerateRawValues(c_long(0), 0)
(k, v) = keys.GetNext() (k, v) = keys.GetNext()
while k is not None: while k is not None:
map[k.value] = v map[k.value] = v
(k, v) = keys.GetNext() (k, v) = keys.GetNext()
# print(f"{k}:{v}")
return map return map
def GetAttributes(self): def GetAttributes(self):
map = {} map = {}
kind = self.GetKind() kind = self.GetKind()
if kind == ModelObjectKind.ERROR: # print(f"GetAttributes: {kind}")
if kind is not None and kind.value == ModelObjectKind.ERROR.value:
print(f"ERROR from GetAttributes")
return map return map
if kind == ModelObjectKind.INTRINSIC or \ if kind.value == ModelObjectKind.INTRINSIC.value or \
kind == ModelObjectKind.TARGET_OBJECT or \ kind.value == ModelObjectKind.TARGET_OBJECT.value or \
kind == ModelObjectKind.TARGET_OBJECT_REFERENCE: kind.value == ModelObjectKind.TARGET_OBJECT_REFERENCE.value:
return self.GetRawValueMap() return self.GetRawValueMap()
return self.GetKeyValueMap() return self.GetKeyValueMap()
@ -245,6 +322,9 @@ class ModelObject(object):
def GetOffspring(self, path): def GetOffspring(self, path):
next = self next = self
for element in path: for element in path:
if next is None:
return None
kind = next.GetKind()
if element.startswith("["): if element.startswith("["):
idx = element[1:len(element)-1] idx = element[1:len(element)-1]
if "x" not in idx: if "x" not in idx:
@ -252,12 +332,17 @@ class ModelObject(object):
else: else:
idx = int(idx, 16) idx = int(idx, 16)
next = next.GetElement(idx) next = next.GetElement(idx)
# THIS IS RELATIVELY HORRIBLE - replace with GetRawValue?
elif kind is not None and kind.value == ModelObjectKind.TARGET_OBJECT.value:
map = next.GetAttributes()
next = map[element]
else: else:
next = next.GetKeyValue(element) next = next.GetKeyValue(element)
if next is None: #if next is None:
print(f"{element} not found") # print(f"{element} not found")
return next return next
def GetValue(self): def GetValue(self):
value = self.GetIntrinsicValue() value = self.GetIntrinsicValue()
if value is None: if value is None:
@ -266,9 +351,3 @@ class ModelObject(object):
return None return None
return value.value return value.value
def GetTypeKind(self):
kind = self.GetKind()
if kind == ModelObjectKind.TARGET_OBJECT or \
kind == ModelObjectKind.INTRINSIC:
return self.GetTargetInfo()
return None

View file

@ -0,0 +1,37 @@
## ###
# IP: GHIDRA
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##
from ctypes import *
from comtypes import BSTR, COMError
from comtypes.gen import DbgMod
from comtypes.hresult import S_OK, S_FALSE
from pybag.dbgeng import exception
class StringDisplayableConcept(object):
def __init__(self, concept):
self._concept = concept
concept.AddRef()
# StringDisplayableConcept
def ToDisplayString(self, context):
try:
val = BSTR()
self._concept.ToDisplayString(context._obj, None, byref(val))
except COMError as ce:
return None
return val.value

View file

@ -64,8 +64,11 @@ class ProcessState(object):
if description is not None: if description is not None:
commands.STATE.trace.snapshot(description) commands.STATE.trace.snapshot(description)
if first: if first:
if util.is_kernel():
commands.create_generic("Sessions")
commands.put_processes() commands.put_processes()
commands.put_environment() commands.put_environment()
commands.put_threads()
if self.threads: if self.threads:
commands.put_threads() commands.put_threads()
self.threads = False self.threads = False
@ -98,6 +101,7 @@ class ProcessState(object):
commands.put_threads(running=True) commands.put_threads(running=True)
def record_exited(self, exit_code, description=None): def record_exited(self, exit_code, description=None):
# print("RECORD_EXITED")
if description is not None: if description is not None:
commands.STATE.trace.snapshot(description) commands.STATE.trace.snapshot(description)
proc = util.selected_process() proc = util.selected_process()

View file

@ -536,6 +536,7 @@ def delete_breakpoint(breakpoint: sch.Schema('BreakpointSpec')):
@REGISTRY.method @REGISTRY.method
@util.dbg.eng_thread
def read_mem(process: sch.Schema('Process'), range: AddressRange): def read_mem(process: sch.Schema('Process'), range: AddressRange):
"""Read memory.""" """Read memory."""
# print("READ_MEM: process={}, range={}".format(process, range)) # print("READ_MEM: process={}, range={}".format(process, range))

View file

@ -8,13 +8,13 @@
<attribute name="Utility" schema="ANY" /> <attribute name="Utility" schema="ANY" />
<attribute name="_display" schema="STRING" hidden="yes" /> <attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" /> <attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="ANY" hidden="yes" /> <attribute schema="ANY"/>
</schema> </schema>
<schema name="SessionContainer" canonical="yes" elementResync="NEVER" attributeResync="NEVER"> <schema name="SessionContainer" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
<interface name="Configurable" /> <interface name="Configurable" />
<element schema="Session" /> <element schema="Session" />
<attribute name="_order" schema="INT" hidden="yes" /> <attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="ANY" hidden="yes" /> <attribute schema="ANY"/>
</schema> </schema>
<schema name="Session" elementResync="NEVER" attributeResync="NEVER"> <schema name="Session" elementResync="NEVER" attributeResync="NEVER">
<interface name="Activatable" /> <interface name="Activatable" />
@ -34,7 +34,7 @@
<attribute name="_focus" schema="Selectable" required="yes" hidden="yes" /> <attribute name="_focus" schema="Selectable" required="yes" hidden="yes" />
<attribute name="_display" schema="STRING" hidden="yes" /> <attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" /> <attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="ANY" hidden="yes" /> <attribute schema="ANY"/>
</schema> </schema>
<schema name="Selectable" elementResync="NEVER" attributeResync="NEVER"> <schema name="Selectable" elementResync="NEVER" attributeResync="NEVER">
<element schema="OBJECT" /> <element schema="OBJECT" />
@ -53,7 +53,7 @@
<interface name="BreakpointSpecContainer" /> <interface name="BreakpointSpecContainer" />
<element schema="BreakpointSpec" /> <element schema="BreakpointSpec" />
<attribute name="_order" schema="INT" hidden="yes" /> <attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="ANY" hidden="yes" /> <attribute schema="ANY" />
</schema> </schema>
<schema name="AvailableContainer" canonical="yes" elementResync="ALWAYS" attributeResync="NEVER"> <schema name="AvailableContainer" canonical="yes" elementResync="ALWAYS" attributeResync="NEVER">
<interface name="Configurable" /> <interface name="Configurable" />
@ -65,7 +65,7 @@
<interface name="Configurable" /> <interface name="Configurable" />
<element schema="Process" /> <element schema="Process" />
<attribute name="_order" schema="INT" hidden="yes" /> <attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="ANY" hidden="yes" /> <attribute schema="ANY" />
</schema> </schema>
<schema name="BreakpointSpec" canonical="yes" elementResync="NEVER" attributeResync="NEVER"> <schema name="BreakpointSpec" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
<interface name="BreakpointSpec" /> <interface name="BreakpointSpec" />
@ -132,7 +132,7 @@
<attribute name="_display" schema="STRING" hidden="yes" /> <attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_short_display" schema="STRING" hidden="yes" /> <attribute name="_short_display" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" /> <attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="ANY" hidden="yes" /> <attribute schema="ANY" />
</schema> </schema>
<schema name="Environment" elementResync="NEVER" attributeResync="NEVER"> <schema name="Environment" elementResync="NEVER" attributeResync="NEVER">
<interface name="Environment" /> <interface name="Environment" />
@ -152,7 +152,7 @@
<interface name="ModuleContainer" /> <interface name="ModuleContainer" />
<element schema="Module" /> <element schema="Module" />
<attribute name="_order" schema="INT" hidden="yes" /> <attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="ANY" hidden="yes" /> <attribute schema="ANY" />
</schema> </schema>
<schema name="Memory" canonical="yes" elementResync="NEVER" attributeResync="NEVER"> <schema name="Memory" canonical="yes" elementResync="NEVER" attributeResync="NEVER">
<interface name="Memory" /> <interface name="Memory" />
@ -178,7 +178,7 @@
<interface name="Configurable" /> <interface name="Configurable" />
<element schema="Thread" /> <element schema="Thread" />
<attribute name="_order" schema="INT" hidden="yes" /> <attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="ANY" hidden="yes" /> <attribute schema="ANY" />
</schema> </schema>
<schema name="Method" elementResync="NEVER" attributeResync="NEVER"> <schema name="Method" elementResync="NEVER" attributeResync="NEVER">
<interface name="Method" /> <interface name="Method" />
@ -207,7 +207,7 @@
<attribute name="_short_display" schema="STRING" hidden="yes" /> <attribute name="_short_display" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" /> <attribute name="_order" schema="INT" hidden="yes" />
<attribute name="Advance" schema="Method" required="yes" fixed="yes" hidden="yes" /> <attribute name="Advance" schema="Method" required="yes" fixed="yes" hidden="yes" />
<attribute schema="ANY" hidden="yes" /> <attribute schema="ANY" />
</schema> </schema>
<schema name="Module" elementResync="NEVER" attributeResync="NEVER"> <schema name="Module" elementResync="NEVER" attributeResync="NEVER">
<interface name="Module" /> <interface name="Module" />
@ -255,7 +255,7 @@
<element schema="StackFrame" /> <element schema="StackFrame" />
<attribute name="_display" schema="STRING" hidden="yes" /> <attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" /> <attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="ANY" hidden="yes" /> <attribute schema="ANY" />
</schema> </schema>
<schema name="SymbolContainer" canonical="yes" elementResync="ONCE" attributeResync="NEVER"> <schema name="SymbolContainer" canonical="yes" elementResync="ONCE" attributeResync="NEVER">
<interface name="SymbolNamespace" /> <interface name="SymbolNamespace" />
@ -284,7 +284,7 @@
<attribute name="Frame Offset" schema="ADDRESS" /> <attribute name="Frame Offset" schema="ADDRESS" />
<attribute name="_display" schema="STRING" hidden="yes" /> <attribute name="_display" schema="STRING" hidden="yes" />
<attribute name="_order" schema="INT" hidden="yes" /> <attribute name="_order" schema="INT" hidden="yes" />
<attribute schema="ANY" hidden="yes" /> <attribute schema="ANY" />
</schema> </schema>
<schema name="Section" elementResync="NEVER" attributeResync="NEVER"> <schema name="Section" elementResync="NEVER" attributeResync="NEVER">
<interface name="Section" /> <interface name="Section" />

View file

@ -37,6 +37,7 @@ from pybag.dbgeng import util as DbgUtil
from pybag.dbgeng.callbacks import DbgEngCallbacks from pybag.dbgeng.callbacks import DbgEngCallbacks
from ghidradbg.dbgmodel.ihostdatamodelaccess import HostDataModelAccess from ghidradbg.dbgmodel.ihostdatamodelaccess import HostDataModelAccess
from _winapi import STILL_ACTIVE
DbgVersion = namedtuple('DbgVersion', ['full', 'name', 'dotted', 'arch']) DbgVersion = namedtuple('DbgVersion', ['full', 'name', 'dotted', 'arch'])
@ -236,6 +237,7 @@ class GhidraDbg(object):
'load_dump' 'load_dump'
]: ]:
setattr(self, name, self.eng_thread(getattr(base, name))) setattr(self, name, self.eng_thread(getattr(base, name)))
self.IS_KERNEL = False
def _new_base(self): def _new_base(self):
self._protected_base = AllDbg() self._protected_base = AllDbg()
@ -364,6 +366,8 @@ class GhidraDbg(object):
@check_thread @check_thread
def pid(self): def pid(self):
try: try:
if is_kernel():
return 0
return self._base._systems.GetCurrentProcessSystemId() return self._base._systems.GetCurrentProcessSystemId()
except exception.E_UNEXPECTED_Error: except exception.E_UNEXPECTED_Error:
# There is no process # There is no process
@ -451,10 +455,27 @@ def get_breakpoints():
@dbg.eng_thread @dbg.eng_thread
def selected_process(): def selected_process():
try: try:
if is_kernel():
do = dbg._base._systems.GetCurrentProcessDataOffset()
id = c_ulong()
offset = c_ulonglong(do)
nproc = dbg._base._systems._sys.GetProcessIdByDataOffset(offset, byref(id))
return id.value
if dbg.use_generics: if dbg.use_generics:
return dbg._base._systems.GetCurrentProcessSystemId() return dbg._base._systems.GetCurrentProcessSystemId()
return dbg._base._systems.GetCurrentProcessId() return dbg._base._systems.GetCurrentProcessId()
except exception.E_UNEXPECTED_Error: except (exception.E_UNEXPECTED_Error, exception.E_NOTIMPL_Error) as e:
# NB: we're intentionally returning 0 instead of None
return 0
@dbg.eng_thread
def selected_process_space():
try:
if is_kernel():
return dbg._base._systems.GetCurrentProcessDataOffset()
return selected_process()
except (exception.E_UNEXPECTED_Error, exception.E_NOTIMPL_Error) as e:
# NB: we're intentionally returning 0 instead of None # NB: we're intentionally returning 0 instead of None
return 0 return 0
@ -462,10 +483,12 @@ def selected_process():
@dbg.eng_thread @dbg.eng_thread
def selected_thread(): def selected_thread():
try: try:
if is_kernel():
return 0
if dbg.use_generics: if dbg.use_generics:
return dbg._base._systems.GetCurrentThreadSystemId() return dbg._base._systems.GetCurrentThreadSystemId()
return dbg._base._systems.GetCurrentThreadId() return dbg._base._systems.GetCurrentThreadId()
except exception.E_UNEXPECTED_Error: except (exception.E_UNEXPECTED_Error, exception.E_NOTIMPL_Error) as e:
return None return None
@ -485,6 +508,10 @@ def selected_frame():
@dbg.eng_thread @dbg.eng_thread
def select_process(id: int): def select_process(id: int):
if is_kernel():
# TODO: Ideally this should get the data offset from the id and then call
# SetImplicitProcessDataOffset
return
if dbg.use_generics: if dbg.use_generics:
id = get_proc_id(id) id = get_proc_id(id)
return dbg._base._systems.SetCurrentProcessId(id) return dbg._base._systems.SetCurrentProcessId(id)
@ -492,6 +519,10 @@ def select_process(id: int):
@dbg.eng_thread @dbg.eng_thread
def select_thread(id: int): def select_thread(id: int):
if is_kernel():
# TODO: Ideally this should get the data offset from the id and then call
# SetImplicitThreadDataOffset
return
if dbg.use_generics: if dbg.use_generics:
id = get_thread_id(id) id = get_thread_id(id)
return dbg._base._systems.SetCurrentThreadId(id) return dbg._base._systems.SetCurrentThreadId(id)
@ -586,14 +617,32 @@ def GetCurrentProcessPeb():
# TODO: upstream? # TODO: upstream?
_dbg = dbg._base _dbg = dbg._base
offset = c_ulonglong() offset = c_ulonglong()
if dbg.is_kernel():
hr = _dbg._systems._sys.GetCurrentProcessDataOffset(byref(offset))
else:
hr = _dbg._systems._sys.GetCurrentProcessPeb(byref(offset)) hr = _dbg._systems._sys.GetCurrentProcessPeb(byref(offset))
exception.check_err(hr) exception.check_err(hr)
return offset.value return offset.value
@dbg.eng_thread
def GetCurrentThreadTeb():
# TODO: upstream?
_dbg = dbg._base
offset = c_ulonglong()
if is_kernel():
hr = _dbg._systems._sys.GetCurrentThreadDataOffset(byref(offset))
else:
hr = _dbg._systems._sys.GetCurrentThreadTeb(byref(offset))
exception.check_err(hr)
return offset.value
@dbg.eng_thread @dbg.eng_thread
def GetExitCode(): def GetExitCode():
# TODO: upstream? # TODO: upstream?
if is_kernel():
return STILL_ACTIVE
exit_code = c_ulong() exit_code = c_ulong()
hr = dbg._base._client._cli.GetExitCode(byref(exit_code)) hr = dbg._base._client._cli.GetExitCode(byref(exit_code))
return exit_code.value return exit_code.value
@ -669,6 +718,21 @@ def get_proc_id(pid):
return None return None
def full_mem():
sizeptr = 64; #int(gdb.parse_and_eval('sizeof(void*)')) * 8
infoLow = DbgEng._MEMORY_BASIC_INFORMATION64()
infoLow.BaseAddress = 0
infoLow.RegionSize = (1 << (sizeptr-1))
infoLow.Protect = 0xFFF
infoLow.Name = "UMEM"
infoHigh = DbgEng._MEMORY_BASIC_INFORMATION64()
infoHigh.BaseAddress = 1 << (sizeptr-1)
infoHigh.RegionSize = (1 << (sizeptr-1))
infoHigh.Protect = 0xFFF
infoHigh.Name = "KMEM"
return [ infoLow, infoHigh ]
@dbg.eng_thread @dbg.eng_thread
def get_thread_id(tid): def get_thread_id(tid):
"""Get the list of all threads""" """Get the list of all threads"""
@ -715,39 +779,107 @@ def get_object(relpath):
if relpath != '': if relpath != '':
pathstr += "."+relpath pathstr += "."+relpath
path = split_path(pathstr) path = split_path(pathstr)
# print(f"PATH: {pathstr}")
return root.GetOffspring(path) return root.GetOffspring(path)
@dbg.eng_thread @dbg.eng_thread
def get_attributes(obj): def get_attributes(obj):
"""Get the list of attributes""" """Get the list of attributes"""
if obj is None:
return None
return obj.GetAttributes() return obj.GetAttributes()
@dbg.eng_thread @dbg.eng_thread
def get_elements(obj): def get_elements(obj):
"""Get the list of all threads""" """Get the list of all threads"""
if obj is None:
return None
return obj.GetElements() return obj.GetElements()
@dbg.eng_thread @dbg.eng_thread
def get_kind(obj): def get_kind(obj):
"""Get the list of all threads""" """Get the list of all threads"""
if obj is None:
return None
kind = obj.GetKind()
if kind is None:
return None
return obj.GetKind().value return obj.GetKind().value
@dbg.eng_thread @dbg.eng_thread
def get_type(obj): def get_type(obj):
"""Get the list of all threads""" """Get the list of all threads"""
if obj is None:
return None
return obj.GetTypeKind() return obj.GetTypeKind()
@dbg.eng_thread @dbg.eng_thread
def get_value(obj): def get_value(obj):
"""Get the list of all threads""" """Get the list of all threads"""
if obj is None:
return None
return obj.GetValue() return obj.GetValue()
@dbg.eng_thread
def get_intrinsic_value(obj):
"""Get the list of all threads"""
if obj is None:
return None
return obj.GetIntrinsicValue()
@dbg.eng_thread
def get_target_info(obj):
"""Get the list of all threads"""
if obj is None:
return None
return obj.GetTargetInfo()
@dbg.eng_thread
def get_type_info(obj):
"""Get the list of all threads"""
if obj is None:
return None
return obj.GetTypeInfo()
@dbg.eng_thread
def get_name(obj):
"""Get the list of all threads"""
if obj is None:
return None
return obj.GetName().value
@dbg.eng_thread
def to_display_string(obj):
"""Get the list of all threads"""
if obj is None:
return None
return obj.ToDisplayString()
@dbg.eng_thread
def get_location(obj):
"""Get the list of all threads"""
if obj is None:
return None
try:
loc = obj.GetLocation()
if loc is None:
return None
return hex(loc.Offset)
except:
return None
conv_map = {} conv_map = {}
@ -762,3 +894,11 @@ def get_convenience_variable(id):
def set_convenience_variable(id, value): def set_convenience_variable(id, value):
conv_map[id] = value conv_map[id] = value
def set_kernel(value):
dbg.IS_KERNEL = value
def is_kernel():
return dbg.IS_KERNEL

View file

@ -733,6 +733,77 @@ python3 -m pip install --no-index -f Debugger-rmi-trace\pypkg\dist -f Debugger-a
Alternatively, if you are trying to quit, but typed "<TT>.exit</TT>", just type Alternatively, if you are trying to quit, but typed "<TT>.exit</TT>", just type
"<TT>quit()</TT>" to terminate the session.</P> "<TT>quit()</TT>" to terminate the session.</P>
<H3><A name="dbgeng_ext"></A>dbgeng-ext</H3>
<P>This launcher extends the base dbgeng launcher adding extra options (a la IDebugClient's CreateProcess2).</P>
<H4>Options</H4>
<UL>
<LI><B>Dir</B>: This is the starting directory for the process.</LI>
<LI><B>Env</B>: This is a composite string containg Environment Variable entries
delineated by '/0' separators. For example, you could redefine USERNAME and USERPROFILE
with the entry 'USERNAME=SomeUser/0USERPROFILE=C:\Users\SomeUser'.</LI>
<LI><B>CreateFlags</B>: Flags used when creating the process, typically either DEBUG_PROCESS(1) or
DEBUG_ONLY_THIS_PROCESS(2) if you do not wish to follow spawned processes. Other possible values
are defined by processes.h's CreateProcessCreationFlags.</LI>
<LI><B>CreateFlags (Engine)</B>: Engine-specific flags used when creating the process (defined in dbgeng.h).
Typically, these are set to 0.</LI>
<LI><B>VerifierFlags (Engine)</B>: Flags used by the Application Verifier. Typically unused, but, if desired,
CreateEngineFlags must include DEBUG_ECREATE_PROCESS_USE_VERIFIER_FLAGS(2).</LI>
</UL>
<H3><A name="dbgeng_remote"></A>dbgeng-remote</H3>
<P>This launcher extends the base dbgeng launcher adding an option for connecting through a remote process server.
</P>
<H4>Options</H4>
<UL>
<LI><B>Connection</B>: This is the connection string specifying the transport options for
communicating with the remote server. A typical example might be 'tcp:port=12345,server=192.168.0.2''
for a process server launched on the machine at 192.168.0.2 using:
<PRE>
dbgsrv -t tcp:port=12345
</PRE>
</LI>
</UL>
<H3><A name="dbgeng_kernel"></A>dbgeng-kernel</H3>
<P>This version of the dbgeng should be used for kernel-debugging of a remote machine. Options are the
same as the base dbgeng, except for the connection-string arguments. For remote
debugging, the target machine should be booted with the appropriate options, set using BCDEDIT or the
equivalent, such as:
</P>
<UL style='list-style-type: none'><LI>
<PRE>
bcdedit /debug ON
bdcedit /dbgsettings NET HOSTIP:IP PORT:54321 KEY:1.1.1.1
</PRE>
</LI></UL>
<P>
where IP= the address of the machine runing Ghidra.
</P>
<H4>Options</H4>
<UL>
<LI><B>Arguments</B>: This is the connection string specifying the transport options for
communicating with the remote target. A typical example might be 'net:port=54321,key=1.1.1.1'.'
</LI>
</UL>
<H3><A name="dbgeng_ttd"></A>TTD (Time-Travel Debugging)</H3> <H3><A name="dbgeng_ttd"></A>TTD (Time-Travel Debugging)</H3>
<P>This is a nascent extension to our launcher for the Windows Debugger. The launcher itself <P>This is a nascent extension to our launcher for the Windows Debugger. The launcher itself