Files
BinaryHardening-cfgrip/types.hpp
T
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

219 lines
7.2 KiB
C++

#pragma once
#include <cstdint>
#include <string>
#include <vector>
#include <sstream>
#include <iomanip>
#include <iostream>
#include <fstream>
#include <memory>
using namespace std;
using addr_t = uint64_t;
struct ImportEntry
{
addr_t address;
string name;
string library;
};
struct IndirectTarget
{
addr_t instruction_addr;
addr_t resolved_target;
string name;
string library;
bool resolved;
};
struct XrefEntry
{
addr_t target;
vector<pair<addr_t, string>> callers;
};
struct Instruction
{
addr_t address;
vector<uint8_t> bytes;
string mnemonic;
string operands;
uint8_t size;
int64_t stack_offset = 0;
};
struct BasicBlock
{
addr_t address;
vector<Instruction> instructions;
vector<addr_t> successors;
bool is_prolog;
bool is_epilog;
};
struct Function
{
addr_t address;
addr_t end_address = 0;
string name;
vector<BasicBlock> blocks;
bool is_thunk = false;
};
struct CFG
{
string binary_path;
string arch_str;
string format_str;
string mode_str;
addr_t entry_point;
vector<Function> functions;
vector<ImportEntry> imports;
vector<IndirectTarget> indirect_targets;
vector<XrefEntry> xrefs;
bool has_stack_offsets = false;
static string escapeJSON(const string& s)
{
ostringstream os;
for (char c : s)
{
switch (c)
{
case '"': os << "\\\""; break;
case '\\': os << "\\\\"; break;
case '\n': os << "\\n"; break;
case '\r': os << "\\r"; break;
case '\t': os << "\\t"; break;
default: os << c;
}
}
return os.str();
}
string toJSON() const
{
ostringstream os;
os << "{\n";
os << " \"binary\": \"" << escapeJSON(binary_path) << "\",\n";
os << " \"mode\": \"" << escapeJSON(mode_str) << "\",\n";
os << " \"arch\": \"" << escapeJSON(arch_str) << "\",\n";
os << " \"format\": \"" << escapeJSON(format_str) << "\",\n";
os << " \"entry_point\": \"0x" << hex << entry_point << "\",\n";
os << " \"imports\": [\n";
for (size_t i = 0; i < imports.size(); ++i) {
os << " {\n";
os << " \"address\": \"0x" << hex << imports[i].address << "\",\n";
os << " \"name\": \"" << escapeJSON(imports[i].name) << "\",\n";
os << " \"library\": \"" << escapeJSON(imports[i].library) << "\"\n";
os << " }";
if (i < imports.size() - 1) os << ",";
os << "\n";
}
os << " ],\n";
os << " \"indirect_targets\": [\n";
for (size_t i = 0; i < indirect_targets.size(); ++i) {
const auto& it = indirect_targets[i];
os << " {\n";
os << " \"instruction\": \"0x" << hex << it.instruction_addr << "\",\n";
if (it.resolved) {
os << " \"resolved_target\": \"0x" << it.resolved_target << "\",\n";
} else {
os << " \"resolved_target\": null,\n";
}
os << " \"name\": \"" << escapeJSON(it.name) << "\",\n";
os << " \"library\": \"" << escapeJSON(it.library) << "\"\n";
os << " }";
if (i < indirect_targets.size() - 1) os << ",";
os << "\n";
}
os << " ],\n";
os << " \"functions\": [\n";
for (size_t i = 0; i < functions.size(); ++i) {
const auto& f = functions[i];
os << " {\n";
os << " \"address\": \"0x" << hex << f.address << "\",\n";
if (f.end_address)
os << " \"end_address\": \"0x" << hex << f.end_address << "\",\n";
os << " \"name\": \"" << escapeJSON(f.name) << "\",\n";
if (f.is_thunk)
os << " \"is_thunk\": true,\n";
os << " \"blocks\": [\n";
for (size_t j = 0; j < f.blocks.size(); ++j) {
const auto& b = f.blocks[j];
os << " {\n";
os << " \"address\": \"0x" << hex << b.address << "\",\n";
os << " \"size\": " << dec << b.instructions.size() << ",\n";
os << " \"is_prolog\": " << (b.is_prolog ? "true" : "false") << ",\n";
os << " \"is_epilog\": " << (b.is_epilog ? "true" : "false") << ",\n";
os << " \"instructions\": [\n";
for (size_t k = 0; k < b.instructions.size(); ++k) {
const auto& inst = b.instructions[k];
os << " {\n";
os << " \"address\": \"0x" << hex << inst.address << "\",\n";
os << " \"size\": " << dec << (int)inst.size << ",\n";
os << " \"mnemonic\": \"" << escapeJSON(inst.mnemonic) << "\",\n";
os << " \"operands\": \"" << escapeJSON(inst.operands) << "\"";
if (has_stack_offsets) {
os << ",\n";
os << " \"stack_offset\": " << dec << inst.stack_offset;
}
os << "\n";
os << " }";
if (k < b.instructions.size() - 1) os << ",";
os << "\n";
}
os << " ],\n";
os << " \"successors\": [\n";
for (size_t k = 0; k < b.successors.size(); ++k) {
os << " \"0x" << hex << b.successors[k] << "\"";
if (k < b.successors.size() - 1) os << ",";
os << "\n";
}
os << " ]\n";
os << " }";
if (j < f.blocks.size() - 1) os << ",";
os << "\n";
}
os << " ]\n";
os << " }";
if (i < functions.size() - 1) os << ",";
os << "\n";
}
if (!xrefs.empty())
{
os << " ],\n";
os << " \"xrefs\": [\n";
for (size_t i = 0; i < xrefs.size(); ++i) {
const auto& x = xrefs[i];
os << " {\n";
os << " \"target\": \"0x" << hex << x.target << "\",\n";
os << " \"callers\": [\n";
for (size_t j = 0; j < x.callers.size(); ++j) {
os << " {\n";
os << " \"address\": \"0x" << hex << x.callers[j].first << "\",\n";
os << " \"type\": \"" << escapeJSON(x.callers[j].second) << "\"\n";
os << " }";
if (j < x.callers.size() - 1) os << ",";
os << "\n";
}
os << " ]\n";
os << " }";
if (i < xrefs.size() - 1) os << ",";
os << "\n";
}
os << " ]\n";
}
else
{
os << " ]\n";
}
os << "}\n";
return os.str();
}
};