Instrumentation

You can implement process instrumentation in your Python scripts by using the provided set of classes: System, Process, Thread, Module and Window. Each one acts as a snapshot of the processes, threads and DLL modules in the system.

A System object is a snapshot of all running processes. It contains Process objects, which in turn are snapshots of threads and modules, containing Thread and Module objects.

System objects also contain Window objects, representing the windows in the current desktop.

Note

You don’t need to be attached as a debugger for these classes to work.

The System class

The System class groups functionality that lets you instrument some global aspects of the machine where you installed WinAppDbg. It also behaves like a snapshot of the running processes. It can enumerate processes and perform operations on a batch of processes.

Example #1: knowing on which platform we’re running

Download

from winappdbg import System, version

# Show the Windows version and the current architecture.
print "WinAppDbg %s" % version
print "Running on %s for the %s architecture." % (System.os, System.arch)
if System.wow64:
    print "Running in 32 bit emulation mode."
print "From this Python VM we can attach to %d-bit processes." % System.bits

Example #2: enumerating running processes

Download

from winappdbg import System

# Create a system snaphot.
system = System()

# Now we can enumerate the running processes.
for process in system:
    print "%d:\t%s" % ( process.get_pid(), process.get_filename() )

Example #3: starting a new process

Download

from winappdbg import System

import sys

# Instance a System object.
system = System()

# Get the target application.
command_line = system.argv_to_cmdline( sys.argv[ 1 : ] )

# Start a new process.
process = system.start_process( command_line ) # see the docs for more options

# Show info on the new process.
print "Started process %d (%d bits)" % ( process.get_pid(), process.get_bits() )

The System class has many more features, so we’ll be coming back to it later on in the tutorial.

The Process class

The Process class lets you manipulate any process in the system. You can get a Process instance by enumerating a System snapshot, or instancing one directly by providing the process ID.

A Process object allows you to manipulate the process memory (read, write, allocate and free operations), create new threads in the process, and more. It also acts as a snapshot of it’s threads and DLL modules.

Example #4: enumerating threads and DLL modules in a process

Download

from winappdbg import Process, HexDump

def print_threads_and_modules( pid ):

    # Instance a Process object.
    process = Process( pid )
    print "Process %d" % process.get_pid()

    # Now we can enumerate the threads in the process...
    print "Threads:"
    for thread in process.iter_threads():
        print "\t%d" % thread.get_tid()

    # ...and the modules in the process.
    print "Modules:"
    bits = process.get_bits()
    for module in process.iter_modules():
        print "\t%s\t%s" % (
            HexDump.address( module.get_base(), bits ),
            module.get_filename()
        )

Example #5: killing a process

Download

from winappdbg import Process

def process_kill( pid ):

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

    # Kill the process.
    process.kill()

Example #6: reading the process memory

Download

from winappdbg import Process

def process_read( pid, address, length ):

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

    # Read the process memory.
    data = process.read( address, length )

    # You can also change the process memory.
    # process.write( address, "example data" )

    # Return a Python string with the memory contents.
    return data

Example #7: getting the command line for a process

Download

from winappdbg import Process

def show_command_line( pid ):

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

    # Print the process command line.
    print process.get_command_line()

    # The same thing could be done with the environment variables.
    #import pprint
    #pprint.pprint( process.get_environment() )

Example #8: getting the environment variables for a process

Download

from winappdbg import Process

def show_environment( pid ):

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

    # Get its environment variables.
    environment = process.get_environment()

    # Print the environment variables.
    for variable, value in sorted( environment.items() ):
        print "%s=%s" % (variable, value)

Example #9: loading a DLL into the process

Download

from winappdbg import Process

def load_dll( pid, filename ):

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

    # Load the DLL library in the process.
    process.inject_dll( filename )

Example #10: getting the process memory map

Download

from winappdbg import win32, Process, HexDump

def print_memory_map( pid ):

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

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

    # Get the process memory map.
    memoryMap = process.get_memory_map()

    # Now you could do this...
    #
    #   from winappdbg import CrashDump
    #   print CrashDump.dump_memory_map( memoryMap ),
    #
    # ...but let's do it the hard way:

    # For each memory block in the map...
    print "Address   \tSize      \tState     \tAccess    \tType"
    for mbi in memoryMap:

        # Address and size of memory block.
        BaseAddress = HexDump.address(mbi.BaseAddress, bits)
        RegionSize  = HexDump.address(mbi.RegionSize,  bits)

        # State (free or allocated).
        if   mbi.State == win32.MEM_RESERVE:
            State   = "Reserved  "
        elif mbi.State == win32.MEM_COMMIT:
            State   = "Commited  "
        elif mbi.State == win32.MEM_FREE:
            State   = "Free      "
        else:
            State   = "Unknown   "

        # Page protection bits (R/W/X/G).
        if mbi.State != win32.MEM_COMMIT:
            Protect = "          "
        else:
