diff --git a/patcher/patcher.cpp b/patcher/patcher.cpp index bcc4e0e8..d64a22ea 100644 --- a/patcher/patcher.cpp +++ b/patcher/patcher.cpp @@ -117,34 +117,32 @@ struct Patcher { ptr--; } - // Keep 1 byte marging, might be another string or terminator - return availableSpace - 1; + // Keep 1 byte margin, might be another string or terminator + return availableSpace > 0 ? availableSpace - 1 : 0; } - void logMainFunctionCode() { - auto mainCodeData = mainSection->get_data(); - spdlog::info("Found main function at offset {} with size {}", mainOffset, - mainSize); + void logMainFunctionCode(COFFI::section *objMainSection, uint32_t functionOffset, uint32_t functionSize) { + auto mainCodeData = objMainSection->get_data(); + spdlog::info("Found main function at offset {} with size {}", functionOffset, functionSize); spdlog::info("Main function code:"); std::string s; - for (uint32_t i = 0; i < mainSize; i++) { + for (uint32_t i = 0; i < functionSize; i++) { if (i > 0 && i % 16 == 0) { spdlog::info("{}", s); s.clear(); } if (s.size() > 0) s += " "; - s += fmt::format("{:02X}", mainCodeData[i]); + s += fmt::format("{:02X}", mainCodeData[functionOffset + i]); } if (s.size() > 0) spdlog::info("{}", s); } - void logRelocations() { - auto §ionRelocations = mainSection->get_relocations(); - spdlog::info("Relocations for section {} (containing main function):", - mainSymbol->get_section_number()); + void logRelocations(COFFI::section *objMainSection, uint32_t symbolSectionNumber) { + auto §ionRelocations = objMainSection->get_relocations(); + spdlog::info("Relocations for section {} (containing main function):", symbolSectionNumber); for (auto &reloc : sectionRelocations) { spdlog::info(" Relocation at offset 0x{:x}, type: {}, symbol index: {}", reloc.get_virtual_address(), reloc.get_type(), @@ -182,8 +180,8 @@ struct Patcher { return true; } - // Tries to find an import in the input PE file and return its IAT address - uint32_t findImportInPE(const std::string &functionName) { + // Returns the VA of the import address table entry for the specified function + uint64_t findImportVA(const std::string &functionName) { auto &directories = peReader.get_directories(); // Import table is directory entry 1 @@ -192,29 +190,28 @@ struct Patcher { return 0; } - uint32_t importRVA = directories[1]->get_virtual_address(); - uint32_t importSize = directories[1]->get_size(); + uint32_t importTableRVA = directories[1]->get_virtual_address(); + uint32_t importTableSize = directories[1]->get_size(); - if (importRVA == 0 || importSize == 0) { + if (importTableRVA == 0 || importTableSize == 0) { spdlog::error("Invalid import directory"); return 0; } - spdlog::info("Import directory at RVA: 0x{:x}, size: 0x{:x}", importRVA, - importSize); + spdlog::info("Import directory at RVA: 0x{:x}, size: 0x{:x}", importTableRVA, importTableSize); // Find which section contains the import table COFFI::section *importSection = nullptr; - uint32_t importFileOffset = 0; + uint32_t importSectionOffset = 0; auto §ions = peReader.get_sections(); for (auto §ion : sections) { uint32_t sectionRVA = section->get_virtual_address(); uint32_t sectionSize = section->get_virtual_size(); - if (importRVA >= sectionRVA && importRVA < sectionRVA + sectionSize) { + if (importTableRVA >= sectionRVA && importTableRVA < sectionRVA + sectionSize) { importSection = section; - importFileOffset = importRVA - sectionRVA; + importSectionOffset = importTableRVA - sectionRVA; break; } } @@ -226,7 +223,7 @@ struct Patcher { // Parse import descriptors const char *sectionData = importSection->get_data(); - const char *importData = sectionData + importFileOffset; + const char *importData = sectionData + importSectionOffset; // Import descriptor structure (20 bytes each) struct ImportDescriptor { @@ -247,14 +244,14 @@ struct Patcher { spdlog::info("Checking imports from DLL: {}", dllName); // Parse the import lookup table - uint32_t lookupRVA = desc[i].originalFirstThunk; + uint32_t lookupTableRVA = desc[i].originalFirstThunk; uint32_t iatRVA = desc[i].firstThunk; - if (lookupRVA == 0) - lookupRVA = iatRVA; // Use IAT if no lookup table + if (lookupTableRVA == 0) + lookupTableRVA = iatRVA; // Use IAT if no lookup table const uint32_t *lookupTable = - reinterpret_cast(getRVAData(lookupRVA)); + reinterpret_cast(getRVAData(lookupTableRVA)); const uint32_t *iatTable = reinterpret_cast(getRVAData(iatRVA)); @@ -277,10 +274,11 @@ struct Patcher { std::string funcName(funcNamePtr); if (funcName == functionName) { - uint32_t iatAddress = imageBase + iatRVA + (j * sizeof(uint32_t)); - spdlog::info("Found import '{}' in {} at IAT address: 0x{:x}", - functionName, dllName, iatAddress); - return iatAddress; + uint32_t iatEntryRVA = iatRVA + (j * sizeof(uint32_t)); + uint64_t iatEntryVA = imageBase + iatEntryRVA; + spdlog::info("Found import '{}' in {} at IAT RVA: 0x{:x} (VA: 0x{:x})", + functionName, dllName, iatEntryRVA, iatEntryVA); + return iatEntryVA; } } } @@ -289,35 +287,35 @@ struct Patcher { return 0; } - bool processRelocations(char *textStart, char *textEnd, char *&rdataPtr) { + bool processRelocations(uint32_t injectedCodeRVA, uint32_t injectedCodeSize, + COFFI::section *targetTextSection, COFFI::section *targetRdataSection, + COFFI::section *objMainSection, uint32_t &rdataUsedBytes) { auto &symbols = *objReader.get_symbols(); - auto §ions = objReader.get_sections(); - auto §ionRelocations = mainSection->get_relocations(); - uint32_t rdataSize = rdataSection->get_data_size(); - char *textSectionStart = const_cast(textSection->get_data()); - const char *textSectionEnd = - textSectionStart + textSection->get_data_size(); - const char *rdataEndPtr = rdataSection->get_data() + rdataSize; - char *rdataStart = const_cast(rdataSection->get_data()); - // char* rdataEndPtr = rdataPtr + rdataSize; + auto &objSections = objReader.get_sections(); + auto §ionRelocations = objMainSection->get_relocations(); + + char *textSectionData = const_cast(targetTextSection->get_data()); + char *rdataSectionData = const_cast(targetRdataSection->get_data()); + uint32_t rdataAvailableSpace = findAvailableSpaceAtEnd(targetRdataSection); + spdlog::info("Found {} bytes of available space in .rdata section", rdataAvailableSpace); + + uint32_t rdataStartOffset = targetRdataSection->get_data_size() - rdataAvailableSpace; + char *rdataPtr = rdataSectionData + rdataStartOffset; + const char *rdataEndPtr = rdataSectionData + targetRdataSection->get_data_size(); - size_t textStartRVA = - (textStart - textSectionStart) + textSection->get_virtual_address(); - size_t textEndRVA = - (textEnd - textSectionStart) + textSection->get_virtual_address(); - - spdlog::info("Processing relocations..."); + spdlog::info("Processing relocations for injected code at RVA 0x{:x}...", injectedCodeRVA); for (auto &reloc : sectionRelocations) { if (reloc.get_type() != 6) continue; // Only handle type 6 (IMAGE_REL_I386_DIR32) - uint32_t relocRVA = reloc.get_virtual_address() + textStartRVA; + uint32_t relocOffsetInCode = reloc.get_virtual_address(); + uint32_t relocRVA = injectedCodeRVA + relocOffsetInCode; std::string symbolName = reloc.get_symbol(); - uint32_t resolvedAddress = 0; + uint64_t resolvedVA = 0; - spdlog::info("Processing relocation at VA 0x{:x} for symbol: {}", - relocRVA + imageBase, symbolName); + spdlog::info("Processing relocation at code offset 0x{:x} (RVA 0x{:x}, VA 0x{:x}) for symbol: {}", + relocOffsetInCode, relocRVA, imageBase + relocRVA, symbolName); if (symbolName.starts_with("__imp__")) { // Import symbol handling @@ -327,7 +325,7 @@ struct Patcher { functionName = functionName.substr(0, atPos); } spdlog::info("Looking for import function: {}", functionName); - resolvedAddress = findImportInPE(functionName); + resolvedVA = findImportVA(functionName); } else if (symbolName.starts_with("??_C@")) { // String constant handling @@ -340,7 +338,7 @@ struct Patcher { } if (constSymbol) { - auto constSection = sections[constSymbol->get_section_number() - 1]; + auto constSection = objSections[constSymbol->get_section_number() - 1]; auto constData = constSection->get_data(); uint32_t constOffset = constSymbol->get_value(); @@ -356,35 +354,31 @@ struct Patcher { // Copy string to .rdata std::memcpy(rdataPtr, constData + constOffset, stringLen); - size_t rdataRVA = - (rdataPtr - rdataStart) + rdataSection->get_virtual_address(); - resolvedAddress = imageBase + rdataRVA; + uint32_t stringRVA = (rdataPtr - rdataSectionData) + targetRdataSection->get_virtual_address(); + resolvedVA = imageBase + stringRVA; - spdlog::info("Copied string constant '{}' to .rdata at RVA 0x{:x} " - "(VA: 0x{:x})", - std::string(constData + constOffset), rdataRVA, - resolvedAddress); + spdlog::info("Copied string constant '{}' to .rdata at RVA 0x{:x} (VA: 0x{:x})", + std::string(constData + constOffset), stringRVA, resolvedVA); rdataPtr += stringLen; + rdataUsedBytes += stringLen; } } // Apply the relocation - if (resolvedAddress != 0) { - uint32_t patchOffset = relocRVA - textSection->get_virtual_address(); - uint32_t *ptr = - reinterpret_cast(textSectionStart + patchOffset); - ptr[0] = resolvedAddress; - spdlog::info( - "Applied relocation: patched offset 0x{:x} with address 0x{:x}", - relocRVA + imageBase, resolvedAddress); + if (resolvedVA != 0) { + uint32_t patchOffset = relocRVA - targetTextSection->get_virtual_address(); + uint32_t *patchPtr = reinterpret_cast(textSectionData + patchOffset); + *patchPtr = static_cast(resolvedVA); // Store the resolved VA + spdlog::info("Applied relocation: patched RVA 0x{:x} (VA 0x{:x}) with VA 0x{:x}", + relocRVA, imageBase + relocRVA, resolvedVA); } } return true; } - bool redirectEntryPoint(uint64_t dstVA) { + bool redirectEntryPoint(uint32_t targetCodeRVA) { // Patch the entry point to jump to our injected code auto optionalHeader = peReader.get_optional_header(); if (!optionalHeader) { @@ -394,13 +388,14 @@ struct Patcher { uint32_t entryPointRVA = optionalHeader->get_entry_point_address(); uint64_t entryPointVA = imageBase + entryPointRVA; + uint64_t targetCodeVA = imageBase + targetCodeRVA; spdlog::info("Entry point at RVA: 0x{:x} (VA: 0x{:x})", entryPointRVA, entryPointVA); - spdlog::info("Target injection at VA: 0x{:x}", dstVA); + spdlog::info("Target injection at RVA: 0x{:x} (VA: 0x{:x})", targetCodeRVA, targetCodeVA); // Find which section contains the entry point COFFI::section* entrySection = nullptr; - uint32_t entryFileOffset = 0; + uint32_t entrySectionOffset = 0; auto& sections = peReader.get_sections(); for (auto& section : sections) { @@ -409,7 +404,7 @@ struct Patcher { if (entryPointRVA >= sectionRVA && entryPointRVA < sectionRVA + sectionSize) { entrySection = section; - entryFileOffset = entryPointRVA - sectionRVA; + entrySectionOffset = entryPointRVA - sectionRVA; break; } } @@ -422,13 +417,13 @@ struct Patcher { // Calculate relative jump offset // jmp rel32 instruction: E9 xx xx xx xx (5 bytes) // offset = target_address - (current_address + 5) - int32_t jumpOffset = static_cast(dstVA - (entryPointVA + 5)); + int32_t jumpOffset = static_cast(targetCodeVA - (entryPointVA + 5)); spdlog::info("Patching entry point with jmp instruction (offset: 0x{:x})", static_cast(jumpOffset)); // Patch the entry point with jmp instruction - char* entryData = const_cast(entrySection->get_data()) + entryFileOffset; + char* entryData = const_cast(entrySection->get_data()) + entrySectionOffset; entryData[0] = static_cast(0xE9); // jmp rel32 opcode *reinterpret_cast(&entryData[1]) = jumpOffset; @@ -439,61 +434,40 @@ struct Patcher { bool patchAndSave() { // Check available space in both sections uint32_t textAvailableSpace = findAvailableSpaceAtEnd(textSection); - uint32_t rdataAvailableSpace = findAvailableSpaceAtEnd(rdataSection); - spdlog::info("Found {} bytes of available space (null bytes) at end of " - ".text section", - textAvailableSpace); - spdlog::info("Found {} bytes of available space in .rdata section", - rdataAvailableSpace); + spdlog::info("Found {} bytes of available space (null bytes) at end of .text section", textAvailableSpace); if (textAvailableSpace < mainSize) { - spdlog::error( - "Not enough space in .text section! Need {} bytes, found {} bytes", - mainSize, textAvailableSpace); + spdlog::error("Not enough space in .text section! Need {} bytes, found {} bytes", mainSize, textAvailableSpace); return false; } // Calculate injection points - uint32_t textSize = textSection->get_data_size(); - uint32_t rdataSize = rdataSection->get_data_size(); - uint32_t textInjectionOffset = textSize - textAvailableSpace; - uint32_t rdataInjectionOffset = rdataSize - rdataAvailableSpace; + uint32_t textSectionSize = textSection->get_data_size(); + uint32_t textInjectionOffset = textSectionSize - textAvailableSpace; + uint32_t injectionRVA = textSection->get_virtual_address() + textInjectionOffset; - uint64_t textInjectionRVA = - textSection->get_virtual_address() + textInjectionOffset; - uint64_t rdataInjectionRVA = - rdataSection->get_virtual_address() + rdataInjectionOffset; + spdlog::info("Injecting {} bytes at .text section offset 0x{:x} (RVA: 0x{:x}, VA: 0x{:x})", + mainSize, textInjectionOffset, injectionRVA, imageBase + injectionRVA); - spdlog::info( - "Injecting {} bytes at .text section offset 0x{:x} (VA: 0x{:x})", - mainSize, textInjectionOffset, textInjectionRVA + imageBase); + // Copy main function code to injection point + char *textSectionData = const_cast(textSection->get_data()); + char *injectionPtr = textSectionData + textInjectionOffset; + const uint8_t *mainCode = reinterpret_cast(mainSection->get_data()) + mainOffset; + std::memcpy(injectionPtr, mainCode, mainSize); - // Create copies of section data - auto textSectionData = textSection->get_data(); - auto rdataData = rdataSection->get_data(); - - char *dstText = const_cast(textSectionData) + textInjectionOffset; - char *dstRdata = const_cast(rdataData) + rdataInjectionOffset; - - // Copy main function code - const uint8_t *mainCode = - reinterpret_cast(mainSection->get_data()) + mainOffset; - std::memcpy(dstText, mainCode, mainSize); - - spdlog::info("Injected code into existing .text section space (size " - "unchanged: 0x{:x})", - textSize); + spdlog::info("Injected code into existing .text section space (size unchanged: 0x{:x})", textSectionSize); // Process relocations - char *rdataPtr = dstRdata; - if (!processRelocations(dstText, dstText + mainSize, rdataPtr)) { + uint32_t rdataUsedBytes = 0; + if (!processRelocations(injectionRVA, mainSize, textSection, rdataSection, mainSection, rdataUsedBytes)) { return false; } + spdlog::info("Used {} bytes in .rdata section for string constants", rdataUsedBytes); + // Patch the entry point to jump to our injected code - uint64_t targetVA = textInjectionRVA + imageBase; - if (!redirectEntryPoint(targetVA)) { + if (!redirectEntryPoint(injectionRVA)) { return false; } @@ -504,9 +478,8 @@ struct Patcher { return false; } - spdlog::info( - "Successfully patched PE file! Main function injected at VA: 0x{:x}", - textInjectionRVA + imageBase); + spdlog::info("Successfully patched PE file! Main function injected at RVA: 0x{:x} (VA: 0x{:x})", + injectionRVA, imageBase + injectionRVA); return true; } @@ -517,8 +490,8 @@ struct Patcher { return false; calculateMainSize(); - logMainFunctionCode(); - logRelocations(); + logMainFunctionCode(mainSection, mainOffset, mainSize); + logRelocations(mainSection, mainSymbol->get_section_number()); if (!validateSections()) return false;