#GEDZAC Mitosis eZine Issue 4
#MITOSIS ARTICLE#
#Tecnicas de ganchos con las funciones de Windows
#Autor: MachineDramon

Ya que este articulo es un poquito grande os dejo al inicio un indice para que se orienten mejor de lo que se trata este articulo :).

=====[ 2. Indice ]=====================
1.0.0.0 Contenidos
2.0.0.0 Introduccion
3.0.0.0 Metodos de enganche
3.1.0.0 Ganchos antes de la ejecucion
3.2.0.0 Enganchar durante la ejecucion
3.2.1.0 Enganchar nuestro propio proceso usando IAT
3.2.2.0 Enganchar nuestro propio proceso reescribiendo el entry point
3.2.3.0 Enganchar sin modificar las funciones originales
3.2.4.0 Enganchando otros procesos
3.2.4.1 Inyeccion DLL
3.2.4.2 Codigo independiente
3.2.4.3 Cambio Raw
4.0.0.0 Finalizando


=====[ 2. Introduccion ]=====================

Este texto trata las tecnicas de enganchar las funciones de la API en Windows. Todos los ejemplos aqui descritos funcionan perfectamente en sistemas basados en tecnologia NT version NT 4.0 y superior ( windows NT 4.0, Windows 2000 , Windows XP, Windows 2003). Probablemente tambien funcionara en futuros sistemas Windows. Deberias familiarzarte con procesos en windows, esamblador, estructura de ficheros PE y algunas funciones de la API , para comprender enteramente el texto. Cuando usamos el termino "Enganchar la API" aqui, me refiero al cambio completo de la API. Asi, cuando una API enganchada es llamada, nuestro codigo es ejecutado inmediatamente. No trato solamente los casos de monitorizar la API. Escribire todo sobre esta tecnica.



=====[ 3. Tecnicas de ganchos ]=====================


Nuestra meta es normalmente reemplazar el codigo de alguna funcion con nuestro codigo. Este problema puede ser solucionada a veces antes de ejecutar el proceso. Esto puede hacerse la mayoria de las veces con codigo con privilegios de usuario y la meta es, por ejemplo, cambiar el comportamiento del programa. Un ejemplo de esto puede ser crackear una aplicacion: un programa que pide el cd original al iniciarse (como ocurria en el juego Atlantis) y nosotros queremos ejecutarlo sin CD. Si modificamos la funcion que compueba que el tipo de disco sea cdrom, podemos ejecutarlo desde el disco duro. Esto no puede hacerse o no queremos hacerlo cuando queremos enganchar procesos de sistema (como servicios) o en el caso en que no sepamos el proceso victima. Entonces tendremos que usar la tecnica de ganchos en ejecucion. Ejemplos de esta tecnica son los rootkits o los virus con tecnicas anti-antivirus.


=====[ 3.1 Ganchos antes de la ejecucion ]=====================

Se trata de cambios del modulo fisico (normalmente .exe o .dll) en donde esta la funcion que nosotros queremos cambiar. Tenemos por lo menos tres posibilidades de hacer esto. La primera es encontrar el punto de entrada de esa funcion y basicamente reescribir el codigo. Esto esta limitado por el tamaсo de la funcion pero siempre podemos cargar otro modulo dinamicamente (API LoadLibrary) y asi tendriamos suficiente espacio. Las funciones del kernel (kernel32.dll) pueden ser usadas en todos los casos porque todos los procesos en windows tienen su propia copia de este modulo. Otra ventaja es cuando sabemos en que SO sera modificado el modulo. Podemos usar directamente un puntero a la funcion, en este caso seria la direccion de LoadLibraryA. Esto es asi porque la direccion del modulo kernel en memoria es estatica para una determinada version de Windows.

