Debugging

Debugging operations are performed by the Debug class. You can receive notification of debugging events by passing a custom event handler to the Debug object when creating it - each event is represented by an Event object. Custom event handlers can also be subclasses of the EventHandler class.

Debug objects can also set breakpoints, watches and hooks and support the use of labels.

The Debug class

A Debug object provides methods to launch new processes, attach to and detach from existing processes, and manage breakpoints. It also contains a System snapshot to instrument debugged processes - this snapshot is updated automatically for processes being debugged.

When you’re finished using the Debug object, you must either call its stop() method from a finally block, or put the Debug object inside a with statement.

Note

In previous examples we have used a System.request_debug_privileges() call to get debug privileges. When using the Debug class we don’t need to do that - it’s taken care of automatically in the constructor.

Example #1: starting a new process and waiting for it to finish

Download


from winappdbg import Debug

import sys

# Instance a Debug object.
debug = Debug()
try:

    # Start a new process for debugging.
    debug.execv( sys.argv[ 1 : ] )

    # Wait for the debugee to finish.
    debug.loop()

# Stop the debugger.
finally:
    debug.stop()

Example #2: attaching to a process and waiting for it to finish

Download


from winappdbg import Debug

import sys

# Get the process ID from the command line.
pid = int( sys.argv[1] )

# Instance a Debug object.
debug = Debug()
try:

    # Attach to a running process.
    debug.attach( pid )

    # Wait for the debugee to finish.
    debug.loop()

# Stop the debugger.
finally:
    debug.stop()

Example #3: attaching to a process by filename

Download


from winappdbg import Debug

import sys

# Get the process filename from the command line.
filename = sys.argv[1]

# Instance a Debug object.
debug = Debug()
try:

    # Lookup the currently running processes.
    debug.system.scan_processes()

    # For all processes that match the requested filename...
    for ( process, name ) in debug.system.find_processes_by_filename( filename ):
        print process.get_pid(), name

        # Attach to the process.
        debug.attach( process.get_pid() )

    # Wait for all the debugees to finish.
    debug.loop()

# Stop the debugger.
finally:
    debug.stop()

Example #4: killing the debugged process when the debugger is closed

Download


from winappdbg import Debug

import sys

# Instance a Debug object, set the kill on exit property to True.
debug = Debug( bKillOnExit = True )

# The user can stop debugging with Control-C.
try:
    print "Hit Control-C to stop debugging..."

    # Start a new process for debugging.
    debug.execv( sys.argv[ 1 : ] )

    # Wait for the debugee to finish.
    debug.loop()

# If the user presses Control-C...
except KeyboardInterrupt:
    print "Interrupted by user."

    # Stop debugging. This kills all debugged processes.
    debug.stop()

The interactive debugger

The Debug class also contains an implementation of a simple console debugger. It can come in handy when testing your scripts, or to manually handle unexpected situations.

Example #5: running an interactive debugger session

Download


from winappdbg import Debug

def simple_debugger( argv ):

    # Instance a Debug object.
    debug = Debug()
    try:

        # Start a new process for debugging.
        debug.execv( argv )

        # Launch the interactive debugger.
        debug.interactive()

    # Stop the debugger.
    finally:
        debug.stop()

The Event class

So far we have seen how to attach to or start processes. But a debugger also needs to react to events that happen in the debugee, and this is done by passing a callback function as the eventHandler parameter when instancing the Debug object. This callback, when called, will receive as parameter an Event object which describes the event and contains a reference to the Debug object itself.

Every Event object has the following set of common methods to get information from them:

Method Description
get_event_name Returns the name of the event.
get_event_description Returns a user-friendly description of the event.
get_event_code Returns the event code constant, as defined by the Win32 API.
get_pid Returns the ID of the process where the event occurred.
get_tid Returns the ID of the thread where the event occurred.
get_process Returns the Process object.
get_thread Returns the Thread object.

Then depending on the event type, you can get more information that’s specific to each type.

