Skip to content

Watchpoints

Watchpoints are a special type of hardware breakpoint that triggers when a specific memory location is accessed. You can set a watchpoint to trigger on certain memory access conditions, or upon execution (equivalent to a hardware breakpoint).

Features of watchpoints are shared with breakpoints, so you can set asynchronous watchpoints and use properties in the same way.

libdebug API for Watchpoints

The watchpoint() function in the Debugger object sets a watchpoint at a specific address. While you can also use the breakpoint API to set up a watchpoint, a specific API is provided for your convenience:

Function Signature

d.watchpoint(position, condition='w', length=1, callback=None, file='hybrid') 

Parameters:

Argument Type Description
position int | str The address or symbol where the watchpoint will be set.
condition str The type of access (see later section).
length int The size of the word being watched (see later section).
callback Callable | bool (see callback signature here) Used to create asyncronous watchpoints (read more on the debugging flow of stopping events).
file str The backing file for relative addressing. Refer to the memory access section for more information on addressing modes.

Returns:

Return Type Description
Breakpoint Breakpoint The breakpoint object created.

Valid Access Conditions

The condition parameter specifies the type of access that triggers the watchpoint. Default is write access.

Condition Description Supported Architectures
"r" Read access AArch64
"w" Write access AMD64, AArch64
"rw" Read/write access AMD64, AArch64
"x" Execute access AMD64

Valid Word Lengths

The length parameter specifies the size of the word being watched. By default, the watchpoint is set to watch a single byte.

The following table summarizes compatible configurations of watchpoints across architectures. For execution hardware breakpoint configurations, please refer to this table instead.

Architecture Address Alignment Supported Lengths Comment
AMD64 Aligned to length 1, 2, 4, 8 E.g. 4-byte watchpoint needs to be aligned to 4-byte addresses. Misalignment causes architecturally undefined behavior.
i386 Aligned to length 1, 2, 4 E.g. 4-byte watchpoint needs to be aligned to 4-byte addresses. Misalignment causes architecturally undefined behavior
AArch64 8 bytes Any length from 1 to 8 bytes

Watchpoint alignment validation on x86

Some debuggers automatically handle unaligned watchpoints by adjusting the address or using multiple hardware registers. However, libdebug intentionally raises a ValueError in these cases, requiring the user to explicitly manage unaligned watchpoints.

Callback Signature

If you wish to create an asynchronous watchpoint, you will have to provide a callback function. Since internally watchpoints are implemented as hardware breakpoints, the callback signature is the same as for breakpoints. As for breakpoints, if you want to leave the callback empty, you can set callback to True.

Callback Signature

def callback(t: ThreadContext, bp: Breakpoint):

Parameters:

Argument Type Description
t ThreadContext The thread that hit the breakpoint.
bp Breakpoint The breakpoint object that triggered the callback.

Example usage of asynchronous watchpoints

def on_watchpoint_hit(t, bp):
    print(f"RAX: {t.regs.rax:#x}")

    if bp.hit_count == 100:
        print("Hit count reached 100")
        bp.disable()

d.watchpoint(0x11f0, condition="rw", length=8, callback=on_watchpoint_hit, file="binary")