Search code examples
c++winapivirtual-memorydll-injection

CreateRemoteThread MessageBoxA causes remote process to crash


I made a simple program that calculates the RVA of the function MessaegBoxA in user32.dll and then adds that offset to the base loading address of the dll in the remote process's memory to get the address of the function MessageBoxA. I made a dummy program that outputs the address of the function in its memory using GetProcAddress and then implement my own function in my program to display the address it calculated for the same function in the remote process. They always match so I'm certain my function for finding the address of MessageBoxA in remote process's is not the problem.

I made a struct that contains all the necessary information and parameters needed for ThreadProc to execute MessageBoxA in the remote process once I load it using WriteProcessMemory.

typedef struct
{
    typedef int (WINAPI* _MessageBoxA)(HWND, LPCSTR, LPCSTR, UINT);
    _MessageBoxA MessageBoxA;
    //These are the four parameters passed to MessageBoxA
    HWND hwnd;
    LPCSTR msg;
    LPCSTR caption;
    UINT mb;
}MB_DATA, *PMB_DATA;

When I try this on my own dummy program, the message box shows up but with weird text contrary to the strings I specified in the msg and caption members of MB_DATA. It says the following for the caption asic_string::erase and for the message it says u). And when I try to do this in any other process than my dummy process it crashes the remote process. I made a function to iterate through the modules that have been loaded in the process with tlhelp32 functions to make sure user32.dll is present and it is and my function for finding the address of the function in the process doesn't return NULL like it would if the dll were not present.

Here all relevant functions and my main function:

dependencies.hpp

#include <windows.h>
#include <tlhelp32.h>
#include <stdio.h>
#include <iostream>
using namespace std;

typedef struct
{
    typedef int (WINAPI* _MessageBoxA)(HWND, LPCSTR, LPCSTR, UINT);
    _MessageBoxA MessageBoxA;
    HWND hwnd;
    LPCSTR msg;
    LPCSTR caption;
    UINT mb;
}MB_DATA, *PMB_DATA;

//Map the dll into memory
void* GetFileImage(char path[])
{
    HANDLE hFile = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL);
    if(hFile == INVALID_HANDLE_VALUE){printf("Error getting file handle: %d", (int)GetLastError());return NULL;}

    HANDLE file_map = CreateFileMapping(hFile, NULL, PAGE_READONLY|SEC_IMAGE, 0, 0, "KernelMap");
    if(file_map == INVALID_HANDLE_VALUE){printf("Error mapping file: %d", (int)GetLastError());return NULL;}

    LPVOID file_image = MapViewOfFile(file_map, FILE_MAP_READ, 0, 0, 0);
    if(file_image == 0){printf("Error getting mapped view: %d", (int)GetLastError());return NULL;}

    return file_image;
}

//Get to the function export directory and find the offset for the specified function from the 
//address in memory the dll was loaded at
DWORD_PTR RVAddress(char* image, const char* proc_name)
{
    PIMAGE_DOS_HEADER pDos_hdr = (PIMAGE_DOS_HEADER)image;
    PIMAGE_NT_HEADERS pNt_hdr = (PIMAGE_NT_HEADERS)(image+pDos_hdr->e_lfanew);
    IMAGE_OPTIONAL_HEADER opt_hdr = pNt_hdr->OptionalHeader;
    IMAGE_DATA_DIRECTORY exp_entry = opt_hdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
    PIMAGE_EXPORT_DIRECTORY pExp_dir = (PIMAGE_EXPORT_DIRECTORY)(image+exp_entry.VirtualAddress);

    DWORD* func_table = (DWORD*)(image+pExp_dir->AddressOfFunctions);
    WORD* ord_table = (WORD*)(image+pExp_dir->AddressOfNameOrdinals);
    DWORD* name_table = (DWORD*)(image+pExp_dir->AddressOfNames);

    for(u_int i=0;i<pExp_dir->NumberOfNames;i++)
    {
        char* name = (char*)(image+name_table[i]);
        if(strcmp(proc_name, name) == 0)
        {
            return (DWORD_PTR)func_table[ord_table[i]];
        }
    }

    return (DWORD_PTR)0;
}

