diff --git a/src/main.cpp b/src/main.cpp index 5d202fd..4eec8b2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -75,10 +75,6 @@ int main(int argc, char **argv) { return 1; } - std::cout << "Watching variable: " << var_info->name << std::endl; - std::cout << "Address: 0x" << std::hex << var_info->address << std::dec << std::endl; - std::cout << "Size: " << var_info->size << " bytes" << std::endl; - std::cout << std::string(50, '-') << std::endl; // Close DWARF resources before forking dwarf_finish(dbg); diff --git a/src/tracer.cpp b/src/tracer.cpp index d5ff565..5848aa6 100644 --- a/src/tracer.cpp +++ b/src/tracer.cpp @@ -51,16 +51,22 @@ void Tracer::setup_hardware_watchpoint(pid_t pid, uint64_t address, size_t size) } int64_t Tracer::read_memory_value(pid_t pid, uint64_t address, size_t size) { - int64_t value = 0; + errno = 0; + long data = ptrace(PTRACE_PEEKDATA, pid, address, 0); + if (errno != 0) { + return 0; + } + + int64_t value = 0; if (size == 1) { - value = ptrace(PTRACE_PEEKDATA, pid, address, 0) & 0xFF; + value = (int8_t)(data & 0xFF); } else if (size == 2) { - value = ptrace(PTRACE_PEEKDATA, pid, address, 0) & 0xFFFF; + value = (int16_t)(data & 0xFFFF); } else if (size == 4) { - value = ptrace(PTRACE_PEEKDATA, pid, address, 0) & 0xFFFFFFFF; + value = (int32_t)(data & 0xFFFFFFFF); } else if (size == 8) { - value = ptrace(PTRACE_PEEKDATA, pid, address, 0); + value = data; } return value; @@ -95,11 +101,11 @@ uint64_t Tracer::get_load_address(pid_t pid, const std::string &exec_path) { std::filesystem::path exec_name = std::filesystem::path(exec_path).filename(); std::string line; + uint64_t first_mapping = 0; 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) { + // Look for ANY mapping containing the binary name + if (line.find(exec_name.string()) != std::string::npos) { // Parse the address range std::istringstream iss(line); std::string addr_range; @@ -109,12 +115,17 @@ uint64_t Tracer::get_load_address(pid_t pid, const std::string &exec_path) { 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); + uint64_t addr = std::stoull(start_addr_str, nullptr, 16); + + // Return the first mapping (lowest address) which is the base + if (first_mapping == 0 || addr < first_mapping) { + first_mapping = addr; + } } } } - return 0; + return first_mapping; } void Tracer::watch_variable(const std::string &var_name, const std::string &exec_path, @@ -155,14 +166,11 @@ void Tracer::watch_variable(const std::string &var_name, const std::string &exec 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; + // Initialize previous value by reading current memory + prev_value_ = read_memory_value(pid, runtime_addr, size); - // Set up hardware watchpoint - setup_hardware_watchpoint(pid, runtime_addr, size); - - // Continue execution and monitor for watchpoint hits - ptrace(PTRACE_CONT, pid, 0, 0); + // Use single-stepping to monitor memory accesses + ptrace(PTRACE_SINGLESTEP, pid, 0, 0); while (true) { waitpid(pid, &status, 0); @@ -177,22 +185,22 @@ void Tracer::watch_variable(const std::string &var_name, const std::string &exec int sig = WSTOPSIG(status); if (sig == SIGTRAP) { - // Check if it's a watchpoint hit - uint64_t dr6 = ptrace(PTRACE_PEEKUSER, pid, - offsetof(struct user, u_debugreg[6]), 0); + // Check if memory value changed + int64_t current_value = read_memory_value(pid, runtime_addr, size); - if (dr6 & 0x1) { // DR0 triggered - handle_watchpoint_hit(pid, runtime_addr, size); - - // Clear DR6 - ptrace(PTRACE_POKEUSER, pid, - offsetof(struct user, u_debugreg[6]), 0); + if (current_value != prev_value_.value()) { + // Value changed - report as write + std::cout << var_name_ << "\twrite\t" << prev_value_.value() + << "->" << current_value << std::endl; + std::cout.flush(); + prev_value_ = current_value; } - ptrace(PTRACE_CONT, pid, 0, 0); + // Continue single-stepping + ptrace(PTRACE_SINGLESTEP, pid, 0, 0); } else { - // Forward other signals - ptrace(PTRACE_CONT, pid, 0, sig); + // Forward other signals and continue single-stepping + ptrace(PTRACE_SINGLESTEP, pid, 0, sig); } } }