Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ find_package(spdlog CONFIG REQUIRED)
find_package(fmt CONFIG REQUIRED)
find_package(ZLIB REQUIRED)
find_package(Lua REQUIRED)
find_package(nlohmann_json CONFIG REQUIRED)

file(GLOB_RECURSE HYPERION_SOURCES
"src/*.cpp"
Expand All @@ -54,6 +55,7 @@ target_link_libraries(${PROJECT_NAME} PRIVATE
fmt::fmt
ZLIB::ZLIB
${LUA_LIBRARIES}
nlohmann_json::nlohmann_json
)

if(WIN32)
Expand Down
13 changes: 10 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ Disassembly: Zydis (x86/x64) + Capstone (ARM, ARM64, MIPS, PPC)
- Script API: get_name, set_name, get_insn, get_bytes, get_functions, get_xrefs_to, set_comment, goto_addr, patch_byte, get_segments, get_arch, create_function
- See [docs/scripting.md](docs/scripting.md) and [docs/plugins.md](docs/plugins.md)

**MCP Server (Model Context Protocol)**
- Built-in headless MCP server for AI integration (Cursor, Claude Desktop, etc.)
- Access disassembly, decompilation, xrefs, and binary structures directly via AI prompts
- Start the server by passing `--mcp` to the executable:
`./build/Release/Hyperion --mcp` (or equivalent on your platform)
- Supports checking status, finding functions, string references, modifying comments/names, and decompiling over stdio.

**Customization**
- Settings panel (Ctrl+,): fonts, colors, keybinds, advanced options
- Editable keybinds (press-to-assign, persisted)
Expand Down Expand Up @@ -152,9 +159,9 @@ Dependencies (pulled via vcpkg): imgui (docking), glfw, zydis, capstone, spdlog,

| Platform | Status |
|----------|--------|
| Windows x64 | Full support |
| Linux x64 | Builds, full support |
| macOS (Intel + Apple Silicon) | Builds, full support |
| Windows x64 | Full support (UI + MCP) |
| Linux x64 | Builds, full support (UI + MCP) |
| macOS (Intel + Apple Silicon) | Builds, full support (UI + MCP) |

## Status

Expand Down
1 change: 1 addition & 0 deletions hyperion_mcp.log
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[01:23:51.459] [info] Starting MCP stdio server...
2 changes: 1 addition & 1 deletion src/core/analysis/analyzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -985,7 +985,7 @@ void Analyzer::recover_structs() {
total_size = static_cast<u32>(off) + sz;

u32 sid = db_.types.add_struct(name, total_size);
u32 field_idx = 0;
[[maybe_unused]] u32 field_idx = 0;
for (auto& [off, sz] : fields_set) {
std::string fname = fmt::format("field_{:X}", off);
u32 type_id = 0;
Expand Down
8 changes: 6 additions & 2 deletions src/core/analysis/rtti.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace hype {

namespace {

const u8* va_to_ptr(PEImage& img, va_t addr, size_t* max_len = nullptr) {
[[maybe_unused]] const u8* va_to_ptr(PEImage& img, va_t addr, size_t* max_len = nullptr) {
for (auto& seg : img.segments) {
if (seg.contains(addr)) {
size_t off = static_cast<size_t>(addr - seg.va);
Expand Down Expand Up @@ -65,6 +65,10 @@ std::string demangle_rtti(const std::string& mangled) {

} // namespace

}

namespace hype {

void RTTIParser::parse(PEImage& img, AnalysisDB& db) {
if (img.arch != Arch::X64) return;

Expand All @@ -78,7 +82,7 @@ void RTTIParser::parse(PEImage& img, AnalysisDB& db) {
}

void RTTIParser::find_type_descriptors(PEImage& img) {
constexpr std::string_view kPattern = ".?AV";
[[maybe_unused]] constexpr std::string_view kPattern = ".?AV";

for (auto& seg : img.segments) {
if (seg.executable() || seg.data.empty()) continue;
Expand Down
4 changes: 2 additions & 2 deletions src/core/decompiler/dce.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ bool DCE::is_dead_stack_op(const PcodeInsn& op, const PcodeFunc&) {
}

void DCE::run(PcodeFunc& func) {
static constexpr int RSP_ID = 4;
static constexpr int RBP_ID = 5;
[[maybe_unused]] static constexpr int RSP_ID = 4;
[[maybe_unused]] static constexpr int RBP_ID = 5;

// Pass 1: eliminate ONLY redundant prologue/epilogue register saves/restores
// and flag computations not used by branches.
Expand Down
8 changes: 4 additions & 4 deletions src/core/decompiler/lifter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@

namespace hype {

static constexpr int REG_RAX = 0, REG_RCX = 1, REG_RDX = 2, REG_RBX = 3;
static constexpr int REG_RSP = 4, REG_RBP = 5, REG_RSI = 6, REG_RDI = 7;
static constexpr int REG_R8 = 8, REG_R9 = 9, REG_R10 = 10, REG_R11 = 11;
static constexpr int REG_R12 = 12, REG_R13 = 13, REG_R14 = 14, REG_R15 = 15;
[[maybe_unused]] static constexpr int REG_RAX = 0, REG_RCX = 1, REG_RDX = 2, REG_RBX = 3;
[[maybe_unused]] static constexpr int REG_RSP = 4, REG_RBP = 5, REG_RSI = 6, REG_RDI = 7;
[[maybe_unused]] static constexpr int REG_R8 = 8, REG_R9 = 9, REG_R10 = 10, REG_R11 = 11;
[[maybe_unused]] static constexpr int REG_R12 = 12, REG_R13 = 13, REG_R14 = 14, REG_R15 = 15;
static constexpr int REG_ZF = 100, REG_CF = 101, REG_SF = 102, REG_OF = 103;

static const struct { u16 zreg; const char* name; int id; int size; } kRegTable[] = {
Expand Down
4 changes: 2 additions & 2 deletions src/core/decompiler/lifter_arm64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@

namespace hype {

static constexpr int REG_ARM64_SP = ARM64_REG_SP;
[[maybe_unused]] static constexpr int REG_ARM64_SP = ARM64_REG_SP;
static constexpr int REG_ARM64_XZR = ARM64_REG_XZR;
static constexpr int REG_ARM64_WZR = ARM64_REG_WZR;
static constexpr int REG_ARM64_NZCV = ARM64_REG_NZCV;
[[maybe_unused]] static constexpr int REG_ARM64_NZCV = ARM64_REG_NZCV;

Varnode LifterARM64::reg_vn(int reg_id) {
if (reg_id == ARM64_REG_INVALID) return vn_const(0);
Expand Down
34 changes: 30 additions & 4 deletions src/core/decompiler/type_infer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,32 @@

namespace hype {

DecompType DecompType::make_void() { return {DTypeKind::Void, nullptr, 0, "", false}; }
DecompType DecompType::make_bool() { return {DTypeKind::Bool, nullptr, 0, "", false}; }
DecompType DecompType::make_char() { return {DTypeKind::Char, nullptr, 0, "", false}; }
DecompType DecompType::make_int(int bits, bool sign) {
switch (bits) {
case 8: return {sign ? DTypeKind::Int8 : DTypeKind::UInt8, nullptr, 0, "", false};
case 16: return {sign ? DTypeKind::Int16 : DTypeKind::UInt16, nullptr, 0, "", false};
case 32: return {sign ? DTypeKind::Int32 : DTypeKind::UInt32, nullptr, 0, "", false};
default: return {sign ? DTypeKind::Int64 : DTypeKind::UInt64, nullptr, 0, "", false};
}
}
DecompType DecompType::make_sizet() { return {DTypeKind::SizeT, nullptr, 0, "", false}; }
DecompType DecompType::make_ptr(DecompType pointee, bool c) {
DecompType t{DTypeKind::Pointer, nullptr, 0, "", c};
t.inner = std::make_shared<DecompType>(std::move(pointee));
return t;
}
DecompType DecompType::make_array(DecompType inner, u32 count) {
DecompType t{DTypeKind::Array, nullptr, static_cast<int>(count), "", false};
t.inner = std::make_shared<DecompType>(std::move(inner));
return t;
}
DecompType DecompType::make_struct(std::string name) {
return {DTypeKind::Struct, nullptr, 0, std::move(name), false};
}

int DecompType::bit_width() const {
switch (kind) {
case DTypeKind::Bool: case DTypeKind::Int8: case DTypeKind::UInt8: case DTypeKind::Char: return 8;
Expand Down Expand Up @@ -80,7 +106,7 @@ void TypeInfer::init_known_funcs() {
{"CreateFileA", void_ptr, {const_char_ptr, uint32, uint32, void_ptr, uint32, uint32, void_ptr},
{"lpFileName", "dwDesiredAccess", "dwShareMode", "lpSecurityAttributes",
"dwCreationDisposition", "dwFlagsAndAttributes", "hTemplateFile"}},
{"CreateFileW", void_ptr, {DecompType::make_ptr(DecompType{DTypeKind::WChar}), uint32, uint32, void_ptr, uint32, uint32, void_ptr},
{"CreateFileW", void_ptr, {DecompType::make_ptr(DecompType{DTypeKind::WChar, nullptr, 0, "", false}), uint32, uint32, void_ptr, uint32, uint32, void_ptr},
{"lpFileName", "dwDesiredAccess", "dwShareMode", "lpSecurityAttributes",
"dwCreationDisposition", "dwFlagsAndAttributes", "hTemplateFile"}},
{"CloseHandle", int32, {void_ptr}, {"hObject"}},
Expand Down Expand Up @@ -166,7 +192,7 @@ void TypeInfer::infer_from_calls(const PcodeFunc& func) {

void TypeInfer::infer_params(const PcodeFunc& func) {
for (auto& p : func.params)
set_type(p.id, DecompType{DTypeKind::Int64});
set_type(p.id, DecompType{DTypeKind::Int64, nullptr, 0, "", false});

for (auto& blk : func.blocks) {
for (auto& op : blk.ops) {
Expand Down Expand Up @@ -219,7 +245,7 @@ const KnownFunc* TypeInfer::find_known(const std::string& name) const {
DecompType TypeInfer::get_type(int var_id) const {
auto it = types_.find(var_id);
if (it != types_.end()) return it->second;
return DecompType{DTypeKind::Int64};
return DecompType{DTypeKind::Int64, nullptr, 0, "", false};
}

std::string TypeInfer::get_var_name(int var_id) const {
Expand All @@ -231,7 +257,7 @@ std::string TypeInfer::get_var_name(int var_id) const {
void TypeInfer::run(PcodeFunc& func) {
types_.clear();
names_.clear();
ret_type_ = DecompType{DTypeKind::Int64};
ret_type_ = DecompType{DTypeKind::Int64, nullptr, 0, "", false};

init_known_funcs();
infer_params(func);
Expand Down
32 changes: 11 additions & 21 deletions src/core/decompiler/type_infer.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,34 +12,24 @@ enum class DTypeKind : u8 {
Int8, Int16, Int32, Int64,
UInt8, UInt16, UInt32, UInt64,
Float, Double,
Pointer, Array, FuncPtr, SizeT
Pointer, Array, FuncPtr, Struct, SizeT
};

struct DecompType {
DTypeKind kind = DTypeKind::Int64;
std::shared_ptr<DecompType> inner;
int array_count = 0;
std::string struct_name;
bool is_const = false;

static DecompType make_void() { return {DTypeKind::Void}; }
static DecompType make_bool() { return {DTypeKind::Bool}; }
static DecompType make_char() { return {DTypeKind::Char}; }
static DecompType make_int(int bits, bool sign = true) {
switch (bits) {
case 8: return {sign ? DTypeKind::Int8 : DTypeKind::UInt8};
case 16: return {sign ? DTypeKind::Int16 : DTypeKind::UInt16};
case 32: return {sign ? DTypeKind::Int32 : DTypeKind::UInt32};
default: return {sign ? DTypeKind::Int64 : DTypeKind::UInt64};
}
}
static DecompType make_sizet() { return {DTypeKind::SizeT}; }
static DecompType make_ptr(DecompType pointee, bool c = false) {
DecompType t;
t.kind = DTypeKind::Pointer;
t.inner = std::make_shared<DecompType>(std::move(pointee));
t.is_const = c;
return t;
}
static DecompType make_void();
static DecompType make_bool();
static DecompType make_char();
static DecompType make_int(int bits, bool sign = true);
static DecompType make_sizet();
static DecompType make_ptr(DecompType pointee, bool c = false);
static DecompType make_array(DecompType inner, u32 count);
static DecompType make_struct(std::string name);

std::string to_string() const;
bool is_pointer() const { return kind == DTypeKind::Pointer; }
Expand Down Expand Up @@ -80,7 +70,7 @@ class TypeInfer {
std::unordered_map<int, DecompType> types_;
std::unordered_map<int, std::string> names_;
std::vector<KnownFunc> known_funcs_;
DecompType ret_type_{DTypeKind::Int64};
DecompType ret_type_{DTypeKind::Int64, nullptr, 0, "", false};
};

}
9 changes: 5 additions & 4 deletions src/core/disasm/disassembler.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ namespace hype {
enum class InsnType : u8 {
Unknown, Nop, Mov, Push, Pop,
Call, Ret, Jmp, Jcc,
Cmp, Test,
Add, Sub, Mul, Div,
And, Or, Xor, Not, Shl, Shr,
Lea, Int, Syscall, Other
Cmp, Test, Setcc,
Add, Sub, Mul, Div, Imul, Idiv,
Inc, Dec,
And, Or, Xor, Not, Shl, Shr, Sar, Rol, Ror,
Lea, Int, Syscall, Movsx, Movzx, Other
};

enum class OpType : u8 { None, Reg, Imm, Mem };
Expand Down
16 changes: 8 additions & 8 deletions src/core/loader/elf_loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ constexpr u16 EM_AARCH64 = 183;

// Segment types
constexpr u32 PT_LOAD = 1;
constexpr u32 PT_DYNAMIC = 2;
[[maybe_unused]] constexpr u32 PT_DYNAMIC = 2;

// Segment flags
constexpr u32 PF_X = 1;
Expand All @@ -38,8 +38,8 @@ constexpr u32 PF_R = 4;

// Section types
constexpr u32 SHT_SYMTAB = 2;
constexpr u32 SHT_STRTAB = 3;
constexpr u32 SHT_DYNAMIC = 6;
[[maybe_unused]] constexpr u32 SHT_STRTAB = 3;
[[maybe_unused]] constexpr u32 SHT_DYNAMIC = 6;
constexpr u32 SHT_DYNSYM = 11;

// Symbol binding/type
Expand All @@ -48,11 +48,11 @@ constexpr u8 STB_WEAK = 2;
constexpr u8 STT_FUNC = 2;

// Dynamic tags
constexpr i64 DT_NEEDED = 1;
constexpr i64 DT_PLTGOT = 3;
constexpr i64 DT_STRTAB = 5;
constexpr i64 DT_JMPREL = 23;
constexpr i64 DT_PLTRELSZ = 2;
[[maybe_unused]] constexpr i64 DT_NEEDED = 1;
[[maybe_unused]] constexpr i64 DT_PLTGOT = 3;
[[maybe_unused]] constexpr i64 DT_STRTAB = 5;
[[maybe_unused]] constexpr i64 DT_JMPREL = 23;
[[maybe_unused]] constexpr i64 DT_PLTRELSZ = 2;

#pragma pack(push, 1)
struct Elf32_Ehdr {
Expand Down
5 changes: 4 additions & 1 deletion src/core/loader/pe_loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ bool safe_add(size_t a, size_t b, size_t& result) {
return result >= a;
}

bool safe_add32(u32 a, u32 b, u32& result) {
[[maybe_unused]] bool safe_add32(u32 a, u32 b, u32& result) {
result = a + b;
return result >= a;
}
Expand Down Expand Up @@ -472,6 +472,9 @@ void PELoader::parse_exceptions(PEImage& img) {
struct RtFunc { u32 begin_rva; u32 end_rva; u32 unwind_rva; };
u32 count = pdata.size / sizeof(RtFunc);

[[maybe_unused]] constexpr u32 kMaxExceptions = 500'000;
if (count > kMaxExceptions) count = kMaxExceptions;

for (u32 i = 0; i < count; ++i) {
size_t entry_off;
if (!safe_add(raw_off, static_cast<size_t>(i) * sizeof(RtFunc), entry_off)) break;
Expand Down
2 changes: 2 additions & 0 deletions src/debugger/instrumentation_cb.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
#ifndef NOMINMAX
#define NOMINMAX
#endif
#ifdef _WIN32
#include <windows.h>
#endif

namespace hype {

Expand Down
31 changes: 26 additions & 5 deletions src/main.cpp
Original file line number Diff line number Diff line change
@@ -1,15 +1,36 @@
#include "ui/app.h"
#include "mcp/mcp_server.h"
#include <spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <iostream>
#include <string_view>

int main(int argc, char** argv) {
(void)argv;
spdlog::set_level(spdlog::level::info);
spdlog::set_pattern("[%H:%M:%S.%e] [%l] %v");
bool run_mcp = false;
for (int i = 1; i < argc; ++i) {
if (std::string_view(argv[i]) == "--mcp") {
run_mcp = true;
}
}

if (run_mcp) {
// When running MCP over stdio, redirect logs to a file to avoid corrupting stdout JSON
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("hyperion_mcp.log", true);
auto logger = std::make_shared<spdlog::logger>("mcp_logger", file_sink);
spdlog::set_default_logger(logger);
spdlog::set_level(spdlog::level::info);
spdlog::set_pattern("[%H:%M:%S.%e] [%l] %v");
} else {
spdlog::set_level(spdlog::level::info);
spdlog::set_pattern("[%H:%M:%S.%e] [%l] %v");
}

hype::App app;

if (argc > 1) {
// will be handled via command line in future
if (run_mcp) {
hype::McpServer mcp(app);
mcp.run_stdio();
return 0;
}

return app.run();
Expand Down
Loading
Loading