##            Protect = "0x%.08x" % mbi.Protect
            if   mbi.Protect & win32.PAGE_NOACCESS:
                Protect = "--- "
            elif mbi.Protect & win32.PAGE_READONLY:
                Protect = "R-- "
            elif mbi.Protect & win32.PAGE_READWRITE:
                Protect = "RW- "
            elif mbi.Protect & win32.PAGE_WRITECOPY:
                Protect = "RC- "
            elif mbi.Protect & win32.PAGE_EXECUTE:
                Protect = "--X "
            elif mbi.Protect & win32.PAGE_EXECUTE_READ:
                Protect = "R-X "
            elif mbi.Protect & win32.PAGE_EXECUTE_READWRITE:
                Protect = "RWX "
            elif mbi.Protect & win32.PAGE_EXECUTE_WRITECOPY:
                Protect = "RCX "
            else:
                Protect = "??? "
            if   mbi.Protect & win32.PAGE_GUARD:
                Protect += "G"
            else:
                Protect += "-"
            if   mbi.Protect & win32.PAGE_NOCACHE:
                Protect += "N"
            else:
                Protect += "-"
            if   mbi.Protect & win32.PAGE_WRITECOMBINE:
                Protect += "W"
            else:
                Protect += "-"
            Protect += "   "

        # Type (file mapping, executable image, or private memory).
        if   mbi.Type == win32.MEM_IMAGE:
            Type    = "Image     "
        elif mbi.Type == win32.MEM_MAPPED:
            Type    = "Mapped    "
        elif mbi.Type == win32.MEM_PRIVATE:
            Type    = "Private   "
        elif mbi.Type == 0:
            Type    = "Free      "
        else:
            Type    = "Unknown   "

        # Print the memory block information.
        fmt = "%s\t%s\t%s\t%s\t%s"
        print fmt % ( BaseAddress, RegionSize, State, Protect, Type )

Example #11: searching the process memory

Download

from winappdbg import Process, HexDump

def memory_search( pid, bytes ):

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

    # Search for the string in the process memory.
    for address in process.search_bytes( bytes ):

        # Print the memory address where it was found.
        print HexDump.address( address )

    # You could also use process.search_regexp to use regular expressions,
    # or process.search_text for Unicode strings,
    # or process.search_hexa for raw bytes represented in hexa.

Example #12: dumping ASCII strings from the process memory

Download

from winappdbg import Process, HexDump

def strings( pid ):

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

    # For each ASCII string found in the process memory...
    for address, size, data in process.strings():

        # Print the string and the memory address where it was found.
        print "%s: %s" % ( HexDump.address(address), data )

The Thread class

A Thread object lets you manipulate any thread in any process in the system. You can get a Thread instance by enumerating a Process snapshot, or instancing one manually by providing the thread ID.

You can manipulate the thread context (read and write to it’s registers), perform typical debugger operations (getting stack traces, etc), suspend and resume execution, and more.

Example #13: freeze all threads in a process

Download

from winappdbg import Process, System

def freeze_threads( pid ):

    # Request debug privileges.
    System.request_debug_privileges()

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

    # This would also do the trick...
    #
    #   process.suspend()
    #
    # ...but let's do it the hard way:

    # Lookup the threads in the process.
    process.scan_threads()

    # For each thread in the process...
    for thread in process.iter_threads():

        # Suspend the thread execution.
        thread.suspend()

def unfreeze_threads( pid ):

    # Request debug privileges.
    System.request_debug_privileges()

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

    # This would also do the trick...
    #
    #   process.resume()
    #
    # ...but let's do it the hard way:

    # Lookup the threads in the process.
    process.scan_threads()

    # For each thread in the process...
    for thread in process.iter_threads():

        # Resume the thread execution.
        thread.resume()

Example #14: print a thread’s context

Download

from winappdbg import Thread, CrashDump, System

def print_thread_context( tid ):

    # Request debug privileges.
    System.request_debug_privileges()

    # Instance a Thread object.
    thread = Thread( tid )

    # Suspend the thread execution.
    thread.suspend()

    # Get the thread context.
    try:
        context = thread.get_context()

    # Resume the thread execution.
    finally:
        thread.resume()

    # Display the thread context.
    print
    print CrashDump.dump_registers( context ),

