DLL (Dynamic Link Libraries)

To use a DLL, it first has to be loaded into the process; then its functions can be used

Creating a DLL

Visual Studio: new project of type Dynamic-Link Library (DLL)
Visual Studio: new project of type Dynamic-Link Library (DLL)
The dllmain.cpp generated by the template
The dllmain.cpp generated by the template
We can rename dllmain.cpp to dllmain.c

Removing precompiled headers

The project may come with framework.h, pch.h and pch.cpp, but they are not needed and can be deleted
The template files that can be deleted
The template files that can be deleted
After deleting them, we adjust the properties
Project menu, Properties option
Project menu, Properties option
Project properties: Precompiled Header set to Use (/Yu)
Project properties: Precompiled Header set to Use (/Yu)
It is changed to Not Using Precompiled Headers
It is changed to Not Using Precompiled Headers

Exporting a function

c
#include <Windows.h>

// Exported function
extern "C" __declspec(dllexport) VOID WINAPI HelloWorld(VOID)
{
    MessageBoxA(NULL, "Hello, World!", "DLL Message", MB_ICONINFORMATION);
}

// Entry point for the DLL
BOOL WINAPI DllMain(HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
    switch (ul_reason_for_call) {
        case DLL_PROCESS_ATTACH:
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            break;
    }
    return TRUE;
}

Listing exported functions

terminal
dumpbin /exports sampleDLL.dll

Load-Time Linking vs Run-Time Linking

Load-time: Windows loads the DLL when the program starts, before main. You call the function directly (MessageBoxA(...)) and the compiler records it in the .exe's IAT. If the DLL is missing, the program does not even start.
Run-time: you load the DLL whenever you want with LoadLibraryA, look up the function with GetProcAddress and call it through a function pointer (typedef + cast). If the DLL is missing, LoadLibraryA returns NULL and you decide what to do. When you are done, FreeLibrary.
Method used: Run-Time
The generated DLL next to the executable that loads it
The generated DLL next to the executable that loads it
c
#include <stdio.h>
#include <Windows.h>

// Function type matching the DLL export: VOID WINAPI HelloWorld(VOID);
typedef VOID(WINAPI* fnHelloWorld)(VOID);

int main() {

    // Load the DLL
    HMODULE hModule = GetModuleHandleA("DllGenerador.dll");
    BOOL loadedManually = FALSE;

    if (hModule == NULL) {
        hModule = LoadLibraryA("DllGenerador.dll");
        if (hModule == NULL) {
            printf("[!] LoadLibraryA Failed With Error: %lu\n", GetLastError());
            return -1;
        }

        loadedManually = TRUE;
    }

    printf("[+] DLL loaded successfully, handle: 0x%p\n", hModule);

    // Get a pointer to the memory address of the DLL's HelloWorld and store it in pHelloWorld
    PVOID pHelloWorld = GetProcAddress(hModule, "HelloWorld");

    if (pHelloWorld == NULL) {
        printf("[!] GetProcAddress Failed With Error: %lu\n", GetLastError());
        if (loadedManually) FreeLibrary(hModule);
        return -1;
    }

    // Cast the memory address so it matches the function type above
    fnHelloWorld HelloWorld = (fnHelloWorld)pHelloWorld;

    // Run the function
    HelloWorld();

    if (loadedManually)
        FreeLibrary(hModule);

    return 0;
}

RunDLL32

terminal
rundll32.exe <dllname>, <function exported to run>
terminal
rundll32 .\DllGenerador.dll, HelloWorld