More examples

Set a debugging timeout

Sometimes you’ll want to set a maximum time to debug your target, especially when fuzzing or analyzing malware. This is an example on how to code a custom debugging loop with a timeout. It launches the Windows Calculator and stops when the target process is closed or after a 5 seconds timeout.

Download


from winappdbg import *  # NOQA
from time import time

# Using the Debug object in a "with" context ensures proper cleanup.
with Debug( bKillOnExit = True ) as dbg:

    # Run the Windows Calculator (calc.exe).
    dbg.execl('calc.exe')

    # For the extra paranoid: this makes sure calc.exe dies
    # even if our own process is killed from the Task Manager.
    System.set_kill_on_exit_mode(True)

    # The execution time limit is 5 seconds.
    maxTime = time() + 5

    # Loop while calc.exe is alive and the time limit wasn't reached.
    while dbg and time() < maxTime:
        try:

            # Get the next debug event.
            dbg.wait(1000)  # 1 second accuracy

            # Show the current time on screen.
            print time()

        # If wait() times out just try again.
        # On any other error stop debugging.
        except WindowsError, e:
            if e.winerror in (win32.ERROR_SEM_TIMEOUT,
                              win32.WAIT_TIMEOUT):
                continue
            raise

        # Dispatch the event and continue execution.
        try:
            dbg.dispatch()
        finally:
            dbg.cont()

Dump the memory of a process

This is an example on how to dump the memory map and contents of a process into an SQLite database. A table is created where each row is a memory region, and the columns are the properties of that region (address, size, mapped filename, etc.) and it’s data. The data is compressed using zlib to reduce the database size, but simply commenting out line 160 stores the data in uncompressed form.

Download


import sys
import zlib
import ntpath
import winappdbg
from winappdbg import win32

try:
    import sqlite3 as sqlite
except ImportError:
    from pysqlite2 import dbapi2 as sqlite

# Create a snaphot of running processes.
system = winappdbg.System()
system.request_debug_privileges()
system.scan_processes()

# Get all processes that match the requested filenames.
for filename in sys.argv[1:]:
    print "Looking for: %s" % filename
    for process, pathname in system.find_processes_by_filename(filename):
        pid  = process.get_pid()
        bits = process.get_bits()
        print "Dumping memory for process ID %d (%d bits)" % (pid, bits)

        # Parse the database filename.
        dbfile   = '%d.db' % pid
        if ntpath.exists(dbfile):
            counter = 1
            while 1:
                dbfile = '%d_%.3d.db' % (pid, counter)
                if not ntpath.exists(dbfile):
                    break
                counter += 1
            del counter
        print "Creating database %s" % dbfile

        # Connect to the database and get a cursor.
        database = sqlite.connect(dbfile)
        cursor   = database.cursor()

        # Create the table for the memory map.
        cursor.execute("""
            CREATE TABLE MemoryMap (
                Address INTEGER PRIMARY KEY,
                Size    INTEGER,
                State   STRING,
                Access  STRING,
                Type    STRING,
                File    STRING,
                Data    BINARY
            )
        """)

        # Get a memory map of the process.
        memoryMap       = process.get_memory_map()
        mappedFilenames = process.get_mapped_filenames(memoryMap)

        # For each memory block in the map...
        for mbi in memoryMap:

            # Address and size of memory block.
            BaseAddress = mbi.BaseAddress
            RegionSize  = 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:
                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 += "-"

            # 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    = ""
            else:
                Type    = "Unknown"

            # Mapped file name, if any.
            FileName = mappedFilenames.get(BaseAddress, None)

            # Read the data contained in the memory block, if any.
            Data = None
            if mbi.has_content():
                print 'Reading %s-%s' % (
                    winappdbg.HexDump.address(BaseAddress, bits),
                    winappdbg.HexDump.address(BaseAddress + RegionSize, bits)
                )
                Data = process.read(BaseAddress, RegionSize)
                Data = zlib.compress(Data, zlib.Z_BEST_COMPRESSION)
                Data = sqlite.Binary(Data)

            # Output a row in the table.
            cursor.execute(
                'INSERT INTO MemoryMap VALUES (?, ?, ?, ?, ?, ?, ?)',
                (BaseAddress, RegionSize, State, Protect, Type, FileName, Data)
            )

        # Commit the changes, close the cursor and the database.
        database.commit()
        cursor.close()
        database.close()
        print "Ok."
print "Done."

Find alphanumeric addresses to jump to

This example will find all memory addresses in a target process that are executable and whose address consists of alphanumeric characters only. This is useful when exploiting a stack buffer overflow and the input string is limited to alphanumeric characters only.

Note that in 64 bit processors most memory addresses are not alphanumeric, so this example is meaningful for 32 bits only.

Download


    # Note:
    # This simple approach seems fast enough. But if there's ever a need to
    # optimize this for 32 bits Windows this is how it could be done: since
    # the system allocation granularity is 64k, it should be possible to
    # precalculate the lower 16 bits of all possible alphanumeric addresses,
    # then only test the higher 16 bits of the address for each memory region.

from struct import pack
from winappdbg import System, Process, HexDump

# Iterator of alphanumeric executable addresses.
def iterate_alnum_jump_addresses(process):

    # Determine the size of a pointer in the current architecture.
    if System.bits == 32:
        fmt = 'L'
    elif System.bits == 64:
        fmt = 'Q'
        print "Warning! 64 bit addresses are not likely to be alphanumeric!"
    else:
        raise NotImplementedError

    # Get an iterator for the target process memory.
    iterator = process.generate_memory_snapshot()

    # Iterate the memory regions of the target process.
    for mbi in iterator:

        # Discard non executable memory.
        if not mbi.is_executable():
            continue

        # Get the module that owns this memory region, if any.
        address = mbi.BaseAddress
        module  = process.get_module_at_address(address)

        # Yield each alphanumeric address in this memory region.
        max_address = address + mbi.RegionSize
        while address < max_address:
            packed = pack(fmt, address)
            if packed.isalnum():
                yield address, packed, module
            address = address + 1

