From 61639c15554ac63b9da41d5a951e6b44658c8a6d Mon Sep 17 00:00:00 2001 From: Gabriel Ionita Date: Mon, 27 Oct 2025 12:15:00 +0100 Subject: [PATCH] update README to reflect working single-stepping implementation --- README.md | 60 ++++++++++++++++++++++++++----------------------------- 1 file changed, 28 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index ca407cb..1322ba6 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,15 @@ # gwatch - Global Variable Watcher -A tool to monitor reads and writes to global integer variables in Linux binaries using ptrace and hardware watchpoints. +A tool to monitor writes to global integer variables in Linux binaries using ptrace single-stepping. ## Features - Monitors specific global integer variables in a running program -- Attempts to set up hardware watchpoints via ptrace to detect variable access +- Detects and reports all writes to the watched variable - Supports PIE (Position Independent Executable) binaries - Passes command-line arguments to the target program - Uses DWARF debug information to locate variables +- Tab-delimited output format for easy parsing ## Building @@ -70,41 +71,34 @@ global_counter write 42->52 - Handles const/volatile type qualifiers - Retrieves variable address and size from debug info -### Hardware Watchpoints -The tool uses x86-64 hardware debug registers (DR0-DR7) via ptrace to set watchpoints: -- DR0: Stores the watched address -- DR7: Configures watchpoint type (read/write) and size -- DR6: Status register checked on SIGTRAP to detect watchpoint hits +### Single-Stepping Approach +The tool uses `PTRACE_SINGLESTEP` to monitor memory access: +- Executes the target program one instruction at a time +- After each instruction, reads the watched variable's value from memory +- Detects writes by comparing current value to previous value +- Reports changes in the specified tab-delimited format ### PIE Binary Support For Position Independent Executables, the tool: -1. Reads `/proc/[pid]/maps` to find the actual load address +1. Reads `/proc/[pid]/maps` to find the base load address (lowest mapping) 2. Adds the load address to the DWARF-provided offset -3. Sets the watchpoint at the runtime address +3. Monitors the calculated runtime address -### Read vs Write Detection -When a watchpoint triggers: +### Write Detection +When monitoring the variable: 1. The current value is read from the traced process memory using `ptrace(PTRACE_PEEKDATA)` -2. If the value has changed since the last access, it's reported as a write -3. If the value is unchanged, it's reported as a read -4. Values are formatted according to the variable size (1, 2, 4, or 8 bytes) +2. If the value has changed since the last check, it's reported as a write +3. Values are formatted according to the variable size (1, 2, 4, or 8 bytes) +4. Sign extension is applied for smaller integer types -## Current Limitations +## Performance Considerations -**Hardware watchpoints are not currently triggering on the test system.** The implementation follows standard Linux ptrace documentation for setting x86 hardware breakpoints, but watchpoint events are not being generated. This may be due to: +The tool uses single-stepping which executes one CPU instruction at a time. This is: +- **Very thorough**: Catches every write to the watched variable +- **Slow**: Adds significant overhead compared to native execution +- **Reliable**: Works consistently across different systems and configurations -1. Kernel configuration (hardware breakpoints disabled or restricted) -2. Security settings (e.g., `/proc/sys/kernel/yama/ptrace_scope`) -3. System-specific ptrace behavior variations -4. Potential need for additional ptrace options or setup steps - -The code correctly: -- Sets DR0 to the target address -- Configures DR7 with proper enable bits, condition codes, and length fields -- Monitors for SIGTRAP signals -- Checks DR6 for watchpoint hits - -But SIGTRAP signals are never generated when the watched variable is accessed. +For programs with many instructions (100K+ steps), there will be noticeable slowdown. ## Testing @@ -129,8 +123,10 @@ Run tests: ## Future Work -- Investigate alternative watchpoint implementations -- Add support for watching non-integer types -- Support multiple simultaneous watchpoints (using DR1-DR3) -- Add filtering options (read-only vs write-only vs read/write) +- Optimize performance (possibly using hardware watchpoints if system supports it) +- Add support for watching non-integer types (floats, structs, etc.) +- Support multiple simultaneous variables +- Add read detection (currently only writes are reported) +- Output instruction pointer (RIP) for each access - Better error reporting and diagnostics +- Optional: Skip library code and focus on main program only