El arte del API Hooking.

Iniciado por fary, 12 Diciembre 2015, 21:18 PM

0 Miembros y 2 Visitantes están viendo este tema.

fary

Bueno, en este texto voy a intentar explicar de que se trata el API hooking.

Una descripción rapida de lo que es este metodo sería algo así: realizar un gancho a una función del sistema para poder modificar su retorno o bién sus parametros.

¿Que podemos hacer con este metodo?

Pues de todo lo que se nos ocurra desde ocultar archivos, procesos, hasta espiar conversaciones de skype, bloquear llamadas al sistema de detecminados procesos, etc.


¿Que nociones debo tener para aplicar correctamente esta técnica?


Se deberá tener nociones de ensamblador, ademas el ejemplo que aquí usare esta en FASM, además de saber manejar un poco Ollydbg.


¿En que consiste dicha técnica?

Consiste en modificar los primeros bytes de la funcion que queremos hookear,  en esos primeros bytes escribiremos un salto a nuestra funcion (Que emulara la función original pero con los parametros que nosotros queramos).

Para modificar los primeros bytes de la API deberémos dar permisos de escritura y ejecución ya que si no no podremos escribir nuestro salto.


Dicho todo esto pasemos al código y a aprender su funcionamiento.

En este caso hookearemos la API  MessageBoxA, para ello crearemos nuestro propio ejecutable:

Código (asm) [Seleccionar]
include 'win32ax.inc'

.data
       msg1            db 'hola',0


.code
start:

   invoke MessageBoxA,0,msg1,0,0
   ret

.end start                    


la salida del programa es la siguiente:



Obtetivo: que nuestro programa cante lo que nosotros queramos.

El primer paso será ver el inicio de la API desde el olly DBG para que cuando nosotros escribamos el salto no dejemos ninguna instruccion a medias (si dejamos alguna instruccion a medias nuestro programa petara)

Asique abrimos OllyDbg y vemos que contiene el inicio de la API.



A nosotros en realidad solo nos interesan los primero 5 bytes, que es lo que ocupa el salto (jmp + Direccion).

En este caso nos viene perfecto ya que las 3 primeras instrucciones:

Código (asm) [Seleccionar]
mov edi, edi
push ebp
mov ebp, esp


Ocupan exactamente 5 bytes.

Bien, copiamos esas 3 instrucciones y las guardamos en un archivo de texto para no olvidarlas, ya que nos harán falta mas adelante.

Una vez hecho esto, lo que haremos será lo siguiente, obtendremos la dirección de MessageBoxA, y sobreescribiremos sus primeros bytes para que salte a nuestra función.

Código (asm) [Seleccionar]

include 'win32ax.inc'

.data
       msg1            db 'hola',0
       USER32          db 'USER32.DLL',0
       funcion         db 'MessageBoxA',0
       dirfun          dd ?
       proteccion      dd ?

.code
start:

   call Hook

   invoke MessageBoxA,0,msg1,0,0
   ret

   proc Hook
       invoke LoadLibrary, USER32
       invoke GetProcAddress,eax,funcion   ; Obtenemos la  direccion de la función
       mov [dirfun],eax

       mov ebx,eax

       mov eax, mMessageBox     ; Calculamos el salto relativo.
       sub eax,ebx
       sub eax,5


       push eax

       invoke VirtualProtect,[dirfun],5,PAGE_EXECUTE_READWRITE,addr proteccion   ; damos derechos de acceso.

       pop eax

       mov ebx, [dirfun]

       mov byte[ebx],0xE9  ;escribimos un jmp al inicio de la API verdadera
       inc ebx
       mov dword[ebx],eax   ; escribimos el salto que tiene que dar en el inicio de la api verdadera
       add ebx,4

       ret
   endp

   proc mMessageBox     ; Funcion que saltará cuando el programa llame a la API Original

       ret
   endp

.end start    



El texto esta comentado pero de igual forma puntualizaré algunas cosillas.

1- Para poder escribir sobre la API original es OBLIGATORIO dar derechos de escritura, en este ejemplo esto se hace con VirtualProtect
2- Tendremos que calcular el salto relativo a nuestra función,  como se ve claramente en el código
3- Llegados a este punto ya tenemos establecido el hook por lo que si probamos a ejecutar el programa de arriba no imprimira nada en pantalla, ya que hemos roto los primeros bytes de la API, es más, queda totalmente inutilizada llegados a este punto.

Ahora tendremos que programar la funcion mMessageBox, para que cante lo que nosotros queramos.

Algunos datos que tenemos que tener en cuenta es que los valores que se introdujeron en la pila sigue ahí, junto con la dirección de retorno de la función, nosotros tendremos que recuperar la funcion de retorno para que el programa siga su ejecución correctamente.

Para aplicar esto debemos saber que, al realizar un call lo que hace el procesador es introducir en la pila la siguiente instruccion que se ejecutara cuando la función a la que llamamos retorne. Por eso lo que nosotros hacemos será recuperarla, introducir lo que nosotros queramos que cante la API, ejecutar los primeros bytes originales de la API y saltar a la dirección original de la API + 5 bytes (recordemos que en esos primeros 5 bytes esta el hook, por eso los emulamos desde nuestra funcion).

Ahí va el código completo del hook comentado.

Código (asm) [Seleccionar]
include 'win32ax.inc'

.data
       msg1            db 'hola',0
       USER32          db 'USER32.DLL',0
       funcion         db 'MessageBoxA',0
       dirfun          dd ?
       proteccion      dd ?
       msghook         db 'API Hookeada!',0

.code
start:

   call Hook

   invoke MessageBoxA,0,msg1,0,0
   ret

   proc Hook
       invoke LoadLibrary, USER32
       invoke GetProcAddress,eax,funcion   ; Obtenemos la  direccion de la función
       mov [dirfun],eax

       mov ebx,eax

       mov eax, mMessageBox     ; Calculamos el salto relativo.
       sub eax,ebx
       sub eax,5


       push eax

       invoke VirtualProtect,[dirfun],5,PAGE_EXECUTE_READWRITE,addr proteccion   ; damos derechos de acceso.

       pop eax

       mov ebx, [dirfun]

       mov byte[ebx],0xE9  ;escribimos un jmp al inicio de la API verdadera
       inc ebx
       mov dword[ebx],eax   ; escribimos el salto que tiene que dar en el inicio de la api verdadera
       add ebx,4

       ret
   endp

   proc mMessageBox     ; Funcion que saltará cuando el programa llame a la API Original
       pop eax       ; obtenemos la direccion de retorno que introdujo el call

       pop ebx       ; sacamos los parametros original que se pasaron a la API
       pop ebx
       pop ebx
       pop ebx

       push 0       ; Introducimos los parametros que queremos que ejecute nuestra API
       push 0
       push msghook
       push 0

       push eax     ; Introducimos la direccion de retorno original

       mov edi, edi    ; Ejecutamos los 5 primeros bytes originales de la API
       push ebp
       mov ebp, esp

       mov ebx, [dirfun]   ; Calculamos el salto hacia la API original
       add ebx,5

       jmp ebx           ; Saltamos a la direccion de la API original + 5 bytes

       ret
   endp

.end start


Ejecutamos nuestro programa y veremos que solo cantará lo que nosotros queramos.  :D



¿Pero, como puedo hookear una función que esta en otro proceso?

Estando en su mismo proceso, esto se puede conseguir inyectando nuestra DLL con el hook en el proceso o bién desde una inyección de código.

Bueno pues de momento eso ha sido todo, lo he intentado explicar de manera simple y para que se entienda bien.

Decir también que los ejemplos han sido desarrollados y probados en una máquina x64 con Windows 7.

Cualquier duda o colsulta será respondida gustosamente.

saludos!

Un byte a la izquierda.

kub0x

Un tema muy interesante y obligatorio para aquellos interesados en ingeniería inversa o malware.

Hace tiempo hice un template en C++, le pasas el typedef de la función original y luego llamas a la función Hook() pasándole el  puntero a la función original y a la sustituta y ello solo redirecciona las llamadas de la original a la última.

https://github.com/FreeJaus/gea-PRISM/blob/master/Programming/DirectX%20Overlay/Tibia%20Suite%20Unmanaged/APIHook.h

Saludos y lo leeré cuando lo publiques ;)
Viejos siempre viejos,
Ellos tienen el poder,
Y la juventud,
¡En el ataúd! Criaturas Al poder.

Visita mi perfil en ResearchGate


fary

Hola kub0x !

El problema en tu código es que imagínate que las primeras instrucciones no ocupan la longitud que tu esperas, que crees que pasaría?  :laugh:

Lo que yo hice para implementar eso mismo fue hacer una tabla con las instrucciones mas comunes que solían aparecer en los primeros bytes y ya calcular cuando ocupaban.

saludos.
Un byte a la izquierda.

kub0x

Me ha gustado, hace poco me también me animé a hacerlo en ASM y se ve más bonito. Otra funcionalidad sería unhookear la API, hacer la llamada original, hacer tus cosillas y rehookear. ¿Seguirás publicando?

Sobre mi code, tienes razón, juego con que el prólogo ocupa generalmente 6 bytes, hasta ahora no he tenido problemas, aunque tampoco habré hookeado más de 30 APIs.

Saludos!
Viejos siempre viejos,
Ellos tienen el poder,
Y la juventud,
¡En el ataúd! Criaturas Al poder.

Visita mi perfil en ResearchGate


fary

Para hacer eso, solo hay que establecer una bandera, para que nuestro gancho sepa cuando modificar los parámetros y cuando no, fíjate:

Código (asm) [Seleccionar]
include 'win32ax.inc'

.data
        msg1            db 'hola',0
        USER32          db 'USER32.DLL',0
        funcion         db 'MessageBoxA',0
        dirfun          dd ?
        proteccion      dd ?
        msghook         db 'API Hookeada!',0

        HookState       dd 0

.code
start:

    call Hook

    invoke MessageBoxA,0,msg1,0,0

    mov [HookState],1      ; No Hook

    invoke MessageBoxA,0,msg1,0,0

    mov [HookState],0   ; Hook de nuevo

    invoke MessageBoxA,0,msg1,0,0

    ret

    proc Hook
        mov [HookState],0

        invoke LoadLibrary, USER32
        invoke GetProcAddress,eax,funcion   ; Obtenemos la  direccion de la función
        mov [dirfun],eax

        mov ebx,eax

        mov eax, mMessageBox     ; Calculamos el salto relativo.
        sub eax,ebx
        sub eax,5


        push eax

        invoke VirtualProtect,[dirfun],5,PAGE_EXECUTE_READWRITE,addr proteccion   ; damos derechos de acceso.

        pop eax

        mov ebx, [dirfun]

        mov byte[ebx],0xE9  ;escribimos un jmp al inicio de la API verdadera
        inc ebx
        mov dword[ebx],eax   ; escribimos el salto que tiene que dar en el inicio de la api verdadera
        add ebx,4

        ret
    endp

    proc mMessageBox     ; Funcion que saltará cuando el programa llame a la API Original
        cmp [HookState],1 ; Si no queremos que se establezca el hook
        je NoHook

        pop eax       ; obtenemos la direccion de retorno que introdujo el call

        pop ebx       ; sacamos los parametros original que se pasaron a la API
        pop ebx
        pop ebx
        pop ebx

        push 0       ; Introducimos los parametros que queremos que ejecute nuestra API
        push 0
        push msghook
        push 0

        push eax

        NoHook:

        mov edi, edi    ; Ejecutamos los 5 primeros bytes originales de la API
        push ebp
        mov ebp, esp

        mov ebx, [dirfun]   ; Calculamos el salto hacia la API original
        add ebx,5

        jmp ebx           ; Saltamos a la direccion de la API original + 5 bytes

        ret
    endp

.end start       


Sí, si la gente se interesa seguiré escribiendo cosillas.
Un byte a la izquierda.

zerointhewill

claro pe man excelente muy bueno en verdad  :D