mirror of
https://gitlab.com/BinaryHardening/cfgrip
synced 2026-07-26 12:41:08 +00:00
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
This commit is contained in:
@@ -28,6 +28,14 @@ class Binary
|
||||
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, addr_t>> getDataRanges() const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
virtual vector<pair<addr_t, addr_t>> getRuntimeFunctions() const
|
||||
{
|
||||
return {};
|
||||
}
|
||||
virtual vector<pair<addr_t, string>> getExportedFunctions() const = 0;
|
||||
virtual vector<ImportEntry> getImportedFunctions() const
|
||||
{
|
||||
|
||||
@@ -290,6 +290,19 @@ bool ELFLoader::parseHeader()
|
||||
}
|
||||
}
|
||||
|
||||
// Collect data section ranges (allocatable, non-executable)
|
||||
for (uint16_t i = 0; i < shnum; i++)
|
||||
{
|
||||
uint64_t sh_flags = 0;
|
||||
size_t so = shoff + i * 64;
|
||||
if (so + 64 > m_data.size()) continue;
|
||||
sh_flags = r64(m_data, so + 8);
|
||||
bool is_exec = (sh_flags & 0x4) != 0;
|
||||
bool is_alloc = (sh_flags & 0x2) != 0;
|
||||
if (is_alloc && !is_exec && sections[i].addr && sections[i].size)
|
||||
m_data_ranges.push_back({sections[i].addr, sections[i].addr + sections[i].size});
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < shnum; i++)
|
||||
{
|
||||
string sname = sectionName(sections[i].name_idx);
|
||||
@@ -391,6 +404,11 @@ vector<uint8_t> ELFLoader::readBytes(addr_t vaddr, size_t size) const
|
||||
return {};
|
||||
}
|
||||
|
||||
vector<pair<addr_t, addr_t>> ELFLoader::getDataRanges() const
|
||||
{
|
||||
return m_data_ranges;
|
||||
}
|
||||
|
||||
vector<pair<addr_t, addr_t>> ELFLoader::getExecutableRanges() const
|
||||
{
|
||||
return m_exec_ranges;
|
||||
|
||||
@@ -16,6 +16,7 @@ class ELFLoader : public Binary
|
||||
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>> getDataRanges() const override;
|
||||
vector<pair<addr_t, addr_t>> getExecutableRanges() const override;
|
||||
vector<pair<addr_t, string>> getExportedFunctions() const override;
|
||||
vector<ImportEntry> getImportedFunctions() const override;
|
||||
@@ -39,6 +40,7 @@ class ELFLoader : public Binary
|
||||
addr_t m_entry;
|
||||
vector<uint8_t> m_data;
|
||||
vector<pair<addr_t, addr_t>> m_exec_ranges;
|
||||
vector<pair<addr_t, addr_t>> m_data_ranges;
|
||||
vector<tuple<uint64_t, addr_t, uint64_t>> m_file_map;
|
||||
vector<pair<addr_t, string>> m_exports;
|
||||
vector<ImportEntry> m_imports;
|
||||
|
||||
+60
-1
@@ -120,9 +120,58 @@ bool PELoader::parseHeaders()
|
||||
}
|
||||
}
|
||||
|
||||
// Collect data section ranges (non-executable readable)
|
||||
for (const auto& s : sections)
|
||||
{
|
||||
if (s.raw_ptr == 0) continue;
|
||||
bool is_exec = (s.characteristics & 0x20000000) != 0;
|
||||
bool is_read = (s.characteristics & 0x40000000) != 0;
|
||||
if (!is_exec && is_read)
|
||||
{
|
||||
addr_t start = s.vaddr;
|
||||
addr_t end = start + (s.vsize ? s.vsize : s.raw_size);
|
||||
m_data_ranges.push_back({start, end});
|
||||
}
|
||||
}
|
||||
|
||||
// Parse .pdata (exception handler table) for precise function boundaries
|
||||
uint32_t pdata_rva = 0;
|
||||
uint32_t pdata_size = 0;
|
||||
uint32_t data_dir_off = (magic == 0x10B) ? opt_hdr_off + 96 : opt_hdr_off + 112;
|
||||
if (magic == 0x20B)
|
||||
{
|
||||
pdata_rva = r32(m_data, data_dir_off + 24);
|
||||
pdata_size = r32(m_data, data_dir_off + 28);
|
||||
}
|
||||
if (pdata_rva && pdata_size >= 12)
|
||||
{
|
||||
uint32_t pdata_off = 0;
|
||||
for (const auto& s : sections)
|
||||
{
|
||||
addr_t va = pdata_rva + m_image_base;
|
||||
if (va >= s.vaddr && va < s.vaddr + (s.vsize ? s.vsize : s.raw_size))
|
||||
{
|
||||
pdata_off = s.raw_ptr + static_cast<uint32_t>(va - s.vaddr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pdata_off)
|
||||
{
|
||||
size_t nentries = pdata_size / 12;
|
||||
for (size_t i = 0; i < nentries; i++)
|
||||
{
|
||||
uint32_t off = pdata_off + i * 12;
|
||||
if (off + 12 > m_data.size()) break;
|
||||
uint32_t begin_rva = r32(m_data, off);
|
||||
uint32_t end_rva = r32(m_data, off + 4);
|
||||
if (begin_rva && end_rva && end_rva > begin_rva)
|
||||
m_pdata.push_back({begin_rva + m_image_base, end_rva + m_image_base});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
@@ -260,11 +309,21 @@ vector<uint8_t> PELoader::readBytes(addr_t vaddr, size_t size) const
|
||||
return result;
|
||||
}
|
||||
|
||||
vector<pair<addr_t, addr_t>> PELoader::getDataRanges() const
|
||||
{
|
||||
return m_data_ranges;
|
||||
}
|
||||
|
||||
vector<pair<addr_t, addr_t>> PELoader::getExecutableRanges() const
|
||||
{
|
||||
return m_exec_ranges;
|
||||
}
|
||||
|
||||
vector<pair<addr_t, addr_t>> PELoader::getRuntimeFunctions() const
|
||||
{
|
||||
return m_pdata;
|
||||
}
|
||||
|
||||
vector<pair<addr_t, string>> PELoader::getExportedFunctions() const
|
||||
{
|
||||
return m_exports;
|
||||
|
||||
@@ -16,7 +16,9 @@ class PELoader : public Binary
|
||||
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>> getDataRanges() const override;
|
||||
vector<pair<addr_t, addr_t>> getExecutableRanges() const override;
|
||||
vector<pair<addr_t, addr_t>> getRuntimeFunctions() const override;
|
||||
vector<pair<addr_t, string>> getExportedFunctions() const override;
|
||||
vector<ImportEntry> getImportedFunctions() const override;
|
||||
const string& getPath() const override { return m_path; }
|
||||
@@ -32,6 +34,8 @@ class PELoader : public Binary
|
||||
addr_t m_image_base;
|
||||
vector<uint8_t> m_data;
|
||||
vector<pair<addr_t, addr_t>> m_exec_ranges;
|
||||
vector<pair<addr_t, addr_t>> m_data_ranges;
|
||||
vector<pair<addr_t, addr_t>> m_pdata;
|
||||
vector<pair<addr_t, string>> m_exports;
|
||||
vector<ImportEntry> m_imports;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user