#windows #x86-64 #breakpoints #hardware #api-bindings

hwbp

A fully-featured Rust library for managing hardware breakpoints on Windows via x86 debug registers

3 releases

new 0.1.2 May 22, 2025
0.1.1 May 17, 2025
0.1.0 May 17, 2025

#325 in Windows APIs

Download history 232/week @ 2025-05-14

232 downloads per month

Unlicense

33KB
732 lines

HWBP

A fully-featured Rust library for managing hardware breakpoints on Windows via x86 debug registers.

HWBP provides a clean API to set, manage, and handle hardware breakpoints for watching memory execution, read & write access.

Limitations

All of the following limitations are due to the hardware limitations of x86/AMD64 architecture.

  • Maximum of 4 hardware breakpoints: There are only 4 debug registers available for breakpoints (DR0-DR3).
  • Thread-specific: Breakpoints can only be applied to existing threads, not to threads created after setting the breakpoint. [^1]
  • Size restrictions: Breakpoints can only monitor 1, 2, 4, or 8 bytes of memory, depending on the architecture.

[^1]: You can, however, work around this limitation by hooking ntdll!Kernel32ThreadInitThunkFunction, which is called when a thread is created. Here is an example.

Usage

First things first, you need to initialize the library:

hwbp::init();

This will initialize the exception handler. However, if you have one already, you can call dispatch_exception instead.

Now you need to obtain Context from current thread:

let mut ctx = Context::current().unwrap();

You can also obtain Context from a specific thread:

let mut ctx = Context::for_thread(42).unwrap();

Once you have a Context, you can create a new HWBP using Context::unused method:

let mut x = 0;
let hwbp = ctx
    .unused()
    .unwrap()
    .watch_variable_write(&x, |_| {
        println!("callback!")
    })
    .unwrap()
    .with_enabled(true)
    .build()
    .unwrap();

You can also make up any HWBP you want:

let mut hwbp = ctx
    .unused()
    .unwrap()
    .with_enabled(true)
    .with_address(0x12345678)
    .with_condition(Condition::ReadWrite)
    .with_size(Size::EightBytes)
    .with_callback(|_| {
        println!("callback!")
    })
    .build_and_set()
    .unwrap();

This will create a new HWBP and set it to context, however, not yet applied to the current thread.

ctx.apply_for_current_thread().expect("Failed to apply");

Voila!

For more examples, check out the examples directory!

To free the library you just call free:

hwbp::free();

Dependencies

~124MB
~2M SLoC