Files
omath/tests/general/unit_test_pe_pattern_scan_loaded.cpp
Orange++ 2af77d30d4 Feature/elf pattern scan (#133)
* added some code

* improvement

* visit

* added scanner code

* update

* fixed naming

* added const

* added type casting

* added file

* patch

* added unlikely

* added in module scanner

* fixing test

* fix

* remove

* improvement

* fix

* Update source/utility/elf_pattern_scan.cpp

Co-authored-by: Saikari <lin@sz.cn.eu.org>

* rev

* fix

* patch

* decomposed method

* fix

* fix

* improvement

* fix

* fix

* commented stuff

---------

Co-authored-by: Saikari <lin@sz.cn.eu.org>
2026-01-01 03:37:55 +03:00

98 lines
4.5 KiB
C++

// Tests for PePatternScanner::scan_for_pattern_in_loaded_module
#include <gtest/gtest.h>
#include <omath/utility/pe_pattern_scan.hpp>
#include <vector>
#include <cstdint>
#include <cstring>
using namespace omath;
static std::vector<std::uint8_t> make_fake_module(std::uint32_t base_of_code,
std::uint32_t size_code,
const std::vector<std::uint8_t>& code_bytes)
{
// Constants
constexpr std::uint32_t e_lfanew = 0x80;
constexpr std::uint32_t nt_sig = 0x4550; // "PE\0\0"
constexpr std::uint16_t opt_magic = 0x020B; // PE32+
constexpr std::uint16_t num_sections = 1;
constexpr std::uint16_t opt_hdr_size = 0xF0; // Standard PE32+ optional header size
constexpr std::uint32_t section_table_off = e_lfanew + 4 + 20 + opt_hdr_size; // sig(4) + FileHdr(20)
constexpr std::uint32_t section_header_size = 40;
constexpr std::uint32_t text_characteristics = 0x60000020; // code | execute | read
const std::uint32_t headers_end = section_table_off + section_header_size;
const std::uint32_t code_end = base_of_code + size_code;
const std::uint32_t total_size = std::max(headers_end, code_end) + 0x100; // leave some padding
std::vector<std::uint8_t> buf(total_size, 0);
auto w16 = [&](std::size_t off, std::uint16_t v) { std::memcpy(buf.data() + off, &v, sizeof(v)); };
auto w32 = [&](std::size_t off, std::uint32_t v) { std::memcpy(buf.data() + off, &v, sizeof(v)); };
auto w64 = [&](std::size_t off, std::uint64_t v) { std::memcpy(buf.data() + off, &v, sizeof(v)); };
// DOS header
w16(0x00, 0x5A4D); // e_magic "MZ"
w32(0x3C, e_lfanew); // e_lfanew
// NT signature
w32(e_lfanew, nt_sig);
// FileHeader (starts at e_lfanew + 4)
const std::size_t fh_off = e_lfanew + 4;
w16(fh_off + 2, num_sections); // NumberOfSections
w16(fh_off + 16, opt_hdr_size); // SizeOfOptionalHeader
// OptionalHeader PE32+ (starts at e_lfanew + 4 + 20)
const std::size_t opt_off = fh_off + 20;
w16(opt_off + 0, opt_magic); // Magic
w32(opt_off + 4, size_code); // SizeOfCode
w32(opt_off + 16, 0); // AddressOfEntryPoint (unused in test)
w32(opt_off + 20, base_of_code); // BaseOfCode
w64(opt_off + 24, 0); // ImageBase
w32(opt_off + 32, 0x1000); // SectionAlignment
w32(opt_off + 36, 0x200); // FileAlignment
w32(opt_off + 56, code_end); // SizeOfImage (simple upper bound)
w32(opt_off + 60, headers_end); // SizeOfHeaders
w32(opt_off + 108, 0); // NumberOfRvaAndSizes (0 directories)
// Section header (.text) at section_table_off
const std::size_t sh_off = section_table_off;
std::memcpy(buf.data() + sh_off + 0, ".text", 5); // Name[8]
w32(sh_off + 8, size_code); // VirtualSize
w32(sh_off + 12, base_of_code); // VirtualAddress
w32(sh_off + 16, size_code); // SizeOfRawData
w32(sh_off + 20, base_of_code); // PointerToRawData
w32(sh_off + 36, text_characteristics); // Characteristics
// Place code bytes at BaseOfCode
if (base_of_code + code_bytes.size() <= buf.size())
std::memcpy(buf.data() + base_of_code, code_bytes.data(), code_bytes.size());
return buf;
}
TEST(PePatternScanLoaded, FindsPatternAtBase)
{
const std::vector<std::uint8_t> code = {0x90, 0x01, 0x02, 0x03, 0x04};
auto buf = make_fake_module(0x200, static_cast<std::uint32_t>(code.size()), code);
const auto res = PePatternScanner::scan_for_pattern_in_loaded_module(buf.data(), "90 01 02");
ASSERT_TRUE(res.has_value());
// address should point somewhere in our buffer; check offset
const uintptr_t addr = res.value();
const uintptr_t base = reinterpret_cast<uintptr_t>(buf.data());
EXPECT_EQ(addr - base, 0x200u);
}
TEST(PePatternScanLoaded, WildcardMatches)
{
const std::vector<std::uint8_t> code = {0xDE, 0xAD, 0xBE, 0xEF};
constexpr std::uint32_t base_of_code = 0x300;
auto buf = make_fake_module(base_of_code, static_cast<std::uint32_t>(code.size()), code);
const auto res = PePatternScanner::scan_for_pattern_in_loaded_module(buf.data(), "DE ?? BE", ".text");
ASSERT_TRUE(res.has_value());
const uintptr_t addr = res.value();
const uintptr_t base = reinterpret_cast<uintptr_t>(buf.data());
EXPECT_EQ(addr - base, base_of_code);
}