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