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)
__init__(target)[source]

target must be a debuggable WinProcess.

classmethod attach(target)[source]

attach to target (must be a WinProcess)

Return type:Debugger
detach(target=None)[source]

Detach from all debugged processes or process target

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 be None)
  • any callable (addr and type must NOT be None) (NON-TESTED)

If the bp type is STANDARD_BP or MEMORY_BREAKPOINT, target can be None (all targets) or a process.

If the bp type is HARDWARE_EXEC_BP, target can be None (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 handle addr
  • bp the MemBP that handle addr
disable_all_memory_breakpoints(target=None)[source]

Restore all pages to their original access rights. If target is None, use current_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, use current_process

data is the result of the corresponding call to disable_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:

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 and DBG_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 implement on_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) –
on_rip(rip_info)[source]

Called on rip_info event

Parameters:rip_info (RIP_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 (A ProcessSymbolHandler) for the current_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)
add_bp(bp, target=None)[source]

Add a breakpoint, bp is a “class:Breakpoint

If the bp type is STANDARD_BP, target must be None.

If the bp type is HARDWARE_EXEC_BP, target can be None (all threads), or some threads of the process

del_bp(bp, targets=None)[source]

Delete a breakpoint

get_exception_code()[source]

Return ExceptionCode of current exception

get_exception_context()[source]

Return context of current exception

on_exception(exc)[source]

Called on exception

single_step()[source]

Make the current thread to single step

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 when DLL 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.Breakpoint(addr)[source]

An standard (Int3) breakpoint (type == STANDARD_BP)

trigger(dbg, exception)[source]

Called when breakpoint is hit

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)

__init__(addr, size=None, events=None)[source]

size: the size of the memory breakpoint.

events: a string representing the events that interest the BP (any of “RWX”)

trigger(dbg, exception)[source]

Called when breakpoint is hit

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

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
|
\---64
dbghelp.dll
symsrv.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 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.

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 the SymbolInfo corresponding to the symbol name

Returns: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'
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 at addr

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 a file_handle or the direct path 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 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.

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 the SymbolInfo corresponding to the symbol name

Returns: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'
search(mask, mod=0, tag=0, options=SYMSEARCH_ALLITEMS(0x8), callback=None)

Search the symbols matching mask (Windbg like).

Returns:[SymbolInfo] – A list of SymbolInfo
>>> 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 the SymbolInfo corresponding to the symbol name

Returns: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'
load(name)[source]

Load the SymbolModule associated with the loaded module name (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 at addr

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 a file_handle or the direct path 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 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.

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 the SymbolInfo corresponding to the symbol name

Returns: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'
search(mask, mod=0, tag=0, options=SYMSEARCH_ALLITEMS(0x8), callback=None)

Search the symbols matching mask (Windbg like).

Returns:[SymbolInfo] – A list of SymbolInfo
>>> 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 of VirtualSymbolHandler

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