//Add the RVA returned from RVAddress to the address of the dll to find the function in the
//process memory
LPVOID GetProcAddressEx(DWORD dwPid, char* mod_path, char* function_name, char* mod_name)
{
    HANDLE hSnapshot = INVALID_HANDLE_VALUE;
    MODULEENTRY32 me32;
    me32.dwSize = sizeof(MODULEENTRY32);

    hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE|TH32CS_SNAPMODULE32, dwPid);
    if(hSnapshot == INVALID_HANDLE_VALUE){printf("Snapshot failed");return 0;}

    if(!(Module32First(hSnapshot, &me32)))
    {
        printf("Mod32First failed");
        return 0;
    }

    BOOL found = FALSE;

    while(Module32Next(hSnapshot, &me32))
    {
        if(stricmp(me32.szModule, mod_name) == 0)
        {
            CloseHandle(hSnapshot);
            found = TRUE;
            break;
        }
    }

    if(found == FALSE){return 0;}

    DWORD_PTR RVA = (DWORD_PTR)RVAddress((char*)GetFileImage(mod_path), function_name);

    LPVOID func_addr = me32.modBaseAddr+RVA;
    return func_addr;
}

main.cpp

#include "dependencies.hpp"
#define FUNC_SIZE 1024

typedef int (WINAPI* _MessageBoxA)(HWND, LPCSTR, LPCSTR, UINT);

int main()
{
    MB_DATA mb_data;
    mb_data.hwnd = NULL;
    mb_data.msg = "Hey";
    mb_data.caption = "Yo";
    mb_data.mb = MB_OK;

    SIZE_T nBytes = 0;

    char proc_path[MAX_PATH];
    char kernel_path[MAX_PATH];
    char user32_path[MAX_PATH];

    //get full path to the current process and store it in proc_path
    GetModuleFileName(GetModuleHandle(NULL), proc_path, MAX_PATH);
    //get full path to kernel32.dll and store it in kernel_path
    GetModuleFileName(GetModuleHandle("kernel32.dll"), kernel_path, MAX_PATH);
    //get full path to user3.dll and store it in user32_path
    GetModuleFileName(GetModuleHandle("user32.dll"), user32_path, MAX_PATH);

    //show all processes running and their PID's
    system("tasklist");

    DWORD dwPid = 0;
    printf("PID: ");
    scanf("%lu", &dwPid);
    //if dwPid is 0 assign it the pid of the current process
    if(dwPid == 0)
    {
        dwPid = GetCurrentProcessId();
    }

    //Get a handle to the process with all access rights
    HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
    //make sure the handle is valid
    if(hProc == NULL){printf("Error obtaining handle to process: %lu", GetLastError());return 1;}

    //Get the address of the function in the remote process
    LPVOID _MessageBoxA1 = GetProcAddressEx(dwPid, user32_path, (char*)"MessageBoxA", (char*)"user32.dll");
    //assign the pointer to the address to the member MessageBoxA of the MB_DATA structure
    mb_data.MessageBoxA = (_MessageBoxA)_MessageBoxA1;

    //allocate 2mb for our the ThreadProc callback function and the MB_DATA structure
    LPVOID lpBase = VirtualAllocEx(hProc, NULL, 2048, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    //did the allocation work
    if(lpBase == NULL){printf("Error allocating space: %lu", GetLastError());return 1;}

    //so I can check what was written with CheatEngine
    cout << "Base address of memory allocated in remote process: " << lpBase << endl;

    //Write the function into memory
    if(WriteProcessMemory(hProc, lpBase, (LPVOID)ThreadProc, FUNC_SIZE, &nBytes) == 0)
    {
        printf("Error writing function to process");
        return 1;
    }

    //the address the space left after having written ThreadProc into memory 
    LPVOID lpBuffer = lpBase+FUNC_SIZE;

    //Write the MB_DATA structure into the memory of the remote process
    if(WriteProcessMemory(hProc, lpBuffer, &mb_data, sizeof(MB_DATA), &nBytes) == 0)
    {
        printf("Error writing buffer to process");
    }

    //Run the ThreadProc function passing the MB_DATA structure to it as its lpParam parameter
    if(CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)lpBase, lpBuffer, 0, NULL) == NULL)
    {
        printf("Error creating remote thread: %lu", GetLastError());
        return 1;
    }

    //print a list of all the dll's being used by the process
    EnumerateModules(dwPid);

    system("pause");

    return 0;
}

Any help would be greatly appreciated. Thank you very much! :)


Solution

  • mb_data.msg and mb_data.caption point to what in the another process ?? this is already enough for crash error. what is in ThreadProc not visible, but i not sure that it have no relocs. really ThreadProc must be member function of MB_DATA and access only it members. from you post obviously that you not debug remote process at injection time. also obviously that task is over your current level