Tambien podemos hacer uso del comportamiento de los modulos cargados dinamicamente, por el cual su parte de inicializacion es ejecutada inmediatamente despues de cargar el modulo en memoria. En la parte de inicializacion de un nuevo modulo no estamos limitados. La segunda posibilidad de reemplazar funciones en un modulo reside en la extension. Entonces tenemos que elegir entre reemplazar los 5 primeros bytes con un salto relativo o reescribir la IAT. En el caso de poner un salto relativo, redirigiria la ejecucion del modulo a nuestro codigo. En el otro caso, cuando una es llamada una funcion cuyo registro de IAT ha sido modificado, nuestro codigo seria ejecutado inmediatamente despues de esta llamada. Pero cambiar la extension un modulo no es tarea facil porque hay que controlar muy bien las cabeceras. (Nota del traductor: no se refiere a la extension de fichero .exe o .dll eh? se refiere al tamaсo que ocupa, que reside en la cabecera del modulo) La tercera posibilidad es reemplazar el modulo entero. Es decir creamos nuestra propia version del modulo qu puede cargar el modulo original y llamar las funciones que no deseamos modificar. Pero reescribimos las funciones que si nos interesan. Este metodo no es util para modulos muy grandes pues aveces exportan cientos de funciones.


=====[ 3.2 Ganchos en ejecucion ]=====================

Los ganchos antes de la ejecucion es mas bien para aplicaciones concretas o un modulo concreto. Si reemplazamos un funcion de kernel32.dll o de ntdll.dll obtendremos el comportamiento deseado en todos aquellos procesos que sean ejecutados despues, aunque es dificil atinar con un codigo perfecto y exacto para las funciones o modulos nuevos. Pero el principal problema es que nuestros ganchos solo contemplaran los procesos ejecutados posteriormente (asi pues para todos los procesos tendriamos que reiniciar el sistema). Otro problema es el acceso a estos ficheros, que en sistemas NT estan protegidos. Los ganchos en ejecucion solucionan esto. Si bien este metodo requiere muchos mas conocimientos, el resultado es perfecto.Los ganchos en ejecucion pueden aplicarse solo a procesos para los que tengamos acceso a su zona de memoria. Para escribir en esta zona usaremos la funcion APi WriteProcessMemory. Comencemos por enganchar nuestro propio proceso durante la ejecucion.


=====[ 3.2.1 Enganchar nuestro propio proceso usando IAT ]=========

Hay muchas posibilidades aqui. En prinicpio te mostrare como enganchar funciones mediante la reescritura de la IAT. Este dibujo muestra la estructura de un fichero PE:

+-------------------------------+ - offset 0
| Cabecera MS DOS ("MZ")|
+-------------------------------+
| firma de PE ("PE")* * |
+-------------------------------+
| .text | - codigo de modulo* * |
| Codigo de programa* * * * * * |
| * * * * * * * * * * * * * * * |
+-------------------------------+
| .data | - datos iniciados * * | <--GLOBAL Y STATIC
| Datos Inicializados * * * * * |
| * * * * * * * * * * * * * * * |
+-------------------------------+
| .idata | - info sobre funciones importadas
| Import Table | y datos* * * * |
| * * * * * * * * * * * * * * * |
+-------------------------------+
| .edata | - info sobre funciones exportadas
| Export Table | y datos* * * * |
| * * * * * * * * * * * * * * * |
+-------------------------------+
| * * * * Debug symbols * * * * |
+-------------------------------+