Method Applicable events Description
get_filename Process creation and destruction, DLL library load and unload. Returns the filename of the EXE or DLL.
get_exit_code Process and thread destruction. Returns the exit code of the process or thread.
get_exception_name Exceptions. Returns the Win32 API constant name for the exception code.
get_exception_code Exceptions. Returns the Win32 API constant value for the exception code.
get_exception_address Exceptions. Returns the memory address where the exception has occurred. For exceptions not involving memory operations, the current execution pointer is returned.
is_system_defined_exception Exceptions. Returns True if the exception was caused by the operating system rather than the application code. Most notably, one such exception is always raised when attaching to a process, and then running a process from the debugger (right after process initialization is complete).
is_first_chance Exceptions. If True, the exception hasn’t been passed yet to the exception handlers of the debuggee. If False, the exception was passed to the exception handlers but none of them could handle it.
is_nested Exceptions. If True, the exception was raised when handing at least one more exception. Many exceptions can be nested that way. Call get_nexted_exceptions to get a list of those nested exceptions.
get_fault_address Exceptions caused by invalid memory access. Returns the memory address where the invalid access has occurred.

Example #6: handling debug events

Download


from winappdbg import Debug, HexDump, win32

def my_event_handler( event ):

    # Get the process ID where the event occured.
    pid = event.get_pid()

    # Get the thread ID where the event occured.
    tid = event.get_tid()

    # Find out if it's a 32 or 64 bit process.
    bits = event.get_process().get_bits()

    # Get the value of EIP at the thread.
    address = event.get_thread().get_pc()

    # Get the event name.
    name = event.get_event_name()

    # Get the event code.
    code = event.get_event_code()

    # If the event is an exception...
    if code == win32.EXCEPTION_DEBUG_EVENT:

        # Get the exception user-friendly description.
        name = event.get_exception_description()

        # Get the exception code.
        code = event.get_exception_code()

        # Get the address where the exception occurred.
        try:
            address = event.get_fault_address()
        except NotImplementedError:
            address = event.get_exception_address()

    # If the event is a process creation or destruction,
    # or a DLL being loaded or unloaded...
    elif code in ( win32.CREATE_PROCESS_DEBUG_EVENT,
                   win32.EXIT_PROCESS_DEBUG_EVENT,
                   win32.LOAD_DLL_DEBUG_EVENT,
                   win32.UNLOAD_DLL_DEBUG_EVENT ):

        # Get the filename.
        filename = event.get_filename()
        if filename:
            name = "%s [%s]" % ( name, filename )

    # Show a descriptive message to the user.
    print "-" * 79
    format_string = "%s (0x%s) at address 0x%s, process %d, thread %d"
    message = format_string % ( name,
                                HexDump.integer(code, bits),
                                HexDump.address(address, bits),
                                pid,
                                tid )
    print message

def simple_debugger( argv ):

    # Instance a Debug object, passing it the event handler callback.
    debug = Debug( my_event_handler, bKillOnExit = True )
    try:

        # Start a new process for debugging.
        debug.execv( argv )

        # Wait for the debugee to finish.
        debug.loop()

    # Stop the debugger.
    finally:
        debug.stop()

The Crash and CrashDAO classes

Crashes are exceptions a program can’t recover from (also known as second-chance exceptions or last chance exceptions). A crash dump is a collection of information from a crash in a program that can (hopefully!) help you reproduce or fix the bug that caused it in the first place.

WinAppDbg provides the Crash class to generate and manipulate crash dumps. When instancing a Crash object only the most basic information is collected, you have to call the fetch_extra_data method to collect more data. This lets you control which information to gather and when - for example you may be interested in gathering more information only under certain conditions, or for certain kinds of exceptions.

Crash objects also support heuristic signatures that can be used to try to determine whether two crashes were caused by the same bug, in order to discard duplicates. It can also try to guess how exploitable would the found crashes be, using similar heuristics to those of !exploitable.

Now, the next step would be storing the crash dump somewhere for later examination. The most crude way to do this is using the standard pickle module, or similar modules like cerealizer. This is easy and guaranteed to work, but not very comfortable! Crash dumps stored that way are hard to read outside Python.

A more flexible way to store crash dumps is using the CrashDAO class. It uses SQLAlchemy to connect to any supported SQL database, create the required tables if needed, and store multiple crash dumps in it. This is the preferred method, since it’s easier to access and manipulate the information outside Python, and you can store crashes from multiple machines into the same database.

Old versions of WinAppDbg (1.4 and older) supported DBM databases through the CrashContainer class, SQLite databases with the CrashTable class, and SQL Server databases with the CrashTableMSSQL class. They are now deprecated and, while still present for backwards compatibility (for the time being) its use is not recommended.

Example #7: saving crash dumps

Download


from winappdbg import win32, Debug, HexDump, Crash

try:
    from winappdbg import CrashDAO
except ImportError:
    raise ImportError("Error: SQLAlchemy is not installed!")

def my_event_handler( event ):

    # Get the event name.
    name = event.get_event_name()

    # Get the event code.
    code = event.get_event_code()

    # Get the process ID where the event occured.
    pid = event.get_pid()

    # Get the thread ID where the event occured.
    tid = event.get_tid()

    # Get the value of EIP at the thread.
    pc = event.get_thread().get_pc()

    # Show something to the user.
    bits = event.get_process().get_bits()
    format_string = "%s (%s) at address %s, process %d, thread %d"
    message = format_string % ( name,
                                HexDump.integer(code, bits),
                                HexDump.address(pc, bits),
                                pid,
                                tid )
    print message

    # If the event is a crash...
    if code == win32.EXCEPTION_DEBUG_EVENT and event.is_last_chance():
        print "Crash detected, storing crash dump in database..."

        # Generate a minimal crash dump.
        crash = Crash( event )

        # You can turn it into a full crash dump (recommended).
        # crash.fetch_extra_data( event, takeMemorySnapshot = 0 ) # no memory dump
        # crash.fetch_extra_data( event, takeMemorySnapshot = 1 ) # small memory dump
        crash.fetch_extra_data( event, takeMemorySnapshot = 2 ) # full memory dump

        # Connect to the database. You can use any URL supported by SQLAlchemy.
        # For more details see the reference documentation.
        dao = CrashDAO( "sqlite:///crashes.sqlite" )
        #dao = CrashDAO( "mysql+MySQLdb://root:toor@localhost/crashes" )

        # Store the crash dump in the database.
        dao.add( crash )

        # If you do this instead, heuristics are used to detect duplicated
        # crashes so they aren't added to the database.
        # dao.add( crash, allow_duplicates = False )

        # You can also launch the interactive debugger from here. Try it! :)
        # event.debug.interactive()

        # Kill the process.
        event.get_process().kill()

def simple_debugger( argv ):

    # Instance a Debug object, passing it the event handler callback.
    debug = Debug( my_event_handler, bKillOnExit = True )
    try:

        # Start a new process for debugging.
        debug.execv( argv )

        # Wait for the debugee to finish.
        debug.loop()

    # Stop the debugger.
    finally:
        debug.stop()

The EventHandler class

Using a callback function is not very flexible when your code is too large. For that reason, the EventHandler class is provided.

Instead of a function, you can define a subclass of EventHandler where each method of your class should match an event - for example, to receive notification on new DLL libraries being loaded, define the load_dll method in your class. If you don’t want to receive notifications on a specific event, simply don’t define the method in your class.

These are the most important event notification methods:

Notification name What does it mean? When is it received?
create_process The debugger has attached to a new process. When attaching to a process, when starting a new process for debugging, or when the debugee starts a new process and the bFollow flag was set to True.
exit_process A debugee process has finished executing. When a process terminates by itself or when the Process.kill method is called.
create_thread A debugee process has started a new thread. When the process creates a new thread or when the Process.start_thread method is called.
exit_thread A thread in a debugee process has finished executing. When a thread terminates by itself or when the Thread.kill method is called.
load_dll A module in a debugee process was loaded. When a process loads a DLL module by itself or when the Process.inject_dll method is called.
unload_dll A module in a debugee process was unloaded. When a process unloads a DLL module by itself.
exception An exception was raised by the debugee. When a hardware fault is triggered or when the process calls RaiseException().
output_string The debuggee has sent a debug string. When the process calls OutputDebugString().

The event handler can also receive notifications for specific exceptions as a different event. When you define the method for that exception, it takes precedence over the more generic exception method.

These are the most important exception notification methods:

Notification name What does it mean? When is it received
access_violation An access violation exception was raised by the debugee. When the debuggee tries to access invalid memory.
ms_vc_exception A C++ exception was raised by the debugee. When the debuggee calls RaiseException() with a custom exception code. This is what the implementation of throw() of the Visual Studio runtime does.
breakpoint A breakpoint exception was raised by the debugee. When a hardware fault is triggered by the int3 opcode, when the process calls DebugBreak(), or when a code breakpoint set by your program is triggered.
single_step A single step exception was raised by the debugee. When a hardware fault is triggered by the trap flag or the icebp opcode, or when a hardware breakpoint set by your program is triggered.
guard_page A guard page exception was raised by the debugee. When a guard page is hit or when a page breakpoint set by your program is triggered.

In addition to all this, the EventHandler class provides a simple method for API hooking: the apiHooks class property. This property is a dictionary of tuples, specifying which API calls to hook on what DLL libraries, and what parameters does each call take (using ctypes definitions). That’s it! The EventHandler class will automatically hooks this APIs for you when the corresponding library is loaded, and a method of your subclass will be called when entering and leaving the API function.

Note

One thing to be careful with when hooking API functions: all pointers should be declared as having the void type. Otherwise ctypes gets too “helpful” and tries to access the memory pointed to by them… and crashes, since those pointers only work in the debugged process.

Example #8: tracing execution

Download


from winappdbg import Debug, EventHandler, HexDump


class MyEventHandler( EventHandler ):


    # Create process events go here.
    def create_process( self, event ):

        # Start tracing the main thread.
        event.debug.start_tracing( event.get_tid() )


    # Create thread events go here.
    def create_thread( self, event ):

        # Start tracing the new thread.
        event.debug.start_tracing( event.get_tid() )


    # Single step events go here.
    def single_step( self, event ):

        # Show the user where we're running.
        thread = event.get_thread()
        pc     = thread.get_pc()
        code   = thread.disassemble( pc, 0x10 ) [0]
        bits   = event.get_process().get_bits()
        print "%s: %s" % ( HexDump.address(code[0], bits), code[2].lower() )


def simple_debugger( argv ):

    # Instance a Debug object using the "with" statement.
    # Note how we don't need to call "debug.stop()" anymore.
    with Debug( MyEventHandler(), bKillOnExit = True ) as debug:

        # Start a new process for debugging.
        debug.execv( argv )

        # Wait for the debugee to finish.
        debug.loop()


Example #9: intercepting API calls

Download

from winappdbg.win32 import *  # NOQA


class MyEventHandler( EventHandler ):


    # Here we set which API calls we want to intercept.
    apiHooks = {

        # Hooks for the kernel32 library.
        'kernel32.dll' : [

            #  Function            Parameters
            ( 'CreateFileA'     , (PVOID, DWORD, DWORD, PVOID, DWORD, DWORD, HANDLE) ),
            ( 'CreateFileW'     , (PVOID, DWORD, DWORD, PVOID, DWORD, DWORD, HANDLE) ),

        ],

        # Hooks for the advapi32 library.
        'advapi32.dll' : [

            #  Function            Parameters
            ( 'RegCreateKeyExA' , (HKEY, PVOID, DWORD, PVOID, DWORD, REGSAM, PVOID, PVOID, PVOID) ),
            ( 'RegCreateKeyExW' , (HKEY, PVOID, DWORD, PVOID, DWORD, REGSAM, PVOID, PVOID, PVOID) ),

        ],
    }


    # Now we can simply define a method for each hooked API.
    # Methods beginning with "pre_" are called when entering the API,
    # and methods beginning with "post_" when returning from the API.


    def pre_CreateFileA( self, event, ra, lpFileName, dwDesiredAccess,
             dwShareMode, lpSecurityAttributes, dwCreationDisposition,
                                dwFlagsAndAttributes, hTemplateFile ):

        self.__print_opening_ansi( event, "file", lpFileName )

    def pre_CreateFileW( self, event, ra, lpFileName, dwDesiredAccess,
             dwShareMode, lpSecurityAttributes, dwCreationDisposition,
                                dwFlagsAndAttributes, hTemplateFile ):

        self.__print_opening_unicode( event, "file", lpFileName )

    def pre_RegCreateKeyExA( self, event, ra, hKey, lpSubKey, Reserved,
                                        lpClass, dwOptions, samDesired,
                                       lpSecurityAttributes, phkResult,
                                                     lpdwDisposition ):

        self.__print_opening_ansi( event, "key", lpSubKey )

    def pre_RegCreateKeyExW( self, event, ra, hKey, lpSubKey, Reserved,
                                        lpClass, dwOptions, samDesired,
                                       lpSecurityAttributes, phkResult,
                                                     lpdwDisposition ):

        self.__print_opening_unicode( event, "key", lpSubKey )


    def post_CreateFileA( self, event, retval ):
        self.__print_success( event, retval )

    def post_CreateFileW( self, event, retval ):
        self.__print_success( event, retval )

    def post_RegCreateKeyExA( self, event, retval ):
        self.__print_reg_success( event, retval )

    def post_RegCreateKeyExW( self, event, retval ):
        self.__print_reg_success( event, retval )


    # Some helper private methods...

    def __print_opening_ansi( self, event, tag, pointer ):
        string = event.get_process().peek_string( pointer )
        tid    = event.get_tid()
        print  "%d: Opening %s: %s" % (tid, tag, string)

    def __print_opening_unicode( self, event, tag, pointer ):
        string = event.get_process().peek_string( pointer, fUnicode = True )
        tid    = event.get_tid()
        print  "%d: Opening %s: %s" % (tid, tag, string)

    def __print_success( self, event, retval ):
        tid = event.get_tid()
        if retval:
            print "%d: Success: %x" % (tid, retval)
        else:
            print "%d: Failed!" % tid

    def __print_reg_success( self, event, retval ):
        tid = event.get_tid()
        if retval:
            print "%d: Failed! Error code: %x" % (tid, retval)
        else:
            print "%d: Success!" % tid


The EventSift class

If you’re debugging more than one process at a time, keeping track of everything can be trickier. For that reason there’s also a class called EventSift. You can wrap your EventHandler class with it to create a new EventHandler instance for each debugged process.

That way, your EventHandler can be written as if only a single process was being debugged, but you can attach to as many processes as you want. Each EventHandler will only “see” its own debugee.

Example #10: sifting events per process

Download



# This class was written assuming only one process is attached.
# If you used it directly it would break when attaching to another
# process, or when a child process is spawned.
class MyEventHandler (EventHandler):

    def create_process(self, event):
        self.first = True
        self.name = event.get_process().get_filename()
        print "Attached to %s" % self.name

    def breakpoint(self, event):
        if self.first:
            self.first = False
            print "First breakpoint reached at %s" % self.name

    def exit_process(self, event):
        print "Detached from %s" % self.name


# Now when debugging we use the EventForwarder to be able to work with
# multiple processes while keeping our code simple. :)
def simple_debugger():

    handler = EventSift(MyEventHandler)
    #handler = MyEventHandler()  # try uncommenting this line...
    with Debug(handler) as debug:
        debug.execl("calc.exe")
        debug.execl("notepad.exe")
        debug.execl("charmap.exe")
        debug.loop()


Breakpoints, watches and hooks

A Debug object provides a small set of methods to set breakpoints, watches and hooks. These methods in turn use an underlying, more sophisticated interface that is described at the wiki page HowBreakpointsWork.

