19 marzo 2014

API Hooking desmitificado (Windows)

En muchas ocasiones se escucha hablar de 'API Hooking', tanto en su vertiente 'maliciosa' (por ejemplo, cuando se habla de rootkits) como en su vertiente positiva (los Antivirus usan esta técnica).

He podido comprobar que a mucha gente le suena a algo muy lejano o complejo, por eso, me he propuesto escribir sobre el tema de una forma práctica. Mi objetivo es hablar de entornos Windows y Linux.

De entrada, para clarificar conceptos, cuando hablamos de API nos referimos a todas las llamadas a funciones ajenas a un programa que generalmente se encuentran en DLLs o son llamadas directamente al sistema. Nos vamos a centrar en las DLLs, en el 'userland'

Lo que implica el Hooking es realmente sencillo de explicar: Tenemos identificada una función determinada a la que nos gustaría poder alterar su funcionamiento, por ejemplo, una función para listar los procesos del sistema. Nuestro objetivo es que esa función envíe unos datos alterados, por lo que es necesario que esa función no sea llamada directamente por el programa sino por nosotros para poder aplicarle los filtros que queramos.


Para conseguir este escenario lo que hemos de hacer es crear una dll que contenga la función que queramos hookear (tiene que tener la misma 'signature' para que cuando sea llamada desde el programa funcione correctamente) teniendo en cuenta que hemos de tener la precaución de 'guardar' la información de la función original. Esto es así ya que vamos a necesitar llamarla nosotros para posteriormente aplicarle los filtros. Al código 'extra' que le vamos a añadir para cambiar el funcionamiento se le conoce con el nombre de 'Trampolines'

Para facilitar la labor de crear DLLs para realizar API Hooking existen bastantes librerías, algunas de pago, otras totalmente OpenSource, de todas ellas destaco:
Oficial de Microsoft
Última versión 2006
Con licencia comercial
Gratuita
OpenSource
Se usa en grandes proyectos
  • Mhook
OpenSource
Fácil de implementar

Una vez tenemos nuestra DLL, necesitamos inyectarla en un proceso para alterar su funcionamiento, para ello existen varias técnicas

Fuente: http://blog.opensecurityresearch.com/2013/01/windows-dll-injection-basics.html
En concreto debemos tener en cuenta:

1- ¿ Vamos a cargar la DLL o vamos a copiar el código de la DLL 'a fuego' dentro del proceso ?. Lo primero es mas ligero, lo segundo hace más indetectable el hook.

2- Elegir el método para cargar la DLL:
  • CreateRemoteThread()
Muy conocida
Estándar
Tiene problemas con Windows Vista y superiores
  • NtCreateThreadEx()
No es oficial
Es más compleja de usar
Funciona en Vista y superiores
  • Suspender, inyectar y resumir:
Mucho más invasiva
Más efectiva

Una vez completada la teoría, vamos a la práctica para poner en conjunción todo lo hablado previamente. De entrada necesitamos una DLL que haga algo interesante. Vamos a usar este ejemplo basado en Mhook ya que es bastante gráfico y didáctico.

Si ojeamos el código un poco por encima llegamos a esta parte:

NTSTATUS WINAPI HookedNtQuerySystemInformation(
    __in       SYSTEM_INFORMATION_CLASS SystemInformationClass,
    __inout    PVOID                    SystemInformation,
    __in       ULONG                    SystemInformationLength,
    __out_opt  PULONG                   ReturnLength
    )
{
    NTSTATUS status = OriginalNtQuerySystemInformation(SystemInformationClass,
        SystemInformation,
        SystemInformationLength,
        ReturnLength);

    if (SystemProcessInformation == SystemInformationClass && STATUS_SUCCESS == status)
    {
        //
        // Loop through the list of processes
        //

        PMY_SYSTEM_PROCESS_INFORMATION pCurrent = NULL;
        PMY_SYSTEM_PROCESS_INFORMATION pNext    = (PMY_SYSTEM_PROCESS_INFORMATION)SystemInformation;
        
        do
        {
            pCurrent = pNext;
            pNext    = (PMY_SYSTEM_PROCESS_INFORMATION)((PUCHAR)pCurrent + pCurrent->NextEntryOffset);

            if (!wcsncmp(pNext->ImageName.Buffer, L"calc.exe", pNext->ImageName.Length))
            {
                if (0 == pNext->NextEntryOffset)
                {
                    pCurrent->NextEntryOffset = 0;
                }
                else
                {
                    pCurrent->NextEntryOffset += pNext->NextEntryOffset;
                }

                pNext = pCurrent;
            }            
        } 
        while(pCurrent->NextEntryOffset != 0);
    }

    return status;
}  