La parte importante para nosotros aqui es la Import Address Table(IAT) en la zona .idata . Esta parte contiene una descripcion de las funciones importadas y sus direcciones. Ahora es importante saber como se crean los ficheros PE. Cuando se llama un funcion de la API indirectamente en un lenguaje de programacion (es decir llamar usando el nombre , en vez de su direccion especifica en el SO) el compilador no las convierte en llamadas directas al modulo, sino que las convierte en saltos a direcciones, y estas direcciones se encuentran almacenadas en la IAT. Cuando el proceso se carga el cargador de proceso del Windows se encarga de rellear la IAT para cada sistema. Esta es la razon por la que el mismo binario funciona en versiones distintas del windows, a pesar de que tienen distintas direcciones para un mismo modulo. Por tanto si fuesemos capaz de encontrar una funcion, que queremos enganchar, en la IAT, podriamos cambiar facilmente la direccion y la instruccion jmp redirigiria el flujo de ejecucion a nuestro codigo. Cualquier llamada despues de hacer esto ejecutaria nuestro codigo. (Nota del traductor: He traducido el ultimo parrafo intentando que se comprenda lo mejor posible, pero aun asi prefiero dar una explicacion propia sobre la IAT. Si queremos realizar una llamada a una api de windows podemos escribir:

call direccion_de_la_api_en_memoria ( por ejemplo 0x07728188)

pero como en cada version de windows esa direccion cambia necesitamos de alguna manera que escribiendo la misma instruccion se ejecute lo mismo Lo que se hace es guardar estas direcciones en la IAT. La IAT es rellenada por el So al crear el PE. La forma de acceder a las direcciones de la IAT es la siguiente:

-------------------
call direccion api1
......
codigo
......

direccion:
jmp dword ptr [IAT]

--------------------
Asi un PE funciona en cualquier windows. La ventaja de este metodo es que el resultado es perfecto. La desventaja es que para cambiar el comportamiento de una funcion, deberiamos enganchar varias funciones (me explico, si queremos cambiar el comportamiento de un programa en la busqueda de un fichero tendremos que cambiar FindFirstFile y FindNextFile, pero estas funciones tienen version ANSI y WIDE, asique tendremos que cambiar FindFirstFileA, FindNextFileA, FindFirstFileW y FindNextFileW en la IAT. Y aun quedan otras como FindFirstFileExA y su version WIDE FindFirstFileExW que son llamadas por las funciones comentadas previamente. Sabemos que FindFirstFileW llama a FindFirstFileExW pero esto se hace directamente , sin usar la IAT. Y aun algunas otras. Hay por ejemplo funciones ShellApi como SHGetDesktopFolder que tambien llaman directamente a FindFirstFileW o FindFirstFileExW). Pero si lo conseguimos con todas ellas el resultado sera perfecto. Podemos usar ImageDirectoryEntryToData de imagehlp.dll para encontrar la IAT facilmente.

PVOID ImageDirectoryEntryToData(
IN LPVOID Base,
IN BOOLEAN MappedAsImage,
IN USHORT DirectoryEntry,
OUT PULONG Size
)


Usaremos Instance de nuestra aplicacion como Base (Instance se obtiene con una llamada a GetModuleHandle:

hInstance = GetModuleHandleA(NULL)


), y como DirectoryEntry usaremos la constante IMAGE_DIRECTORY_ENTRY_IMPORT.

#define IMAGE_DIRECTORY_ENTRY_IMPORT 1

El resultado de esta funcion es un puntero al primer registro de la IAT. Los registros de la IAT son estructuras que estan definidas como IMAGE_IMPORT_DESCRIPTOR. Asi el resultado es un puntero a IMAGE_IMPORT_DESCRIPTOR.

typedef struct _IMAGE_THUNK_DATA {
union {
PBYTE ForwarderString
PDWORD Function
DWORD Ordinal
PIMAGE_IMPORT_BY_NAME AddressOfData
}
} IMAGE_THUNK_DATA,*PIMAGE_THUNK_DATA

typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics
PIMAGE_THUNK_DATA OriginalFirstThunk
}
DWORD TimeDateStamp
DWORD ForwarderChain
DWORD Name
PIMAGE_THUNK_DATA FirstThunk
} IMAGE_IMPORT_DESCRIPTOR,*PIMAGE_IMPORT_DESCRIPTOR


El valor de Name en IMAGE_IMPORT_DESCRIPTOR es una referencia al nombre del modulo. Si queremos enganchar una funcion, por ejemplo del kernel32.dll, tenemos que encontrar aquella entrada de la tabla que corresponde al descriptor de kernel32.dll.

NOTA del traductor:
Aunque Holy_father lo da por supuesto, prefiero aclarar algo antes de continuar, me refiero a la forma de recorrer la tabla. La IAT parece ser que esta implementada como un vector estatico, donde cada casilla contiene la direccion del descriptor. Por tanto si tenemos la direccion de la primera entrada de la tabla, que nos la ha devuelto ImageDirectoryEntryToData, la direccion de la siguiente entrada se incrementa en uno, y asi podemos recorrer la tabla)

Llamaremos a ImageDirectoryEntryToData en un principio e intentaremos encontrar aquel descriptor con Name "kernel32.dll" ( puede haber mas de un descriptor con este nombre). Finalmente tendremos que encontrar nuestra funcion en la lista de todas las funciones de ese descriptor (la direccion de nuestra funcion la obtenemos con GetProcAddress). Si la encontramos debemos usar VirtualProtect para cambiar la proteccion de pagina de memoria y despues de esto podremos escribir en esta parte de memoria. Despues de escribir la direccion debemos restaurar la proteccion original. Antes de llamar a VirtualProtect tenemos que saber cierta informacion sobre esta pagina de memoria. Esta info se obtiene con VirtualQuery. Podemos aсadir algunas pruebas en caso de algunas llamadas fallen ( por ejemplo no continuar si la primera llamada a VirtualProtect falla, etc)

PCSTR pszHookModName = "kernel32.dll",pszSleepName = "Sleep"
HMODULE hKernel = GetModuleHandle(pszHookModName)
PROC pfnNew = (PROC)0x12345678, //la nueva direccion aqui
pfnHookAPIAddr = GetProcAddress(hKernel,pszSleepName)

ULONG ulSize
PIMAGE_IMPORT_DESCRIPTOR pImportDesc =
(PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData(
hInstance,
TRUE,
IMAGE_DIRECTORY_ENTRY_IMPORT,
&ulSize
)

while (pImportDesc->Name)
{
PSTR pszModName = (PSTR)((PBYTE) hInstance + pImportDesc->Name)
if (stricmp(pszModName, pszHookModName) == 0)
break
pImportDesc++
}

PIMAGE_THUNK_DATA pThunk =
(PIMAGE_THUNK_DATA)((PBYTE) hInstance + pImportDesc->FirstThunk)

while (pThunk->u1.Function)
{
PROC* ppfn = (PROC*) &pThunk->u1.Function
BOOL bFound = (*ppfn == pfnHookAPIAddr)

if (bFound)
{
MEMORY_BASIC_INFORMATION mbi
VirtualQuery(
ppfn,
&mbi,
sizeof(MEMORY_BASIC_INFORMATION)
)
VirtualProtect(
mbi.BaseAddress,
mbi.RegionSize,
PAGE_READWRITE,
&mbi.Protect)
)

*ppfn = *pfnNew

DWORD dwOldProtect
VirtualProtect(
mbi.BaseAddress,
mbi.RegionSize,
mbi.Protect,
&dwOldProtect
)
break
}
pThunk++
}

El resultado de una llamada a Sleep(1000) podria ser:

00407BD8: 68E8030000 push 0000003E8h
00407BDD: E812FAFFFF call Sleep

........
codigo
........

Sleep: es es el salto a la IAT
004075F4: FF25BCA14000 jmp dword ptr [00040A1BCh]



tabla original:
0040A1BC: 79 67 E8 77 00 00 00 00

nueva tabla:
0040A1BC: 78 56 34 12 00 00 00 00


Asique al final el salto es a 0x12345678.


=====[ 3.2.2 Enganchar nuestro propio proceso reescribiendo el entry point ]===

El metodo de reescribir primero unas intrucciones en el punto de entrada de la funcion es realmente simple. Como en el caso de reescribir la IAT, tenemos que cambiar la proteccion de pagina antes de nada. Aqui seran los 5 primeros bytes de la funcion que queremos enganchar. Despues tendremos que localizar memoria dinamica para la estructura MEMORY_BASIC_INFORMATION. El comienzo de la funcion lo obtenemos con GetProcAddres. En esta direccion insertaremos un salto relativo a nuestro codigo. El siguiente programa llama a Sleep(5000) (espera 5 segundos), entonces la funcion Sleep es enganchada y redirigida a new_sleep, y finalmente llama de nuevo a Sleep(5000). Como la nueva funcion new_sleep no hace nada y termina inmediatamente el programa entero tardara solo 5 segundos en lugar de 10.

