diff --git a/src/tracer.cpp b/src/tracer.cpp index 391b134..65f356c 100644 --- a/src/tracer.cpp +++ b/src/tracer.cpp @@ -1,6 +1,9 @@ #include "tracer.hpp" +#include #include +#include #include +#include #include #include #include @@ -17,23 +20,19 @@ #define DR7_LEN_8 0x2 void Tracer::setup_hardware_watchpoint(pid_t pid, uint64_t address, size_t size) { - struct user_regs_struct regs; - - // Read debug registers - uint64_t dr7 = ptrace(PTRACE_PEEKUSER, pid, - offsetof(struct user, u_debugreg[7]), 0); - // Use DR0 for our watchpoint ptrace(PTRACE_POKEUSER, pid, offsetof(struct user, u_debugreg[0]), address); // Configure DR7 for DR0 + uint64_t dr7 = 0; + // Set local enable bit for DR0 (bit 0) dr7 |= (1 << 0); - // Set condition to break on read/write (bits 16-17) + // Set condition to break on read/write (bits 16-17 for DR0) dr7 |= (DR7_BREAK_ON_RW << 16); - // Set length based on size (bits 18-19) + // Set length based on size (bits 18-19 for DR0) uint64_t len_code; if (size == 1) len_code = DR7_LEN_1; else if (size == 2) len_code = DR7_LEN_2; @@ -42,7 +41,10 @@ void Tracer::setup_hardware_watchpoint(pid_t pid, uint64_t address, size_t size) dr7 |= (len_code << 18); - // Write back DR7 + // Also set GE (bit 9) for exact breakpoint detection + dr7 |= (1 << 9); + + // Write DR7 ptrace(PTRACE_POKEUSER, pid, offsetof(struct user, u_debugreg[7]), dr7); } @@ -54,6 +56,39 @@ void Tracer::handle_watchpoint_hit(pid_t pid, uint64_t address) { << std::endl; } +uint64_t Tracer::get_load_address(pid_t pid, const std::string &exec_path) { + std::string maps_path = std::format("/proc/{}/maps", pid); + std::ifstream maps_file(maps_path); + + if (!maps_file.is_open()) { + std::cerr << "Error: Cannot open " << maps_path << std::endl; + return 0; + } + + std::filesystem::path exec_name = std::filesystem::path(exec_path).filename(); + std::string line; + + while (std::getline(maps_file, line)) { + // Look for executable mapping containing the binary name + if (line.find(exec_name.string()) != std::string::npos && + line.find(" r-xp ") != std::string::npos) { + // Parse the address range + std::istringstream iss(line); + std::string addr_range; + iss >> addr_range; + + // Extract start address + size_t dash_pos = addr_range.find('-'); + if (dash_pos != std::string::npos) { + std::string start_addr_str = addr_range.substr(0, dash_pos); + return std::stoull(start_addr_str, nullptr, 16); + } + } + } + + return 0; +} + void Tracer::watch_variable(const std::string &exec_path, uint64_t address, size_t size) { pid_t pid = fork(); @@ -73,8 +108,15 @@ void Tracer::watch_variable(const std::string &exec_path, uint64_t address, size return; } + // Get the actual load address (for PIE binaries) + uint64_t load_addr = get_load_address(pid, exec_path); + uint64_t runtime_addr = load_addr + address; + + std::cout << std::format("Load address: 0x{:x}", load_addr) << std::endl; + std::cout << std::format("Runtime address: 0x{:x}", runtime_addr) << std::endl; + // Set up hardware watchpoint - setup_hardware_watchpoint(pid, address, size); + setup_hardware_watchpoint(pid, runtime_addr, size); // Continue execution and monitor for watchpoint hits ptrace(PTRACE_CONT, pid, 0, 0); @@ -97,7 +139,7 @@ void Tracer::watch_variable(const std::string &exec_path, uint64_t address, size offsetof(struct user, u_debugreg[6]), 0); if (dr6 & 0x1) { // DR0 triggered - handle_watchpoint_hit(pid, address); + handle_watchpoint_hit(pid, runtime_addr); // Clear DR6 ptrace(PTRACE_POKEUSER, pid, diff --git a/src/tracer.hpp b/src/tracer.hpp index b5f9ba9..c38e8c9 100644 --- a/src/tracer.hpp +++ b/src/tracer.hpp @@ -11,6 +11,7 @@ public: private: void setup_hardware_watchpoint(pid_t pid, uint64_t address, size_t size); void handle_watchpoint_hit(pid_t pid, uint64_t address); + uint64_t get_load_address(pid_t pid, const std::string &exec_path); }; #endif