
Rusty Hell's Gate / Halo's Gate / Tartarus' Gate / FreshyCalls / Biblioteca Syswhispers2
Llamé a este proyecto Mordor porque Hell's Gate / Halo's Gate / Tartarus' Gate me recuerdan a la Puerta Negra de Mordor en El Señor de los Anillos por alguna extraña razón jaja y el proyecto necesita un nombre genial, así que ¿por qué no?
Créditos a Middle-earth: Shadow of War Wiki
El hooking es una técnica utilizada para interceptar llamadas a funciones preexistentes o redirigir el flujo de ejecución de una API legítima a otra ubicación en memoria. Esta ubicación de memoria podría estar controlada por un atacante, un antivirus (AV), un sistema de detección y respuesta en el endpoint (EDR) o un anti-cheat (AC).
Inline Hooking: Esto nos permite reemplazar los primeros bytes (instrucciones de ensamblador) de la función objetivo con una instrucción de salto para redirigir el flujo de ejecución a otra ubicación en memoria.
Import Address Table Hooking (IAT hooking): La tabla de direcciones de importación es una tabla de búsqueda de punteros a funciones importadas desde DLLs o ejecutables. El IAT hooking nos permite reemplazar la dirección de la función en la tabla de direcciones de importación por otra para redirigir el flujo de ejecución del programa.
Créditos a Kyle Mistele
Hell's Gate es una técnica de inyección de procesos que nos permite buscar una serie de bytes llamada syscall stub, desde el módulo ntdll.dll para extraer los números de llamada al sistema y guardarlos en una tabla de memoria dedicada, que luego se utiliza para llamar directamente a las API del sistema. Sin embargo, la limitación de Hell's Gate es que necesita acceso a un módulo ntdll.dll limpio si las funciones están hookeadas. De lo contrario, no puede rellenar los números de llamada al sistema necesarios y finalmente falla al realizar llamadas a la API nativa. Para abordar este problema, nació una hermana gemela llamada Halo's Gate, que no es más que un parche a Hell's Gate basado en una observación muy simple.
Cuando se coloca un hook en una función (jmp <address>) no podremos recuperar dinámicamente los números de llamada al sistema, así que para abordar este problema podemos fijarnos en los números de llamada al sistema de las funciones vecinas y ajustar los cálculos en consecuencia para obtener nuestro número de llamada al sistema, porque los ID de llamada al sistema en el syscall stub se suceden de forma incremental.
Hell's Gate solo comprueba una secuencia de bytes en el siguiente orden 4c8bd1b8, que en ensamblador se ve así:
mov r10, rcx
mov eax, <syscall>
La técnica Halo's Gate hace lo mismo que Hell's Gate, pero con una comprobación adicional para ver si hay un hook en su lugar, verificando si el primer byte de la exportación es e9 (jmp) y, si hay un hook en su lugar, entonces Halo's Gate comienza a mirar las funciones vecinas y ajusta los cálculos en consecuencia para obtener nuestro número de llamada al sistema, ya que el ID de syscall en el stub de syscall sigue uno tras otro de forma incremental.
Sin embargo, no todos los EDR enganchan en la misma ubicación (al inicio de la función). ¿Qué pasa si los EDR enganchan justo después de mov r10, rcx (4c8bd1)? Esto romperá nuestro código.
Tartarus' Gate resuelve este problema añadiendo una comprobación adicional a Halo's Gate, buscando también estos bytes de forma secuencial 4c8bd1e9, lo que en ensamblador se ve así.
mov r10, rcx
jmp <address>
Sin embargo, si todas las funciones están enganchadas, entonces podemos encontrar la primera y desenganchar todas una por una comenzando desde el ID de llamada 0. Este método se llama Veles' Reek en SEKTOR7.
La técnica FreshyCalls busca en el Export Directory funciones que comienzan con Nt (excluyendo Ntdll) y las ordena por direcciones. Sorprendentemente, la dirección más baja es el número de syscall 0 y la siguiente será los números de syscall 1 y 2 y 3... Puedes verificarlo tú mismo en x64 dbg o Windbg.
Syswhispers2 hace lo mismo, pero en lugar de buscar las funciones Nt dentro del directorio de exportación de ntdll.dll, busca funciones que comienzan con Zw. Sorprendentemente, las funciones Zw y las funciones Nt apuntan a los mismos stubs de syscall y tendrán el mismo número de llamada al sistema.
Aquí podemos verificarlo:
La diferencia entre las funciones Zw y Nt se explica por Microsoft aquí:
"*La API de servicios del sistema operativo nativo de Windows se implementa como un conjunto de rutinas que se ejecutan en modo kernel. Estas rutinas tienen nombres que comienzan con el prefijo Nt o Zw. Los controladores en modo kernel pueden llamar a estas rutinas directamente. Las aplicaciones en modo usuario pueden acceder a estas rutinas mediante llamadas al sistema.
Con algunas excepciones, cada rutina de servicios del sistema nativo tiene dos versiones ligeramente diferentes que tienen nombres similares pero prefijos diferentes. Por ejemplo, las llamadas a NtCreateFile y ZwCreateFile realizan operaciones similares y, de hecho, son atendidas por la misma rutina del sistema en modo kernel.
Para las llamadas al sistema desde modo usuario, las versiones Nt y Zw de una rutina se comportan de forma idéntica. Para las llamadas desde un controlador en modo kernel, las versiones Nt y Zw de una rutina difieren en cómo manejan los valores de los parámetros que el llamador pasa a la rutina.
Un controlador en modo kernel llama a la versión Zw de una rutina de servicios del sistema nativo para informar a la rutina de que los parámetros provienen de una fuente confiable en modo kernel. En este caso, la rutina asume que puede usar los parámetros de forma segura sin validarlos primero. Sin embargo, si los parámetros pueden provenir de una fuente en modo usuario o de una fuente en modo kernel, el controlador llama en su lugar a la versión Nt de la rutina, que determina, basándose en el historial del hilo llamador, si los parámetros se originaron en modo usuario o en modo kernel*"
Hell's Gate: Esto analizará ntdll.dll para encontrar el inicio del stub de syscall (4c8bd1b8) y luego recuperar el ID de syscall. Sin embargo, si el stub de syscall está enganchado, entonces nuestro código fallará.
Halo's Gate: Lo mismo que Hell's Gate, pero añade una comprobación adicional para ver si los primeros hooks están en su lugar comprobando si el primer byte es e9 (jmp) y si hay un hook en su lugar, entonces Halo's gate comienza a mirar las funciones vecinas y ajusta los cálculos en consecuencia para obtener nuestro número de llamada al sistema, ya que el ID de syscall en el stub de syscall se sucede de forma incremental.