最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c - Why do I not get a MessageBox popup when calling MessageBoxA - Stack Overflow

programmeradmin2浏览0评论

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.

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.

Share Improve this question edited Feb 8 at 0:47 JohnA asked Feb 7 at 23:38 JohnAJohnA 6361 gold badge8 silver badges23 bronze badges 6
  • 1 One item to learn is that C language is different than C++. For example, C++ has inheritance, exceptions, and function overloading. Function overloading makes compatibility with C a bit difficult. – Thomas Matthews Commented Feb 7 at 23:49
  • 1 FYI, traditionally, error messages are sent to stderr via fprintf. Many operating systems have different output "pipes" for regular and error outputs. (So you can write scripts that redirect error or standard output). – Thomas Matthews Commented Feb 7 at 23:55
  • @pmacfarlane That's what I was thinking, I'll go ahead and add some Windows tag, appreciate the tip! :-) – JohnA Commented Feb 8 at 0:46
  • You by fact call MessageBoxA("Hello World!"). But MessageBox take not 1 parameter but 4 and first parameter must be parent window handle or 0. But not string. So your code and must not work. It wrong by design. You need write shellcode, which call messagebox in process – RbMm Commented Feb 8 at 2:45
  • 2 "This also seems like quite a lot of code compared to various articles I've read online." What are those other articles? It looks like you are trying to do code injection, which is quite complicated, far more complicated than just calling MessageBoxA(nullptr, "Hello, World!", "Title", MB_OK) directly. – Raymond Chen Commented Feb 8 at 3:12
 |  Show 1 more comment

2 Answers 2

Reset to default 5

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:

  • create a DLL whose DllMain creates a local thread, and then you can have that thread do whatever you want. Then have your app allocate remoteString to hold the path to that DLL, and use CreateRemoteThread() to invoke LoadLibrary() with remoteString as its input parameter.

    This is the easiest and most common way to inject remote code using CreateRemoteThread(), as LoadLibrary() is compatible with CreateRemoteThread().

  • create a DLL that implements a SetWindowsHookEx() callback, such as WH_CALLWNDPROC or WH_GETMESSAGE, and then you can do what you want inside of that callback. Then have your app use SetWindowsHookEx() to install the DLL as the relevant hook type in the target process, and then use SendMessage() or Post[Thread]Message() to the target process to trigger the callback.

  • Have your app allocate a remote memory block that holds CPU instructions that call MessageBoxA() directly. Give that memory block PAGE_EXECUTE rights with VirtualProtectEx(). Then use CreateRemoteThread() to invoke that code block as-if it were a normal function.

    This is a difficult technique if you are not familiar with how CPU instructions work in memory.

After a quick search in the Win32 API documentation, it looks like you're using CreateRemoteThread wrong.

As defined in section parameters, lpStartAddress must exist in the remote process and lpParameter is the parameter passed to the threaded function:

[in] lpStartAddress

A pointer to the application-defined function of type LPTHREAD_START_ROUTINE to be executed by the thread and represents the starting address of the thread in the remote process. The function must exist in the remote process. For more information, see ThreadProc.

[in] lpParameter

A pointer to a variable to be passed to the thread function.

The ThreadProc documentation is a good illustration:

An application-defined function that serves as the starting address for a thread. Specify this address when calling the CreateThread, CreateRemoteThread, or CreateRemoteThreadEx function.

The LPTHREAD_START_ROUTINE type defines a pointer to this callback function. ThreadProc is a placeholder for the application-defined function name.

This is the type of function you can point to with CreateRemoteThread. This way, CreateRemoteThread and your custom function agree on a function prototype for this mechanism to work.

Another hint of misuse of the mechanism would be the prototype of MessageBoxA itself. This function appears to have only one non-optional parameter, and that is an unsigned integer ([in] UINT uType) :

int MessageBoxA(
  [in, optional] HWND   hWnd,
  [in, optional] LPCSTR lpText,
  [in, optional] LPCSTR lpCaption,
  [in]           UINT   uType
);

On the other hand, CreateRemoteThread defines lpParameter as a pointer to any type ([in] LPVOID lpParameter):

HANDLE CreateRemoteThread(
  [in]  HANDLE                 hProcess,
  [in]  LPSECURITY_ATTRIBUTES  lpThreadAttributes,
  [in]  SIZE_T                 dwStackSize,
  [in]  LPTHREAD_START_ROUTINE lpStartAddress,
  [in]  LPVOID                 lpParameter,
  [in]  DWORD                  dwCreationFlags,
  [out] LPDWORD                lpThreadId
);

The last indication in this direction is the following part of the CreateRemoteThread documentation (return values):

Note that CreateRemoteThread may succeed even if lpStartAddress points to data, code, or is not accessible.

My understanding is that the thread is created even if lpStartAddress is invalid, and then, when the thread executes, it throws an exception. Creation is successful, but nothing happens as a result of the exception. As suggested in the section on the return value, a call to GetLastError would be a good idea to get more details on what's going on and get extended error information.

My suggestion would be to first check for the error with a call to GetLastError and then try to define a function in ThreadProc format which would interpret the void* parameter as LPCSTR and this function would call MessageBoxA with all the parameters hardcoded except the character string (title -> lpCaption or contents of the message box -> lpText) which would be the newly converted LPCSTR. If you want to give your custom function more parameters, I suggest you use the same method as suggested with the LPCSTR, but this time with a custom object.

Hope this helps

发布评论

评论列表(0)

  1. 暂无评论