cwindowswinapicode-injectiondll-injection

Why do I not get a MessageBox popup when calling MessageBoxA


So I'm learning C coming from years of C# and I figured that a good starting project would be something a bit more low level.

I'm trying to invoke a MessageBox in a blank WPF App and it seems like everything would work but for some reason I'm not getting a MessageBox.

Both the WpfApp and the App I built are both compiled into x64 versions so I don't see why there would be any architectural issues.

Debug Output looks fine too.

I've tried running both applications with administrator privileges. I've tried invoking into different apps such as Notepad (both 32 and 64 bit version), as well as the Calculator.

Process ID for WpfApp1.exe found: 948
Target process found. PID: 948
Opened handle to process.
Handle to user32.dll loaded successfully.
Address of MessageBoxA: 00007ffc67fc8b70
Allocated memory at remote address: 00000262a8500000
Successfully wrote message to remote memory.
Remote thread created successfully.
Remote thread completed.

But I guess there's something wrong here?

#include <stdio.h>
#include <windows.h>
#include <tlhelp32.h>
#include <unistd.h>

DWORD GetPID(const char *processName);

int main(void) {
    DWORD processId = GetPID("WpfApp1.exe");

    if (!processId) {
        printf("Process not found\n");
        fflush(stdout);
        return 1;
    }
    printf("Target process found. PID: %lu\n", processId);
    fflush(stdout);

    HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId);
    if (!hProcess) {
        printf("Failed to open process. Error code: %lu\n", GetLastError());
        fflush(stdout);
        return 1;
    }
    printf("Opened handle to process.\n");
    fflush(stdout);

    // Explicitly load user32.dll
    HMODULE hUser32 = LoadLibrary("user32.dll");
    if (!hUser32) {
        printf("Failed to load user32.dll. Error code: %lu\n", GetLastError());
        fflush(stdout);
        CloseHandle(hProcess);
        return 1;
    }
    printf("Handle to user32.dll loaded successfully.\n");
    fflush(stdout);

    LPVOID msgBoxAddress = (LPVOID)GetProcAddress(hUser32, "MessageBoxA");
    if (!msgBoxAddress) {
        printf("Failed to find address of MessageBoxA. Error code: %lu\n", GetLastError());
        fflush(stdout);
        FreeLibrary(hUser32);
        CloseHandle(hProcess);
        return 1;
    }
    printf("Address of MessageBoxA: %p\n", msgBoxAddress);
    fflush(stdout);

    LPVOID remoteString = VirtualAllocEx(hProcess, NULL, 256, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (!remoteString) {
        printf("Failed to allocate memory in target process. Error code: %lu\n", GetLastError());
        fflush(stdout);
        FreeLibrary(hUser32);
        CloseHandle(hProcess);
        return 1;
    }
    printf("Allocated memory at remote address: %p\n", remoteString);
    fflush(stdout);

    if (!WriteProcessMemory(hProcess, remoteString, "Hello World!", 13, NULL)) {
        printf("Failed to write to remote memory. Error code: %lu\n", GetLastError());
        fflush(stdout);
        VirtualFreeEx(hProcess, remoteString, 0, MEM_RELEASE);
        FreeLibrary(hUser32);
        CloseHandle(hProcess);
        return 1;
    }
    printf("Successfully wrote message to remote memory.\n");
    fflush(stdout);

    HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)msgBoxAddress, remoteString, 0, NULL);
    if (!hThread) {
        printf("Failed to create remote thread. Error code: %lu\n", GetLastError());
        fflush(stdout);
        VirtualFreeEx(hProcess, remoteString, 0, MEM_RELEASE);
        FreeLibrary(hUser32);
        CloseHandle(hProcess);
        return 1;
    }
    printf("Remote thread created successfully.\n");
    fflush(stdout);

    WaitForSingleObject(hThread, INFINITE);
    printf("Remote thread completed.\n");
    fflush(stdout);

    VirtualFreeEx(hProcess, remoteString, 0, MEM_RELEASE);
    FreeLibrary(hUser32);
    CloseHandle(hThread);
    CloseHandle(hProcess);
    return 0;
}

DWORD GetPID(const char *processName) {
    PROCESSENTRY32 processEntry;
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

    if (hSnapshot == INVALID_HANDLE_VALUE) {
        printf("Failed to create snapshot. Error code: %lu\n", GetLastError());
        fflush(stdout);
        return 0;
    }

    processEntry.dwSize = sizeof(processEntry);
    if (Process32First(hSnapshot, &processEntry)) {
        do {
            if (strcmp(processEntry.szExeFile, processName) == 0) {
                DWORD pid = processEntry.th32ProcessID;
                CloseHandle(hSnapshot);
                printf("Process ID for %s found: %lu\n", processName, pid);
                fflush(stdout);
                return pid;
            }
        } while (Process32Next(hSnapshot, &processEntry));
    }

    printf("Failed to find process: %s\n", processName);
    fflush(stdout);
    CloseHandle(hSnapshot);
    return 0;
}

This also seems like quite a lot of code compared to various articles I've read online.


Solution

  • CreateRemoteThread() simply cannot call MessagBoxA() directly, as MessageBoxA() does not have the correct function signature that CreateRemoteThread() is expecting:

    int WINAPI MessageBoxA(
      [in, optional] HWND   hWnd,
      [in, optional] LPCSTR lpText,
      [in, optional] LPCSTR lpCaption,
      [in]           UINT   uType
    );
    
    DWORD WINAPI ThreadProc(
      _In_ LPVOID lpParameter
    );
    

    CreateRemoteThread() can pass only 1 parameter to the target function, but MessageBoxA() takes 4 parameters.

    So, in this situation, you will have to change your approach. You can either: