How CAPEv2 Breakpoints (bp0-bp3) Enable Dynamic Unpacking: A Technical Guide
CAPEv2 breakpoints utilize hardware debug registers (DR0-DR3) to set up to four execution breakpoints that can bypass anti-debug tricks, capture instruction traces, and automatically dump unpacked payloads without manual modification of malware samples.
The kevoreilly/capev2 sandbox provides a powerful hardware-assisted debugging mechanism through its bp0 to bp3 options, allowing analysts to perform dynamic unpacking on protected binaries. By leveraging these CAPEv2 breakpoints in combination with actions like skip, goto, and step2oep, you can neutralize packer stubs and capture the original entry point execution in an automated pipeline.
Understanding CAPEv2 Breakpoint Options (bp0-bp3)
CAPEv2 exposes four hardware breakpoint slots (bp0, bp1, bp2, bp3) that map directly to the x86 debug registers DR0 through DR3. These breakpoints are resolved at runtime and can target various address formats.
Supported Address Formats for Hardware Breakpoints
Each CAPEv2 breakpoint accepts one of the following formats:
- RVA (Relative Virtual Address):
bp0=0x1234– resolved against the image base - VA (Virtual Address):
bp0=0x7ff60000– used directly without translation - Module Export:
bp0=kernel32.dll::CreateFileW– resolved to the function address - Special Token
ep:bp0=ep– entry point of the main module or DLL entry point
Dynamic Base Selection Mechanisms
When using RVA format, CAPEv2 determines the image base through three mechanisms:
base-on-api: Waits for a specific API call (e.g.,base-on-api=NtReadFile), extracts the module base from the call stack, and applies subsequent RVA offsets to that baseeptoken: Watches module load events, calculates the entry point address from the image base plus entry point RVA- Default fallback: Assumes the first loaded module (
main.exe) if no base information is supplied
The base-on-api method is particularly effective for unpackers that relocate themselves or load additional modules after initial execution.
Trace Configuration with depth and count
After a CAPEv2 breakpoint triggers, you can control the instruction trace behavior:
depth: Determines call handlingdepth=0(default): Step over callsdepth>0: Step into functions to specified recursion depth
count: Limits the number of traced instructions (default: 128)
Example configuration for capturing 500 instructions at the unpacked entry point without entering subroutines:
bp3=ep,depth=0,count=500
Breakpoint Actions for Anti-Debug Bypass and Execution Control
CAPEv2 breakpoints support programmable actions that execute when the breakpoint hits. These actions, specified as action0 through action3, enable automated unpacking without manual intervention.
| Action | Syntax | Effect |
|---|---|---|
| Skip | action0=skip |
Skips the current instruction (registers unchanged, execution continues at next instruction) |
| Return | action0=ret |
Forces return from current function by replacing EIP/RIP with the stack's return address |
| Goto API | action0=goto:ntdll::NtAllocateVirtualMemory |
Patches execution to jump to specified API, bypassing anti-debug checks |
| Step to OEP | action0=step2oep |
Traces execution until reaching the original entry point (used by UPX rule) |
| Set Zero Flag | action0=SetZeroFlag |
Manipulates CPU flags to alter conditional jump outcomes |
Action strings are parsed by the monitor component in lib/cuckoo/core/monitor.py, which coordinates with the debugger to apply the specified behavior.
Practical Workflow: Using CAPEv2 Breakpoints for Dynamic Unpacking
Implementing an effective unpacking strategy with CAPEv2 breakpoints follows a systematic workflow that combines breakpoint placement, anti-debug neutralization, and execution tracing.
- Locate the unpacker stub using existing YARA rules (e.g.,
Guloader.yar,UPX.yar) or manual analysis in a disassembler - Create initial breakpoints on stub addresses using
bp0with RVA or$macro references (e.g.,bp0=$trap0) - Attach neutralizing actions such as
action0=skiporaction0=goto:ntdll::NtAllocateVirtualMemoryto bypass anti-debug checks - Set entry point tracing with
bp3=epto capture execution at the unpacked payload's entry point - Configure trace parameters using
depth=0(step-over) andcount=500(instruction limit) to capture sufficient unpacked behavior - Submit the sample with the combined
cape_optionsstring via the Web UI or API - Inspect results in the Debugger tab or
debug.logto analyze the unpacked code execution
Real-World Example: Unpacking UPX Binaries
The built-in analyzer/windows/data/yara/UPX.yar rule demonstrates production-grade unpacking with CAPEv2 breakpoints:
cape_options = "bp0=$upx32*,bp0=$upx64*,hc0=1,action0=step2oep,imprec=1"
This configuration:
- Sets
bp0on the UPX stub entry (wildcarded for 32/64-bit) - Uses
action0=step2oepto automatically trace from the stub to the original entry point - Enables import reconstruction with
imprec=1
To capture the unpacked execution trace, extend the options:
cape_options = "bp0=$upx32*,bp0=$upx64*,hc0=1,action0=step2oep,imprec=1,bp1=ep,depth=0,count=500"
Now the sandbox bypasses the UPX packer, reconstructs imports, and records 500 instructions of the unpacked payload's execution.
Key Source Files and Implementation Details
Understanding the implementation of CAPEv2 breakpoints requires familiarity with these core components:
| File | Purpose |
|---|---|
docs/book/src/usage/monitor.rst |
Comprehensive documentation of bp0-bp3 syntax, ep token, base-on-api, and trace options |
[lib/cuckoo/core/monitor.py](https://github.com/kevoreilly/capev2/blob/master/lib/cuckoo/core/monitor.py) |
Core implementation parsing cape_options, resolving breakpoints, setting hardware registers (DR0-DR3), and coordinating actions |
[lib/cuckoo/core/trace.py](https://github.com/kevoreilly/capev2/blob/master/lib/cuckoo/core/trace.py) |
Handles single-step tracing after breakpoint hits, implementing depth (step-into vs step-over) and count logic |
analyzer/windows/data/yara/Guloader.yar |
Production example of complex breakpoint chains with multiple bp0 assignments and action0=skip for anti-debug bypass |
analyzer/windows/data/yara/UPX.yar |
Demonstrates action0=step2oep for automated original entry point detection in packed binaries |
Summary
CAPEv2 breakpoints provide hardware-assisted debugging capabilities that enable automated dynamic unpacking of protected malware samples. Key takeaways include:
- Four hardware slots (
bp0-bp3) map to x86 debug registers DR0-DR3, supporting RVA, VA, module exports, and the specialeptoken - Dynamic base resolution via
base-on-apiensures breakpoints hit correctly even when unpackers relocate code or load additional modules - Programmable actions (
skip,ret,goto,step2oep) neutralize anti-debug checks and automate navigation to unpacked payloads - Configurable tracing with
depthandcountparameters captures precise instruction sequences at the original entry point - YARA integration allows persistent breakpoint configurations in rules like
Guloader.yarandUPX.yarfor repeatable analysis
Frequently Asked Questions
How do I set a CAPEv2 breakpoint on a specific API call rather than a raw address?
Use the module export syntax in your cape_options string. For example, bp0=kernel32.dll::CreateFileW tells the debugger to resolve and break on the address of CreateFileW at runtime. This is particularly useful when targeting unpacked code that dynamically imports APIs.
What is the difference between using bp0=ep and action0=step2oep for unpacking?
bp0=ep sets a hardware breakpoint on the entry point address of the main module, which works well if you know where the unpacked payload starts. action0=step2oep is an active tracing action used in rules like UPX.yar that single-steps through the unpacker stub until it detects the transition to the original entry point, automatically handling cases where the OEP is obfuscated or calculated at runtime.
Why does my breakpoint miss when analyzing a packed sample that relocates itself?
Packed samples often unpack to a different memory location than their initial load address. If you specify an RVA without a base address, the debugger defaults to the first loaded module (main.exe), which may be incorrect. Use base-on-api=ApiName to dynamically capture the correct image base from the call stack when a specific API is invoked, or use the ep token which resolves the entry point after the module loads.
Can I chain multiple actions to a single breakpoint?
Yes, CAPEv2 supports chaining multiple actions to a single breakpoint slot. The action string is parsed by lib/cuckoo/core/monitor.py, allowing combinations like action0=skip,count=0 or more complex sequences. However, remember that all four breakpoint slots share the same hardware registers (DR0-DR3), so if you need step-over tracing, one slot must remain free for the debugger to track return addresses.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →