cwindowswinapivisual-c++reverse-engineering

Recursion chicken and egg problem in C (Resolving forwarded exports)


I’m working on a Windows PE loader that needs to resolve forwarded exports, which sometimes require recursively calling myGetProcAddress → ResolveForwarder → myGetProcAddress again.

Because of this mutual recursion, I’m running into a compiler issue; if I define one function before the other, the other is “undefined.”


FARPROC ResolveForwarder(LPCSTR forwarderString) {
    LPCSTR forwardedDllName, forwardedFunctionName = NULL;
    WORD ordinal = 0;
    FARPROC functionAddr = NULL;
    const char* localForwarder = _strdup(forwarderString);
    char* dot = strrchr(localForwarder, '.');
    if (dot != NULL) {

        DWORD_PTR length = (DWORD_PTR)dot - (DWORD_PTR)localForwarder;
        // USHORT_MAX
        if (length <= (WORD)0xffff) {
            *dot = '\0';
           
            forwardedDllName = localForwarder;
            if (*(dot + 1) == '#') {
                // forwarded by ordinal. Let's call myGetProcAddress with an ordinal
                ordinal = (WORD)strtoul(dot + 2, 0, 10);
                functionAddr = myGetProcAddress(myGetModuleHandle(forwardedDllName), MAKEINTRESOURCEA(ordinal));
            }
            else {
                // forwarded by name. Calling myGetProcAddress with the forwarded  function name
                forwardedFunctionName = dot + 1;
                // error: myGetProcAddress undefined
                functionAddr = myGetProcAddress(myGetModuleHandle(forwardedDllName), forwardedFunctionName);
            }
            // STATUS_SUCCESS
            return functionAddr;
        }
    }
    // STATUS_INVALID_IMAGE_FORMAT
    return NULL;
}

FARPROC myGetProcAddress(HMODULE hModule, LPCSTR lpProcName) {
    BYTE* dllBase = (BYTE*)hModule;
    PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)hModule;
    PIMAGE_NT_HEADERS ntHeaders = (PIMAGE_NT_HEADERS)((BYTE*)hModule + dosHeader->e_lfanew);
    PIMAGE_EXPORT_DIRECTORY exportDirectory = (PIMAGE_EXPORT_DIRECTORY)((BYTE*)hModule + ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);

    DWORD* addressOfFunctions = (DWORD*)((BYTE*)hModule + exportDirectory->AddressOfFunctions);
    WORD* addressOfNameOrdinals = (WORD*)((BYTE*)hModule + exportDirectory->AddressOfNameOrdinals);
    DWORD* addressOfNames = (DWORD*)((BYTE*)hModule + exportDirectory->AddressOfNames);
    DWORD numberOfNames = exportDirectory->NumberOfNames;
    WORD ordinal = 0;
    FARPROC functionAddr = NULL;
    DWORD functionRVA = 0;
    if ((DWORD_PTR)lpProcName <= (WORD)0xFFFF) {
        // ordinal
        ordinal = (WORD)lpProcName;
    }
    else {
        ordinal = LdrpNameToOrdinal(lpProcName, numberOfNames, dllBase, addressOfNames, addressOfNameOrdinals);
    }
    functionRVA = addressOfFunctions[ordinal];
    if (functionRVA > ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress && functionRVA < ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress + ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size) {
                // function is forwarded
        // parse, call ResolveForwarder with the new values
        LPCSTR forwarderString = (const char*)dllBase + functionRVA;
        return ResolveForwarder(forwarderString);
    }
    else {
        return (FARPROC)(dllBase + functionRVA);
    }
    return functionAddr;

}

The idea being that myGetProcAddress, in case it recognizes a forwarder, calls ResolveForwarder internally, which then calls myGetProcAddress with the new values, in a callback like fashion:

// inside myGetProcAddress
if (isForwarded) {
                // function is forwarded
            // parse, call ResolveForwarder with the new values
        LPCSTR forwarderString = (const char*)dllBase + functionRVA;
        
        functionAddr = ResolveForwarder(forwarderString); 
               // ResolveForwarder calls myGetProcAddress with new values
    }
    else {
        functionAddr = (FARPROC)(dllBase + functionRVA);
    }
    return functionAddr;

However, this evidently creates a chicken and egg problem: If myGetProcAddress calls ResolveForwarder, and ResolveForwarder internally calls myGetProcAddress, there will be one function that doesn't recognize the other (because we have to place one before the other in the code). So there are issues like this

'myGetProcAddress' undefined; assuming extern returning int

My question is, how do we make it work in this manner, and are there better methods than my current idea?

If anyone has any suggestions, please let me know!

What happened: Compiler / linker errors, because one function is placed before the other in the code, it means it won't be able to call the other function.

What I expected to happen: myGetProcAddress and ResolveForwarder call each other to correctly resolve the forwarded exports in Windows.

If there is a better, more reliable method that doesn't rely on what I've been doing now, I'll be happy to get suggestions to do it.


Solution

  • You can forward-declare them (in the C file, or put them in a header). I just ran into this same thing yesterday, actually.

    FARPROC ResolveForwarder(LPCSTR forwarderString);
    FARPROC myGetProcAddress(HMODULE hModule, LPCSTR lpProcName);
    
    // [The rest of your C file and implementations of the 2 functions]
    

    Or when using a header:

    // [header.h]
    FARPROC ResolveForwarder(LPCSTR forwarderString);
    FARPROC myGetProcAddress(HMODULE hModule, LPCSTR lpProcName);
    
    // [your C file]
    #include "header.h"
    // [full function implementation here]