Example #15: print a thread’s code disassembly

Download

from winappdbg import Thread, CrashDump, System

def print_thread_disassembly( tid ):

    # Request debug privileges.
    System.request_debug_privileges()

    # Instance a Thread object.
    thread = Thread( tid )

    # Suspend the thread execution.
    thread.suspend()

    # Get the thread's currently running code.
    try:
        eip  = thread.get_pc()
        code = thread.disassemble_around( eip )

        # You can also do this:
        # code = thread.disassemble_around_pc()

        # Or even this:
        # process = thread.get_process()
        # code    = process.disassemble_around( eip )

    # Resume the thread execution.
    finally:
        thread.resume()

    # Display the disassembled code.
    print
    print CrashDump.dump_code( code, eip ),

The Module class

A Module object lets you manipulate any thread in any process in the system. You can get a Module instance by enumerating a Process snapshot. Module objects can be used to resolve the addresses of exported functions in the process address space.

Example #16: resolve an API function in a process

Download

from winappdbg import Process, System

def print_api_address( pid, modName, procName ):

    # Request debug privileges.
    System.request_debug_privileges()

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

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

    # Get the module.
    module = process.get_module_by_name( modName )
    if not module:
        print "Module not found: %s" % modName
        return

    # Resolve the requested API function address.
    address = module.resolve( procName )

    # Print the address.
    if address:
        print "%s!%s == 0x%.08x" % ( modName, procName, address )
    else:
        print "Could not resolve %s in module %s" % (procName, modName)

The Window class

A Window object lets you manipulate any window in the current desktop. You can get a Window instance by querying a System object.

Example #17: enumerate the top-level windows

Download

from winappdbg import System, HexDump

# Create a system snaphot.
system = System()

# Now we can enumerate the top-level windows.
for window in system.get_windows():
    handle  = HexDump.integer( window.get_handle() )
    caption = window.get_text()
    if caption is not None:
        print "%s:\t%s" % ( handle, caption )

Example #18: minimize all top-level windows

Download

from winappdbg import System, HexDump

# Create a system snaphot.
system = System()

# Enumerate the top-level windows.
for window in system.get_windows():

    # Minimize the window.
    if not window.is_minimized():
        window.minimize()

    # You could also maximize, restore, show, hide, enable and disable.
    # For example:
    #
    # if window.is_maximized():
    #     window.restore()
    #
    # if not window.is_visible():
    #     window.show()
    #
    # if not window.is_disabled():
    #     window.enable()
    #
    # ...and so on.

Example #19: traverse the windows tree

Download

from winappdbg import System, HexDump


def show_window_tree( window, indent = 0 ):

    # Show this window's handle and caption.
    # Use some ASCII art to show the layout. :)
    handle  = HexDump.integer( window.get_handle() )
    caption = window.get_text()
    line = ""
    if indent > 0:
        print "|   " * indent
        line = "|   " * (indent - 1) + "|---"
    else:
        print "|"
    if caption is not None:
        line += handle + ": " + caption
    else:
        line += handle
    print line

    # Recursively show the child windows.
    for child in window.get_children():
        show_window_tree( child, indent + 1 )


def main():

    # Create a system snaphot.
    system = System()

    # Get the Desktop window.
    root = system.get_desktop_window()

    # Now show the window tree.
    show_window_tree(root)

    # You can also ge the tree as a Python dictionary:
    # tree = root.get_tree()
    # print tree

Example #20: get windows by screen position

Download

from winappdbg import System, HexDump
import sys

try:

    # Get the coordinates from the command line.
    x = int( sys.argv[1] )
    y = int( sys.argv[2] )

    # Get the window at the requested position.
    window   = System.get_window_at( x, y )

    # Get the window coordinates.
    rect     = window.get_screen_rect()
    position = (rect.left, rect.top, rect.right, rect.bottom)
    size     = (rect.right - rect.left, rect.bottom - rect.top)

    # Print the window information.
    print "Handle:   %s" % HexDump.integer( window.get_handle() )
    print "Caption:  %s" % window.text
    print "Class:    %s" % window.classname
    print "Style:    %s" % HexDump.integer( window.style )
    print "ExStyle:  %s" % HexDump.integer( window.exstyle )
    print "Position: (%i, %i) - (%i, %i)" % position
    print "Size:     (%i, %i)" % size

except WindowsError:
    print "No window at those coordinates!"

Example #21: find windows by class and caption

Download

from winappdbg import System, HexDump
import sys


