I'm trying to get my DLL-Injection Program in C to work. I have no clue why it doesn't work and am now seeking help from the community ;)
To summarize: The purpose of my program is to Inject and execute the User32!MessageBoxA function within the target program notepad.exe using a remotely called LoadLibraryA which gets executed after the loader sucessfully loaded Kernel32.dll.
For now I just want to inject USER32.dll.
My comment on line no. 76 describes my Issue in much more detail:
/** Issue at line no. 76:
* NOW: The previous steps all went as expected.
* BUT NOW HERE'S WHERE IS THE PROBLEM.
* PROBLEM: Ideally, on the next Iteration the remote Thread should call LoadLibraryA with USER32.dll causing the USER32.dll to be injected into the target.
* But by using the gcc debugger within VSCode I found that directly after breaking the current switch case and entering the
* next Iteration, the LOAD_DLL_DEBUG_EVENT will indeed be triggered BUT not with USER32.dll.
*
* This lets me to believe that the LoadLibraryA call from the remote thread must not be executing at all.
*
* Please let me know how I can get this to be working as expected.
* Any help will be strongly appreciated!
*/
This is the full code including my annotations:
#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <psapi.h>
int main() {
// Get the system directory for later use
char* sysdir = calloc(MAX_PATH, sizeof(char));
size_t sysdir_length = strlen(sysdir);
sysdir_length = GetSystemDirectory(sysdir, MAX_PATH);
if(sysdir_length == 0) {
printf("Couldn't get system directory!\n");
return EXIT_FAILURE;
}
// This specifies the absolute path to the target program that the DLL will be injected into
const char* targetProcessPath = "C:\\Windows\\System32\\notepad.exe";
STARTUPINFO si;
PROCESS_INFORMATION pi;
memset(&si, 0, sizeof(STARTUPINFO));
memset(&pi, 0, sizeof(PROCESS_INFORMATION));
// Create the target Program with Debug Flag set
if (!CreateProcess(targetProcessPath, NULL, NULL, NULL, FALSE, DEBUG_PROCESS, NULL, NULL, &si, &pi)) {
printf("Failed to create the process: %d\n", GetLastError());
return 1;
}
printf("Debugging process %s (PID: %d)\n", targetProcessPath, pi.dwProcessId);
printf("Now Printing information from the loader...\n");
// Create pointer to store the address of the target's LoadLibraryA function
FARPROC remote_LoadLibraryA;
// Set up the debug Event Handler
DEBUG_EVENT debugEvent;
HANDLE remoteThread;
char* modulename = calloc(MAX_PATH, sizeof(char));
/** Debug loop.
* 1. Logs information everytime a DLL is being loaded within the target (including the loader)
* 2. Resolves the address for KERNEL32!LoadLibraryA within the target and stores it in remote_LoadLibraryA
* 3. Creates a remote thread to execute LoadLibraryA() once Kernel32 has been loaded by the loader
* 4. Receives the Event for the remotely created Thread and continues execution
* 5. PROBLEM: Does not Load the specified USER32.dll
*/
while (1) {
if (!WaitForDebugEvent(&debugEvent, INFINITE)) {
printf("WaitForDebugEvent failed: %d\n", GetLastError());
break;
}
switch (debugEvent.dwDebugEventCode) {
// This will be triggered every time the target program tries to create a thread
// Here we'll also receive our remotely created thread
case CREATE_THREAD_DEBUG_EVENT:
// Quick error Handling
if(debugEvent.u.CreateThread.hThread == NULL) break;
// This will check if the thread created is our external LoadLibrary call
if(GetThreadId(debugEvent.u.CreateThread.hThread) != GetThreadId(remoteThread)) break; // break if that's not the case
printf("Received the remote Thread creation for our LoadLibraryA function call!\n");
printf("Expecting the next Module to be User32.dll\n");
ContinueDebugEvent(debugEvent.dwProcessId, GetThreadId(remoteThread), DBG_CONTINUE);
/**
* NOW: The previous steps all went as expected.
* BUT NOW HERE'S WHERE IS THE PROBLEM.
* PROBLEM: Ideally, on the next Iteration the remote Thread should call LoadLibraryA with USER32.dll causing the USER32.dll to be injected into the target.
* But by using the gcc debugger within VSCode I found that directly after breaking the current switch case and entering the
* next Iteration, the LOAD_DLL_DEBUG_EVENT will indeed be triggered BUT not with USER32.dll.
*
* This lets me to believe that the LoadLibraryA call from the remote thread must not be executing at all.
*
* Please let me know how I can get this to be working as expected.
* Any help will be strongly appreciated!
*/
break;
case EXCEPTION_DEBUG_EVENT:
// This will prevent our target from executing after the loader has finished it's job
if(debugEvent.u.Exception.ExceptionRecord.ExceptionCode == EXCEPTION_BREAKPOINT) {
printf("Breakpoint hit at address 0x%p\n", debugEvent.u.Exception.ExceptionRecord.ExceptionAddress);
printf("Press Enter to continue...\n");
getchar(); // Wait for user input
}
break;
// Triggers every time the target or the loader executes a LoadLibrary variant
case LOAD_DLL_DEBUG_EVENT:
// Whatever the DLL is that the target wants to load we always want to get the Module Handle first...
HMODULE hModule = (HMODULE)debugEvent.u.LoadDll.lpBaseOfDll;
if(hModule == NULL) {
printf("Error: A Null-module has been logged!\n");
break;
}
// and print out the module name!
if(GetModuleFileName(hModule, modulename, MAX_PATH) <= 0) {
printf("Error: Module name could not be resolved!\n");
break;
}
printf("Module %s loaded.\n", modulename);
// Now we can check if the module's the one from which we want to execute LoadLibrary from
if(_stricmp(modulename+sysdir_length, "\\kernel32.dll") != 0) break; // break if that's not the case
// Otherwise let the User know we found what we we're searching for!
printf("Kernel32.dll found! We can now execute a LoadLibraryA externally to inject our DLL!\n");
// Then get the address of the function we're interested in calling
remote_LoadLibraryA = GetProcAddress(hModule, "LoadLibraryA");
// Quick error handling
if(remote_LoadLibraryA == NULL) {
printf("Could not resolve the Address for LoadLibraryA! Exit now...\n");
return EXIT_FAILURE;
}
// Also print the external address for debugging purposes
printf("LoadLibraryA located at %p\n", remote_LoadLibraryA);
// Create a String containing the absolute path to the DLL we want to inject
const char* user32_str = "user32.dll";
char* local_param = calloc(sysdir_length+strlen(user32_str), sizeof(char));
memcpy(local_param, sysdir, sysdir_length);
strcat(local_param, user32_str);
printf("Now trying to inject %s...\n", local_param);
// Copy String to targets memory
LPVOID ext_param = VirtualAllocEx(pi.hProcess, NULL, strlen(local_param), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if(ext_param == NULL) {
printf("Could not allocate virtual memory on target! Exit now...\n");
return EXIT_FAILURE;
}
size_t bw = 0;
if(!WriteProcessMemory(pi.hProcess, ext_param, local_param, strlen(local_param), &bw) || bw != strlen(local_param)) {
printf("Could not write to the targets memory! Exit now...\n");
return EXIT_FAILURE;
}
// This is the remote Thread that will use LoadLibraryA to inject our DLL
remoteThread = CreateRemoteThread(\
pi.hProcess, // Thread will be created inside of our target. If it was successfull we'll catch the creation within the next iteration
NULL, // I'm not quite sure about the Security Attributes needed here
0, // No specific stack reservation needed
(LPTHREAD_START_ROUTINE) remote_LoadLibraryA,
ext_param, // Pass our remotely allocated String as a Parameter to our external LoadLibrary function
DEBUG_PROCESS, // Make sure to set the debug flag to capture the Event (LoadLibrary) within the next iteration of our debugging loop
NULL\
);
// Quick Error handling
if (remoteThread == NULL) {
printf("Remote thread creation failed\n");
return EXIT_FAILURE;
}
// Notice: After breaking the switch, the next Iteration will cause the CREATE_THREAD_DEBUG_EVENT to occur
break;
case EXIT_PROCESS_DEBUG_EVENT:
printf("Process has exited.\n");
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return 0;
}
ContinueDebugEvent(debugEvent.dwProcessId, debugEvent.dwThreadId, DBG_CONTINUE);
}
free(modulename);
free(sysdir);
return 0;
}