Files
x86byte 6c2dca78ab Function boundary detection: multi-pass prolog, tail-call, .pdata, data pointers
- Extended prolog patterns: MSVC x64 callee-saves, sub rsp >= 0x20, enter
- Tail-call detection: jmp to prolog candidates seeds new functions
- endbr64/endbr32 CET skipping at function starts
- PE .pdata exception table parsing for precise start/end boundaries
- Data-section function pointer scanning (vtables, callbacks)
- PLT stub detection with is_thunk tag
- end_address and is_thunk fields in Function struct and JSON output
- getDataRanges() and getRuntimeFunctions() in Binary interface
- README docs for all new features + research paper reference
2026-06-30 10:04:56 +01:00

190 lines
5.4 KiB
C++

#include "disasm/engine.hpp"
#include <cstring>
Disassembler::Disassembler() : m_handle(0), m_initialized(false) {}
Disassembler::~Disassembler()
{
if (m_initialized) cs_close(&m_handle);
}
bool Disassembler::init(Arch arch)
{
cs_arch a;
cs_mode m;
if (arch == Arch::X64) { a = CS_ARCH_X86; m = CS_MODE_64; }
else if (arch == Arch::X86) { a = CS_ARCH_X86; m = CS_MODE_32; }
else return false;
if (cs_open(a, m, &m_handle) != CS_ERR_OK) return false;
cs_option(m_handle, CS_OPT_DETAIL, CS_OPT_ON);
m_initialized = true;
return true;
}
vector<Instruction> Disassembler::disassemble(const vector<uint8_t>& bytes, addr_t base, size_t count)
{
vector<Instruction> result;
if (!m_initialized || bytes.empty()) return result;
const uint8_t* code = bytes.data();
size_t size = bytes.size();
addr_t addr = base;
size_t n = count ? count : size;
cs_insn* insn = cs_malloc(m_handle);
if (!insn) return result;
while (cs_disasm_iter(m_handle, &code, &size, &addr, insn))
{
Instruction inst;
inst.address = insn->address;
inst.mnemonic = insn->mnemonic;
inst.operands = insn->op_str;
inst.size = static_cast<uint8_t>(insn->size);
inst.bytes.assign(insn->bytes, insn->bytes + insn->size);
result.push_back(inst);
if (count && result.size() >= count) break;
}
cs_free(insn, 1);
return result;
}
bool Disassembler::isProlog(const vector<Instruction>& insts) const
{
for (const auto& inst : insts)
{
if (inst.mnemonic == "push" && (inst.operands == "rbp" || inst.operands == "ebp"))
return true;
if (inst.mnemonic == "push")
{
const string& ops = inst.operands;
if (ops == "r15" || ops == "r14" || ops == "r13" || ops == "r12" ||
ops == "rbx" || ops == "rdi" || ops == "rsi")
return true;
}
if (inst.mnemonic == "sub")
{
const string& ops = inst.operands;
size_t p = ops.find("rsp");
if (p == string::npos) p = ops.find("esp");
if (p != string::npos && (p == 0 || ops[p-1] == ' ' || ops[p-1] == ','))
{
size_t x = ops.rfind("0x");
if (x != string::npos && x > p)
{
try {
uint64_t val = stoull(ops.substr(x), nullptr, 16);
if (val >= 0x20) return true;
} catch (...) {}
}
}
}
if (inst.mnemonic == "enter")
return true;
}
return false;
}
bool Disassembler::isEpilog(const Instruction& inst) const
{
return inst.mnemonic == "leave" || inst.mnemonic == "ret" || inst.mnemonic == "retf";
}
bool Disassembler::isReturn(const Instruction& inst) const
{
return inst.mnemonic == "ret" || inst.mnemonic == "retf" || inst.mnemonic == "iret" || inst.mnemonic == "iretd";
}
bool Disassembler::isTrap(const Instruction& inst) const
{
return inst.mnemonic == "hlt" || inst.mnemonic == "ud2" || inst.mnemonic == "int3" || inst.mnemonic == "ud0";
}
bool Disassembler::isCall(const Instruction& inst) const
{
return inst.mnemonic == "call";
}
bool Disassembler::isUnconditionalBranch(const Instruction& inst) const
{
return inst.mnemonic == "jmp" || inst.mnemonic == "ljmp";
}
bool Disassembler::isConditionalBranch(const Instruction& inst) const
{
return inst.mnemonic.size() == 2 && inst.mnemonic[0] == 'j' && inst.mnemonic != "jmp";
}
bool Disassembler::isIndirectBranch(const Instruction& inst) const
{
const string& ops = inst.operands;
if (inst.mnemonic == "call" || inst.mnemonic == "jmp")
{
if (ops.find('[') != string::npos) return true;
if (ops.size() >= 3 && ops[0] == 'r' && isdigit(ops[1])) return true;
if (ops.size() >= 4 && ops[0] == 'r' && ops[1] == '1' && isdigit(ops[2])) return true;
if (ops.size() >= 3 && ops[0] == 'e' && isdigit(ops[1])) return true;
if (ops[0] == '*') return true;
}
return false;
}
addr_t Disassembler::getBranchTarget(const Instruction& inst) const
{
const string& ops = inst.operands;
if (ops.empty()) return 0;
size_t pos = string::npos;
if (ops[0] == '0' && ops.size() > 2 && ops[1] == 'x')
pos = 0;
else if (ops[0] >= '0' && ops[0] <= '9')
pos = 0;
else
pos = ops.find("0x");
if (pos == string::npos) return 0;
try {
return stoull(ops.substr(pos), nullptr, 16);
} catch (...) {
return 0;
}
}
int64_t Disassembler::getRIPDisp(const Instruction& inst) const
{
if (!m_initialized) return 0;
auto bytes = inst.bytes;
if (bytes.empty()) return 0;
cs_insn* insn = cs_malloc(m_handle);
if (!insn) return 0;
const uint8_t* code = bytes.data();
size_t size = bytes.size();
addr_t addr = inst.address;
int64_t disp = 0;
if (cs_disasm_iter(m_handle, &code, &size, &addr, insn))
{
if (insn->detail)
{
for (int i = 0; i < insn->detail->x86.op_count; i++)
{
const auto& op = insn->detail->x86.operands[i];
if (op.type == X86_OP_MEM && op.mem.base == X86_REG_RIP)
{
disp = op.mem.disp;
break;
}
}
}
}
cs_free(insn, 1);
return disp;
}