mirror of
https://github.com/orange-cpp/omath.git
synced 2026-06-10 09:14:34 +00:00
improved file scanners
This commit is contained in:
@@ -35,13 +35,22 @@ static std::vector<std::byte> make_elf64_with_text_section(const std::vector<std
|
||||
|
||||
std::vector<std::byte> buf(total_size, std::byte{0});
|
||||
|
||||
auto w8 = [&](std::size_t off, std::uint8_t v) { buf[off] = std::byte{v}; };
|
||||
auto w8 = [&](std::size_t off, std::uint8_t v)
|
||||
{
|
||||
buf[off] = std::byte{v};
|
||||
};
|
||||
auto w16 = [&](std::size_t off, std::uint16_t v)
|
||||
{ std::memcpy(buf.data() + off, &v, 2); };
|
||||
{
|
||||
std::memcpy(buf.data() + off, &v, 2);
|
||||
};
|
||||
auto w32 = [&](std::size_t off, std::uint32_t v)
|
||||
{ std::memcpy(buf.data() + off, &v, 4); };
|
||||
{
|
||||
std::memcpy(buf.data() + off, &v, 4);
|
||||
};
|
||||
auto w64 = [&](std::size_t off, std::uint64_t v)
|
||||
{ std::memcpy(buf.data() + off, &v, 8); };
|
||||
{
|
||||
std::memcpy(buf.data() + off, &v, 8);
|
||||
};
|
||||
|
||||
// --- ELF64 file header ---
|
||||
// e_ident
|
||||
@@ -53,19 +62,19 @@ static std::vector<std::byte> make_elf64_with_text_section(const std::vector<std
|
||||
w8(5, 1); // ELFDATA2LSB
|
||||
w8(6, 1); // EV_CURRENT
|
||||
// rest of e_ident is 0
|
||||
w16(16, 2); // e_type = ET_EXEC
|
||||
w16(16, 2); // e_type = ET_EXEC
|
||||
w16(18, 62); // e_machine = EM_X86_64
|
||||
w32(20, 1); // e_version
|
||||
w64(24, 0); // e_entry
|
||||
w64(32, 0); // e_phoff
|
||||
w32(20, 1); // e_version
|
||||
w64(24, 0); // e_entry
|
||||
w64(32, 0); // e_phoff
|
||||
w64(40, static_cast<std::uint64_t>(shdr_table_off)); // e_shoff
|
||||
w32(48, 0); // e_flags
|
||||
w32(48, 0); // e_flags
|
||||
w16(52, 64); // e_ehsize
|
||||
w16(54, 56); // e_phentsize
|
||||
w16(56, 0); // e_phnum
|
||||
w16(56, 0); // e_phnum
|
||||
w16(58, static_cast<std::uint16_t>(shdr_size)); // e_shentsize
|
||||
w16(60, static_cast<std::uint16_t>(num_sections)); // e_shnum
|
||||
w16(62, 2); // e_shstrndx = 2 (.shstrtab is section index 2)
|
||||
w16(62, 2); // e_shstrndx = 2 (.shstrtab is section index 2)
|
||||
|
||||
// --- section data (.text) ---
|
||||
const std::size_t copy_len = std::min(code_bytes.size(), text_size);
|
||||
@@ -104,9 +113,9 @@ static std::vector<std::byte> make_elf64_with_text_section(const std::vector<std
|
||||
// Section 1: .text
|
||||
{
|
||||
const std::size_t base = shdr_table_off + 1 * shdr_size;
|
||||
w32(base + 0, 1); // sh_name → index 1 in shstrtab → ".text"
|
||||
w32(base + 4, 1); // sh_type = SHT_PROGBITS
|
||||
w64(base + 8, 6); // sh_flags = SHF_ALLOC|SHF_EXECINSTR
|
||||
w32(base + 0, 1); // sh_name → index 1 in shstrtab → ".text"
|
||||
w32(base + 4, 1); // sh_type = SHT_PROGBITS
|
||||
w64(base + 8, 6); // sh_flags = SHF_ALLOC|SHF_EXECINSTR
|
||||
w64(base + 16, static_cast<std::uint64_t>(text_off)); // sh_addr (same as offset in test)
|
||||
w64(base + 24, static_cast<std::uint64_t>(text_off)); // sh_offset
|
||||
w64(base + 32, static_cast<std::uint64_t>(text_size)); // sh_size
|
||||
@@ -116,8 +125,8 @@ static std::vector<std::byte> make_elf64_with_text_section(const std::vector<std
|
||||
// Section 2: .shstrtab
|
||||
{
|
||||
const std::size_t base = shdr_table_off + 2 * shdr_size;
|
||||
w32(base + 0, 0); // sh_name → index 0 → "" (good enough for scanner)
|
||||
w32(base + 4, 3); // sh_type = SHT_STRTAB
|
||||
w32(base + 0, 0); // sh_name → index 0 → "" (good enough for scanner)
|
||||
w32(base + 4, 3); // sh_type = SHT_STRTAB
|
||||
w64(base + 24, static_cast<std::uint64_t>(shstrtab_off)); // sh_offset
|
||||
w64(base + 32, static_cast<std::uint64_t>(shstrtab_size)); // sh_size
|
||||
}
|
||||
@@ -151,6 +160,18 @@ TEST(unit_test_elf_pattern_scan_memory, finds_pattern_with_wildcard)
|
||||
EXPECT_EQ(result->target_offset, 0);
|
||||
}
|
||||
|
||||
TEST(unit_test_elf_pattern_scan_memory, consteval_finds_pattern_with_wildcard)
|
||||
{
|
||||
const std::vector<std::uint8_t> code = {0x00, 0xDE, 0xAD, 0xBE, 0xEF};
|
||||
const auto buf = make_elf64_with_text_section(code);
|
||||
|
||||
const auto result =
|
||||
ElfPatternScanner::scan_for_pattern_in_memory_file<"DE ?? BE EF">(std::span<const std::byte>{buf}, ".text");
|
||||
|
||||
ASSERT_TRUE(result.has_value());
|
||||
EXPECT_EQ(result->target_offset, 1);
|
||||
}
|
||||
|
||||
TEST(unit_test_elf_pattern_scan_memory, pattern_not_found_returns_nullopt)
|
||||
{
|
||||
const std::vector<std::uint8_t> code = {0x01, 0x02, 0x03, 0x04};
|
||||
@@ -182,8 +203,8 @@ TEST(unit_test_elf_pattern_scan_memory, missing_section_returns_nullopt)
|
||||
const std::vector<std::uint8_t> code = {0x90, 0x90};
|
||||
const auto buf = make_elf64_with_text_section(code);
|
||||
|
||||
const auto result = ElfPatternScanner::scan_for_pattern_in_memory_file(std::span<const std::byte>{buf},
|
||||
"90 90", ".nonexistent");
|
||||
const auto result = ElfPatternScanner::scan_for_pattern_in_memory_file(std::span<const std::byte>{buf}, "90 90",
|
||||
".nonexistent");
|
||||
|
||||
EXPECT_FALSE(result.has_value());
|
||||
}
|
||||
@@ -201,8 +222,8 @@ TEST(unit_test_elf_pattern_scan_memory, matches_file_scan)
|
||||
}
|
||||
|
||||
const auto file_result = ElfPatternScanner::scan_for_pattern_in_file(tmp_path, "48 89 E5 DE AD", ".text");
|
||||
const auto mem_result =
|
||||
ElfPatternScanner::scan_for_pattern_in_memory_file(std::span<const std::byte>{buf}, "48 89 E5 DE AD", ".text");
|
||||
const auto mem_result = ElfPatternScanner::scan_for_pattern_in_memory_file(std::span<const std::byte>{buf},
|
||||
"48 89 E5 DE AD", ".text");
|
||||
|
||||
std::filesystem::remove(tmp_path);
|
||||
|
||||
@@ -212,3 +233,27 @@ TEST(unit_test_elf_pattern_scan_memory, matches_file_scan)
|
||||
EXPECT_EQ(file_result->raw_base_addr, mem_result->raw_base_addr);
|
||||
EXPECT_EQ(file_result->target_offset, mem_result->target_offset);
|
||||
}
|
||||
|
||||
TEST(unit_test_elf_pattern_scan_memory, consteval_file_scan_finds_pattern)
|
||||
{
|
||||
const std::vector<std::uint8_t> code = {0x48, 0x89, 0xE5, 0xDE, 0xAD};
|
||||
const auto buf = make_elf64_with_text_section(code);
|
||||
const auto tmp_path = std::filesystem::temp_directory_path() / "omath_elf_consteval_test.elf";
|
||||
{
|
||||
std::ofstream out(tmp_path, std::ios::binary);
|
||||
out.write(reinterpret_cast<const char*>(buf.data()), static_cast<std::streamsize>(buf.size()));
|
||||
}
|
||||
|
||||
const auto result = ElfPatternScanner::scan_for_pattern_in_file<"48 ?? E5">(tmp_path, ".text");
|
||||
|
||||
std::filesystem::remove(tmp_path);
|
||||
|
||||
ASSERT_TRUE(result.has_value());
|
||||
EXPECT_EQ(result->target_offset, 0);
|
||||
}
|
||||
|
||||
TEST(unit_test_elf_pattern_scan_memory, consteval_loaded_module_null_returns_nullopt)
|
||||
{
|
||||
const auto result = ElfPatternScanner::scan_for_pattern_in_loaded_module<"DE AD">(nullptr);
|
||||
EXPECT_FALSE(result.has_value());
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// Tests for MachOPatternScanner::scan_for_pattern_in_memory_file
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <gtest/gtest.h>
|
||||
#include <omath/utility/macho_pattern_scan.hpp>
|
||||
#include <span>
|
||||
@@ -37,38 +39,43 @@ static std::vector<std::byte> make_macho64_with_text_section(const std::vector<s
|
||||
constexpr std::size_t total_size = text_raw_off + text_raw_size;
|
||||
constexpr std::uint64_t text_vmaddr = 0x1000ULL;
|
||||
|
||||
constexpr std::uint32_t cmd_size =
|
||||
static_cast<std::uint32_t>(seg_size + sect_hdr_size); // segment + 1 section
|
||||
constexpr std::uint32_t cmd_size = static_cast<std::uint32_t>(seg_size + sect_hdr_size); // segment + 1 section
|
||||
|
||||
std::vector<std::byte> buf(total_size, std::byte{0});
|
||||
|
||||
auto w32 = [&](std::size_t off, std::uint32_t v) { std::memcpy(buf.data() + off, &v, 4); };
|
||||
auto w64 = [&](std::size_t off, std::uint64_t v) { std::memcpy(buf.data() + off, &v, 8); };
|
||||
auto w32 = [&](std::size_t off, std::uint32_t v)
|
||||
{
|
||||
std::memcpy(buf.data() + off, &v, 4);
|
||||
};
|
||||
auto w64 = [&](std::size_t off, std::uint64_t v)
|
||||
{
|
||||
std::memcpy(buf.data() + off, &v, 8);
|
||||
};
|
||||
|
||||
// MachHeader64
|
||||
w32(0, mh_magic_64);
|
||||
w32(4, 0x0100000C); // cputype = CPU_TYPE_ARM64 (doesn't matter for scan)
|
||||
w32(12, 2); // filetype = MH_EXECUTE
|
||||
w32(16, 1); // ncmds = 1
|
||||
w32(20, cmd_size); // sizeofcmds
|
||||
w32(4, 0x0100000C); // cputype = CPU_TYPE_ARM64 (doesn't matter for scan)
|
||||
w32(12, 2); // filetype = MH_EXECUTE
|
||||
w32(16, 1); // ncmds = 1
|
||||
w32(20, cmd_size); // sizeofcmds
|
||||
|
||||
// SegmentCommand64 at 0x20
|
||||
constexpr std::size_t seg_off = hdr_size;
|
||||
w32(seg_off + 0, lc_segment_64);
|
||||
w32(seg_off + 4, cmd_size);
|
||||
std::memcpy(buf.data() + seg_off + 8, "__TEXT", 6); // segname
|
||||
w64(seg_off + 24, text_vmaddr); // vmaddr
|
||||
w64(seg_off + 32, text_raw_size); // vmsize
|
||||
w64(seg_off + 40, text_raw_off); // fileoff
|
||||
w64(seg_off + 48, text_raw_size); // filesize
|
||||
w32(seg_off + 64, 1); // nsects
|
||||
w64(seg_off + 24, text_vmaddr); // vmaddr
|
||||
w64(seg_off + 32, text_raw_size); // vmsize
|
||||
w64(seg_off + 40, text_raw_off); // fileoff
|
||||
w64(seg_off + 48, text_raw_size); // filesize
|
||||
w32(seg_off + 64, 1); // nsects
|
||||
|
||||
// Section64 at 0x68
|
||||
constexpr std::size_t sect_off = seg_off + seg_size;
|
||||
std::memcpy(buf.data() + sect_off + 0, "__text", 6); // sectname
|
||||
std::memcpy(buf.data() + sect_off + 0, "__text", 6); // sectname
|
||||
std::memcpy(buf.data() + sect_off + 16, "__TEXT", 6); // segname
|
||||
w64(sect_off + 32, text_vmaddr); // addr
|
||||
w64(sect_off + 40, text_raw_size); // size
|
||||
w64(sect_off + 32, text_vmaddr); // addr
|
||||
w64(sect_off + 40, text_raw_size); // size
|
||||
w32(sect_off + 48, static_cast<std::uint32_t>(text_raw_off)); // offset (file offset)
|
||||
|
||||
// Section data
|
||||
@@ -105,6 +112,18 @@ TEST(unit_test_macho_memory_file_scan, finds_pattern_with_wildcard)
|
||||
EXPECT_EQ(result->target_offset, 0);
|
||||
}
|
||||
|
||||
TEST(unit_test_macho_memory_file_scan, consteval_finds_pattern_with_wildcard)
|
||||
{
|
||||
const std::vector<std::uint8_t> code = {0x00, 0xDE, 0xAD, 0xBE, 0xEF};
|
||||
const auto buf = make_macho64_with_text_section(code);
|
||||
|
||||
const auto result =
|
||||
MachOPatternScanner::scan_for_pattern_in_memory_file<"DE ?? BE EF">(std::span<const std::byte>{buf});
|
||||
|
||||
ASSERT_TRUE(result.has_value());
|
||||
EXPECT_EQ(result->target_offset, 1);
|
||||
}
|
||||
|
||||
TEST(unit_test_macho_memory_file_scan, pattern_not_found_returns_nullopt)
|
||||
{
|
||||
const std::vector<std::uint8_t> code = {0x01, 0x02, 0x03};
|
||||
@@ -143,3 +162,27 @@ TEST(unit_test_macho_memory_file_scan, raw_addr_and_virtual_addr_correct)
|
||||
EXPECT_EQ(result->raw_base_addr, expected_raw_off);
|
||||
EXPECT_EQ(result->virtual_base_addr, 0x1000u);
|
||||
}
|
||||
|
||||
TEST(unit_test_macho_memory_file_scan, consteval_file_scan_finds_pattern)
|
||||
{
|
||||
const std::vector<std::uint8_t> code = {0x48, 0x89, 0xE5, 0xDE, 0xAD};
|
||||
const auto buf = make_macho64_with_text_section(code);
|
||||
const auto tmp_path = std::filesystem::temp_directory_path() / "omath_macho_consteval_test.bin";
|
||||
{
|
||||
std::ofstream out(tmp_path, std::ios::binary);
|
||||
out.write(reinterpret_cast<const char*>(buf.data()), static_cast<std::streamsize>(buf.size()));
|
||||
}
|
||||
|
||||
const auto result = MachOPatternScanner::scan_for_pattern_in_file<"48 ?? E5">(tmp_path);
|
||||
|
||||
std::filesystem::remove(tmp_path);
|
||||
|
||||
ASSERT_TRUE(result.has_value());
|
||||
EXPECT_EQ(result->target_offset, 0);
|
||||
}
|
||||
|
||||
TEST(unit_test_macho_memory_file_scan, consteval_loaded_module_null_returns_nullopt)
|
||||
{
|
||||
const auto result = MachOPatternScanner::scan_for_pattern_in_loaded_module<"DE AD">(nullptr);
|
||||
EXPECT_FALSE(result.has_value());
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// Tests for PePatternScanner::scan_for_pattern_in_memory_file
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <gtest/gtest.h>
|
||||
#include <omath/utility/pe_pattern_scan.hpp>
|
||||
#include <span>
|
||||
@@ -10,8 +12,7 @@ using namespace omath;
|
||||
// Reuse the fake-module builder from unit_test_pe_pattern_scan_loaded.cpp but
|
||||
// lay out the buffer as a raw PE *file* (ptr_raw_data != virtual_address).
|
||||
static std::vector<std::byte> make_fake_pe_file(std::uint32_t virtual_address, std::uint32_t ptr_raw_data,
|
||||
std::uint32_t section_size,
|
||||
const std::vector<std::uint8_t>& code_bytes)
|
||||
std::uint32_t section_size, const std::vector<std::uint8_t>& code_bytes)
|
||||
{
|
||||
constexpr std::uint32_t e_lfanew = 0x80;
|
||||
constexpr std::uint32_t nt_sig = 0x4550;
|
||||
@@ -24,9 +25,18 @@ static std::vector<std::byte> make_fake_pe_file(std::uint32_t virtual_address, s
|
||||
const std::uint32_t total_size = ptr_raw_data + section_size + 0x100;
|
||||
std::vector<std::byte> buf(total_size, std::byte{0});
|
||||
|
||||
auto w16 = [&](std::size_t off, std::uint16_t v) { std::memcpy(buf.data() + off, &v, 2); };
|
||||
auto w32 = [&](std::size_t off, std::uint32_t v) { std::memcpy(buf.data() + off, &v, 4); };
|
||||
auto w64 = [&](std::size_t off, std::uint64_t v) { std::memcpy(buf.data() + off, &v, 8); };
|
||||
auto w16 = [&](std::size_t off, std::uint16_t v)
|
||||
{
|
||||
std::memcpy(buf.data() + off, &v, 2);
|
||||
};
|
||||
auto w32 = [&](std::size_t off, std::uint32_t v)
|
||||
{
|
||||
std::memcpy(buf.data() + off, &v, 4);
|
||||
};
|
||||
auto w64 = [&](std::size_t off, std::uint64_t v)
|
||||
{
|
||||
std::memcpy(buf.data() + off, &v, 8);
|
||||
};
|
||||
|
||||
// DOS header
|
||||
w16(0x00, 0x5A4D);
|
||||
@@ -48,10 +58,10 @@ static std::vector<std::byte> make_fake_pe_file(std::uint32_t virtual_address, s
|
||||
// Section header (.text)
|
||||
const std::size_t sh_off = section_table_off;
|
||||
std::memcpy(buf.data() + sh_off, ".text", 5);
|
||||
w32(sh_off + 8, section_size); // VirtualSize
|
||||
w32(sh_off + 12, virtual_address); // VirtualAddress
|
||||
w32(sh_off + 16, section_size); // SizeOfRawData
|
||||
w32(sh_off + 20, ptr_raw_data); // PointerToRawData
|
||||
w32(sh_off + 8, section_size); // VirtualSize
|
||||
w32(sh_off + 12, virtual_address); // VirtualAddress
|
||||
w32(sh_off + 16, section_size); // SizeOfRawData
|
||||
w32(sh_off + 20, ptr_raw_data); // PointerToRawData
|
||||
|
||||
// Place code at raw file offset
|
||||
const std::size_t copy_len = std::min(code_bytes.size(), static_cast<std::size_t>(section_size));
|
||||
@@ -87,13 +97,24 @@ TEST(unit_test_pe_memory_file_scan, finds_pattern_with_wildcard)
|
||||
EXPECT_EQ(result->target_offset, 0);
|
||||
}
|
||||
|
||||
TEST(unit_test_pe_memory_file_scan, consteval_finds_pattern_with_wildcard)
|
||||
{
|
||||
const std::vector<std::uint8_t> code = {0x00, 0xDE, 0xAD, 0xBE, 0xEF};
|
||||
const auto buf = make_fake_pe_file(0x2000, 0x600, static_cast<std::uint32_t>(code.size()), code);
|
||||
|
||||
const auto result =
|
||||
PePatternScanner::scan_for_pattern_in_memory_file<"DE ?? BE EF">(std::span<const std::byte>{buf});
|
||||
|
||||
ASSERT_TRUE(result.has_value());
|
||||
EXPECT_EQ(result->target_offset, 1);
|
||||
}
|
||||
|
||||
TEST(unit_test_pe_memory_file_scan, pattern_not_found_returns_nullopt)
|
||||
{
|
||||
const std::vector<std::uint8_t> code = {0x01, 0x02, 0x03};
|
||||
const auto buf = make_fake_pe_file(0x1000, 0x400, static_cast<std::uint32_t>(code.size()), code);
|
||||
|
||||
const auto result =
|
||||
PePatternScanner::scan_for_pattern_in_memory_file(std::span<const std::byte>{buf}, "AA BB CC");
|
||||
const auto result = PePatternScanner::scan_for_pattern_in_memory_file(std::span<const std::byte>{buf}, "AA BB CC");
|
||||
|
||||
EXPECT_FALSE(result.has_value());
|
||||
}
|
||||
@@ -126,3 +147,27 @@ TEST(unit_test_pe_memory_file_scan, raw_addr_differs_from_virtual_address)
|
||||
// virtual_base_addr = virtual_address + image_base (image_base = 0)
|
||||
EXPECT_EQ(result->virtual_base_addr, 0x3000u);
|
||||
}
|
||||
|
||||
TEST(unit_test_pe_memory_file_scan, consteval_file_scan_finds_pattern)
|
||||
{
|
||||
const std::vector<std::uint8_t> code = {0x48, 0x89, 0xE5, 0xDE, 0xAD};
|
||||
const auto buf = make_fake_pe_file(0x2000, 0x600, static_cast<std::uint32_t>(code.size()), code);
|
||||
const auto tmp_path = std::filesystem::temp_directory_path() / "omath_pe_consteval_test.exe";
|
||||
{
|
||||
std::ofstream out(tmp_path, std::ios::binary);
|
||||
out.write(reinterpret_cast<const char*>(buf.data()), static_cast<std::streamsize>(buf.size()));
|
||||
}
|
||||
|
||||
const auto result = PePatternScanner::scan_for_pattern_in_file<"48 ?? E5">(tmp_path);
|
||||
|
||||
std::filesystem::remove(tmp_path);
|
||||
|
||||
ASSERT_TRUE(result.has_value());
|
||||
EXPECT_EQ(result->target_offset, 0);
|
||||
}
|
||||
|
||||
TEST(unit_test_pe_memory_file_scan, consteval_loaded_module_null_returns_nullopt)
|
||||
{
|
||||
const auto result = PePatternScanner::scan_for_pattern_in_loaded_module<"DE AD">(nullptr);
|
||||
EXPECT_FALSE(result.has_value());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user