# How CAPEv2 Breakpoints (bp0-bp3) Enable Dynamic Unpacking: A Technical Guide

> Discover how CAPEv2 breakpoints bp0-bp3 bypass anti-debug tricks and enable automatic unpacking of malware payloads with this technical guide.

- Repository: [Kevin O'Reilly/capev2](https://github.com/kevoreilly/capev2)
- Tags: deep-dive
- Published: 2026-03-05

---

**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:

1. **`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 base
2. **`ep` token**: Watches module load events, calculates the entry point address from the image base plus entry point RVA
3. **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 handling
  - `depth=0` (default): Step over calls
  - `depth>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:

```text
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`](https://github.com/kevoreilly/capev2/blob/main/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.

1. **Locate the unpacker stub** using existing YARA rules (e.g., `Guloader.yar`, `UPX.yar`) or manual analysis in a disassembler
2. **Create initial breakpoints** on stub addresses using `bp0` with RVA or `$` macro references (e.g., `bp0=$trap0`)
3. **Attach neutralizing actions** such as `action0=skip` or `action0=goto:ntdll::NtAllocateVirtualMemory` to bypass anti-debug checks
4. **Set entry point tracing** with `bp3=ep` to capture execution at the unpacked payload's entry point
5. **Configure trace parameters** using `depth=0` (step-over) and `count=500` (instruction limit) to capture sufficient unpacked behavior
6. **Submit the sample** with the combined `cape_options` string via the Web UI or API
7. **Inspect results** in the Debugger tab or `debug.log` to 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:

```text
cape_options = "bp0=$upx32*,bp0=$upx64*,hc0=1,action0=step2oep,imprec=1"

```

This configuration:
- Sets `bp0` on the UPX stub entry (wildcarded for 32/64-bit)
- Uses `action0=step2oep` to 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:

```text
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`](https://github.com/kevoreilly/capev2/blob/master/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/main/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/main/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`](https://github.com/kevoreilly/capev2/blob/master/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`](https://github.com/kevoreilly/capev2/blob/master/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 special `ep` token
- **Dynamic base resolution** via `base-on-api` ensures 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 `depth` and `count` parameters captures precise instruction sequences at the original entry point
- **YARA integration** allows persistent breakpoint configurations in rules like `Guloader.yar` and `UPX.yar` for 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`](https://github.com/kevoreilly/capev2/blob/main/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.