MEH Security

Dll Injection

This article is under construction

Before we learn about DLL injection and Process Hollowing, we first have to learn about what exactly a DLL is.

What is a DLL?

A Dll is simply a library of shared code. Different programs and other libraries rely on these to function, as to not reinvent the wheel.
On windows, a Dynamic Link Library, or DLL is the most common and standard format for these.

Different Types of Libraries

There are two types of libraries,

In a static library, the library code is linked/included directly into the program, as if it was embedded into it's own code.
Whereas with a DLL, a separate DLL file which is loaded from disk into each process that needs it, and functions are exported from the DLL to be used by the host process. Thus the host must Dynamically Link the library at runtime, rather than at compile time.
Dll's save disk space, as you don't need to package the same library into each program that needs it, they can instead load it from disk.

DLL Structure

Dlls can be written nearly identical to any other c/c++ program. In an executeable, (.exe), there is an exported function known as the entrypoint, commonly implemented as main.

int main(int argc, char** argv) 

A dll doesn't need a custom entry point, as they're not meant to be executed on their own. However, an entrypoint is still created, known as DllMain.

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
    switch ( ul_reason_for_call ) {
    case DLL_PROCESS_ATTACH:
        // Our DLL has been attached to a process
        break;
    case DLL_PROCESS_DETACH:
        //  Our DLL has been freed from a process
        break;
    default:
         break;
    }
    return TRUE;
}

The DllMain entrypoint provieds you with two important and useful parameters, the first being the module handle.
This is the handle to the current DLL that was just loaded. The second, is an unsigned int32, which specifies the reason for this function being called.
DllMain can be called for multiple reasons, including when it is being attached to a process, as well as detached.

Now for the necescary disclaimer. Everyone on the internet will advice heavily against doing anything with or in DllMain. This is because in the most standard cases, you don't know exactly when your library will be loaded or unloaded. Additionally Loader Lock prevents many actions from occuring when you execute code inside of a DllMain.

However, the Dlls we'll be constructing only have a single export, the entrypoint. They're not meant for shared code, they're meant to be injected into a living process. So our first action, is to create our own thread where we can execute our custom code away from DllMain.
We will have full custom control over when the library is loaded and unloaded.

DWORD WINAPI MyThread(LPVOID lpParam)
{
    // our custom thread where our own code will execute
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
    // if our DLL was just attached to a process
    if (ul_reason_for_call == DLL_PROCESS_ATTACH) {
        // Create a thread at our MyThread function, and pass the handle to this module so we can unload if we wish.
        CreateThread(NULL, NULL, MyThread, hModule, NULL, NULL);
    }
    return TRUE;
}

Now, our injected DLL will remain loaded into the process as long as it lives, so in order to free it, we must revise our thread routine.

DWORD WINAPI MyThread(LPVOID lpParam)
{
    // add our code here, when we're done, unload
    FreeLibraryAndExitThread((HMODULE)lpParam, 0);
}

The FreeLibraryAndExitThread function (msdn) does exactly what it sounds like. It will free the library we tell it to (in this case, our current library with the parameter passed in from DllMain), and exit the current thread that we created. As a process of this, the DllMain function will be called again, this time with ul_reason_for_call equalling DLL_PROCESS_DETACH.

Test Dll code

Let's write some simple code for our DLL.

#include <Windows.h>

DWORD WINAPI MyThread(LPVOID lpParam)
{
    for(;;) { // infinite loop
        Sleep(100); // sleep so we don't take too much CPU
        if (GetAsyncKeyState(VK_LBUTTON)) { // check if left mouse button is being pressed
            MessageBoxA(NULL, "Left Mouse Button Pressed", "Alert!", MB_OK | MB_SYSTEMMODAL); // show a MessageBox
        }
        if (GetAsyncKeyState(VK_END)) break; // if the END key is being pressed, break the infinite loop
    }
    FreeLibraryAndExitThread((HMODULE)lpParam, 0); // then unload our library
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
    // if our DLL was just attached to a process
    if (ul_reason_for_call == DLL_PROCESS_ATTACH) {
        // Create a thread at our MyThread function, and pass the handle to this module so we can unload if we wish.
        CreateThread(NULL, NULL, MyThread, hModule, NULL, NULL);
    }
    return TRUE;
}

