How to inject a x86 dll into a 32bits process

112 views Asked by At

I have a qt gui build as a DLL when compiled as x64 it works correctly, however, when I compile it as x86 and inject it into a 32bits process, its DllMain entry never gets called.

After a lot of time searching and recompiling the qt source I figured out it maybe is not a problem with qt because I have been able to reproduce the issue with a "clean" "non-qt" dll project:

source

#include <Windows.h>
BOOL APIENTRY DllMain( HMODULE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    OutputDebugString(L"DllMain");
    return TRUE;
}

And this is how I'm injecting the dll:

void injectDLL(DWORD processID, const QString& dllPath)
{
    // Open the target process.
    HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processID);
    if (!processHandle)
    {
        qDebug() << "Failed to open process";
        return;
    }

    // Allocate memory in the target process.
    LPVOID remoteMemory = VirtualAllocEx(processHandle, NULL, dllPath.length(), MEM_COMMIT, PAGE_READWRITE);
    if (!remoteMemory)
    {
        qDebug() << "Failed to allocate memory in the remote process";
        CloseHandle(processHandle);
        return;
    }

    // Write the path of the DLL to be injected into the allocated memory in the target process.
    if (!WriteProcessMemory(processHandle, remoteMemory, dllPath.toStdWString().c_str(), dllPath.length() * sizeof(wchar_t), NULL))
    {
        qDebug() << "Failed to write path to the remote process memory";
        VirtualFreeEx(processHandle, remoteMemory, 0, MEM_RELEASE);
        CloseHandle(processHandle);
        return;
    }

    // Get the address of the LoadLibraryW function.
    LPVOID loadLibraryAddress = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW");
    if (!loadLibraryAddress)
    {
        qDebug() << "Failed to get the address of LoadLibraryW";
        VirtualFreeEx(processHandle, remoteMemory, 0, MEM_RELEASE);
        CloseHandle(processHandle);
        return;
    }

    // Create a remote thread in the target process and pass the address of LoadLibraryW and the address of the allocated memory as arguments.
    HANDLE remoteThread = CreateRemoteThread(processHandle, NULL, 0, (LPTHREAD_START_ROUTINE)loadLibraryAddress, remoteMemory, 0, NULL);
    if (!remoteThread)
    {
        qDebug() << "Failed to create a remote thread in the target process";
        VirtualFreeEx(processHandle, remoteMemory, 0, MEM_RELEASE);
        CloseHandle(processHandle);
        return;
    }

    // Wait for the remote thread to finish.
    WaitForSingleObject(remoteThread, INFINITE);

    // Cleanup.
    CloseHandle(remoteThread);
    VirtualFreeEx(processHandle, remoteMemory, 0, MEM_RELEASE);
    CloseHandle(processHandle);
}


int main()
{
    // wmplayer.exe pid
    injectDLL(51836, "D:/repos/Dll/Debug/Dll.dll");
    return 0;
}

I'm testing injecting the dll into C:\Program Files (x86)\Windows Media Player\wmplayer.exe which is an x32 exe.

I'm debugging the dll on vs22 but after injected it never gets called, and neither the OutputDebugString(L"DllMain"); is printed.

I'm trying to fix the problem on my qt dll, which probably is the same as the sample I provided above.

What I'm missing or doing wrong?

1

There are 1 answers

1
Hunter Pollock On

Are you sure the process id that is passed into the injectDll() method is correct? I suggest implementing the following method and declaring it like this.

int findProcess(const char* processName) {
    HANDLE handle
    PROCESSENTRY32 pe;
    int pid = 0;
    BOOL result;

    handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    if (handle == INVALID_HANDLE_VALUE)
        return pid;

    pe.dwSize = sizeof(PROCESSENTRY32);
    result = Process32First(handle, &pe);
    while (result) {
        if (strcmp(processName, pe.szExeFile) == 0) {
            pid = pe.th32ProcessId;
            break;
        }
        result = Process32Next(handle, &pe);
    }

    CloseHandle(handle);
    return pid;
}

void injectDLL(DWORD processID, const QString& dllPath)
{
    HANDLE processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processID);
    if (!processHandle)
    {
        qDebug() << "Failed to open process";
        return;
    }

    LPVOID remoteMemory = VirtualAllocEx(processHandle, NULL,
        dllPath.length(), MEM_COMMIT, PAGE_READWRITE);
    if (!remoteMemory)
    {
        qDebug() << "Failed to allocate memory in the remote process";
        CloseHandle(processHandle);
        return;
    }

    if (!WriteProcessMemory(processHandle, remoteMemory, 
        dllPath.toStdWString().c_str(), dllPath.length() * sizeof(wchar_t), NULL))
    {
        qDebug() << "Failed to write path to the remote process memory";
        VirtualFreeEx(processHandle, remoteMemory, 0, MEM_RELEASE);
        CloseHandle(processHandle);
        return;
    }

    LPVOID loadLibraryAddress = 
        GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW");
    if (!loadLibraryAddress)
    {
        qDebug() << "Failed to get the address of LoadLibraryW";
        VirtualFreeEx(processHandle, remoteMemory, 0, MEM_RELEASE);
        CloseHandle(processHandle);
        return;
    }

    
    HANDLE remoteThread = CreateRemoteThread(processHandle, NULL, 0, 
        (LPTHREAD_START_ROUTINE)loadLibraryAddress, remoteMemory, 0, NULL);
    if (!remoteThread)
    {
        qDebug() << "Failed to create a remote thread in the target process";
        VirtualFreeEx(processHandle, remoteMemory, 0, MEM_RELEASE);
        CloseHandle(processHandle);
        return;
    }

    WaitForSingleObject(remoteThread, INFINITE);

    CloseHandle(remoteThread);
    VirtualFreeEx(processHandle, remoteMemory, 0, MEM_RELEASE);
    CloseHandle(processHandle);
}

int main()
{
    injectDLL(findProcess("wmplayer.exe"), "D:/repos/Dll/Debug/Dll.dll");
    return 0;
}