I'm writing a WinDbg extension and I'm trying to get the list of loaded kernel modules, each with a version number and description. As far as I know, both are available in the PE file's resources.
So at first I tried the following, using IDebugSymbols3 interface:
//I'm not checking return error codes for brevity
// pDebugSymb is a pointer to IDebugSymbols3 that you can get by
// doing QueryInterface on IDebugClient
ULONG ncntLoaded, ncntUnloaded;
hr = pDebugSymb->GetNumberModules(&ncntLoaded, &ncntUnloaded);
for(ULONG m = 0; m < ncntLoaded; m++)
{
ULONG64 uiBase;
hr = pDebugSymb->GetModuleByIndex(m, &uiBase);
ULONG uichSz = 0;
hr = pDebugSymb->GetModuleVersionInformationWide(m, uiBase,
L"\\VarFileInfo\\Translation",
NULL, 0, &uichSz);
if(SUCCEEDED(hr))
{
//Continue on ...
}
else
{
Log_Error(L"hr=0x%x", hr);
}
}
This is just the part that fails.
What happens is that when I run it on a live x64 Windows 10 kernel target, GetModuleVersionInformationWide
returns either 0xD0000147
or 0x80070715
error for almost all modules.
I tried doing this first:
//Reload all modules
hr = pDebugSymb->ReloadWide(L"/s"); // same as .reload /s
but that didn't help either.
Then I decided to get to the resource section via the loaded PE files manually:
// pDataSpace = pointer to IDebugDataSpaces
LIST_ENTRY* psLoadedModuleList = NULL;
HRESULT hr = pDataSpace->ReadDebuggerData(DEBUG_DATA_PsLoadedModuleListAddr,
&psLoadedModuleList, sizeof(psLoadedModuleList), NULL);
LIST_ENTRY* pEntry = psLoadedModuleList;
for(;pEntry;)
{
LIST_ENTRY* pFlink;
ULONG uicbRead;
hr = pDataSpace->ReadVirtual((ULONG64)pEntry, &pFlink, sizeof(pFlink), &uicbRead);
assert(SUCCEEDED(hr));
pEntry = pFlink;
if(pEntry == psLoadedModuleList)
{
//Done
break;
}
KLDR_DATA_TABLE_ENTRY dte;
hr = pDataSpace->ReadVirtual((ULONG64)CONTAINING_RECORD(pEntry, KLDR_DATA_TABLE_ENTRY, InLoadOrderLinks),
&dte, sizeof(dte), &uicbRead);
assert(SUCCEEDED(hr));
void* pBaseAddr = dte.DllBase;
DWORD uicbSz = dte.SizeOfImage;
BYTE* pMem = new BYTE[uicbSz];
uicbRead = 0;
hr = pDataSpace->ReadVirtual((ULONG64)pBaseAddr, pMem, uicbSz, &uicbRead);
assert(SUCCEEDED(hr));
Log(L"mod_sz=0x%X, mod_read=0x%X", uicbSz, uicbRead);
delete[] pMem;
}
where:
typedef struct _KLDR_DATA_TABLE_ENTRY
{
LIST_ENTRY InLoadOrderLinks;
PVOID ExceptionTable;
ULONG ExceptionTableSize;
PVOID GpValue;
PNON_PAGED_DEBUG_INFO NonPagedDebugInfo;
PVOID DllBase;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
USHORT LoadCount;
USHORT __Unused5;
PVOID SectionPointer;
ULONG CheckSum;
PVOID LoadedImports;
PVOID PatchInformation;
} KLDR_DATA_TABLE_ENTRY, *PKLDR_DATA_TABLE_ENTRY;
but when I run my code above, again on the live x64 kernel target (Win10), I'm seeing that my mod_read
in the log is several KB less than mod_sz
for almost all modules.
Upon further research, it seems like the PE file sections that are marked with IMAGE_SCN_MEM_DISCARDABLE
are not present in memory. I'm still not sure why ReadVirtual
doesn't load them upon request though.
So any idea how to get to the version resources in those loaded modules?
(Apart from reading them from the actual files.)