def find_window():

    # If two arguments are given, the first is the classname
    # and the second is the caption text.
    if len(sys.argv) > 2:
        classname = sys.argv[1]
        caption   = sys.argv[2]
        if not classname:
            classname = None
        if not caption:
            caption   = None
        window = System.find_window( classname, caption )

    # If only one argument is given, try the caption text, then the classname.
    else:
        try:
            window = System.find_window( windowName = sys.argv[1] )
        except WindowsError:
            window = System.find_window( className = sys.argv[1] )

    return window


def show_window( window ):

    # Get the window coordinates.
    rect     = window.get_screen_rect()
    position = (rect.left, rect.top, rect.right, rect.bottom)
    size     = (rect.right - rect.left, rect.bottom - rect.top)

    # Print the window information.
    print "Handle:   %s" % HexDump.integer( window.get_handle() )
    print "Caption:  %s" % window.text
    print "Class:    %s" % window.classname
    print "Style:    %s" % HexDump.integer( window.style )
    print "ExStyle:  %s" % HexDump.integer( window.exstyle )
    print "Position: (%i, %i) - (%i, %i)" % position
    print "Size:     (%i, %i)" % size


def main():
    try:
        show_window( find_window() )
    except WindowsError:
        print "No window found!"

Example #22: kill a program using its window

Download

def user_confirmed():
    print
    answer = raw_input( "Are you sure you want to kill this program? (y/N):" )
    answer = answer.strip().upper()
    return answer.startswith("Y")


def main():

    # Find the window.
    try:
        window = find_window()
    except WindowsError:
        print "No window found!"
        return

    # Show the window info to the user.
    show_window( window )

    # Ask the user for confirmation.
    if user_confirmed():

        # Kill the program.
        window.kill()

Back to the System class

As promised, we’re back on the System class to see more of its features. We’ll now see how to access the Windows Registry and work with system services.

Example #23: exporting a Registry key

Download

import struct

from winappdbg import System, win32

#RegistryEditorVersion = "REGEDIT4"  # for Windows 95
RegistryEditorVersion = "Windows Registry Editor Version 5.00"

# Helper function to serialize data to hexadecimal format.
def reg_hexa(value, type):
    return "hex(%x):%s" % (type, ",".join( ["%.2x" % ord(x) for x in value] ))

# Registry export function.
def reg_export( reg_path, filename ):

    # Queue of registry keys to visit.
    queue = []

    # Get the registry key the user requested.
    key = System.registry[ reg_path ]

    # Add it to the queue.
    queue.append( key )

    # Open the output file.
    with open(filename, "wb") as output:

        # Write the file format header.
        output.write( "%s\r\n" % RegistryEditorVersion )

        # For each registry key in the queue...
        while queue:
            key = queue.pop()

            # Write the key path.
            output.write( "\r\n[%s]\r\n" % key.path )

            # If there's a default value, write it.
            default = str(key)
            if default:
                output.write( "@=\"%s\"\r\n" % default )

            # For each value in the key...
            for name, value in key.iteritems():

                # Skip the default value since we already wrote it.
                if not name:
                    continue

                # Serialize the name.
                s_name = "\"%s\"" % name

                # Serialize the value.
                t_value = key.get_value_type(name)
                if t_value == win32.REG_SZ and type(value) == str:
                    s_value = "\"%s\"" % value.replace("\"", "\\\"")
                elif t_value == win32.REG_DWORD:
                    s_value = "dword:%.8X" % value
                else:
                    if t_value == win32.REG_QWORD:
                        value = struct.pack("<Q", value)
                    elif t_value == win32.REG_DWORD:
                        value = struct.pack("<L", value)
                    elif t_value == win32.REG_DWORD_BIG_ENDIAN:
                        value = struct.pack(">L", value)
                    elif t_value == win32.REG_MULTI_SZ:
                        if not value:
                            value = ""
                        elif type(value) == str:
                            value = "\0".join(value)
                        else:
                            value = u"\0".join(value)
                    if type(value) == str:
                        s_value = reg_hexa(value, t_value)
                    else:
                        s_value = reg_hexa(value.encode("UTF-16"), t_value)

                # Write the name and value.
                output.write( "%s=%s\r\n" % (s_name, s_value) )

Example #24: searching the Registry

Download

from winappdbg import System, Color