What the above does, is infinitely loop, and whenever the user clicks (anywhere), a popup message box is created. Very annoying...
If the END key is pressed, the loop is broken, and the library is ejected.


Library Loading

One of the most simple and straightforward WINAPIs, is LoadLibrary.
Simply pass a the pointer to a NULL-terminated string (char*) path to a DLL, and it'll load it into the current process. DllMain will be called, with the DLL_PROCESS_ATTACH parameter, and a handle to the module will be returned.

However, our goal is to load our DLL into a different process, not our own. For this, we need an injector, and some hacky techniques to call the function LoadLibraryA in a different process.

Code Injection

Fortuneatly, LoadLibraryA is in the library Kernel32.dll, which is loaded into the same virtual memory address for every process. This means calling

&LoadLibraryA

in our injector process, will return the same address of our target function in the target process. Lets look at how to call a function in a remote process.

Steps:

First, lets go over some code to find our process, get it's PID, and open a handle to it.

// function to take care of this for us
// pass a string name of the window
HANDLE openProcessByWindowName(const char* windowName){
    // call FindWindowA to get a handle to the window
    HWND hwnd = FindWindowA(NULL, windowName);

    // create a DWORD, then call GetWindowThreadProcessId to get the PID from it
    DWORD pid;
    GetWindowThreadProcessId(hwnd, &pid);

    // open a handle now we have the PID, and return that handle
    return OpenProcess(PROCESS_ALL_ACCESS , FALSE, pid);
}

Now here's where things get hacky. CreateRemoteThread will create a thread in a remote process. We need the address to create the thread at, and we are allowed control over the RCX register, or the first argument. Fortuneatly, LoadLibrary only takes one argument.
Our first step in this procedure is to allocate some memory in the target to write our parameter to (the full path to the DLL).

First, we allocate a page of memory. We don't care where, and we don't need much memory.

LPVOID pParam = VirtualAllocEx(hProc, 0, 0x100, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);

Next, we write our parameter. We pass the handle to our process, the address of memory we just allocated, then our character buffer, specify the length of our buffer, and the last param can be nullptr.

WriteProcessMemory(hProc, pParam, dllPath, strlen(dllPath), nullptr);

Finally, all we have to do is call CreateRemoteThread to get things started. We'll pass our process handle, then the only params we need to pass are the address to create the thread in (LoadLibraryA), and our first argument (RCX, the char* we wrote in target's memory).

CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)&LoadLibraryA, pParam, NULL, NULL);

Our final code is:

#include <Windows.h>

HANDLE openProcessByWindowName(const char* windowName){
    HWND hwnd = FindWindowA(NULL, windowName);
    DWORD pid;
    GetWindowThreadProcessId(hwnd, &pid);
    return OpenProcess(PROCESS_ALL_ACCESS , FALSE, pid);
}

void injectDll(HANDLE hProc, const char* dllPath){
    LPVOID pParam = VirtualAllocEx(hProc, 0, 0x100, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
    WriteProcessMemory(hProc, pParam, dllPath, strlen(dllPath), NULL);
    CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)&LoadLibraryA, pParam, NULL, NULL);
}

int main(int argc, char** argv){
    injectDll(openProcessByWindowName("untitled - notepad"), "C:/test.dll");
}

If you're going bare bones, here's the entirety of your dll injector:

#include <Windows.h>
int main(int argc, char** argv) {
    DWORD pid;
    GetWindowThreadProcessId(FindWindowA(NULL, argv[1]), &pid);
    HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
    LPVOID pParam = VirtualAllocEx(hProc, 0, 0x100, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
    WriteProcessMemory(hProc, pParam, argv[2], strlen(argv[2]), NULL);
    CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)&LoadLibraryA, pParam, NULL, NULL);
}

Then to run, injector.exe "untitled - notepad" C:/test.dll. Note this doesn't have really any error checking, and you should expand upon this greatly.
None the less, a fully functional DLL injector can be written in literally 9 lines of C code.