Jason Larke
Jason Larke

Reputation: 5609

Exported function forwarded to itself?

I ran into an extremely weird issue today when messing around with the parsing the Windows Portable Executable file structure today. Specifically in the Export table.

I found myself getting a Stack Overflow (so this seemed like the most appropriate QA board) when trying to resolve the function address of an Exported function in a DLL.

I've written my own version of GetProcAddress which does the parsing manually rather than calling the existing GetProcAddress method. Please don't just tell me to use the existing GetProcAddress method, it's not suitable for my current situation and I want to learn something from this.

For most of the situations I encounter, my version has worked admirably and hasn't hit any issues. However the function was tested against a DLL named API-MS-Win-Core-ProcessThreads-L1-1-0.dll (as part of a recursive parse of Kernel32.dll) and this is when the StackOverflow occurred.

I've narrowed it down to the following function exported from API-MS-Win-Core-ProcessThreads-L1-1-0.dll:

CreateRemoteThreadEx

Now, this exported function is actually a forwarded export. Usually this is no worries; I've written my function so that it should handle forwarded exports. However this function is forwarded to

api-ms-win-core-processthreads-l1-1-0.CreateRemoteThreadEx

Anyone seeing the problem here? Stepping through the code, my GetProcAddress function then calls LoadLibrary on api-ms-win-core-processthreads-l1-1-0 and then attempts to recursively lookup CreateRemoteThreadEx. On the very next iteration, however, the CreateRemoteThreadEx function is again forwarded... to

api-ms-win-core-processthreads-l1-1-0.CreateRemoteThreadEx

And so begins the StackOverflow. After a bit more investigation I found that the result of calling

LoadLibraryA("api-ms-win-core-processthreads-l1-1-0");

Returns the same result as

LoadLibraryA("kernel32.dll");

I'm stumped.

Here's my current code:

#include <Windows.h>

#define MKPTR(p1,p2) ((DWORD_PTR)(p1) + (DWORD_PTR)(p2))

INT LookupExport(IMAGE_DOS_HEADER* pDosHd, DWORD* pNames, DWORD nNames, LPCSTR lpProcName)
{
    // Do a binary search on the name pointer table
    INT start = 0, 
        index = -1,
        middle = -1, 
        end = nNames - 1,
        cmp = 0;

    CHAR *pName;

    while (start <= end && index == -1)
    {
        middle = (start + end) >> 1;
        pName = (CHAR*)MKPTR(pDosHd, pNames[middle]);

        if ((cmp = strcmp(pName, lpProcName)) == 0)
            index = middle; // found
        else if (cmp < 0)
            start = middle + 1;
        else
            end = middle;
    }

    return index;
}

FARPROC InternalGetProcAddress(HMODULE hModule, LPCSTR lpProcName)
{
    BOOL ordinalSearch = HIWORD(lpProcName) == 0;
    WORD ordinal = 0;
    IMAGE_DOS_HEADER *pDosHd = (IMAGE_DOS_HEADER*)hModule;

    if (pDosHd->e_magic != IMAGE_DOS_SIGNATURE)
        return NULL;

    IMAGE_NT_HEADERS *pNtHd = (IMAGE_NT_HEADERS*)MKPTR(pDosHd, pDosHd->e_lfanew);
    if (pNtHd->Signature != IMAGE_NT_SIGNATURE)
        return NULL;

    IMAGE_DATA_DIRECTORY directory = pNtHd->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
    if (directory.Size == 0 || directory.VirtualAddress == 0)
        return NULL;

    IMAGE_EXPORT_DIRECTORY *pExports = (IMAGE_EXPORT_DIRECTORY*)MKPTR(pDosHd, directory.VirtualAddress);
    if (!ordinalSearch)
    {
        INT index = LookupExport(pDosHd, (DWORD*)MKPTR(pDosHd, pExports->AddressOfNames), pExports->NumberOfNames, lpProcName);
        if (index == -1)
            return NULL;
        ordinal = ((WORD*)MKPTR(pDosHd, pExports->AddressOfNameOrdinals))[index];
    }
    else
    {
        ordinal = LOWORD(lpProcName);
    }

    INT delta = pExports->Base - 1;
    DWORD dwAddress = ((DWORD*)MKPTR(pDosHd, pExports->AddressOfFunctions))[ordinal - delta];
    // Check whether forwarded:
    if (dwAddress >= directory.VirtualAddress && dwAddress < (directory.VirtualAddress + directory.Size))
    {
        CHAR pForward[256];
        strcpy(pForward, (CHAR*)MKPTR(pDosHd, dwAddress));
        CHAR *pFunction = strchr(pForward, '.');
        if (pFunction == NULL)
            return NULL;

        // break into seperate parts and recurse
        *pFunction++ = 0;
        return InternalGetProcAddress(LoadLibraryA(pForward), pFunction);
    }

    return (FARPROC)MKPTR(hModule, dwAddress);
}

Any insight would be greatly appreciated.

Upvotes: 6

Views: 3159

Answers (2)

Elmue
Elmue

Reputation: 8138

The accepted answer worked once on Windows 7, but then Microsoft changed the structures in Windows 8 and completely again in Windows 10, where fast lookup hashes were added.

The following code works on Windows 10 and 11:

struct kHost
{
    DWORD Flags;
    DWORD ImportOffset;
    DWORD ImportSize;
    DWORD HostOffset;
    DWORD HostSize;
};
struct kHash
{
    DWORD Hash;
    DWORD Index;
};
struct kNamespace
{
    DWORD Flags;    // 0x01 if schema is sealed
    DWORD NameOffset;
    DWORD NameSize;
    DWORD HashSize; // HashSize == NameSize - 4
    DWORD HostOffset;
    DWORD HostCount;
};
struct kHeader
{
    DWORD Version;   // 6 for Win10/Win11
    DWORD TotalSize; // Bytes
    DWORD Flags;     // 0x01 if schema is sealed
    DWORD Count;     // Count of namespaces and hashes
    DWORD NamespaceOffset;
    DWORD HashOffset;
    DWORD HashMultiplier;
};

