I have written a simple DLL injector in C++. Now, I want to unload the injected DLL using the unload function shown below. However, when I run the code, the FreeLibrary function returns 0 and the DLL is not unloaded. ( in UnLoadDLL func )
Here is the code for the relevant functions:
InjectDLL Func:
bool InjectDLL(DWORD pid, const std::string& dllPath, HMODULE& outModule) {
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
if (!hProcess) {
std::cerr << "Failed to open process. Error: " << GetLastError() << "\n";
return false;
}
// Allocate memory in remote process
LPVOID pRemoteMem = VirtualAllocEx(hProcess, nullptr, dllPath.size() + 1,
MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (!pRemoteMem) {
std::cerr << "VirtualAllocEx failed. Error: " << GetLastError() << "\n";
CloseHandle(hProcess);
return false;
}
// Write DLL path to remote process memory
if (!WriteProcessMemory(hProcess, pRemoteMem, dllPath.c_str(), dllPath.size() + 1, nullptr)) {
std::cerr << "WriteProcessMemory failed. Error: " << GetLastError() << "\n";
VirtualFreeEx(hProcess, pRemoteMem, 0, MEM_RELEASE);
CloseHandle(hProcess);
return false;
}
// Get address of LoadLibraryA
LPTHREAD_START_ROUTINE pLoadLibrary =
(LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA");
if (!pLoadLibrary) {
std::cerr << "GetProcAddress for LoadLibraryA failed.\n";
VirtualFreeEx(hProcess, pRemoteMem, 0, MEM_RELEASE);
CloseHandle(hProcess);
return false;
}
// Create remote thread to load DLL
HANDLE hThread = CreateRemoteThread(hProcess, nullptr, 0, pLoadLibrary, pRemoteMem, 0, nullptr);
if (!hThread) {
std::cerr << "CreateRemoteThread failed. Error: " << GetLastError() << "\n";
VirtualFreeEx(hProcess, pRemoteMem, 0, MEM_RELEASE);
CloseHandle(hProcess);
return false;
}
// Wait for thread to finish
WaitForSingleObject(hThread, INFINITE);
// Get handle of loaded module (returned value of LoadLibrary)
DWORD_PTR moduleBase = 0;
if (!GetExitCodeThread(hThread, (LPDWORD)&moduleBase) || moduleBase == 0) {
std::cerr << "Failed to get module base address or LoadLibrary failed.\n";
CloseHandle(hThread);
VirtualFreeEx(hProcess, pRemoteMem, 0, MEM_RELEASE);
CloseHandle(hProcess);
return false;
}
CloseHandle(hThread);
VirtualFreeEx(hProcess, pRemoteMem, 0, MEM_RELEASE);
CloseHandle(hProcess);
outModule = (HMODULE)moduleBase;
std::cout << "DLL Injected at address: " << std::hex << moduleBase << std::dec << "\n";
return true;
}
UnLoadDLL Func:
bool UnloadDLL(DWORD pid, HMODULE hModule) {
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
if (!hProcess) {
std::cerr << "Failed to open process for unload. Error: " << GetLastError() << "\n";
return false;
}
// Get address of FreeLibrary
LPTHREAD_START_ROUTINE pFreeLibrary =
(LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandleA("kernel32.dll"), "FreeLibrary");
if (!pFreeLibrary) {
std::cerr << "GetProcAddress for FreeLibrary failed.\n";
CloseHandle(hProcess);
return false;
}
// Create remote thread to free DLL
HANDLE hThread = CreateRemoteThread(hProcess, nullptr, 0, pFreeLibrary, (LPVOID)hModule, 0, nullptr);
if (!hThread) {
std::cerr << "CreateRemoteThread failed. Error: " << GetLastError() << "\n";
CloseHandle(hProcess);
return false;
}
WaitForSingleObject(hThread, INFINITE);
DWORD exitCode = 0;
if (!GetExitCodeThread(hThread, &exitCode)) {
std::cerr << "GetExitCodeThread failed. Error: " << GetLastError() << "\n";
CloseHandle(hThread);
CloseHandle(hProcess);
return false;
}
CloseHandle(hThread);
CloseHandle(hProcess);
if (exitCode == 0) {
std::cerr << "FreeLibrary failed inside remote process.\n";
return false;
}
std::cout << "DLL Unloaded successfully.\n";
return true;
}
main:
int main() {
DWORD pid;
std::string dllPath = "D:\\programming\\injectDll\\main.dll";
std::cout << "Enter target process ID (PID): ";
std::cin >> pid;
// std::cout << "Enter full DLL path to inject: ";
// std::cin >> dllPath;
HMODULE injectedModule = nullptr;
if (InjectDLL(pid, dllPath, injectedModule)) {
std::cout << "Press Enter to unload DLL...";
std::cin.ignore();
std::cin.get();
UnloadDLL(pid, injectedModule);
}
return 0;
}
Can anyone help me understand why FreeLibrary fails and how to properly unload the injected DLL?
Thank you!
FreeLibraryAndExitThreaddoesn't have the appropriate prototype for a thread function.GetExitCodeThreadtrick to get the loaded DLL handle won't work for 64bitGetExitCodeThread()gives you a 32bit value, so a 64bit module handle will be truncated, which would explain whyFreeLibrary()fails. You will have to get the module handle in a different way. See the duplicate questions