#include #include #include #include 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 §ions = 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 §ion : 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(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(mainCodeData) + mainOffset; // Create a copy of the section data to modify std::vector newTextData(reinterpret_cast(textSectionData), reinterpret_cast(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(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; }