// See https://www.geoffchappell.com/studies/windows/win32/apisetschema/index.htm
void PrintApiSet()
{
    assert(sizeof(kHost)      == 0x14);
    assert(sizeof(kHash)      == 0x08);
    assert(sizeof(kNamespace) == 0x18);
    assert(sizeof(kHeader)    == 0x1C);
    assert(sizeof(TCHAR) == 2); // CString must be compiled as Unicode

    #ifdef _WIN64
        LONG_PTR pPEB = (LONG_PTR)__readgsqword(0x60);
        LONG_PTR pOff = pPEB + 0x68;
    #else
        LONG_PTR pPEB = (LONG_PTR)__readfsdword(0x30);
        LONG_PTR pOff = pPEB + 0x38;
    #endif

    BYTE*    pStart  = (BYTE*)((LONG_PTR*)pOff)[0];
    kHeader* pHeader = (kHeader*)pStart;

    wprintf(L"Struct Version:   %d\n", pHeader->Version);
    if (pHeader->Version < 6)
    {
        wprintf(L"ERROR This code works only on Windows 10 / 11.");
        return;
    }

    wprintf(L"Nammespace Count: %d\n", pHeader->Count);
    wprintf(L"Size in Bytes:    %d\n", pHeader->TotalSize);

    wprintf(L"--------------------------------------------------\n");
    wprintf(L"Namespace to Host DLL list:\n");

    kNamespace* pNsBase = (kNamespace*)(pStart + pHeader->NamespaceOffset);
    for (DWORD N=0; N<pHeader->Count; N++)
    {
        kNamespace* pNs = &pNsBase[N];
        CString s_Namespace((WCHAR*)(pStart + pNs->NameOffset), pNs->NameSize/2);

        DWORD NsHash = 0;
        for (DWORD C=0; C<pNs->HashSize/2; C++)
        {
            WCHAR c_Char = s_Namespace.GetAt(C);
            if (c_Char >= 'A' && c_Char <= 'Z') c_Char += 0x20; // ASCII lowercase

            NsHash *= pHeader->HashMultiplier;
            NsHash += c_Char;
        }
        wprintf(L"%3d - Namespace: '%s' (Hash 0x%08X)\n", N+1, s_Namespace, NsHash);

        kHost* pHostBase = (kHost*)(pStart + pNs->HostOffset);
        for (DWORD T=0; T<pNs->HostCount; T++)
        {
            kHost* pHost = &pHostBase[T];
            if (pHost->HostSize)
            {
                CString s_Host((WCHAR*)(pStart + pHost->HostOffset), pHost->HostSize/2);
                wprintf(L"         Host %d: '%s'\n", T+1, s_Host);
            }
            if (pHost->ImportSize)
            {
                CString s_Import((WCHAR*)(pStart + pHost->ImportOffset), pHost->ImportSize/2);
                wprintf(L"       Import %d: '%s'\n", T+1, s_Import);
            }
        }       
    }

    wprintf(L"\n\n--------------------------------------------------\n\n");
    wprintf(L"Sorted Hash lookup table:\n");

    kHash* pHashes = (kHash*)(pStart + pHeader->HashOffset);
    for (DWORD H=0; H<pHeader->Count; H++)
    {
        kHash* pHash = &pHashes[H];
        wprintf(L"%3d - Hash 0x%08X --> Index %d\n", H+1, pHash->Hash, pHash->Index +1);
    }
}

I wrote this code to see what Microsoft is hiding in these structures. With each Windows version there are more namespaces. While Windows 7 had only 35, in Windows 10 there were already 818 and in Windows 11 there are 959. The following is the output from a Windows 11 (Version 10.0.22621)

Stackoverflow does not allow me to post them all.

Struct Version:   6
Nammespace Count: 959
Size in Bytes:    125580
--------------------------------------------------
Namespace to Host DLL list:
  1 - Namespace: 'api-ms-onecoreuap-print-render-l1-1-0' (Hash 0xBFEC7B66)
         Host 1: 'printrenderapihost.dll'
  2 - Namespace: 'api-ms-win-appmodel-advertisingid-l1-1-0' (Hash 0x36B865B7)
         Host 1: 'kernel.appcore.dll'
  3 - Namespace: 'api-ms-win-appmodel-identity-l1-2-0' (Hash 0x1079FB19)
         Host 1: 'kernel.appcore.dll'
  4 - Namespace: 'api-ms-win-appmodel-lifecyclepolicy-l1-1-0' (Hash 0x827A4DFA)
         Host 1: 'rmclient.dll'
  5 - Namespace: 'api-ms-win-appmodel-runtime-internal-l1-1-9' (Hash 0x59E37344)
         Host 1: 'kernel.appcore.dll'
  6 - Namespace: 'api-ms-win-appmodel-runtime-l1-1-6' (Hash 0x3655E8BE)
         Host 1: 'kernel.appcore.dll'
  7 - Namespace: 'api-ms-win-appmodel-state-l1-1-2' (Hash 0x58078FA5)
         Host 1: 'kernel.appcore.dll'
  8 - Namespace: 'api-ms-win-appmodel-state-l1-2-0' (Hash 0x58078FA6)
         Host 1: 'kernel.appcore.dll'
  9 - Namespace: 'api-ms-win-appmodel-unlock-l1-1-0' (Hash 0x6D292052)
         Host 1: 'kernel.appcore.dll'
 10 - Namespace: 'api-ms-win-audiocore-spatial-config-l1-1-0' (Hash 0x2F1B0FEE)
         Host 1: 'windows.media.devices.dll'
 11 - Namespace: 'api-ms-win-base-bootconfig-l1-1-0' (Hash 0x29C0E68B)
         Host 1: 'advapi32.dll'
 12 - Namespace: 'api-ms-win-base-util-l1-1-0' (Hash 0x67899ABD)
         Host 1: 'advapi32.dll'
 13 - Namespace: 'api-ms-win-composition-redirection-l1-1-0' (Hash 0x02DA2B52)
         Host 1: 'dwmredir.dll'
 14 - Namespace: 'api-ms-win-composition-windowmanager-l1-1-0' (Hash 0xAB82A0E1)
         Host 1: 'udwm.dll'
 15 - Namespace: 'api-ms-win-containers-cmclient-l1-1-1' (Hash 0xBD7BA52B)
         Host 1: 'cmclient.dll'
 16 - Namespace: 'api-ms-win-containers-cmclient-l1-2-0' (Hash 0xBD7BA52C)
         Host 1: 'cmclient.dll'
 17 - Namespace: 'api-ms-win-containers-cmclient-l1-3-0' (Hash 0xBD7BA52D)
         Host 1: 'cmclient.dll'
 18 - Namespace: 'api-ms-win-containers-cmclient-l1-4-0' (Hash 0xBD7BA52E)
         Host 1: 'cmclient.dll'
 19 - Namespace: 'api-ms-win-containers-cmclient-l1-5-1' (Hash 0xBD7BA52F)
         Host 1: 'cmclient.dll'
 20 - Namespace: 'api-ms-win-containers-cmdiagclient-l1-1-2' (Hash 0xE3CC78E0)
         Host 1: 'cmclient.dll'
 21 - Namespace: 'api-ms-win-containers-cmservicingclient-l1-1-1' (Hash 0xCBAECA39)
         Host 1: 'cmclient.dll'
 22 - Namespace: 'api-ms-win-containers-cmservicingclient-l1-2-0' (Hash 0xCBAECA3A)
         Host 1: 'cmclient.dll'
 23 - Namespace: 'api-ms-win-core-apiquery-l1-1-1' (Hash 0x1C800B1F)
         Host 1: 'ntdll.dll'
 24 - Namespace: 'api-ms-win-core-apiquery-l2-1-0' (Hash 0x1C800EE0)
         Host 1: 'kernelbase.dll'
 25 - Namespace: 'api-ms-win-core-appcompat-l1-1-1' (Hash 0x1DB92D3C)
         Host 1: 'kernelbase.dll'
 26 - Namespace: 'api-ms-win-core-appinit-l1-1-0' (Hash 0x4AA1AE6E)
         Host 1: 'kernel32.dll'
         Host 2: 'kernelbase.dll'
       Import 2: 'kernel32.dll'
 27 - Namespace: 'api-ms-win-core-atoms-l1-1-0' (Hash 0x51A9ECBD)
         Host 1: 'kernel32.dll'
 28 - Namespace: 'api-ms-win-core-backgroundtask-l1-1-0' (Hash 0x38A2443A)
         Host 1: 'kernelbase.dll'
 29 - Namespace: 'api-ms-win-core-bicltapi-l1-1-6' (Hash 0xD97902F7)
         Host 1: 'bi.dll'
 30 - Namespace: 'api-ms-win-core-biplmapi-l1-1-5' (Hash 0x5B370DBD)
         Host 1: 'twinapi.appcore.dll'
         
