Didier Stevens

Friday 8 September 2017

Quickpost: DllDemo

Filed under: Quickpost — Didier Stevens @ 0:00

This is a quick demo on loading DLLs with standard Windows tools.

I wrote a DLL for this demo:

#include <windows.h>

extern "C" __declspec(dllexport) void ExportedFunction(void)
{
	OutputDebugString("ExportedFunction");
	MessageBox(NULL, "Hello from ExportedFunction, DemoDll!", "DemoDll", MB_OK);;
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved)
{
	switch (fdwReason)
	{
	case DLL_PROCESS_ATTACH:
		OutputDebugString("DLL_PROCESS_ATTACH");
		MessageBox(NULL, "Hello from DllMain, DemoDll!", "DemoDll", MB_OK);;
		break;

	case DLL_THREAD_ATTACH:
		OutputDebugString("DLL_THREAD_ATTACH");
		break;

	case DLL_THREAD_DETACH:
		OutputDebugString("DLL_THREAD_DETACH");
		break;

	case DLL_PROCESS_DETACH:
		OutputDebugString("DLL_PROCESS_DETACH");
		break;
	}

	return TRUE;
}

A message box is displayed when DllMain (the entrypoint for DLLs) is called when the DLL is loaded into a process, and another message box is displayed when function ExportedFunction is executed.

Function ExportedFunction is prefixed by __declspec(dllexport) to export the function, and with extern “C” to prevent C++ name mangling.

One method to load this DLL into a process, is to use the rundll32 command (since I’m doing this demo on Windows 10, I’m using the 64-bit version of my DLL):

No message box is displayed: the DLL was not loaded. The reason is that rundll32 requires you to specify an exported function that it needs to call.

With dumpbin, we can get an overview of the exported functions of a DLL:

We can specify the name of the exported function we want to call like this:

We can see the message box from the entrypoint, and then the message box from the exported function we called:

This means that the DLL was loaded into the rundll32 process, and that the called function was executed.

Exported functions have an ordinal too (a number to identify exported functions), and that number can be used too to specify the function, like this:

If we use rundll32 with a function that is not exported by the DLL, then the DLL is loaded:

rundll32 will display an error because it could not find the exported function:

But as we could see, the DLL got loaded.

Despite the name would make you think (rundll32), the version of rundll32 I used is a 64-bit executable, and that’s why I used a 64-bit DLL.

32-bit DLLs have to be loaded into 32-bit processses, and 64-bit DLLs into 64-bit processes.

However, rundll32 will start a “proxy” process if you mix bitness, so that DLLs will always be loaded.

Let’s look at the 4 possible combinations:

64-bit rundll32 with 32-bit DLL:

Taking a look with process explorer, we see that 64-bit rundll32 started 32-bit rundll32 to load the 32-bit DLL:

64-bit rundll32 with 64-bit DLL:

In this case, there is no need for a “proxy” process:

32-bit rundll32 with 32-bit DLL:

In this case too, there is no need for a “proxy” process:

And finally, 32-bit rundll32 with 64-bit DLL:

Here we see that 32-bit rundll32 started 64-bit rundll32 to load the 64-bit DLL:

The following is another method: rundll32 shell32.dll,Control_RunDLL C:\Demo\DemoDLL-64-bit.dll

You have to provide an absolute path.

Finally, another method to load a DLL is with regsvr32. I’ve started to use regsvr32 long ago, when I developed ActiveX object: you used it to deploy ActiveX objects on machines.

By default, regsvr32 will load the DLL and call exported function DllRegisterServer:

Since function DllRegisterServer is not exported by the demo DLL, we get an error:

But the DLL was loaded. If we export a function named DllRegisterServer in our DLL, it would get executed.

regsvr32 can be used to call other exported functions too (DllUnRegisterServer and DllInstall), as can be seen from the help dialog when you run regsvr32 without arguments:

DemoDll_V0_0_0_1.zip (https)
MD5: 51ED8255B71097269BFF9B5ADBFDC392
SHA256: 599BA297705B15580A297C3F47429225C38EA9FAA4A8DF27BCE49C918964AD30


Quickpost info


Blog at WordPress.com.