# Iterate and print alphanumeric executable addresses.
def print_alnum_jump_addresses(pid):

    # Request debug privileges so we can inspect the memory of services too.
    System.request_debug_privileges()

    # Suspend the process so there are no malloc's and free's while iterating.
    process = Process(pid)
    process.suspend()
    try:

        # For each executable alphanumeric address...
        for address, packed, module in iterate_alnum_jump_addresses(process):

            # Format the address for printing.
            numeric = HexDump.address(address, process.get_bits())
            ascii   = repr(packed)

            # Format the module name for printing.
            if module:
                modname = module.get_name()
            else:
                modname = ""

            # Try to disassemble the code at this location.
            try:
                code = process.disassemble(address, 16)[0][2]
            except NotImplementedError:
                code = ""

            # Print it.
            print numeric, ascii, modname, code

    # Resume the process when we're done.
    # This is inside a "finally" block, so if the program is interrupted
    # for any reason we don't leave the process suspended.
    finally:
        process.resume()

Show processes DEP settings

Beginning with Windows XP SP3, it’s possible to query a process and find out its Data Execution Prevention (DEP) settings. It may have DEP enabled or disabled, DEP-ATL thunking emulation enabled or disabled, and these settings may be changeable on runtime or permanent for the lifetime of the process.

This example shows all 32 bits processes the current user has permission to access and shows their DEP settings.

Download


from winappdbg import System, Table
from winappdbg.win32 import PROCESS_DEP_ENABLE, \
                            PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION, \
                            ERROR_ACCESS_DENIED

# Prepare the table.
header = ( " PID ", "DEP ", "DEP-ATL ", "Permanent ", "Filename " )
separator = [ " " * len(x) for x in header ]
table = Table()
table.addRow( *header )
table.addRow( *separator )

# Request debug privileges.
System.request_debug_privileges()

# Scan for running processes.
system = System()
try:
    system.scan_processes()
    #system.scan_process_filenames()
except WindowsError:
    system.scan_processes_fast()

# For each running process...
for process in system.iter_processes():
    try:

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

        # Skip "special" process IDs.
        if pid in (0, 4, 8):
            continue

        # Skip 64 bit processes.
        if process.get_bits() != 32:
            continue

        # Get the DEP policy flags.
        flags, permanent = process.get_dep_policy()

        # Determine if DEP is enabled.
        if flags & PROCESS_DEP_ENABLE:
            dep = " X"
        else:
            dep = ""

        # Determine if DEP-ATL thunk emulation is enabled.
        if flags & PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION:
            atl = ""
        else:
            atl = "   X"

        # Determine if the current DEP flag is permanent.
        if permanent:
            perm = "    X"
        else:
            perm = ""

    # Skip processes we don't have permission to access.
    except WindowsError, e:
        if e.winerror == ERROR_ACCESS_DENIED:
            continue
        raise

    # Get the filename.
    filename = process.get_filename()

    # Add the process to the table.
    table.addRow( pid, dep, atl, perm, filename )

# Print the table.
table.show()

Choose the disassembler you want to use

WinAppDbg supports several disassembler engines. When more than one compatible engine is installed a default one is picked. However, you can manually select which one you want to use.

This example shows you how to list the supported disassembler engines for the desired architecture and pick one.

Download


from sys import argv

from winappdbg import Disassembler, HexInput, CrashDump

# If there are no command line arguments...
if len( argv ) == 1:

    # Show the help message.
    print "Usage:"
    print "  %s <file> [offset] [size] [arch] [engine]" % argv[0]

    # Show the available disassembler engines.
    print
    print "Supported disassembler engines:"
    print "-------------------------------"
    available = Disassembler.get_available_engines()
    for engine in Disassembler.get_all_engines():
        print
        print "Name: %s" % engine.name
        print "Description: %s" % engine.desc
        print "Available: %s" % ("YES" if engine in available else "NO")
        print "Supported architectures: %s" % ", ".join( engine.supported )

# If there are command line arguments...
else:

    # Get the arguments from the command line.
    filename = argv[1]
    try:
        offset = HexInput.address( argv[2] )
    except IndexError:
        offset = 0
    try:
        size = HexInput.integer( argv[3] )
    except IndexError:
        size = 0
    try:
        arch = argv[4]
    except IndexError:
        arch = None
    try:
        engine = argv[5]
    except IndexError:
        engine = None

    # Load the requested disassembler engine.
    disasm = Disassembler( arch, engine )

    # Load the binary code.
    with open( filename, 'rb' ) as fd:
        fd.seek( offset )
        if size:
            code = fd.read( size )
        else:
            code = fd.read()

    # Disassemble the code.
    disassembly = disasm.decode( offset, code )

    # Show the disassembly.
    print CrashDump.dump_code( disassembly, offset )

Enumerate all named global atoms

Global atoms are WORD numeric values that can be associated to arbitrary strings. They are used primarily for IPC purposes on Windows XP (Vista and 7 don’t seem to be using them anymore). This example shows how to retrieve the string from any atom value.

Download


from winappdbg.win32 import GlobalGetAtomName, MAXINTATOM

# print all valid named global atoms to standard output.
def print_atoms():
    for x in xrange(0, MAXINTATOM):
        try:
            n = GlobalGetAtomName(x)
            if n == "#%d" % x:      # comment out to print
                continue            # valid numeric atoms
            print "Atom %4x: %r" % (x, n)
        except WindowsError:
            pass