Add capstone disassembler

This commit is contained in:
momo5502
2025-08-16 11:53:40 +02:00
parent f12fd47d1d
commit 3b72ae9709
7 changed files with 202 additions and 3 deletions

5
.gitmodules vendored
View File

@@ -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

8
deps/CMakeLists.txt vendored
View File

@@ -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)

1
deps/capstone vendored Submodule

Submodule deps/capstone added at b69d944fbe

View File

@@ -17,6 +17,7 @@ endif()
target_link_libraries(analyzer PRIVATE
reflect
debugger
capstone
windows-emulator
windows-gdb-stub
backend-selection

View File

@@ -1,6 +1,7 @@
#include "std_include.hpp"
#include "analysis.hpp"
#include "disassembler.hpp"
#include "windows_emulator.hpp"
#include <utils/lazy_object.hpp>
@@ -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<uint8_t> 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())

View File

@@ -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<const uint8_t> 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_ = {};
}

View File

@@ -0,0 +1,105 @@
#pragma once
#include <capstone/capstone.h>
#include <utils/finally.hpp>
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<cs_insn>() 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<cs_insn> instructions_{};
explicit instructions(const std::span<cs_insn> 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<const uint8_t> data, size_t count) const;
csh get_handle() const
{
return this->handle_;
}
private:
csh handle_{};
void release();
};