sábado, 10 de mayo de 2008

Articulo de Programacion #2 (Parte 3)

Dll Injection (Parte 2)


En la pasada parte de este articulo de programacion #2 vimos algunas de las apis necesarias para realizar el cometido de una inyeccion de una dll en el contexto de un proceso remoto, ahora veremos ya la aplicacion de las mismas.

Una de las funciones vistas crucial para lograr ejecutar esta tecnica es la funcion CreateRemoteThread, esta funcion no es mas que la funcion final en la cadena de funciones para crear un hilo. CreateRemoteThread termina invocando la funcion NtCreateThread en la libreria ntdll.dll, que asu vez invoca el servicio Nt NtCreateThread en el kernel (ntoskrnl.exe) este ultimo se encarga de crear el objeto tipo Thread necesario para el manejo y funcionamiento del mismo, el objeto que es el cuerpo del hilo creado. Es asi como esta funcion recaba un manejador o handle al proceso destino, el cual obtenemos mediante OpenProcess, la cadena es algo como:


Modo usuario:CreateThread->CreateRemoteThread->NtCreateThread|Modo Kernel:KiServiceTable->NtCreateThread->PspCreateThread

Ahora bien, cuando un thread se crea en nuestro propio proceso, tenemos la certeza que el codigo ejecutado va a ejecutarse de la manera como lo programamos, ya que las variables y direcciones las tenemos localizadas en el contexto de memoria de nuestro proceso de la memoria virtual. Como con un thread o hilo remoto estaremos en un contexto diferente, las direcciones que querramos acceder no contendran la misma informacion que la del contexto de nuestro proceso, incluso podrian ser invalidas, recordemos el manejo de la memoria virtual, que permite que las direcciones apunten a diferentes paginas de la memoria fisica para cada proceso, diferentes mundos distintos de memoria, apuntando a una misma memoria ram.

La tecnica de inyeccion de una dll consiste en obligar a un proceso a cargar una dll, como bien aprendimos, para cargar una dll necesitamos usar la funcion LoadLibrary, pero en este caso haremos que el proceso remoto lo use sin que lo tenga en sus planes de hacerlo =), para cumplir esto, necesitamos crear un thread alterno en su contexto, ya con esto concluimos que necesitamos de crear un thread en el proceso donde queremos que cargue la dll usando CreateRemoteThread y hacer que ejecute la funcion LoadLibraryA.


HMODULE WINAPI LoadLibrary(

LPCTSTR lpFileName

);


Esta funcion requiere un parametro el cual es la ruta al archivo de la dll que va a ser cargada, esa ruta debe estar escrita en alguna direccion de memoria del proceso remoto, no es lo mismo tener la cadena de caracteres en nuestro proceso, y usar esa direccion en el proceso remoto, la direccion virtual no apunta a la misma pagina de memoria fisica, por lo cual necesitamos acceder al otro proceso escribir los datos que vamos a usar.

Programando en lenguaje c o cualquier otro lenguaje, hemos visto la necesidad de alojar memoria dinamicamente, segun las necesidades de nuestro programa y no tener que estar haciendo uso de muchas variables declaradas o arreglos, asi bien, se haria con un malloc usando la libreria estandar, o hasta usando la funcion new en c++; estados dos funciones al final hacen uso de la apis de windows, la cual invoca a VirtualAlloc una api para reserver o alojar nueva memoria virtual con respecto a una nueva pagina de memoria fisica tomada del directorio de paginas de la misma. En este caso necesitamos tener un espacio de memoria en el proceso remoto, para ello no podriamos usar malloc, new o VirtualAlloc, para ello windows nos ofrece otra api llamada VirtualAllocEx, esta nos permite alojar memoria en el contexto de un proceso que no sea el nuestro, es la funcion que es invocada por VirtualAlloc usando como handle NtCurrentProcess ((HANDLE)0xFFFFFFFF). Veamos la definicion de VirtualAllocEx:


LPVOID VirtualAllocEx(

HANDLE hProcess,

LPVOID lpAddress,

SIZE_T dwSize,

DWORD flAllocationType,

DWORD flProtect

);


Veamos los parametros de esta funcion, como ya pudimos ver en la entrada anterior, esta funcion requiere un handle o manejador al proceso donde se va alojar el espacio, este se obtiene usando OpenProcess con el ProcessId del proceso remoto, lpAddress: este parametro se indica la direccion donde queremos que se aloje la memoria, si especificamos un valor y ese espacio ya esta reservado o alojado la funcion VirtualAllocEx fallara, por lo cual, en este parametro debemos pasar 0 o NULL, de esta manera el sistema operativo usara la direccion de memoria que mas convenga y donde encuentre espacio, el siguiente parametro, dwSize especifica la cantidad de bytes que se necesitan alojar, estos se redondean al tamaño de pagina mas proximo (generalmente 4096 bytes o 4 kb), el siguiente parametro se especifica el tipo de alojamiento ya sea para reservar o para alojar, cuando es una reservacion, la memoria virtual no es accesible pero tampoco puede ser reservado otra vez (MEM_RESERVE), cuando es alojado (MEM_COMMIT) la memoria virtual es referenciada con una pagina de la memoria fisica y a partir de entonces se vuelve valida, cabe decir que una direccion de memoria no puede ser alojado sin que haya sido reservada primero, es por ello que en esta funcion por lo general se usan los dos flags combinados (MEM_RESERVE|MEM_COMMIT), el ultimo parametro es el tipo de proteccion que va a tener la pagina alojada, existen diferentes tipos de protecciones, entre las mas importantes se encuentran PAGE_READ, PAGE_WRITE, PAGE_READWRITE, PAGE_NOACCESS, PAGE_EXECUTE, PAGE_EXECUTE_READWRITE, despues se explicaran cada uno de ellos.


