viernes, 1 de abril de 2011

Inyectando código en un proceso

|||   Inyecting code into another process   |||
|||                      by ca0s                      |||

|| 0x01 - Intro
|| 0x02 - APIs
|| 0x03 - Inyectando con DLL
|| 0x04 - Inyectando sin DLL
|| 0x05 - Links
|| 0x06 - Bye

----
0x01
----
Voy a explicar mas o menos como inyectar nuestro propio código en otro proceso. Primero inyectaremos una dll,
después lo haremos a pelo.

----
0x02
----
Primero necesitamos conocer algunas APIs de windows:

OpenProcess:
HANDLE WINAPI OpenProcess(
  __in  DWORD dwDesiredAccess,
  __in  BOOL bInheritHandle,
  __in  DWORD dwProcessId
);

Abre el proceso especificando su PID en dwProcessId, con los privilegios deseados. Retorna un manejador
al proceso, o NULL en caso de error.

VirtualAllocEx:
LPVOID WINAPI VirtualAllocEx(
  __in      HANDLE hProcess,
  __in_opt  LPVOID lpAddress,
  __in      SIZE_T dwSize,
  __in      DWORD flAllocationType,
  __in      DWORD flProtect
);

Reserva memoria en el espacio de otro proceso, comenzando en lpAddress (si se le pasa NULL el sistema elige
por nosotros), con el tamaño dwSize y con los permisos que le asignemos (usaremos PAGE_EXECUTE_READWRITE).
Devuelve un puntero a esa memoriam o NULL en caso de error.

WriteProcessMemory:
BOOL WINAPI WriteProcessMemory(
  __in   HANDLE hProcess,
  __in   LPVOID lpBaseAddress,
  __in   LPCVOID lpBuffer,
  __in   SIZE_T nSize,
  __out  SIZE_T *lpNumberOfBytesWritten
);

Escribe en la memoria dada por lpBaseAddress el contenido apuntado por lpBuffer, hasta nSize bytes. Devuelve FALSE
en caso de error.

CreateRemoteThread:
HANDLE WINAPI 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
);

Lanza un hilo en el proceso hProcess (abierto con OpenProcess), estando en ese hilo la funcion apuntada por
lpStartAddress con los argumentos apuntados por lpParameter.

También usaremos Tlhelp32.h para obtener el PID de un proceso sabiendo su nombre.

    HANDLE processList=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32 pInfo;
    BOOL st=TRUE;
    pInfo.dwSize=sizeof(PROCESSENTRY32);
    Process32First(processList, &pInfo);
    int myPid=0;
    do
    {
        if(strcmp(pInfo.szExeFile, "test.exe")==0)
        {
            myPid=pInfo.th32ProcessID;
            break;
        }
        Process32Next(lista, &pInfo);
    }
    while(st!=FALSE);


Ver MSDN para mas info.

----
0x03
----
Primero necesitamos una DLL que inyectar, la cual al ser programada por nosotros hará lo que
queramos.


#include
#include
#include
BOOL APIENTRY DllMain (HINSTANCE hInst,   
                       DWORD reason,      
                       LPVOID reserved)    
{
    switch (reason)
    {
      case DLL_PROCESS_ATTACH:
        MessageBoxA(NULL, "Hi!", "Hey!", 0);
        break;
    }
    return TRUE;
}

Ahora tenemos que hacer al proceso cargar esa DLL. Para ello:
- Abrimos el proceso, reservamos memoria en él, y escribimos en esa memoria la ruta de la DLL.
- Obtener la dirección de LoadLibrary con GetProcAddress. LoadLibrary se encuentra en kernel32.dll, y su
dirección es compartida, por lo que podemos cargarla desde el inyector y usarla desde el inyectado.
- Lanzar un hilo en el proceso abierto, dando como punto de entrada la dirección de LoadLibrary y como argumento
la dirección del nombre de la DLL previamente escrito.

Todo junto queda así:

#include
#include
#include

void error(char *err);

HANDLE myProc=NULL;

int main(int argc, char *argv[])
{

    HANDLE processList=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32 pInfo;
    BOOL st=TRUE;
    pInfo.dwSize=sizeof(PROCESSENTRY32);
    Process32First(processList, &pInfo);
    int myPid=0;
    do
    {
        if(strcmp(pInfo.szExeFile, "test.exe")==0)
        {
            myPid=pInfo.th32ProcessID;
            break;
        }
        Process32Next(processList, &pInfo);
    }
    while(st!=FALSE);

    // Abrir el proceso
    printf("[+] Opening process %i\n", myPid);
    myProc=OpenProcess(PROCESS_ALL_ACCESS, FALSE, myPid);
    if(myProc==NULL) error("[-] Error abriendo proceso.\n");
    else printf("[+] Proceso abierto.\n");
   
    // Reservar memoria para el argumento (ruta de la DLL)
    char thData[]="dll.dll";
    LPVOID dirToArg=VirtualAllocEx(myProc, NULL, strlen(thData), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if(dirToArg==NULL) error("[-] Error reservando memoria para argumento.\n");
    else printf("[+] Memoria reservada para argumento (%i bytes).\n", strlen(thData)); 
    // Escribir la ruta de la DLL en la memoria reservada
    SIZE_T written=0;
    if(WriteProcessMemory(myProc, dirToArg, (LPVOID)&thData, strlen(thData), &written)==0) error("[-] Error escribiendo memoria.\n");
    else printf("[+] Memoria escrita (arg %i bytes).\n", written);
   
    // Lanzar un hilo con LoadLibrary
    HANDLE rThread=CreateRemoteThread(myProc, NULL, 0, (LPTHREAD_START_ROUTINE)GetProcAddress(LoadLibrary("Kernel32.dll"), "LoadLibraryA"), dirToArg, 0, NULL);
    if(rThread==NULL) error("[-] Error creando el hilo.\n");
    else printf("[+] Hilo creado.\n");
   
    CloseHandle(myProc);
}

void error(char *err)
{
     if(myProc!=NULL) CloseHandle(myProc);
     printf("%s", err);
     exit(0);
}

----
0x04
----
Aquí la cosa se complica. Las diferencias entre un método y otro son:
- Ahora no podemos utilizar cadenas de texto directamente como argumentos a funciones. Ya veremos por qué.
- No podemos llamar a ninguna función que no carguemos previamente con LoadLibrary + GetProcAddress. Esas
dos están cargadas en todos los ejecutables de Windows desde kernel32, así que podemos usar un puntero a ellas.
- Tenemos que escribir todo el código de nuestras funciones en el espacio de memoria del proceso.
- Necesitaremos estructuras mas complejas como argumento de nuestros hilos remotos.

Así que el proceso de inyección quedaría así:
- Crear una estructura de datos con TODAS las cadenas de texto que vayamos a usar en el código inyectado, y
con un puntero a LoadLibrary y GetProcAddress.
- Abrir el proceso.
- Reservar memoria para la estructura de datos. Escribirla.
- Reservar memoria para nuestro código. Escribirlo.
- Lanzar el hilo remoto, dando como punto de entrada el puntero a neustro código y como argumento el puntero
a nuestra estructura de datos.

Y el código queda así (en el ejemplo inyecto un MessageBoxA, cargado desde User32.dll)

#include
#include
#include

void error(char *err);
static DWORD WINAPI myFunc(LPVOID data);

HANDLE myProc=NULL;

// Con esto cargaremos punteros a LoadLibrary y GetProcAddress en nuestra estrucura de datos
typedef int (WINAPI *datLoadLibrary)(LPCTSTR);
typedef int (WINAPI *datGetProcAddress)(HMODULE, LPCSTR);

int main(int argc, char *argv[])
{
    if(argc<2) error("Uso: hook.exe PROCESO\n");
    // Esta es nuestra estructura de argumentos
    struct {
       char lnUser32[50];                                 // -> "User32.dll"
       char fnMessageBoxA[50];                       // -> "MessageBoxA"
       datLoadLibrary apiLoadLibrary;               // Puntero a LoadLibrary
       datGetProcAddress apiGetProcAddress;  // Puntero a GetProcAddress
       char Msg[50];                                  // Texto que usaremos en MessageBoxA
       } thData;
    strcpy(thData.lnUser32, "User32.dll");
    strcpy(thData.fnMessageBoxA, "MessageBoxA");
    strcpy(thData.Msg, "Hola!");
    thData.apiLoadLibrary=GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
    thData.apiGetProcAddress=GetProcAddress(GetModuleHandle("kernel32.dll"), "GetProcAddress");
   
    int funcSize=600; // El tamaño de nuestra función. Podria calcularse de forma exacta, pero como ejemplo
                            // exagerado nos vale .

    HANDLE processList=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32 pInfo;
    BOOL st=TRUE;
    pInfo.dwSize=sizeof(PROCESSENTRY32);
    Process32First(processList, &pInfo);
    int myPid=0;
    do
    {
        if(strcmp(pInfo.szExeFile, argv[1])==0)
        {
            myPid=pInfo.th32ProcessID;
            break;
        }
        Process32Next(processList, &pInfo);
    }
    while(st!=FALSE);
   
    // Abrir proceso
    printf("[+] Abriendo proceso %i\n", myPid);
    myProc=OpenProcess(PROCESS_ALL_ACCESS, FALSE, myPid);
    if(myProc==NULL) error("[-] Error abriendo proceso.\n");
    else printf("[+] Proceso abierto.\n");
   
    // Reservar memoria para argumentos
    LPVOID dirToArg=VirtualAllocEx(myProc, NULL, sizeof(thData), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if(dirToArg==NULL) error("[-] Error reservando memoria para arg.\n");
    else printf("[+] Memoria reservada para arg (%i bytes).\n", sizeof(thData)); 
    // Escribir argumentos
    SIZE_T written=0;
    if(WriteProcessMemory(myProc, dirToArg, (LPVOID)&thData, sizeof(thData), &written)==0) error("[-] Error escribiendo la estructura de datos.\n");
    else printf("[+] Memoria escrita (arg %i bytes).\n", written);
     
    // Reservar memoria para la funcion
    LPVOID dirToWrite=VirtualAllocEx(myProc, NULL, funcSize, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if(dirToWrite==NULL) error("[-] Error rreservando memoria para codigo.\n");
    else printf("[+] Memoria reservada para codigo (%i bytes).\n", funcSize);  
    // Escribimos el codigo de nuestra funcion
    if(WriteProcessMemory(myProc, dirToWrite, (LPVOID)myFunc, funcSize, &written) == 0) error("[-]Error escribiendo memoria.\n");
    else printf("[+] Memoria escrita (codigo).\n");
   
    // Lanzamos el hilo
    HANDLE rThread=CreateRemoteThread(myProc, NULL, 0, (LPTHREAD_START_ROUTINE)dirToWrite, dirToArg, 0, NULL);
    if(rThread==NULL) error("[-] Error lanzando hilo.\n");
    else printf("[+] Hilo lanzado.\n");
    CloseHandle(myProc);
   
    return 0;
}
   
void error(char *err)
{
     if(myProc!=NULL) CloseHandle(myProc);
     printf("%s", err);
     exit(0);
}

static DWORD WINAPI myFunc(LPVOID data)
{
    // Cargamos nuestros datos en una estructura como la que hicimos
     struct {
         char lnUser32[50];
         char fnMessageBoxA[50];
         datLoadLibrary apiLoadLibrary;
         datGetProcAddress apiGetProcAddress;
         char MSG[50];
     } *thData;
     thData=data;
     // Podemos conseguir cualquier API con estas dos, siempre que tengamos su nombre en la estructura
     void *apiDir=(void *)thData->apiGetProcAddress((HANDLE)thData->apiLoadLibrary(thData->lnUser32), thData->fnMessageBoxA);
     // Puntero a funcion similar a MessageBoxA
     INT WINAPI (*myMessageBox)(HWND, LPCSTR, LPCSTR, UINT);
     myMessageBox=apiDir;
     myMessageBox(NULL, thData->MSG, thData->MSG, 0);
     return;
}    

----
0x05
----
MSDN - http://msdn.microsoft.com/en-us/library \\ De aquí saqué mucha info para hacer ésto.
foro.elhacker.net
www.ka0labs.net
st4ck-3rr0r.blogspot.com
www.diosdelared.com
www.0x3a.com.ar
www.evilzone.org

----
0x06
----
Creo que todo está mas o menos bien explicado. Yo tampoco soy un experto en ésto así que puede que se me
haya colado algún gazapo, cualquier cosa avisadme.
Bytez.

1 comentario: