Skeleton Code to Create PoC DLL

In this page, you can find skeleton code that can be used to generate a DLL that creates a file in user’s temp directory. It can be compiled in Visual Studio. The generated DLL can be used as proof of concept. For example, if you want to test DLL hijacking, DLL side-loading, reflective DLL injection and similar techniques.

DLL skeleton code

The code listed below can be compiled with Microsoft Visual Studio to generate a DLL. This DLL exports a single function and as soon as it executes, it creates an empty file.

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

#define DllExport __declspec(dllexport)

extern "C" DllExport void __stdcall PayloadFunction(WCHAR* fname)
{
	// create a logfile in user's temp
	// this file will be used as a proof of payload execution

	WCHAR* lpBuffer = new WCHAR[MAX_PATH];
	lpBuffer[MAX_PATH - 1] = '\0';

	DWORD len = ::GetTempPathW(MAX_PATH, lpBuffer);
	if (!len)
	{
		CHAR msgbuf[50];
		sprintf_s(msgbuf, 50, "[-] GetTempPathW has failed: %d", GetLastError());
		::OutputDebugString(msgbuf);
		return 1;
	}
	
	size_t numberofelements = wcslen(lpBuffer) + wcslen(logfname) + 1;
	WCHAR* logfilepath = new WCHAR[numberofelements];
	wcscpy_s(logfilepath, numberofelements, lpBuffer);
	wcscat_s(logfilepath, numberofelements, logfname);

	HANDLE hFile = ::CreateFileW(
		logfilepath,
		GENERIC_ALL,
		FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
		NULL,
		CREATE_NEW,
		FILE_ATTRIBUTE_NORMAL,
		NULL
	);
	if (hFile == INVALID_HANDLE_VALUE)
	{
		CHAR msgbuf[50];
		sprintf_s(msgbuf, 50, "[-] CreateFileW has failed: %d", GetLastError());
		::OutputDebugString(msgbuf);
		return 1;
	}

	::CloseHandle(hFile);

	return 0;
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
{
	WCHAR fname[] = L"payload_test.tmp";

	switch (fdwReason)
	{
		case DLL_PROCESS_ATTACH:
			PayloadFunction(fname);
			break;
		case DLL_THREAD_ATTACH:
		case DLL_PROCESS_DETACH:
		case DLL_THREAD_DETACH:
			break;
	}
	return 1;
}

You can execute this DLL using rundll32.exe on a Windows system:

rundll32.exe <DLL_name>,<exported_function_name>

To view the exported functions, dumpbin.exe - a tool that is part of Visual Studio - can be used:

dumpbin.exe /EXPORTS <name of the dll>

We can also call the exported function using the ordinal name. In this case, the ordinal of the exported function is 1 and thus the call would be:

rundll32.exe calc.dll,#1

Build and use your own DLL launcher

When you execute the DLL with rundll32.exe, you are not able to view any output messages. For example, if you are using printf() in the DLL there is no output. We can deal with this by building our own launcher. The code listed below once compiled, creates an executable that gets as input a DLL, loads it and executes it.

#include <windows.h>
#include <stdio.h>
#include <iostream>

int main(int argc, char** argv)
{
	printf("[+] Loading DLL...\n");
	CHAR* dllname = argv[1];

	HMODULE hLibrary = LoadLibraryA(dllname);
	if (hLibrary == NULL)
	{
		printf("[-] LoadLibraryA has failed: %d\n", GetLastError());
		return 1;
	}

	printf("[+] Handle of the loaded DLL: 0x%p\n", hLibrary);

	FARPROC gpa = GetProcAddress(hLibrary, MAKEINTRESOURCEA(1));
	if (gpa)
	{
		printf("[+] Module Address: 0x%p\n", gpa);
	}
	else
	{
		printf("[-] Last Error: %d\n", GetLastError());
		return 1;
	}

	if (FreeLibrary(hLibrary))
	{
		printf("[+] Library has been unloaded successfuly!\n");
	}
	
	return 0;
}

The tools

The tools used for this article were:

tags: #Windows API