Fix tenet plugin pull request (#417)

This commit is contained in:
Maurice Heumann
2025-07-17 20:05:28 +02:00
committed by GitHub
4 changed files with 344 additions and 3 deletions

View File

@@ -8,12 +8,11 @@
#include "object_watching.hpp"
#include "snapshot.hpp"
#include "analysis.hpp"
#include "tenet_tracer.hpp"
#include <utils/finally.hpp>
#include <utils/interupt_handler.hpp>
#include <cstdio>
#ifdef OS_EMSCRIPTEN
#include <event_handler.hpp>
#endif
@@ -24,6 +23,7 @@ namespace
{
mutable bool use_gdb{false};
bool log_executable_access{false};
bool tenet_trace{false};
std::filesystem::path dump{};
std::filesystem::path minidump_path{};
std::string registry_path{"./registry"};
@@ -296,6 +296,13 @@ namespace
win_emu->log.log("Using emulator: %s\n", win_emu->emu().get_name().c_str());
std::optional<tenet_tracer> tenet_tracer{};
if (options.tenet_trace)
{
win_emu->log.log("Tenet Tracer enabled. Output: tenet_trace.log\n");
tenet_tracer.emplace(*win_emu, "tenet_trace.log");
}
register_analysis_callbacks(context);
watch_system_objects(*win_emu, options.modules, options.verbose_logging);
@@ -405,6 +412,7 @@ namespace
printf(" -e, --emulation <path> Set emulation root path\n");
printf(" -a, --snapshot <path> Load snapshot dump from path\n");
printf(" --minidump <path> Load minidump from path\n");
printf(" -t, --tenet-trace Enable Tenet tracer\n");
printf(" -i, --ignore <funcs> Comma-separated list of functions to ignore\n");
printf(" -p, --path <src> <dst> Map Windows path to host path\n");
printf(" -r, --registry <path> Set registry path (default: ./registry)\n\n");
@@ -451,6 +459,10 @@ namespace
{
options.concise_logging = true;
}
else if (arg == "-t" || arg == "--tenet-trace")
{
options.tenet_trace = true;
}
else if (arg == "-m" || arg == "--module")
{
if (args.size() < 2)

View File

@@ -6,15 +6,16 @@
#include <array>
#include <deque>
#include <queue>
#include <mutex>
#include <thread>
#include <ranges>
#include <atomic>
#include <vector>
#include <mutex>
#include <string>
#include <chrono>
#include <memory>
#include <fstream>
#include <sstream>
#include <functional>
#include <filesystem>
#include <optional>
@@ -23,6 +24,7 @@
#include <unordered_set>
#include <condition_variable>
#include <cstdio>
#include <cassert>
#include <platform/platform.hpp>

View File

@@ -0,0 +1,269 @@
#include "std_include.hpp"
#include "tenet_tracer.hpp"
#include <utils/finally.hpp>
#include <iomanip>
namespace
{
std::string format_hex(uint64_t value)
{
std::stringstream ss;
ss << "0x" << std::hex << value;
return ss.str();
}
std::string format_byte_array(const uint8_t* data, size_t size)
{
std::stringstream ss;
for (size_t i = 0; i < size; ++i)
{
ss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(data[i]);
}
return ss.str();
}
void parse_and_accumulate_changes(const std::string& line, std::map<std::string, std::string>& changes)
{
size_t start = 0;
while (start < line.length())
{
size_t end = line.find(',', start);
if (end == std::string::npos)
{
end = line.length();
}
std::string pair_str = line.substr(start, end - start);
size_t equals_pos = pair_str.find('=');
if (equals_pos != std::string::npos)
{
std::string key = pair_str.substr(0, equals_pos);
std::string value = pair_str.substr(equals_pos + 1);
changes[key] = value;
}
start = end + 1;
}
}
}
tenet_tracer::tenet_tracer(windows_emulator& win_emu, const std::filesystem::path& log_filename)
: win_emu_(win_emu),
log_file_(log_filename)
{
if (!log_file_)
{
throw std::runtime_error("TenetTracer: Failed to open log file -> " + log_filename.string());
}
auto& emu = win_emu_.emu();
auto* read_hook = emu.hook_memory_read(0, 0xFFFFFFFFFFFFFFFF, [this](uint64_t a, const void* d, size_t s) {
this->log_memory_read(a, d, s); //
});
read_hook_ = scoped_hook(emu, read_hook);
auto* write_hook = emu.hook_memory_write(0, 0xFFFFFFFFFFFFFFFF, [this](uint64_t a, const void* d, size_t s) {
this->log_memory_write(a, d, s); //
});
write_hook_ = scoped_hook(emu, write_hook);
auto* execute_hook = emu.hook_memory_execution([&](uint64_t address) {
this->process_instruction(address); //
});
execute_hook_ = scoped_hook(emu, execute_hook);
}
tenet_tracer::~tenet_tracer()
{
filter_and_write_buffer();
if (log_file_.is_open())
{
log_file_.close();
}
}
void tenet_tracer::filter_and_write_buffer()
{
if (raw_log_buffer_.empty())
{
return;
}
const auto* exe_module = win_emu_.mod_manager.executable;
if (!exe_module)
{
for (const auto& line : raw_log_buffer_)
{
log_file_ << line << '\n';
}
return;
}
if (!raw_log_buffer_.empty())
{
log_file_ << raw_log_buffer_.front() << '\n';
}
bool currently_outside = false;
std::map<std::string, std::string> accumulated_changes;
for (size_t i = 1; i < raw_log_buffer_.size(); ++i)
{
const auto& line = raw_log_buffer_[i];
size_t rip_pos = line.find("rip=0x");
if (rip_pos == std::string::npos)
{
continue;
}
char* end_ptr = nullptr;
uint64_t address = std::strtoull(line.c_str() + rip_pos + 6, &end_ptr, 16);
bool is_line_inside = exe_module->is_within(address);
const auto _1 = utils::finally([&] {
currently_outside = !is_line_inside; //
});
if (!is_line_inside)
{
parse_and_accumulate_changes(line, accumulated_changes);
continue;
}
const auto _2 = utils::finally([&] {
log_file_ << line << '\n'; //
});
if (!currently_outside || accumulated_changes.empty())
{
continue;
}
std::stringstream summary_line;
bool first = true;
auto rip_it = accumulated_changes.find("rip");
std::string last_rip;
if (rip_it != accumulated_changes.end())
{
last_rip = rip_it->second;
accumulated_changes.erase(rip_it);
}
for (const auto& pair : accumulated_changes)
{
if (!first)
{
summary_line << ",";
}
summary_line << pair.first << "=" << pair.second;
first = false;
}
if (!last_rip.empty())
{
if (!first)
{
summary_line << ",";
}
summary_line << "rip=" << last_rip;
}
log_file_ << summary_line.str() << '\n';
accumulated_changes.clear();
}
raw_log_buffer_.clear();
}
void tenet_tracer::log_memory_read(uint64_t address, const void* data, size_t size)
{
if (!mem_read_log_.str().empty())
{
mem_read_log_ << ";";
}
mem_read_log_ << format_hex(address) << ":" << format_byte_array(static_cast<const uint8_t*>(data), size);
}
void tenet_tracer::log_memory_write(uint64_t address, const void* data, size_t size)
{
if (!mem_write_log_.str().empty())
{
mem_write_log_ << ";";
}
mem_write_log_ << format_hex(address) << ":" << format_byte_array(static_cast<const uint8_t*>(data), size);
}
void tenet_tracer::process_instruction(uint64_t address)
{
auto& emu = win_emu_.emu();
std::stringstream trace_line;
std::array<uint64_t, GPRs_TO_TRACE.size()> current_regs;
for (size_t i = 0; i < GPRs_TO_TRACE.size(); ++i)
{
current_regs[i] = emu.reg<uint64_t>(GPRs_TO_TRACE[i].first);
}
bool first_entry = true;
auto append_separator = [&] {
if (!first_entry)
{
trace_line << ",";
}
first_entry = false;
};
if (is_first_instruction_)
{
for (size_t i = 0; i < GPRs_TO_TRACE.size(); ++i)
{
append_separator();
trace_line << GPRs_TO_TRACE[i].second << "=" << format_hex(current_regs[i]);
}
is_first_instruction_ = false;
}
else
{
for (size_t i = 0; i < GPRs_TO_TRACE.size(); ++i)
{
if (previous_registers_[i] != current_regs[i])
{
append_separator();
trace_line << GPRs_TO_TRACE[i].second << "=" << format_hex(current_regs[i]);
}
}
}
append_separator();
trace_line << "rip=" << format_hex(address);
const auto mem_reads = mem_read_log_.str();
if (!mem_reads.empty())
{
append_separator();
trace_line << "mr=" << mem_reads;
}
const auto mem_writes = mem_write_log_.str();
if (!mem_writes.empty())
{
append_separator();
trace_line << "mw=" << mem_writes;
}
raw_log_buffer_.push_back(trace_line.str());
previous_registers_ = current_regs;
mem_read_log_.str("");
mem_read_log_.clear();
mem_write_log_.str("");
mem_write_log_.clear();
}

View File

@@ -0,0 +1,58 @@
#pragma once
#include <windows_emulator.hpp>
#include <emulator/x86_register.hpp>
#include <emulator/scoped_hook.hpp>
constexpr std::array<std::pair<x86_register, std::string_view>, 16> GPRs_TO_TRACE = {
{
{x86_register::rax, "rax"},
{x86_register::rbx, "rbx"},
{x86_register::rcx, "rcx"},
{x86_register::rdx, "rdx"},
{x86_register::rsi, "rsi"},
{x86_register::rdi, "rdi"},
{x86_register::rbp, "rbp"},
{x86_register::rsp, "rsp"},
{x86_register::r8, "r8"},
{x86_register::r9, "r9"},
{x86_register::r10, "r10"},
{x86_register::r11, "r11"},
{x86_register::r12, "r12"},
{x86_register::r13, "r13"},
{x86_register::r14, "r14"},
{x86_register::r15, "r15"},
},
};
class tenet_tracer
{
public:
tenet_tracer(windows_emulator& win_emu, const std::filesystem::path& log_filename);
~tenet_tracer();
tenet_tracer(tenet_tracer&) = delete;
tenet_tracer(const tenet_tracer&) = delete;
tenet_tracer& operator=(tenet_tracer&) = delete;
tenet_tracer& operator=(const tenet_tracer&) = delete;
private:
void filter_and_write_buffer();
void log_memory_read(uint64_t address, const void* data, size_t size);
void log_memory_write(uint64_t address, const void* data, size_t size);
void process_instruction(uint64_t address);
windows_emulator& win_emu_;
std::ofstream log_file_;
std::vector<std::string> raw_log_buffer_;
std::array<uint64_t, GPRs_TO_TRACE.size()> previous_registers_{};
bool is_first_instruction_ = true;
std::stringstream mem_read_log_;
std::stringstream mem_write_log_;
scoped_hook read_hook_;
scoped_hook write_hook_;
scoped_hook execute_hook_;
};