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 debug(path, args=None, dwCreationFlags=0, show_windows=False)[source]¶
Create a process and debug it.
- Return type:
- 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.
- 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(target=None)[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
- 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:
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:
An
int
A
str
of form (breakpoint will be put whenDLL
is loaded):
"DLL!ApiName"
"DLL!Offset"
where offset is a int (“16”, “0x10”, ..)
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)¶
- 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)¶
- 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
- property 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:
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 name- Returns:
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'
- handle¶
The handle of the symbol handler
- 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 module
Note
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.
- property 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 name- Returns:
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*") [<SymbolInfoW name="CreateFileInternal" addr=0x100f2120 tag=SymTagFunction>, <SymbolInfoW name="CreateFileMoniker" addr=0x10117d80 tag=SymTagFunction>, <SymbolInfoW 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:
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 name- Returns:
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'
- handle¶
The handle of the symbol handler
- load(name)[source]¶
Load the
SymbolModule
associated with the loaded modulename
(as found in the PEB)- Returns:
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 module
Note
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.
- property 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 name- Returns:
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*") [<SymbolInfoW name="CreateFileInternal" addr=0x100f2120 tag=SymTagFunction>, <SymbolInfoW name="CreateFileMoniker" addr=0x10117d80 tag=SymTagFunction>, <SymbolInfoW 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:
_IMAGEHLP_MODULEW64
Represent a loaded symbol module (see MSDN _IMAGEHLP_MODULEW64)
Note
This represent a module in the
symbol space
for symbol resolution. This can be completly virtual (particularly in the case ofVirtualSymbolHandler
- property addr¶
The load address of the module
- property name¶
The name of the module
- property path¶
The full path and file name of the file from which symbols were loaded.
- property 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'
- property 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
SymbolInfoW
- class windows.debug.symbols.SymbolInfoA[source]¶
Bases:
_SYMBOL_INFO
,SymbolInfoBase
- displacement¶
The displacement between the addresse and the start of the symbol (name)
- CHAR_TYPE¶
alias of
c_char
- __int__()¶
An alias for
addr
- __str__()¶
The fullname of the symbol in the windbg format
mod!sym+displacement
- property addr¶
The address of the symbol
- property fullname¶
The fullname of the symbol in the windbg format
mod!sym+displacement
- property module¶
The module containing the symbol
- Type:
- property name¶
The name of the symbol
- property start¶
The address of the start of the symbol If the symbol include a displacement, it is not taken into account
- property tag¶
The Tag of the module
- Type: