DLL Injection
DLL injection consists on injecting a DLL within a memory page inside the virtual address space of a target process, before invoking a thread that calls LoadLibraryA
from kernel32.dll
to load that DLL.
Overview
DLL injection consists on injecting a DLL within a memory region inside the virtual address space of a target process, before invoking a thread that calls LoadLibraryA
from kernel32.dll
to load that DLL. This process injection techniques involves:
Toolhelp32
: To enumerate running processes within a read-only snapshot and search for a process by its name.OpenProcess
: To get a handle on a specific running process with necessary access rights.VirtualAllocEx
: To allocate a memory region within the virtual address space of the target process using its handle.WriteProcessMemory
: To copy the DLL path inside the allocated memory space.GetProcAddress
: To retrieve the LoadLibraryA address from kernel32.dll module.CreateRemoteThread
: To create a thread within the remote target process that will execute the LoadLibraryA loading the specified DLL.
Explanation
For the DLL file, After that, we add a MessageBox inside DLL_PROCESS_ATTACH, so that it will be called when the generated DLL file is loaded by the calling process
Full DLL code
1 |
|
Once our DLL file is created, we begin by creating a read-only snapshot of all the system running processes using CreateToolhelp32Snapshot
1 | HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,NULL); |
TH32CS_SNAPPROCESS
: To include all running processes inside the snapshotNULL
: To indicate the current process
Now, we enumerate the running processes within the snapshot with Process32First
and Process32Next
in order to find the process that matches a specific process name and return a handle on it
Bu before using these 2 functions, we have to create a PROCESSENTRY32
instance that will store all process information and set its dwSize
to the size of the structure in bytes
1 | PROCESSENTRY32 pe32; |
1 | if (Process32First(snapshot, &pe32)) |
snapshot
: The current read-only snapshot we took of the running process in the system&pe32
: A pointer to aPROCESSENTRY32
structure that will contain the process information such as the name of the executable file, the process identifier, and the process identifier of the parent process
And the same syntax will be applied in Process32Next
.
We then use that pe32
pointer to check for a match between the name of the executable file for the process szExeFile
and the wstring of the target process name in our case
1 | if (pe32.szExeFile == ProcessName) |
If there’s a match, we will open a handle to the remote target process using OpenProcess
1 | HANDLE hprocess=nullptr; |
PROCESS_ALL_ACCESS
: To specify the access rights we want to have on the processFALSE
: As we don’t want the child processes of this process to inherit this handlepe32.th32ProcessID
: To specify the pid of the target process that matched our target process name previously
We then close the handle to the read-only snapshot and return the handle to the target process
1 | CloseHandle(snapshot); |
Having a handle to the process, we allocate a memory region within the virtual address space of the remote target process using VirtualAllocEx
1 | LPVOID sc = VirtualAllocEx(hprocess, NULL, sizeof(DLLPath)+1, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE); |
hprocess
: Handle to the target processNULL
: As we do not have any specific starting address to specify for the allocation of the memory region, so we put it to NULL to let the function determine where to allocate the regionsizeof(DLLPath)
: To specify the size of the path to the DLL we will inject into memoryMEM_COMMIT|MEM_RESERVE
: To reserve and commit pages in one stepPAGE_READWRITE
: As we only need these rights to write the DLL path into the newly allocated memory region
Now, we copy the DLL path into the allocated memory region using WriteProcessMemory
1 | WriteProcessMemory(hprocess, sc, DLL_path, sizeof(DLLPath)+1, NULL) |
hprocess
: Handle to the target process that we want to modifysc
: A pointer to the base address of the process we want to start writing fromDLLPath
: A pointer to the buffer that contains the data to be written to the allocated memory regionstrlen(DLL_path)+1
: Size of the DLL path taking into account the null terminator byteNULL
: As we do not need to number of bytes written
We then get a handle to kernel32.dll using GetModuleHandleA
1 | HMODULE hkernel32 = GetModuleHandleA("kernel32.dll"); |
kernel32.dll
: The loaded module
We will use GetProcAddress
to return the address of the LoadLibraryA
function from kernel32.dll
1 | FARPROC loadlibAddr = GetProcAddress(hkernel32, "LoadLibraryA"); |
hkernel32
: The handle to to kernel32.dll from which we will load the desired proceduresLoadLibraryA
: function that will load the specified DLL as a module within the target process
Next, we create a remote thread that starts execution at the start address of LoadLibraryA
to load our DLL file
1 | DWORD TID; |
hprocess
: Handle to the target processNULL
: To get the default security descriptors for the thread and make it non-inheritable0
: To use the default size for the executable(LPTHREAD_START_ROUTINE)loadlibAddr
: To start execution at the starting address ofLoadLibraryA
sc
: The parameter ofLoadLibraryA
function with the pointer to the DLL path0
: To make the thread run immediately after creation&TID
: Pointer to the variable that receives the thread identifier
Demonstration
We execute our DLL injector and get the following output
This screenshot below shows that the thread with Thread ID 8852 calls kernel32.dll to execute LoadLibraryA which loads our DLL file.
We can see that the DLL was correctly loaded within the target process’s modules
Full code
1 | using namespace std; |