Podemos ver que la función que vamos a 'Hookear' es NtQuerySystemInformation() y el código extra que vamos a interponer entre la función original y la función hecha a medida lo que hace es filtrar el resultado de NtQuerySystemInformation() y eliminar cualquier referencia a los procesos que se llamen calc.exe, por lo que a la postre, sería una suerte de rootkit para ocultar procesos.

Para hacer la inyección vamos a tomar Process Explorer y usar la herramienta Remote DLL Injector para cargar nuestra DLL y de esa forma ocultar los procesos que se llamen calc.exe

Lo primero es compilar el código, para eso se puede usar Visual Studio que generará la DLL correctamente. 

Una vez tengamos la DLL vamos a inyectarla a process explorer


En este escenario nos interesa quedarnos con el PID del proceso asociado a process explorer ya que lo vamos a necesitar cuando usemos Remote Dll Injector


Básicamente esta herramienta nos pide que le digamos el PID sobre el que inyectar y la ruta de la DLL que deseamos cargar, si lo ejecutamos contra Process Explorer, el resultado es el siguiente:


¡¡ Sorpresa !! el proceso calc.exe ha desaparecido de la lista de procesos, ahora Process Explorer es incapaz de listar ese proceso ya que cuando llama a NtQuerySystemInformation() en realidad está llamando a nuestra función quien a su vez llama a la original pero filtra el resultado

El siguiente paso lógico es: ¿y si hacemos la inyección persistente? Para eso, Windows nos ofrece una clave del registro donde indicarle las DLLs que queremos que se inyecten automáticamente en el sistema, llamada AppInit_DLLs. Esta clave es de libre uso en Windows XP y 7 pero a partir de Windows 8 viene configurada por defecto para cargar solo DLLs que estén firmadas digitalmente por un certificado reconocido. Obviamente esa restricción se puede deshabilitar




Si la configuramos para que cargue nuestra DLL, el efecto final será que todos los procesos del sistema la van a cargar y de esa forma ningún proceso (en teoría) va a ser capaz de listar los procesos con nombre calc.exe

25 comments :

uno dijo...

Hola Yago, no entiendo... ¿Porque dices que la función de la DLL que inyectemos tiene que llamarse igual a la función de la API que queremos hookear?, en IAT hooking o Inline hooking (los dos metodos más comunes en Windows) no es necesario. En IAT se modifiría esta tabla cambiando la dirección del puntero de la funcion original a la posicion de memoria de la funcion de hook (independientemente del nombre, el cual no hace falta), y en inline hooking se haría un trampolin a la dirección de nuestra función (independientemente del nombre también). ¿Te referias a algun otro tipo de hook especifico en donde es extrictamente necesario saber el nombre de la función?


Sin acritud

ghjtyu dijo...

donde esta el boton para compartir esta publicacion en twitter?

Anonimo dijo...

Muy buen articulo, claro y conciso. Para cuando uno más extenso y detallado?

Alejandro Ramos dijo...

¿? Si te animas, podemos publicarlo como contribución.

JJ dijo...

Muchas gracias, muy buen artículo. En mi caso, que no soy más que un "aficionado" a la parte de seguridad (me agrupo más bien en el mundo de los "fucking sysadmin"), siempre hecho en falta a estos artículos tan entendibles la parte de "defensa", es decir, cosas que para gente no docta en la materia, que entendemos la problemática gracias a vosotros, podamos intentar implementar o al menos conocer para que no nos pasen cosas así en un momento dado (no sé si EMET en casos así hace algo, si estando en un dominio el quitar la restricción de la DLL certificada se puede configurar para que sí o sí esté así y no se altere, etc..).
Una vez más, muchas gracias y enhorabuena!

JJ dijo...

Perdón, por ejemplo, como referencia de "defensa" muy básica para poder ver más cosas:
http://blogs.technet.com/b/markrussinovich/archive/2011/02/27/3390475.aspx

En Windows 7 y Windows 8, el Code Signed no está habilitado por defecto (sí en 2008R2):

http://msdn.microsoft.com/en-us/library/dd744762(v=vs.85).aspx

En Windows 8/8.1 y Windows Server 2012/2012R2 parece igual (salvo si se usa secure boot)

http://support.microsoft.com/kb/2853424

De nuevo gracias, es sólo por intentar aportar ayuda para "defenderse" :-)

Yago Jesus dijo...

Tienes toda la razón, donde pone 'el mismo nombre' quise poner 'la misma signature', donde sí tiene sentido esta frase es en el post que haré sobre Linux y ld.so.preload

Gracias por la corrección !

Yago Jesus dijo...

Efectivamente, es en 8 y no en 7 donde viene 'hardenizada', ya lo he corregido. Muchas gracias !!

sate dijo...

Que tal soy nuevo por aca y justamente estaba tratando de modificar una dll al menos uno de sus parametros para una demostracion, pero esta en hexadecimal lo eh intentado realizar con el software lamado reflector.8 pero no he podido para poderlo traducir a lenguaje de alto nivel ya que lei que con este podria hacerlo espero y me puedas ayudar gracias.

Anonimo dijo...

No se si yo estoy mal o me parece que la función "hookeada" HookedNtQuerySystemInformation no verifica el nombre del primer proceso que recibe?

alex87 dijo...

Alguien me puede decir cual es el problema? gracias.



Traceback (most recent call last):

File "C:\Users\Alex\Desktop\cryp.pyw", line 13, in

from M2Crypto import EVP

ImportError: No module named M2Crypto

Bruno Cardenas Cyberoff dijo...

dame tu facebook faz

Fer dijo...

Tenes idea como llevar la fecha y hora a un formato entendible? Gracias

Joel dijo...

yo tenia ese mismo problema y lo solucione instalando el M2Crypto en la misma ruta donde esta el msgstore.db.crypt5

Joel dijo...

instala el M2Crypto en la misma ruta donde esta el msgstore.db.crypt5

pablofranciscoperezperez dijo...

esta chido el chat

Cabsha dijo...

¿Alguién sabrá como acceder a las URLs de imágenes que están guardadas en la db?

FABIAN dijo...

A MI ME SALE EL ERROR SISTEMA NO PUEDE ENCONTRAR EL ARCHIVO ESPECIFICADO ESTO YA AL FINALIZAR

Javi dijo...

Después de hacer lo que comentas me aparece otro error:

Traceback (most recent call last):

File "pwncrypt5.py", line 36, in

decrypt(sys.argv[1],sys.argv[2])

File "pwncrypt5.py", line 30, in decrypt

sys.stdout.write(cipher.final())

File "C:\Python27\lib\site-packages\M2Crypto\EVP.py", line 128, in final

return m2.cipher_final(self.ctx)

M2Crypto.EVP.EVPError: bad decrypt

¿Alguna idea?

Javi dijo...

Hola, con python me sale este error:


Traceback (most recent call last):

File "pwncrypt5.py", line 36, in

decrypt(sys.argv[1],sys.argv[2])

File "pwncrypt5.py", line 30, in decrypt

sys.stdout.write(cipher.final())

File "C:\Python27\lib\site-packages\M2Crypto\EVP.py", line 128, in final

return m2.cipher_final(self.ctx)

M2Crypto.EVP.EVPError: bad decrypt

¿Alguna idea?

sate dijo...

por aqui seria mejor no lo crees

conker21 dijo...

como se usa este codigo en python, como invovarlo? donde copio el codigo? y donde pongo el archivo que quiero desencriptar, donde se coloca el correo? agradeceria un tutorial mas detallado, principiante, gracias!!

Alex Torres Torrescrack dijo...

Hola Yago, primero gracias como siempre por compartir, recien escribi algo del tema y quizas puedan complementarse ambos, por si a alguien le interesa seguir leyendo y practicando del tema dejo el enlace:

http://www.elladodelmal.com/2014/03/uso-de-api-hooking-en-windows-para.html

aguml dijo...

hola amigo, soy nuevo en eso y en tu codigo tengo dudas. ¿En que punto se hace el trampolin? ¿Donde haces que ejecute tu codigo? ¿Como haces para calcular la direccion del salto a tu codigo? Si pones un salto en el inicio de la api se destruye parte del codigo de la api que es necesario. ¿Como se hace para que ejecute el codigo que has destruido? Ya ves que tengo muchas dudas.

Alexander Aguirre dijo...

Yago, una pregunta existe manera de hacer un debug en visual studio por ejemplo que es lo q uso...