Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions include/omath/utility/elf_pattern_scan.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <cstdint>
#include <filesystem>
#include <optional>
#include <span>
#include <string_view>
#include "section_scan_result.hpp"
namespace omath
Expand All @@ -21,5 +22,10 @@ namespace omath
static std::optional<SectionScanResult>
scan_for_pattern_in_file(const std::filesystem::path& path_to_file, const std::string_view& pattern,
const std::string_view& target_section_name = ".text");

[[nodiscard]]
static std::optional<SectionScanResult>
scan_for_pattern_in_memory_file(std::span<const std::byte> file_data, const std::string_view& pattern,
const std::string_view& target_section_name = ".text");
};
} // namespace omath
6 changes: 6 additions & 0 deletions include/omath/utility/macho_pattern_scan.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <cstdint>
#include <filesystem>
#include <optional>
#include <span>
#include <string_view>
#include "section_scan_result.hpp"
namespace omath
Expand All @@ -21,5 +22,10 @@ namespace omath
static std::optional<SectionScanResult>
scan_for_pattern_in_file(const std::filesystem::path& path_to_file, const std::string_view& pattern,
const std::string_view& target_section_name = "__text");

[[nodiscard]]
static std::optional<SectionScanResult>
scan_for_pattern_in_memory_file(std::span<const std::byte> file_data, const std::string_view& pattern,
const std::string_view& target_section_name = "__text");
};
} // namespace omath
6 changes: 6 additions & 0 deletions include/omath/utility/pe_pattern_scan.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <cstdint>
#include <filesystem>
#include <optional>
#include <span>
#include <string_view>
#include "section_scan_result.hpp"
namespace omath
Expand All @@ -23,5 +24,10 @@ namespace omath
static std::optional<SectionScanResult>
scan_for_pattern_in_file(const std::filesystem::path& path_to_file, const std::string_view& pattern,
const std::string_view& target_section_name = ".text");

[[nodiscard]]
static std::optional<SectionScanResult>
scan_for_pattern_in_memory_file(std::span<const std::byte> file_data, const std::string_view& pattern,
const std::string_view& target_section_name = ".text");
};
} // namespace omath
105 changes: 105 additions & 0 deletions source/utility/elf_pattern_scan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <array>
#include <fstream>
#include <omath/utility/elf_pattern_scan.hpp>
#include <span>
#include <utility>
#include <variant>
#include <vector>
Expand Down Expand Up @@ -140,6 +141,87 @@ namespace
std::uintptr_t raw_base_addr{};
std::vector<std::byte> data;
};
template<FileArch arch>
std::optional<ExtractedSection> get_elf_section_from_memory_impl(const std::span<const std::byte> data,
const std::string_view& section_name)
{
using FH = typename ElfHeaders<arch>::FileHeader;
using SH = typename ElfHeaders<arch>::SectionHeader;

if (data.size() < sizeof(FH))
return std::nullopt;

const auto* file_header = reinterpret_cast<const FH*>(data.data());

const auto shoff = static_cast<std::size_t>(file_header->e_shoff);
const auto shnum = static_cast<std::size_t>(file_header->e_shnum);
const auto shstrndx = static_cast<std::size_t>(file_header->e_shstrndx);

const auto shstrtab_hdr_off = shoff + shstrndx * sizeof(SH);
if (shstrtab_hdr_off + sizeof(SH) > data.size())
return std::nullopt;

const auto* shstrtab_hdr = reinterpret_cast<const SH*>(data.data() + shstrtab_hdr_off);
const auto shstrtab_off = static_cast<std::size_t>(shstrtab_hdr->sh_offset);
const auto shstrtab_size = static_cast<std::size_t>(shstrtab_hdr->sh_size);

if (shstrtab_off + shstrtab_size > data.size())
return std::nullopt;

const auto* shstrtab = reinterpret_cast<const char*>(data.data() + shstrtab_off);

for (std::size_t i = 0; i < shnum; ++i)
{
const auto sect_hdr_off = shoff + i * sizeof(SH);
if (sect_hdr_off + sizeof(SH) > data.size())
continue;

const auto* section = reinterpret_cast<const SH*>(data.data() + sect_hdr_off);

if (std::cmp_greater_equal(section->sh_name, shstrtab_size))
continue;

if (std::string_view{shstrtab + section->sh_name} != section_name)
continue;

const auto raw_off = static_cast<std::size_t>(section->sh_offset);
const auto sec_size = static_cast<std::size_t>(section->sh_size);

if (raw_off + sec_size > data.size())
return std::nullopt;

ExtractedSection out;
out.virtual_base_addr = static_cast<std::uintptr_t>(section->sh_addr);
out.raw_base_addr = raw_off;
out.data.assign(data.data() + raw_off, data.data() + raw_off + sec_size);
return out;
}
return std::nullopt;
}

std::optional<ExtractedSection> get_elf_section_by_name_from_memory(const std::span<const std::byte> data,
const std::string_view& section_name)
{
constexpr std::string_view valid_elf_signature = "\x7F"
"ELF";
if (data.size() < ei_nident)
return std::nullopt;

if (std::string_view{reinterpret_cast<const char*>(data.data()), valid_elf_signature.size()}
!= valid_elf_signature)
return std::nullopt;

const auto class_byte = static_cast<uint8_t>(data[ei_class]);

if (class_byte == elfclass64)
return get_elf_section_from_memory_impl<FileArch::x64>(data, section_name);

if (class_byte == elfclass32)
return get_elf_section_from_memory_impl<FileArch::x32>(data, section_name);

return std::nullopt;
}

