I would like to write about basic runtime dll injection which one method that used by malware creator to inject their process to other application. The objective of this tutorial is to get the concept and step by step injecting dll to other process.
There are some steps required to inject dll into other running process which are below

1. GetWindowThreadProcessId : Get the PID of the running application that become our host to inject our dll. In this case we are finding the host application based on the windows title .eg Untitled – Paint
DWORD GetWindowThreadProcessId(
HWND hWnd,
LPDWORD lpdwProcessId
);
Sample :
DWORD process_id = NULL;
const char* window_title = "Untitled - Paint";
GetWindowThreadProcessId(FindWindow(NULL, windows_title), &process_id);
hWnd
A handle to the window.
lpdwProcessId
A pointer to a variable that receives the process identifier. If this parameter is not NULL, GetWindowThreadProcessId copies the identifier of the process to the variable; otherwise, it does not.
2. GetFullPathName : Get the full path of the dll which we are going to inject to our host application
DWORD GetFullPathNameA(
LPCSTR lpFileName,
DWORD nBufferLength,
LPSTR lpBuffer,
LPSTR *lpFilePart
);
Sample
const char* dll_name = "rio_malware.dll";
GetFullPathName(dll_name, MAX_PATH, dll_path, nullptr)
lpFileName
The name of the file.
nBufferLength
The size of the buffer to receive the null-terminated string for the drive and path, in TCHARs.
lpBuffer
A pointer to a buffer that receives the null-terminated string for the drive and path.
lpFilePart
A pointer to a buffer that receives the address (within lpBuffer) of the final file name component in the path. This parameter can be NULL.
If lpBuffer refers to a directory and not a file, lpFilePart receives zero.
3. OpenProcess : Create a new process on the host application
HANDLE OpenProcess(
DWORD dwDesiredAccess,
BOOL bInheritHandle,
DWORD dwProcessId
);
Sample
DWORD process_id = NULL;
HANDLE h_process = OpenProcess(PROCESS_ALL_ACCESS, NULL, process_id);
dwDesiredAccess
The access to the process object. This access right is checked against the security descriptor for the process. This parameter can be one or more of the process access rights.
If the caller has enabled the SeDebugPrivilege privilege, the requested access is granted regardless of the contents of the security descriptor.
bInheritHandle
If this value is TRUE, processes created by this process will inherit the handle. Otherwise, the processes do not inherit this handle.
dwProcessId
The identifier of the local process to be opened.
4. VirtualAllocEx : Prepare the memory allocation for the dll to be loaded to the host application.
LPVOID VirtualAllocEx(
HANDLE hProcess,
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flAllocationType,
DWORD flProtect
);
Sample
void* allocate_memory = VirtualAllocEx(h_process, nullptr, MAX_PATH, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
hProcess
The handle to a process. The function allocates memory within the virtual address space of this process.
The handle must have the PROCESS_VM_OPERATION access right. For more information, see Process Security and Access Rights.
lpAddress
The pointer that specifies a desired starting address for the region of pages that you want to allocate.
If you are reserving memory, the function rounds this address down to the nearest multiple of the allocation granularity.
If you are committing memory that is already reserved, the function rounds this address down to the nearest page boundary. To determine the size of a page and the allocation granularity on the host computer, use the GetSystemInfo function.
If lpAddress is NULL, the function determines where to allocate the region.
If this address is within an enclave that you have not initialized by calling InitializeEnclave, VirtualAllocEx allocates a page of zeros for the enclave at that address. The page must be previously uncommitted, and will not be measured with the EEXTEND instruction of the Intel Software Guard Extensions programming model.
If the address in within an enclave that you initialized, then the allocation operation fails with the ERROR_INVALID_ADDRESS error.
dwSize
The size of the region of memory to allocate, in bytes.
If lpAddress is NULL, the function rounds dwSize up to the next page boundary.
If lpAddress is not NULL, the function allocates all pages that contain one or more bytes in the range from lpAddress to lpAddress+dwSize. This means, for example, that a 2-byte range that straddles a page boundary causes the function to allocate both pages.
flAllocationType
The type of memory allocation. This parameter must contain one of the following values.
MEM_COMMIT 0x00001000 Allocates memory charges (from the overall size of memory and the paging files on disk) for the specified reserved memory pages. The function also guarantees that when the caller later initially accesses the memory, the contents will be zero. Actual physical pages are not allocated unless/until the virtual addresses are actually accessed. To reserve and commit pages in one step, call VirtualAllocEx with MEM_COMMIT | MEM_RESERVE.
Attempting to commit a specific address range by specifying MEM_COMMIT without MEM_RESERVE and a non-NULL lpAddress fails unless the entire range has already been reserved. The resulting error code is ERROR_INVALID_ADDRESS.
An attempt to commit a page that is already committed does not cause the function to fail. This means that you can commit pages without first determining the current commitment state of each page.
If lpAddress specifies an address within an enclave, flAllocationType must be MEM_COMMIT.
MEM_RESERVE 0x00002000 Reserves a range of the process's virtual address space without allocating any actual physical storage in memory or in the paging file on disk. You commit reserved pages by calling VirtualAllocEx again with MEM_COMMIT. To reserve and commit pages in one step, call VirtualAllocEx with MEM_COMMIT | MEM_RESERVE.
Other memory allocation functions, such as malloc and LocalAlloc, cannot use reserved memory until it has been released.
MEM_RESET 0x00080000 Indicates that data in the memory range specified by lpAddress and dwSize is no longer of interest. The pages should not be read from or written to the paging file. However, the memory block will be used again later, so it should not be decommitted. This value cannot be used with any other value.
Using this value does not guarantee that the range operated on with MEM_RESET will contain zeros. If you want the range to contain zeros, decommit the memory and then recommit it.
When you use MEM_RESET, the VirtualAllocEx function ignores the value of fProtect. However, you must still set fProtect to a valid protection value, such as PAGE_NOACCESS.
VirtualAllocEx returns an error if you use MEM_RESET and the range of memory is mapped to a file. A shared view is only acceptable if it is mapped to a paging file.
MEM_RESET_UNDO 0x1000000
MEM_RESET_UNDO should only be called on an address range to which MEM_RESET was successfully applied earlier. It indicates that the data in the specified memory range specified by lpAddress and dwSize is of interest to the caller and attempts to reverse the effects of MEM_RESET. If the function succeeds, that means all data in the specified address range is intact. If the function fails, at least some of the data in the address range has been replaced with zeroes.
This value cannot be used with any other value. If MEM_RESET_UNDO is called on an address range which was not MEM_RESET earlier, the behavior is undefined. When you specify MEM_RESET, the VirtualAllocEx function ignores the value of flProtect. However, you must still set flProtect to a valid protection value, such as PAGE_NOACCESS.
Windows Server 2008 R2, Windows 7, Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP: The MEM_RESET_UNDO flag is not supported until Windows 8 and Windows Server 2012.
This parameter can also specify the following values as indicated.
MEM_LARGE_PAGES 0x20000000 Allocates memory using large page support.
The size and alignment must be a multiple of the large-page minimum. To obtain this value, use the GetLargePageMinimum function.
If you specify this value, you must also specify MEM_RESERVE and MEM_COMMIT.
MEM_PHYSICAL 0x00400000 Reserves an address range that can be used to map Address Windowing Extensions (AWE) pages. This value must be used with MEM_RESERVE and no other values.
MEM_TOP_DOWN 0x00100000 Allocates memory at the highest possible address. This can be slower than regular allocations, especially when there are many allocations.
flProtect
The memory protection for the region of pages to be allocated. If the pages are being committed, you can specify any one of the memory protection constants.
If lpAddress specifies an address within an enclave, flProtect cannot be any of the following values:
PAGE_NOACCESS
PAGE_GUARD
PAGE_NOCACHE
PAGE_WRITECOMBINE
5. WriteProcessMemory : Write the memory allocated with the dll that we want to load.
BOOL WriteProcessMemory(
HANDLE hProcess,
LPVOID lpBaseAddress,
LPCVOID lpBuffer,
SIZE_T nSize,
SIZE_T *lpNumberOfBytesWritten
);
Sample
HANDLE h_process = OpenProcess(PROCESS_ALL_ACCESS, NULL, process_id);
DWORD process_id = NULL;
WriteProcessMemory(h_process, allocate_memory, dll_path, MAX_PATH, nullptr)
hProcess
A handle to the process memory to be modified. The handle must have PROCESS_VM_WRITE and PROCESS_VM_OPERATION access to the process.
lpBaseAddress
A pointer to the base address in the specified process to which data is written. Before data transfer occurs, the system verifies that all data in the base address and memory of the specified size is accessible for write access, and if it is not accessible, the function fails.
lpBuffer
A pointer to the buffer that contains data to be written in the address space of the specified process.
nSize
The number of bytes to be written to the specified process.
lpNumberOfBytesWritten
A pointer to a variable that receives the number of bytes transferred into the specified process. This parameter is optional. If lpNumberOfBytesWritten is NULL, the parameter is ignored.
6. CreateRemoteThread : Create the remote process on the host application with application in range memory we specified
HANDLE CreateRemoteThread(
HANDLE hProcess,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
Sample
void* allocate_memory = VirtualAllocEx(h_process, nullptr, MAX_PATH, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
HANDLE h_process = OpenProcess(PROCESS_ALL_ACCESS, NULL, process_id);
CreateRemoteThread(h_process, nullptr, NULL, LPTHREAD_START_ROUTINE(LoadLibraryA), allocate_memory, NULL, nullptr);4
hProcess
A handle to the process in which the thread is to be created. The handle must have the PROCESS_CREATE_THREAD, PROCESS_QUERY_INFORMATION, PROCESS_VM_OPERATION, PROCESS_VM_WRITE, and PROCESS_VM_READ access rights, and may fail without these rights on certain platforms. For more information, see Process Security and Access Rights.
lpThreadAttributes
A pointer to a SECURITY_ATTRIBUTES structure that specifies a security descriptor for the new thread and determines whether child processes can inherit the returned handle. If lpThreadAttributes is NULL, the thread gets a default security descriptor and the handle cannot be inherited. The access control lists (ACL) in the default security descriptor for a thread come from the primary token of the creator.
Windows XP: The ACLs in the default security descriptor for a thread come from the primary or impersonation token of the creator. This behavior changed with Windows XP with SP2 and Windows Server 2003.
dwStackSize
The initial size of the stack, in bytes. The system rounds this value to the nearest page. If this parameter is 0 (zero), the new thread uses the default size for the executable. For more information, see Thread Stack Size.
lpStartAddress
A pointer to the application-defined function of type LPTHREAD_START_ROUTINE to be executed by the thread and represents the starting address of the thread in the remote process. The function must exist in the remote process. For more information, see ThreadProc.
lpParameter
A pointer to a variable to be passed to the thread function.
dwCreationFlags
The flags that control the creation of the thread.
0 The thread runs immediately after creation.
CREATE_SUSPENDED 0x00000004 The thread is created in a suspended state, and does not run until the ResumeThread function is called.
STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000 The dwStackSize parameter specifies the initial reserve size of the stack. If this flag is not specified, dwStackSize specifies the commit size.
lpThreadId
A pointer to a variable that receives the thread identifier.
If this parameter is NULL, the thread identifier is not returned.
#include <iostream>
#include <Windows.h>
#include <string>
#include <thread>
#include <libloaderapi.h>
using namespace std;
bool get_processID(const char* windows_title, DWORD &process_id) {
GetWindowThreadProcessId(FindWindow(NULL, windows_title), &process_id);
if (process_id == NULL) {
return true;
}
return false;
}
void error(const char* error_title, const char* error_message) {
MessageBox(NULL, error_message, error_title,NULL);
exit(-1);
}
bool file_exist(string file_name) {
struct stat buffer;
return (stat(file_name.c_str(), &buffer) == 0);
}
int main(){
DWORD process_id = NULL;
char dll_path[MAX_PATH];
const char* dll_name = "rio_malware.dll";
const char* window_title = "Untitled - Paint";
if (!file_exist(dll_name)) {
error("DLL File Exist","DLL File does not exist");
}
//Get the full path of the
if (!GetFullPathName(dll_name, MAX_PATH, dll_path, nullptr)) {
error("Get Fullpath Name", "Failed to get full path");
}
//Get the process ID based on the window title
if (get_processID(window_title, process_id)) {
error("Process ID", "Failed to get Process ID");
}
//Create a process
HANDLE h_process = OpenProcess(PROCESS_ALL_ACCESS, NULL, process_id);
if (!h_process) {
error("Open Process", "Failed Open Process");
}
//Allocate memory to store the path of .dll
void* allocate_memory = VirtualAllocEx(h_process, nullptr, MAX_PATH, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (!allocate_memory) {
error("allocate memory", "Failed Allocate Memory");
}
//Write the path to the host memory
if (!WriteProcessMemory(h_process, allocate_memory, dll_path, MAX_PATH, nullptr)) {
error("Write Memory", "Failed Write Memory");
}
//Create thread on the host
HANDLE h_thread = CreateRemoteThread(h_process, nullptr, NULL, LPTHREAD_START_ROUTINE(LoadLibraryA), allocate_memory, NULL, nullptr);
if (!h_thread) {
error("execute", "error execute");
}
CloseHandle(h_process);
VirtualFreeEx(h_process, allocate_memory, NULL, MEM_RELEASE);
}
One comment