.......

386 - Namespace: 'ext-ms-onecore-shellchromeapi-l1-1-2' (Hash 0xC5FD9EE3)
387 - Namespace: 'ext-ms-onecore-shellremindersapi-l1-1-0' (Hash 0xA883CEB0)
388 - Namespace: 'ext-ms-onecore-shlwapi-l1-1-0' (Hash 0x759C03FF)
         Host 1: 'shlwapi.dll'
389 - Namespace: 'ext-ms-onecore-spectrumsyncclient-l1-1-0' (Hash 0xAA91553C)
         Host 1: 'spectrumsyncclient.dll'
390 - Namespace: 'ext-ms-win-adsi-activeds-l1-1-0' (Hash 0x0C45A639)
         Host 1: 'activeds.dll'
391 - Namespace: 'ext-ms-win-advapi32-auth-l1-1-0' (Hash 0xEDDC63B3)
         Host 1: 'advapi32.dll'
392 - Namespace: 'ext-ms-win-advapi32-encryptedfile-l1-1-1' (Hash 0x3C12E6B1)
         Host 1: 'advapi32.dll'
393 - Namespace: 'ext-ms-win-advapi32-eventlog-ansi-l1-1-0' (Hash 0x1C6F72EB)
         Host 1: 'advapi32.dll'
394 - Namespace: 'ext-ms-win-advapi32-eventlog-l1-1-2' (Hash 0x308CEDB1)
         Host 1: 'advapi32.dll'
395 - Namespace: 'ext-ms-win-advapi32-hwprof-l1-1-0' (Hash 0x39E28B33)
         Host 1: 'advapi32.dll'
396 - Namespace: 'ext-ms-win-advapi32-idletask-l1-1-0' (Hash 0x3F86B222)
         Host 1: 'advapi32.dll'
397 - Namespace: 'ext-ms-win-advapi32-lsa-l1-1-3' (Hash 0xE078E237)
         Host 1: 'advapi32.dll'
398 - Namespace: 'ext-ms-win-advapi32-msi-l1-1-0' (Hash 0x5600D40E)
         Host 1: 'advapi32.dll'
399 - Namespace: 'ext-ms-win-advapi32-npusername-l1-1-0' (Hash 0x577CCB03)
         Host 1: 'advapi32.dll'
400 - Namespace: 'ext-ms-win-advapi32-ntmarta-l1-1-0' (Hash 0x2E1CCD0C)
         Host 1: 'advapi32.dll'
401 - Namespace: 'ext-ms-win-advapi32-psm-app-l1-1-0' (Hash 0xEAC0A253)
         Host 1: 'twinapi.appcore.dll'
402 - Namespace: 'ext-ms-win-advapi32-registry-l1-1-1' (Hash 0x96F15BBE)
         Host 1: 'advapi32.dll'
403 - Namespace: 'ext-ms-win-advapi32-safer-l1-1-0' (Hash 0x8ADFAF8C)
         Host 1: 'advapi32.dll'
404 - Namespace: 'ext-ms-win-advapi32-shutdown-l1-1-0' (Hash 0x7E846D45)
         Host 1: 'advapi32.dll'
405 - Namespace: 'ext-ms-win-appcompat-aeinv-l1-1-1' (Hash 0xB9567F3F)
         Host 1: 'aeinv.dll'
406 - Namespace: 'ext-ms-win-appcompat-aepic-l1-1-0' (Hash 0x67937D46)
         Host 1: 'aepic.dll'
407 - Namespace: 'ext-ms-win-appcompat-apphelp-l1-1-2' (Hash 0x7D5B1F4A)
         Host 1: 'apphelp.dll'
408 - Namespace: 'ext-ms-win-appcompat-pcacli-l1-1-0' (Hash 0x7B8426AE)
         Host 1: 'pcacli.dll'
409 - Namespace: 'ext-ms-win-appmodel-activation-l1-1-2' (Hash 0x991E25C7)
         Host 1: 'activationmanager.dll'
410 - Namespace: 'ext-ms-win-appmodel-appcontainerpath-l1-1-0' (Hash 0x29C5EB58)
411 - Namespace: 'ext-ms-win-appmodel-appexecutionalias-l1-1-4' (Hash 0x670018B6)
         Host 1: 'apisethost.appexecutionalias.dll'
412 - Namespace: 'ext-ms-win-appmodel-datasharingservice-extensions-l1-1-0' (Hash 0x4933C951)
413 - Namespace: 'ext-ms-win-appmodel-daxcore-l1-1-3' (Hash 0x224CC975)
         Host 1: 'daxexec.dll'
414 - Namespace: 'ext-ms-win-appmodel-deployment-l1-1-1' (Hash 0xC409A178)
415 - Namespace: 'ext-ms-win-appmodel-deploymentvolumes-l1-1-1' (Hash 0x8DA3FDFB)
416 - Namespace: 'ext-ms-win-appmodel-opc-l1-1-0' (Hash 0xE33E0E0D)
         Host 1: 'opcservices.dll'