def reg_search( search ):

    # Show the user what we're searching for.
    print "Searching for: %r" % search

    # For each Registry key...
    for path in System.registry.iterkeys():

        # Try to open the key. On error skip it.
        try:
            key = System.registry[ path ]
        except Exception:
            continue

        # Get the default value. On error skip it.
        try:
            default = str(key)
        except KeyError:
            default = ""
        except Exception:
            continue

        # Does the default value match?
        if search in default:
            text = "%s\\@: %s" % ( path, default )
            highlight( search, text )

        # Does the key match?
        elif search in path[ path.rfind("\\") : ]:
            highlight( search, path )

        # For each Registry value...
        for name in key.iterkeys():

            # Try to get the value. On error ignore it.
            try:
                value = key[name]
            except Exception:
                value = ""

            # Registry values can be of many data types.
            # For this search we need to force all values to be strings.
            if type(value) not in (str, unicode):
                value = str(value)

            # Do the name or value match?
            if search in name or search in value:
                text = "%s\\%s: %r" % ( path, name, value )
                highlight( search, text )

# Helper function to print text with a highlighted search string.
def highlight( search, text ):
    if can_highlight:
        try:
            Color.default()
            p = 0
            t = len( text )
            s = len( search )
            while p < t:
                q = text.find( search )
                if q < p:
                    q = t
                sys.stdout.write( text[ p : q ] )
                Color.red()
                Color.light()
                sys.stdout.write( text[ q : q + s ] )
                Color.default()
                sys.stdout.write("\r\n")
                p = q + s
        finally:
            Color.default()
    else:
        print text

# Determine if the output is a console or a file.
# Trying to use colors fails if the output is not the console.
can_highlight = Color.can_use_colors()

Example #25: listing system services

Download

from winappdbg import System, win32

def show_services():

    # Get the list of services.
    services = System.get_services()

    # You could get only the running services instead.
    # services = System.get_active_services()

    # For each service descriptor...
    for descriptor in services:

        # Print the service information, the easy way.
        # print str(descriptor)

        # You can also do it the hard way, accessing its members.
        print "Service name: %s" % descriptor.ServiceName
        print "Display name: %s" % descriptor.DisplayName
        if   descriptor.ServiceType & win32.SERVICE_INTERACTIVE_PROCESS:
            print "Service type: Win32 GUI"
        elif descriptor.ServiceType & win32.SERVICE_WIN32:
            print "Service type: Win32"
        elif descriptor.ServiceType & win32.SERVICE_DRIVER:
            print "Service type: Driver"
        if   descriptor.CurrentState == win32.SERVICE_CONTINUE_PENDING:
            print "Current status: RESTARTING..."
        elif descriptor.CurrentState == win32.SERVICE_PAUSE_PENDING:
            print "Current status: PAUSING..."
        elif descriptor.CurrentState == win32.SERVICE_PAUSED:
            print "Current status: PAUSED"
        elif descriptor.CurrentState == win32.SERVICE_RUNNING:
            print "Current status: RUNNING"
        elif descriptor.CurrentState == win32.SERVICE_START_PENDING:
            print "Current status: STARTING..."
        elif descriptor.CurrentState == win32.SERVICE_STOP_PENDING:
            print "Current status: STOPPING..."
        elif descriptor.CurrentState == win32.SERVICE_STOPPED:
            print "Current status: STOPPED"
        print

# When invoked from the command line,
# call the show_services() function.

Example #26: stopping and starting a system service

Download

from time import sleep
from winappdbg import System, win32


# Function that restarts a service.
# Requires UAC elevation in Windows Vista and above.
def restart_service( service ):
    try:

        # Get the display name.
        try:
            display_name = System.get_service_display_name( service )
        except WindowsError:
            display_name = service

        # Get the service descriptor.
        descriptor = System.get_service( service )

        # Is the service running?
        if descriptor.CurrentState != win32.SERVICE_STOPPED:

            # Tell the service to stop.
            print "Stopping service \"%s\"..." % display_name
            System.stop_service( service )

            # Wait for the service to stop.
            wait_for_service( service, win32.SERVICE_STOP_PENDING )
            print "Service stopped successfully."

        # Tell the service to start.
        print "Starting service \"%s\"..." % display_name
        System.start_service( service )

        # Wait for the service to start.
        wait_for_service( service, win32.SERVICE_START_PENDING )
        print "Service started successfully."

        # Show the new process ID.
        # This feature requires Windows XP and above.
        descriptor = System.get_service( service )
        try:
            print "New process ID is: %d" % descriptor.ProcessId
        except AttributeError:
            pass

    # On error, show an error message.
    except WindowsError, e:
        print str(e)


# Helper function to wait for the service to change its state.
def wait_for_service( service, wait_state, timeout = 20 ):
    descriptor = System.get_service( service )
    while descriptor.CurrentState == wait_state:
        timeout -= 1
        if timeout <= 0:
            raise RuntimeException( "Error: timed out." )
        sleep( 0.5 )
        descriptor = System.get_service( service )