import os.path
import ctypes
import copy
import itertools
from collections import namedtuple
import windows
import windows.generated_def as gdef
from windows import winproxy
from windows.pycompat import basestring
DEFAULT_DBG_OPTION = gdef.SYMOPT_DEFERRED_LOADS + gdef.SYMOPT_UNDNAME
[docs]
def set_dbghelp_path(path):
"""Set the path of the ``dbghelp.dll`` file to use. It allow to configure a different version of the DLL handling PDB downloading.
If ``path`` is a directory, the final ``dbghelp.dll`` will be computed as
``path\<current_process_bitness>\dbghelp.dll``.
This allow to use the same script transparently in both 32b & 64b python interpreters.
"""
loaded_modules = [m.name.lower() for m in windows.current_process.peb.modules]
if os.path.isdir(path):
path = os.path.join(path, str(windows.current_process.bitness), u"dbghelp.dll")
if "dbghelp.dll" in loaded_modules:
raise ValueError("setup_dbghelp_path should be called before any dbghelp function")
# Change the DLL used by DbgHelpProxy
winproxy.DbgHelpProxy.APIDLL = path
return
# Load symbol config from ENV if present
try:
env_dbghelp_path = windows.system.environ["PFW_DBGHELP_PATH"]
# Setup the dbghelp path used by PFW
set_dbghelp_path(env_dbghelp_path)
except KeyError as e:
pass
class SymbolInfoBase(object):
"""Represent a Symbol.
This class in based on the class `SYMBOL_INFO <https://docs.microsoft.com/en-us/windows/win32/api/dbghelp/ns-dbghelp-symbol_info>`_
with the handling on displacement embeded into it.
"""
# Init on ctypes struct is not always called
# resolver & displacement should be set manually
CHAR_TYPE = None
def __init__(self, *args, **kwargs):
self.resolver = kwargs.get("resolver", None)
self.displacement = kwargs.get("displacement", 0)
def as_type(self):
# assert self.Address == 0 ?
return SymbolType(self.Index, self.ModBase, self.resolver)
@property
def name(self):
"""The name of the symbol"""
if not self.NameLen:
return None
size = self.NameLen
addr = ctypes.addressof(self) + type(self).Name.offset
# self.NameLen should not include the final \x00
# But some W api (like SymSearchW) returns SYMBOL_INFOW with the leading \x00
# For NtCreateFile:
# A() API -> NameLen == 12
# W() API -> NameLen == 13
return (self.CHAR_TYPE * size).from_address(addr)[:].strip(u"\x00")
@property
def fullname(self):
"""The fullname of the symbol in the windbg format ``mod!sym+displacement``"""
return str(self)
@property
def addr(self):
"""The address of the symbol"""
return self.Address + self.displacement
@property
def start(self):
"""The address of the start of the symbol
If the symbol include a displacement, it is not taken into account
"""
return self.Address
@property # Fixed ?
def module(self):
"""The module containing the symbol
:type: :class:`SymbolModule`
"""
return self.resolver.get_module(self.ModBase)
@property
def tag(self):
"""The Tag of the module
:type: :class:`~windows.generated_def.winstructs.SymTagEnum`
"""
return gdef.SymTagEnum.mapper[self.Tag]
def __int__(self):
"""An alias for ``addr``"""
return self.addr
def __str__(self):
"""The fullname of the symbol in the windbg format ``mod!sym+displacement``"""
if self.displacement:
return "{self.module.name}!{self.name}+{self.displacement:#x}".format(self=self)
return "{self.module.name}!{self.name}".format(self=self)
def __repr__(self):
if self.displacement:
return '<{0} name="{1}" start={2:#x} displacement={3:#x} tag={4}>'.format(type(self).__name__, self.name, self.start, self.displacement, self.tag.name)
return '<{0} name="{1}" start={2:#x} tag={3}>'.format(type(self).__name__, self.name, self.start, self.tag.name)
[docs]
class SymbolInfoA(gdef.SYMBOL_INFO, SymbolInfoBase):
CHAR_TYPE = gdef.CHAR
class SymbolInfoW(gdef.SYMBOL_INFOW, SymbolInfoBase):
"""Represent a Symbol.
This class in based on the class `SYMBOL_INFO <https://docs.microsoft.com/en-us/windows/win32/api/dbghelp/ns-dbghelp-symbol_info>`_
with the handling on displacement embeded into it.s
Exemple:
>>> sh = windows.debug.symbols.VirtualSymbolHandler()
>>> mod = sh.load_file(r"c:\windows\system32\kernelbase.dll")
>>> sym1 = sh["kernelbase!CreateFileW"]
>>> sym2 = sh[int(sym1) + 3]
>>> sym2
<SymbolInfoW name="CreateFileW" start=0x100f20b0 displacement=0x3 tag=SymTagPublicSymbol>
>>> hex(sym2.start)
'0x100f20b0L'
>>> hex(sym2.addr)
'0x100f20b3L'
>>> hex(sym2.displacement)
'0x3L'
>>> str(sym2)
'kernelbase!CreateFileW+0x3'
"""
CHAR_TYPE = gdef.WCHAR
# Unicode everywhere !
SymbolInfo = SymbolInfoW
class SymbolType(object):
def __init__(self, typeid, modbase, resolver):
# Inheritance ?
self.resolver = resolver
self._typeid = typeid # Kind of a handle. Different of typeid property.
self.modbase = modbase
def _get_type_info(self, typeinfo, ires=None):
res = ires
if res is None:
res = TST_TYPE_RES_TYPE.get(typeinfo, gdef.DWORD)()
try:
windows.winproxy.SymGetTypeInfo(self.resolver.handle, self.modbase, self._typeid, typeinfo, ctypes.byref(res))
except WindowsError as e:
if e.winerror == gdef.ERROR_INVALID_FUNCTION:
# invalid function on object -> None
# We may lose some information with that between no return value VS error
# Explicit verification on attributes per tags ?
return None
raise
if ires is not None:
return ires
newres = res.value
if isinstance(res, gdef.LPWSTR):
windows.winproxy.LocalFree(res)
return newres
@property
def name(self):
return self._get_type_info(gdef.TI_GET_SYMNAME)
@property
def size(self):
return self._get_type_info(gdef.TI_GET_LENGTH)
@property
def tag(self):
return self._get_type_info(gdef.TI_GET_SYMTAG)
# Diff type/typeid ?
@property
def type(self):
return self.new_typeid(self._get_type_info(gdef.TI_GET_TYPE))
@property
def typeid(self):
return self.new_typeid(self._get_type_info(gdef.TI_GET_TYPEID))
@property
def basetype(self):
return gdef.BasicType.mapper[self._get_type_info(gdef.TI_GET_BASETYPE)]
@property
def parent(self):
return self.new_typeid(self._get_type_info(gdef.TI_GET_CLASSPARENTID))
@property
def datakind(self):
return gdef.DataKind.mapper[self._get_type_info(gdef.TI_GET_DATAKIND)]
@property
def udtkind(self):
return gdef.UdtKind.mapper[self._get_type_info(gdef.TI_GET_UDTKIND)]
@property
def offset(self):
return self._get_type_info(gdef.TI_GET_OFFSET)
@property
def count(self):
# Only valid on tag == SymTagArrayType
return self._get_type_info(gdef.TI_GET_COUNT)
@property
def nb_children(self):
return self._get_type_info(gdef.TI_GET_CHILDRENCOUNT)
@property
def value(self):
return self._get_type_info(gdef.TI_GET_VALUE)
@property
def address(self):
return self._get_type_info(gdef.TI_GET_ADDRESS)
@property
def children(self):
count = self.nb_children
if count is None:
return None
class res_struct(ctypes.Structure):
_fields_ = [("Count", gdef.ULONG), ("Start", gdef.ULONG), ("Types", (gdef.ULONG * count))]
x = res_struct()
x.Count = count
x.Start = 0
self._get_type_info(gdef.TI_FINDCHILDREN, x)
return [self.new_typeid(ch) for ch in x.Types]
# Constructor
@classmethod
def from_symbol_info(cls, syminfo, resolver):
return cls(syminfo.TypeIndex, syminfo.ModBase, resolver)
# Constructor
def new_typeid(self, newtypeid):
if newtypeid is None:
return None
return type(self)(newtypeid, self.modbase, self.resolver)
def __repr__(self):
if self.tag == gdef.SymTagBaseType:
return '<{0} <basetype> {1!r}>'.format(type(self).__name__, self.basetype)
elif self.tag == gdef.SymTagArrayType:
target_type = self.type.name
array_size = self.count
return '<{0} {1}[{2}] tag={3!r}>'.format(type(self).__name__, target_type, array_size, self.tag)
elif self.tag == gdef.SymTagPointerType:
target_type = self.type.name
if self.type.tag == gdef.SymTagBaseType:
target_type = repr(self.type.basetype)
else:
target_type = self.type.name
return '<{0} PTR TO "{1}" tag={2!r}>'.format(type(self).__name__, target_type, self.tag)
return '<{0} name="{1}" tag={2!r}>'.format(type(self).__name__, self.name, self.tag)
[docs]
class SymbolModule(gdef.IMAGEHLP_MODULEW64):
"""Represent a loaded symbol module
(see `MSDN _IMAGEHLP_MODULEW64 <https://docs.microsoft.com/en-us/windows/win32/api/dbghelp/ns-dbghelp-imagehlp_modulew64>`_)
.. note::
This represent a module in the ``symbol space`` for symbol resolution.
This can be completly virtual (particularly in the case of :class:`VirtualSymbolHandler`
"""
# Init on ctypes struct is not always called
# resolver should be set manually
def __init__(self, resolver):
self.resolver = resolver
@property
def addr(self):
"""The load address of the module"""
return self.BaseOfImage
@property
def name(self):
"""The name of the module"""
return self.ModuleName
@property
def path(self):
"""The full path and file name of the file from which symbols were loaded."""
return self.LoadedImageName
@property
def type(self):
"""The type of module (:class:`~windows.generated_def.winstructs.SYM_TYPE`),
which can be one of:
=========== =========================
SymCoff COFF symbols.
SymCv CodeView symbols.
SymDeferred Symbol loading deferred.
SymDia DIA symbols.
SymExport Symbols generated from a DLL export table.
SymNone No symbols are loaded.
SymPdb PDB symbols.
SymSym .sym file.
SymVirtual The virtual module created by SymLoadModuleEx with SLMFLAG_VIRTUAL.
=========== =========================
"""
return self.SymType
@property
def pdb(self):
"""The local path of the loaded PDB if present
Exemple:
>>> sh = windows.debug.symbols.VirtualSymbolHandler()
>>> mod = sh.load_file(r"c:\windows\system32\kernelbase.dll")
>>> mod.pdb
'd:\\symbols\\wkernelbase.pdb\\017FA9C5278235B7E6BFBA74A9A5AAD91\\wkernelbase.pdb'
"""
LoadedPdbName = self.LoadedPdbName
if not LoadedPdbName:
return None
return LoadedPdbName
def __repr__(self):
pdb_basename = self.LoadedPdbName.split("\\")[-1]
return '<{0} name="{1}" type={2} pdb="{3}" addr={4:#x}>'.format(type(self).__name__, self.name, self.type.value.name, pdb_basename, self.addr)
# https://docs.microsoft.com/en-us/windows/win32/debug/symbol-handler-initialization
class SymbolHandler(object):
"""Base class of symbol handler"""
def __init__(self, handle, search_path=None, invade_process=False):
# https://docs.microsoft.com/en-us/windows/desktop/api/dbghelp/nf-dbghelp-syminitialize
# This value should be unique and nonzero, but need not be a process handle.
# be sure to use the correct handle.
self.handle = handle #: The handle of the symbol handler
if not engine.options_already_setup:
engine.set_options(DEFAULT_DBG_OPTION)
winproxy.SymInitializeW(handle, search_path, invade_process)
def load_module(self, file_handle=None, path=None, name=None, addr=0, size=0, data=None, flags=0):
"""Load a module at a given ``addr``. The module to load can be pass via a ``file_handle``
or the direct ``path`` of the file to load.
:return: :class:`SymbolModule` -- The loaded module
.. note::
The logic of ``SymLoadModuleEx`` seems somewhat strange about the naming of the loaded module.
A custom module ``name`` is only taken into account if the file is passed via a File handle.
To make it more intuitive, if this function is call with a ``path`` and ``name`` and no ``file_handle``,
it will open the path and directly call ``SymLoadModuleEx`` with a file handle and a name.
"""
# Is that a bug in SymLoadModuleEx ?
# To get a custom name for a module it use "path"
# So we need to use file_handle and set a custom path
# ! BUT it means we cannot get a custom name for a module where the path is not explicit and need to be searched
if name is not None and file_handle is None and os.path.exists(path):
try:
f = open(path)
file_handle = windows.utils.get_handle_from_file(f)
path = name
except Exception as e:
pass
try:
load_addr = winproxy.SymLoadModuleExW(self.handle, file_handle, path, name, addr, size, data, flags)
except WindowsError as e:
# if e.winerror == 0:
# Already loaded ?
# What if someone try to load another PE at the same BaseOfDll ?
# return BaseOfDll
raise
return self.get_module(load_addr)
def load_file(self, path, name=None, addr=0, size=0, data=None, flags=0):
"""Load the module ``path`` at ``addr``
:return: :class:`SymbolModule` -- The loaded module
"""
return self.load_module(path=path, name=name, addr=addr, size=size, data=data, flags=flags)
def unload(self, addr):
"""Unload the module at ``addr``"""
return winproxy.SymUnloadModule64(self.handle, addr)
@staticmethod
@ctypes.WINFUNCTYPE(gdef.BOOL, gdef.PVOID, gdef.DWORD64, ctypes.py_object)
def modules_aggregator(modname, modaddr, ctx):
ctx.append(modaddr)
return True
@property
def modules(self):
"""The list of loaded modules
:return: [:class:`SymbolModule`] -- A list of modules
"""
res = []
windows.winproxy.SymEnumerateModulesW64(self.handle, self.modules_aggregator, res)
return [self.get_module(addr) for addr in res]
def get_module(self, base):
modinfo = SymbolModule(self)
modinfo.SizeOfStruct = ctypes.sizeof(modinfo)
winproxy.SymGetModuleInfoW64(self.handle, base, modinfo)
return modinfo
def symbol_and_displacement_from_address(self, addr):
displacement = gdef.DWORD64()
max_len_size = 0x1000
full_size = ctypes.sizeof(SymbolInfo) + (max_len_size - 1)
buff = windows.utils.BUFFER(SymbolInfo)(size=full_size)
sym = buff[0]
sym.SizeOfStruct = ctypes.sizeof(SymbolInfo)
sym.MaxNameLen = max_len_size
winproxy.SymFromAddrW(self.handle, addr, displacement, buff)
sym.resolver = self
sym.displacement = displacement.value
return sym
def symbol_from_name(self, name):
max_len_size = 0x1000
full_size = ctypes.sizeof(SymbolInfo) + (max_len_size - 1)
buff = windows.utils.BUFFER(SymbolInfo)(size=full_size)
sym = buff[0]
sym.SizeOfStruct = ctypes.sizeof(SymbolInfo)
sym.MaxNameLen = max_len_size
windows.winproxy.SymFromNameW(self.handle, name, buff)
sym.resolver = self
sym.displacement = 0
return sym
def resolve(self, name_or_addr):
"""Resolve ``name_or_addr``.
If its an int -> Return the :class:`SymbolInfo` at the address.
If its a string -> Return the :class:`SymbolInfo` corresponding to the symbol name
:return: :class:`SymbolInfo`
.. note::
``__getitem__`` is an alias for ``resolve()``
Exemple:
>>> sh = windows.debug.symbols.VirtualSymbolHandler()
>>> mod = sh.load_file(r"c:\windows\system32\kernelbase.dll")
>>> mod
<SymbolModule name="kernelbase" type=SymPdb pdb="wkernelbase.pdb" addr=0x10000000>
>>> sh.resolve("kernelbase!CreateFileInternal")
<SymbolInfoA name="CreateFileInternal" addr=0x100f2120 tag=SymTagFunction>
>>> sh[0x100f2042]
<SymbolInfoA name="ReadFile" addr=0x100f1ee0 displacement=0x162 tag=SymTagFunction>
>>> str(sh[0x100f2042])
'kernelbase!ReadFile+0x162'
"""
# Only returns None if symbol is not Found ?
if isinstance(name_or_addr, windows.pycompat.anybuff):
return self.symbol_from_name(name_or_addr)
try:
return self.symbol_and_displacement_from_address(name_or_addr)
except WindowsError as e:
if e.winerror != gdef.ERROR_MOD_NOT_FOUND:
raise
# We could not resolve and address -> return None
return None
__getitem__ = resolve
"""Alias to resolve for simpler use"""
@staticmethod
@ctypes.WINFUNCTYPE(gdef.BOOL, ctypes.POINTER(SymbolInfo), gdef.ULONG , ctypes.py_object)
def simple_aggregator(info, size, ctx):
sym = info[0]
fullsize = sym.SizeOfStruct + (sym.NameLen * ctypes.sizeof(SymbolInfo.CHAR_TYPE))
cpy = windows.utils.BUFFER(SymbolInfo)(size=fullsize)
ctypes.memmove(cpy, info, fullsize)
ctx.append(cpy[0])
return True
def search(self, mask, mod=0, tag=0, options=gdef.SYMSEARCH_ALLITEMS, callback=None):
"""Search the symbols matching ``mask`` (``Windbg`` like).
:return: [:class:`SymbolInfo`] -- A list of :class:`SymbolInfo`
>>> sh = windows.debug.symbols.VirtualSymbolHandler()
>>> mod = sh.load_file(r"c:\windows\system32\kernelbase.dll")
>>> sh.search("kernelbase!CreateFile*")
[<SymbolInfoW name="CreateFileInternal" addr=0x100f2120 tag=SymTagFunction>,
<SymbolInfoW name="CreateFileMoniker" addr=0x10117d80 tag=SymTagFunction>,
<SymbolInfoW name="CreateFile2" addr=0x1011e690 tag=SymTagFunction>,
...]
"""
res = []
if callback is None:
callback = self.simple_aggregator
else:
callback = ctypes.WINFUNCTYPE(gdef.BOOL, ctypes.POINTER(SymbolInfo), gdef.ULONG , ctypes.py_object)(callback)
addr = getattr(mod, "addr", mod) # Retrieve mod.addr, else use the value directly
windows.winproxy.SymSearchW(self.handle, gdef.DWORD64(addr), 0, tag, mask, 0, callback, res, options)
for sym in res:
sym.resolver = self
sym.displacement = 0
return res
def get_symbols(self, addr, callback=None):
res = []
if callback is None:
callback = self.simple_aggregator
else:
callback = ctypes.WINFUNCTYPE(gdef.BOOL, ctypes.POINTER(SymbolInfo), gdef.ULONG , ctypes.py_object)(callback)
try:
windows.winproxy.SymEnumSymbolsForAddrW(self.handle, addr, callback, res)
except WindowsError as e:
if e.winerror == gdef.ERROR_MOD_NOT_FOUND:
return []
raise
for sym in res:
sym.resolver = self
sym.displacement = 0
return res
# Type stuff
def get_type(self, name, mod=0):
max_len_size = 0x1000
full_size = ctypes.sizeof(SymbolInfo) + (max_len_size - 1)
buff = windows.utils.BUFFER(SymbolInfo)(size=full_size)
buff[0].SizeOfStruct = ctypes.sizeof(SymbolInfo)
buff[0].MaxNameLen = max_len_size
windows.winproxy.SymGetTypeFromNameW(self.handle, mod, name, buff)
return SymbolType.from_symbol_info(buff[0], resolver=self)
# TODO: mets de l'huile pour w4kfu
class StackWalker(object):
def __init__(self, resolver, process=None, thread=None, context=None):
self.resolver = resolver
if process is None and thread is None:
raise ValueError("At least a process or thread must be provided")
if process is None:
process = thread.owner
self.process = process
self.thread = thread
self.context = context
if windows.current_process.bitness == 32 and process.bitness == 64:
raise NotImplementedError("StackWalking 64b does not seems to works from 32b process")
def _stack_frame_generator(self):
ctx, machine = self._get_effective_context_and_machine()
frame = self._setup_initial_frame_from_context(ctx, machine)
thread_handle = self.thread.handle if self.thread else None
while True:
try:
windows.winproxy.StackWalkEx(machine,
# dbg.current_process.handle,
self.resolver.handle,
thread_handle,
# 0,
frame,
ctypes.byref(ctx),
None,
winproxy.resolve(winproxy.SymFunctionTableAccess64),
winproxy.resolve(winproxy.SymGetModuleBase64),
None,
0)
except WindowsError as e:
if not e.winerror:
return # No_ERROR -> end of stack walking
raise
yield type(frame).from_buffer_copy(frame) # Make a copy ?
def __iter__(self):
return self._stack_frame_generator()
# Autorise to force the retrieving of 32b stack when code is currently on 64b code ?
def _get_effective_context_and_machine(self):
ctx = self.context or self.thread.context
if self.process.bitness == 32:
# Process is 32b, so the context is inevitably x86
return (ctx, gdef.IMAGE_FILE_MACHINE_I386)
if windows.current_process.bitness == 32:
# If we are 32b, we will only be able to handle x86 stack
# ctx is obligatory a 32b one, as the case us32/target64 is handled
# in __init__ with a NotImplementedError
return (ctx, gdef.IMAGE_FILE_MACHINE_I386)
if self.process.bitness == 64:
# Process is 64b, so the context is inevitably x64
return (ctx, gdef.IMAGE_FILE_MACHINE_AMD64)
# Thing get a little more complicated here :)
# We are a 64b process and target is 32b.
# So we must find-out if we are in 32 or 64b world at the moment.
# The context_syswow.SegCS give us the information
# The context32.SegCs would be always 32
ctxsyswow = dbg.current_thread.context_syswow
if ctxsyswow.SegCs == gdef.CS_USER_32B:
return (ctx, gdef.IMAGE_FILE_MACHINE_I386)
return (ctxsyswow, gdef.IMAGE_FILE_MACHINE_AMD64)
def _setup_initial_frame_from_context(self, ctx, machine):
frame = gdef.STACKFRAME_EX()
frame.AddrPC.Mode = gdef.AddrModeFlat
frame.AddrFrame.Mode = gdef.AddrModeFlat
frame.AddrStack.Mode = gdef.AddrModeFlat
frame.AddrPC.Offset = ctx.pc
frame.AddrStack.Offset = ctx.sp
if machine == gdef.IMAGE_FILE_MACHINE_I386:
frame.AddrFrame.Offset = ctx.Ebp
# Need RBP on 64b ?
return frame
[docs]
class VirtualSymbolHandler(SymbolHandler):
"""A SymbolHandler where its handle is not a valid process handle
Allow to create/resolve symbol in a 'virtual' process
But all API needing a real process handle will fail
"""
VIRTUAL_HANDLER_COUNTER = itertools.count(0x11223344)
def __init__(self, search_path=None):
handle = next(self.VIRTUAL_HANDLER_COUNTER)
super(VirtualSymbolHandler, self).__init__(handle, search_path, False)
# The VirtualSymbolHandler is not based on an existing process
# So load() in its simplest for should just take the path of the file to load
load = SymbolHandler.load_file
"""An alias for :func:`VirtualSymbolHandler.load_file`"""
[docs]
def refresh(self):
"""Do nothing for a :class:`VirtualSymbolHandler`"""
return False
[docs]
class ProcessSymbolHandler(SymbolHandler):
def __init__(self, process, search_path=None, invade_process=False):
super(ProcessSymbolHandler, self).__init__(process.handle, search_path, invade_process)
self.target = process
# The ProcessSymbolHandler is based on an existing process
# So load() in its simplest form should be able to load the symbol for an existing
# module that is already loaded
# Question: should be able to load other module at other address ?
[docs]
def load(self, name):
"""Load the :class:`SymbolModule` associated with the loaded module ``name`` (as found in the PEB)
:return: :class:`SymbolModule`
Exemple:
>>> sh = windows.debug.symbols.ProcessSymbolHandler(windows.test.pop_proc_64())
<windows.debug.symbols.ProcessSymbolHandler object at 0x033A2C30>
>>> sh
<windows.debug.symbols.ProcessSymbolHandler object at 0x033A2C30>
>>> sh.load("kernelbase.dll")
<SymbolModule name="kernelbase" type=SymDeferred pdb="" addr=0x7ffb5b090000>
>>> sh["kernelbase!CreateProcessA"]
<SymbolInfoA name="CreateProcessA" start=0x7ffb5b2371f0 tag=SymTagPublicSymbol>
"""
mods = [x for x in self.target.peb.modules if x.name == name]
if not mods:
raise ValueError("Could not find module <{0}>".format(name))
assert len(mods) == 1 # Load all if multiple match ?
mod = mods[0]
return self.load_module(addr=mod.baseaddr, path=mod.fullname)
[docs]
def refresh(self):
"""Update the list of loaded modules to match the modules present in the target process
.. note::
This function only call `SymRefreshModuleList <https://docs.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-symrefreshmodulelist>`_ for now.
It seems that this function do not handle refreshing a 64b target from a 32b python
Also, on a 32b target from a 64b python it seems to only load symbols for the 64b modules (ntdll + syswow dll)
Exemple:
>>> sh = windows.debug.symbols.ProcessSymbolHandler(windows.test.pop_proc_64())
>>> sh.modules
[]
>>> sh.refresh()
44
>>> sh.modules
[<SymbolModule name="notepad" type=SymDeferred pdb="" addr=0x7ff772b80000>,
<SymbolModule name="ntdll" type=SymDeferred pdb="" addr=0x7ffb5d860000>,
<SymbolModule name="KERNEL32" type=SymDeferred pdb="" addr=0x7ffb5bb90000>,
<SymbolModule name="KERNELBASE" type=SymDeferred pdb="" addr=0x7ffb5b090000>,
...]
"""
return windows.winproxy.SymRefreshModuleList(self.handle)
def stackwalk(self, ctx):
pass
[docs]
class SymbolEngine(object):
"""Represent the global symbol engine. Just a proxy to get/set global engine options
Its instance can be accessed using ``windows.debug.symbols.engine``
Exemple:
>>> windows.debug.symbols.engine.options
6L
>>> windows.debug.symbols.engine.options = gdef.SYMOPT_UNDNAME
>>> windows.debug.symbols.engine.options
2L
"""
def __init__(self):
# use to now if we need to call the setup of options
# At the first DbgHelp call
self.options_already_setup = False
def set_options(self, options):
self.options_already_setup = True
return windows.winproxy.SymSetOptions(options)
def get_options(self):
return windows.winproxy.SymGetOptions()
options = property(get_options, set_options)
"""The options of the Symbol engine
(`see options <https://docs.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-symsetoptions#parameters>`_)
.. note::
Default options are: ``gdef.SYMOPT_DEFERRED_LOADS + gdef.SYMOPT_UNDNAME``
"""
engine = SymbolEngine()
"""The instance of the :class:`SymbolEngine`"""
TST_TYPE_RES_TYPE = {
gdef.TI_GET_SYMNAME: gdef.LPWSTR,
gdef.TI_GET_LENGTH: gdef.ULONG64,
gdef.TI_GET_ADDRESS: gdef.ULONG64,
gdef.TI_GTIEX_REQS_VALID: gdef.ULONG64,
gdef.TI_GET_SYMTAG: gdef.SymTagEnum,
gdef.TI_GET_VALUE: windows.com.Variant,
}