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: MIT
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-ttd.bat||GHIDRA||||END|
data/debugger-launchers/remote-dbgeng.bat||GHIDRA||||END|
src/main/py/LICENSE||GHIDRA||||END|
src/main/py/MANIFEST.in||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
def map(self, proc: int, offset: int):
space = self.defaultSpace
if proc == 0:
space = self.defaultSpace
else:
space = f'{self.defaultSpace}{proc}'
return self.defaultSpace, Address(space, offset)
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
raise ValueError(
f"Address {address} is not in process {proc.GetProcessID()}")
if address.space == f'{self.defaultSpace}{proc}':
return address.offset
raise ValueError(f"Address {address} is not in process {proc}")
DEFAULT_MEMORY_MAPPER = DefaultMemoryMapper('ram')

View file

@ -22,6 +22,7 @@ import socket
import sys
import time
from comtypes import c_ulong
from ghidratrace import sch
from ghidratrace.client import Client, Address, AddressRange, TraceObject
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 .dbgmodel.imodelobject import ModelObjectKind
from .dbgeng.idebugclient5 import *
PAGE_SIZE = 4096
@ -68,10 +69,11 @@ GENERIC_KEY_PATTERN = '[{key}]'
class ErrorWithCode(Exception):
def __init__(self, code):
self.code = code
def __str__(self)->str:
def __str__(self) -> str:
return repr(self.code)
@ -216,6 +218,8 @@ def start_trace(name):
with STATE.trace.open_tx("Create Root Object"):
root = STATE.trace.create_root_object(schema_xml, 'DbgRoot')
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")
@ -261,6 +265,67 @@ def ghidra_trace_create(command=None, initial_break=True, timeout=DbgEng.WAIT_IN
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
def ghidra_trace_kill():
"""
@ -370,7 +435,7 @@ def ghidra_trace_set_snap(snap=None):
def quantize_pages(start, end):
return (start // PAGE_SIZE * PAGE_SIZE, (end+PAGE_SIZE-1) // PAGE_SIZE*PAGE_SIZE)
return (start // PAGE_SIZE * PAGE_SIZE, (end + PAGE_SIZE - 1) // PAGE_SIZE * PAGE_SIZE)
@util.dbg.eng_thread
@ -453,7 +518,7 @@ def putmem_state(address, length, state, pages=True):
nproc = util.selected_process()
base, addr = STATE.trace.memory_mapper.map(nproc, start)
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)
@ -718,6 +783,7 @@ def ghidra_trace_get_obj(path):
class TableColumn(object):
def __init__(self, head):
self.head = head
self.contents = [head]
@ -735,6 +801,7 @@ class TableColumn(object):
class Tabular(object):
def __init__(self, heads):
self.columns = [TableColumn(h) for h in heads]
self.columns[-1].is_last = True
@ -925,7 +992,7 @@ def put_available():
ppath = AVAILABLE_PATTERN.format(pid=id)
procobj = STATE.trace.create_object(ppath)
keys.append(AVAILABLE_KEY_PATTERN.format(pid=id))
pidstr = ('0x{:x}' if radix ==
pidstr = ('0x{:x}' if radix ==
16 else '0{:o}' if radix == 8 else '{}').format(id)
procobj.set_value('PID', id)
procobj.set_value('Name', name)
@ -1077,6 +1144,8 @@ def put_regions():
regions = util.dbg._base.memory_list()
except Exception:
regions = []
if len(regions) == 0:
regions = util.full_mem()
mapper = STATE.trace.memory_mapper
keys = []
@ -1087,15 +1156,17 @@ def put_regions():
regobj = STATE.trace.create_object(rpath)
(start_base, start_addr) = map_address(r.BaseAddress)
regobj.set_value('Range', start_addr.extend(r.RegionSize))
regobj.set_value('_readable', r.Protect ==
regobj.set_value('_readable', r.Protect ==
None or r.Protect & 0x66 != 0)
regobj.set_value('_writable', r.Protect ==
regobj.set_value('_writable', r.Protect ==
None or r.Protect & 0xCC != 0)
regobj.set_value('_executable', r.Protect ==
regobj.set_value('_executable', r.Protect ==
None or r.Protect & 0xF0 != 0)
regobj.set_value('AllocationBase', hex(r.AllocationBase))
regobj.set_value('Protect', hex(r.Protect))
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()
STATE.trace.proxy_object_path(
MEMORY_PATTERN.format(procnum=nproc)).retain_values(keys)
@ -1296,37 +1367,44 @@ def ghidra_trace_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"):
istate = compute_proc_state(index)
obj.set_value('State', istate)
if np.endswith("Sessions"):
key = '[{:x}]'.format(index)
if np.endswith("Processes"):
create_generic(obj.path)
id = util.get_proc_id(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"):
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"):
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 util.is_kernel():
key = '[{:x}]'.format(index)
else:
id = util.get_thread_id(index)
key = '{:x} [{:x}]'.format(id, index)
if np.endswith("Frames"):
mo = util.get_object(obj.path)
map = util.get_attributes(mo)
attr = map["Attributes"]
if attr is None:
return
create_generic(obj.path+".Attributes")
map = util.get_attributes(attr)
map = util.get_attributes(attr)
pc = util.get_value(map["InstructionOffset"])
(pc_base, pc_addr) = map_address(pc)
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"):
create_generic(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))
(base_base, base_addr) = map_address(base)
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):
@ -1348,58 +1431,81 @@ def create_generic(path):
def put_generic(node):
#print(f"put_generic: {node}")
# print(f"put_generic: {node}")
nproc = util.selected_process()
if nproc is None:
return
nthrd = util.selected_thread()
mapper = STATE.trace.memory_mapper
mo = util.get_object(node.path)
kind = util.get_kind(mo)
type = util.get_type(mo)
vstr = util.get_value(mo)
# print(f"MO={mo}")
mapper = STATE.trace.register_mapper
attributes = util.get_attributes(mo)
# print(f"ATTR={attributes}")
mapper = STATE.trace.register_mapper
values = []
for key, value in attributes.items():
if value is None:
continue
kind = util.get_kind(value)
vstr = util.get_value(value)
#print(f"key={key} kind={kind} value={vstr} type={type}")
if kind == ModelObjectKind.PROPERTY_ACCESSOR.value or \
kind == ModelObjectKind.SYNTHETIC.value or \
kind == ModelObjectKind.METHOD.value:
if vstr is not None:
key += " : " + vstr
apath = node.path+'.'+key
aobj = STATE.trace.create_object(apath)
aobj.insert()
else:
try:
if node.path.endswith('.User'):
values.append(mapper.map_value(nproc, key, vstr))
node.set_value(key, hex(vstr))
except Exception as e:
pass # Error is printed by another mechanism
if attributes is not None:
for key, value in attributes.items():
kind = util.get_kind(value)
if kind == ModelObjectKind.METHOD.value:
continue
# print(f"key={key} kind={kind}")
if kind != ModelObjectKind.INTRINSIC.value:
apath = node.path + '.' + key
aobj = STATE.trace.create_object(apath)
set_display(key, value, aobj)
aobj.insert()
else:
val = util.get_value(value)
try:
if node.path.endswith('.User'):
# print(f"PUT_REG: {key} {val}")
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:
print(f"Attribute exception for {key} {type(val)}: {e}")
elements = util.get_elements(mo)
# print(f"ELEM={elements}")
keys = []
for el in elements:
index = el[0]
key = GENERIC_KEY_PATTERN.format(key=index)
lpath = node.path+key
lobj = STATE.trace.create_object(lpath)
update_by_container(node.path, index, lobj)
lobj.insert()
keys.append(key)
node.retain_values(keys)
if elements is not None:
for el in elements:
index = el[0]
key = GENERIC_KEY_PATTERN.format(key=index)
lpath = node.path + key
lobj = STATE.trace.create_object(lpath)
update_by_container(node.path, el, lobj)
lobj.insert()
keys.append(key)
node.retain_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):
nproc = util.selected_process()
mapper = STATE.trace.memory_mapper

View file

@ -155,6 +155,13 @@ library DbgMod
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 {
ULONGLONG BaseAddress;

View file

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

View file

@ -22,10 +22,13 @@ from comtypes.gen import DbgMod
from comtypes.hresult import S_OK, S_FALSE
from pybag.dbgeng import exception
from comtypes import BSTR
from comtypes.gen.DbgMod import *
from .iiterableconcept import IterableConcept
from .istringdisplayableconcept import StringDisplayableConcept
from .ikeyenumerator import KeyEnumerator
from .irawenumerator import RawEnumerator
class ModelObjectKind(Enum):
@ -45,6 +48,7 @@ class ModelObject(object):
def __init__(self, obj):
self._obj = obj
self.concept = None
self.dconcept = None
exception.wrap_comclass(self._obj)
def Release(self):
@ -96,12 +100,16 @@ class ModelObject(object):
return RawEnumerator(keys, kind)
def GetConcept(self, ref):
ifc = POINTER(IUnknown)()
metadata = POINTER(DbgMod.IKeyStore)()
hr = self._obj.GetConcept(ref._iid_, byref(ifc), byref(metadata))
if hr != S_OK:
try:
ifc = POINTER(IUnknown)()
metadata = POINTER(DbgMod.IKeyStore)()
hr = self._obj.GetConcept(ref._iid_, byref(ifc), byref(metadata))
if hr != S_OK:
return None
return cast(ifc, POINTER(ref))
except Exception as e:
print(f"GetConcept exception: {e}")
return None
return cast(ifc, POINTER(ref))
def GetContext(self, context):
raise exception.E_NOTIMPL_Error
@ -111,10 +119,14 @@ class ModelObject(object):
def GetIntrinsicValue(self):
var = VARIANT()
hr = self._obj.GetIntrinsicValue(var)
if hr != S_OK:
try:
hr = self._obj.GetIntrinsicValue(var)
if hr != S_OK:
return None
return var
except Exception as e:
print(f"GetIntrinsicValue exception: {e}")
return None
return var
def GetIntrinsicValueAs(self, vt):
raise exception.E_NOTIMPL_Error
@ -140,8 +152,31 @@ class ModelObject(object):
exception.check_err(hr)
return kind
def GetLocation(self, location):
raise exception.E_NOTIMPL_Error
# DOESN"T WORK YET
# 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):
raise exception.E_NOTIMPL_Error
@ -152,18 +187,55 @@ class ModelObject(object):
def GetRawReference(self, kind, name, searchFlags, object):
raise exception.E_NOTIMPL_Error
def GetRawValue(self, kind, name, searchFlags, object):
raise exception.E_NOTIMPL_Error
def GetRawValue(self, kind, name, searchFlags):
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):
location = POINTER(DbgMod._Location)()
location = DbgMod._Location()
type = POINTER(DbgMod.IDebugHostType)()
hr = self._obj.GetTargetInfo(location, byref(type))
exception.check_err(hr)
return type
return ModelObject(type)
def GetTypeInfo(self, type):
raise exception.E_NOTIMPL_Error
def GetTypeInfo(self):
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):
raise exception.E_NOTIMPL_Error
@ -198,23 +270,28 @@ class ModelObject(object):
return map
def GetRawValueMap(self):
# print(f"GetRawValueMap: {self}")
map = {}
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()
while k is not None:
map[k.value] = v
(k, v) = keys.GetNext()
# print(f"{k}:{v}")
return map
def GetAttributes(self):
map = {}
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
if kind == ModelObjectKind.INTRINSIC or \
kind == ModelObjectKind.TARGET_OBJECT or \
kind == ModelObjectKind.TARGET_OBJECT_REFERENCE:
if kind.value == ModelObjectKind.INTRINSIC.value or \
kind.value == ModelObjectKind.TARGET_OBJECT.value or \
kind.value == ModelObjectKind.TARGET_OBJECT_REFERENCE.value:
return self.GetRawValueMap()
return self.GetKeyValueMap()
@ -245,6 +322,9 @@ class ModelObject(object):
def GetOffspring(self, path):
next = self
for element in path:
if next is None:
return None
kind = next.GetKind()
if element.startswith("["):
idx = element[1:len(element)-1]
if "x" not in idx:
@ -252,12 +332,17 @@ class ModelObject(object):
else:
idx = int(idx, 16)
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:
next = next.GetKeyValue(element)
if next is None:
print(f"{element} not found")
#if next is None:
# print(f"{element} not found")
return next
def GetValue(self):
value = self.GetIntrinsicValue()
if value is None:
@ -266,9 +351,3 @@ class ModelObject(object):
return None
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:
commands.STATE.trace.snapshot(description)
if first:
if util.is_kernel():
commands.create_generic("Sessions")
commands.put_processes()
commands.put_environment()
commands.put_threads()
if self.threads:
commands.put_threads()
self.threads = False
@ -98,6 +101,7 @@ class ProcessState(object):
commands.put_threads(running=True)
def record_exited(self, exit_code, description=None):
# print("RECORD_EXITED")
if description is not None:
commands.STATE.trace.snapshot(description)
proc = util.selected_process()

View file

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

View file

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

View file

@ -37,6 +37,7 @@ from pybag.dbgeng import util as DbgUtil
from pybag.dbgeng.callbacks import DbgEngCallbacks
from ghidradbg.dbgmodel.ihostdatamodelaccess import HostDataModelAccess
from _winapi import STILL_ACTIVE
DbgVersion = namedtuple('DbgVersion', ['full', 'name', 'dotted', 'arch'])
@ -236,6 +237,7 @@ class GhidraDbg(object):
'load_dump'
]:
setattr(self, name, self.eng_thread(getattr(base, name)))
self.IS_KERNEL = False
def _new_base(self):
self._protected_base = AllDbg()
@ -364,6 +366,8 @@ class GhidraDbg(object):
@check_thread
def pid(self):
try:
if is_kernel():
return 0
return self._base._systems.GetCurrentProcessSystemId()
except exception.E_UNEXPECTED_Error:
# There is no process
@ -451,10 +455,27 @@ def get_breakpoints():
@dbg.eng_thread
def selected_process():
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:
return dbg._base._systems.GetCurrentProcessSystemId()
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
return 0
@ -462,10 +483,12 @@ def selected_process():
@dbg.eng_thread
def selected_thread():
try:
if is_kernel():
return 0
if dbg.use_generics:
return dbg._base._systems.GetCurrentThreadSystemId()
return dbg._base._systems.GetCurrentThreadId()
except exception.E_UNEXPECTED_Error:
except (exception.E_UNEXPECTED_Error, exception.E_NOTIMPL_Error) as e:
return None
@ -485,6 +508,10 @@ def selected_frame():
@dbg.eng_thread
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:
id = get_proc_id(id)
return dbg._base._systems.SetCurrentProcessId(id)
@ -492,6 +519,10 @@ def select_process(id: int):
@dbg.eng_thread
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:
id = get_thread_id(id)
return dbg._base._systems.SetCurrentThreadId(id)
@ -586,7 +617,23 @@ def GetCurrentProcessPeb():
# TODO: upstream?
_dbg = dbg._base
offset = c_ulonglong()
hr = _dbg._systems._sys.GetCurrentProcessPeb(byref(offset))
if dbg.is_kernel():
hr = _dbg._systems._sys.GetCurrentProcessDataOffset(byref(offset))
else:
hr = _dbg._systems._sys.GetCurrentProcessPeb(byref(offset))
exception.check_err(hr)
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
@ -594,6 +641,8 @@ def GetCurrentProcessPeb():
@dbg.eng_thread
def GetExitCode():
# TODO: upstream?
if is_kernel():
return STILL_ACTIVE
exit_code = c_ulong()
hr = dbg._base._client._cli.GetExitCode(byref(exit_code))
return exit_code.value
@ -669,6 +718,21 @@ def get_proc_id(pid):
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
def get_thread_id(tid):
"""Get the list of all threads"""
@ -715,39 +779,107 @@ def get_object(relpath):
if relpath != '':
pathstr += "."+relpath
path = split_path(pathstr)
# print(f"PATH: {pathstr}")
return root.GetOffspring(path)
@dbg.eng_thread
def get_attributes(obj):
"""Get the list of attributes"""
if obj is None:
return None
return obj.GetAttributes()
@dbg.eng_thread
def get_elements(obj):
"""Get the list of all threads"""
if obj is None:
return None
return obj.GetElements()
@dbg.eng_thread
def get_kind(obj):
"""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
@dbg.eng_thread
def get_type(obj):
"""Get the list of all threads"""
if obj is None:
return None
return obj.GetTypeKind()
@dbg.eng_thread
def get_value(obj):
"""Get the list of all threads"""
if obj is None:
return None
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 = {}
@ -762,3 +894,11 @@ def get_convenience_variable(id):
def set_convenience_variable(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
"<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>
<P>This is a nascent extension to our launcher for the Windows Debugger. The launcher itself