The break_at method sets a code breakpoint at the given address. Every time the code is run by any thread, a callback function is called. This is useful to know when certain parts of the debugee’s code are being run (for example, set it at the beginning of a function to see how many times it’s called).

The hook_function method sets a code breakpoint at the beginning of a function and allows you to set two callbacks - one when entering the function and another when returning from it. It works pretty much like the apiHooks property of the EventHandler class, only it doesn’t need the function to be exported by a DLL library. It’s useful for intercepting calls to internal functions of the debugee, if you know where they are.

The watch_variable method sets a hardware breakpoint at the given address. Every time a read or write access is made to that address, a callback function is called. It’s useful for tracking accesses to a variable (for example, a member of a C++ object in the heap). It works only on specific threads, to monitor the variable on the entire process you must set a watch for each thread.

Finally, the watch_buffer method sets a page breakpoint at the given address range. Every time a read or write access is made to that part of the memory a callback function is called. It’s similar to watch_variable but it works for the entire process, not just a single thread, and it allows any range to be specified (watch_variable only works for small address ranges, from 1 to 8 bytes).

Debug objects also allow stalking. Stalking basically means to set one-shot breakpoints - that is, breakpoints that are automatically disabled after they’re hit for the first time. The term was originally coined by Pedram Amini for his Process Stalker tool, and this technique is key to differential debugging.

The stalking methods and their equivalents are the following:

Stalking method Equivalent to
stalk_at break_at
stalk_function hook_function
stalk_variable watch_variable
stalk_buffer watch_buffer

Example #11: setting a breakpoint

Download



# This function will be called when our breakpoint is hit.
def action_callback( event ):
    process = event.get_process()
    thread  = event.get_thread()

    # Get the address of the top of the stack.
    stack   = thread.get_sp()

    # Get the return address of the call.
    address = process.read_pointer( stack )

    # Get the process and thread IDs.
    pid     = event.get_pid()
    tid     = event.get_tid()

    # Show a message to the user.
    message = "kernel32!CreateFileW called from %s by thread %d at process %d"
    print message % ( HexDump.address(address, process.get_bits()), tid, pid )


class MyEventHandler( EventHandler ):

    def load_dll( self, event ):

        # Get the new module object.
        module = event.get_module()

        # If it's kernel32.dll...
        if module.match_name("kernel32.dll"):

            # Get the process ID.
            pid = event.get_pid()

            # Get the address of CreateFile.
            address = module.resolve( "CreateFileW" )

            # Set a breakpoint at CreateFile.
            event.debug.break_at( pid, address, action_callback )

            # If you use stalk_at instead of break_at,
            # the message will only be shown once.
            #
            # event.debug.stalk_at( pid, address, action_callback )


Example #12: hooking a function

Download

from winappdbg.win32 import PVOID


# This function will be called when the hooked function is entered.
def wsprintf( event, ra, lpOut, lpFmt ):

    # Get the format string.
    process = event.get_process()
    lpFmt   = process.peek_string( lpFmt, fUnicode = True )

    # Get the vararg parameters.
    count      = lpFmt.replace( '%%', '%' ).count( '%' )
    thread     = event.get_thread()
    if process.get_bits() == 32:
        parameters = thread.read_stack_dwords( count, offset = 3 )
    else:
        parameters = thread.read_stack_qwords( count, offset = 3 )

    # Show a message to the user.
    showparams = ", ".join( [ hex(x) for x in parameters ] )
    print "wsprintf( %r, %s );" % ( lpFmt, showparams )


class MyEventHandler( EventHandler ):

    def load_dll( self, event ):

        # Get the new module object.
        module = event.get_module()

        # If it's user32...
        if module.match_name("user32.dll"):

            # Get the process ID.
            pid = event.get_pid()

            # Get the address of wsprintf.
            address = module.resolve( "wsprintfW" )

            # This is an approximated signature of the wsprintf function.
            # Pointers must be void so ctypes doesn't try to read from them.
            # Varargs are obviously not included.
            signature = ( PVOID, PVOID )

            # Hook the wsprintf function.
            event.debug.hook_function( pid, address, wsprintf, signature = signature)

            # Use stalk_function instead of hook_function
            # to be notified only the first time the function is called.
            #
            # event.debug.stalk_function( pid, address, wsprintf, signature = signature)


