Code Injector - C

Iniciado por Dark_Side, 12 de Janeiro , 2008, 06:50:10 AM

tópico anterior - próximo tópico

0 Membros e 1 Visitante estão vendo este tópico.

Dark_Side

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:



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

Informações sobre as funções:


LoadLibrary
http://msdn2.microsoft.com/en-us/library/ms684175(VS.85).aspx

FreeLibrary
http://msdn2.microsoft.com/en-us/library/ms683152(VS.85).aspx

GetModuleHandle
http://msdn2.microsoft.com/en-us/library/ms683199(VS.85).aspx

OpenProcess
http://msdn2.microsoft.com/en-us/library/ms684320(VS.85).aspx

CloseHandle
http://msdn2.microsoft.com/en-us/library/ms724211(VS.85).aspx

VirtualAllocEx
http://msdn2.microsoft.com/en-us/library/aa366890(VS.85).aspx

VirtualFreeEx
http://msdn2.microsoft.com/en-us/library/aa366894(VS.85).aspx

WriteProcessMemory
http://msdn2.microsoft.com/en-us/library/ms681674(VS.85).aspx

CreateRemoteThread
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/

É isso...
Bye.

Shady



Mundus Vult Decipi

Mateus

H4X with axes 8)

Kratos

To quase chorando aqui ..sniff...sniff rsrs

Eu estava exatamente procurando algo deste tipo.
::: "A vida é bela pra quem sabe curtir" :::