Patcher
This commit is contained in:
parent
3537c91324
commit
a8143e6ac0
|
@ -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<const uint32_t *>(getRVAData(lookupRVA));
|
||||
reinterpret_cast<const uint32_t *>(getRVAData(lookupTableRVA));
|
||||
const uint32_t *iatTable =
|
||||
reinterpret_cast<const uint32_t *>(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<char *>(textSection->get_data());
|
||||
const char *textSectionEnd =
|
||||
textSectionStart + textSection->get_data_size();
|
||||
const char *rdataEndPtr = rdataSection->get_data() + rdataSize;
|
||||
char *rdataStart = const_cast<char *>(rdataSection->get_data());
|
||||
// char* rdataEndPtr = rdataPtr + rdataSize;
|
||||
auto &objSections = objReader.get_sections();
|
||||
auto §ionRelocations = objMainSection->get_relocations();
|
||||
|
||||
char *textSectionData = const_cast<char *>(targetTextSection->get_data());
|
||||
char *rdataSectionData = const_cast<char *>(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<uint32_t *>(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<uint32_t *>(textSectionData + patchOffset);
|
||||
*patchPtr = static_cast<uint32_t>(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<int32_t>(dstVA - (entryPointVA + 5));
|
||||
int32_t jumpOffset = static_cast<int32_t>(targetCodeVA - (entryPointVA + 5));
|
||||
|
||||
spdlog::info("Patching entry point with jmp instruction (offset: 0x{:x})",
|
||||
static_cast<uint32_t>(jumpOffset));
|
||||
|
||||
// Patch the entry point with jmp instruction
|
||||
char* entryData = const_cast<char*>(entrySection->get_data()) + entryFileOffset;
|
||||
char* entryData = const_cast<char*>(entrySection->get_data()) + entrySectionOffset;
|
||||
entryData[0] = static_cast<char>(0xE9); // jmp rel32 opcode
|
||||
*reinterpret_cast<int32_t*>(&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<char *>(textSection->get_data());
|
||||
char *injectionPtr = textSectionData + textInjectionOffset;
|
||||
const uint8_t *mainCode = reinterpret_cast<const uint8_t *>(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<char *>(textSectionData) + textInjectionOffset;
|
||||
char *dstRdata = const_cast<char *>(rdataData) + rdataInjectionOffset;
|
||||
|
||||
// Copy main function code
|
||||
const uint8_t *mainCode =
|
||||
reinterpret_cast<const uint8_t *>(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;
|
||||
|
|
Loading…
Reference in New Issue