Friday, July 31, 2026

Stranger in a strange land

Amazing discovery of the day: LoadTypeLibEx can load type info from a 32-bit DLL into a 64-bit process and vice versa.

If an app includes a piece of logic for reading a typelib from an arbitrary, user chosen file, you might want to be prepared to encounter one of different bitness. Fortunately, there is a marker; call GetLibAttr and check the syskind. UPDATE: syskind seems to be unreliable. Here I am, looking at a 64-bit build of ieframe.dll, but the syskind says Win32.

Incidentally, LoadLibraryEx with either AS_DATAFILE or AS_IMAGE_RESOURCE flag can load DLLs of differing bitness, too, and get resources from them - that's all that's needed for getting to the type info. Debugging shows, however, that LoadTypeLibEx uses neither of those, it loads the DLL as a data file. That might be because the typelib API (first shipped with 16-bit Word for Windows 6 in 1994 as a part of OLE 2) by far predates the availability of LoadLibraryEx (Windows XP, year 2003). Maybe that's because the typelib API also works with TLB and OLB files, and those are not DLLs (i. e. not PE files).


I could not find a ready made, API level marker for the bitness of a DLL loaded as data. Neither GetModuleInformation nor Module32First/Next return that directly. That said, either of those can return the base address of the module, and the base address is where the DLL file's DOS/PE headers are, and parsing those is not that hard. The DOS header starts at the module base address, and contains an offset to the NT header, and that one contains the architecture of the DLL. The check might go like this:

#include <psapi.h>
// ...
HINSTANCE hInst; // Handle of the loaded module
MODULEINFO mi;
GetModuleInformation(GetCurrentProcess(), hInst, &mi, sizeof mi);
const char *pBase = (const char*)mi.lpBaseOfDll;
const _IMAGE_DOS_HEADER* pDOSHeader = (const _IMAGE_DOS_HEADER*)pBase;
const _IMAGE_NT_HEADERS* pNTHeader = (const _IMAGE_NT_HEADERS*)(pBase + pDOSHeader->e_lfanew);
WORD Machine = pNTHeader->FileHeader.Machine;

And then match the machine type against codes in winnt.h. IMAGE_FILE_MACHINE_AMD64 means 64-bit Intel, IMAGE_FILE_MACHINE_I386 means 32-bit Intel, etc.

No comments:

Post a Comment