417 - Namespace: 'ext-ms-win-appmodel-registrycompatibility-l1-1-0' (Hash 0xCAE70EBC)
         Host 1: 'appxdeploymentextensions.desktop.dll'
418 - Namespace: 'ext-ms-win-appmodel-restrictedappcontainer-internal-l1-1-0' (Hash 0x7EAD1460)
         Host 1: 'kernel.appcore.dll'
419 - Namespace: 'ext-ms-win-appmodel-shellexecute-l1-1-0' (Hash 0x138A1338)
         Host 1: 'windows.storage.dll'
420 - Namespace: 'ext-ms-win-appmodel-state-ext-l1-2-0' (Hash 0xEFE0A4EB)
         Host 1: 'kernel.appcore.dll'
421 - Namespace: 'ext-ms-win-appmodel-usercontext-l1-1-0' (Hash 0x9DE7516B)
422 - Namespace: 'ext-ms-win-appmodel-viewscalefactor-l1-1-0' (Hash 0x67EA7F1B)
423 - Namespace: 'ext-ms-win-appxdeploymentclient-appxdeploy-l1-1-1' (Hash 0xBAF9BB5E)
         Host 1: 'appxdeploymentclient.dll'
424 - Namespace: 'ext-ms-win-appxdeploymentclient-appxdeployonecore-l1-1-1' (Hash 0xBC617A69)
         Host 1: 'appxdeploymentclient.dll'
425 - Namespace: 'ext-ms-win-audio-spatial-systemsound-l1-1-0' (Hash 0x36D811C2)
426 - Namespace: 'ext-ms-win-audiocore-coreaudiopolicymanager-l1-1-0' (Hash 0x4B5CAC0E)
         Host 1: 'coreaudiopolicymanagerext.dll'
427 - Namespace: 'ext-ms-win-audiocore-pal-l1-2-0' (Hash 0x2E1EAAE0)
428 - Namespace: 'ext-ms-win-audiocore-policymanager-l1-1-0' (Hash 0x3BB90C3F)
429 - Namespace: 'ext-ms-win-audiocore-spatial-l1-1-0' (Hash 0xE650D616)
430 - Namespace: 'ext-ms-win-authz-claimpolicies-l1-1-0' (Hash 0xFA04D711)
         Host 1: 'authz.dll'
431 - Namespace: 'ext-ms-win-authz-context-l1-1-0' (Hash 0x447C7E0E)
         Host 1: 'authz.dll'
432 - Namespace: 'ext-ms-win-authz-remote-l1-1-0' (Hash 0xAB6036E9)
         Host 1: 'logoncli.dll'
433 - Namespace: 'ext-ms-win-base-psapi-l1-1-0' (Hash 0x2484538F)
         Host 1: 'psapi.dll'
434 - Namespace: 'ext-ms-win-base-rstrtmgr-l1-1-0' (Hash 0x134D6603)
         Host 1: 'rstrtmgr.dll'
435 - Namespace: 'ext-ms-win-biometrics-winbio-core-l1-1-5' (Hash 0x7494FF9C)
         Host 1: 'winbio.dll'
436 - Namespace: 'ext-ms-win-biometrics-winbio-l1-1-0' (Hash 0x937B8A04)
         Host 1: 'winbio.dll'
437 - Namespace: 'ext-ms-win-biometrics-winbio-l1-2-0' (Hash 0x937B8A05)
         Host 1: 'winbioext.dll'
438 - Namespace: 'ext-ms-win-biometrics-winbio-l1-3-0' (Hash 0x937B8A06)
         Host 1: 'winbioext.dll'
439 - Namespace: 'ext-ms-win-bluetooth-apis-internal-l1-1-0' (Hash 0xDAC4F8F0)
         Host 1: 'bluetoothapis.dll'
440 - Namespace: 'ext-ms-win-bluetooth-apis-l1-1-0' (Hash 0xB0931192)
         Host 1: 'bluetoothapis.dll'
441 - Namespace: 'ext-ms-win-bluetooth-apis-private-l1-1-0' (Hash 0xB0240E9C)
         Host 1: 'bluetoothapis.dll'
442 - Namespace: 'ext-ms-win-branding-winbrand-l1-1-2' (Hash 0x899478A5)
         Host 1: 'winbrand.dll'
443 - Namespace: 'ext-ms-win-branding-winbrand-l1-2-0' (Hash 0x899478A6)
         Host 1: 'winbrand.dll'
444 - Namespace: 'ext-ms-win-casting-device-l1-1-0' (Hash 0x67322CCA)
445 - Namespace: 'ext-ms-win-casting-lockscreen-l1-1-0' (Hash 0xA5343569)
         Host 1: 'miracastreceiverext.dll'
446 - Namespace: 'ext-ms-win-casting-receiver-l1-1-1' (Hash 0x8C725E51)
         Host 1: 'hubuiext.dll'
447 - Namespace: 'ext-ms-win-casting-shell-l1-1-0' (Hash 0x6A353FDC)
         Host 1: 'castingshellext.dll'
448 - Namespace: 'ext-ms-win-ci-management-l1-1-3' (Hash 0x4C1F2678)
         Host 1: 'manageci.dll'
449 - Namespace: 'ext-ms-win-ci-xbox-l1-1-0' (Hash 0x7E0A2628)
450 - Namespace: 'ext-ms-win-cloudap-tbal-l1-1-0' (Hash 0x72D17C08)
451 - Namespace: 'ext-ms-win-clouddomainjoin-usermanagement-l1-1-0' (Hash 0x36974572)
452 - Namespace: 'ext-ms-win-cluster-clusapi-l1-1-5' (Hash 0x850F2E22)
         Host 1: 'clusapi.dll'
453 - Namespace: 'ext-ms-win-cluster-resutils-l1-1-3' (Hash 0xDC26F6E6)
         Host 1: 'resutils.dll'
454 - Namespace: 'ext-ms-win-cmd-util-l1-1-0' (Hash 0x24C08355)
         Host 1: 'cmdext.dll'
455 - Namespace: 'ext-ms-win-cng-rng-l1-1-1' (Hash 0x7B42EFC8)
         Host 1: 'bcryptprimitives.dll'
456 - Namespace: 'ext-ms-win-com-apartmentrestriction-l1-1-0' (Hash 0xE397E484)
457 - Namespace: 'ext-ms-win-com-clbcatq-l1-1-0' (Hash 0x9BA9A49A)
         Host 1: 'clbcatq.dll'
458 - Namespace: 'ext-ms-win-com-coml2-l1-1-1' (Hash 0x27C4ABE7)
         Host 1: 'coml2.dll'
459 - Namespace: 'ext-ms-win-com-ole32-l1-1-5' (Hash 0xD81C13E7)
         Host 1: 'ole32.dll'
460 - Namespace: 'ext-ms-win-com-ole32-l1-2-0' (Hash 0xD81C13E8)
         Host 1: 'ole32.dll'
461 - Namespace: 'ext-ms-win-com-ole32-l1-3-0' (Hash 0xD81C13E9)
         Host 1: 'ole32.dll'
462 - Namespace: 'ext-ms-win-com-ole32-l1-4-0' (Hash 0xD81C13EA)
         Host 1: 'ole32.dll'
463 - Namespace: 'ext-ms-win-com-psmregister-l1-1-0' (Hash 0x31689CC1)
         Host 1: 'kernel.appcore.dll'
464 - Namespace: 'ext-ms-win-com-psmregister-l1-2-2' (Hash 0x31689CC2)
         Host 1: 'kernel.appcore.dll'
465 - Namespace: 'ext-ms-win-com-psmregister-l1-3-1' (Hash 0x31689CC3)
         Host 1: 'kernel.appcore.dll'
466 - Namespace: 'ext-ms-win-com-sta-l1-1-0' (Hash 0x64E9C5EE)
         Host 1: 'ole32.dll'
467 - Namespace: 'ext-ms-win-com-suspendresiliency-l1-1-0' (Hash 0x98C93A8D)
468 - Namespace: 'ext-ms-win-composition-ghost-l1-1-0' (Hash 0xC3968116)
         Host 1: 'dwmghost.dll'
469 - Namespace: 'ext-ms-win-composition-holographic-l1-1-0' (Hash 0xABD36EE7)
         Host 1: 'hologramcompositor.dll'
470 - Namespace: 'ext-ms-win-composition-init-l1-1-0' (Hash 0x6F25EF77)
         Host 1: 'dwminit.dll'
471 - Namespace: 'ext-ms-win-compositor-hosting-l1-1-1' (Hash 0xC5B2973C)
         Host 1: 'ism.dll'
472 - Namespace: 'ext-ms-win-compositor-hosting-l1-2-1' (Hash 0xC5B2973D)
         Host 1: 'ism.dll'
473 - Namespace: 'ext-ms-win-compositor-hosting-l1-3-0' (Hash 0xC5B2973E)
         Host 1: 'ism.dll'
474 - Namespace: 'ext-ms-win-containers-policymanagercli-l1-1-1' (Hash 0xDA5E8DE2)
475 - Namespace: 'ext-ms-win-core-app-package-registration-l1-1-1' (Hash 0x6FAA1F08)
476 - Namespace: 'ext-ms-win-core-app-package-volume-l1-1-0' (Hash 0x9C822987)
477 - Namespace: 'ext-ms-win-core-container-init-l1-1-0' (Hash 0x6073EAB8)
478 - Namespace: 'ext-ms-win-core-dhcp6client-l1-1-0' (Hash 0x8D232EE8)
479 - Namespace: 'ext-ms-win-core-game-streaming-l1-1-0' (Hash 0x2766F82D)
         Host 1: 'gamestreamingext.dll'
         
......
        

749 - Namespace: 'ext-ms-win-ole32-ie-ext-l1-1-0' (Hash 0x9C2996B4)
         Host 1: 'ole32.dll'
750 - Namespace: 'ext-ms-win-ole32-oleautomation-l1-1-0' (Hash 0x0E2EFA69)
         Host 1: 'ole32.dll'
751 - Namespace: 'ext-ms-win-oleacc-l1-1-2' (Hash 0x8566AA31)
         Host 1: 'oleacc.dll'
752 - Namespace: 'ext-ms-win-onecore-shutdown-l1-1-0' (Hash 0x1338C98C)
         Host 1: 'twinapi.appcore.dll'
753 - Namespace: 'ext-ms-win-oobe-query-l1-1-0' (Hash 0x7987540C)
754 - Namespace: 'ext-ms-win-packagevirtualizationcontext-l1-1-0' (Hash 0x162B5ADA)
         Host 1: 'daxexec.dll'
755 - Namespace: 'ext-ms-win-parentalcontrols-setup-l1-1-0' (Hash 0x68866A8F)
         Host 1: 'wpcapi.dll'
756 - Namespace: 'ext-ms-win-perception-device-l1-1-1' (Hash 0x25AF0084)
         Host 1: 'perceptiondevice.dll'
757 - Namespace: 'ext-ms-win-pinenrollment-enrollment-l1-1-2' (Hash 0x7D5462F2)
         Host 1: 'pinenrollmenthelper.dll'
758 - Namespace: 'ext-ms-win-printer-prntvpt-l1-1-2' (Hash 0xAFFF56A3)
         Host 1: 'prntvpt.dll'
759 - Namespace: 'ext-ms-win-printer-winspool-core-l1-1-0' (Hash 0xAC546BFC)
         Host 1: 'winspool.drv'
760 - Namespace: 'ext-ms-win-printer-winspool-l1-1-4' (Hash 0x1A21E1A4)
         Host 1: 'winspool.drv'
761 - Namespace: 'ext-ms-win-printer-winspool-l1-2-0' (Hash 0x1A21E1A5)
         Host 1: 'winspool.drv'
762 - Namespace: 'ext-ms-win-profile-extender-l1-1-0' (Hash 0x14B070DF)
         Host 1: 'userenv.dll'
763 - Namespace: 'ext-ms-win-profile-load-l1-1-0' (Hash 0xC6E0BCA0)
764 - Namespace: 'ext-ms-win-profile-profsvc-l1-1-0' (Hash 0xD2DEAEDF)
         Host 1: 'profsvcext.dll'
765 - Namespace: 'ext-ms-win-profile-userenv-l1-1-1' (Hash 0xC17C2AE4)
         Host 1: 'profext.dll'
766 - Namespace: 'ext-ms-win-provisioning-platform-l1-1-2' (Hash 0x6FBCC8CF)
         Host 1: 'provplatformdesktop.dll'
767 - Namespace: 'ext-ms-win-ras-rasapi32-l1-1-2' (Hash 0x67101BCC)
         Host 1: 'rasapi32.dll'
768 - Namespace: 'ext-ms-win-ras-rasdlg-l1-1-0' (Hash 0x45564066)
         Host 1: 'rasdlg.dll'
769 - Namespace: 'ext-ms-win-ras-rasman-l1-1-0' (Hash 0xB24E34CB)
         Host 1: 'rasman.dll'
770 - Namespace: 'ext-ms-win-ras-tapi32-l1-1-1' (Hash 0x98042EDC)
         Host 1: 'tapi32.dll'
771 - Namespace: 'ext-ms-win-raschapext-eap-l1-1-0' (Hash 0x6CF300BA)
         Host 1: 'raschapext.dll'
