Instrumentation

You can implement process instrumentation in your Python scripts by using the provided set of classes: System, Process, Thread and Module. 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 processes. A Process object contains Thread and Module objects.

Note

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

The System class

The System class basically behaves like a snapshot of the running processes. It can enumerate processes and perform operations on a batch of processes.

Example #1: enumerating running processes

Download

from winappdbg import System

# Request debugging privileges for the current process
# This is needed to get some information from services
# (Try commenting out this line to see what happens!)
System.request_debug_privileges()

# 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 #2: 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
system.start_process( command_line )    # see the docs for more options

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 #3: 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:"
    for module in process.iter_modules():
        print "\t%s\t%s" % ( HexDump.address( module.get_base() ), module.get_filename() )

Example #4: 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 #5: 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 )

    # Return a Python string with the memory contents
    return data

Example #6: 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 #7: getting the process memory map

Download

from winappdbg import win32, Process, HexDump

def print_memory_map( pid ):

    # Instance a Process object
    process = Process( pid )

    # 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)
        RegionSize  = HexDump.address(mbi.RegionSize)

        # 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 )

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 #8: 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:

        # 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:

        # Resume the thread execution
        thread.resume()

Example #9: 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 #10: 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 thread context
    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 #11: 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)