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
ULONG exportDirSize;
auto exportDir = (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData((PVOID)FileMapVA,
FALSE,
Info.isVirtual,
IMAGE_DIRECTORY_ENTRY_EXPORT,
&exportDirSize);
if(exportDirSize == 0 || exportDir == nullptr ||
@ -97,7 +97,7 @@ static void ReadExportDirectory(MODINFO & Info, ULONG_PTR FileMapVA)
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);
@ -139,7 +139,7 @@ static void ReadExportDirectory(MODINFO & Info, ULONG_PTR FileMapVA)
auto & entry = Info.exports.back();
entry.ordinal = i + exportDir->Base;
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;
if(entry.forwarded)
{
@ -227,7 +227,7 @@ static void ReadImportDirectory(MODINFO & Info, ULONG_PTR FileMapVA)
// Get the import directory and its size
ULONG importDirSize;
auto importDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)RtlImageDirectoryEntryToData((PVOID)FileMapVA,
FALSE,
Info.isVirtual,
IMAGE_DIRECTORY_ENTRY_IMPORT,
&importDirSize);
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;
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)
@ -325,7 +325,7 @@ static void ReadTlsCallbacks(MODINFO & Info, ULONG_PTR FileMapVA)
// Get the TLS directory
ULONG tlsDirSize;
auto tlsDir = (PIMAGE_TLS_DIRECTORY)RtlImageDirectoryEntryToData((PVOID)FileMapVA,
FALSE,
Info.isVirtual,
IMAGE_DIRECTORY_ENTRY_TLS,
&tlsDirSize);
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;
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)
return;
@ -370,7 +371,7 @@ static void ReadBaseRelocationTable(MODINFO & Info, ULONG_PTR FileMapVA)
// Get address and size of base relocation table
ULONG totalBytes;
auto baseRelocBlock = (PIMAGE_BASE_RELOCATION)RtlImageDirectoryEntryToData((PVOID)FileMapVA,
FALSE,
Info.isVirtual,
IMAGE_DIRECTORY_ENTRY_BASERELOC,
&totalBytes);
if(baseRelocBlock == nullptr || totalBytes == 0 ||
@ -442,7 +443,7 @@ static void ReadDebugDirectory(MODINFO & Info, ULONG_PTR FileMapVA)
// Get the debug directory and its size
ULONG debugDirSize;
auto debugDir = (PIMAGE_DEBUG_DIRECTORY)RtlImageDirectoryEntryToData((PVOID)FileMapVA,
FALSE,
Info.isVirtual,
IMAGE_DIRECTORY_ENTRY_DEBUG,
&debugDirSize);
if(debugDirSize == 0 || debugDir == nullptr ||
@ -474,12 +475,12 @@ static void ReadDebugDirectory(MODINFO & Info, ULONG_PTR FileMapVA)
BYTE PdbFileName[1];
};
const auto supported = [&Info](PIMAGE_DEBUG_DIRECTORY entry)
const auto supported = [&Info, FileMapVA](PIMAGE_DEBUG_DIRECTORY entry)
{
// Check for valid RVA
ULONG_PTR offset = 0;
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)
offset = entry->PointerToRawData;
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)?
{
// 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')
return entry->SizeOfData >= sizeof(CV_INFO_PDB20) && entry->SizeOfData < sizeof(CV_INFO_PDB20) + DOS_MAX_PATH_LENGTH;
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
ULONG_PTR offset = 0;
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)
offset = entry->PointerToRawData;
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
ULONG totalBytes;
auto baseRuntimeFunctions = (PRUNTIME_FUNCTION)RtlImageDirectoryEntryToData((PVOID)FileMapVA,
FALSE,
Info.isVirtual,
IMAGE_DIRECTORY_ENTRY_EXCEPTION,
&totalBytes);
if(baseRuntimeFunctions == nullptr || totalBytes == 0 ||
@ -854,9 +855,9 @@ bool ModLoad(duint Base, duint Size, const char* FullPath, bool loadSymbols)
}
// 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);
@ -879,12 +880,13 @@ bool ModLoad(duint Base, duint Size, const char* FullPath, bool loadSymbols)
else
{
// This was a virtual module -> read it remotely
Memory<unsigned char*> data(Size);
MemRead(Base, data(), data.size());
info.mappedData.realloc(Size);
MemRead(Base, info.mappedData(), info.mappedData.size());
// Get information from the local buffer
// 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
@ -904,7 +906,7 @@ bool ModLoad(duint Base, duint Size, const char* FullPath, bool loadSymbols)
EXCLUSIVE_RELEASE();
// Put labels for virtual module exports
if(virtualModule)
if(info.isVirtual)
{
if(info.entry >= Base && info.entry < Base + Size)
LabelSet(info.entry, "EntryPoint", false, true);

View File

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