The Win32 API wrappers
The win32 submodule provides a collection of useful API wrappers for most operations needed by a debugger. This will allow you to perform any task that the abstraction layer for some reason can’t deal with, or won’t deal with in the way you need. In most cases you won’t need to resort to this, but it’s important to know it’s there.
Except in some rare cases, the rationale to port the API calls to Python was:
Take Python basic types as input, return Python basic types as output.
Functions that in C take an output pointer and a size as input, in Python take neither and return the output data directly (the wrapper takes care of allocating the memory buffers).
Functions that in C have to be called twice (first to get the buffer size, then to get the data) in Python only have to be called once (returns the data directly).
Functions in C with more than one output pointer return tuples of data in Python.
Functions in C that return an error condition, raise a Python exception (WindowsError) on error and return the data on success.
Default parameter values were added when possible. The default for all optional pointers is NULL. The default flags are usually the ones that provide all possible access (for example, the default flags value for GetThreadContext is CONTEXT_ALL)
For APIs with ANSI and Widechar versions, both versions are wrapped. If at least one parameter is a Unicode string en Widechar version is called (and all string parameters are converted to Unicode), otherwise the ANSI version is called. Either ANSI or Widechar versions can be used explicitly (for example, CreateFile can be called as CreateFileA or CreateFileW).
All handles returned by API calls are wrapped around the Handle class. This allows you to use the with statement to ensure proper cleanup, and causes handles to be closed automatically when they go out of scope, thus preventing handle leaks.
Example #1: finding a DLL in the search path
import sys
from winappdbg import win32
try:
fullpath, basename = win32.SearchPath(None, sys.argv[1], ".dll")
except WindowsError as 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)
Example #2: killing a process by attaching to it
import _thread as thread
import sys
from winappdbg import win32
def processKiller(dwProcessId):
# Attach to the process.
win32.DebugActiveProcess(dwProcessId)
# Quit the current thread.
thread.exit()
Example #3: enumerating heap blocks using the Toolhelp library
from winappdbg.win32 import (
DWORD,
SIZE_T,
TH32CS_SNAPHEAPLIST,
CreateToolhelp32Snapshot,
Heap32First,
Heap32ListFirst,
Heap32ListNext,
Heap32Next,
sizeof,
)
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:
# Print the heap id and the entry address and size.
print(fmt % (entry.th32HeapID, entry.dwAddress, entry.dwBlockSize))
# Next entry in the heap.
entry = Heap32Next(entry)
# Next heap in the list.
heap = Heap32ListNext(hSnapshot)
# No need to call CloseHandle, the handle is closed automatically when it goes out of scope.
return
Example #4: enumerating modules using the Toolhelp library
from winappdbg.win32 import (
DWORD,
SIZE_T,
TH32CS_SNAPMODULE,
CreateToolhelp32Snapshot,
Module32First,
Module32Next,
sizeof,
)
def print_modules(pid):
# Determine if we have 32 bit or 64 bit pointers.
if sizeof(SIZE_T) == sizeof(DWORD):
fmt = "%.8x %.8x %s"
hdr = "%-8s %-8s %s"
else:
fmt = "%.16x %.16x %s"
hdr = "%-16s %-16s %s"
# Print a banner.
print("Modules for process %d:" % pid)
print()
print(hdr % ("Address", "Size", "Path"))
# Create a snapshot of the process, only take the heap list.
hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid)
# Enumerate the modules.
module = Module32First(hSnapshot)
while module is not None:
# Print the module address, size and pathname.
print(
fmt
% (
module.modBaseAddr,
module.modBaseSize,
module.szExePath.decode("latin-1"),
)
)
# Next module in the process.
module = Module32Next(hSnapshot)
# No need to call CloseHandle, the handle is closed automatically when it goes out of scope.
return
Example #5: enumerating device drivers
from winappdbg.win32 import (
DWORD,
SIZE_T,
EnumDeviceDrivers,
GetDeviceDriverBaseName,
GetDeviceDriverFileName,
sizeof,
)
def print_drivers(fFullPath=False):
# Determine if we have 32 bit or 64 bit pointers.
if sizeof(SIZE_T) == sizeof(DWORD):
fmt = "%.08x\t%s"
hdr = "%-8s\t%s"
else:
fmt = "%.016x\t%s"
hdr = "%-16s\t%s"
# Get the list of loaded device drivers.
ImageBaseList = EnumDeviceDrivers()
# Filter out None values (happens when SeDebugPrivilege is not enabled)
ValidDrivers = [ImageBase for ImageBase in ImageBaseList if ImageBase is not None]
print("Total entries returned: %d" % len(ImageBaseList))
print("Valid device drivers found: %d" % len(ValidDrivers))
if len(ValidDrivers) == 0 and len(ImageBaseList) > 0:
print("WARNING: EnumDeviceDrivers returned NULL addresses.")
print("This typically means SeDebugPrivilege is not enabled.")
print("Try running this script as Administrator.")
return
if len(ValidDrivers) == 0:
print("No device drivers found.")
return
print()
print(hdr % ("Image base", "File name"))
# For each valid device driver...
for ImageBase in ValidDrivers:
# Get the device driver filename.
if fFullPath:
DriverName = GetDeviceDriverFileName(ImageBase)
else:
DriverName = GetDeviceDriverBaseName(ImageBase)
# Print the device driver image base and filename.
print(fmt % (ImageBase, DriverName))