Hi,
Podemos utilizar diversos métodos para injetar códigos executáveis em um processo. Injetar uma DLL no aplicativo (DLL Injection) é um dos mais comuns. Em tópicos anteriores, falei um pouco sobre tal técnica. Neste, abordarei uma forma que, apesar de diferente implementação, segue uma lógica parecida.
A idéia básica seria, em vez de injetar uma DLL externa no processo alvo, copiar em seu espaço de memória o código que deverá ser executado e, posteriormente, fazer com que este processo execute-o.
Antes de escrevemos o código da função, precisamos alocar um espaço suficiente para comportá-lo. Isso pode ser feito através da função VirtualAllocEx(). Sintaxe:
LPVOID WINAPI VirtualAllocEx(
HANDLE hProcess, // Handle do processo onde queremos alocar um espaço
LPVOID lpAddress, // Endereço inicial da região a ser alocada
DWORD dwSize, // Tamanho, em bytes, do espaço a ser alocado
DWORD flAllocationType, // Tipo de alocação
DWORD flProtect // Tipo de acesso
);
Para escrever o código, podemos utilizar a função WriteProcessMemory(), cuja definição é:
BOOL WriteProcessMemory(
HANDLE hProcess, // Handle do processo em que os dados serão escritos
LPVOID lpBaseAddress, // Endereço inicial no qual irá se escrever
LPVOID lpBuffer, // Buffer a ser escrito
DWORD cbWrite, // Número de bytes a escrever
LPDWORD lpNumberOfBytesWritten // Retorno dos bytes escritos
);
E para executar o código após ter sido escrito:
HANDLE CreateRemoteThread(
HANDLE hProcess, // Processo no qual a thread será criada
LPSECURITY_ATTRIBUTES lpThreadAttributes, // Ponteiro para uma estrutura LPSECURITY_ATTRIBUTES
DWORD dwStackSize, // Tamanho inicial da stack em bytes
LPTHREAD_START_ROUTINE lpStartAddress, // Endereço da função que será executada
LPVOID lpParameter, // Argumento passado à função
DWORD dwCreationFlags, // Flags para criação da thread
LPDWORD lpThreadId // Retorno da identificação da thread
);
O que devemos fazer então é, basicamente, alocar um espaço para armazenar o código da função e seu argumento, escrevê-los em seus respectivos endereços, e chamar a função CreateRemoteThread() contendo os endereços da função e do argumento escritos no processo.
Segue abaixo um código de exemplo que mostra uma mensagem no processo "notepad.exe":
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <tlhelp32.h> // Listar processos
////////////////////////////////////////////////////////////////////////////////
/* Ponteiro para a função MessageBoxA localizada em user32.dll
Protótipo:
int MessageBox(HWND hWnd, // Handle da janela onde a mensagem será exibida
LPCTSTR lpText, // Texto
LPCTSTR lpCaption, // Título
UINT uType // Estilo
)
*/
typedef int (WINAPI * pMessageBox)(HWND,LPCSTR,LPCSTR,UINT);
////////////////////////////////////////////////////////////////////////////////
/* Definições do ponteiro para a função */
typedef struct
{
pMessageBox MsgBox; // int MessageBox(HWND,LPCSTR,LPCSTR,UINT);
HWND hWnd; // HWND hWnd
char Msg[255]; // LPCSTR lpText
char Titulo[255]; // LPCSTR lpCaption
UINT Tipo; // UINT uType
}Argumentos;
////////////////////////////////////////////////////////////////////////////////
/* Funções internas do programa */
DWORD ObterPID(LPCSTR NomeProcesso);
LPVOID Injetar(DWORD pID, LPVOID pFuncao, Argumentos * pArg);
BOOL ElevarPrivilegios();
////////////////////////////////////////////////////////////////////////////////
/* Função que será injetada e executada no processo remoto. */
static DWORD WINAPI FuncaoCodigo(Argumentos * p) // p = ponteiro para uma estrutura Argumentos
{
p->MsgBox(p->hWnd,p->Msg,p->Titulo,p->Tipo); // Chama a função MessageBox
return 0;
}
////////////////////////////////////////////////////////////////////////////////
// Esta função será utilizada para calcular o tamanho de FuncaoCodigo()
static void FimFuncaoCodigo() { }
////////////////////////////////////////////////////////////////////////////////
int main()
{
DWORD pID;
LPVOID Addr;
Argumentos Arg;
// Carrega a DLL user32 para obter o endereço da função MessageBoxA
HINSTANCE DLL = LoadLibrary("user32.dll");
if(!DLL)
return 1;
memset(&Arg,0x0,sizeof(Arg));
Arg.MsgBox = (pMessageBox)GetProcAddress(DLL,"MessageBoxA");
Arg.hWnd = 0;
strncpy(Arg.Msg,"Exemplo xD",sizeof(Arg.Msg));
strncpy(Arg.Titulo,"Teste",sizeof(Arg.Titulo));
Arg.Tipo = MB_ICONASTERISK;
FreeLibrary(DLL); // Nenhum outro uso será feito com a DLL carregada
pID = ObterPID("notepad.exe"); // Processo alvo
Addr = Injetar(pID,&FuncaoCodigo,&Arg); // Injeta e executa a função
if(!Addr)
printf("Erro ao injetar codigo!");
else
printf("Codigo injetado com sucesso no endereco: 0x%X.",Addr);
getchar();
return 0;
}
////////////////////////////////////////////////////////////////////////////////
/* Obter o PID de um processo através do seu nome */
DWORD ObterPID(LPCSTR NomeProcesso)
{
PROCESSENTRY32 pProcEntry;
HANDLE SnapShot;
DWORD pID = 0;
if(!(SnapShot=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0)))
return 0;
if(!(SnapShot=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0)))
return 0;
memset(&pProcEntry,0x0,sizeof(PROCESSENTRY32));
pProcEntry.dwSize=sizeof(PROCESSENTRY32);
if(!Process32First(SnapShot,&pProcEntry))
return 0;
while(Process32Next(SnapShot,&pProcEntry)) // Percorre a lista de processos
if(!stricmp(pProcEntry.szExeFile,NomeProcesso)) // Compara o nome
{
pID = pProcEntry.th32ProcessID; // Obtém o PID
break;
}
CloseHandle(SnapShot);
return pID;
}
////////////////////////////////////////////////////////////////////////////////
/* Injetar e executar a função no processo */
LPVOID Injetar(DWORD pID, LPVOID pFuncao, Argumentos * pArg)
{
HANDLE Proc;
LPVOID AddrFunc, AddrArg;
DWORD BytesFunc, BytesArg;
if(!pID || !pFuncao)
return NULL;
if(!eleva_privilegios())
return NULL;
Proc = OpenProcess(PROCESS_ALL_ACCESS,FALSE,pID); // Abre o processo alvo
if(!Proc)
return NULL;
// Calcula o tamanho da função a ser injetada
BytesFunc = (DWORD)&FimFuncaoCodigo - (DWORD)&FuncaoCodigo;
// Tamanho da estrutura Argumentos
BytesArg = sizeof(Argumentos);
// Alocar um espaço destinado à função e ao argumento que será passado a ela
AddrFunc = VirtualAllocEx(Proc,0,BytesFunc,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
AddrArg = VirtualAllocEx(Proc,0,BytesArg,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
if(!AddrFunc || ! AddrArg)
goto Finalizar;
if(!WriteProcessMemory(Proc,AddrFunc,pFuncao,BytesFunc,NULL) || // Escreve a função
!WriteProcessMemory(Proc,AddrArg,pArg,BytesArg,NULL)) // Escreve o argumento
goto Finalizar;
//Executa a função passando o argumento
if(!CreateRemoteThread(Proc, NULL, 0, (LPTHREAD_START_ROUTINE)AddrFunc, AddrArg,0,NULL))
AddrFunc = NULL;
Finalizar: // Liberar o espaço alocado e fechar o handle do processo
if(AddrFunc)
VirtualFreeEx(Proc,AddrFunc,BytesFunc,MEM_RELEASE);
if(AddrArg)
VirtualFreeEx(Proc,AddrArg,BytesArg,MEM_RELEASE);
CloseHandle(Proc);
return AddrFunc; // Endereço no qual começa o código da função injeta no processo alvo
}
////////////////////////////////////////////////////////////////////////////////
/* Elevar privilégios do programa. Modo debug -> acesso a processos do sistema */
int eleva_privilegios()
{
TOKEN_PRIVILEGES tp;
HANDLE hToken;
LUID luid;
if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &hToken ))
return 0;
if(!LookupPrivilegeValue("", "SeDebugPrivilege", &luid))
return 0;
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
return AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL );
}
////////////////////////////////////////////////////////////////////////////////
Imagem de exemplo:
(//http://one.xthost.info/darkside17/projetos/c-cpp/code-injector/codeinjector.jpg)
Observações importantes:
1) A técnica não funciona em Windows 9x.
2) A função a ser injetada não deve chamar diretamente funções que não estejam localizadas nas DLL's kernel32.dll e user32.dll. O motivo é que somente essas DLL's são sempre mapeadas no mesmo endereço em cada processo, portanto, você pode obter os endereços de suas funções através do programa injetor e utilizá-los na função sem problemas. Caso você precise chamar uma função que esteja em uma DLL diferente, você deve passar os endereços das funções LoadLibraryA() e GetProcAddress() entre os argumentos, e usar as respectivas funções para obter o endereço desejado. Se a função estiver em uma DLL que já foi carregada pelo processo alvo, você pode utilizar GetModuleHandle() no lugar de LoadLibraryA().
Embora a DLL user32.dll seja mapeada no mesmo endereço em cada processo, nem sempre ela é carregada. Caso isso ocorra, e você deseja executar uma função contida nela, será necessário carregá-la antes de tentar obter o endereço de tal função. Isso vale tanto para o programa injetor quanto para o programa alvo.
3) Você não deve utilizar strings estáticas na função a ser injetada, pois essas strings são armazenadas na seção ".data" do executável que irá injetá-la. Para referenciá-las, são usados ponteiros. Se você copiar o código de uma função que contém uma string estática, o processo alvo tentaria acessar uma região que não existe em seu contexto, mas sim no do programa injetor. Portanto, defina um espaço para cada strings na estrutura "Argumentos" e passe esta estrutura como parâmetro.
Código no formato .C + executável:
http://one.xthost.info/darkside17/proje ... jector.zip (http://one.xthost.info/darkside17/projetos/c-cpp/code-injector/CodeInjector.zip)
Informações sobre as funções:
LoadLibrary
http://msdn2.microsoft.com/en-us/library/ms684175(VS.85 (http://msdn2.microsoft.com/en-us/library/ms684175(VS.85)).aspx
FreeLibrary
http://msdn2.microsoft.com/en-us/library/ms683152(VS.85 (http://msdn2.microsoft.com/en-us/library/ms683152(VS.85)).aspx
GetModuleHandle
http://msdn2.microsoft.com/en-us/library/ms683199(VS.85 (http://msdn2.microsoft.com/en-us/library/ms683199(VS.85)).aspx
OpenProcess
http://msdn2.microsoft.com/en-us/library/ms684320(VS.85 (http://msdn2.microsoft.com/en-us/library/ms684320(VS.85)).aspx
CloseHandle
http://msdn2.microsoft.com/en-us/library/ms724211(VS.85 (http://msdn2.microsoft.com/en-us/library/ms724211(VS.85)).aspx
VirtualAllocEx
http://msdn2.microsoft.com/en-us/library/aa366890(VS.85 (http://msdn2.microsoft.com/en-us/library/aa366890(VS.85)).aspx
VirtualFreeEx
http://msdn2.microsoft.com/en-us/library/aa366894(VS.85 (http://msdn2.microsoft.com/en-us/library/aa366894(VS.85)).aspx
WriteProcessMemory
http://msdn2.microsoft.com/en-us/library/ms681674(VS.85 (http://msdn2.microsoft.com/en-us/library/ms681674(VS.85)).aspx
CreateRemoteThread
http://msdn2.microsoft.com/en-us/library/ms682437(VS.85 (http://msdn2.microsoft.com/en-us/library/ms682437(VS.85)).aspx
Informações adicionais sobre a técnica:
http://www.codeguru.com/Cpp/W-P/system/ ... php/c5767/ (http://www.codeguru.com/Cpp/W-P/system/processesmodules/article.php/c5767/)
É isso...
Bye.
Maravilhoso.
ponto aê õôô
To quase chorando aqui ..sniff...sniff rsrs
Eu estava exatamente procurando algo deste tipo.