Skip to content
KitploitKITPLOIT
HerramientasExploitsBlog
Log in
Enviar
HerramientasExploitsBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

FeedsContactoPrivacidad© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
Herramientas/GitHubGitHub/zypherion-technologies/hallwatch
Herramientas DefensivasAnálisis de MalwareAnálisis de BinariosInteligencia de AmenazasRespuesta a Incidentes
GitHubzypherion-technologies/hallwatch

HallWatch

Usermode detector que captura indirect syscalls. Atrapa Hell's Hall, Tartarus' Gate, RecycledGate y VEH syscalls & Many more.

Ver Repositorio

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
871010hace 3 mesesRevisado por Kitploit
Compartir

HallWatch

License: GPL v3 Website Discord Telegram X

Copyright (C) 2026 Adam Zypherion <[email protected]> — licenciado bajo GPL-3.0.


Las syscalls indirectas son simples una vez que has visto una. El loader recorre ntdll, lee el SSN del prólogo de Nt*, encuentra los dos bytes 0F 05 un poco más adelante, establece r10/rax/rdx/r8/r9 por sí mismo, y salta (jmp) directamente a esos dos bytes. La syscall ocurre desde dentro de ntdll. Tu hook sobre la exportación de kernel32 nunca se toca. Tu hook sobre la exportación de ntdll nunca se toca. [RSP] en el momento de la SYSCALL apunta de vuelta a la página RWX del loader, pero nada de la llamada parece inusual desde fuera.

HellHall proc
    mov  r10, rcx
    mov  eax, dwSSN
    jmp  qword ptr [qAddr]
    ret
HellHall endp

Las variantes tienen nombres. Tartarus' Gate y RecycledGate usan la instrucción syscall de un stub pero el SSN de otro diferente, así que incluso si registras el nombre del stub que reporta tu hook, es una mentira. VEH syscall provoca intencionadamente una violación de acceso y usa su propio VEH para reescribir el contexto de manera que RIP aterrice en la instrucción syscall de ntdll con los registros ya preparados. Hell's Gate no usa ntdll en absoluto. El loader escribe 0F 05 en su propia página RWX y ejecuta desde allí.

La respuesta en modo kernel (KM) es un driver; quizás llegue a esto con el tiempo y publique un proyecto, pero no va a suceder pronto :D


PAGE_GUARD parece que debería funcionar. Marca la página que contiene los bytes de syscall con PAGE_GUARD, captura el STATUS_GUARD_PAGE_VIOLATION en un VEH, inspecciona, redirige a un trampolín privado, establece el flag de trap, sale de un solo paso, rearma la página. Una trampa por llamada Nt, sin importar cómo llegó el loader allí. Funciona contra todo excepto Hell's Gate.

El problema es que el SO no coopera. PAGE_GUARD es de un solo uso, ¿qué significa eso? Cada vez que se dispara, el bit se borra y tienes que volver a colocarlo. Tu manejador hace syscalls (NtProtect para volver a poner la guardia, NtContinue para reanudar) y esas syscalls tienen stubs, y esos stubs están en la página que acabas de proteger. Trabajé alrededor de la mayor parte de eso con stubs de syscall privados construidos en una página separada que poseíamos (asignar RWX, escribir mov r10,rcx; mov eax,SSN; syscall; ret, bloquear RX, nunca tocar ntdll), pero era frágil. Cada versión menor de Windows cambiaba la sincronización. Cada hilo que la muestra generaba era otra carrera contra el worker de integridad que reconstruía la guardia. El éxito de la demostración estaba entre el 30 y el 50 por ciento en diez ejecuciones en la misma máquina.

Eventualmente dejé de intentar convencer a Windows de que PAGE_GUARD se comportara como yo quería, y probé sobrescribir el byte en su lugar.


Si tienes problemas para entenderlo, mira este video :)

https://github.com/user-attachments/assets/0cb670fd-e51b-413e-bf00-08f9297888ed


El byte en la instrucción syscall es 0F 05. El primer byte solo (0F) es el prefijo para una familia de códigos de operación de dos bytes que incluye SYSCALL, CPUID y RDTSC. Por sí solo no es ejecutable; la CPU necesita el segundo byte para decodificar. Si reemplazas el primer byte con 0xCC (INT3), el par se convierte en CC 05, que la CPU decodifica como INT3 seguido de un byte extraviado al que nunca llega. Cualquier camino de código que aterrice en esa dirección genera EXCEPTION_BREAKPOINT.

Ese es el mecanismo completo. Nuestro VEH captura el breakpoint, busca a qué stub pertenece la dirección (construimos el mapa al inicio enumerando las exportaciones de ntdll), ejecuta tres comprobaciones sobre quien haya aparecido, y establece Context->Rip a un trampolín privado que realiza la syscall real y retorna. El byte permanece CC. El siguiente llamador lo encontrará de la misma manera, así que no hay cambios de ida y vuelta en la protección de página.

Para ser claros: sí, esto es hooking mediante sobrescritura de byte. Solo que no está en el byte que la gente suele entender por "hook de ntdll". El hook clásico de EDR sobrescribe los primeros bytes del stub con un JMP <my_func>:

ntdll!NtAllocateVirtualMemory:
  E9 ?? ?? ?? ??       jmp my_hook     ; sobrescribe "mov r10, rcx"
  ...
  0F 05                syscall
  C3                   ret

Eso es exactamente lo que las syscalls indirectas evitan. El loader lee el SSN por sí mismo y salta directamente al 0F 05, el prólogo (y tu jmp) nunca se ejecuta, el hook nunca se dispara. Lo que nosotros hacemos es sobrescribir el propio byte de syscall:

ntdll!NtAllocateVirtualMemory:
  4C 8B D1             mov r10, rcx     ; intacto
  B8 18 00 00 00       mov eax, 18h     ; intacto
  CC 05                int3 / 05        ; antes era 0F 05, escribimos CC sobre el 0F
  C3                   ret

Ahora no importa cómo llegaste a esa dirección: a través del prólogo, a través de un salto indirecto que omite el prólogo, a través de una reescritura de contexto VEH-syscall que coloca RIP allí, etc. Si la CPU ejecuta ese byte, atrapa. Misma familia de técnica que el hook clásico, ubicación diferente, cobertura completamente diferente.

El trampolín se ve así:

F3 0F 1E FA              endbr64
49 89 CA                 mov r10, rcx
B8 <stub SSN>            mov eax, ssn
0F 05                    syscall
C3                       ret

Las tres comprobaciones no han cambiado desde la versión de PAGE_GUARD porque eran las tres comprobaciones correctas; solo necesitaban un lugar confiable donde vivir.

Dirección de retorno. [RSP] es donde la syscall habría retornado. Para una llamada real está dentro de ntdll, kernel32, kernelbase o una de las DLL de runtime relacionadas. Para Hell's Hall está dentro de cualquier página RWX desde la que el loader esté ejecutando. Tenemos una lista corta de destinos de retorno de confianza construida al inicio llamando a GetModuleHandle para esos nombres y leyendo los rangos .text de sus cabeceras PE.

SSN. El prólogo del stub ya se ha ejecutado antes de que llegue al byte de syscall, así que eax contiene el valor que se cargó en él. Si el loader hizo un intercambio de Tartarus, ese valor no coincidirá con el SSN que leímos de este mismo stub durante la enumeración. Registramos la discrepancia, y el trampolín escribe el SSN correcto antes de su propia syscall, por lo que la función kernel que se ejecuta es la que pertenece al byte, no la que el loader quería. La técnica se registra y neutraliza en el mismo paso.

Recorrido de pila. RtlVirtualUnwind desde el contexto actual, cinco marcos hacia arriba. El RIP de cada marco debería estar dentro de un módulo conocido y debería tener una entrada RUNTIME_FUNCTION. El shellcode y los gadgets ROP fallan esto incluso cuando [RSP] en sí parece de confianza, lo cual un loader puede falsificar (puede predecir aproximadamente dónde estará su llamador en la memoria y forjar una dirección de retorno creíble allí).

Si alguna comprobación falla, empujamos una pequeña estructura en un anillo libre de bloqueos y el hilo de drenaje la imprime la próxima vez que se despierte:

Descargar herramienta