Então, estou aprendendo C depois de anos de C# e imaginei que um bom projeto inicial seria algo um pouco mais simples.
Estou tentando invocar uma MessageBox em um aplicativo WPF em branco e parece que tudo funcionaria, mas por algum motivo não estou obtendo uma MessageBox.
Tanto o WpfApp quanto o aplicativo que criei são compilados em versões x64, então não vejo por que haveria problemas de arquitetura.
A saída de depuração também parece boa.
Tentei executar ambos os aplicativos com privilégios de administrador. Tentei invocar em diferentes aplicativos, como o Notepad (versão de 32 e 64 bits), assim como a Calculadora.
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.
Mas acho que há algo errado aqui?
#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;
}
Isso também parece bastante código comparado a vários artigos que li online.
CreateRemoteThread()
simplesmente não pode chamarMessagBoxA()
diretamente , poisMessageBoxA()
não tem a assinatura de função correta queCreateRemoteThread()
está esperando:CreateRemoteThread()
pode passar apenas 1 parâmetro para a função de destino, masMessageBoxA()
aceita 4 parâmetros.Então, nessa situação, você terá que mudar sua abordagem. Você pode:
crie uma DLL que
DllMain
crie um thread local, e então você pode fazer com que esse thread faça o que você quiser. Então faça com que seu aplicativo aloqueremoteString
para manter o caminho para essa DLL, e useCreateRemoteThread()
para invocarLoadLibrary()
comremoteString
como seu parâmetro de entrada.Esta é a maneira mais fácil e comum de injetar código remoto usando
CreateRemoteThread()
, poisLoadLibrary()
é compatível comCreateRemoteThread()
.crie uma DLL que implemente um
SetWindowsHookEx()
retorno de chamada, comoWH_CALLWNDPROC
ouWH_GETMESSAGE
, e então você pode fazer o que quiser dentro desse retorno de chamada. Então faça seu aplicativo usarSetWindowsHookEx()
para instalar a DLL como o tipo de hook relevante no processo de destino, e então useSendMessage()
ouPost[Thread]Message()
para o processo de destino para disparar o retorno de chamada.Faça com que seu aplicativo aloque um bloco de memória remoto que contenha instruções da CPU que chamam
MessageBoxA()
diretamente. DêPAGE_EXECUTE
direitos a esse bloco de memória comVirtualProtectEx()
. Então useCreateRemoteThread()
para invocar esse bloco de código como se fosse uma função normal.Esta é uma técnica difícil se você não estiver familiarizado com o funcionamento das instruções da CPU na memória.
Após uma rápida pesquisa na documentação da API do Win32, parece que você está usando
CreateRemoteThread
errado.Conforme definido na seção parameters ,
lpStartAddress
deve existir no processo remoto elpParameter
é o parâmetro passado para a função encadeada:A
ThreadProc
documentação é uma boa ilustração:Este é o tipo de função para a qual você pode apontar com
CreateRemoteThread
. Desta forma,CreateRemoteThread
e sua função personalizada concordam com um protótipo de função para que este mecanismo funcione.Outra dica de mau uso do mecanismo seria o protótipo dele
MessageBoxA
mesmo. Esta função parece ter apenas um parâmetro não opcional, que é um inteiro sem sinal ([in] UINT uType
) :Por outro lado,
CreateRemoteThread
definelpParameter
como um ponteiro para qualquer tipo ([in] LPVOID lpParameter
):A última indicação nessa direção é a seguinte parte da
CreateRemoteThread
documentação ( valores de retorno ):Meu entendimento é que o thread é criado mesmo se
lpStartAddress
for inválido, e então, quando o thread é executado, ele lança uma exceção. A criação é bem-sucedida, mas nada acontece como resultado da exceção. Conforme sugerido na seção sobre o valor de retorno, uma chamada paraGetLastError
seria uma boa ideia para obter mais detalhes sobre o que está acontecendo e obter informações de erro estendidas.Minha sugestão seria primeiro verificar o erro com uma chamada para
GetLastError
e então tentar definir uma função noThreadProc
formato que interpretaria o parâmetro void* como LPCSTR e essa função chamariaMessageBoxA
com todos os parâmetros codificados, exceto a sequência de caracteres (title ->lpCaption
ou contents of the message box ->lpText
) que seria o LPCSTR recém-convertido. Se você quiser dar mais parâmetros à sua função personalizada, sugiro que use o mesmo método sugerido com o LPCSTR, mas dessa vez com um objeto personalizado.Espero que isso ajude