108 lines
3.4 KiB
Markdown
108 lines
3.4 KiB
Markdown
# gwatch - Global Variable Watcher
|
|
|
|
A tool to monitor reads and writes to global integer variables in Linux binaries using ptrace and hardware watchpoints.
|
|
|
|
## Features
|
|
|
|
- Monitors specific global integer variables in a running program
|
|
- Attempts to set up hardware watchpoints via ptrace to detect variable access
|
|
- Supports PIE (Position Independent Executable) binaries
|
|
- Passes command-line arguments to the target program
|
|
- Uses DWARF debug information to locate variables
|
|
|
|
## Building
|
|
|
|
```bash
|
|
cmake -B build
|
|
cmake --build build
|
|
```
|
|
|
|
## Usage
|
|
|
|
```bash
|
|
gwatch --var <symbol> --exec <path> [-- arg1 arg2 ... argN]
|
|
```
|
|
|
|
**Arguments:**
|
|
- `--var <symbol>`: Name of the global variable to watch (required)
|
|
- `--exec <path>`: Path to the executable to monitor (required)
|
|
- `-- arg1 arg2 ...`: Optional arguments to pass to the target program
|
|
|
|
**Note:** The target binary must be compiled with debug symbols (`-g` flag).
|
|
|
|
**Examples:**
|
|
|
|
```bash
|
|
# Watch global_counter in test_access
|
|
./build/gwatch --var global_counter --exec ./test_access
|
|
|
|
# Watch with program arguments
|
|
./build/gwatch --var global_counter --exec ./test_with_args -- hello world 123
|
|
```
|
|
|
|
## Implementation Details
|
|
|
|
### Variable Detection
|
|
- Uses libdwarf to parse DWARF debug information
|
|
- Identifies global integer variables (int, long, short, char, unsigned variants)
|
|
- 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
|
|
|
|
### PIE Binary Support
|
|
For Position Independent Executables, the tool:
|
|
1. Reads `/proc/[pid]/maps` to find the actual load address
|
|
2. Adds the load address to the DWARF-provided offset
|
|
3. Sets the watchpoint at the runtime address
|
|
|
|
## Current Limitations
|
|
|
|
**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:
|
|
|
|
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.
|
|
|
|
## Testing
|
|
|
|
Test programs are provided:
|
|
- `test_access.c`: Program that reads and writes to `global_counter`
|
|
- `test_with_args.c`: Program that accepts command-line arguments and modifies `global_counter`
|
|
|
|
Compile test programs with:
|
|
```bash
|
|
gcc -g -O0 -o test_access test_access.c
|
|
gcc -g -O0 -o test_with_args test_with_args.c
|
|
```
|
|
|
|
Run tests:
|
|
```bash
|
|
# Basic test
|
|
./build/gwatch --var global_counter --exec ./test_access
|
|
|
|
# Test with arguments
|
|
./build/gwatch --var global_counter --exec ./test_with_args -- hello world 123
|
|
```
|
|
|
|
## 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)
|
|
- Better error reporting and diagnostics
|