Ahora ya explicada esa funcion necesitaremos de poder escribir en la memoria del proceso remoto, esto lo haremos con una funcion para depuradores de la api Win32 llamado WriteProcessMemory, veamos su definicion:


BOOL WriteProcessMemory(

HANDLE hProcess,

LPVOID lpBaseAddress,

LPCVOID lpBuffer,

SIZE_T nSize,

SIZE_T* lpNumberOfBytesWritten

);


El primer parametro es un manejador al proceso remoto, el segundo nos indica la direccion virtual en el CONTEXTO del proceso remoto donde vamos a escribir los datos contenidos en el tercer parametro (lpBuffer), este tercer parametro es un puntero dentro del contexto de nuestro PROPIO proceso, es decir, puede ser una variable o buffer alojado dinamicamente con malloc o VirtualAlloc dentro de nuestro proceso, este buffer sera copiado exactamente igual en la direccion especificada en el parametro anterior, y el numero de bytes a copiar seran los que sean especificados en el cuarto parametro (nSize), el ultimo parametro es opcional y podemos pasar NULL o 0, aunque si queremos saber cuantos bytes se copiaron, podemos pasar la direccion de memoria de una variable la cual sera llenada por esta funcion con el numero de bytes copiados.

Ahora si, con todo esta teoria ya podemos empezar aplicar la tecnica =), lo que haremos es inyectar una dll llamada hack.dll en la unidad C:\ de nuestro disco en el proceso del buscaminas:


char DllPath[MAX_PATH]="C:\\hack.dll";
HWND Buscaminas;
DWORD BuscaminasPID;
HANDLE BuscaminasHandle;
HMODULE hKernel;
PVOID LoadLibraryAddress, RemoteVirtualAddress;
BOOL bSuccess;
HANDLE hRemoteThread;

/*Obtenemos la direccion base de la libreria kernel32 donde reside LoadLibraryA*/
hKernel=GetModuleHandle("kernel32.dll");

/*Buscamos en la libreria la funcion exportada*/
LoadLibraryAddress=(PVOID)GetProcAddress(hKernel, "LoadLibraryA");


/*Nota: Aqui no nos preocupamos por la memoria virtual del contexto del otro proceso con respecto a las funciones que acabos de recabar, ya que todas son proyectadas en la misma direccion de memoria en todos los procesos =)*/

/*Intentamos encontrar el handle a la ventana del buscaminas*/

Buscaminas=FindWindow(NULL, "Buscaminas");


if(!Buscaminas)
return; //error

/*Recabamos el processId del proceso que posee la ventana*/
GetWindowThreadProcessId(Buscaminas, &BuscaminasPID);

/*Abrimos una referencia al proceso con el process id obtenido*/
BuscaminasHandle=OpenProcess(PROCESS_ALL_ACCESS, FALSE, BuscaminasPID);

/*Alojamos memoria en el proceso remoto =)*/
RemoteVirtualAddress=VirtualAllocEx(BuscaminasHandle, NULL, strlen(DllPath), MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);


if(!RemoteVirtualAddress)
return; //error

/*Escribimos en el espacio remoto que acabamos de alojar el string al camino de la dll*/
bSuccess=WriteProcessMemory(BuscaminasHandle, RemoteVirtualAddress, DllPath, strlen(DllPath), NULL);


if(!bSuccess)
return; //error

/*Creamos el hilo remoto y pasamos de parametro la direccion de memoria donde se encuentra nuestra cadena de caracteres que tiene la ruta de nuestro ejecutable =D*/
hRemoteThread=CreateRemoteThread(BuscaminasHandle, NULL, NULL, (LPTHREAD_START_ROUTINE)LoadLibraryAddress, (LPVOID)RemoteVirtualAddress, 0);


if(!hRemoteTHread)
return; //error

/*Esperamos a que el thread remoto termine de ejecutarse, este paso es opcional*/

WaitForSingleObject(hRemoteThread, INFINITE);


Y con esto terminamos este segundo articulo de programacion =D, espero que te haya gustado, jajaja, proximamente hare el tercero donde explicare una tecnica fundamental en la administracion del sistema operativo, recabar los procesos en el sistema, hasta la proxima xD.

No hay comentarios: