mirror of
https://gitlab.com/BinaryHardening/cfgrip
synced 2026-07-26 12:41:08 +00:00
213 lines
6.9 KiB
C++
213 lines
6.9 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;
|
|
string name;
|
|
vector<BasicBlock> blocks;
|
|
};
|
|
|
|
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";
|
|
os << " \"name\": \"" << escapeJSON(f.name) << "\",\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();
|
|
}
|
|
};
|