From 5a250d5824400e01c96ebcf34ae1eeff7986af3f Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 25 Aug 2024 19:31:08 +0200 Subject: [PATCH] Implement gdb remote stub --- .gitmodules | 4 + deps/CMakeLists.txt | 4 + deps/mini-gdbstub | 1 + deps/mini-gdbstub.cmake | 11 ++ src/emulator/typed_emulator.hpp | 5 + src/windows_emulator/CMakeLists.txt | 1 + src/windows_emulator/main.cpp | 185 +++++++++++++++++++++++++++- 7 files changed, 210 insertions(+), 1 deletion(-) create mode 160000 deps/mini-gdbstub create mode 100644 deps/mini-gdbstub.cmake diff --git a/.gitmodules b/.gitmodules index f54d65e2..364aa5d1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -11,3 +11,7 @@ path = deps/reflect url = https://github.com/qlibs/reflect.git shallow = true +[submodule "deps/mini-gdbstub"] + path = deps/mini-gdbstub + url = ../mini-gdbstub.git + shallow = true diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index c791919f..0e4ca9c4 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -9,3 +9,7 @@ add_library(reflect INTERFACE) target_include_directories(reflect INTERFACE "${CMAKE_CURRENT_LIST_DIR}/reflect" ) + +########################################## + +include(mini-gdbstub.cmake) \ No newline at end of file diff --git a/deps/mini-gdbstub b/deps/mini-gdbstub new file mode 160000 index 00000000..e0fc5933 --- /dev/null +++ b/deps/mini-gdbstub @@ -0,0 +1 @@ +Subproject commit e0fc5933373ca709688d819e486b14ba0cba9e79 diff --git a/deps/mini-gdbstub.cmake b/deps/mini-gdbstub.cmake new file mode 100644 index 00000000..b4ed7b63 --- /dev/null +++ b/deps/mini-gdbstub.cmake @@ -0,0 +1,11 @@ +file(GLOB_RECURSE SRC_FILES CONFIGURE_DEPENDS + mini-gdbstub/lib/*.c +) + +list(SORT SRC_FILES) + +add_library(mini-gdbstub ${SRC_FILES}) + +target_include_directories(mini-gdbstub PUBLIC + "${CMAKE_CURRENT_LIST_DIR}/mini-gdbstub/include" +) diff --git a/src/emulator/typed_emulator.hpp b/src/emulator/typed_emulator.hpp index 8ab14b06..36b36113 100644 --- a/src/emulator/typed_emulator.hpp +++ b/src/emulator/typed_emulator.hpp @@ -15,6 +15,11 @@ public: static constexpr registers stack_pointer = StackPointer; static constexpr registers instruction_pointer = InstructionPointer; + void start_from_ip(const std::chrono::microseconds timeout = {}, const size_t count = 0) + { + this->start(this->read_instruction_pointer(), 0, timeout, count); + } + void write_register(registers reg, const void* value, const size_t size) { this->write_raw_register(static_cast(reg), value, size); diff --git a/src/windows_emulator/CMakeLists.txt b/src/windows_emulator/CMakeLists.txt index de2bb40f..3974af95 100644 --- a/src/windows_emulator/CMakeLists.txt +++ b/src/windows_emulator/CMakeLists.txt @@ -17,6 +17,7 @@ target_link_libraries(windows_emulator PRIVATE phnt::phnt reflect unicorn_emulator + mini-gdbstub ) set_property(GLOBAL PROPERTY VS_STARTUP_PROJECT windows_emulator) diff --git a/src/windows_emulator/main.cpp b/src/windows_emulator/main.cpp index 49726ff9..159bd21b 100644 --- a/src/windows_emulator/main.cpp +++ b/src/windows_emulator/main.cpp @@ -9,6 +9,10 @@ #include +extern "C" { +#include +} + #define GS_SEGMENT_ADDR 0x6000000ULL #define GS_SEGMENT_SIZE (20 << 20) // 20 MB @@ -286,8 +290,182 @@ namespace }); } + enum class gdb_registers + { + rax = 0, + rbx, + rcx, + rdx, + rsi, + rdi, + rbp, + rsp, + r8, + r9, + r10, + r11, + r12, + r13, + r14, + r15, + + rip, + eflags, + + end, + }; + + std::unordered_map register_map{ + {gdb_registers::rax, x64_register::rax}, + {gdb_registers::rbx, x64_register::rbx}, + {gdb_registers::rcx, x64_register::rcx}, + {gdb_registers::rdx, x64_register::rdx}, + {gdb_registers::rsi, x64_register::rsi}, + {gdb_registers::rdi, x64_register::rdi}, + {gdb_registers::rbp, x64_register::rbp}, + {gdb_registers::rsp, x64_register::rsp}, + {gdb_registers::r8, x64_register::r8}, + {gdb_registers::r9, x64_register::r9}, + {gdb_registers::r10, x64_register::r10}, + {gdb_registers::r11, x64_register::r11}, + {gdb_registers::r12, x64_register::r12}, + {gdb_registers::r13, x64_register::r13}, + {gdb_registers::r14, x64_register::r14}, + {gdb_registers::r15, x64_register::r15}, + {gdb_registers::rip, x64_register::rip}, + {gdb_registers::eflags, x64_register::rflags}, + }; + void run() { + gdbstub_t stub{}; + + target_ops ops{}; + ops.cont = +[](void* args) + { + static_cast(static_cast(args))->start_from_ip(); + return ACT_RESUME; + }; + + ops.stepi = +[](void* args) + { + static_cast(static_cast(args))->start_from_ip({}, 1); + return ACT_RESUME; + }; + + ops.read_reg = +[](void* args, int regno, size_t* value) + { + 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; + } + catch (...) + { + *value = 0; + return 0; + } + }; + + ops.write_reg = +[](void* args, const int regno, const size_t value) + { + 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; + } + catch (...) + { + return 1; + } + }; + + ops.read_mem = +[](void* args, const size_t addr, const size_t len, void* val) + { + try + { + static_cast(args)->read_memory(addr, val, len); + return 0; + } + catch (...) + { + return 1; + } + }; + + ops.write_mem = +[](void* args, const size_t addr, const size_t len, void* val) + { + try + { + static_cast(args)->write_memory(addr, val, len); + return 0; + } + catch (...) + { + return 1; + } + }; + + static std::unordered_map hooks{}; + + ops.set_bp = +[](void* args, const size_t addr, bp_type_t) + { + try + { + const auto entry = hooks.find(addr); + if (entry != hooks.end()) + { + 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(); + }); + + return true; + } + catch (...) + { + return false; + } + }; + + ops.del_bp = +[](void* args, const size_t addr, bp_type_t) + { + try + { + const auto entry = hooks.find(addr); + if (entry == hooks.end()) + { + return false; + } + + static_cast(args)->delete_hook(entry->second); + hooks.erase(entry); + + return true; + } + catch (...) + { + return false; + } + }; + + ops.on_interrupt = +[](void* args) + { + static_cast(args)->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")); + const auto emu = unicorn::create_x64_emulator(); auto context = setup_context(*emu); @@ -353,9 +531,13 @@ namespace emu->reg(x64_register::rcx, execution_context.value()); emu->reg(x64_register::rdx, context.ntdll.image_base); + emu->reg(x64_register::rip, entry1); + + try { - emu->start(entry1); + gdbstub_run(&stub, static_cast(emu.get())); + //emu->start_from_ip(); } catch (...) { @@ -364,6 +546,7 @@ namespace } printf("Emulation done.\n"); + gdbstub_close(&stub); } }