From 8adc73de71930e1f3eec1c81d1dbb395fab3c0e5 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sun, 12 Jan 2025 11:39:20 +0100 Subject: [PATCH] Prepare custom gdb-stub implementation --- src/CMakeLists.txt | 1 + src/analyzer/CMakeLists.txt | 1 + src/analyzer/main.cpp | 3 + src/gdb-stub/CMakeLists.txt | 21 ++++++ src/gdb-stub/gdb_stub.cpp | 130 ++++++++++++++++++++++++++++++++++++ src/gdb-stub/gdb_stub.hpp | 48 +++++++++++++ 6 files changed, 204 insertions(+) create mode 100644 src/gdb-stub/CMakeLists.txt create mode 100644 src/gdb-stub/gdb_stub.cpp create mode 100644 src/gdb-stub/gdb_stub.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 24b3d98f..77aef22b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,5 +1,6 @@ add_subdirectory(common) add_subdirectory(emulator) +add_subdirectory(gdb-stub) add_subdirectory(unicorn-emulator) add_subdirectory(windows-emulator) if (NOT MOMO_BUILD_AS_LIBRARY) diff --git a/src/analyzer/CMakeLists.txt b/src/analyzer/CMakeLists.txt index 54c497d1..2fbf8005 100644 --- a/src/analyzer/CMakeLists.txt +++ b/src/analyzer/CMakeLists.txt @@ -15,6 +15,7 @@ target_precompile_headers(analyzer PRIVATE std_include.hpp) target_link_libraries(analyzer PRIVATE reflect windows-emulator + gdb-stub ) set_property(GLOBAL PROPERTY VS_STARTUP_PROJECT analyzer) diff --git a/src/analyzer/main.cpp b/src/analyzer/main.cpp index d33d82ea..6dbdafdc 100644 --- a/src/analyzer/main.cpp +++ b/src/analyzer/main.cpp @@ -4,6 +4,7 @@ #include #include "object_watching.hpp" +#include "gdb-stub/gdb_stub.hpp" namespace { @@ -229,6 +230,8 @@ int main(const int argc, char** argv) { try { + gdb_stub::run_gdb_stub(network::address{"0.0.0.0:28960", AF_INET}); + auto args = bundle_arguments(argc, argv); const auto options = parse_options(args); diff --git a/src/gdb-stub/CMakeLists.txt b/src/gdb-stub/CMakeLists.txt new file mode 100644 index 00000000..ca23c4c1 --- /dev/null +++ b/src/gdb-stub/CMakeLists.txt @@ -0,0 +1,21 @@ +file(GLOB_RECURSE SRC_FILES CONFIGURE_DEPENDS + *.cpp + *.hpp + *.rc +) + +list(SORT SRC_FILES) + +add_library(gdb-stub ${SRC_FILES}) + +momo_assign_source_group(${SRC_FILES}) + +target_link_libraries(gdb-stub PRIVATE + common +) + +target_include_directories(gdb-stub INTERFACE + "${CMAKE_CURRENT_LIST_DIR}" +) + +momo_strip_target(gdb-stub) diff --git a/src/gdb-stub/gdb_stub.cpp b/src/gdb-stub/gdb_stub.cpp new file mode 100644 index 00000000..85dd688c --- /dev/null +++ b/src/gdb-stub/gdb_stub.cpp @@ -0,0 +1,130 @@ +#include "gdb_stub.hpp" + +#include +#include +#include + +namespace gdb_stub +{ + namespace + { + constexpr size_t CHECKSUM_SIZE = 2; + + uint8_t compute_checksum(const std::string_view data) + { + uint8_t csum = 0; + for (const auto c : data) + { + csum += static_cast(c); + } + + return csum; + } + + network::tcp_client_socket accept_client(const network::address& bind_address) + { + network::tcp_server_socket server{bind_address.get_family()}; + if (!server.bind(bind_address)) + { + return false; + } + + return server.accept(); + } + + struct packet_queue + { + std::string buffer{}; + std::queue packets{}; + + void enqueue(const std::string& data) + { + buffer.append(data); + this->process(); + } + + void process() + { + while (true) + { + this->trim_start(); + + const auto end = this->buffer.find_first_of('#'); + if (end == std::string::npos) + { + break; + } + + const auto packet_size = end + CHECKSUM_SIZE + 1; + + if (packet_size > this->buffer.size()) + { + break; + } + + auto packet = this->buffer.substr(0, packet_size); + this->buffer.erase(0, packet_size); + + this->enqueue_packet(std::move(packet)); + } + } + + void enqueue_packet(std::string packet) + { + constexpr auto END_BYTES = CHECKSUM_SIZE + 1; + + if (packet.size() < (END_BYTES + 1) // + || packet.front() != '$' // + || packet[packet.size() - END_BYTES] != '#') + { + return; + } + + const auto checksum = strtoul(packet.c_str() + packet.size() - CHECKSUM_SIZE, nullptr, 16); + assert((checksum & 0xFF) == checksum); + + packet.erase(packet.begin()); + packet.erase(packet.size() - END_BYTES, END_BYTES); + + const auto computed_checksum = compute_checksum(packet); + + if (computed_checksum == checksum) + { + this->packets.push(std::move(packet)); + } + } + + void trim_start() + { + while (!this->buffer.empty() && this->buffer.front() != '$') + { + buffer.erase(buffer.begin()); + } + } + }; + } + + bool run_gdb_stub(const network::address& bind_address) + { + const auto client = accept_client(bind_address); + if (!client) + { + return false; + } + + packet_queue queue{}; + + while (true) + { + std::string packet{}; + if (!client.receive(packet)) + { + break; + } + + queue.enqueue(packet); + } + + return true; + } +} diff --git a/src/gdb-stub/gdb_stub.hpp b/src/gdb-stub/gdb_stub.hpp new file mode 100644 index 00000000..c8a09ae8 --- /dev/null +++ b/src/gdb-stub/gdb_stub.hpp @@ -0,0 +1,48 @@ +#pragma once +#include +#include + +namespace gdb_stub +{ + /* + enum class gdb_action : uint8_t + { + none, + resume, + shutdown, + }; + + enum class breakpoint_type : uint8_t + { + software, + hardware_exec, + hardware_write, + hardware_read, + hardware_read_write, + }; + + 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(breakpoint_type type, size_t addr, size_t size) = 0; + virtual bool del_bp(breakpoint_type type, size_t addr, size_t size) = 0; + + virtual void on_interrupt() = 0; + + virtual std::string get_target_description() const = 0; + }; + + bool run_gdb_stub(gdb_stub_handler& handler, size_t register_count, const network::address& bind_address); + */ + bool run_gdb_stub(const network::address& bind_address); +}