From 89f3dcf21a5353d21ca16d2a8cab79573c3de3ae Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 25 Aug 2024 20:51:29 +0200 Subject: [PATCH] Better gdb stub abstraction --- src/windows_emulator/gdb_stub.cpp | 126 +++++++++++++++++++++++++ src/windows_emulator/gdb_stub.hpp | 29 ++++++ src/windows_emulator/main.cpp | 148 ++++++++++++++++-------------- 3 files changed, 236 insertions(+), 67 deletions(-) create mode 100644 src/windows_emulator/gdb_stub.cpp create mode 100644 src/windows_emulator/gdb_stub.hpp diff --git a/src/windows_emulator/gdb_stub.cpp b/src/windows_emulator/gdb_stub.cpp new file mode 100644 index 00000000..388ed646 --- /dev/null +++ b/src/windows_emulator/gdb_stub.cpp @@ -0,0 +1,126 @@ +#include "std_include.hpp" +#include "gdb_stub.hpp" + +#include + +extern "C" { +#include +} + +namespace +{ + gdb_action_t map_gdb_action(const gdb_action action) + { + switch (action) + { + case gdb_action::none: + return ACT_NONE; + case gdb_action::resume: + return ACT_RESUME; + case gdb_action::shutdown: + return ACT_SHUTDOWN; + } + + throw std::runtime_error("Bad action"); + } + + gdb_stub_handler& get_handler(void* args) + { + return *static_cast(args); + } + + gdb_action_t cont(void* args) + { + return map_gdb_action(get_handler(args).cont()); + } + + gdb_action_t stepi(void* args) + { + return map_gdb_action(get_handler(args).stepi()); + } + + int read_reg(void* args, const int regno, size_t* value) + { + return get_handler(args).read_reg(regno, value) ? 0 : 1; + } + + int write_reg(void* args, const int regno, const size_t value) + { + return get_handler(args).write_reg(regno, value) ? 0 : 1; + } + + int read_mem(void* args, const size_t addr, const size_t len, void* val) + { + return get_handler(args).read_mem(addr, len, val) ? 0 : 1; + } + + int write_mem(void* args, const size_t addr, const size_t len, void* val) + { + return get_handler(args).write_mem(addr, len, val) ? 0 : 1; + } + + bool set_bp(void* args, const size_t addr, const bp_type_t type) + { + (void)type; + assert(type == BP_SOFTWARE); + + return get_handler(args).set_bp(addr); + } + + bool del_bp(void* args, const size_t addr, const bp_type_t type) + { + (void)type; + assert(type == BP_SOFTWARE); + + return get_handler(args).del_bp(addr); + } + + void on_interrupt(void* args) + { + get_handler(args).on_interrupt(); + } + + target_ops get_target_ops() + { + target_ops ops{}; + + ops.cont = cont; + ops.stepi = stepi; + ops.read_reg = read_reg; + ops.write_reg = write_reg; + ops.read_mem = read_mem; + ops.write_mem = write_mem; + ops.set_bp = set_bp; + ops.del_bp = del_bp; + ops.on_interrupt = on_interrupt; + + return ops; + } +} + +bool run_gdb_stub(gdb_stub_handler& handler, std::string target_description, const size_t register_count, + std::string bind_address) +{ + const arch_info_t info + { + target_description.data(), + static_cast(register_count), + sizeof(uint64_t), + }; + + auto ops = get_target_ops(); + + gdbstub_t stub{}; + + if (!gdbstub_init(&stub, &ops, info, bind_address.data())) + { + return false; + } + + const auto _ = utils::finally([&] + { + gdbstub_close(&stub); + }); + + return gdbstub_run(&stub, &handler); +} diff --git a/src/windows_emulator/gdb_stub.hpp b/src/windows_emulator/gdb_stub.hpp new file mode 100644 index 00000000..7a4ed4d5 --- /dev/null +++ b/src/windows_emulator/gdb_stub.hpp @@ -0,0 +1,29 @@ +#pragma once + +enum class gdb_action : uint8_t +{ + none, + resume, + shutdown, +}; + +struct gdb_stub_handler +{ + virtual ~gdb_stub_handler() = default; + + virtual gdb_action cont() = 0; + virtual gdb_action stepi() = 0; + + virtual bool read_reg(int regno, size_t* value) = 0; + virtual bool write_reg(int regno, size_t value) = 0; + + virtual bool read_mem(size_t addr, size_t len, void* val) = 0; + virtual bool write_mem(size_t addr, size_t len, void* val) = 0; + + virtual bool set_bp(size_t addr) = 0; + virtual bool del_bp(size_t addr) = 0; + + virtual void on_interrupt() = 0; +}; + +bool run_gdb_stub(gdb_stub_handler& handler, std::string target_description, size_t register_count, std::string bind_address); diff --git a/src/windows_emulator/main.cpp b/src/windows_emulator/main.cpp index 159bd21b..06065476 100644 --- a/src/windows_emulator/main.cpp +++ b/src/windows_emulator/main.cpp @@ -1,3 +1,5 @@ +#include + #include "std_include.hpp" #include "emulator_utils.hpp" @@ -9,9 +11,7 @@ #include -extern "C" { -#include -} +#include "gdb_stub.hpp" #define GS_SEGMENT_ADDR 0x6000000ULL #define GS_SEGMENT_SIZE (20 << 20) // 20 MB @@ -336,94 +336,100 @@ namespace {gdb_registers::eflags, x64_register::rflags}, }; - void run() + class x64_gdb_stub_handler : public gdb_stub_handler { - gdbstub_t stub{}; - - target_ops ops{}; - ops.cont = +[](void* args) + public: + x64_gdb_stub_handler(x64_emulator& emu) + : emu_(&emu) { - static_cast(static_cast(args))->start_from_ip(); - return ACT_RESUME; - }; + } - ops.stepi = +[](void* args) + ~x64_gdb_stub_handler() override { - static_cast(static_cast(args))->start_from_ip({}, 1); - return ACT_RESUME; - }; + for (const auto& hook : this->hooks_) + { + this->emu_->delete_hook(hook.second); + } - ops.read_reg = +[](void* args, int regno, size_t* value) + this->hooks_.clear(); + } + + gdb_action cont() override + { + this->emu_->start_from_ip(); + return gdb_action::resume; + } + + gdb_action stepi() override + { + this->emu_->start_from_ip({}, 1); + return gdb_action::resume; + } + + bool read_reg(const int regno, size_t* value) override { try { const auto mapped_register = register_map.at(static_cast(regno)); - static_cast(args)->read_raw_register(static_cast(mapped_register), value, sizeof(*value)); - return 0; + this->emu_->read_register(mapped_register, value, sizeof(*value)); + return true; } catch (...) { *value = 0; - return 0; + return true; } - }; + } - ops.write_reg = +[](void* args, const int regno, const size_t value) + bool write_reg(const int regno, const size_t value) override { try { const auto mapped_register = register_map.at(static_cast(regno)); - static_cast(args)->write_raw_register(static_cast(mapped_register), &value, sizeof(value)); - return 0; + this->emu_->write_register(mapped_register, &value, sizeof(value)); + return true; } catch (...) { - return 1; + return false; } - }; + } - ops.read_mem = +[](void* args, const size_t addr, const size_t len, void* val) + bool read_mem(const size_t addr, const size_t len, void* val) override { try { - static_cast(args)->read_memory(addr, val, len); - return 0; + this->emu_->read_memory(addr, val, len); + return true; } catch (...) { - return 1; + return false; } - }; + } - ops.write_mem = +[](void* args, const size_t addr, const size_t len, void* val) + bool write_mem(const size_t addr, const size_t len, void* val) override { try { - static_cast(args)->write_memory(addr, val, len); - return 0; + this->emu_->write_memory(addr, val, len); + return true; } catch (...) { - return 1; + return false; } - }; + } - static std::unordered_map hooks{}; - - ops.set_bp = +[](void* args, const size_t addr, bp_type_t) + bool set_bp(const size_t addr) override { try { - const auto entry = hooks.find(addr); - if (entry != hooks.end()) + this->del_bp(addr); + + this->hooks_[addr] = this->emu_->hook_memory_execution(addr, 1, [this](uint64_t, size_t) { - static_cast(args)->delete_hook(entry->second); - hooks.erase(entry); - } - - hooks[addr] = static_cast(args)->hook_memory_execution(addr, 1, [args](uint64_t, size_t) - { - static_cast(args)->stop(); + this->on_interrupt(); }); return true; @@ -432,20 +438,20 @@ namespace { return false; } - }; + } - ops.del_bp = +[](void* args, const size_t addr, bp_type_t) + bool del_bp(const size_t addr) override { try { - const auto entry = hooks.find(addr); - if (entry == hooks.end()) + const auto entry = this->hooks_.find(addr); + if (entry == this->hooks_.end()) { return false; } - static_cast(args)->delete_hook(entry->second); - hooks.erase(entry); + this->emu_->delete_hook(entry->second); + this->hooks_.erase(entry); return true; } @@ -453,19 +459,21 @@ namespace { return false; } - }; + } - ops.on_interrupt = +[](void* args) + void on_interrupt() override { - static_cast(args)->stop(); - }; + this->emu_->stop(); + } - constexpr arch_info_t info{ - const_cast("i386:x86-64"), static_cast(gdb_registers::end), sizeof(uint64_t) - }; - - gdbstub_init(&stub, &ops, info, const_cast("0.0.0.0:28960")); + private: + x64_emulator* emu_{}; + std::unordered_map hooks_{}; + }; + void run() + { + constexpr bool use_gdb = true; const auto emu = unicorn::create_x64_emulator(); auto context = setup_context(*emu); @@ -511,7 +519,7 @@ namespace watch_object(*emu, context.process_params); watch_object(*emu, context.kusd); - emu->hook_memory_execution(0, std::numeric_limits::max(), [&](const uint64_t address, const size_t) + /*emu->hook_memory_execution(0, std::numeric_limits::max(), [&](const uint64_t address, const size_t) { if (address == 0x1800D52F4) { @@ -524,7 +532,7 @@ namespace emu->reg(x64_register::rax), emu->reg(x64_register::rbx), emu->reg(x64_register::rcx), emu->reg(x64_register::rdx), emu->reg(x64_register::r8), emu->reg(x64_register::r9), emu->reg(x64_register::rdi), emu->reg(x64_register::rsi)); - }); + });*/ const auto execution_context = context.gs_segment.reserve(); @@ -536,8 +544,15 @@ namespace try { - gdbstub_run(&stub, static_cast(emu.get())); - //emu->start_from_ip(); + if (use_gdb) + { + x64_gdb_stub_handler handler{*emu}; + run_gdb_stub(handler, "i386:x86-64", static_cast(gdb_registers::end), "0.0.0.0:28960"); + } + else + { + emu->start_from_ip(); + } } catch (...) { @@ -546,7 +561,6 @@ namespace } printf("Emulation done.\n"); - gdbstub_close(&stub); } }