Gray Hat Python

Gray Hat Python: Python Programming for Hackers and Reverse Engineers · Justin Seitz ·200 pages

Python for Windows security tooling — debugger internals, all three breakpoint types, function hooking, DLL/code injection, fuzzing with Sulley, and IDAPython automation. Uses ctypes to interface directly with the Windows API.

Capabilities (9)
  • Use ctypes to call Windows API functions from Python for security tooling
  • Implement Windows debugger loop with WaitForDebugEvent and debug event handlers
  • Implement three breakpoint types: software (INT3), hardware (DR registers), memory (page guard)
  • Hook functions using JMP patching with trampoline for original function call
  • Inject DLLs into remote processes via VirtualAllocEx + WriteProcessMemory + CreateRemoteThread
  • Build mutation fuzzers for network protocols with crash detection
  • Use Sulley framework for structured protocol fuzzing with blocks and primitives
  • Script IDA Pro with IDAPython for automated analysis, function naming, and xref traversal
  • Use PyEmu for safe x86 emulation and malware analysis without code execution
How to use

Install this skill and Claude can design Windows security tooling in Python using ctypes — including debugger event loops, all three breakpoint types, function hook trampolines, DLL injection sequences, mutation fuzzers, and IDAPython automation scripts

Why it matters

Building custom security tools that interface directly with the Windows API produces instrumentation that is harder to detect and more precisely targeted than generic frameworks, while understanding debugger and injection mechanics enables analysts to recognize and detect these patterns in malware

Example use cases
  • Implementing a hardware breakpoint on a target function to log call arguments without patching memory (avoiding INT3 detection by the monitored process)
  • Building a mutation fuzzer for a proprietary binary protocol that catches access violations to identify the crash offset for further exploit development
  • Writing an IDAPython script to scan all functions for known cryptographic S-box constants and automatically rename matching functions with a crypto_ prefix

Gray Hat Python Skill

Core Philosophy

Python’s ctypes library lets you call Windows API functions directly, making it ideal for building security tools: debuggers, hooks, fuzzers, and injectors. Write Python to do what would otherwise require C.


ctypes Fundamentals for Windows Hacking

from ctypes import *
import ctypes

# Load a DLL
kernel32 = windll.kernel32
# Call a function
pid = kernel32.GetCurrentProcessId()

# C struct in Python
class CONTEXT(Structure):
    _fields_ = [
        ("ContextFlags", c_ulong),
        ("Eax", c_ulong),
        ("Ebx", c_ulong),
        # ... etc
    ]

# Pass by reference
pid = c_ulong(0)
kernel32.GetWindowThreadProcessId(hwnd, byref(pid))

Debugger Design Fundamentals

Debug Events

A debugger receives events from the OS when debugging a process:

Event CodeMeaning
EXCEPTION_DEBUG_EVENTBreakpoint hit, access violation, etc.
CREATE_THREAD_DEBUG_EVENTNew thread created
CREATE_PROCESS_DEBUG_EVENTProcess created
EXIT_THREAD_DEBUG_EVENTThread exited
EXIT_PROCESS_DEBUG_EVENTProcess exited
LOAD_DLL_DEBUG_EVENTDLL loaded
OUTPUT_DEBUG_STRING_EVENTOutputDebugString called

Debugger Loop

def run(self):
    while self.debugger_active:
        self.get_debug_event()

def get_debug_event(self):
    debug_event = DEBUG_EVENT()
    if kernel32.WaitForDebugEvent(byref(debug_event), INFINITE):
        if debug_event.dwDebugEventCode == EXCEPTION_DEBUG_EVENT:
            self.handle_exception(debug_event)
        kernel32.ContinueDebugEvent(
            debug_event.dwProcessId,
            debug_event.dwThreadId,
            DBG_CONTINUE
        )

Breakpoint Types

Software Breakpoints (INT 3 / 0xCC)

  • How: replace first byte of instruction with 0xCC (INT 3 opcode)
  • When CPU hits INT 3: raises EXCEPTION_BREAKPOINT
  • Debugger catches exception, restores original byte, handles event
  • Detectable: reading from the patched memory shows 0xCC
def bp_set(self, address):
    if address not in self.breakpoints:
        original_byte = self.read_process_memory(address, 1)
        self.write_process_memory(address, "\xCC")
        self.breakpoints[address] = original_byte

def handle_breakpoint(self):
    current_ip = self.get_instruction_pointer()
    current_ip -= 1  # INT3 advances EIP by 1
    self.set_instruction_pointer(current_ip)
    if current_ip in self.breakpoints:
        self.write_process_memory(current_ip, self.breakpoints[current_ip])

Hardware Breakpoints

  • How: use x86 debug registers (DR0–DR3 for addresses, DR7 for control)
  • Triggered by CPU itself (no byte modification)
  • Types: execution, write, read/write (data watchpoints)
  • Maximum 4 active at once
  • Stealthy: can’t be detected by reading memory

