More examples

Set a debugging timeout

Sometimes you’ll want to set a maximum time to debug your target, especially when fuzzing. 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 *
from time import time

dbg = Debug(bKillOnExit = True)
try:
    dbg.execl('calc.exe')
    maxTime = time() + 5    # 5 seconds timeout
    while dbg.get_debugee_count() > 0 and time() < maxTime:
        try:
            print time()
            event = dbg.wait(1000)
        except WindowsError, e:
            if win32.winerror(e) in (win32.ERROR_SEM_TIMEOUT, win32.WAIT_TIMEOUT):
                continue
            raise
        try:
            dbg.dispatch(event)
        finally:
            dbg.cont(event)
finally:
    dbg.stop()

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 os
import sys
import zlib
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:]:
    for process, pathname in system.find_processes_by_filename(filename):
        pid = process.get_pid()
        print "Dumping memory for process ID %d" % pid

        # Parse the database filename
        dbfile   = '%d.db' % pid
        if os.path.exists(dbfile):
            counter = 1
            while 1:
                dbfile = '%d_%.3d.db' % (pid, counter)
                if not os.path.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),
                    winappdbg.HexDump.address(BaseAddress + RegionSize)
                )
                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.

Download

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

# Iterator of alphanumeric executable addresses
def iterate_alnum_jump_addresses(memory_snapshot):

    # Determine the size of a pointer in the current architecture
    if System.bits == 32:
        fmt = 'L'
    elif System.bits == 64:
        fmt = 'Q'
    else:
        raise NotImplementedError

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

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

        # Yield each alphanumeric address in this memory region.
        address     = mbi.BaseAddress
        max_address = address + mbi.RegionSize
        while address < max_address:
            packed = pack(fmt, address)
            if packed.isalnum():
                yield address, packed
            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:

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

        # Print each executable alphanumeric address.
        for address, packed in iterate_alnum_jump_addresses(iterator):
            print HexDump.address(address), repr(packed)

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

Trace all calls to text drawing in GDI

This example hooks all text drawing functions in GDI and prints the text. It can be useful to extract text messages and logs from GUI programs.

Download

from winappdbg import Debug, EventHandler, DebugLog
from ctypes import *

#------------------------------------------------------------------------------

# BOOL TextOut(
#   __in  HDC hdc,
#   __in  int nXStart,
#   __in  int nYStart,
#   __in  LPCTSTR lpString,
#   __in  int cbString
# );

def TextOutA(event, ra, hdc, nXStart, nYStart, lpString, cbString):
    log_ansi(event, "TextOutA", lpString, cbString)

def TextOutW(event, ra, hdc, nXStart, nYStart, lpString, cbString):
    log_wide(event, "TextOutW", lpString, cbString)

# BOOL ExtTextOut(
#   __in  HDC hdc,
#   __in  int X,
#   __in  int Y,
#   __in  UINT fuOptions,
#   __in  const RECT *lprc,
#   __in  LPCTSTR lpString,
#   __in  UINT cbCount,
#   __in  const INT *lpDx
# );
def ExtTextOutA(event, ra, hdc, X, Y, fuOptions, lprc, lpString, cbCount, lpDx):
    log_ansi(event, "ExtTextOutA", lpString, cbCount)

def ExtTextOutW(event, ra, hdc, X, Y, fuOptions, lprc, lpString, cbCount, lpDx):
    log_wide(event, "ExtTextOutW", lpString, cbCount)

# typedef struct _POLYTEXT {
#   int     x;
#   int     y;
#   UINT    n;
#   LPCTSTR lpstr;
#   UINT    uiFlags;
#   RECT    rcl;
#   int     *pdx;
# } POLYTEXT, *PPOLYTEXT;
class POLYTEXT(Structure):
    _fields_ = [
        ('x',       c_int),
        ('y',       c_int),
        ('n',       c_uint),
        ('lpstr',   c_void_p),
        ('uiFlags', c_uint),
        ('rcl',     c_uint * 4),
        ('pdx',     POINTER(c_int)),
    ]

# BOOL PolyTextOut(
#   __in  HDC hdc,
#   __in  const POLYTEXT *pptxt,
#   __in  int cStrings
# );

def PolyTextOutA(event, ra, hdc, pptxt, cStrings):
    process = event.get_process()
    sizeof_polytext = sizeof(POLYTEXT)
    while cStrings:
        txt = process.read_structure(pptxt, POLYTEXT)
        log_ansi(event, "PolyTextOutA", txt.lpstr, txt.n)
        pptxt = pptxt + sizeof_polytext
        cStrings = cStrings - 1

def PolyTextOutW(event, ra, hdc, pptxt, cStrings):
    process = event.get_process()
    sizeof_polytext = sizeof(POLYTEXT)
    while cStrings:
        txt = process.read_structure(pptxt, POLYTEXT)
        log_wide(event, "PolyTextOutW", txt.lpstr, txt.n)
        pptxt = pptxt + sizeof_polytext
        cStrings = cStrings - 1

#------------------------------------------------------------------------------

def log_ansi(event, fn, lpString, nCount):
    if lpString and nCount:
        if c_int(nCount).value == -1:
            lpString = event.get_process().peek_string(lpString, fUnicode = False)
        else:
            lpString = event.get_process().peek(lpString, nCount)
        print DebugLog.log_text("%s( %r );" % (fn, lpString))

def log_wide(event, fn, lpString, nCount):
    if lpString and nCount:
        if c_int(nCount).value == -1:
            lpString = event.get_process().peek_string(lpString, fUnicode = True)
        else:
            lpString = event.get_process().peek(lpString, nCount * 2)
            lpString = unicode(lpString, 'U16', 'replace')
        print DebugLog.log_text("%s( %r );" % (fn, lpString))

class MyEventHandler( EventHandler ):
    def load_dll(self, event):
        pid = event.get_pid()
        module = event.get_module()
        if module.match_name("gdi32.dll"):
            event.debug.hook_function(pid, module.resolve("TextOutA"),       TextOutA,       paramCount = 5)
            event.debug.hook_function(pid, module.resolve("TextOutW"),       TextOutW,       paramCount = 5)
            event.debug.hook_function(pid, module.resolve("ExtTextOutA"),    ExtTextOutA,    paramCount = 8)
            event.debug.hook_function(pid, module.resolve("ExtTextOutW"),    ExtTextOutW,    paramCount = 8)
            event.debug.hook_function(pid, module.resolve("PolyTextOutA"),   PolyTextOutA,   paramCount = 2)
            event.debug.hook_function(pid, module.resolve("PolyTextOutW"),   PolyTextOutW,   paramCount = 2)

def simple_debugger(argv):
    print DebugLog.log_text("Trace started on %s" % argv[0])
    debug = Debug( MyEventHandler() )
    try:
        debug.execv(argv)
        debug.loop()
    finally:
        debug.stop()
    print DebugLog.log_text("Trace stopped on %s" % argv[0])

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. 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