Example #13: watching a variable

Download



# This function will be called when the breakpoint is hit.
def entering( event ):

    # Get the thread object.
    thread = event.get_thread()

    # Get the thread ID.
    tid = thread.get_tid()

    # Get the return address location (the top of the stack).
    stack_top = thread.get_sp()

    # Get the return address and the parameters from the stack.
    bits = event.get_process().get_bits()
    if bits == 32:
        return_address, hModule, lpProcName = thread.read_stack_dwords( 3 )
    else:
        return_address = thread.read_stack_qwords( 1 )
        registers  = thread.get_context()
        hModule    = registers['Rcx']
        lpProcName = registers['Rdx']

    # Get the string from the process memory.
    procedure_name = event.get_process().peek_string( lpProcName )

    # Show a message to the user.
    message = "%s: GetProcAddress(%s, %r);"
    print message % (
        HexDump.address(return_address, bits),
        HexDump.address(hModule, bits),
        procedure_name
    )

    # Watch the DWORD at the top of the stack.
    try:
        event.debug.stalk_variable( tid, stack_top, 4, returning )
        #event.debug.watch_variable( tid, stack_top, 4, returning )

    # If no more slots are available, set a code breakpoint at the return address.
    except RuntimeError:
        event.debug.stalk_at( event.get_pid(), return_address, returning_2 )


# This function will be called when the variable is accessed.
def returning( event ):

    # Get the address of the watched variable.
    variable_address = event.breakpoint.get_address()

    # Stop watching the variable.
    event.debug.dont_stalk_variable( event.get_tid(), variable_address )
    #event.debug.dont_watch_variable( event.get_tid(), variable_address )

    # Get the return address (in the stack).
    return_address = event.get_process().read_uint( variable_address )

    # Get the return value (in the registers).
    registers = event.get_thread().get_context()
    if event.get_process().get_bits() == 32:
        return_value = registers['Eax']
    else:
        return_value = registers['Rax']

    # Show a message to the user.
    message = "%.08x: GetProcAddress() returned 0x%.08x"
    print message % ( return_address, return_value )


# This function will be called if we ran out of hardware breakpoints,
# and we ended up setting a code breakpoint at the return address.
def returning_2( event ):

    # Get the return address from the breakpoint.
    return_address = event.breakpoint.get_address()

    # Remove the code breakpoint.
    event.debug.dont_stalk_at( event.get_pid(), return_address )

    # Get the return value (in the registers).
    registers = event.get_thread().get_context()
    if event.get_process().get_bits() == 32:
        return_value = registers['Eax']
    else:
        return_value = registers['Rax']

    # Show a message to the user.
    message = "%.08x: GetProcAddress() returned 0x%.08x"
    print message % ( return_address, return_value )


# This event handler sets a breakpoint at kernel32!GetProcAddress.
class MyEventHandler( EventHandler ):

    def load_dll( self, event ):

        # Get the new module object.
        module = event.get_module()

        # If it's kernel32...
        if module.match_name("kernel32.dll"):

            # Get the process ID.
            pid = event.get_pid()

            # Get the address of GetProcAddress.
            address = module.resolve( "GetProcAddress" )

            # Set a breakpoint at the entry of the GetProcAddress function.
            event.debug.break_at( pid, address, entering )


Example #14: watching a buffer

Download



