9. windows.debug – Debugging¶
Note
See sample Debugging
Note
If you are interrested by symbols (PDB) handling, go to subsection windows.debug.symbols – Using symbols.
You can also look at the symbols-related samples: Symbols
9.1. Debugger¶
The Debugger is the base class to perform the debugging of a remote process.
The Debugger have some functions called on given event that can be implemented by subclasses.
All Memory-breakpoint are disabled when callind a public callback or a breakpoint trigger() function.
This means that those methods see the original current_process memory access rights.
-
class
windows.debug.Debugger(target)[source]¶ A debugger based on standard Win32 API. Handle :
- Standard BP (int3)
- Hardware-Exec BP (DrX)
- Memory BP (virtual_protect)
-
classmethod
attach(target)[source]¶ attach to
target(must be aWinProcess)Return type: DebuggerNote
-
classmethod
debug(path, args=None, dwCreationFlags=0, show_windows=False)[source]¶ Create a process and debug it.
Return type: Debugger
-
loop()[source]¶ Debugging loop: handle event / dispatch to breakpoint. Returns when all targets are dead/detached
-
add_bp(bp, addr=None, type=None, target=None)[source]¶ Add a breakpoint, bp can be:
- a
Breakpoint(addr and type must beNone) - any callable (addr and type must NOT be
None) (NON-TESTED)
If the
bptype isSTANDARD_BPorMEMORY_BREAKPOINT, target can beNone(all targets) or a process.If the
bptype isHARDWARE_EXEC_BP, target can beNone(all targets), a process or a thread.- a
-
del_bp(bp, targets=None)[source]¶ Delete a breakpoint, if targets is
None: delete it from all targets
-
single_step()[source]¶ Make the
current_threadsingle_step.Debugger.on_single_stepwill be called after that
-
get_memory_breakpoint_at(addr, process=None)[source]¶ Get the memory breakpoint that handle
addrReturn values are:
Falseif the page has no memory breakpoint (real fault)Noneif the page as memBP but None handleaddrbpthe MemBP that handleaddr
-
disable_all_memory_breakpoints(target=None)[source]¶ Restore all pages to their original access rights. If target is
None, usecurrent_processReturns: a mapping of all disabled breakpoints that must be passed to restore_all_memory_breakpoints()
-
restore_all_memory_breakpoints(data, target=None)[source]¶ Re-setup all memory breakpoints, affecting pages access rights. If target is
None, usecurrent_processdatais the result of the corresponding call todisable_all_memory_breakpoints()
-
DisabledMemoryBreakpoint(**kwds)[source]¶ A context-manager that disable all memory breakpoints and restore them on exit
-
get_exception_bitness(exc)[source]¶ Return the bitness in which the exception occured. Useful when debugingg a 32b process from a 64bits one
Returns: int– 32 or 64
-
static
kill_on_exit(choice)[source]¶ If set to True(default in Windows) will kill all attached process on thread exit. Otherwise, the thread detaches from all processes being debugged on exit.
See: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-debugsetprocesskillonexit
-
on_setup()[source]¶ Called on the first breakpoint event occuring in the debugger. This callback allow to setup hook / interact with the debugee when ready:
- If
on_setup()is overriden by a subclass it will be called andon_exception()will NOT be called for this event (first BP). - If
on_setup()is not defined the first BP will trigger anon_exception().
Note
see sample on_setup
- If
-
on_exception(exception)[source]¶ Called on exception event other that known breakpoint or requested single step.
exceptionis one of the following type:The default behaviour is to return
DBG_CONTINUEfor the known exception code andDBG_EXCEPTION_NOT_HANDLEDelse
-
on_single_step(exception)[source]¶ Called on requested single step
exceptionis one of the following type:There is no default implementation, if you use
Debugger.single_step()you should implementon_single_step
-
on_create_process(create_process)[source]¶ Called on create_process event
Parameters: create_process (CREATE_PROCESS_DEBUG_INFO) –
-
on_exit_process(exit_process)[source]¶ Called on exit_process event
Parameters: exit_process (EXIT_PROCESS_DEBUG_INFO) –
-
on_create_thread(create_thread)[source]¶ Called on create_thread event
Parameters: create_thread (CREATE_THREAD_DEBUG_INFO) –
-
on_exit_thread(exit_thread)[source]¶ Called on exit_thread event
Parameters: exit_thread (EXIT_THREAD_DEBUG_INFO) –
-
on_load_dll(load_dll)[source]¶ Called on load_dll event
Parameters: load_dll (LOAD_DLL_DEBUG_INFO) –
-
on_unload_dll(unload_dll)[source]¶ Called on unload_dll event
Parameters: unload_dll (UNLOAD_DLL_DEBUG_INFO) –
-
on_output_debug_string(debug_string)[source]¶ Called on debug_string event
Parameters: debug_string (OUTPUT_DEBUG_STRING_INFO) –
9.2. SymbolDebugger¶
Note
See sample SymbolDebugger
-
class
windows.debug.SymbolDebugger(*args, **kwargs)[source]¶ Bases:
windows.debug.debugger.DebuggerA debugger using the symbol API (hence PDB) for name resolution. To use PDB, a correct version of dbghelp should be configured as well as
_NT_SYMBOL_PATH. (See windows.debug.symbols – Using symbols)This debugger add a
current_resolvervariable (AProcessSymbolHandler) for thecurrent_process.
9.3. LocalDebugger¶
Note
See sample LocalDebugger
The Debugger is the base class to perform the debugging the current process.
It is based on VectoredException() (see VectoredException())
There is not much documentation for now as the code might change soon.
-
class
windows.debug.LocalDebugger[source]¶ A debugger interface around
AddVectoredExceptionHandler().Handle:
- Standard BP (int3)
- Hardware-Exec BP (DrX)
9.4. Breakpoint¶
Standard breakpoints types expect an address as argument.
An address can be:
When a breakpoint is hit, its trigger function is called with the debugger and a
DEBUG_EXECEPTION_EVENT structure as argument.
-
class
windows.debug.HXBreakpoint(addr)[source]¶ An hardware-execution breakpoint (type ==
HARDWARE_EXEC_BP)-
trigger(dbg, exception)¶ Called when breakpoint is hit
-
-
class
windows.debug.MemoryBreakpoint(addr, size=None, events=None)[source]¶ A memory breakpoint (type ==
MEMORY_BREAKPOINT)
Note
MemoryBreakpoint are triggered based on the fault address only (as I don’t know a way to get the size of the read/write causing the fault without embedding a disassembler).
This means that a MEMBP at address X won’t be triggered by a write of size 4 at address X - 1. it’s sad I know.
-
class
windows.debug.FunctionCallBP(addr)[source]¶ A Breakpoint that allow to trigger at the return of a function
-
__init__(addr)¶ x.__init__(…) initializes x; see help(type(x)) for signature
-
break_on_ret(dbg, exception)[source]¶ Setup a breakpoint at the return address of the function, this breakpoint will call
ret_trigger()
-
get_ret_addr(dbg, exception)[source]¶ Get the return address of the current target, only valid in the trigger() function.
-
ret_trigger(dbg, exception)[source]¶ Called at the return of the function if
break_on_ret()was called
-
trigger(dbg, exception)¶ Called when breakpoint is hit
-
Note
See sample windows.debug.FunctionCallBP
-
class
windows.debug.FunctionBP(addr=None, target=None)[source]¶ A breakpoint that accepts a function from
windows.winproxyand able to:- Extract the arguments of the functions
- Break at the return of the function
-
__init__(addr=None, target=None)¶ x.__init__(…) initializes x; see help(type(x)) for signature
-
arguments(dbg)¶ TEST PARAM DICT
-
break_on_ret(dbg, exception)¶ Setup a breakpoint at the return address of the function, this breakpoint will call
ret_trigger()
-
extract_arguments(cproc, cthread)¶ Extracts the functions parameters in an
OrderedDict
-
get_ret_addr(dbg, exception)¶ Get the return address of the current target, only valid in the trigger() function.
-
ret_trigger(dbg, exception)¶ Called at the return of the function if
break_on_ret()was called
-
trigger(dbg, exception)¶ Called when breakpoint is hit
Note
See sample windows.debug.FunctionBP
9.5. windows.debug.symbols – Using symbols¶
The windows.debug.symbols module provide classes to load PDB and resolve name/address.
In its current state, this module does not handle types.
Note
See samples Symbols
9.5.1. Configuration¶
In order to be able to automatically download PDB and parse remote _NT_SYMBOL_PATH, a debug version of the DLL dbghelp.dll must be used.
(See MSDN: DbgHelp Versions)
As it is NOT recommended to replace system32/dbghelp.dll, its path must be provided to PythonForWindows.
This path must be provided before any call to the dbghelp.dll APIs.
Also, the symsrv.dll DLL should be present in the same directory as dbghelp.dll (See SymSrv Installation)
There is 2 ways to pass this information to PythonForWindows:
- Using the function
set_dbghelp_path()
- Using the environment variable
PFW_DBGHELP_PATH
- If this variable exists it will simply trigger a call to
set_dbghelp_path(PFW_DBGHELP_PATH)
If the given path is a directory, the final path will be computer as path\<current_process_bitness>\dbghelp.dll.
This allow to use the same script (or environment variable) transparently in bot 32b & 64b python interpreters.
Note
For example, on my computer my setup is done through the environment variable: PFW_DBGHELP_PATH=D:\pysym\bin
This directory have the following layout:
$ tree /A /F %PFW_DBGHELP_PATH%D:\PYSYM\BIN| symsrv.yes|+---32| dbghelp.dll| symsrv.dll|\---64dbghelp.dllsymsrv.dll
9.5.2. Helpers¶
-
windows.debug.symbols.set_dbghelp_path(path)[source]¶ Set the path of the
dbghelp.dllfile to use. It allow to configure a different version of the DLL handling PDB downloading.If
pathis a directory, the finaldbghelp.dllwill be computed aspath\<current_process_bitness>\dbghelp.dll.This allow to use the same script transparently in both 32b & 64b python interpreters.
-
class
windows.debug.symbols.SymbolEngine[source]¶ Represent the global symbol engine. Just a proxy to get/set global engine options
Its instance can be accessed using
windows.debug.symbols.engineExemple:
>>> windows.debug.symbols.engine.options 6L >>> windows.debug.symbols.engine.options = gdef.SYMOPT_UNDNAME >>> windows.debug.symbols.engine.options 2L
-
options¶ The options of the Symbol engine (see options)
Note
Default options are:
gdef.SYMOPT_DEFERRED_LOADS + gdef.SYMOPT_UNDNAME
-
9.5.3. VirtualSymbolHandler¶
-
class
windows.debug.symbols.VirtualSymbolHandler(search_path=None)[source]¶ Bases:
windows.debug.symbols.SymbolHandlerA 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
-
__getitem__(name_or_addr)¶ Resolve
name_or_addr.If its an int -> Return the
SymbolInfoat the address. If its a string -> Return theSymbolInfocorresponding to the symbol nameReturns: SymbolInfoNote
__getitem__is an alias forresolve()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'
-
load(path, name=None, addr=0, size=0, data=None, flags=0)¶ An alias for
VirtualSymbolHandler.load_file()
-
load_file(path, name=None, addr=0, size=0, data=None, flags=0)¶ Load the module
pathataddrReturns: SymbolModule– The loaded module
-
load_module(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 afile_handleor the directpathof the file to load.Returns: SymbolModule– The loaded moduleNote
The logic of
SymLoadModuleExseems somewhat strange about the naming of the loaded module. A custom modulenameis only taken into account if the file is passed via a File handle. To make it more intuitive, if this function is call with apathandnameand nofile_handle, it will open the path and directly callSymLoadModuleExwith a file handle and a name.
-
modules¶ The list of loaded modules
Returns: [ SymbolModule] – A list of modules
-
refresh()[source]¶ Do nothing for a
VirtualSymbolHandler
-
resolve(name_or_addr)¶ Resolve
name_or_addr.If its an int -> Return the
SymbolInfoat the address. If its a string -> Return theSymbolInfocorresponding to the symbol nameReturns: SymbolInfoNote
__getitem__is an alias forresolve()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'
-
search(mask, mod=0, tag=0, options=SYMSEARCH_ALLITEMS(0x8), callback=None)¶ Search the symbols matching
mask(Windbglike).Returns: [ SymbolInfo] – A list ofSymbolInfo>>> sh = windows.debug.symbols.VirtualSymbolHandler() >>> mod = sh.load_file(r"c:\windows\system32\kernelbase.dll") >>> sh.search("kernelbase!CreateFile*") [<SymbolInfoA name="CreateFileInternal" addr=0x100f2120 tag=SymTagFunction>, <SymbolInfoA name="CreateFileMoniker" addr=0x10117d80 tag=SymTagFunction>, <SymbolInfoA name="CreateFile2" addr=0x1011e690 tag=SymTagFunction>, ...]
-
unload(addr)¶ Unload the module at
addr
-
9.5.4. ProcessSymbolHandler¶
-
class
windows.debug.symbols.ProcessSymbolHandler(process, search_path=None, invade_process=False)[source]¶ Bases:
windows.debug.symbols.SymbolHandler-
__getitem__(name_or_addr)¶ Resolve
name_or_addr.If its an int -> Return the
SymbolInfoat the address. If its a string -> Return theSymbolInfocorresponding to the symbol nameReturns: SymbolInfoNote
__getitem__is an alias forresolve()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'
-
load(name)[source]¶ Load the
SymbolModuleassociated with the loaded modulename(as found in the PEB)Returns: SymbolModuleExemple:
>>> 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>
-
load_file(path, name=None, addr=0, size=0, data=None, flags=0)¶ Load the module
pathataddrReturns: SymbolModule– The loaded module
-
load_module(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 afile_handleor the directpathof the file to load.Returns: SymbolModule– The loaded moduleNote
The logic of
SymLoadModuleExseems somewhat strange about the naming of the loaded module. A custom modulenameis only taken into account if the file is passed via a File handle. To make it more intuitive, if this function is call with apathandnameand nofile_handle, it will open the path and directly callSymLoadModuleExwith a file handle and a name.
-
modules¶ The list of loaded modules
Returns: [ SymbolModule] – A list of modules
-
refresh()[source]¶ Update the list of loaded modules to match the modules present in the target process
Note
This function only call 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>, ...]
-
resolve(name_or_addr)¶ Resolve
name_or_addr.If its an int -> Return the
SymbolInfoat the address. If its a string -> Return theSymbolInfocorresponding to the symbol nameReturns: SymbolInfoNote
__getitem__is an alias forresolve()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'
-
search(mask, mod=0, tag=0, options=SYMSEARCH_ALLITEMS(0x8), callback=None)¶ Search the symbols matching
mask(Windbglike).Returns: [ SymbolInfo] – A list ofSymbolInfo>>> sh = windows.debug.symbols.VirtualSymbolHandler() >>> mod = sh.load_file(r"c:\windows\system32\kernelbase.dll") >>> sh.search("kernelbase!CreateFile*") [<SymbolInfoA name="CreateFileInternal" addr=0x100f2120 tag=SymTagFunction>, <SymbolInfoA name="CreateFileMoniker" addr=0x10117d80 tag=SymTagFunction>, <SymbolInfoA name="CreateFile2" addr=0x1011e690 tag=SymTagFunction>, ...]
-
unload(addr)¶ Unload the module at
addr
-
9.5.5. SymbolModule¶
-
class
windows.debug.symbols.SymbolModule(resolver)[source]¶ Bases:
windows.generated_def.winstructs._IMAGEHLP_MODULE64Represent a loaded symbol module (see MSDN IMAGEHLP_MODULE64)
Note
This represent a module in the
symbol spacefor symbol resolution. This can be completly virtual (particularly in the case ofVirtualSymbolHandler-
addr¶ The load address of the module
-
name¶ The name of the module
-
path¶ The full path and file name of the file from which symbols were loaded.
-
pdb¶ 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'
-
type¶ The type of module (
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.
-
9.5.6. SymbolInfo¶
-
windows.debug.symbols.SymbolInfo¶ alias of
windows.debug.symbols.SymbolInfoA
-
class
windows.debug.symbols.SymbolInfoA[source]¶ Bases:
windows.generated_def.winstructs._SYMBOL_INFO,windows.debug.symbols.SymbolInfoBaseRepresent a Symbol. This class in based on the class 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 <SymbolInfoA 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'
-
displacement¶ The displacement between the addresse and the start of the symbol (name)
-
CHAR_TYPE¶ alias of
ctypes.c_char
-
__int__()¶ An alias for
addr
-
__str__()¶ The fullname of the symbol in the windbg format
mod!sym+displacement
-
addr¶ The address of the symbol
-
fullname¶ The fullname of the symbol in the windbg format
mod!sym+displacement
-
module¶ The module containing the symbol
Type: SymbolModule
-
name¶ The name of the symbol
-
start¶ The address of the start of the symbol If the symbol include a displacement, it is not taken into account
-
tag¶ The Tag of the module
Type: SymTagEnum
-