From 3b72ae97097a7f54a5b8fa3e49df3b921488db3a Mon Sep 17 00:00:00 2001 From: momo5502 Date: Sat, 16 Aug 2025 11:53:40 +0200 Subject: [PATCH] Add capstone disassembler --- .gitmodules | 5 +- deps/CMakeLists.txt | 8 +++ deps/capstone | 1 + src/analyzer/CMakeLists.txt | 1 + src/analyzer/analysis.cpp | 25 +++++++- src/analyzer/disassembler.cpp | 60 +++++++++++++++++++ src/analyzer/disassembler.hpp | 105 ++++++++++++++++++++++++++++++++++ 7 files changed, 202 insertions(+), 3 deletions(-) create mode 160000 deps/capstone create mode 100644 src/analyzer/disassembler.cpp create mode 100644 src/analyzer/disassembler.hpp diff --git a/.gitmodules b/.gitmodules index aa39392f..a574631c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -33,4 +33,7 @@ path = deps/minidump_cpp url = https://github.com/redthing1/minidump_cpp shallow = true - +[submodule "deps/capstone"] + path = deps/capstone + url = https://github.com/capstone-engine/capstone.git + shallow = true \ No newline at end of file diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 3d13f896..ac18fc89 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -29,5 +29,13 @@ add_subdirectory(minidump_cpp) ########################################## +option(CAPSTONE_X86_SUPPORT "" ON) +option(CAPSTONE_X86_ATT_DISABLE "" ON) +option(CAPSTONE_ARCHITECTURE_DEFAULT "" OFF) +option(CAPSTONE_BUILD_STATIC_MSVC_RUNTIME "" OFF) +add_subdirectory(capstone) + +########################################## + include(googletest.cmake) include(zlib.cmake) diff --git a/deps/capstone b/deps/capstone new file mode 160000 index 00000000..b69d944f --- /dev/null +++ b/deps/capstone @@ -0,0 +1 @@ +Subproject commit b69d944fbe0fcc82a2e1c0e846eecdb7f2e18881 diff --git a/src/analyzer/CMakeLists.txt b/src/analyzer/CMakeLists.txt index a0ed8607..2186409b 100644 --- a/src/analyzer/CMakeLists.txt +++ b/src/analyzer/CMakeLists.txt @@ -17,6 +17,7 @@ endif() target_link_libraries(analyzer PRIVATE reflect debugger + capstone windows-emulator windows-gdb-stub backend-selection diff --git a/src/analyzer/analysis.cpp b/src/analyzer/analysis.cpp index 9c3277f5..3f063a77 100644 --- a/src/analyzer/analysis.cpp +++ b/src/analyzer/analysis.cpp @@ -1,6 +1,7 @@ #include "std_include.hpp" #include "analysis.hpp" +#include "disassembler.hpp" #include "windows_emulator.hpp" #include @@ -149,7 +150,7 @@ namespace } } - void handle_function_details(analysis_context& c, const std::string_view function) + void handle_function_details(const analysis_context& c, const std::string_view function) { if (function == "GetEnvironmentVariableA" || function == "ExpandEnvironmentStringsA") { @@ -234,6 +235,25 @@ namespace } } + bool is_return(const emulator& emu, const uint64_t address) + { + std::vector instruction_bytes(15, 0); + const auto result = emu.try_read_memory(address, instruction_bytes.data(), instruction_bytes.size()); + if (!result) + { + return false; + } + + disassembler disasm{}; + const auto instructions = disasm.disassemble(instruction_bytes, 1); + if (instructions.empty()) + { + return false; + } + + return cs_insn_group(disasm.get_handle(), &instructions[0], CS_GRP_RET); + } + void handle_instruction(analysis_context& c, const uint64_t address) { auto& win_emu = *c.win_emu; @@ -313,7 +333,8 @@ namespace win_emu.log.print(is_interesting_call ? color::yellow : color::gray, "Executing entry point: %s (0x%" PRIx64 ")\n", binary->name.c_str(), address); } - else if (is_previous_main_exe && binary != previous_binary) + else if (is_previous_main_exe && binary != previous_binary && + !is_return(c.win_emu->emu(), win_emu.process.previous_ip)) { auto nearest_entry = binary->address_names.upper_bound(address); if (nearest_entry == binary->address_names.begin()) diff --git a/src/analyzer/disassembler.cpp b/src/analyzer/disassembler.cpp new file mode 100644 index 00000000..160a4e92 --- /dev/null +++ b/src/analyzer/disassembler.cpp @@ -0,0 +1,60 @@ +#include "std_include.hpp" + +#include "disassembler.hpp" + +disassembler::disassembler() +{ + const auto res = cs_open(CS_ARCH_X86, CS_MODE_64, &this->handle_); + if (res != CS_ERR_OK) + { + throw std::runtime_error("Failed to initialize capstone"); + } +} + +disassembler::~disassembler() +{ + this->release(); +} + +disassembler::disassembler(disassembler&& obj) noexcept +{ + this->operator=(std::move(obj)); +} + +disassembler& disassembler::operator=(disassembler&& obj) noexcept +{ + if (this != &obj) + { + this->release(); + this->handle_ = obj.handle_; + obj.handle_ = 0; + } + + return *this; +} + +void disassembler::release() +{ + if (this->handle_) + { + cs_close(&this->handle_); + this->handle_ = 0; + } +} + +instructions disassembler::disassemble(const std::span data, const size_t count) const +{ + cs_insn* insts{}; + const auto inst_count = cs_disasm(this->handle_, data.data(), data.size(), count, 0, &insts); + return instructions{std::span(insts, inst_count)}; +} + +void instructions::release() +{ + if (!this->instructions_.empty()) + { + cs_free(this->instructions_.data(), this->instructions_.size()); + } + + this->instructions_ = {}; +} diff --git a/src/analyzer/disassembler.hpp b/src/analyzer/disassembler.hpp new file mode 100644 index 00000000..c055bb20 --- /dev/null +++ b/src/analyzer/disassembler.hpp @@ -0,0 +1,105 @@ +#pragma once + +#include +#include + +class instructions +{ + public: + instructions() = default; + ~instructions() + { + this->release(); + } + + instructions(instructions&& obj) noexcept + : instructions() + { + this->operator=(std::move(obj)); + } + + instructions& operator=(instructions&& obj) noexcept + { + if (this != &obj) + { + this->release(); + this->instructions_ = obj.instructions_; + obj.instructions_ = {}; + } + + return *this; + } + + instructions(const instructions&) = delete; + instructions& operator=(const instructions&) = delete; + + operator std::span() const + { + return this->instructions_; + } + + bool empty() const noexcept + { + return this->instructions_.empty(); + } + + size_t size() const noexcept + { + return this->instructions_.size(); + } + + const cs_insn* data() const noexcept + { + return this->instructions_.data(); + } + + const cs_insn& operator[](const size_t index) const + { + return this->instructions_[index]; + } + + auto begin() const + { + return this->instructions_.begin(); + } + auto end() const + { + return this->instructions_.end(); + } + + private: + friend class disassembler; + std::span instructions_{}; + + explicit instructions(const std::span insts) + : instructions_(insts) + { + } + + void release(); +}; + +class disassembler +{ + public: + disassembler(); + ~disassembler(); + + disassembler(disassembler&& obj) noexcept; + disassembler& operator=(disassembler&& obj) noexcept; + + disassembler(const disassembler& obj) = delete; + disassembler& operator=(const disassembler& obj) = delete; + + instructions disassemble(std::span data, size_t count) const; + + csh get_handle() const + { + return this->handle_; + } + + private: + csh handle_{}; + + void release(); +};