class MyHook (object):

    # Keep record of the buffers we watch.
    def __init__(self):
        self.__watched  = dict()
        self.__previous = None


    # This function will be called when entering the hooked function.
    def entering( self, event, ra, hFile, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead, lpOverlapped ):

        # Ignore calls using a NULL pointer.
        if not lpBuffer:
            return

        # Show a message to the user.
        print "\nReadFile:\n\tHandle %x\n\tExpected bytes: %d" % ( hFile, nNumberOfBytesToRead )

        # Stop watching the previous buffer.
        if self.__previous:
            event.debug.dont_watch_buffer( self.__previous )
            self.__previous = None

        # Remember the location of the buffer and its size.
        self.__watched[ event.get_tid() ] = ( lpBuffer, lpNumberOfBytesRead )


    # This function will be called when leaving the hooked function.
    def leaving( self, event, return_value ):

        # If the function call failed ignore it.
        if return_value == 0:
            print "\nReadFile:\n\tStatus: FAIL"
            return

        # Get the buffer location and size.
        tid     = event.get_tid()
        process = event.get_process()
        ( lpBuffer, lpNumberOfBytesRead ) = self.__watched[ tid ]
        del self.__watched[ tid ]

        # Watch the buffer for access.
        pid     = event.get_pid()
        address = lpBuffer
        size    = process.read_dword( lpNumberOfBytesRead )
        action  = self.accessed
        self.__previous = event.debug.watch_buffer( pid, address, size, action )

        # Use stalk_buffer instead of watch_buffer to be notified
        # only of the first access to the buffer.
        #
        # self.__previous = event.debug.stalk_buffer( pid, address, size, action )

        # Show a message to the user.
        print "\nReadFile:\n\tStatus: SUCCESS\n\tRead bytes: %d" % size


    # This function will be called every time the procedure name buffer is accessed.
    def accessed( self, event ):

        # Show the user where we're running.
        thread = event.get_thread()
        pc     = thread.get_pc()
        code   = thread.disassemble( pc, 0x10 ) [0]
        print "%s: %s" % (
            HexDump.address(code[0], thread.get_bits()),
            code[2].lower()
        )


class MyEventHandler( EventHandler ):

    # Called on guard page exceptions NOT raised by our breakpoints.
    def guard_page( self, event ):
        print event.get_exception_name()

    # Called on DLL load events.
    def load_dll( self, event ):

        # Get the new module object.
        module = event.get_module()

        # If it's kernel32...
        if module.match_name( "kernel32.dll" ):

            # Get the process ID.
            pid = event.get_pid()

            # Get the address of the function to hook.
            address = module.resolve( "ReadFile" )

            # This is an approximated signature of the function.
            # Pointers must be void so ctypes doesn't try to read from them.
            signature = ( win32.HANDLE, win32.PVOID, win32.DWORD, win32.PVOID, win32.PVOID )

            # Hook the function.
            hook = MyHook()
            event.debug.hook_function( pid, address, hook.entering, hook.leaving, signature = signature )


Labels

Labels are used to represent memory locations in a more user-friendly way than simply using their addresses. This is useful to provide a better user interface, both for input and output. Also, labels can be useful when DLL libraries in a debugee are relocated on each run - memory addresses change every time, but labels don’t.

For example, the label “kernel32!CreateFileA” always points to the CreateFileA function of the kernel32.dll library. The actual memory address, on the other hand, may change across Windows versions.

In addition to exported functions, debugging symbols are used whenever possible.

A complete explanation on how labels work can be found at the Advanced Topics section of this document.

Example #15: getting the label for a given memory address

Download


from winappdbg import System, Process

def print_label( pid, address ):

    # Request debug privileges.
    System.request_debug_privileges()

    # Instance a Process object.
    process = Process( pid )

    # Lookup it's modules.
    process.scan_modules()

    # Resolve the requested label address.
    label = process.get_label_at_address( address )

    # Print the label.
    print "%s == 0x%.08x" % ( label, address )

Example #16: resolving a label back into a memory address

Download


from winappdbg import System, Process

def print_label_address( pid, label ):

    # Request debug privileges.
    System.request_debug_privileges()

    # Instance a Process object.
    process = Process( pid )

    # Lookup it's modules.
    process.scan_modules()

    # Resolve the requested label address.
    address = process.resolve_label( label )

    # Print the address.
    print "%s == 0x%.08x" % ( label, address )