0

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!

5
  • FreeLibraryAndExitThread doesn't have the appropriate prototype for a thread function. Commented Jun 9 at 15:07
  • @molbdnilo Yes, I initially wrote it incorrectly, but I have edited the question and corrected it to FreeLibrary Commented Jun 9 at 15:17
  • 1
    Are you compiling for 32bit or 64bit? The GetExitCodeThread trick to get the loaded DLL handle won't work for 64bit Commented Jun 9 at 18:03
  • @RemyLebeau 64bit, why ? Commented Jun 9 at 18:46
  • 1
    @MahdiKarami Because GetExitCodeThread() gives you a 32bit value, so a 64bit module handle will be truncated, which would explain why FreeLibrary() fails. You will have to get the module handle in a different way. See the duplicate questions Commented Jun 9 at 18:57

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.