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: Debugger
Note
-
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
bp
type isSTANDARD_BP
orMEMORY_BREAKPOINT
, target can beNone
(all targets) or a process.If the
bp
type 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_thread
single_step
.Debugger.on_single_step
will be called after that
-
get_memory_breakpoint_at
(addr, process=None)[source]¶ Get the memory breakpoint that handle
addr
Return values are:
False
if the page has no memory breakpoint (real fault)None
if the page as memBP but None handleaddr
bp
the MemBP that handleaddr
-
disable_all_memory_breakpoints
(target=None)[source]¶ Restore all pages to their original access rights. If target is
None
, usecurrent_process
Returns: 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_process
data
is 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.
exception
is one of the following type:The default behaviour is to return
DBG_CONTINUE
for the known exception code andDBG_EXCEPTION_NOT_HANDLED
else
-
on_single_step
(exception)[source]¶ Called on requested single step
exception
is 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.Debugger
A 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_resolver
variable (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.winproxy
and 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.dll
file to use. It allow to configure a different version of the DLL handling PDB downloading.If
path
is a directory, the finaldbghelp.dll
will 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.engine
Exemple:
>>> 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.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
-
__getitem__
(name_or_addr)¶ Resolve
name_or_addr
.If its an int -> Return the
SymbolInfo
at the address. If its a string -> Return theSymbolInfo
corresponding to the symbol nameReturns: SymbolInfo
Note
__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
path
ataddr
Returns: 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_handle
or the directpath
of the file to load.Returns: SymbolModule
– The loaded moduleNote
The logic of
SymLoadModuleEx
seems somewhat strange about the naming of the loaded module. A custom modulename
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 apath
andname
and nofile_handle
, it will open the path and directly callSymLoadModuleEx
with 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
SymbolInfo
at the address. If its a string -> Return theSymbolInfo
corresponding to the symbol nameReturns: SymbolInfo
Note
__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
(Windbg
like).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
SymbolInfo
at the address. If its a string -> Return theSymbolInfo
corresponding to the symbol nameReturns: SymbolInfo
Note
__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
SymbolModule
associated with the loaded modulename
(as found in the PEB)Returns: 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>
-
load_file
(path, name=None, addr=0, size=0, data=None, flags=0)¶ Load the module
path
ataddr
Returns: 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_handle
or the directpath
of the file to load.Returns: SymbolModule
– The loaded moduleNote
The logic of
SymLoadModuleEx
seems somewhat strange about the naming of the loaded module. A custom modulename
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 apath
andname
and nofile_handle
, it will open the path and directly callSymLoadModuleEx
with 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
SymbolInfo
at the address. If its a string -> Return theSymbolInfo
corresponding to the symbol nameReturns: SymbolInfo
Note
__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
(Windbg
like).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_MODULE64
Represent a loaded symbol module (see MSDN IMAGEHLP_MODULE64)
Note
This represent a module in the
symbol space
for 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.SymbolInfoBase
Represent 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
-