You can implement process instrumentation in your Python scripts by using the provided set of classes: System, Process, Thread and Module. 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 processes. A Process object contains Thread and Module objects.
Note
You don’t need to be attached as a debugger for these classes to work.
The System class basically behaves like a snapshot of the running processes. It can enumerate processes and perform operations on a batch of processes.
from winappdbg import System
# Request debugging privileges for the current process
# This is needed to get some information from services
# (Try commenting out this line to see what happens!)
System.request_debug_privileges()
# Create a system snaphot
system = System()
# Now we can enumerate the running processes
for process in system:
print "%d:\t%s" % ( process.get_pid(), process.get_filename() )
from winappdbg import System
import sys
# Instance a System object
system = System()
# Get the target application
command_line = system.argv_to_cmdline( sys.argv[ 1 : ] )
# Start a new process
system.start_process( command_line ) # see the docs for more options
The Process class lets you manipulate any process in the system. You can get a Process instance by enumerating a System snapshot, or instancing one directly by providing the process ID.
A Process object allows you to manipulate the process memory (read, write, allocate and free operations), create new threads in the process, and more. It also acts as a snapshot of it’s threads and DLL modules.
from winappdbg import Process, HexDump
def print_threads_and_modules( pid ):
# Instance a Process object
process = Process( pid )
print "Process %d" % process.get_pid()
# Now we can enumerate the threads in the process...
print "Threads:"
for thread in process.iter_threads():
print "\t%d" % thread.get_tid()
# ...and the modules in the process
print "Modules:"
for module in process.iter_modules():
print "\t%s\t%s" % ( HexDump.address( module.get_base() ), module.get_filename() )
from winappdbg import Process
def process_kill( pid ):
# Instance a Process object
process = Process( pid )
# Kill the process
process.kill()
from winappdbg import Process
def process_read( pid, address, length ):
# Instance a Process object
process = Process( pid )
# Read the process memory
data = process.read( address, length )
# Return a Python string with the memory contents
return data
from winappdbg import Process
def load_dll( pid, filename ):
# Instance a Process object
process = Process( pid )
# Load the DLL library in the process
process.inject_dll( filename )
from winappdbg import win32, Process, HexDump
def print_memory_map( pid ):
# Instance a Process object
process = Process( pid )
# Get the process memory map
memoryMap = process.get_memory_map()
# Now you could do this...
#
# from winappdbg import CrashDump
# print CrashDump.dump_memory_map( memoryMap ),
#
# ...but let's do it the hard way:
# For each memory block in the map...
print "Address \tSize \tState \tAccess \tType"
for mbi in memoryMap:
# Address and size of memory block
BaseAddress = HexDump.address(mbi.BaseAddress)
RegionSize = HexDump.address(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:
## Protect = "0x%.08x" % mbi.Protect
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 += "-"
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 = "Free "
else:
Type = "Unknown "
# Print the memory block information
fmt = "%s\t%s\t%s\t%s\t%s"
print fmt % ( BaseAddress, RegionSize, State, Protect, Type )
A Thread object lets you manipulate any thread in any process in the system. You can get a Thread instance by enumerating a Process snapshot, or instancing one manually by providing the thread ID.
You can manipulate the thread context (read and write to it’s registers), perform typical debugger operations (getting stack traces, etc), suspend and resume execution, and more.
from winappdbg import Process, System
def freeze_threads( pid ):
# Request debug privileges
System.request_debug_privileges()
# Instance a Process object
process = Process( pid )
# This would also do the trick...
#
# process.suspend()
#
# ...but let's do it the hard way:
# Lookup the threads in the process
process.scan_threads()
# For each thread in the process...
for thread in process:
# Suspend the thread execution
thread.suspend()
def unfreeze_threads( pid ):
# Request debug privileges
System.request_debug_privileges()
# Instance a Process object
process = Process( pid )
# This would also do the trick...
#
# process.resume()
#
# ...but let's do it the hard way:
# Lookup the threads in the process
process.scan_threads()
# For each thread in the process...
for thread in process:
# Resume the thread execution
thread.resume()
from winappdbg import Thread, CrashDump, System
def print_thread_context( tid ):
# Request debug privileges
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 ),
from winappdbg import Thread, CrashDump, System
def print_thread_disassembly( tid ):
# Request debug privileges
System.request_debug_privileges()
# Instance a Thread object
thread = Thread( tid )
# Suspend the thread execution
thread.suspend()
# Get the thread's currently running code
try:
eip = thread.get_pc()
code = thread.disassemble_around( eip )
# You can also do this:
# code = thread.disassemble_around_pc()
# Or even this:
# process = thread.get_process()
# code = process.disassemble_around(eip)
# Resume the thread execution
finally:
thread.resume()
# Display the thread context
print
print CrashDump.dump_code( code, eip ),
A Module object lets you manipulate any thread in any process in the system. You can get a Module instance by enumerating a Process snapshot. Module objects can be used to resolve the addresses of exported functions in the process address space.
from winappdbg import Process, System
def print_api_address( pid, modName, procName ):
# Request debug privileges
System.request_debug_privileges()
# Instance a Process object
process = Process( pid )
# Lookup it's modules
process.scan_modules()
# Get the module
module = process.get_module_by_name( modName )
if not module:
print "Module not found: %s" % modName
return
# Resolve the requested API function address
address = module.resolve( procName )
# Print the address
if address:
print "%s!%s == 0x%.08x" % ( modName, procName, address )
else:
print "Could not resolve %s in module %s" % (procName, modName)