reman3/patcher/patcher.cpp

196 lines
6.6 KiB
C++

#include <windows.h>
#include <coffi/coffi.hpp>
#include <CLI11.hpp>
#include <spdlog/spdlog.h>
int main(int argc, char *argv[]) {
CLI::App app("Patcher");
std::string inputFile;
std::string outputFile;
app.add_option("-i,--input", inputFile, "Input exe file to patch")
->required();
app.add_option("-o,--output", outputFile, "Output patched exe file")
->required();
CLI11_PARSE(app, argc, argv);
// Load the object file containing the main function
COFFI::coffi objReader;
std::string objPath = SRC_OBJECT;
SPDLOG_INFO("Loading object file: {}", objPath);
if (!objReader.load(objPath)) {
spdlog::error("Failed to load object file: {}", objPath);
return 1;
}
// Load the source PE file
COFFI::coffi peReader;
SPDLOG_INFO("Loading PE file: {}", inputFile);
if (!peReader.load(inputFile)) {
spdlog::error("Failed to load PE file: {}", inputFile);
return 1;
}
// Find the 'main' function in the object file
auto &symbols = *objReader.get_symbols();
COFFI::symbol *mainSymbol = nullptr;
for (auto &sym : symbols) {
SPDLOG_INFO("Symbol: {}", sym.get_name());
if (sym.get_name() == "_ref") {
mainSymbol = &sym;
break;
}
}
if (!mainSymbol) {
spdlog::error("Could not find 'main' symbol in object file");
return 1;
}
// Get the section containing the main function
auto &sections = objReader.get_sections();
auto mainSection = sections[mainSymbol->get_section_number() - 1];
// Calculate main function size using next symbol method
uint32_t mainOffset = mainSymbol->get_value();
uint32_t mainSize = 0;
// Find the next symbol in the same section to calculate size
uint32_t nextSymbolOffset = UINT32_MAX;
for (auto &sym : symbols) {
if (sym.get_section_number() == mainSymbol->get_section_number() &&
sym.get_value() > mainOffset && sym.get_value() < nextSymbolOffset) {
nextSymbolOffset = sym.get_value();
}
}
if (nextSymbolOffset != UINT32_MAX) {
mainSize = nextSymbolOffset - mainOffset;
spdlog::info(
"Calculated main function size: {} bytes (next symbol at offset {})",
mainSize, nextSymbolOffset);
} else {
// If no next symbol found, use remaining section size
mainSize = mainSection->get_data_size() - mainOffset;
spdlog::info("No next symbol found, using remaining section size: {} bytes",
mainSize);
}
auto mainCodeData = mainSection->get_data();
spdlog::info("Found main function at offset {} with size {}", mainOffset,
mainSize);
// List relocations for the main function section
auto& sectionRelocations = mainSection->get_relocations();
spdlog::info("Relocations for section {} (containing main function):", mainSymbol->get_section_number());
for (auto& reloc : sectionRelocations) {
spdlog::info(" Relocation at offset 0x{:x}, type: {}, symbol index: {}",
reloc.get_virtual_address(),
reloc.get_type(),
reloc.get_symbol_table_index());
// Get symbol name for this relocation
spdlog::info(" -> Symbol: {}", reloc.get_symbol());
}
spdlog::info("Main function code:");
std::string s;
for (uint32_t i = 0; i < mainSize; i++) {
if (i > 0 && i % 16 == 0) {
spdlog::info("{}", s);
s.clear();
}
if (s.size() > 0)
s += " ";
s += fmt::format("{:02X}", mainCodeData[i]);
}
if (s.size() > 0)
spdlog::info("{}", s);
// Find .text section in PE file
auto &peSections = peReader.get_sections();
COFFI::section *textSection = nullptr;
for (auto &section : peSections) {
if (section->get_name() == ".text") {
textSection = section;
break;
}
}
if (!textSection) {
spdlog::error("Could not find .text section in PE file");
return 1;
}
// Get image base and calculate actual VA
uint64_t imageBase = 0;
auto winHeader = peReader.get_win_header();
if (winHeader) {
imageBase = winHeader->get_image_base();
}
uint32_t textSectionRVA = textSection->get_virtual_address();
uint32_t textSectionSize = textSection->get_virtual_size();
uint64_t textSectionVA = imageBase + textSectionRVA;
uint64_t textSectionEndVA = textSectionVA + textSectionSize;
spdlog::info("PE Image base: 0x{:x}", imageBase);
spdlog::info(".text section RVA: 0x{:x}, size: 0x{:x}", textSectionRVA, textSectionSize);
spdlog::info(".text section VA: 0x{:x} - 0x{:x}", textSectionVA, textSectionEndVA);
// Find available space at the end of .text section (look for null bytes)
auto textSectionData = textSection->get_data();
uint32_t originalTextSize = textSection->get_data_size();
// Search backwards from the end to find contiguous null bytes
uint32_t availableSpace = 0;
for (int32_t i = originalTextSize - 1; i >= 0; i--) {
if (reinterpret_cast<const uint8_t*>(textSectionData)[i] == 0x00) {
availableSpace++;
} else {
break; // Found non-null byte, stop counting
}
}
spdlog::info("Found {} bytes of available space (null bytes) at end of .text section", availableSpace);
if (availableSpace < mainSize) {
spdlog::error("Not enough space in .text section! Need {} bytes, found {} bytes",
mainSize, availableSpace);
return 1;
}
// Calculate injection offset (place code at start of null space)
uint32_t injectionOffset = originalTextSize - availableSpace;
uint64_t injectionVA = textSectionVA + injectionOffset;
spdlog::info("Injecting {} bytes at .text section offset 0x{:x} (VA: 0x{:x})",
mainSize, injectionOffset, injectionVA);
// Copy the main function code into the available space
const uint8_t* mainCode = reinterpret_cast<const uint8_t*>(mainCodeData) + mainOffset;
// Create a copy of the section data to modify
std::vector<uint8_t> newTextData(reinterpret_cast<const uint8_t*>(textSectionData),
reinterpret_cast<const uint8_t*>(textSectionData) + originalTextSize);
// Copy our code into the null space
std::memcpy(newTextData.data() + injectionOffset, mainCode, mainSize);
// Update the .text section with modified data (same size)
textSection->set_data(reinterpret_cast<const char*>(newTextData.data()), newTextData.size());
spdlog::info("Injected code into existing .text section space (size unchanged: 0x{:x})", originalTextSize);
// Save the modified PE file to output path
spdlog::info("Saving patched PE file to: {}", outputFile);
if (!peReader.save(outputFile)) {
spdlog::error("Failed to save patched PE file to: {}", outputFile);
return 1;
}
spdlog::info("Successfully patched PE file! Main function injected at VA: 0x{:x}", injectionVA);
return 0;
}