(NOTA del traductor:
----------------------------
Holy_father ha puesto este codigo en win32asm. El codigo se entiende bien pero lo voy a poner primero en C porque creo que se queda uno con una vision mas general en la primera lectura. El codigo lo escribo sobre la marcha mientras traduzco pero creo que esta bien. Luego puedes continuar echarle un vistazo a como quedaria en win32asm:
.....

static void new_sleep()
{
......
}
.....
Sleep(5000)

HMODULE Hkernel=GetModuleHandleA("kernel32.dll")
FARPROC HSleep=GetProcAddress(Hkernel,"Sleep")

LPVOID pmbi=VirtualAlloc(0,sizeof(MEMORY_BASIC_INFORMATION)
,MEM_COMMIT,PAGE_READWRITE)

if (pmbi)
{

DWORD pinfo=VirtualQuery(HSleep,Hcode,
sizeof(MEMORY_BASIC_INFORMATION))

if (pinfo)
{
/*
como la funcion Sleep se acaba de ejecutar es probable que
aun este en cache asique vaciamos de la cache los 5 primeros
bytes de la funcion Sleep
*/
FlushInstructionCache(GetCurrentProcess(),HSleep,5)


//la estructura que hemos reservado es una mbi
//tenemos que poner mbi.Protect al valor devuelto por
//VirtualProtect en lpflOldProtect
//14h (= 20) es el desplazamiento de Protect dentro de la mbi

DWORD dwOldProtect=(DWORD)pmbi + 14h
PDWORD pOldProtect=(PDWORD)dOldProtect

DWORD dwsize=(DWORD)pmbi + 00ch
PDWORD psize=(PDWORD)dwsize

if (VirtualProtect( pmbi , *psize, PAGE_EXECUTE_READWRITE,
pOldProtect))
{
//el primer byte contieen el hex opcode de jmp
PWORD pSleep=(PWORD)HSleep;
(pSleep[0])= (BYTE)0E9h; //jmp;;

DWORD offset_jmp=(DWORD)(PBYTE)new_sleep-(DWORD)(PBYTE)Sleep-5;

//los siguientes contienen el offset;;
memcpy(&Sleep[1],&offset_jmp,4);

DWORD tmp_oldprotect;
VirtualProtect( pmbi , *psize, pOldProtect, &tmp_oldprotect);
VirtualFree(pmbi,0,MEM_RELEASE);
}
Sleep(5000);
ExitProcess(0);

=====[ 3.2.4 Other process hooking ]==========================
Ahora haremos algo practico con los ganchos en ejecucion. Quien quiere enganchar su propio proceso? Esto servia para aprender la teoria pero no es muy practico.Te mostrare tres metodos de enganchar otros procesos. Dos de ellos usan CreateRemoteThread que esta solo en Windows con tecnologia NT. El problema de los ganchos para mi no es tan interesante en versiones antiguas de windows. Despues de todo intentare explicar un metodo que no lo probe, asique podria no funcionar. Primero veamos un poco sobre CreateRemoteThread. Como la ayuda de esta funcion dice, crea un hilo nuevo en cualquier proceso y ejecuta su codigo.

HANDLE CreateRemoteThread(
HANDLE hProcess,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
DWORD dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId);


El manejador de hProcess lo conseguimos con OpenProcess. Aqui tenemos que tener los privilegios necesarios. El puntero lpStartAddress apunta a un lugar de memoria del procedo OBJETIVO donde esta la primera instruccion del nuevo hilo. Como el nuevo hilo es creado en el proceso objetivo este estara en la zona de memoria del proceso objetivo. El puntero lpParameter apunta a un argumento que sera el del nuevo hilo.
;
;
=====[ 3.2.4.1 Inyeccion DLL]===================
Podemos ejecutar un nuevo hilo desde cualquier parte en la memoria de un proceso objetivo. Esto es inutil amenos que tengamos codigo propio en el. El primer metodo hace esto. Usa GetProcAddress para obtener la direcion actual de LoadLibrary. Entonces pasa como lpStartAddress la direccion de LoadLibrary. La funcion LoadLibrary tiene un solo parametro, igual que la funcion para nuevo hilo en el proceso objetivo.

HINSTANCE LoadLibrary(
LPCTSTR lpLibFileName
);


Usaremos esta similidad de parametros y pasaremos el nombre la la DLL como lpParameter. Despues de ejecutar el nuevo hilo, lpParameter contendra lpLibFileName. Lo mas importante es el comportamiento descrito arriba. Despues de cargar el nuevo modulo en la memoria del proceso victima, la parte de inicializacion es ejecutada. Si colocamos funciones especificas que enganchen lo que nosotros queramos en el modulo, ya esta todo hecho. Despues de ejecutar la parte de inicializacion, el hilo no tendra nada que hacer y se carrera. Pero nuestro modulo estara todavia en memoria. Ese metodo esta curioso y es facil de implementar. Se le llama Inyeccion DLL. Pero si te pasa como a mi, no te gusta tener que usar DLLs. Pero si no te importa tener librerias esta es la forma mas facil y mas rapida (desde el punto de vista del programador)

=====[ 3.2.4.2 Codigo Independiente ]==========================

Este metodo es muy dificil pero es impresionante. Codigo independiente es el codigo sin direcciones estaticas. Todo es relativo en el hacia cualquier otra parte del codigo propio. Este codigo es hecho la mayoria de las veces si no sabemos la direccion donde este codigo sera ejecutado. Seguramente seria posible obtener esta direccion y reprogramar nuestro codigo para que funconara en la nueva direccion sin errores, pero esto seria incluso mas dificil que haciendo codigo independiente. Un ejemplo de este tipo de codigo puede ser el codigo de un virus. El virus que infecta ejecutables se aсade por si mismo al ejecutable. En diferentes ejecutables el codigo del virus estara en sitios diferente dependiendo de las estructuras, la longitud, etc En principio tenemos que insertar nuestro codigo en un proceso objetivo. Entonces la funcion CreateRemoteThread nos permitira ejecutar nuestro codigo.Asi, al principio tenemos que obtener informacion sobre el proceso victima y abrirlo con OpenProcess. La funcion VirtualAllocEx nos reserva espacio en la zona de memoria del proceso victima. Finalmente usamos WriteProcessMemory para escribir nuestro codigo en la memoria reservada y lo ejecutamos. En CreateRemoteThread, lpStartAddress sera la direccion de la memoria reservada y lpParameter puede ser lo que queramos. Me gusta este metodo porque no necesitamos ficheros innecesarios.

=====[ 3.2.4.3 Cambio Raw; ]=============================
No hy una funcion CreateRemoteThread en versiones de windows antiguas (sin NT). Asique no podemos usar esta funcion para los ganchos. Hay probablmente mejores metodos para enganchar que el metodo que voy a contar. De hecho nose si funcionara en la practica (uno nunca sabe cuando usa windows) pero teoricamente esta todo correcto. No necesitamos en absoluto tener nuestro codigo en el proceso victima para enganchar sus funciones. Tenemos la funcion WriteProcessMemory (deberia estar en todas las versiones de windows) y tenemos OpenProcess tambien. Lo ultimo que necesitamos es VirtualProtectEx que permite cambiar el acceso a paginas de memoria en otros procesos. No veo ninguna razon por la que no podamos enganchar funciones directamente desde nuestro proceso.


=====[ 4. Finalizando ]======================
Este pequeсo manual termina aqui. Agradecere cualquier comentario que describa metodos no mencionados de enganche, estoy seguro que hay un monton. Tambien agradecere cualquier comentario en las partes que no estan descritas tan detalladamente. Puedes enviarme codigo fuente; si quieres para los problemas de ganchos que por ejemplo estuve demasiado vago para escribir. La meta de este docmento es mostrar detalles de cada tecnica de ganchos. Espero haber hecho parte de esto. Agradecimieno especial a Z0MBiE por su trabajo, que asi no tuve que escribir yo mismo y dedicar aсos estudiando tablas para conseguir la longitud de instruccion.


(C) Mitosis 4 - GEDZAC LABS