Sometimes we are working from which we would like to extract data on the fly and use it in our own program. However, the developer doesn’t provide an API, either deliberately, to block the use of his application, or just by lack of time.
For examples, video games which (generally) do not offer an API allowing you to see the enemy’s hit points, or even to recover the entire map.
Other times it will be a more professional application, displaying data, but not allowing it to be simply retrieved for processing. We can also try to automate the operation of this kind of software by creating a bot.
Today we will see the basics for creating this kind of API through DLL injection.
C/CPP DLL injection
The easiest solution to injecting code into a native Windows process is through DLL injection.
To do this, just follow these steps:
- Create a DLL running the code of your choice in the DllMain entry point
- Allocate memory in the target process space to store the DLL location
- Create a thread in the target process that will run LoadLibraryA from the Windows API
- Once this is done, the code located in the DLL’s DllMain will be executed in the address space of the target process. This means that it will be possible to simply read the memory of the target process, but also to call functions of this process.
Here is the commented code of a small DLL injector:
#include
#include
int main( int argc, char **argv) {
// We get the path of the DLL to inject from the command line
string DLLPath;
if (argc > 1) {
DLLPath = string(argv[1]);
}
else {
cerr << "Error: DLL path not defined" << endl; return EXIT_FAILURE;
}
// The second argument of the line is the ID of the process that will be injected
// If it is not present, we exit
string ProcessIdStr;
if (argc > 2) {
ProcessIdStr = argv[2];
}
else {
cerr << "Error: PID not defined" << endl;
return EXIT_FAILURE;
}
DWORD ID = std::stoi(ProcessIdStr);
// Then we open it with the appropriate permissions
HANDLE Process = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, ID);
// We get the address of the LoadLibrary instruction in the target process
LPVOID LoadLibrary = (LPVOID) GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
// We allocate a memory space in this process to store the name of the DLL that it will load
LPVOID Memory = (LPVOID) VirtualAllocEx(Process, NULL, DLLPath.length() + 1, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
// We write in this memory space the name of the DLL to inject
WriteProcessMemory(Process, (LPVOID) Memory, DLLPath.c_str(), DLLPath.length() + 1, NULL);
// And finally we create a thread running in the context of the target process
// This thread will only execute a LoadLibrary instruction with the path of the DLL to inject as argument.
HANDLE RemoteThread = CreateRemoteThread(Process, NULL, NULL, (LPTHREAD_START_ROUTINE) LoadLibrary, (LPVOID) Memory, NULL, NULL);
if(RemoteThread == NULL) {
cerr << "Error: Failed to create remote thread (" << GetLastError() << ")" << endl;
}
else {
cerr << "Info: Remote thread successfully created" << endl;
}
// Finally we close access to the process and free the memory that had been allocated to it
CloseHandle(Process);
VirtualFreeEx(Process, (LPVOID) Memory, 0, MEM_RELEASE);
return EXIT_SUCCESS;
}
This code takes as parameter the path of the DLL to inject, and the PID of the target process. Once launched the DllMain function, the injected DLL will be executed in the context of the target process, as if it were a thread of that process.
This means that he will be able to have access to all the variables of this process, but also to perform functions thereof.
In a future article, we’ll see how to inject code without going through DLL injection, then we’ll also see how to use DLL injection to manually load a C# assembly in a Mono Runtime environment.
Have a good day!
Leave A Comment