mirror of
https://gitlab.com/BinaryHardening/cfgrip
synced 2026-07-26 12:41:08 +00:00
cfgrip: standalone CFG extraction tool from PE/ELF binaries
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
#include "loader/binary.hpp"
|
||||
#include "loader/elf.hpp"
|
||||
#include "loader/pe.hpp"
|
||||
#include <memory>
|
||||
|
||||
unique_ptr<Binary> Binary::create(const string& path)
|
||||
{
|
||||
auto elf = make_unique<ELFLoader>();
|
||||
if (elf->load(path)) return elf;
|
||||
|
||||
auto pe = make_unique<PELoader>();
|
||||
if (pe->load(path)) return pe;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
#include "types.hpp"
|
||||
|
||||
|
||||
|
||||
enum class BinaryFormat
|
||||
{
|
||||
PE,
|
||||
ELF,
|
||||
UNKNOWN
|
||||
};
|
||||
|
||||
enum class Arch
|
||||
{
|
||||
X86,
|
||||
X64,
|
||||
UNKNOWN
|
||||
};
|
||||
|
||||
class Binary
|
||||
{
|
||||
public:
|
||||
virtual ~Binary() = default;
|
||||
virtual bool load(const string& path) = 0;
|
||||
virtual BinaryFormat getFormat() const = 0;
|
||||
virtual Arch getArch() const = 0;
|
||||
virtual addr_t getEntryPoint() const = 0;
|
||||
virtual addr_t getImageBase() const = 0;
|
||||
virtual vector<uint8_t> readBytes(addr_t vaddr, size_t size) const = 0;
|
||||
virtual vector<pair<addr_t, addr_t>> getExecutableRanges() const = 0;
|
||||
virtual vector<pair<addr_t, string>> getExportedFunctions() const = 0;
|
||||
virtual vector<ImportEntry> getImportedFunctions() const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
virtual const string& getPath() const = 0;
|
||||
|
||||
static unique_ptr<Binary> create(const string& path);
|
||||
};
|
||||
+332
@@ -0,0 +1,332 @@
|
||||
#include "loader/elf.hpp"
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
|
||||
static const uint64_t EI_NIDENT = 16;
|
||||
static const uint32_t PT_LOAD = 1;
|
||||
static const uint32_t PT_DYNAMIC = 2;
|
||||
static const uint32_t DT_NULL = 0;
|
||||
static const uint32_t DT_NEEDED = 1;
|
||||
static const uint32_t DT_INIT = 12;
|
||||
static const uint32_t DT_FINI = 13;
|
||||
static const uint32_t DT_INIT_ARRAY = 25;
|
||||
static const uint32_t DT_FINI_ARRAY = 26;
|
||||
static const uint32_t DT_INIT_ARRAYSZ = 27;
|
||||
static const uint32_t DT_FINI_ARRAYSZ = 28;
|
||||
static const uint32_t DT_STRTAB = 5;
|
||||
static const uint32_t DT_SYMTAB = 6;
|
||||
static const uint32_t DT_STRSZ = 10;
|
||||
static const uint32_t SHT_SYMTAB = 2;
|
||||
static const uint32_t SHT_DYNSYM = 11;
|
||||
|
||||
ELFLoader::ELFLoader()
|
||||
: m_arch(Arch::X64), m_entry(0),
|
||||
m_init_array(0), m_init_array_size(0),
|
||||
m_fini_array(0), m_fini_array_size(0),
|
||||
m_init(0), m_fini(0) {}
|
||||
|
||||
ELFLoader::~ELFLoader() {}
|
||||
|
||||
bool ELFLoader::load(const string& path)
|
||||
{
|
||||
m_path = path;
|
||||
ifstream f(path, ios::binary);
|
||||
if (!f) return false;
|
||||
|
||||
f.seekg(0, ios::end);
|
||||
size_t sz = f.tellg();
|
||||
f.seekg(0, ios::beg);
|
||||
|
||||
m_data.resize(sz);
|
||||
f.read((char*)m_data.data(), sz);
|
||||
f.close();
|
||||
|
||||
if (m_data.size() < EI_NIDENT) return false;
|
||||
if (m_data[0] != 0x7F || m_data[1] != 'E' || m_data[2] != 'L' || m_data[3] != 'F')
|
||||
return false;
|
||||
|
||||
return parseHeader();
|
||||
}
|
||||
|
||||
static uint16_t r16(const vector<uint8_t>& d, size_t off)
|
||||
{
|
||||
if (off + 2 > d.size()) return 0;
|
||||
return d[off] | (d[off+1] << 8);
|
||||
}
|
||||
|
||||
static uint32_t r32(const vector<uint8_t>& d, size_t off)
|
||||
{
|
||||
if (off + 4 > d.size()) return 0;
|
||||
return d[off] | (d[off+1] << 8) | (d[off+2] << 16) | (d[off+3] << 24);
|
||||
}
|
||||
|
||||
static uint64_t r64(const vector<uint8_t>& d, size_t off)
|
||||
{
|
||||
if (off + 8 > d.size()) return 0;
|
||||
return (uint64_t)d[off] | ((uint64_t)d[off+1] << 8) |
|
||||
((uint64_t)d[off+2] << 16) | ((uint64_t)d[off+3] << 24) |
|
||||
((uint64_t)d[off+4] << 32) | ((uint64_t)d[off+5] << 40) |
|
||||
((uint64_t)d[off+6] << 48) | ((uint64_t)d[off+7] << 56);
|
||||
}
|
||||
|
||||
struct Elf64_Ehdr
|
||||
{
|
||||
uint8_t ident[16];
|
||||
uint16_t type;
|
||||
uint16_t machine;
|
||||
uint32_t version;
|
||||
uint64_t entry;
|
||||
uint64_t phoff;
|
||||
uint64_t shoff;
|
||||
uint32_t flags;
|
||||
uint16_t ehsize;
|
||||
uint16_t phentsize;
|
||||
uint16_t phnum;
|
||||
uint16_t shentsize;
|
||||
uint16_t shnum;
|
||||
uint16_t shstrndx;
|
||||
};
|
||||
|
||||
struct Elf64_Phdr
|
||||
{
|
||||
uint32_t type;
|
||||
uint32_t flags;
|
||||
uint64_t offset;
|
||||
uint64_t vaddr;
|
||||
uint64_t paddr;
|
||||
uint64_t filesz;
|
||||
uint64_t memsz;
|
||||
uint64_t align;
|
||||
};
|
||||
|
||||
struct Elf64_Shdr
|
||||
{
|
||||
uint32_t name;
|
||||
uint32_t type;
|
||||
uint64_t flags;
|
||||
uint64_t addr;
|
||||
uint64_t offset;
|
||||
uint64_t size;
|
||||
uint32_t link;
|
||||
uint32_t info;
|
||||
uint64_t addralign;
|
||||
uint64_t entsize;
|
||||
};
|
||||
|
||||
struct Elf64_Dyn
|
||||
{
|
||||
int64_t d_tag;
|
||||
uint64_t d_val;
|
||||
};
|
||||
|
||||
bool ELFLoader::parseHeader()
|
||||
{
|
||||
if (m_data.size() < 64) return false;
|
||||
|
||||
uint8_t cls = m_data[4];
|
||||
bool is_64 = (cls == 2);
|
||||
|
||||
if (is_64)
|
||||
{
|
||||
m_arch = Arch::X64;
|
||||
if (m_data.size() < 64) return false;
|
||||
uint16_t type = r16(m_data, 16);
|
||||
m_entry = r64(m_data, 24);
|
||||
uint64_t phoff = r64(m_data, 32);
|
||||
uint64_t shoff = r64(m_data, 40);
|
||||
uint16_t phnum = r16(m_data, 56);
|
||||
uint16_t shnum = r16(m_data, 60);
|
||||
uint16_t shstrndx = r16(m_data, 62);
|
||||
|
||||
for (uint16_t i = 0; i < phnum; i++)
|
||||
{
|
||||
size_t off = phoff + i * 56;
|
||||
if (off + 56 > m_data.size()) break;
|
||||
uint32_t ptype = r32(m_data, off);
|
||||
uint32_t pflags = r32(m_data, off + 4);
|
||||
uint64_t poffset = r64(m_data, off + 8);
|
||||
uint64_t pvaddr = r64(m_data, off + 16);
|
||||
uint64_t pfilesz = r64(m_data, off + 32);
|
||||
uint64_t pmemsz = r64(m_data, off + 40);
|
||||
|
||||
if (ptype == PT_LOAD && (pflags & 1))
|
||||
{
|
||||
m_exec_ranges.push_back({pvaddr, pvaddr + pmemsz});
|
||||
}
|
||||
|
||||
if (ptype == PT_DYNAMIC)
|
||||
{
|
||||
addr_t dyn_va = pvaddr;
|
||||
size_t dyn_sz = pmemsz;
|
||||
size_t dyn_off = poffset;
|
||||
|
||||
size_t end = dyn_off + dyn_sz;
|
||||
for (size_t j = dyn_off; j + 16 <= end; j += 16)
|
||||
{
|
||||
int64_t tag = (int64_t)r64(m_data, j);
|
||||
uint64_t val = r64(m_data, j + 8);
|
||||
|
||||
if (tag == DT_NULL) break;
|
||||
if (tag == DT_NEEDED && val)
|
||||
{
|
||||
uint64_t strtab = 0;
|
||||
for (size_t k = dyn_off; k + 16 <= end; k += 16)
|
||||
{
|
||||
int64_t t2 = (int64_t)r64(m_data, k);
|
||||
uint64_t v2 = r64(m_data, k + 8);
|
||||
if (t2 == DT_STRTAB) { strtab = v2; break; }
|
||||
}
|
||||
if (strtab)
|
||||
{
|
||||
size_t name_off = strtab + val;
|
||||
if (name_off < m_data.size())
|
||||
{
|
||||
string lib;
|
||||
for (size_t c = name_off; c < m_data.size() && m_data[c]; c++)
|
||||
lib += (char)m_data[c];
|
||||
ImportEntry e;
|
||||
e.address = 0;
|
||||
e.name = lib;
|
||||
e.library = lib;
|
||||
m_imports.push_back(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tag == DT_INIT) m_init = val;
|
||||
if (tag == DT_FINI) m_fini = val;
|
||||
if (tag == DT_INIT_ARRAY) m_init_array = val;
|
||||
if (tag == DT_INIT_ARRAYSZ) m_init_array_size = val;
|
||||
if (tag == DT_FINI_ARRAY) m_fini_array = val;
|
||||
if (tag == DT_FINI_ARRAYSZ) m_fini_array_size = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < shnum; i++)
|
||||
{
|
||||
size_t off = shoff + i * 64;
|
||||
if (off + 64 > m_data.size()) break;
|
||||
uint32_t stype = r32(m_data, off + 4);
|
||||
|
||||
if (stype == SHT_DYNSYM || stype == SHT_SYMTAB)
|
||||
{
|
||||
uint64_t sym_off = r64(m_data, off + 24);
|
||||
uint64_t sym_sz = r64(m_data, off + 32);
|
||||
uint32_t link = r32(m_data, off + 40);
|
||||
uint64_t entsize = r64(m_data, off + 56);
|
||||
if (entsize == 0) entsize = 24;
|
||||
|
||||
uint64_t strtab_off = 0;
|
||||
uint64_t strtab_sz = 0;
|
||||
for (uint16_t k = 0; k < shnum; k++)
|
||||
{
|
||||
size_t soff = shoff + k * 64;
|
||||
if (soff + 64 > m_data.size()) break;
|
||||
if (r32(m_data, soff + 4) == SHT_SYMTAB || r32(m_data, soff + 4) == SHT_DYNSYM)
|
||||
continue;
|
||||
if ((uint32_t)k == link)
|
||||
{
|
||||
strtab_off = r64(m_data, soff + 24);
|
||||
strtab_sz = r64(m_data, soff + 32);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
size_t nsym = sym_sz / entsize;
|
||||
for (size_t j = 0; j < nsym; j++)
|
||||
{
|
||||
size_t sym_ent = sym_off + j * entsize;
|
||||
if (sym_ent + 16 > m_data.size()) break;
|
||||
uint32_t sym_name_off = r32(m_data, sym_ent);
|
||||
uint8_t sym_info = m_data[sym_ent + 4];
|
||||
uint64_t sym_val = r64(m_data, sym_ent + 8);
|
||||
|
||||
uint8_t sym_type = sym_info & 0xF;
|
||||
uint8_t sym_bind = sym_info >> 4;
|
||||
|
||||
if (sym_val && sym_type == 2 && sym_name_off && strtab_off)
|
||||
{
|
||||
size_t name_pos = strtab_off + sym_name_off;
|
||||
if (name_pos < m_data.size())
|
||||
{
|
||||
string sym_name;
|
||||
for (size_t c = name_pos; c < m_data.size() && m_data[c]; c++)
|
||||
sym_name += (char)m_data[c];
|
||||
if (!sym_name.empty())
|
||||
{
|
||||
m_symbols[sym_val] = sym_name;
|
||||
if (sym_bind == 0) m_exports.push_back({sym_val, sym_name});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (stype == SHT_DYNSYM) break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_arch = Arch::X86;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ELFLoader::parseDynamic()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ELFLoader::parseSections()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ELFLoader::parseSymbols()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
vector<uint8_t> ELFLoader::readBytes(addr_t vaddr, size_t size) const
|
||||
{
|
||||
vector<uint8_t> result;
|
||||
for (const auto& r : m_exec_ranges)
|
||||
{
|
||||
if (vaddr >= r.first && vaddr < r.second)
|
||||
{
|
||||
addr_t offset = vaddr;
|
||||
for (const auto& r2 : m_exec_ranges)
|
||||
{
|
||||
if (offset >= r2.first && offset < r2.second)
|
||||
{
|
||||
addr_t file_off = 0;
|
||||
size_t max_read = min(size, (size_t)(r2.second - offset));
|
||||
size_t copy_start = offset - r2.first;
|
||||
if (copy_start < m_data.size())
|
||||
{
|
||||
size_t to_copy = min(max_read, m_data.size() - copy_start);
|
||||
result.assign(m_data.begin() + copy_start, m_data.begin() + copy_start + to_copy);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
vector<pair<addr_t, addr_t>> ELFLoader::getExecutableRanges() const
|
||||
{
|
||||
return m_exec_ranges;
|
||||
}
|
||||
|
||||
vector<pair<addr_t, string>> ELFLoader::getExportedFunctions() const
|
||||
{
|
||||
return m_exports;
|
||||
}
|
||||
|
||||
vector<ImportEntry> ELFLoader::getImportedFunctions() const
|
||||
{
|
||||
return m_imports;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
#include "loader/binary.hpp"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
class ELFLoader : public Binary
|
||||
{
|
||||
public:
|
||||
ELFLoader();
|
||||
~ELFLoader() override;
|
||||
|
||||
bool load(const string& path) override;
|
||||
BinaryFormat getFormat() const override { return BinaryFormat::ELF; }
|
||||
Arch getArch() const override { return m_arch; }
|
||||
addr_t getEntryPoint() const override { return m_entry; }
|
||||
addr_t getImageBase() const override { return 0; }
|
||||
vector<uint8_t> readBytes(addr_t vaddr, size_t size) const override;
|
||||
vector<pair<addr_t, addr_t>> getExecutableRanges() const override;
|
||||
vector<pair<addr_t, string>> getExportedFunctions() const override;
|
||||
vector<ImportEntry> getImportedFunctions() const override;
|
||||
const string& getPath() const override { return m_path; }
|
||||
|
||||
addr_t getInitArray() const { return m_init_array; }
|
||||
addr_t getFiniArray() const { return m_fini_array; }
|
||||
size_t getInitArraySize() const { return m_init_array_size; }
|
||||
size_t getFiniArraySize() const { return m_fini_array_size; }
|
||||
addr_t getInit() const { return m_init; }
|
||||
addr_t getFini() const { return m_fini; }
|
||||
|
||||
private:
|
||||
bool parseHeader();
|
||||
bool parseDynamic();
|
||||
bool parseSections();
|
||||
bool parseSymbols();
|
||||
|
||||
string m_path;
|
||||
Arch m_arch;
|
||||
addr_t m_entry;
|
||||
vector<uint8_t> m_data;
|
||||
vector<pair<addr_t, addr_t>> m_exec_ranges;
|
||||
vector<pair<addr_t, string>> m_exports;
|
||||
vector<ImportEntry> m_imports;
|
||||
map<addr_t, string> m_symbols;
|
||||
|
||||
addr_t m_init_array;
|
||||
size_t m_init_array_size;
|
||||
addr_t m_fini_array;
|
||||
size_t m_fini_array_size;
|
||||
addr_t m_init;
|
||||
addr_t m_fini;
|
||||
};
|
||||
+276
@@ -0,0 +1,276 @@
|
||||
#include "loader/pe.hpp"
|
||||
#include <fstream>
|
||||
#include <cstring>
|
||||
|
||||
PELoader::PELoader() : m_arch(Arch::X64), m_entry(0), m_image_base(0) {}
|
||||
PELoader::~PELoader() {}
|
||||
|
||||
bool PELoader::load(const string& path)
|
||||
{
|
||||
m_path = path;
|
||||
ifstream f(path, ios::binary);
|
||||
if (!f) return false;
|
||||
|
||||
f.seekg(0, ios::end);
|
||||
size_t sz = f.tellg();
|
||||
f.seekg(0, ios::beg);
|
||||
|
||||
m_data.resize(sz);
|
||||
f.read((char*)m_data.data(), sz);
|
||||
f.close();
|
||||
|
||||
return parseHeaders();
|
||||
}
|
||||
|
||||
static uint16_t r16(const vector<uint8_t>& d, size_t off)
|
||||
{
|
||||
if (off + 2 > d.size()) return 0;
|
||||
return d[off] | (d[off+1] << 8);
|
||||
}
|
||||
|
||||
static uint32_t r32(const vector<uint8_t>& d, size_t off)
|
||||
{
|
||||
if (off + 4 > d.size()) return 0;
|
||||
return d[off] | (d[off+1] << 8) | (d[off+2] << 16) | (d[off+3] << 24);
|
||||
}
|
||||
|
||||
static uint64_t r64(const vector<uint8_t>& d, size_t off)
|
||||
{
|
||||
if (off + 8 > d.size()) return 0;
|
||||
return (uint64_t)d[off] | ((uint64_t)d[off+1] << 8) |
|
||||
((uint64_t)d[off+2] << 16) | ((uint64_t)d[off+3] << 24) |
|
||||
((uint64_t)d[off+4] << 32) | ((uint64_t)d[off+5] << 40) |
|
||||
((uint64_t)d[off+6] << 48) | ((uint64_t)d[off+7] << 56);
|
||||
}
|
||||
|
||||
bool PELoader::parseHeaders()
|
||||
{
|
||||
if (m_data.size() < 64) return false;
|
||||
if (r16(m_data, 0) != 0x5A4D) return false;
|
||||
|
||||
uint32_t pe_off = r32(m_data, 0x3C);
|
||||
if (pe_off + 4 > m_data.size()) return false;
|
||||
if (r32(m_data, pe_off) != 0x00004550) return false;
|
||||
|
||||
uint16_t machine = r16(m_data, pe_off + 4);
|
||||
uint16_t num_sections = r16(m_data, pe_off + 6);
|
||||
|
||||
if (machine == 0x8664) m_arch = Arch::X64;
|
||||
else if (machine == 0x14C) m_arch = Arch::X86;
|
||||
else return false;
|
||||
|
||||
uint16_t opt_hdr_size = r16(m_data, pe_off + 20);
|
||||
uint32_t opt_hdr_off = pe_off + 24;
|
||||
|
||||
if (opt_hdr_off + 2 > m_data.size()) return false;
|
||||
uint16_t magic = r16(m_data, opt_hdr_off);
|
||||
|
||||
if (magic == 0x10B)
|
||||
{
|
||||
m_arch = Arch::X86;
|
||||
m_image_base = r32(m_data, opt_hdr_off + 28);
|
||||
m_entry = r32(m_data, opt_hdr_off + 16) + m_image_base;
|
||||
}
|
||||
else if (magic == 0x20B)
|
||||
{
|
||||
m_arch = Arch::X64;
|
||||
m_image_base = r64(m_data, opt_hdr_off + 24);
|
||||
m_entry = r32(m_data, opt_hdr_off + 16) + m_image_base;
|
||||
}
|
||||
else return false;
|
||||
|
||||
uint16_t section_hdr_size = 40;
|
||||
uint32_t sections_off = opt_hdr_off + opt_hdr_size;
|
||||
|
||||
struct Section
|
||||
{
|
||||
string name;
|
||||
addr_t vaddr;
|
||||
uint32_t vsize;
|
||||
uint32_t raw_ptr;
|
||||
uint32_t raw_size;
|
||||
uint32_t characteristics;
|
||||
};
|
||||
vector<Section> sections;
|
||||
|
||||
for (uint16_t i = 0; i < num_sections; i++)
|
||||
{
|
||||
uint32_t off = sections_off + i * section_hdr_size;
|
||||
if (off + section_hdr_size > m_data.size()) break;
|
||||
|
||||
char name_buf[9] = {};
|
||||
memcpy(name_buf, &m_data[off], 8);
|
||||
Section s;
|
||||
s.name = name_buf;
|
||||
s.vsize = r32(m_data, off + 8);
|
||||
s.vaddr = r32(m_data, off + 12) + m_image_base;
|
||||
s.raw_size = r32(m_data, off + 16);
|
||||
s.raw_ptr = r32(m_data, off + 20);
|
||||
s.characteristics = r32(m_data, off + 36);
|
||||
sections.push_back(s);
|
||||
}
|
||||
|
||||
for (const auto& s : sections)
|
||||
{
|
||||
if (s.characteristics & 0x20000000)
|
||||
{
|
||||
addr_t start = s.vaddr;
|
||||
addr_t end = start + (s.vsize ? s.vsize : s.raw_size);
|
||||
m_exec_ranges.push_back({start, end});
|
||||
}
|
||||
}
|
||||
|
||||
bool is_64 = (m_arch == Arch::X64);
|
||||
uint32_t import_rva = 0;
|
||||
uint32_t data_dir_off = (magic == 0x10B) ? opt_hdr_off + 96 : opt_hdr_off + 112;
|
||||
import_rva = r32(m_data, data_dir_off + 8);
|
||||
|
||||
auto rvaToOffset = [&](uint32_t rva) -> uint32_t {
|
||||
for (const auto& s : sections)
|
||||
{
|
||||
addr_t va = rva + m_image_base;
|
||||
if (va >= s.vaddr && va < s.vaddr + (s.vsize ? s.vsize : s.raw_size))
|
||||
return s.raw_ptr + static_cast<uint32_t>(va - s.vaddr);
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
auto readString = [&](uint32_t off) -> string {
|
||||
string s;
|
||||
for (size_t c = off; c < m_data.size() && m_data[c]; c++)
|
||||
s += (char)m_data[c];
|
||||
return s;
|
||||
};
|
||||
|
||||
if (import_rva)
|
||||
{
|
||||
uint32_t desc_off = rvaToOffset(import_rva);
|
||||
|
||||
for (uint32_t i = 0; ; i++)
|
||||
{
|
||||
uint32_t desc = desc_off + i * 20;
|
||||
if (desc + 20 > m_data.size()) break;
|
||||
|
||||
uint32_t oft_rva = r32(m_data, desc);
|
||||
if (oft_rva == 0) break;
|
||||
|
||||
uint32_t name_rva = r32(m_data, desc + 12);
|
||||
string dll_name;
|
||||
if (name_rva)
|
||||
{
|
||||
uint32_t name_off = rvaToOffset(name_rva);
|
||||
if (name_off) dll_name = readString(name_off);
|
||||
}
|
||||
if (dll_name.empty()) continue;
|
||||
|
||||
uint32_t thunk_rva = r32(m_data, desc + 16);
|
||||
if (thunk_rva == 0) thunk_rva = oft_rva;
|
||||
|
||||
uint32_t thunk_off = rvaToOffset(thunk_rva);
|
||||
if (!thunk_off) continue;
|
||||
|
||||
size_t entry_size = is_64 ? 8 : 4;
|
||||
uint64_t ordinal_flag = is_64 ? 0x8000000000000000ULL : 0x80000000;
|
||||
|
||||
for (uint32_t j = 0; ; j++)
|
||||
{
|
||||
uint32_t entry = static_cast<uint32_t>(thunk_off + j * entry_size);
|
||||
if (entry + entry_size > m_data.size()) break;
|
||||
|
||||
uint64_t val = is_64 ? r64(m_data, entry) : r32(m_data, entry);
|
||||
if (val == 0) break;
|
||||
|
||||
string func_name;
|
||||
if (val & ordinal_flag)
|
||||
{
|
||||
func_name = "ord_" + to_string(val & 0xFFFF);
|
||||
}
|
||||
else
|
||||
{
|
||||
addr_t hint_va = val + m_image_base;
|
||||
uint32_t hint_off = rvaToOffset(static_cast<uint32_t>(val));
|
||||
if (hint_off && hint_off + 2 < m_data.size())
|
||||
func_name = readString(hint_off + 2);
|
||||
}
|
||||
|
||||
addr_t iat_addr = thunk_rva + m_image_base + j * entry_size;
|
||||
ImportEntry e;
|
||||
e.address = iat_addr;
|
||||
e.name = func_name;
|
||||
e.library = dll_name;
|
||||
m_imports.push_back(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PELoader::parseImportTable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PELoader::parseExportTable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
vector<uint8_t> PELoader::readBytes(addr_t vaddr, size_t size) const
|
||||
{
|
||||
vector<uint8_t> result;
|
||||
uint32_t off = 0;
|
||||
uint32_t raw_off = 0;
|
||||
uint32_t raw_end = 0;
|
||||
|
||||
uint16_t num_sections = r16(m_data, r32(m_data, 0x3C) + 6);
|
||||
uint32_t pe_off = r32(m_data, 0x3C);
|
||||
uint16_t opt_sz = r16(m_data, pe_off + 20);
|
||||
uint32_t sections_off = pe_off + 24 + opt_sz;
|
||||
|
||||
for (uint16_t i = 0; i < num_sections; i++)
|
||||
{
|
||||
uint32_t so = sections_off + i * 40;
|
||||
if (so + 40 > m_data.size()) break;
|
||||
addr_t sva = r32(m_data, so + 12) + m_image_base;
|
||||
uint32_t svs = r32(m_data, so + 8);
|
||||
uint32_t srs = r32(m_data, so + 16);
|
||||
uint32_t srp = r32(m_data, so + 20);
|
||||
addr_t end = sva + (svs ? svs : srs);
|
||||
|
||||
if (vaddr >= sva && vaddr < end)
|
||||
{
|
||||
off = srp + static_cast<uint32_t>(vaddr - sva);
|
||||
raw_off = srp;
|
||||
raw_end = srp + srs;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!off) return result;
|
||||
|
||||
size_t available = (raw_end > off) ? (raw_end - off) : 0;
|
||||
size_t to_read = min(size, available);
|
||||
if (off + to_read > m_data.size())
|
||||
to_read = m_data.size() - off;
|
||||
|
||||
if (to_read > 0)
|
||||
result.assign(m_data.begin() + off, m_data.begin() + off + to_read);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
vector<pair<addr_t, addr_t>> PELoader::getExecutableRanges() const
|
||||
{
|
||||
return m_exec_ranges;
|
||||
}
|
||||
|
||||
vector<pair<addr_t, string>> PELoader::getExportedFunctions() const
|
||||
{
|
||||
return m_exports;
|
||||
}
|
||||
|
||||
vector<ImportEntry> PELoader::getImportedFunctions() const
|
||||
{
|
||||
return m_imports;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
#include "loader/binary.hpp"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
class PELoader : public Binary
|
||||
{
|
||||
public:
|
||||
PELoader();
|
||||
~PELoader() override;
|
||||
|
||||
bool load(const string& path) override;
|
||||
BinaryFormat getFormat() const override { return BinaryFormat::PE; }
|
||||
Arch getArch() const override { return m_arch; }
|
||||
addr_t getEntryPoint() const override { return m_entry; }
|
||||
addr_t getImageBase() const override { return m_image_base; }
|
||||
vector<uint8_t> readBytes(addr_t vaddr, size_t size) const override;
|
||||
vector<pair<addr_t, addr_t>> getExecutableRanges() const override;
|
||||
vector<pair<addr_t, string>> getExportedFunctions() const override;
|
||||
vector<ImportEntry> getImportedFunctions() const override;
|
||||
const string& getPath() const override { return m_path; }
|
||||
|
||||
private:
|
||||
bool parseHeaders();
|
||||
bool parseImportTable();
|
||||
bool parseExportTable();
|
||||
|
||||
string m_path;
|
||||
Arch m_arch;
|
||||
addr_t m_entry;
|
||||
addr_t m_image_base;
|
||||
vector<uint8_t> m_data;
|
||||
vector<pair<addr_t, addr_t>> m_exec_ranges;
|
||||
vector<pair<addr_t, string>> m_exports;
|
||||
vector<ImportEntry> m_imports;
|
||||
};
|
||||
Reference in New Issue
Block a user