Hardware Breakpoints and Watchpoints with Frida

About the author. I’m Colin Xiao — an independent reverse engineer, mostly working solo on contract jobs. I dig into compiled code of every kind, including but not limited to: Android and iOS apps, x86/x64/ARM native binaries, packers and obfuscators, DRM and license checks, network protocols and APIs, crypto and signature algorithms, malware, game binaries, and embedded/IoT firmware. I also handle large-scale data collection and vulnerability research. If you have any project in mind, I’d be glad to take it on — whatever it is, as long as it’s within my wheelhouse. Just reach out. You can reach me on X at @colinxiao_lab or on Telegram at https://t.me/ColinXiao666.

This research is published for educational and defensive purposes.

Hardware Breakpoints and Watchpoints with Frida

Previously, when I needed to instrument a memory address I used MemoryAccessMonitor. The catch: that API only monitors once — it can’t stay resident. Under the hood it sets the target memory page to non-readable/non-writable, so any read or write triggers a pre-registered interrupt handler, which calls the callback you passed to gum_memory_access_monitor_new.

So how do you get persistent monitoring? There are a few existing projects:

Hardware breakpoints landed in Frida 16.5.0

From the Frida 16.5.0 release notes:

Some of you may have found yourself in a situation where a piece of data in memory looks interesting, and you want to locate the code responsible for it.

You may have tried Frida’s MemoryAccessMonitor API, but found the page granularity hard to work with.

To address this, hsorbo and I implementing support for hardware breakpoints and watchpoints. The long story short is that thread objects returned by Process.enumerateThreads() now have setHardwareBreakpoint(), setHardwareWatchpoint(), and corresponding methods to unset them at a later point. These are then combined with Process.setExceptionHandler() where you call the unset method and return true to signal that the exception was handled, and execution should be resumed.

The release link includes example implementations. The process goes like this:

  • Use Process.enumerateMallocRanges() to scan the heap for 50 (say, the ammo count in a game). Fire a shot, then match the scan results for 49 to pin down the address where the ammo is stored.
  • Set a hardware watchpoint on that address. When any instruction reads/writes it, an exception is thrown and you print the address (pc) of that instruction.
  • Once you’ve found the assembly that modifies the ammo, it’s easy to move to native-layer inline instrumentation (e.g. read rax), or go further and implement a patch.

Setting a hardware watchpoint:

thread.setHardwareWatchpoint(index, address, size, conditions);
  • index — the hardware register index (e.g. 0–3, limited by the architecture).
  • address — the memory address (convert with ptr(), e.g. ptr('0x1fbf5191884')).
  • size — the number of bytes to monitor (e.g. 1, 2, 4, 8).
  • conditions — the trigger condition: 'w' for write, 'r' for read, 'x' for execute.

Example:

function installWatchpoint(address, size, conditions) {
    const thread = Process.enumerateThreads()[0]; // pick the first thread

    // When the watchpoint hits, an exception is thrown; handle it and disable the watchpoint.
    Process.setExceptionHandler(e => {
        console.log(`=== Handler got ${e.type} at ${e.context.pc}`);
        // Confirm this is the target thread and the exception is a breakpoint / single-step.
        if (Process.getCurrentThreadId() === thread.id && ['breakpoint', 'single-step'].includes(e.type)) {
            thread.unsetHardwareWatchpoint(0); // unset the index-0 watchpoint
            console.log('Watchpoint disabled');
            return true;
        }
        return false;
    });

    thread.setHardwareWatchpoint(0, address, size, conditions); // hardware register [0]
    console.log('Watchpoint ready');
}

// Usage: monitor 4-byte writes at 0x1fbf5191884
installWatchpoint(ptr('0x1fbf5191884'), 4, 'w');

Further reading: hackcatml/frida-watchpoint-tutorial — a tutorial on Frida’s setHardwareWatchpoint.


Colin Xiao — I take on contract reverse-engineering work, and writeups like this are how I show what I can do. If you need help with anything in this space — including but not limited to the areas I cover above — I’m available. No project is too big or too small — if it’s reverse engineering and I can do it, I’ll take it. DM me on X (@colinxiao_lab) or Telegram (@ColinXiao666).

Get in touch