Support for MemoryModule recognition and debugging via virtualmod.
- Fixed some bugs in virtualmod: loadedSize was never set + the mapped data would be destroyed prematurely, causing a crash after initially parsing the module.
- Use raw RVA instead of rebasing when reading module information from memory.
- Set MappedAsImage = true when calling RtlImageDirectoryEntryToData on in-memory sections, as expected.
- This works out of the box for some basic stuff (e.g. symbols, imports, exports are loaded properly) but for full functionality a fix is needed in MemoryModule (see 10250ad4b7
). TL;DR: VirtualSize field in the section header is not properly set by MemoryModule, leading to sections not being parsed properly during page sorting, leading to an error spam and some other annoying behaviour.
This commit is contained in:
parent
17584fcbd6
commit
aa0a5cd82c
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue