Skip to content

Commit 960f6cb

Browse files
author
mylovereturns
committed
update: implement cross-platform UI utilities and expand ARM/ARM64 decompiler support
- Unified file dialogs, directory discovery, and font scanning into a platform-agnostic utility layer. - Expanded ARM64 lifting capabilities and implemented a new 32-bit ARM lifter. - Fixed Windows compilation warnings and improved architecture detection in the decompiler. - Updated .gitignore to exclude vcpkg artifacts and local configuration files.
1 parent 1fef2a8 commit 960f6cb

18 files changed

Lines changed: 804 additions & 518 deletions

.gitignore

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,13 @@ Release/
2626
hyperion_layout.ini
2727
imgui.ini
2828
CONTRIBUTING.md
29-
SKILL.md
29+
SKILL.md
30+
31+
# vcpkg
32+
vcpkg_installed/
33+
vcpkg-manifest-install.log
34+
vcpkg-configuration.json
35+
*.hdb
36+
hyperion_recent.txt
37+
hyperion_settings.ini
38+
hyperion_keybinds.ini

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ find_package(Lua REQUIRED)
3434
file(GLOB_RECURSE HYPERION_SOURCES
3535
"src/*.cpp"
3636
"src/*.h"
37+
"src/*.hpp"
3738
)
3839

3940
add_executable(${PROJECT_NAME} ${HYPERION_SOURCES})

src/core/analysis/analysis_db.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ struct FuncSignature {
7676
};
7777

7878
struct AnalysisDB {
79+
Arch arch = Arch::X64;
7980
va_t image_base = 0;
8081
std::unordered_map<va_t, Insn> insns;
8182
std::unordered_map<va_t, Function> funcs;

src/core/analysis/analyzer.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ Analyzer::Analyzer(PEImage& img, WorkerPool& pool)
4949
disasm_.set_arch(img.arch);
5050
cap_disasm_.set_arch(img.arch);
5151
db_.image_base = img.base;
52+
db_.arch = img.arch;
5253
}
5354

5455
const u8* Analyzer::va_to_ptr(va_t addr, size_t* max_len) {

src/core/decompiler/decompiler.cpp

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,16 @@ std::vector<PseudoLine> Decompiler::decompile(const Function& func, const Analys
3838
}
3939

4040
PcodeFunc pf;
41-
bool is_arm64 = false;
42-
for (auto& [ba, bb] : func.blocks) {
43-
if (!bb.insns.empty() && bb.insns[0].len == 4) {
44-
is_arm64 = true;
45-
break;
46-
}
47-
}
48-
if (is_arm64)
41+
if (db.arch == Arch::ARM64)
4942
pf = arm64_lifter_.lift(func, db);
50-
else
43+
else if (db.arch == Arch::ARM)
44+
pf = arm_lifter_.lift(func, db);
45+
else if (db.arch == Arch::X64 || db.arch == Arch::X86)
5146
pf = lifter_.lift(func, db);
47+
else {
48+
// Fallback for other architectures or unsupported ones
49+
return {{0, fmt::format("// architecture not supported in decompiler yet"), func.entry}};
50+
}
5251
ssa_.build(pf);
5352
dce_.run(pf);
5453
prop_.run(pf);

src/core/decompiler/decompiler.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#pragma once
22
#include "core/decompiler/lifter.h"
33
#include "core/decompiler/lifter_arm64.h"
4+
#include "core/decompiler/lifter_arm.h"
45
#include "core/decompiler/ssa.h"
56
#include "core/decompiler/dce.h"
67
#include "core/decompiler/propagate.h"
@@ -20,6 +21,7 @@ class Decompiler {
2021
private:
2122
Lifter lifter_;
2223
LifterARM64 arm64_lifter_;
24+
LifterARM arm_lifter_;
2325
SSABuilder ssa_;
2426
DCE dce_;
2527
Propagate prop_;

src/core/decompiler/lifter_arm.cpp

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
#include "lifter_arm.h"
2+
#include <capstone/arm.h>
3+
#include <fmt/format.h>
4+
#include <algorithm>
5+
#include <cstring>
6+
7+
namespace hype {
8+
9+
Varnode LifterARM::reg_vn(int reg_id) {
10+
if (reg_id == ARM_REG_INVALID) return vn_const(0);
11+
12+
int sz = 4;
13+
const char* name = "unk";
14+
15+
if (reg_id >= ARM_REG_R0 && reg_id <= ARM_REG_R12) {
16+
static thread_local char buf[8];
17+
std::snprintf(buf, sizeof(buf), "r%d", reg_id - ARM_REG_R0);
18+
name = buf;
19+
} else if (reg_id == ARM_REG_SP) { name = "sp"; }
20+
else if (reg_id == ARM_REG_LR) { name = "lr"; }
21+
else if (reg_id == ARM_REG_PC) { name = "pc"; }
22+
23+
return vn_reg(reg_id, name, sz);
24+
}
25+
26+
Varnode LifterARM::operand_read(const Insn& insn, int idx, PcodeBlock& out) {
27+
if (idx >= insn.op_count) return vn_const(0);
28+
auto& op = insn.ops[idx];
29+
switch (op.type) {
30+
case OpType::Reg:
31+
return reg_vn(op.reg);
32+
case OpType::Imm:
33+
return vn_const(op.val);
34+
case OpType::Mem: {
35+
Varnode addr = vn_const(0);
36+
bool has = false;
37+
if (op.mem.base) {
38+
addr = reg_vn(op.mem.base);
39+
has = true;
40+
}
41+
if (op.mem.index) {
42+
Varnode idx_vn = reg_vn(op.mem.index);
43+
if (has) {
44+
Varnode t = alloc_temp();
45+
emit(out, PcodeOp::ADD, t, {addr, idx_vn});
46+
addr = t;
47+
} else { addr = idx_vn; }
48+
has = true;
49+
}
50+
if (op.mem.disp != 0) {
51+
Varnode d = vn_const(static_cast<u64>(op.mem.disp));
52+
if (has) {
53+
Varnode t = alloc_temp();
54+
emit(out, PcodeOp::ADD, t, {addr, d});
55+
addr = t;
56+
} else { addr = d; }
57+
}
58+
Varnode res = alloc_temp(4);
59+
emit(out, PcodeOp::LOAD, res, {addr});
60+
return res;
61+
}
62+
default: return vn_const(0);
63+
}
64+
}
65+
66+
void LifterARM::operand_write(const Insn& insn, int idx, Varnode val, PcodeBlock& out) {
67+
if (idx >= insn.op_count) return;
68+
auto& op = insn.ops[idx];
69+
if (op.type == OpType::Reg) {
70+
emit(out, PcodeOp::COPY, reg_vn(op.reg), {val});
71+
} else if (op.type == OpType::Mem) {
72+
Varnode addr = vn_const(0);
73+
bool has = false;
74+
if (op.mem.base) {
75+
addr = reg_vn(op.mem.base);
76+
has = true;
77+
}
78+
if (op.mem.index) {
79+
Varnode idx_vn = reg_vn(op.mem.index);
80+
if (has) {
81+
Varnode t = alloc_temp();
82+
emit(out, PcodeOp::ADD, t, {addr, idx_vn});
83+
addr = t;
84+
} else { addr = idx_vn; }
85+
has = true;
86+
}
87+
if (op.mem.disp != 0) {
88+
Varnode d = vn_const(static_cast<u64>(op.mem.disp));
89+
if (has) {
90+
Varnode t = alloc_temp();
91+
emit(out, PcodeOp::ADD, t, {addr, d});
92+
addr = t;
93+
} else { addr = d; }
94+
}
95+
emit(out, PcodeOp::STORE, {}, {addr, val});
96+
}
97+
}
98+
99+
Varnode LifterARM::alloc_temp(int sz) { return vn_temp(next_temp_++, sz); }
100+
101+
void LifterARM::emit(PcodeBlock& b, PcodeOp op, Varnode out, std::vector<Varnode> in, va_t a) {
102+
PcodeInsn p;
103+
p.op = op;
104+
p.output = out;
105+
p.inputs = std::move(in);
106+
p.addr = a ? a : cur_addr_;
107+
p.seq = cur_seq_++;
108+
b.ops.push_back(std::move(p));
109+
}
110+
111+
void LifterARM::lift_insn(const Insn& insn, PcodeBlock& blk, const AnalysisDB& db) {
112+
cur_addr_ = insn.addr;
113+
cur_seq_ = 0;
114+
115+
std::string_view mn(insn.mnemonic);
116+
117+
if (mn == "mov" || mn == "movw" || mn == "movt") {
118+
Varnode src = operand_read(insn, 1, blk);
119+
operand_write(insn, 0, src, blk);
120+
} else if (mn == "add" || mn == "adds") {
121+
Varnode lhs = operand_read(insn, 1, blk);
122+
Varnode rhs = operand_read(insn, 2, blk);
123+
if (insn.op_count == 2) {
124+
lhs = operand_read(insn, 0, blk);
125+
rhs = operand_read(insn, 1, blk);
126+
}
127+
Varnode res = alloc_temp(lhs.size);
128+
emit(blk, PcodeOp::ADD, res, {lhs, rhs});
129+
operand_write(insn, 0, res, blk);
130+
} else if (mn == "sub" || mn == "subs") {
131+
Varnode lhs = operand_read(insn, 1, blk);
132+
Varnode rhs = operand_read(insn, 2, blk);
133+
if (insn.op_count == 2) {
134+
lhs = operand_read(insn, 0, blk);
135+
rhs = operand_read(insn, 1, blk);
136+
}
137+
Varnode res = alloc_temp(lhs.size);
138+
emit(blk, PcodeOp::SUB, res, {lhs, rhs});
139+
operand_write(insn, 0, res, blk);
140+
} else if (mn == "ldr" || mn == "ldrb" || mn == "ldrh") {
141+
Varnode res = operand_read(insn, 1, blk);
142+
operand_write(insn, 0, res, blk);
143+
} else if (mn == "str" || mn == "strb" || mn == "strh") {
144+
Varnode val = operand_read(insn, 0, blk);
145+
operand_write(insn, 1, val, blk);
146+
} else if (mn == "b" || mn == "bl" || mn == "bx" || mn == "blx") {
147+
va_t target = insn.branch_target();
148+
if (mn == "bl" || mn == "blx") {
149+
Varnode fn_addr = vn_const(target);
150+
if (target) {
151+
auto it = db.names.find(target);
152+
if (it != db.names.end()) fn_addr.name = it->second;
153+
}
154+
emit(blk, PcodeOp::CALL, reg_vn(ARM_REG_R0), {fn_addr});
155+
} else if (mn == "bx" && insn.ops[0].type == OpType::Reg && insn.ops[0].reg == ARM_REG_LR) {
156+
blk.has_return = true;
157+
emit(blk, PcodeOp::RETURN, {}, {reg_vn(ARM_REG_R0)});
158+
} else {
159+
emit(blk, PcodeOp::BRANCH, {}, {vn_const(target)});
160+
}
161+
} else {
162+
emit(blk, PcodeOp::NOP, alloc_temp(), {});
163+
}
164+
}
165+
166+
void LifterARM::lift_block(const BasicBlock& bb, const AnalysisDB& db, PcodeBlock& out) {
167+
out.addr = bb.start;
168+
for (auto& insn : bb.insns)
169+
lift_insn(insn, out, db);
170+
}
171+
172+
PcodeFunc LifterARM::lift(const Function& func, const AnalysisDB& db) {
173+
next_temp_ = 256;
174+
PcodeFunc pf;
175+
pf.entry = func.entry;
176+
pf.name = func.name.empty() ? fmt::format("sub_{:X}", func.entry) : func.name;
177+
178+
std::vector<va_t> order;
179+
for (auto& [addr, _] : func.blocks) order.push_back(addr);
180+
std::sort(order.begin(), order.end());
181+
182+
for (int i = 0; i < (int)order.size(); ++i) addr_to_block_[order[i]] = i;
183+
184+
for (int i = 0; i < (int)order.size(); ++i) {
185+
auto it = func.blocks.find(order[i]);
186+
if (it == func.blocks.end()) continue;
187+
PcodeBlock blk;
188+
blk.id = i;
189+
lift_block(it->second, db, blk);
190+
for (va_t s : it->second.succs) {
191+
auto sit = addr_to_block_.find(s);
192+
if (sit != addr_to_block_.end()) blk.succs.push_back(sit->second);
193+
}
194+
pf.blocks.push_back(std::move(blk));
195+
}
196+
197+
for (int i = 0; i < (int)pf.blocks.size(); ++i) {
198+
for (int s : pf.blocks[i].succs)
199+
if (s >= 0 && s < (int)pf.blocks.size())
200+
pf.blocks[s].preds.push_back(i);
201+
}
202+
203+
pf.next_temp = next_temp_;
204+
pf.params = { reg_vn(ARM_REG_R0), reg_vn(ARM_REG_R1), reg_vn(ARM_REG_R2), reg_vn(ARM_REG_R3) };
205+
addr_to_block_.clear();
206+
return pf;
207+
}
208+
209+
}

src/core/decompiler/lifter_arm.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#pragma once
2+
#include "core/decompiler/ir.h"
3+
#include "core/analysis/analysis_db.h"
4+
#include <unordered_map>
5+
#include <string_view>
6+
7+
namespace hype {
8+
9+
class LifterARM {
10+
public:
11+
PcodeFunc lift(const Function& func, const AnalysisDB& db);
12+
13+
private:
14+
void lift_block(const BasicBlock& bb, const AnalysisDB& db, PcodeBlock& out);
15+
void lift_insn(const Insn& insn, PcodeBlock& blk, const AnalysisDB& db);
16+
17+
Varnode reg_vn(int reg_id);
18+
Varnode operand_read(const Insn& insn, int idx, PcodeBlock& out);
19+
void operand_write(const Insn& insn, int idx, Varnode val, PcodeBlock& out);
20+
21+
Varnode alloc_temp(int sz = 4);
22+
void emit(PcodeBlock& b, PcodeOp op, Varnode out, std::vector<Varnode> in, va_t a = 0);
23+
24+
int next_temp_ = 256;
25+
va_t cur_addr_ = 0;
26+
int cur_seq_ = 0;
27+
std::unordered_map<va_t, int> addr_to_block_;
28+
};
29+
30+
}

0 commit comments

Comments
 (0)