Winappdbg 1.5 Tutorial
Winappdbg 1.5 Tutorial
Release 1.5
Mario Vilas
Contents
Introduction
Programming Guide
2.1 Downloading and installing
2.2 Instrumentation . . . . . . .
2.3 Debugging . . . . . . . . .
2.4 Helper classes and functions
2.5 The Win32 API wrappers .
2.6 More examples . . . . . . .
2.7 Advanced topics . . . . . .
2.8 Command line tools . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
3
3
8
25
45
52
55
63
73
ii
CHAPTER 1
Introduction
The WinAppDbg python module allows developers to quickly code instrumentation scripts in Python under a Windows environment.
It uses ctypes to wrap many Win32 API calls related to debugging, and provides a powerful abstraction layer to
manipulate threads, libraries and processes, attach your script as a debugger, trace execution, hook API calls, handle
events in your debugee and set breakpoints of different kinds (code, hardware and memory). Additionally it has no
native code at all, making it easier to maintain or modify than other debuggers on Windows.
The intended audience are QA engineers and software security auditors wishing to test or fuzz Windows applications
with quickly coded Python scripts. Several ready to use tools are shipped and can be used for this purposes.
Current features also include disassembling x86/x64 native code, debugging multiple processes simultaneously and
produce a detailed log of application crashes, useful for fuzzing and automated testing.
Here is a list of software projects that use WinAppDbg in alphabetical order:
Heappie! is a heap analysis tool geared towards exploit writing. It allows you to visualize the heap layout during
the heap spray or heap massaging stage in your exploits. The original version uses vtrace but heres a patch to
use WinAppDbg instead. The patch also adds 64 bit support.
PyPeElf is an open source GUI executable file analyzer for Windows and Linux released under the BSD license.
You can download it here and theres also a blog.
python-haystack is a heap analysis framework, focused on classic C structure matching. The basic functionality is to search in a process memory maps for a specific C Structures. The extended reverse engineering
functionality aims at reversing structures from memory/heap analysis.
SRS is a tool to spy on registry API calls made by the program of your choice.
Tracer.py is a small and cute execution tracer, in the words of its author :) to aid in differential debugging.
unpack.py is a script using WinAppDbg to automatically unpack malware, written by Karl Denton.
And this is a list of some alternatives to WinAppDbg in case it doesnt suit your needs, also in alphabetical order:
ImmLib is a Python library to integrate your custom scripts into Immunity Debugger. It can only function inside
the debugger, but its the best solution if you aim at writing plugins for that debugger instead of standalone tools.
Kenshotos vtrace debugger is a full fledged multiplatform debugger written in Python, and a personal favorite
of mine. I took a few ideas from it when designing WinAppDbg and, while I feel mine is more complete when it
comes to Windows-specific features, this is what Id definitely recommend for multi-OS projects. See also the
community branch.
OllyPython is an OllyDbg plugin that integrates a Python debugger. Naturally it only works within OllyDbg and
is not suitable for standalone projects.
PyDbg is another debugging library for Python that is part of the Paimei framework, but may work separately as
well. It works on Windows and OSX. It predates WinAppDbg by quite some time but its also been unmaintained
for long, and it only works in Python versions 2.4 and 2.5. A newer branch called PyDbg64 implements 64 bit
support for both platforms.
PyDbgEng is a similar project to WinAppDbg, but it uses the Microsoft Debug Engine as a back end while
WinAppDbg uses only bare Win32 API calls. The advantage of this approach is the ability to support kernel
debugging, which is not allowed by the Win32 API alone. The disadvantage is having to install the Windows
SDK/WDK to the machine where you run your scripts (or at least the components needed for debugging). See
also the Buggery project which is based on PyDbgEng.
PyDbgExt is the reverse of PyDbgEng: instead of instancing the Microsoft Debug Engine from a Python interpreter, it embeds a Python interpreter inside the Microsoft debugger WinDbg.
pygdb is a simple wrapper on the GNU debugger that provides a GTK interface to it. Works in Linux and OSX.
PyKd is like PyDbgEng and PyDbgExt combined into one - it can be both used from within the debugger and a
standalone Python interpreter. Being a younger project its still in alpha state, but looks very promising!
PyMem is a memory instrumentation library written in Python for Windows. It provides a subset of the functionality found in WinAppDbg, but if youre developing a tool that only needs to manipulate a process memory
you may find it convenient to support both backends and leave the choice to the user.
python-ptrace is another debugger library for Python with the same goals as WinAppDbg. Here the approach
used was to call the ptrace syscall, so naturally it works only on POSIX systems (BSD, Linux, maybe OSX). If
Kenshotos vtrace is not an option you could try combining this with WinAppDbg to implement a multiplatform
tool.
PythonGdb is an embedded Python interpreter for the GNU debugger. Its already included in GDB 7.
Radare is a console based multiplatform disassembler, debugger and reverse engineering framework. Python is
among the languages supported for plugins and scripting.
Universal Hooker (uhooker) is a Python library to implement function hooks in other processes. While its functionality overlaps with some of WinAppDbg, the hooks implementation of uhooker is superior. Unfortunately
the last update was in 2007. :(
See also the wonderful Python Arsenal for RE for an up to date reference of security related Python tools, available
online and in PDF format.
Chapter 1. Introduction
CHAPTER 2
Programming Guide
Downloads
Online help
Tutorial
Downloads
Online help
Downloads
Online help
Downloads
Online help
Downloads
Online help
2.1.3 Dependencies
Naturally you need the Python interpreter. Its recommended to use Python 2.7. Youll have to install the 32 bit VM
to debug 32 bit targets and the 64 bit VM for 64 bit targets. Both VMs can be installed on the same machine.
If youre still using Python 2.5 64-bit, youll need to install ctypes as well. This is needed to interface with the Win32
API, and WinAppDbg wont work without it. Newer versions of Python already have this module.
The following packages provide extra features and performance improvements, they are very recommended but not
mandatory.
Disassembler
WinAppDbg itself doesnt come with a disassembler, but all of the following are compatible. WinAppDbg will pick
the most suitable one automatically when needed, but you can also decide which one to use.
The diStorm disassembler by Gil Dabah:
Distorm 3.3, 32 bits (GPL v3)
Distorm 3.3, 64 bits (GPL v3)
Distorm 1.7.30, 32 bits (old version, BSD license)
Distorm 1.7.30, 64 bits (old version, BSD license)
The BeaEngine disassembler by BeatriX:
BeaEngine 3.1.0, 32 bits
BeaEngine 3.1.0, 64 bits
The Capstone disassembler by Nguyen Anh Quynh:
Capstone 1.0 bindings for Python 2.6 (Windows, 32 bits)
2.1.4 Install
Simply run the Windows installer package and follow the wizard.
If you prefer to install directly from the sources package, extract it to any temporary folder and run the following
command:
install.bat
You can also install WinAppDbg (stable versions only) from the Cheese Shop using any of the compatible package
managers:
PIP Installs Python
pip install winappdbg
2.1.5 Support
Minimim requirements:
Windows XP
Python 2.5
Recommended platform:
Windows 7
Python 2.7
It might work, but was not tested, under Windows 2000, Wine and ReactOS, and some bugs and missing features are
to be expected in these platforms (mainly due to missing APIs).
Python 3 support was experimental up to WinAppDbg 1.4, and was dropped with WinAppDbg 1.5. There are currently
no plans to support Python 3 in the near future - backwards compatibility would be broken and plenty of code would
need to be refactored just to port it.
While there are still some issues that need ironing out, it may be worth trying out faster Python interpreters such as
PyPy and IronPython.
If you find a bug or have a feature suggestion, dont hesitate to send an email to the winappdbg-users mailing list. Both
comments and complaints are welcome! :)
The following tables show which Python interpreters, operating systems and processor architectures are currently
supported. Full means all features are fully functional. Partial means some features may be broken and/or untested.
Untested means that though no testing was performed it should probably work. Experimental means its not expected
to work and although it might, you can encounter many bugs.
Python interpreters
Version
CPython 2.4 and
earlier
CPython 2.5 through
2.7
CPython 3.0 and
newer
PyPy 1.4 and earlier
PyPy 1.5 and 1.6
PyPy 1.7 and newer
IronPython 2.0 and
newer
Jython 2.5 and earlier
Status
not
supported
full
Notes
Use an older version of WinAppDbg in this case.
not
supported
not
supported
experimental
experimental
experimental
not
supported
Operating systems
Version
Windows 2000 and older
Windows XP
Windows Server 2003
Windows Server 2003 R2
Windows Vista
Windows 7
Windows Server 2008
Windows Server 2008 R2
Windows 8
Windows Server 2012
ReactOS
Linux (using Wine 1.2)
Linux (using Wine 1.3)
Windows + Cygwin
Windows Phone
Status
not supported
full
full
full
full
full
full
full
untested
untested
untested
untested
untested
not supported
not supported
Notes
Some required Win32 API functions didnt exist yet.
Architectures
Version
Intel (32 bits)
Intel (64 bits)
ARM
Status
full
full
not supported
Notes
2.1.7 License
This software is released under the BSD license, so as a user you are entitled to create derivative work and redistribute
it if you wish. A makefile is provided to automatically generate the source distribution package and the Windows
installer, and can also generate the documentation for all the modules using Epydoc. The sources to this documentation
are also provided and can be compiled with Sphinx.
2.2 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 dont need to be attached as a debugger for these classes to work.
The System class has many more features, so well be coming back to it later on in the tutorial.
2.2. Instrumentation
10
\tAccess
\tType"
2.2. Instrumentation
11
##
12
13
14
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 ),
2.2. Instrumentation
15
16
caption = window.get_text()
if caption is not None:
print "%s:\t%s" % ( handle, caption )
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.
2.2. Instrumentation
17
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
window.get_handle() )
window.style )
window.exstyle )
position
except WindowsError:
print "No window at those coordinates!"
18
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
window.get_handle() )
window.style )
window.exstyle )
position
def main():
try:
show_window( find_window() )
except WindowsError:
print "No window found!"
2.2. Instrumentation
19
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()
20
2.2. Instrumentation
21
22
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()
2.2. Instrumentation
23
24
2.3 Debugging
Debugging operations are performed by the Debug class. You can receive notification of debugging events by passing
a custom event handler to the Debug object when creating it - each event is represented by an Event object. Custom
event handlers can also be subclasses of the EventHandler class.
Debug objects can also set breakpoints, watches and hooks and support the use of labels.
2.3. Debugging
25
debug = Debug()
try:
# Start a new process for debugging.
debug.execv( sys.argv[ 1 : ] )
# Wait for the debugee to finish.
debug.loop()
# Stop the debugger.
finally:
debug.stop()
26
Example #4: killing the debugged process when the debugger is closed
Download
from winappdbg import Debug
import sys
# Instance a Debug object, set the kill on exit property to True.
debug = Debug( bKillOnExit = True )
# The user can stop debugging with Control-C.
try:
print "Hit Control-C to stop debugging..."
# Start a new process for debugging.
debug.execv( sys.argv[ 1 : ] )
# Wait for the debugee to finish.
debug.loop()
# If the user presses Control-C...
except KeyboardInterrupt:
print "Interrupted by user."
# Stop debugging. This kills all debugged processes.
debug.stop()
2.3. Debugging
27
28
Most notably, one such exception is always raised when attaching to a process, and then running a process
from the debugger (right after process initialization is complete).
Applicable events: Exceptions.
is_first_chance: If True, the exception hasnt been passed yet to the exception handlers of the debuggee.
If False, the exception was passed to the exception handlers but none of them could handle it.
Applicable events: Exceptions.
is_nested*: If True, the exception was raised when handing at least one more exception.
Many exceptions can be nested that way. Call get_nexted_exceptions to get a list of those nested exceptions.
Applicable events: Exceptions.
get_fault_address: Returns the memory address where the invalid access has occurred.
Applicable events: Exceptions caused by invalid memory access.
Example #6: handling debug events
Download
from winappdbg import Debug, HexDump, win32
def my_event_handler( event ):
# Get the process ID where the event occured.
pid = event.get_pid()
# Get the thread ID where the event occured.
tid = event.get_tid()
# Find out if its a 32 or 64 bit process.
bits = event.get_process().get_bits()
# Get the value of EIP at the thread.
address = event.get_thread().get_pc()
# Get the event name.
name = event.get_event_name()
# Get the event code.
code = event.get_event_code()
# If the event is an exception...
if code == win32.EXCEPTION_DEBUG_EVENT:
# Get the exception user-friendly description.
name = event.get_exception_description()
# Get the exception code.
code = event.get_exception_code()
# Get the address where the exception occurred.
try:
address = event.get_fault_address()
except NotImplementedError:
2.3. Debugging
29
address = event.get_exception_address()
# If the event is a process creation or destruction,
# or a DLL being loaded or unloaded...
elif code in ( win32.CREATE_PROCESS_DEBUG_EVENT,
win32.EXIT_PROCESS_DEBUG_EVENT,
win32.LOAD_DLL_DEBUG_EVENT,
win32.UNLOAD_DLL_DEBUG_EVENT ):
# Get the filename.
filename = event.get_filename()
if filename:
name = "%s [%s]" % ( name, filename )
# Show a descriptive message to the user.
print "-" * 79
format_string = "%s (0x%s) at address 0x%s, process %d, thread %d"
message = format_string % ( name,
HexDump.integer(code, bits),
HexDump.address(address, bits),
pid,
tid )
print message
def simple_debugger( argv ):
# Instance a Debug object, passing it the event handler callback.
debug = Debug( my_event_handler, bKillOnExit = True )
try:
# Start a new process for debugging.
debug.execv( argv )
# Wait for the debugee to finish.
debug.loop()
# Stop the debugger.
finally:
debug.stop()
30
very comfortable! Crash dumps stored that way are hard to read outside Python.
A more flexible way to store crash dumps is using the CrashDAO class. It uses SQLAlchemy to connect to any
supported SQL database, create the required tables if needed, and store multiple crash dumps in it. This is the preferred
method, since its easier to access and manipulate the information outside Python, and you can store crashes from
multiple machines into the same database.
Old versions of WinAppDbg (1.4 and older) supported DBM databases through the CrashContainer class, SQLite
databases with the CrashTable class, and SQL Server databases with the CrashTableMSSQL class. They are now
deprecated and, while still present for backwards compatibility (for the time being) its use is not recommended.
Example #7: saving crash dumps
Download
from sys import exit
from winappdbg import win32, Debug, HexDump, Crash
try:
from winappdbg import CrashDAO
except ImportError:
raise ImportError("Error: SQLAlchemy is not installed!")
def my_event_handler( event ):
# Get the event name.
name = event.get_event_name()
# Get the event code.
code = event.get_event_code()
# Get the process ID where the event occured.
pid = event.get_pid()
# Get the thread ID where the event occured.
tid = event.get_tid()
# Get the value of EIP at the thread.
pc = event.get_thread().get_pc()
# Show something to the user.
bits = event.get_process().get_bits()
format_string = "%s (%s) at address %s, process %d, thread %d"
message = format_string % ( name,
HexDump.integer(code, bits),
HexDump.address(pc, bits),
pid,
tid )
print message
# If the event is a crash...
if code == win32.EXCEPTION_DEBUG_EVENT and event.is_last_chance():
print "Crash detected, storing crash dump in database..."
# Generate a minimal crash dump.
crash = Crash( event )
2.3. Debugging
31
32
2.3. Debugging
33
What does it mean?: A single step exception was raised by the debugee.
When is it received?: When a hardware fault is triggered by the trap flag or the icebp opcode, or when a
hardware breakpoint set by your program is triggered.
guard_page:
What does it mean?: A guard page exception was raised by the debugee.
When is it received?: When a guard page is hit or when a page breakpoint set by your program is triggered.
In addition to all this, the EventHandler class provides a simple method for API hooking: the apiHooks class property.
This property is a dictionary of tuples, specifying which API calls to hook on what DLL libraries, and what parameters
does each call take (using ctypes definitions). Thats it! The EventHandler class will automatically hooks this APIs
for you when the corresponding library is loaded, and a method of your subclass will be called when entering and
leaving the API function.
Note: One thing to be careful with when hooking API functions: all pointers should be declared as having the void
type. Otherwise ctypes gets too helpful and tries to access the memory pointed to by them... and crashes, since those
pointers only work in the debugged process.
34
Parameters
, (PVOID, DWORD, DWORD, PVOID, DWORD, DWORD, HANDLE) ),
, (PVOID, DWORD, DWORD, PVOID, DWORD, DWORD, HANDLE) ),
],
# Hooks for the advapi32 library.
advapi32.dll : [
# Function
Parameters
( RegCreateKeyExA , (HKEY, PVOID, DWORD, PVOID, DWORD, REGSAM, PVOID, PVOID, PVOID) ),
( RegCreateKeyExW , (HKEY, PVOID, DWORD, PVOID, DWORD, REGSAM, PVOID, PVOID, PVOID) ),
],
}
2.3. Debugging
35
36
2.3. Debugging
37
the EventHandler class, only it doesnt need the function to be exported by a DLL library. Its useful for intercepting
calls to internal functions of the debugee, if you know where they are.
The watch_variable method sets a hardware breakpoint at the given address. Every time a read or write access is made
to that address, a callback function is called. Its useful for tracking accesses to a variable (for example, a member of
a C++ object in the heap). It works only on specific threads, to monitor the variable on the entire process you must set
a watch for each thread.
Finally, the watch_buffer method sets a page breakpoint at the given address range. Every time a read or write access
is made to that part of the memory a callback function is called. Its similar to watch_variable but it works for the
entire process, not just a single thread, and it allows any range to be specified (watch_variable only works for small
address ranges, from 1 to 8 bytes).
Debug objects also allow stalking. Stalking basically means to set one-shot breakpoints - that is, breakpoints that are
automatically disabled after theyre hit for the first time. The term was originally coined by Pedram Amini for his
Process Stalker tool, and this technique is key to differential debugging.
The stalking methods and their equivalents are the following:
Stalking method
stalk_at
stalk_function
stalk_variable
stalk_buffer
Equivalent to
break_at
hook_function
watch_variable
watch_buffer
38
2.3. Debugging
39
40
2.3. Debugging
41
# If its kernel32...
if module.match_name("kernel32.dll"):
# Get the process ID.
pid = event.get_pid()
# Get the address of GetProcAddress.
address = module.resolve( "GetProcAddress" )
# Set a breakpoint at the entry of the GetProcAddress function.
event.debug.break_at( pid, address, entering )
42
# This function will be called every time the procedure name buffer is accessed.
def accessed( self, event ):
# Show the user where were running.
thread = event.get_thread()
pc
= thread.get_pc()
code
= thread.disassemble( pc, 0x10 ) [0]
print "%s: %s" % (
HexDump.address(code[0], thread.get_bits()),
code[2].lower()
)
2.3. Debugging
43
2.3.8 Labels
Labels are used to represent memory locations in a more user-friendly way than simply using their addresses. This is
useful to provide a better user interface, both for input and output. Also, labels can be useful when DLL libraries in a
debugee are relocated on each run - memory addresses change every time, but labels dont.
For example, the label kernel32!CreateFileA always points to the CreateFileA function of the kernel32.dll library.
The actual memory address, on the other hand, may change across Windows versions.
In addition to exported functions, debugging symbols are used whenever possible.
A complete explanation on how labels work can be found at the Advanced Topics section of this document.
Example #15: getting the label for a given memory address
Download
from winappdbg import System, Process
def print_label( pid, address ):
# Request debug privileges.
System.request_debug_privileges()
# Instance a Process object.
process = Process( pid )
# Lookup its modules.
process.scan_modules()
# Resolve the requested label address.
label = process.get_label_at_address( address )
# Print the label.
print "%s == 0x%.08x" % ( label, address )
44
45
bk_magenta
bk_yellow
The matching bk_light and bk_dark functions control the brightness of the background, and they work just like light
and dark.
If you want to go back to the default text color, just call the default function. Theres also a bk_default function for
the background color, and a reset method that reverts to the default for both at the same time.
Example #1: printing text with colors
Download
from winappdbg import Color
# Can we use colors?
if Color.can_use_colors():
# Lets be polite: put everything in a try/except block
# so we can reset the console colors before quitting.
try:
# Set black background.
Color.bk_black()
# For each color...
for color in ( "red", "green", "blue", "cyan", "magenta", "yellow", "white" ):
# Set the color.
function = getattr( Color, color )
function()
# For each intensity...
for intensity in ( "light", "dark" ):
# Set the intensity.
function = getattr( Color, intensity )
function()
# Print a message.
print "This is %s %s text on black background." % ( intensity, color )
# Set black text.
Color.black()
# For each color...
for color in ( "red", "green", "blue", "cyan", "magenta", "yellow", "white" ):
# Set the background color.
function = getattr( Color, "bk_" + color )
function()
# For each intensity...
for intensity in ( "light", "dark" ):
# Set the background intensity.
function = getattr( Color, "bk_" + intensity )
function()
46
# Print a message.
print "This is black text on %s %s background." % ( intensity, color )
# Reset the console colors and quit.
finally:
Color.reset()
# No colors available!
else:
print "Cant use colors! Are you redirecting the output to a file?"
2.4.3 Logging
The Logger class implements a simple text logger that can send its output to standard output and/or to a file. There are
many libraries in Python that can do this, but this one has the advantage of being integrated with WinAppDbg objects.
2.4. Helper classes and functions
47
If you want to integrate other logging facilities to your scripts you can also use the functions from the static class
DebugLog, which contains all the WinAppDbg-related implementation of Logger.
Example #3: logging debug events
Download
from os.path import basename, splitext
from winappdbg import Debug, EventHandler, Logger, DebugLog
def main( argv ):
# The log file name will be based on the target executable file name.
logfile = basename( argv[ 0 ] )
logfile = splitext( logfile )[ 0 ] + ".log"
# Instance a global Logger object.
global logger
logger = Logger( logfile )
# Launch the debugger.
try:
simple_debugger( argv )
# On error log the exception and quit.
except:
logger.log_exc()
def my_event_handler( event ):
# Get the Logger object.
global logger
# Log the event.
logger.log_event( event )
def simple_debugger( argv ):
# Instance a Debug object, passing it the event handler callback.
debug = Debug( my_event_handler, bKillOnExit = True )
try:
# Start a new process for debugging.
debug.execv( argv )
# Wait for the debugee to finish.
debug.loop()
# Stop the debugger.
finally:
debug.stop()
48
integer: Convert a string to an integer. Supports decimal, hexadecimal (0x prefix), octal (0o prefix) and binary
(0b prefix).
If no prefix is given, this method still does its best to tell if its hexadecimal or not. If all fails, the number
is assumed to be decimal.
address: Read an hexadecimal value from a string. Unlike integer no attempt is made to detect other formats.
This function was conceived for parsing memory addresses, hence the name.
hexadecimal: Convert a strip of hexadecimal numbers (like OllyDbgs memory view) into binary data.
pattern: Similar to hexadecimal, but it also accepts question marks as wildcards for unknown values in fixed
positions. The return value is a regular expression that can perform a search for the given byte pattern.
is_pattern: Determine if the given argument is a valid hexadecimal pattern to be used with pattern.
integer_list_file: Read a list of integers from a file, assuming a specific file format.
Check the documentation for HexInput.integer_list_file for details.
string_list_file: Read a list of strings from a file, assuming a specific file format.
Check the documentation for HexInput.string_list_file for details.
mixed_list_file: Read a list of integers and strings from a file, assuming a specific file format.
Check the documentation for HexInput.mixed_list_file for details.
49
hexline: Dump a line of hexadecimal numbers from binary data. This is useful for printing bytes in a console
one line at a time.
hexa_word: Convert binary data to a string of hexadecimal WORDs.
hexa_dword: Convert binary data to a string of hexadecimal DWORDs.
hexa_qword: Convert binary data to a string of hexadecimal QWORDs.
hexblock_byte: Dump a block of hexadecimal BYTEs from binary data.
hexblock_word: Dump a block of hexadecimal WORDs from binary data.
hexblock_dword: Dump a block of hexadecimal DWORDs from binary data.
hexblock_qword: Dump a block of hexadecimal QWORDs from binary data.
hexblock_cb: Dump a block of binary data using a callback function to convert each line of text. This allows
you to customize the output.
50
code
= thread.disassemble_around( eip )
context = thread.get_context()
# Display the thread state.
print
print "-" * 79
print "Thread: %s" % HexDump.integer( tid )
print
print CrashDump.dump_registers( context )
print CrashDump.dump_code( code, eip ),
print "-" * 79
# Resume the process execution.
finally:
process.resume()
51
52
except WindowsError, e:
if e.winerror != win32.ERROR_FILE_NOT_FOUND:
raise
fullpath, basename = win32.SearchPath( None, sys.argv[1], .exe )
print "Full path: %s" % fullpath
print "Base name: %s" % basename
2.5.3 Example #3: enumerating heap blocks using the Toolhelp library
Download
from winappdbg.win32 import *
def print_heap_blocks( pid ):
# Determine if we have 32 bit or 64 bit pointers.
if sizeof(SIZE_T) == sizeof(DWORD):
fmt = "%.8x\t%.8x\t%.8x"
hdr = "%-8s\t%-8s\t%-8s"
else:
fmt = "%.16x\t%.16x\t%.16x"
hdr = "%-16s\t%-16s\t%-16s"
# Print a banner.
print "Heaps for process %d:" % pid
print hdr % ("Heap ID", "Address", "Size")
# Create a snapshot of the process, only take the heap list.
hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPHEAPLIST, pid )
# Enumerate the heaps.
heap = Heap32ListFirst( hSnapshot )
while heap is not None:
# For each heap, enumerate the entries.
entry = Heap32First( heap.th32ProcessID, heap.th32HeapID )
while entry is not None:
53
54
55
# Loop while calc.exe is alive and the time limit wasnt 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()
56
57
win32.PAGE_READWRITE:
"
win32.PAGE_WRITECOPY:
"
win32.PAGE_EXECUTE:
"
win32.PAGE_EXECUTE_READ:
"
win32.PAGE_EXECUTE_READWRITE:
"
win32.PAGE_EXECUTE_WRITECOPY:
"
"
win32.PAGE_GUARD:
win32.PAGE_NOCACHE:
win32.PAGE_WRITECOMBINE:
58
59
60
61
62
63
This simple implementation should suit most users needs, however if your project requires something more elaborated,
just derive from the Crash class and reimplement the signature() method with your own custom algorithm.
Where all components are optional and blank spaces are ignored.
The module is a module name as returned by Module.get_name().
The function is a string with an exported function name.
The ordinal is an integer with an exported function ordinal.
The offset is an integer number. It may be an offset from the module base address, or the function address. If
not specified, the default is 0.
If debugging symbols are available, they are used automatically in addition to exported functions. To get the debugging
symbols you need to first install the Microsoft Debugging Tools, and then either install the debugging symbols for your
version of Windows or set up your system to connect to the Microsoft Symbol Server.
Integer numbers in labels may be expressed in any format supported by HexInput.integer(), but by default they are in
hexadecimal format (for example 0x1234).
If only the module or the function are specified, but not both, the exclamation mark (!) may be omitted in fuzzy mode
(explained later in this document). However, resolving the label may be a little slower, as all module names have to be
checked to resolve the ambiguity.
65
Generating labels
To create a new label, use the parse_label static method of the Process class:
>>> import winappdbg
>>> winappdbg.Process.parse_label()
0x0
>>> winappdbg.Process.parse_label(None, None, None)
0x0
>>> winappdbg.Process.parse_label(None, None, 512)
0x200
>>> winappdbg.Process.parse_label("kernel32")
kernel32!
>>> winappdbg.Process.parse_label("kernel32", "CreateFileA")
kernel32!CreateFileA
>>> winappdbg.Process.parse_label("kernel32", 16)
kernel32!#0x10
>>> winappdbg.Process.parse_label("kernel32", None, 512)
kernel32!0x200
>>> winappdbg.Process.parse_label(None, "CreateFileA")
!CreateFileA
>>> winappdbg.Process.parse_label(None, 16)
!#0x10
>>> winappdbg.Process.parse_label(None, "CreateFileA", 512)
!CreateFileA+0x200
>>> winappdbg.Process.parse_label(None, 16, 512)
!#0x10+0x200
>>> winappdbg.Process.parse_label("kernel32", "CreateFileA", 512)
kernel32!CreateFileA+0x200
>>> winappdbg.Process.parse_label("kernel32", 16, 512)
kernel32!#0x10+0x200
# no arguments
# empty label
# offset or address
# module base
# exported function...
# ...by ordinal
# module base + offset
# function in any module...
# ...by ordinal
# ...plus an offset...
# ...by ordinal
# full label...
# ...by ordinal
The get_label_at_address method automatically guesses a good label for any given address in the process.
>>> import winappdbg
>>> aSystem = winappdbg.System()
>>> aSystem.request_debug_privileges()
True
>>> aSystem.scan()
>>> aProcess = aSystem.find_processes_by_filename("calc.exe")[0][0]
>>> aProcess.get_label_at_address(0x7c801a28)
kernel32+0x1a28!
Splitting labels
To split labels back to their original module, function and offset components there are two modes. The strict mode
allows only labels that have been generated with parse_label. The fuzzy mode has a more flexible syntax, and supports
some notation abuses that can only be resolved by a live Process instance.
The split_label method will automatically use the strict mode when called as a static method, and the fuzzy mode
when called as an instance method:
winappdbg.Process.split_method( "kernel32!CreateFileA" )
aProcessInstance.split_method( "CreateFileA" )
The sanitize_label method takes a fuzzy syntax label and converts it to strict syntax. This is useful when reading
labels from user input and storing them for later use, when the process is no longer being debugged.
66
To explicitly use the strict syntax mode, call the split_label_strict method:
>>> import winappdbg
>>> winappdbg.Process.split_label_strict(None)
(None, None, None)
>>> winappdbg.Process.split_label_strict()
(None, None, None)
>>> winappdbg.Process.split_label_strict(0x0)
(None, None, None)
>>> winappdbg.Process.split_label_strict(0x200)
(None, None, 512)
>>> winappdbg.Process.split_label_strict(0x200 ! )
(None, None, 512)
>>> winappdbg.Process.split_label_strict( ! 0x200)
(None, None, 512)
>>> winappdbg.Process.split_label_strict(kernel32 ! )
(kernel32, None, None)
>>> winappdbg.Process.split_label_strict(kernel32 ! CreateFileA)
(kernel32, CreateFileA, None)
>>> winappdbg.Process.split_label_strict(kernel32 ! # 0x10)
(kernel32, 16, None)
>>> winappdbg.Process.split_label_strict(kernel32 ! 0x200)
(kernel32, None, 512)
>>> winappdbg.Process.split_label_strict(kernel32 + 0x200 ! )
(kernel32, None, 512)
>>> winappdbg.Process.split_label_strict( ! CreateFileA)
(None, CreateFileA, None)
>>> winappdbg.Process.split_label_strict( ! # 0x10)
(None, 16, None)
>>> winappdbg.Process.split_label_strict( ! CreateFileA + 0x200)
(None, CreateFileA, 512)
>>> winappdbg.Process.split_label_strict( ! # 0x10 + 0x200)
(None, 16, 512)
>>> winappdbg.Process.split_label_strict(kernel32 ! CreateFileA + 0x200)
(kernel32, CreateFileA, 512)
>>> winappdbg.Process.split_label_strict(kernel32 ! # 0x10 + 0x200)
(kernel32, 16, 512)
# empty label
# empty label
# NULL pointer
# any memory address
# meaningless ! is ignored
# meaningless ! is ignored
# module base
# exported function...
# ...by ordinal
To explicitly use the fuzzy syntax mode, call the split_label_fuzzy method:
>>> import winappdbg
>>> aSystem = winappdbg.System()
>>> aSystem.request_debug_privileges()
True
>>> aSystem.scan()
>>> aProcess = aSystem.find_processes_by_filename("calc.exe")[0][0]
>>> aProcess.split_label_fuzzy( "kernel32" )
(kernel32, None, None)
>>> aProcess.split_label_fuzzy( "kernel32.dll" )
(kernel32, None, None)
>>> aProcess.split_label_fuzzy( "CreateFileA" )
(None, CreateFileA, None)
>>> aProcess.split_label_strict( "0x7c800000" )
# allows no ! sign
67
Resolving labels
The resolve_label method allows you to get the actual memory address the label points at the given process. If the
module is not loaded or the function is not exported, the method fails with an exception.
>>> import winappdbg
>>> aSystem = winappdbg.System()
>>> aSystem.request_debug_privileges()
True
>>> aSystem.scan()
>>> aProcess = aSystem.find_processes_by_filename("calc.exe")[0][0]
>>> aProcess.resolve_label( "kernel32" )
2088763392
>>> aProcess.resolve_label( "KERNEL32" )
2088763392
>>> aProcess.resolve_label( "kernel32.dll" )
2088763392
>>> aProcess.resolve_label( "kernel32 + 0x200" )
2088763904
>>> aProcess.resolve_label( "kernel32 ! CreateFileA" )
2088770088
>>> aProcess.resolve_label( "CreateFileA" )
2088770088
>>> aProcess.resolve_label( " # 16" )
2090010350
>>> aProcess.resolve_label( " # 0x10" )
2090010350
>>> aProcess.resolve_label( "kernel32 ! CreateFileA + 0x200" )
2088770600
>>> aProcess.resolve_label( "CreateFileA + 0x200" )
2088770600
>>> aProcess.resolve_label( "0x7c800000" )
2088763392
>>> aProcess.resolve_label( "0x7c800000 ! CreateFileA" )
2088770088
# module base
# module + offset
68
Breakpoint types
Debug objects support three kinds of breakpoints: code breakpoints, page breakpoints and hardware breakpoints.
Each kind of breakpoint causes an exception to be raised in the debugee. These exceptions are caught and handled
automatically by the debugger.
Breakpoints have to be defined first and enabled later. The rationale behind this is that you can define as many
breakpoints as you want, and then switch them on and off as you need to without having to delete them. This leads to
a more efficient use of resources, and is consistent with what one expects of debuggers.
Code breakpoints are defined by the define_code_breakpoint method, enabled by the enable_code_breakpoint
method. You can guess what are the methods to disable and erase code breakpoints. :)
Similarly, page breakpoints are defined by define_page_breakpoint, hardware breakpoints are defined by define_hardware_breakpoint, and so on.
Code breakpoints
Code breakpoints are implemented by inserting an int3 instruction (xCC) at the address specified. When a thread tries
to execute this instruction, a breakpoint exception is generated. Its global to the process because it overwrites the code
to break at.
When hit, code breakpoints trigger a breakpoint event at your event handler.
Lets look at the signature of define_code_breakpoint:
def define_code_breakpoint(self, dwProcessId, address,
condition = True,
action = None):
Where dwProcessId is the Id of the process where we want to set the breakpoint and address is the location of the
breakpoint in the process memory. The other two parameters are optional and will be explained later.
Page breakpoints
Page breakpoints are implemented by changing the access permissions of a given memory page. This causes a guard
page exception to be generated when the given page is accessed anywhere in the code of the process.
When hit, page breakpoints trigger a guard_page event at your event handler.
Lets see the signature of define_page_breakpoint:
def define_page_breakpoint(self, dwProcessId, address,
pages = 1,
condition = True,
action = None):
Where dwProcessId is the same. But now address needs to be page-aligned and pages is the number of pages
covered by the breakpoint. This is because VirtualProtectEx() works only with entire pages, you cant change the
access permissions on individual bytes.
Hardware breakpoints
Hardware breakpoints are implemented by writing to the debug registers (DR0-DR7) of a given thread, causing a
single step exception to be generated when the given address is accessed anywhere in the code for that thread only.
69
Its important to remember the debug registers have different values for each thread, so this cant be done global to the
process (you can set the same breakpoint in all the threads, though).
When hit, hardware breakpoints trigger a single_step event at your event handler.
The signature of define_hardware_breakpoint is this:
def define_hardware_breakpoint(self, dwThreadId, address,
triggerFlag = BP_BREAK_ON_ACCESS,
sizeFlag = BP_WATCH_DWORD,
condition = True,
action = None):
otherwise it isnt. This allows you to set breakpoints that will only trigger an event under specific conditions (for
example, only stop the execution when EAX equals 0x100, ignore it otherwise).
# condition callback
def eax_is_100(event):
aThread = event.get_thread()
Eax
= aThread.get_context()[Eax]
if Eax == 0x100:
# We are interested on this!
return True
# False alarm, ignore it...
return False
# Will only break when eax is 100 in that process at that address
def break_when_eax_is_100(debug, pid, address):
debug.define_code_breakpoint(pid, address, condition = eax_is_100)
debug.enable_code_breakpoint(pid, address)
The action parameter allows you to set another callback. When not used, the breakpoint is interactive, meaning when
its hit (and its condition callback returns True) the event handler method is called. But when its used, the breakpoint
is automatic, and that means this callback is called instead of the event handler method.
Automatic breakpoints are useful for setting tasks to be done behind the back of the event handler, so they dont
have to be treated as special cases by your event handler routines.
# action callback
def change_eax_value(event):
# Get the thread that hit the breakpoint
aThread = event.get_process()
# Set a new value for the EAX register
aThread.set_register(Eax, 0xBAADF00D)
# Will automatically change the return value of the function
def auto_change_return_value(debug, pid, address):
# address must be the location of the ret instruction
debug.define_code_breakpoint(pid, address, action = change_eax_value)
debug.enable_code_breakpoint(pid, address)
Breakpoints can be both conditional and automatic. Here is another example reusing the code above:
# Will automatically change the return value of the function,
# but only when the original value was 0x100
def conditionally_change_return_value(debug, pid, address):
# address must be the location of the ret instruction
debug.define_code_breakpoint(pid, address, condition = eax_is_100,
action = change_eax_value)
debug.enable_code_breakpoint(pid, address)
71
One-shot breakpoints
Breakpoints of all types can also be one-shot. This means theyre automatically disabled after being hit. This is useful
for one time events, for example a debugger might want to set a one-shot breakpoint at the next instruction for tracing.
You could also set one-shot breakpoints to do code coverage, where multiple executions of the same code are not
relevant.
Note that one-shot breakpoints are only disabled, not deleted, so you can enable them again. Any disabled breakpoint
can be enabled again, as a normal breakpoint or as one-shot, independently of how its been used before.
To set one-shot breakpoints, after defining them use one of the enable_one_shot_code_breakpoint, enable_one_shot_page_breakpoint or enable_one_shot_hardware_breakpoint methods to enable it.
# Will automatically change the return value of the function,
# but only when the original value was 0x100,
# and only the next time the function is called
def conditionally_change_return_value(debug, pid, address):
# address must be the location of the ret instruction
debug.define_code_breakpoint(pid, address, condition = eax_is_100,
action = change_eax_value)
debug.enable_one_shot_code_breakpoint(pid, address)
73
74
pstrings.py :
Dumps all ASCII strings from a live process.
2.8.3 Miscellaneous
SelectMyParent.py :
Allows you to create a new process specifying any other process as its parent, and inherit its handles.
See the blog post by Didier Stevens for the original C version.
hexdump.py :
Shows an hexadecimal dump of the contents of a file.
75