Memory Breakpoints

  • How: set page permissions to PAGE_NOACCESS or PAGE_GUARD
  • Any access to the page triggers an exception
  • Used to monitor large memory regions

Hooking

Soft Hooking (User-mode)

Replace function prologue with JMP to your hook handler:

Original: push ebp; mov ebp, esp; ...
Hooked:   jmp [hook_function]; ...

Hook receives original arguments, can modify them, then call original function (trampoline pattern).

PyDbg Hook Example

def my_hook(dbg, args):
    print "CreateFile called: %s" % dbg.smart_dereference(args[0])
    return DBG_CONTINUE

dbg.bp_set_mem(CreateFileA_address, "my_CreateFileA_hook")
dbg.set_callback(LOAD_DLL_DEBUG_EVENT, my_hook)

DLL and Code Injection

DLL Injection (Windows)

1. OpenProcess(PROCESS_ALL_ACCESS, target_pid)
2. VirtualAllocEx(process_handle, ..., len(dll_path), MEM_COMMIT, PAGE_READWRITE)
3. WriteProcessMemory(process_handle, remote_addr, dll_path, len(dll_path))
4. CreateRemoteThread(process_handle, ..., LoadLibraryA, remote_addr, ...)

The remote thread calls LoadLibraryA with the DLL path → DLL’s DllMain executes in the target process.

Code Injection

Instead of a DLL path, write shellcode directly to remote process memory and execute it:

shellcode = "\x90\x90\xcc"  # NOPs + INT3
remote_mem = kernel32.VirtualAllocEx(h_process, 0, len(shellcode),
                                      MEM_COMMIT, PAGE_EXECUTE_READWRITE)
kernel32.WriteProcessMemory(h_process, remote_mem, shellcode, len(shellcode), 0)
kernel32.CreateRemoteThread(h_process, None, 0, remote_mem, None, 0, 0)

Fuzzing

Dumb Fuzzer Pattern

import socket, struct

def send_request(data):
    s = socket.socket()
    s.connect(("target", 1234))
    s.send(data)
    response = s.recv(1024)
    s.close()
    return response

# Mutation fuzzing
original = "GET / HTTP/1.0\r\n\r\n"
for i in range(100):
    fuzz_data = original + "A" * (i * 100)
    try:
        send_request(fuzz_data)
    except:
        print "Crash at %d bytes" % (i * 100)
        break

Sulley Framework Concepts

Sulley is a Python fuzzing framework that generates structured mutations:

  • Primitives: s_string, s_int, s_binary, s_delim
  • Blocks: group related fields, apply transforms (length calculation, checksum)
  • Sessions: manage connections, track state, detect crashes
import sulley
s_initialize("request")
if s_block_start("header"):
    s_string("HELO", fuzzable=True)
    s_delim(" ", fuzzable=False)
    s_string("target", fuzzable=True)
    s_static("\r\n")
s_block_end()

IDAPython

Script IDA Pro for automated analysis:

# Get function name at address
print idc.GetFunctionName(0x401000)

# Iterate all functions
for func_ea in idautils.Functions():
    print "%x: %s" % (func_ea, idc.GetFunctionName(func_ea))

# Find all calls to a function
for xref in idautils.CodeRefsTo(0x401234, 0):
    print "Called from: %x" % xref.frm

# Rename function based on analysis
idc.MakeNameEx(0x401000, "my_function_name", SN_NOWARN)

# Comment at address
idc.MakeComm(0x401020, "Buffer overflow here")

Use cases: automated vulnerability finding, function naming via heuristics, crypto constant identification, batch analysis of malware samples.


PyEmu: Emulation for Analysis

PyEmu executes x86 instructions without running actual code — useful for:

  • Analyzing malware without executing it
  • Tracing through obfuscated code
  • Understanding algorithm behavior
emu = PEPyEmu("target.exe")
emu.execute(start_address)
emu.set_memory_handler(0x1000, my_mem_read_handler, my_mem_write_handler)
emu.set_register_handler("EAX", my_eax_write_handler)

Windows Debugger Attachment Patterns

# Attach to running process
def attach(self, pid):
    h_process = kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, pid)
    if kernel32.DebugActiveProcess(pid):
        self.debugger_active = True
        self.h_process = h_process

# Launch new process under debugger
def load(self, path_to_exe):
    creation_flags = DEBUG_PROCESS
    startupinfo = STARTUPINFO()
    process_information = PROCESS_INFORMATION()
    kernel32.CreateProcessA(path_to_exe, None, None, None, None,
                            creation_flags, None, None,
                            byref(startupinfo), byref(process_information))
    self.h_process = self.open_process(process_information.dwProcessId)