772 - Namespace: 'ext-ms-win-rastlsext-eap-l1-1-0' (Hash 0x55F9C371)
         Host 1: 'rastlsext.dll'
773 - Namespace: 'ext-ms-win-rdr-davhlpr-l1-1-0' (Hash 0xA1D6D5B0)
         Host 1: 'davhlpr.dll'
774 - Namespace: 'ext-ms-win-reinfo-query-l1-1-0' (Hash 0x5358AD0E)
         Host 1: 'reinfo.dll'
775 - Namespace: 'ext-ms-win-remotewipe-platform-l1-1-0' (Hash 0xE2E23E6F)
776 - Namespace: 'ext-ms-win-resourcemanager-activitycoordinator-l1-1-0' (Hash 0xDB7A7D4F)
         Host 1: 'rmclient.dll'

.......

899 - Namespace: 'ext-ms-win-shell-shell32-l1-4-0' (Hash 0x287A2993)
         Host 1: 'shell32.dll'
900 - Namespace: 'ext-ms-win-shell-shell32-l1-5-0' (Hash 0x287A2994)
         Host 1: 'shell32.dll'
901 - Namespace: 'ext-ms-win-shell-shlwapi-l1-1-2' (Hash 0xB776C185)
         Host 1: 'shlwapi.dll'
902 - Namespace: 'ext-ms-win-shell-shlwapi-l1-2-1' (Hash 0xB776C186)
         Host 1: 'shlwapi.dll'
903 - Namespace: 'ext-ms-win-shell32-shellcom-l1-1-0' (Hash 0x8F71237B)
         Host 1: 'windows.storage.dll'
904 - Namespace: 'ext-ms-win-shell32-shellfolders-l1-1-1' (Hash 0x6F25CAB7)
         Host 1: 'windows.storage.dll'
905 - Namespace: 'ext-ms-win-shell32-shellfolders-l1-2-1' (Hash 0x6F25CAB8)
         Host 1: 'windows.storage.dll'
906 - Namespace: 'ext-ms-win-smbshare-browser-l1-1-0' (Hash 0x0C8EAB38)
         Host 1: 'browser.dll'
907 - Namespace: 'ext-ms-win-smbshare-browserclient-l1-1-0' (Hash 0x39F16CCD)
         Host 1: 'browcli.dll'
908 - Namespace: 'ext-ms-win-smbshare-sscore-l1-1-0' (Hash 0xBFA0416D)
         Host 1: 'sscoreext.dll'
909 - Namespace: 'ext-ms-win-spinf-inf-l1-1-0' (Hash 0xA9AA924A)
         Host 1: 'spinf.dll'
910 - Namespace: 'ext-ms-win-storage-hbaapi-l1-1-1' (Hash 0xBE835565)
         Host 1: 'hbaapi.dll'
911 - Namespace: 'ext-ms-win-storage-iscsidsc-l1-1-0' (Hash 0x457DE373)
         Host 1: 'iscsidsc.dll'
912 - Namespace: 'ext-ms-win-storage-sense-l1-1-0' (Hash 0x2280C3E6)
         Host 1: 'storageusage.dll'
913 - Namespace: 'ext-ms-win-storage-sense-l1-2-5' (Hash 0x2280C3E7)
         Host 1: 'storageusage.dll'
914 - Namespace: 'ext-ms-win-sxs-oleautomation-l1-1-0' (Hash 0x3D8339E2)
         Host 1: 'sxs.dll'
915 - Namespace: 'ext-ms-win-sysmain-pfapi-l1-1-0' (Hash 0x762EE085)
         Host 1: 'pfclient.dll'
916 - Namespace: 'ext-ms-win-sysmain-pfsapi-l1-1-0' (Hash 0x7B9CD906)
         Host 1: 'pfclient.dll'
917 - Namespace: 'ext-ms-win-sysmain-plmapi-l1-1-1' (Hash 0xBBBAE5BA)
         Host 1: 'pfclient.dll'
918 - Namespace: 'ext-ms-win-system-metrics-override-l1-1-0' (Hash 0x0F0C9C4E)
919 - Namespace: 'ext-ms-win-teapext-eap-l1-1-0' (Hash 0x8B3C24FA)
         Host 1: 'eapteapext.dll'
920 - Namespace: 'ext-ms-win-test-sys1-l1-1-0' (Hash 0x68B10243)
921 - Namespace: 'ext-ms-win-test-sys2-l1-1-0' (Hash 0x6A65DAE2)
922 - Namespace: 'ext-ms-win-tsf-inputsetting-l1-1-0' (Hash 0x099EE4DE)
         Host 1: 'input.dll'
923 - Namespace: 'ext-ms-win-tsf-msctf-l1-1-4' (Hash 0x78C1E4B9)
         Host 1: 'msctf.dll'
924 - Namespace: 'ext-ms-win-ttlsext-eap-l1-1-0' (Hash 0x4F281C21)
         Host 1: 'ttlsext.dll'
925 - Namespace: 'ext-ms-win-ui-viewmanagement-l1-1-0' (Hash 0xF30781E1)
926 - Namespace: 'ext-ms-win-uiacore-l1-1-3' (Hash 0x428B8B56)
         Host 1: 'uiautomationcore.dll'
927 - Namespace: 'ext-ms-win-umpoext-umpo-l1-1-0' (Hash 0x2B96B2D0)
         Host 1: 'umpoext.dll'
928 - Namespace: 'ext-ms-win-usp10-l1-1-0' (Hash 0x37513FB1)
         Host 1: 'gdi32full.dll'
929 - Namespace: 'ext-ms-win-uwf-servicing-apis-l1-1-1' (Hash 0x762FA4CD)
         Host 1: 'uwfservicingapi.dll'
930 - Namespace: 'ext-ms-win-uxtheme-themes-l1-1-3' (Hash 0x919B7819)
         Host 1: 'uxtheme.dll'
931 - Namespace: 'ext-ms-win-vmbus-hvsocket-l1-1-0' (Hash 0x47BD7425)
         Host 1: 'hvsocket.sys'
932 - Namespace: 'ext-ms-win-wer-reporting-l1-1-3' (Hash 0x70F0805D)
         Host 1: 'wer.dll'
933 - Namespace: 'ext-ms-win-wer-ui-l1-1-1' (Hash 0xAEB3EC6D)
         Host 1: 'werui.dll'
934 - Namespace: 'ext-ms-win-wer-wct-l1-1-0' (Hash 0x2FF1C363)
         Host 1: 'wer.dll'
935 - Namespace: 'ext-ms-win-wer-xbox-l1-1-4' (Hash 0x6EC9514E)
936 - Namespace: 'ext-ms-win-wevtapi-eventlog-l1-1-3' (Hash 0xF658B7C1)
         Host 1: 'wevtapi.dll'
937 - Namespace: 'ext-ms-win-windowing-internal-l1-1-0' (Hash 0x8DB3B4B2)
938 - Namespace: 'ext-ms-win-winlogon-mincreds-l1-1-0' (Hash 0xBBDC4AC9)
939 - Namespace: 'ext-ms-win-winrt-device-access-l1-1-0' (Hash 0x39328E76)
         Host 1: 'deviceaccess.dll'
940 - Namespace: 'ext-ms-win-winrt-storage-l1-1-0' (Hash 0x41EED0F6)
         Host 1: 'windows.storage.dll'
941 - Namespace: 'ext-ms-win-winrt-storage-l1-2-3' (Hash 0x41EED0F7)
         Host 1: 'windows.storage.dll'
942 - Namespace: 'ext-ms-win-winrt-storage-win32broker-l1-1-0' (Hash 0xA57FE0CF)
         Host 1: 'windows.storage.onecore.dll'
943 - Namespace: 'ext-ms-win-wlan-grouppolicy-l1-1-0' (Hash 0xC2EC31C4)
         Host 1: 'wlgpclnt.dll'
944 - Namespace: 'ext-ms-win-wlan-onexui-l1-1-0' (Hash 0x5E05CE91)
         Host 1: 'onexui.dll'
945 - Namespace: 'ext-ms-win-wlan-scard-l1-1-0' (Hash 0xB1EDAEB2)
         Host 1: 'winscard.dll'
946 - Namespace: 'ext-ms-win-wpc-webfilter-l1-1-0' (Hash 0xE6E65B79)
         Host 1: 'wpcwebfilter.dll'
947 - Namespace: 'ext-ms-win-wpn-phoneext-l1-1-0' (Hash 0x91DA23BF)
948 - Namespace: 'ext-ms-win-wrp-sfc-l1-1-0' (Hash 0xB92790AA)
         Host 1: 'sfc.dll'
949 - Namespace: 'ext-ms-win-wsclient-devlicense-l1-1-1' (Hash 0x46C05410)
         Host 1: 'wsclient.dll'
950 - Namespace: 'ext-ms-win-wwaext-misc-l1-1-0' (Hash 0x129DA949)
         Host 1: 'wwaext.dll'
951 - Namespace: 'ext-ms-win-wwaext-module-l1-1-0' (Hash 0x31FEBC09)
         Host 1: 'wwaext.dll'
952 - Namespace: 'ext-ms-win-wwan-wwapi-l1-1-3' (Hash 0x6BAF8DB0)
         Host 1: 'wwapi.dll'
953 - Namespace: 'ext-ms-win-xaml-controls-l1-1-0' (Hash 0x279BC087)
         Host 1: 'windows.ui.xaml.phone.dll'
954 - Namespace: 'ext-ms-win-xaml-pal-l1-1-0' (Hash 0xB953A514)
955 - Namespace: 'ext-ms-win-xaudio-platform-l1-1-0' (Hash 0xD478E380)
956 - Namespace: 'ext-ms-win-xblauth-console-l1-1-0' (Hash 0x1EEEBFCE)
957 - Namespace: 'ext-ms-win-xboxlive-xboxnetapisvc-l1-1-0' (Hash 0x2FB58FC8)
958 - Namespace: 'ext-ms-win32-subsystem-query-l1-1-0' (Hash 0xEA970CD9)
959 - Namespace: 'ext-ms-windowscore-deviceinfo-l1-1-0' (Hash 0xF8C4702C)


--------------------------------------------------

Sorted Hash lookup table:
  1 - Hash 0x00ECFB9F --> Index 298
  2 - Hash 0x01E007AC --> Index 629
  3 - Hash 0x02DA2B52 --> Index 13
  4 - Hash 0x0380959A --> Index 268
  5 - Hash 0x04318725 --> Index 40
  6 - Hash 0x0448E171 --> Index 491
  7 - Hash 0x04A5EF1D --> Index 261
  8 - Hash 0x04DB14A1 --> Index 127
  9 - Hash 0x05153EA5 --> Index 234
 10 - Hash 0x058DD793 --> Index 809
 11 - Hash 0x05932143 --> Index 500
 
.......
 
952 - Hash 0xFCF97BE3 --> Index 245
953 - Hash 0xFD39B491 --> Index 247
954 - Hash 0xFDB1B9F9 --> Index 613
955 - Hash 0xFE991230 --> Index 332
956 - Hash 0xFE991231 --> Index 333
957 - Hash 0xFE99C139 --> Index 581
958 - Hash 0xFEB430FC --> Index 627
959 - Hash 0xFED2C5E3 --> Index 600

Upvotes: 0

Jason Larke
Jason Larke

Reputation: 5609

Okay after following @sergmat's advice I took a look at the API Set documentation (found here for anyone interested). I've now modified my GetProcAddress code to do a naive lookup of the Api Set table.

#include <Windows.h>

#define MKPTR(p1,p2) ((DWORD_PTR)(p1) + (DWORD_PTR)(p2))

typedef struct _stripped_peb32 {
    BYTE    unused1[0x038];
    PVOID   ApiSet;
    BYTE    unused2[0x1AC];
} PEB32;

typedef struct _stripped_peb64 {
    BYTE    unused1[0x068];
    PVOID   ApiSet;
    BYTE    unused2[0x23C];
} PEB64;

typedef struct _PROCESS_BASIC_INFORMATION {
    PVOID       Reserved1;
    LPVOID      PebBaseAddress;
    PVOID       Reserved2[2];
    ULONG_PTR   UniqueProcessId;
    PVOID       Reserved3;
} PROCESS_BASIC_INFORMATION;

typedef struct _api_set_host {
    DWORD           ImportModuleName;
    WORD            ImportModuleNameLength;
    DWORD           HostModuleName;
    WORD            HostModuleNameLength;
} API_SET_HOST;

typedef struct _api_set_host_descriptor {
    DWORD           NumberOfHosts;
    API_SET_HOST    Hosts[1];
} API_SET_HOST_DESCRIPTOR;

typedef struct _api_set_entry {
    DWORD           Name;
    WORD            NameLength;
    DWORD           HostDescriptor;
} API_SET_ENTRY;

typedef struct _api_set_header {
    DWORD           unknown1;
    DWORD           NumberOfEntries;
    API_SET_ENTRY   Entries[1];
} API_SET_HEADER;

typedef NTSTATUS (__stdcall *fnNtQueryInformationProcess)(HANDLE ProcessHandle, DWORD ProcessInformationClass, PVOID ProcessInformation, ULONG ProcessInformationLength, PULONG ReturnLength);