[[maybe_unused]]
std::optional<ExtractedSection> get_elf_section_by_name(const std::filesystem::path& path,
const std::string_view& section_name)
Expand Down Expand Up @@ -322,4 +404,27 @@ namespace omath
.raw_base_addr = pe_section->raw_base_addr,
.target_offset = offset};
}

std::optional<SectionScanResult>
ElfPatternScanner::scan_for_pattern_in_memory_file(const std::span<const std::byte> file_data,
const std::string_view& pattern,
const std::string_view& target_section_name)
{
const auto section = get_elf_section_by_name_from_memory(file_data, target_section_name);

if (!section.has_value()) [[unlikely]]
return std::nullopt;

const auto scan_result =
PatternScanner::scan_for_pattern(section->data.cbegin(), section->data.cend(), pattern);

if (scan_result == section->data.cend())
return std::nullopt;

const auto offset = std::distance(section->data.begin(), scan_result);

return SectionScanResult{.virtual_base_addr = section->virtual_base_addr,
.raw_base_addr = section->raw_base_addr,
.target_offset = offset};
}
} // namespace omath
114 changes: 114 additions & 0 deletions source/utility/macho_pattern_scan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "omath/utility/pattern_scan.hpp"
#include <cstring>
#include <fstream>
#include <span>
#include <variant>
#include <vector>

Expand Down Expand Up @@ -231,6 +232,96 @@ namespace
return std::nullopt;
}

template<typename HeaderType, typename SegmentType, typename SectionType, std::uint32_t segment_cmd>
std::optional<ExtractedSection> extract_section_from_memory_impl(const std::span<const std::byte> data,
const std::string_view& section_name)
{
if (data.size() < sizeof(HeaderType))
return std::nullopt;

const auto* header = reinterpret_cast<const HeaderType*>(data.data());

std::size_t cmd_offset = sizeof(HeaderType);

for (std::uint32_t i = 0; i < header->ncmds; ++i)
{
if (cmd_offset + sizeof(LoadCommand) > data.size())
return std::nullopt;

const auto* lc = reinterpret_cast<const LoadCommand*>(data.data() + cmd_offset);

if (lc->cmd != segment_cmd)
{
cmd_offset += lc->cmdsize;
continue;
}

if (cmd_offset + sizeof(SegmentType) > data.size())
return std::nullopt;

const auto* segment = reinterpret_cast<const SegmentType*>(data.data() + cmd_offset);

if (!segment->nsects)
{
cmd_offset += lc->cmdsize;
continue;
}

std::size_t sect_offset = cmd_offset + sizeof(SegmentType);

for (std::uint32_t j = 0; j < segment->nsects; ++j)
{
if (sect_offset + sizeof(SectionType) > data.size())
return std::nullopt;

const auto* section = reinterpret_cast<const SectionType*>(data.data() + sect_offset);

if (get_section_name(section->sectname) != section_name)
{
sect_offset += sizeof(SectionType);
continue;
}

const auto raw_off = static_cast<std::size_t>(section->offset);
const auto sec_size = static_cast<std::size_t>(section->size);

if (raw_off + sec_size > data.size())
return std::nullopt;

ExtractedSection out;
out.virtual_base_addr = static_cast<std::uintptr_t>(section->addr);
out.raw_base_addr = raw_off;
out.data.assign(data.data() + raw_off, data.data() + raw_off + sec_size);
return out;
}

cmd_offset += lc->cmdsize;
}

return std::nullopt;
}

[[nodiscard]]
std::optional<ExtractedSection> get_macho_section_by_name_from_memory(const std::span<const std::byte> data,
const std::string_view& section_name)
{
if (data.size() < sizeof(std::uint32_t))
return std::nullopt;

std::uint32_t magic{};
std::memcpy(&magic, data.data(), sizeof(magic));

if (magic == mh_magic_64 || magic == mh_cigam_64)
return extract_section_from_memory_impl<MachHeader64, SegmentCommand64, Section64, lc_segment_64>(
data, section_name);

if (magic == mh_magic_32 || magic == mh_cigam_32)
return extract_section_from_memory_impl<MachHeader32, SegmentCommand32, Section32, lc_segment>(data,
section_name);

return std::nullopt;
}

[[nodiscard]]
std::optional<ExtractedSection> get_macho_section_by_name(const std::filesystem::path& path,
const std::string_view& section_name)
Expand Down Expand Up @@ -346,4 +437,27 @@ namespace omath
.raw_base_addr = macho_section->raw_base_addr,
.target_offset = offset};
}

std::optional<SectionScanResult>
MachOPatternScanner::scan_for_pattern_in_memory_file(const std::span<const std::byte> file_data,
const std::string_view& pattern,
const std::string_view& target_section_name)
{
const auto section = get_macho_section_by_name_from_memory(file_data, target_section_name);

if (!section.has_value()) [[unlikely]]
return std::nullopt;

const auto scan_result =
PatternScanner::scan_for_pattern(section->data.cbegin(), section->data.cend(), pattern);

if (scan_result == section->data.cend())
return std::nullopt;

const auto offset = std::distance(section->data.begin(), scan_result);

return SectionScanResult{.virtual_base_addr = section->virtual_base_addr,
.raw_base_addr = section->raw_base_addr,
.target_offset = offset};
}
} // namespace omath
Loading
Loading