Exporting functions from DLL using the actual function name

In this post I describe the adventure I had in figuring out how I could export a function from a DLL (compiled for 32bit architecture) using the actual name of this function. This post attempts to document in a better way what was found on a stackoverflow thread [1].

This post is split in the following sections:

Name got mangled: Chapter 1

The number one result I got while searching on how I could export a function from DLL came from Microsoft [2]. We can export a function from DLL using the keyword __declspec(dllexport). Looks and it is straightforward, indeed. I wrote the following code in order to generate a DLL that exports the function *DoSomeMagic. After compiling this code, I opened the DLL on CFF Explorer to have a look at the exported function.

#include <windows.h>
#include <debugapi.h>

#define DllExport __declspec(dllexport)

DllExport void WINAPI DoSomeMagic(void)
{
    OutputDebugStringA("DoSomeMagic was executed");
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
    switch(fdwReason)
    {
        case DLL_PROCESS_ATTACH:
            OutputDebugStringA("DllMain was executed");
            break;
        case DLL_PROCESS_DETACH:
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
            break;
    }

    return 1;
}

Τhe linker decorates the name of the function and thus it looks like this on CFF Explorer:

export1

We observe that the function was exported with the name ?DoSomeMagic@@YGXXZ. This means that if we want to call/execute this function, we’ll have to call it in this way. I didn’t like it so I chase how it could be exported with the actual name.

Name got mangled: Chapter 2

What if we change the definition of the function to:

extern “C” DllExport void WINAPI DoSomeMagic(void)

In this case, the name of the exported function is _DoSomeMagic@0 as shown in CFF Explorer:

export2

Last call: name finally exported

There is an even more straightforward way of configuring a function to be exported with its actual name and this is not by creating a definition file. By creating a #pragma comment 3, we can tell the linker about the naming convention of the function we export. By implementing the pragma comment, the initial code changes to:

#include <windows.h>
#include <debugapi.h>

#define DllExport comment(linker, "/EXPORT:DoSomeMagic=?DoSomeMagic@@YGXXZ")

void WINAPI DoSomeMagic(void)
{
    #pragma DllExport
    OutputDebugStringA("DoSomeMagic was executed");
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
    switch(fdwReason)
    {
        case DLL_PROCESS_ATTACH:
            OutputDebugStringA("DllMain was executed");
            break;
        case DLL_PROCESS_DETACH:
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
            break;
    }

    return TRUE;
}

The output from CFF Explorer shows the function is finally exported with its actual name:

export3

We can now call the exported function by its name. Assuming we call the DLL test.dll, we can call the function using runll32.exe:

rundll32.exe test.dll, DoSomeMagic

Exporting Functions Using Module-Definition File

If you choose the path to have the imports in a separate file, a definitions file (.DEF) is the way to go.

The following file instructs the linker that the function DoSomeMagic be exported:

EXPORTS
	DoSomeMagic PRIVATE

In order to compile the source code into a DLL from command line prompt using the Microsoft compiler you would do something like:

cl.exe /nologo source.cpp /LD /link /DEF:defitions.def

If you prefer the GUI version, go to ‘Solution Explorer’, right click on ‘Source Files’ -> Add -> ‘Module’ search for ‘module’ and select ‘Module-Definition File (.def)’

References

[1] https://stackoverflow.com/questions/538134/exporting-functions-from-a-dll-with-dllexport

[2] https://docs.microsoft.com/en-us/cpp/build/exporting-from-a-dll-using-declspec-dllexport?view=vs-2019

[3] https://docs.microsoft.com/en-us/cpp/preprocessor/comment-c-cpp?view=vs-2019