1
0
Fork 0

Merge pull request #3131 from ynwarcs/virtualmod_memorymodule_support

Support for MemoryModule recognition and debugging via virtualmod.
This commit is contained in:
Duncan Ogilvie 2023-07-13 11:27:51 +02:00 committed by GitHub
commit 9746dee02f
Signed by: GitHub
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 24 additions and 20 deletions

View File

@ -82,7 +82,7 @@ static void ReadExportDirectory(MODINFO & Info, ULONG_PTR FileMapVA)
// Get the export directory and its size // Get the export directory and its size
ULONG exportDirSize; ULONG exportDirSize;
auto exportDir = (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData((PVOID)FileMapVA, auto exportDir = (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData((PVOID)FileMapVA,
FALSE, Info.isVirtual,
IMAGE_DIRECTORY_ENTRY_EXPORT, IMAGE_DIRECTORY_ENTRY_EXPORT,
&exportDirSize); &exportDirSize);
if(exportDirSize == 0 || exportDir == nullptr || if(exportDirSize == 0 || exportDir == nullptr ||
@ -97,7 +97,7 @@ static void ReadExportDirectory(MODINFO & Info, ULONG_PTR FileMapVA)
auto rva2offset = [&Info](ULONG64 rva) auto rva2offset = [&Info](ULONG64 rva)
{ {
return ModRvaToOffset(0, Info.headers, rva); return Info.isVirtual ? rva : ModRvaToOffset(0, Info.headers, rva);
}; };
auto addressOfFunctionsOffset = rva2offset(exportDir->AddressOfFunctions); auto addressOfFunctionsOffset = rva2offset(exportDir->AddressOfFunctions);
@ -139,7 +139,7 @@ static void ReadExportDirectory(MODINFO & Info, ULONG_PTR FileMapVA)
auto & entry = Info.exports.back(); auto & entry = Info.exports.back();
entry.ordinal = i + exportDir->Base; entry.ordinal = i + exportDir->Base;
entry.rva = addressOfFunctions[i]; entry.rva = addressOfFunctions[i];
const auto entryVa = ModRvaToOffset(FileMapVA, Info.headers, entry.rva); const auto entryVa = Info.isVirtual ? entry.rva : ModRvaToOffset(FileMapVA, Info.headers, entry.rva);
entry.forwarded = entryVa >= (ULONG64)exportDir && entryVa < (ULONG64)exportDir + exportDirSize; entry.forwarded = entryVa >= (ULONG64)exportDir && entryVa < (ULONG64)exportDir + exportDirSize;
if(entry.forwarded) if(entry.forwarded)
{ {
@ -227,7 +227,7 @@ static void ReadImportDirectory(MODINFO & Info, ULONG_PTR FileMapVA)
// Get the import directory and its size // Get the import directory and its size
ULONG importDirSize; ULONG importDirSize;
auto importDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)RtlImageDirectoryEntryToData((PVOID)FileMapVA, auto importDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)RtlImageDirectoryEntryToData((PVOID)FileMapVA,
FALSE, Info.isVirtual,
IMAGE_DIRECTORY_ENTRY_IMPORT, IMAGE_DIRECTORY_ENTRY_IMPORT,
&importDirSize); &importDirSize);
if(importDirSize == 0 || importDescriptor == nullptr || if(importDirSize == 0 || importDescriptor == nullptr ||
@ -238,7 +238,7 @@ static void ReadImportDirectory(MODINFO & Info, ULONG_PTR FileMapVA)
const ULONG64 ordinalFlag = IMAGE64(Info.headers) ? IMAGE_ORDINAL_FLAG64 : IMAGE_ORDINAL_FLAG32; const ULONG64 ordinalFlag = IMAGE64(Info.headers) ? IMAGE_ORDINAL_FLAG64 : IMAGE_ORDINAL_FLAG32;
auto rva2offset = [&Info](ULONG64 rva) auto rva2offset = [&Info](ULONG64 rva)
{ {
return ModRvaToOffset(0, Info.headers, rva); return Info.isVirtual ? rva : ModRvaToOffset(0, Info.headers, rva);
}; };
for(size_t moduleIndex = 0; importDescriptor->Name != 0; ++importDescriptor, ++moduleIndex) for(size_t moduleIndex = 0; importDescriptor->Name != 0; ++importDescriptor, ++moduleIndex)
@ -325,7 +325,7 @@ static void ReadTlsCallbacks(MODINFO & Info, ULONG_PTR FileMapVA)
// Get the TLS directory // Get the TLS directory
ULONG tlsDirSize; ULONG tlsDirSize;
auto tlsDir = (PIMAGE_TLS_DIRECTORY)RtlImageDirectoryEntryToData((PVOID)FileMapVA, auto tlsDir = (PIMAGE_TLS_DIRECTORY)RtlImageDirectoryEntryToData((PVOID)FileMapVA,
FALSE, Info.isVirtual,
IMAGE_DIRECTORY_ENTRY_TLS, IMAGE_DIRECTORY_ENTRY_TLS,
&tlsDirSize); &tlsDirSize);
if(tlsDir == nullptr /*|| tlsDirSize == 0*/ || // The loader completely ignores the directory size. Setting it to 0 is an anti-debug trick if(tlsDir == nullptr /*|| tlsDirSize == 0*/ || // The loader completely ignores the directory size. Setting it to 0 is an anti-debug trick
@ -340,7 +340,8 @@ static void ReadTlsCallbacks(MODINFO & Info, ULONG_PTR FileMapVA)
return; return;
auto imageBase = HEADER_FIELD(Info.headers, ImageBase); auto imageBase = HEADER_FIELD(Info.headers, ImageBase);
auto tlsArrayOffset = ModRvaToOffset(0, Info.headers, tlsDir->AddressOfCallBacks - imageBase); auto rva = tlsDir->AddressOfCallBacks - imageBase;
auto tlsArrayOffset = Info.isVirtual ? rva : ModRvaToOffset(0, Info.headers, rva);
if(!tlsArrayOffset) if(!tlsArrayOffset)
return; return;
@ -370,7 +371,7 @@ static void ReadBaseRelocationTable(MODINFO & Info, ULONG_PTR FileMapVA)
// Get address and size of base relocation table // Get address and size of base relocation table
ULONG totalBytes; ULONG totalBytes;
auto baseRelocBlock = (PIMAGE_BASE_RELOCATION)RtlImageDirectoryEntryToData((PVOID)FileMapVA, auto baseRelocBlock = (PIMAGE_BASE_RELOCATION)RtlImageDirectoryEntryToData((PVOID)FileMapVA,
FALSE, Info.isVirtual,
IMAGE_DIRECTORY_ENTRY_BASERELOC, IMAGE_DIRECTORY_ENTRY_BASERELOC,
&totalBytes); &totalBytes);
if(baseRelocBlock == nullptr || totalBytes == 0 || if(baseRelocBlock == nullptr || totalBytes == 0 ||
@ -442,7 +443,7 @@ static void ReadDebugDirectory(MODINFO & Info, ULONG_PTR FileMapVA)
// Get the debug directory and its size // Get the debug directory and its size
ULONG debugDirSize; ULONG debugDirSize;
auto debugDir = (PIMAGE_DEBUG_DIRECTORY)RtlImageDirectoryEntryToData((PVOID)FileMapVA, auto debugDir = (PIMAGE_DEBUG_DIRECTORY)RtlImageDirectoryEntryToData((PVOID)FileMapVA,
FALSE, Info.isVirtual,
IMAGE_DIRECTORY_ENTRY_DEBUG, IMAGE_DIRECTORY_ENTRY_DEBUG,
&debugDirSize); &debugDirSize);
if(debugDirSize == 0 || debugDir == nullptr || if(debugDirSize == 0 || debugDir == nullptr ||
@ -474,12 +475,12 @@ static void ReadDebugDirectory(MODINFO & Info, ULONG_PTR FileMapVA)
BYTE PdbFileName[1]; BYTE PdbFileName[1];
}; };
const auto supported = [&Info](PIMAGE_DEBUG_DIRECTORY entry) const auto supported = [&Info, FileMapVA](PIMAGE_DEBUG_DIRECTORY entry)
{ {
// Check for valid RVA // Check for valid RVA
ULONG_PTR offset = 0; ULONG_PTR offset = 0;
if(entry->AddressOfRawData) if(entry->AddressOfRawData)
offset = (ULONG_PTR)ModRvaToOffset(0, Info.headers, entry->AddressOfRawData); offset = Info.isVirtual ? entry->AddressOfRawData : (ULONG_PTR)ModRvaToOffset(0, Info.headers, entry->AddressOfRawData);
else if(entry->PointerToRawData) else if(entry->PointerToRawData)
offset = entry->PointerToRawData; offset = entry->PointerToRawData;
if(!offset) if(!offset)
@ -494,7 +495,7 @@ static void ReadDebugDirectory(MODINFO & Info, ULONG_PTR FileMapVA)
if(entry->Type == IMAGE_DEBUG_TYPE_CODEVIEW) // TODO: support other types (DBG)? if(entry->Type == IMAGE_DEBUG_TYPE_CODEVIEW) // TODO: support other types (DBG)?
{ {
// Get the CV signature and do a final size check if it is valid // Get the CV signature and do a final size check if it is valid
auto signature = *(DWORD*)(Info.fileMapVA + offset); auto signature = *(DWORD*)(FileMapVA + offset);
if(signature == '01BN') if(signature == '01BN')
return entry->SizeOfData >= sizeof(CV_INFO_PDB20) && entry->SizeOfData < sizeof(CV_INFO_PDB20) + DOS_MAX_PATH_LENGTH; return entry->SizeOfData >= sizeof(CV_INFO_PDB20) && entry->SizeOfData < sizeof(CV_INFO_PDB20) + DOS_MAX_PATH_LENGTH;
else if(signature == 'SDSR') else if(signature == 'SDSR')
@ -576,7 +577,7 @@ static void ReadDebugDirectory(MODINFO & Info, ULONG_PTR FileMapVA)
// At this point we know the entry is a valid CV one // At this point we know the entry is a valid CV one
ULONG_PTR offset = 0; ULONG_PTR offset = 0;
if(entry->AddressOfRawData) if(entry->AddressOfRawData)
offset = (ULONG_PTR)ModRvaToOffset(0, Info.headers, entry->AddressOfRawData); offset = Info.isVirtual ? entry->AddressOfRawData : (ULONG_PTR)ModRvaToOffset(0, Info.headers, entry->AddressOfRawData);
else if(entry->PointerToRawData) else if(entry->PointerToRawData)
offset = entry->PointerToRawData; offset = entry->PointerToRawData;
auto cvData = (unsigned char*)(FileMapVA + offset); auto cvData = (unsigned char*)(FileMapVA + offset);
@ -667,7 +668,7 @@ static void ReadExceptionDirectory(MODINFO & Info, ULONG_PTR FileMapVA)
// Get address and size of exception directory // Get address and size of exception directory
ULONG totalBytes; ULONG totalBytes;
auto baseRuntimeFunctions = (PRUNTIME_FUNCTION)RtlImageDirectoryEntryToData((PVOID)FileMapVA, auto baseRuntimeFunctions = (PRUNTIME_FUNCTION)RtlImageDirectoryEntryToData((PVOID)FileMapVA,
FALSE, Info.isVirtual,
IMAGE_DIRECTORY_ENTRY_EXCEPTION, IMAGE_DIRECTORY_ENTRY_EXCEPTION,
&totalBytes); &totalBytes);
if(baseRuntimeFunctions == nullptr || totalBytes == 0 || if(baseRuntimeFunctions == nullptr || totalBytes == 0 ||
@ -854,9 +855,9 @@ bool ModLoad(duint Base, duint Size, const char* FullPath, bool loadSymbols)
} }
// Load module data // Load module data
bool virtualModule = strstr(FullPath, "virtual:\\") == FullPath; info.isVirtual = strstr(FullPath, "virtual:\\") == FullPath;
if(!virtualModule) if(!info.isVirtual)
{ {
auto wszFullPath = StringUtils::Utf8ToUtf16(FullPath); auto wszFullPath = StringUtils::Utf8ToUtf16(FullPath);
@ -879,12 +880,13 @@ bool ModLoad(duint Base, duint Size, const char* FullPath, bool loadSymbols)
else else
{ {
// This was a virtual module -> read it remotely // This was a virtual module -> read it remotely
Memory<unsigned char*> data(Size); info.mappedData.realloc(Size);
MemRead(Base, data(), data.size()); MemRead(Base, info.mappedData(), info.mappedData.size());
// Get information from the local buffer // Get information from the local buffer
// TODO: this does not properly work for file offset -> rva conversions (since virtual modules are SEC_IMAGE) // TODO: this does not properly work for file offset -> rva conversions (since virtual modules are SEC_IMAGE)
GetModuleInfo(info, (ULONG_PTR)data()); info.loadedSize = Size;
GetModuleInfo(info, (ULONG_PTR)info.mappedData());
} }
info.symbols = &EmptySymbolSource; // empty symbol source per default info.symbols = &EmptySymbolSource; // empty symbol source per default
@ -904,7 +906,7 @@ bool ModLoad(duint Base, duint Size, const char* FullPath, bool loadSymbols)
EXCLUSIVE_RELEASE(); EXCLUSIVE_RELEASE();
// Put labels for virtual module exports // Put labels for virtual module exports
if(virtualModule) if(info.isVirtual)
{ {
if(info.entry >= Base && info.entry < Base + Size) if(info.entry >= Base && info.entry < Base + Size)
LabelSet(info.entry, "EntryPoint", false, true); LabelSet(info.entry, "EntryPoint", false, true);

View File

@ -119,6 +119,8 @@ struct MODINFO
ULONG_PTR fileMapVA = 0; ULONG_PTR fileMapVA = 0;
MODULEPARTY party; // Party. Currently used value: 0: User, 1: System MODULEPARTY party; // Party. Currently used value: 0: User, 1: System
bool isVirtual = false;
Memory<unsigned char*> mappedData;
MODINFO() MODINFO()
{ {