API_SET_HEADER *GetApiSetHeader()
{
    fnNtQueryInformationProcess NtQueryInformationProcess = (fnNtQueryInformationProcess)GetProcAddress(LoadLibraryW(L"ntdll.dll"), "NtQueryInformationProcess");
    if (!NtQueryInformationProcess)
        return NULL;

    PROCESS_BASIC_INFORMATION info;
    if (NtQueryInformationProcess(GetCurrentProcess(), 0, &info, sizeof(info), NULL) != S_OK)
        return NULL;

#if defined(_WIN32)
    return (API_SET_HEADER*)(((PEB32*)info.PebBaseAddress)->ApiSet);
#elif defined(_WIN64)
    return (API_SET_HEADER*)(((PEB64*)info.PebBaseAddress)->ApiSet);
#else
    return NULL; // unsupported architecture
#endif
}

HMODULE ResolveImportMap(LPCSTR lpModuleName)
{
    API_SET_HEADER *pHeader = GetApiSetHeader();
    if (pHeader == NULL)
        return NULL;
    API_SET_ENTRY *pEntry = pHeader->Entries;
    API_SET_HOST_DESCRIPTOR* pDescriptor;
    wchar_t module[128];

    // First, normalize the LPCSTR, the API Set table doesn't have the API- prefix
    if (strnicmp("api-", lpModuleName, 4) == 0)
        lpModuleName += 4;

    // Next convert the LPCSTR to a unicode string for comparison and remove the extension (if found)
    mbstowcs(module, lpModuleName, sizeof(module) / sizeof(wchar_t));
    wchar_t *dot = wcsrchr(module, L'.');
    if (dot) *dot = L'\0';

    // Begin the lookup:
    // todo: implement a case-insensitive binary search, not much to be gained for the effort IMO as there's
    //          only 35 entries in the current version of Windows 7, but the option is there for performance nuts.
    for(unsigned long i = 0; i < pHeader->NumberOfEntries; ++i, ++pEntry)
    {
        // Check the top-level host map
        if (wcsnicmp(module, (const wchar_t*)MKPTR(pHeader, pEntry->Name), pEntry->NameLength) == 0)
        {
            pDescriptor = (API_SET_HOST_DESCRIPTOR*)MKPTR(pHeader, pEntry->HostDescriptor);
            // iterate backwards through the hosts to find the most important one (I think this is naive)
            for(unsigned long j = pDescriptor->NumberOfHosts; j > 0; --j)
            {
                if (pDescriptor->Hosts[j - 1].HostModuleNameLength)
                {
                    memcpy(module, (const void*)MKPTR(pHeader, pDescriptor->Hosts[j - 1].HostModuleName), pDescriptor->Hosts[j - 1].HostModuleNameLength);
                    module[pDescriptor->Hosts[j - 1].HostModuleNameLength / sizeof(wchar_t)] = L'\0';
                    return GetModuleHandleW(module); // All the modules should already be loaded, so use GetModuleHandle rather than LoadLibrary
                }
            }
        }
    }

    return NULL;
}

INT LookupExport(IMAGE_DOS_HEADER* pDosHd, DWORD* pNames, DWORD nNames, LPCSTR lpProcName)
{
    // Do a binary search on the name pointer table
    INT start = 0, 
        index = -1,
        middle = -1, 
        end = nNames - 1,
        cmp = 0;

    CHAR *pName;

    while (start <= end && index == -1)
    {
        middle = (start + end) >> 1;
        pName = (CHAR*)MKPTR(pDosHd, pNames[middle]);

        if ((cmp = strcmp(pName, lpProcName)) == 0)
            index = middle; 
        else if (cmp < 0)
            start = middle + 1;
        else
            end = middle;
    }

    return index;
}

FARPROC InternalGetProcAddress(HMODULE hModule, LPCSTR lpProcName)
{
    if (hModule == NULL)
        return NULL;

    BOOL ordinalSearch = HIWORD(lpProcName) == 0;
    WORD ordinal = 0;
    IMAGE_DOS_HEADER *pDosHd = (IMAGE_DOS_HEADER*)hModule;

    if (pDosHd->e_magic != IMAGE_DOS_SIGNATURE)
        return NULL;

    IMAGE_NT_HEADERS *pNtHd = (IMAGE_NT_HEADERS*)MKPTR(pDosHd, pDosHd->e_lfanew);
    if (pNtHd->Signature != IMAGE_NT_SIGNATURE)
        return NULL;

    IMAGE_DATA_DIRECTORY directory = pNtHd->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
    if (directory.Size == 0 || directory.VirtualAddress == 0)
        return NULL;

    IMAGE_EXPORT_DIRECTORY *pExports = (IMAGE_EXPORT_DIRECTORY*)MKPTR(pDosHd, directory.VirtualAddress);
    if (!ordinalSearch)
    {
        INT index = LookupExport(pDosHd, (DWORD*)MKPTR(pDosHd, pExports->AddressOfNames), pExports->NumberOfNames, lpProcName);
        if (index == -1)
            return NULL;
        ordinal = ((WORD*)MKPTR(pDosHd, pExports->AddressOfNameOrdinals))[index];
    }
    else
    {
        ordinal = LOWORD(lpProcName);
    }

    INT ordbase = pExports->Base - 1;
    DWORD dwAddress = ((DWORD*)MKPTR(pDosHd, pExports->AddressOfFunctions))[ordinal - ordbase];
    // Check whether forwarded:
    if (dwAddress >= directory.VirtualAddress && dwAddress < (directory.VirtualAddress + directory.Size))
    {
        CHAR pForward[256];
        strcpy(pForward, (CHAR*)MKPTR(pDosHd, dwAddress));
        CHAR *pFunction = strchr(pForward, '.');
        if (pFunction == NULL)
            return NULL;

        // break into seperate parts and recurse
        *pFunction++ = 0;
        // check if ordinal-forwarded
        if (*pFunction == '#')
            pFunction = (PSTR)(unsigned short)(atoi(++pFunction));

        HMODULE hDestination = LoadLibraryA(pForward);

        // detect an infinite loop, the forward declaration requests the same module handle with 
        // the same function lookup, this could be an Api Set (Windows7+)
        if (hDestination == hModule && (ordinalSearch ? LOWORD(pFunction) == LOWORD(lpProcName) : strcmp(pFunction, lpProcName) == 0))
            hDestination = ResolveImportMap(pForward); // ResolveImportMap will return NULL if not an API Set and so avoid infinite recursion

        return InternalGetProcAddress(hDestination, pFunction);
    }

    return (FARPROC)MKPTR(hModule, dwAddress);
}

Upvotes: 3

Related Questions