mirror of
https://gitlab.com/BinaryHardening/cfgrip
synced 2026-07-26 12:41:08 +00:00
718 lines
23 KiB
C++
718 lines
23 KiB
C++
#include "cfg/builder.hpp"
|
|
#include <queue>
|
|
#include <algorithm>
|
|
|
|
static bool isMemOp(const string& op)
|
|
{
|
|
return op.find('[') != string::npos || op.find('+') != string::npos;
|
|
}
|
|
|
|
static bool regMatch(const string& op, const string& target)
|
|
{
|
|
auto norm = [](const string& s) -> string {
|
|
if (s.size() >= 3 && s[0] == 'r' && isdigit(s[1])) return s.substr(1);
|
|
if (s.size() >= 3 && s[0] == 'e' && isdigit(s[1])) return s.substr(1);
|
|
return s;
|
|
};
|
|
return norm(op) == norm(target) || op == target;
|
|
}
|
|
|
|
CFGBuilder::CFGBuilder(Binary* binary, Disassembler* disasm, bool subsOnly)
|
|
: m_bin(binary), m_dis(disasm), m_subs_only(subsOnly) {}
|
|
|
|
bool CFGBuilder::isVisited(addr_t addr)
|
|
{
|
|
return m_blocks.count(addr) > 0;
|
|
}
|
|
|
|
void CFGBuilder::markVisited(addr_t addr)
|
|
{
|
|
m_blocks.insert(addr);
|
|
}
|
|
|
|
void CFGBuilder::buildGotMap()
|
|
{
|
|
auto imports = m_bin->getImportedFunctions();
|
|
for (const auto& imp : imports)
|
|
{
|
|
if (imp.address)
|
|
m_got[imp.address] = {imp.name, imp.library};
|
|
}
|
|
}
|
|
|
|
addr_t CFGBuilder::resolveGOT(const Instruction& inst)
|
|
{
|
|
if (!m_dis->isIndirectBranch(inst)) return 0;
|
|
int64_t disp = m_dis->getRIPDisp(inst);
|
|
if (disp == 0) return 0;
|
|
|
|
addr_t got = inst.address + inst.size + disp;
|
|
auto it = m_got.find(got);
|
|
if (it != m_got.end())
|
|
{
|
|
if (!it->second.first.empty()) return got;
|
|
}
|
|
|
|
auto bytes = m_bin->readBytes(got, 8);
|
|
if (bytes.size() < 4) return 0;
|
|
|
|
uint64_t target = 0;
|
|
if (bytes.size() >= 8)
|
|
{
|
|
target = bytes[0] | ((uint64_t)bytes[1] << 8) | ((uint64_t)bytes[2] << 16) |
|
|
((uint64_t)bytes[3] << 24) | ((uint64_t)bytes[4] << 32) |
|
|
((uint64_t)bytes[5] << 40) | ((uint64_t)bytes[6] << 48) |
|
|
((uint64_t)bytes[7] << 56);
|
|
}
|
|
else
|
|
{
|
|
target = bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24);
|
|
}
|
|
|
|
return target;
|
|
}
|
|
|
|
addr_t CFGBuilder::resolveJumpTable(const vector<Instruction>& block, const Instruction& inst)
|
|
{
|
|
size_t n = block.size();
|
|
if (n < 3) return 0;
|
|
|
|
int idx = -1;
|
|
for (int i = (int)n - 1; i >= 0; i--)
|
|
{
|
|
if (block[i].address == inst.address) { idx = i; break; }
|
|
}
|
|
if (idx < 2) return 0;
|
|
|
|
for (int i = idx - 1; i >= max(0, idx - 8); i--)
|
|
{
|
|
if (block[i].mnemonic == "lea")
|
|
{
|
|
int64_t disp = m_dis->getRIPDisp(block[i]);
|
|
if (disp == 0) continue;
|
|
addr_t table = block[i].address + block[i].size + disp;
|
|
|
|
int64_t bound = -1;
|
|
for (int j = i - 1; j >= max(0, i - 4); j--)
|
|
{
|
|
if (block[j].mnemonic == "cmp")
|
|
{
|
|
size_t pos = block[j].operands.find("0x");
|
|
if (pos != string::npos)
|
|
{
|
|
try { bound = stoll(block[j].operands.substr(pos), nullptr, 16); }
|
|
catch (...) {}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (bound <= 0) bound = 32;
|
|
|
|
for (int64_t e = 0; e < bound && e < 256; e++)
|
|
{
|
|
auto ent = m_bin->readBytes(table + e * 8, 8);
|
|
if (ent.size() < 4) break;
|
|
uint64_t entry = 0;
|
|
if (ent.size() >= 8)
|
|
entry = ent[0] | ((uint64_t)ent[1] << 8) | ((uint64_t)ent[2] << 16) |
|
|
((uint64_t)ent[3] << 24) | ((uint64_t)ent[4] << 32) |
|
|
((uint64_t)ent[5] << 40) | ((uint64_t)ent[6] << 48) | ((uint64_t)ent[7] << 56);
|
|
else
|
|
entry = ent[0] | (ent[1] << 8) | (ent[2] << 16) | (ent[3] << 24);
|
|
|
|
if (entry == 0 || entry > 0x100000000ULL) break;
|
|
auto ranges = m_bin->getExecutableRanges();
|
|
bool ok = false;
|
|
for (const auto& r : ranges)
|
|
if (entry >= r.first && entry < r.second) { ok = true; break; }
|
|
if (ok) return table;
|
|
else break;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
addr_t CFGBuilder::traceReg(const string& reg, const vector<Instruction>& block, size_t idx)
|
|
{
|
|
if (idx >= block.size() || idx == 0) return 0;
|
|
string cur = reg;
|
|
|
|
for (int i = (int)idx - 1; i >= 0; i--)
|
|
{
|
|
const auto& inst = block[i];
|
|
if (inst.mnemonic == "nop" || inst.mnemonic == "cmp" || inst.mnemonic == "test")
|
|
continue;
|
|
|
|
size_t comma = inst.operands.find(',');
|
|
string dst = comma != string::npos ? inst.operands.substr(0, comma) : inst.operands;
|
|
while (!dst.empty() && dst[0] == ' ') dst = dst.substr(1);
|
|
while (!dst.empty() && dst.back() == ' ') dst.pop_back();
|
|
|
|
if (inst.mnemonic == "mov" || inst.mnemonic == "lea")
|
|
{
|
|
if (dst == cur || dst == "r" + cur.substr(1) || regMatch(dst, cur))
|
|
{
|
|
if (inst.mnemonic == "mov" && !inst.operands.empty())
|
|
{
|
|
size_t pos = inst.operands.rfind("0x");
|
|
if (pos != string::npos)
|
|
{
|
|
try { return stoull(inst.operands.substr(pos), nullptr, 16); }
|
|
catch (...) {}
|
|
}
|
|
}
|
|
if (inst.mnemonic == "lea")
|
|
{
|
|
int64_t disp = m_dis->getRIPDisp(inst);
|
|
if (disp != 0) return inst.address + inst.size + disp;
|
|
}
|
|
if (comma != string::npos)
|
|
{
|
|
string src = inst.operands.substr(comma + 1);
|
|
while (!src.empty() && src[0] == ' ') src = src.substr(1);
|
|
while (!src.empty() && src.back() == ' ') src.pop_back();
|
|
if (!src.empty() && src[0] != '0' && !isMemOp(src))
|
|
{
|
|
cur = src;
|
|
continue;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
if (dst == cur || regMatch(dst, cur)) return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void CFGBuilder::recordIndirect(const Instruction& inst, addr_t target, bool ok, const string& name, const string& lib)
|
|
{
|
|
IndirectTarget it;
|
|
it.instruction_addr = inst.address;
|
|
it.resolved_target = ok ? target : 0;
|
|
it.resolved = ok;
|
|
it.name = name;
|
|
it.library = lib;
|
|
m_targets.push_back(it);
|
|
}
|
|
|
|
BasicBlock CFGBuilder::disassembleBlock(addr_t start, addr_t& next)
|
|
{
|
|
BasicBlock block;
|
|
block.address = start;
|
|
block.is_prolog = false;
|
|
block.is_epilog = false;
|
|
|
|
addr_t cur = start;
|
|
auto ranges = m_bin->getExecutableRanges();
|
|
|
|
auto inExec = [&](addr_t a) -> bool {
|
|
for (const auto& r : ranges)
|
|
if (a >= r.first && a < r.second) return true;
|
|
return false;
|
|
};
|
|
|
|
for (size_t i = 0; i < 5000; ++i)
|
|
{
|
|
if (!inExec(cur)) { next = 0; break; }
|
|
|
|
auto bytes = m_bin->readBytes(cur, 15);
|
|
if (bytes.empty()) { next = 0; break; }
|
|
|
|
auto insts = m_dis->disassemble(bytes, cur, 1);
|
|
if (insts.empty()) { next = 0; break; }
|
|
|
|
auto& inst = insts[0];
|
|
block.instructions.push_back(inst);
|
|
|
|
if (i <= 3 && !block.is_prolog && m_dis->isProlog(block.instructions))
|
|
block.is_prolog = true;
|
|
|
|
if (m_dis->isEpilog(inst))
|
|
block.is_epilog = true;
|
|
|
|
if (m_dis->isReturn(inst)) { next = 0; break; }
|
|
if (m_dis->isTrap(inst)) { next = 0; break; }
|
|
|
|
if (m_dis->isUnconditionalBranch(inst))
|
|
{
|
|
addr_t t = m_dis->getBranchTarget(inst);
|
|
if (t && !m_dis->isIndirectBranch(inst))
|
|
block.successors.push_back(t);
|
|
else if (m_dis->isIndirectBranch(inst))
|
|
{
|
|
addr_t resolved = resolveGOT(inst);
|
|
if (!resolved)
|
|
{
|
|
resolved = resolveJumpTable(block.instructions, inst);
|
|
if (resolved)
|
|
{
|
|
for (int j = (int)block.instructions.size() - 2; j >= 0; j--)
|
|
{
|
|
if (block.instructions[j].mnemonic == "lea")
|
|
{
|
|
int64_t ld = m_dis->getRIPDisp(block.instructions[j]);
|
|
if (ld != 0)
|
|
{
|
|
addr_t tbl = block.instructions[j].address + block.instructions[j].size + ld;
|
|
for (int64_t e = 0; e < 64; e++)
|
|
{
|
|
auto eb = m_bin->readBytes(tbl + e * 8, 8);
|
|
if (eb.size() < 4) break;
|
|
uint64_t entry = 0;
|
|
if (eb.size() >= 8)
|
|
entry = eb[0] | ((uint64_t)eb[1] << 8) | ((uint64_t)eb[2] << 16) |
|
|
((uint64_t)eb[3] << 24) | ((uint64_t)eb[4] << 32) |
|
|
((uint64_t)eb[5] << 40) | ((uint64_t)eb[6] << 48) | ((uint64_t)eb[7] << 56);
|
|
else
|
|
entry = eb[0] | (eb[1] << 8) | (eb[2] << 16) | (eb[3] << 24);
|
|
if (entry == 0 || entry > 0x100000000ULL) break;
|
|
bool ok = false;
|
|
for (const auto& r : ranges)
|
|
if (entry >= r.first && entry < r.second) { ok = true; break; }
|
|
if (ok) block.successors.push_back(entry);
|
|
else break;
|
|
}
|
|
resolved = tbl;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!resolved)
|
|
{
|
|
string reg = inst.operands;
|
|
while (!reg.empty() && reg[0] == ' ') reg = reg.substr(1);
|
|
resolved = traceReg(reg, block.instructions, block.instructions.size() - 1);
|
|
if (resolved)
|
|
{
|
|
block.successors.push_back(resolved);
|
|
recordIndirect(inst, resolved, true, "traced", "");
|
|
}
|
|
else
|
|
{
|
|
recordIndirect(inst, 0, false, "unresolved", "");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
auto git = m_got.find(resolved);
|
|
string nm = git != m_got.end() ? git->second.first : "";
|
|
string lb = git != m_got.end() ? git->second.second : "";
|
|
block.successors.push_back(resolved);
|
|
recordIndirect(inst, resolved, true, nm, lb);
|
|
}
|
|
}
|
|
next = 0; break;
|
|
}
|
|
|
|
if (m_dis->isConditionalBranch(inst))
|
|
{
|
|
addr_t t = m_dis->getBranchTarget(inst);
|
|
if (t && !m_dis->isIndirectBranch(inst))
|
|
block.successors.push_back(t);
|
|
block.successors.push_back(cur + inst.size);
|
|
next = cur + inst.size; break;
|
|
}
|
|
|
|
if (m_dis->isCall(inst))
|
|
{
|
|
addr_t t = m_dis->getBranchTarget(inst);
|
|
if (t && !m_dis->isIndirectBranch(inst))
|
|
{
|
|
m_funcs.insert(t);
|
|
recordIndirect(inst, t, true, "", "");
|
|
}
|
|
else if (m_dis->isIndirectBranch(inst))
|
|
{
|
|
addr_t resolved = resolveGOT(inst);
|
|
if (resolved)
|
|
{
|
|
auto git = m_got.find(resolved);
|
|
string nm = git != m_got.end() ? git->second.first : "";
|
|
string lb = git != m_got.end() ? git->second.second : "";
|
|
auto gb = m_bin->readBytes(resolved, 8);
|
|
if (gb.size() >= 8)
|
|
{
|
|
uint64_t target = gb[0] | ((uint64_t)gb[1] << 8) |
|
|
((uint64_t)gb[2] << 16) | ((uint64_t)gb[3] << 24) |
|
|
((uint64_t)gb[4] << 32) | ((uint64_t)gb[5] << 40) |
|
|
((uint64_t)gb[6] << 48) | ((uint64_t)gb[7] << 56);
|
|
if (target && target < 0x100000000ULL) resolved = target;
|
|
}
|
|
m_funcs.insert(resolved);
|
|
recordIndirect(inst, resolved, true, nm, lb);
|
|
}
|
|
else
|
|
{
|
|
string reg = inst.operands;
|
|
while (!reg.empty() && reg[0] == ' ') reg = reg.substr(1);
|
|
resolved = traceReg(reg, block.instructions, block.instructions.size() - 1);
|
|
if (resolved)
|
|
{
|
|
m_funcs.insert(resolved);
|
|
recordIndirect(inst, resolved, true, "traced", "");
|
|
}
|
|
else
|
|
{
|
|
recordIndirect(inst, 0, false, "unresolved", "");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
cur += inst.size;
|
|
next = cur;
|
|
}
|
|
|
|
return block;
|
|
}
|
|
|
|
set<addr_t> CFGBuilder::findPrologs()
|
|
{
|
|
set<addr_t> cand;
|
|
auto ranges = m_bin->getExecutableRanges();
|
|
|
|
for (const auto& r : ranges)
|
|
{
|
|
addr_t a = r.first;
|
|
while (a < r.second)
|
|
{
|
|
auto bytes = m_bin->readBytes(a, 15);
|
|
if (bytes.empty()) { a += 1; continue; }
|
|
auto insts = m_dis->disassemble(bytes, a, 1);
|
|
if (insts.empty()) { a += 1; continue; }
|
|
auto& inst = insts[0];
|
|
|
|
if (inst.mnemonic == "endbr64")
|
|
{
|
|
auto nb = m_bin->readBytes(a + inst.size, 15);
|
|
auto ni = m_dis->disassemble(nb, a + inst.size, 2);
|
|
if (!ni.empty() && m_dis->isProlog(ni)) cand.insert(a);
|
|
}
|
|
else
|
|
{
|
|
vector<Instruction> tmp{inst};
|
|
if (m_dis->isProlog(tmp)) cand.insert(a);
|
|
}
|
|
|
|
a += inst.size;
|
|
}
|
|
}
|
|
|
|
return cand;
|
|
}
|
|
|
|
Function CFGBuilder::buildFunction(addr_t start, const string& name)
|
|
{
|
|
Function func;
|
|
func.address = start;
|
|
func.name = name;
|
|
|
|
queue<addr_t> q;
|
|
q.push(start);
|
|
|
|
while (!q.empty())
|
|
{
|
|
addr_t a = q.front(); q.pop();
|
|
if (isVisited(a)) continue;
|
|
markVisited(a);
|
|
|
|
addr_t nxt = a;
|
|
auto block = disassembleBlock(a, nxt);
|
|
for (auto s : block.successors)
|
|
if (!isVisited(s)) q.push(s);
|
|
|
|
func.blocks.push_back(block);
|
|
}
|
|
|
|
return func;
|
|
}
|
|
|
|
CFG CFGBuilder::build()
|
|
{
|
|
CFG cfg;
|
|
cfg.binary_path = m_bin->getPath();
|
|
cfg.entry_point = m_bin->getEntryPoint();
|
|
|
|
switch (m_bin->getArch())
|
|
{
|
|
case Arch::X86: cfg.arch_str = "x86"; break;
|
|
case Arch::X64: cfg.arch_str = "x86-64"; break;
|
|
default: cfg.arch_str = "unknown";
|
|
}
|
|
switch (m_bin->getFormat())
|
|
{
|
|
case BinaryFormat::PE: cfg.format_str = "PE"; break;
|
|
case BinaryFormat::ELF: cfg.format_str = "ELF"; break;
|
|
default: cfg.format_str = "unknown";
|
|
}
|
|
|
|
cfg.imports = m_bin->getImportedFunctions();
|
|
buildGotMap();
|
|
|
|
m_prologs = findPrologs();
|
|
|
|
set<addr_t> built;
|
|
queue<addr_t> pending;
|
|
|
|
auto isEmpty = [](const Function& f) -> bool {
|
|
for (const auto& b : f.blocks)
|
|
if (!b.instructions.empty()) return false;
|
|
return true;
|
|
};
|
|
|
|
auto process = [&](addr_t addr, const string& name)
|
|
{
|
|
if (built.count(addr)) return;
|
|
built.insert(addr);
|
|
|
|
auto func = buildFunction(addr, name);
|
|
if (isEmpty(func)) return;
|
|
cfg.functions.push_back(func);
|
|
|
|
for (const auto& b : func.blocks)
|
|
for (const auto& inst : b.instructions)
|
|
if (m_dis->isCall(inst))
|
|
{
|
|
addr_t t = m_dis->getBranchTarget(inst);
|
|
if (t && !m_dis->isIndirectBranch(inst))
|
|
m_funcs.insert(t);
|
|
}
|
|
|
|
for (auto d : m_funcs)
|
|
if (!built.count(d)) pending.push(d);
|
|
};
|
|
|
|
{
|
|
addr_t ep = m_bin->getEntryPoint();
|
|
string ep_name = "entry";
|
|
for (const auto& exp : m_bin->getExportedFunctions())
|
|
if (exp.first == ep) { ep_name = exp.second; break; }
|
|
process(ep, ep_name);
|
|
}
|
|
|
|
if (!m_subs_only)
|
|
{
|
|
for (const auto& exp : m_bin->getExportedFunctions())
|
|
pending.push(exp.first);
|
|
}
|
|
|
|
map<addr_t, string> imp_map;
|
|
for (const auto& imp : m_bin->getImportedFunctions())
|
|
imp_map[imp.address] = imp.name;
|
|
|
|
while (!pending.empty())
|
|
{
|
|
addr_t a = pending.front(); pending.pop();
|
|
if (built.count(a)) continue;
|
|
|
|
string name;
|
|
for (const auto& exp : m_bin->getExportedFunctions())
|
|
if (exp.first == a) { name = exp.second; break; }
|
|
if (name.empty())
|
|
{
|
|
auto it = imp_map.find(a);
|
|
if (it != imp_map.end()) name = it->second;
|
|
}
|
|
|
|
built.insert(a);
|
|
auto func = buildFunction(a, name);
|
|
if (isEmpty(func)) continue;
|
|
cfg.functions.push_back(func);
|
|
|
|
for (const auto& b : func.blocks)
|
|
for (const auto& inst : b.instructions)
|
|
if (m_dis->isCall(inst))
|
|
{
|
|
addr_t t = m_dis->getBranchTarget(inst);
|
|
if (t && !m_dis->isIndirectBranch(inst))
|
|
if (!built.count(t)) pending.push(t);
|
|
}
|
|
|
|
if (!m_subs_only)
|
|
{
|
|
for (auto p : m_prologs)
|
|
if (!built.count(p)) pending.push(p);
|
|
}
|
|
}
|
|
|
|
cfg.indirect_targets = m_targets;
|
|
return cfg;
|
|
}
|
|
|
|
static int64_t stackDelta(const Instruction& inst)
|
|
{
|
|
if (inst.mnemonic == "push") return -8;
|
|
if (inst.mnemonic == "pop") return 8;
|
|
if (inst.mnemonic == "call") return 0;
|
|
if (inst.mnemonic == "ret" || inst.mnemonic == "retf") return 8;
|
|
if (inst.mnemonic == "sub")
|
|
{
|
|
size_t p = inst.operands.find("rsp");
|
|
if (p == string::npos) p = inst.operands.find("esp");
|
|
if (p != string::npos && (p == 0 || inst.operands[p-1] == ' ' || inst.operands[p-1] == ','))
|
|
{
|
|
size_t x = inst.operands.rfind("0x");
|
|
if (x != string::npos && x > p)
|
|
{
|
|
try { return -(int64_t)stoull(inst.operands.substr(x), nullptr, 16); }
|
|
catch (...) {}
|
|
}
|
|
}
|
|
}
|
|
if (inst.mnemonic == "add")
|
|
{
|
|
size_t p = inst.operands.find("rsp");
|
|
if (p == string::npos) p = inst.operands.find("esp");
|
|
if (p != string::npos && (p == 0 || inst.operands[p-1] == ' ' || inst.operands[p-1] == ','))
|
|
{
|
|
size_t x = inst.operands.rfind("0x");
|
|
if (x != string::npos && x > p)
|
|
{
|
|
try { return (int64_t)stoull(inst.operands.substr(x), nullptr, 16); }
|
|
catch (...) {}
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void computeStackDeltas(CFG& cfg)
|
|
{
|
|
for (auto& func : cfg.functions)
|
|
{
|
|
for (auto& block : func.blocks)
|
|
{
|
|
int64_t delta = 0;
|
|
for (auto& inst : block.instructions)
|
|
{
|
|
inst.stack_offset = delta;
|
|
delta += stackDelta(inst);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void jumpThread(CFG& cfg)
|
|
{
|
|
map<addr_t, BasicBlock*> addr_to_block;
|
|
map<addr_t, Function*> addr_to_func;
|
|
for (auto& func : cfg.functions)
|
|
{
|
|
for (auto& block : func.blocks)
|
|
{
|
|
addr_to_block[block.address] = █
|
|
addr_to_func[block.address] = &func;
|
|
}
|
|
}
|
|
|
|
for (auto& func : cfg.functions)
|
|
{
|
|
bool changed = true;
|
|
while (changed)
|
|
{
|
|
changed = false;
|
|
for (auto& block : func.blocks)
|
|
{
|
|
if (block.successors.size() != 1) continue;
|
|
addr_t t = block.successors[0];
|
|
auto it = addr_to_block.find(t);
|
|
if (it == addr_to_block.end()) continue;
|
|
if (addr_to_func[t] != &func) continue;
|
|
|
|
BasicBlock* target = it->second;
|
|
if (!target->instructions.empty() &&
|
|
target->instructions.back().mnemonic == "jmp" &&
|
|
target->successors.size() == 1)
|
|
{
|
|
addr_t redirect = target->successors[0];
|
|
if (redirect != block.address && redirect != t)
|
|
{
|
|
block.successors[0] = redirect;
|
|
changed = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void pruneDeadBlocks(CFG& cfg)
|
|
{
|
|
for (auto& func : cfg.functions)
|
|
{
|
|
if (func.blocks.empty()) continue;
|
|
set<addr_t> reachable;
|
|
queue<addr_t> q;
|
|
reachable.insert(func.blocks[0].address);
|
|
q.push(func.blocks[0].address);
|
|
|
|
map<addr_t, BasicBlock*> addr_map;
|
|
for (auto& b : func.blocks) addr_map[b.address] = &b;
|
|
|
|
while (!q.empty())
|
|
{
|
|
addr_t cur = q.front(); q.pop();
|
|
auto it = addr_map.find(cur);
|
|
if (it == addr_map.end()) continue;
|
|
for (auto s : it->second->successors)
|
|
if (reachable.insert(s).second) q.push(s);
|
|
}
|
|
|
|
vector<BasicBlock> alive;
|
|
for (auto& b : func.blocks)
|
|
if (reachable.count(b.address))
|
|
alive.push_back(move(b));
|
|
func.blocks = move(alive);
|
|
}
|
|
}
|
|
|
|
static void computeXrefs(CFG& cfg)
|
|
{
|
|
map<addr_t, vector<pair<addr_t, string>>> xmap;
|
|
for (const auto& func : cfg.functions)
|
|
{
|
|
for (const auto& block : func.blocks)
|
|
{
|
|
for (const auto& inst : block.instructions)
|
|
{
|
|
if (inst.mnemonic == "call" || inst.mnemonic == "jmp" ||
|
|
(inst.mnemonic.size() == 2 && inst.mnemonic[0] == 'j' && inst.mnemonic != "jn"))
|
|
{
|
|
size_t pos = inst.operands.find("0x");
|
|
if (pos != string::npos)
|
|
{
|
|
try
|
|
{
|
|
addr_t target = stoull(inst.operands.substr(pos), nullptr, 16);
|
|
string type = inst.mnemonic;
|
|
xmap[target].push_back({inst.address, type});
|
|
}
|
|
catch (...) {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (auto& kv : xmap)
|
|
{
|
|
XrefEntry xe;
|
|
xe.target = kv.first;
|
|
xe.callers = move(kv.second);
|
|
cfg.xrefs.push_back(move(xe));
|
|
}
|
|
}
|
|
|
|
void CFGBuilder::clean(CFG& cfg)
|
|
{
|
|
jumpThread(cfg);
|
|
pruneDeadBlocks(cfg);
|
|
computeStackDeltas(cfg);
|
|
cfg.has_stack_offsets = true;
|
|
computeXrefs(cfg);
|
|
}
|