Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
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.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
UnhookMe — Resolvedor y desenganchador dinámico de la API de Windows que detecta y restaura funciones enganchadas (IAT, EAT, parches en línea) para invocar llamadas al sistema no monitoreadas desde malware de Red Team. | Kitploit
Herramientas/GitHubGitHub/mgeeky/unhookme
Herramientas DefensivasRed TeamingDesarrollo de Payloads
GitHubmgeeky/unhookme

UnhookMe

Resolvedor y desenganchador dinámico de la API de Windows que detecta y restaura funciones enganchadas (IAT, EAT, parches en línea) para invocar llamadas al sistema no monitoreadas desde malware de Red Team.

Ver Repositorio
34848hace 4 añosRevisado por Kitploit

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 →
Compartir

UnhookMe - Resolvedor de imports que se desengancha dinámicamente

En la era de los AVs y EDRs intrusivos que introducen parches en caliente a los procesos en ejecución para cumplir con sus requisitos de óptica mejorada, los adversarios modernos deben disponer de una herramienta robusta para sortear estos vigilantes. La implementación propuesta de un resolvedor de imports dinámico capaz de desenganchar funciones utilizadas sobre la marcha es un paso más hacia el fortalecimiento de los esfuerzos de resistencia del adversario.

La solución que propongo aquí es cambiar el uso de imports de WinAPI resueltos por el enlazador, que permanecen visibles en los encabezados PE del ejecutable compilado (específicamente en la Tabla de Direcciones de Importación), para favorecer un enfoque totalmente dinámico que insista en resolver los imports solo de forma dinámica. Dicho resolvedor dinámico puede equiparse con una lógica de desenganche que ocurre en segundo plano, sin ningún tipo de guía por parte del operador.


Ejemplo de uso más simple

Así es como puedes asegurarte de llamar a MessageBoxW desenganchada y sin monitorización:

root@kitploit:~
    RESOLVE(user32, MessageBoxW);
    _MessageBoxW(0, L"Look Ma! I'm unhooked!", L"Third - Unhooked", 0);

Toda la magia ocurre dentro de la macrodefinición RESOLVE, que construye un objeto ImportResolver<T> llamado _MessageBoxW.


Demostración

Animación de demostración de Unhookme

Así es como funciona el ejemplo de UnhookMe:

  1. Primero nos presenta un MessageBoxW que no está sujeto a enganche.
  2. Luego enganchamos nosotros mismos el prólogo de MessageBoxW para que siempre devuelva 0 sin mostrar su mensaje.
  3. Finalmente, resolvemos MessageBoxW dinámicamente usando el resolvedor UnhookingImportResolver, que detectará los parches aplicados al prólogo y restaurará los bytes originales, desenganchando efectivamente la funcionalidad de MessageBoxW.

Mientras se muestran los cuadros de mensaje, estas son las líneas de registro impresas en la salida estándar de la consola:

root@kitploit:~
[~] Resolved symbol kernel32.dll!CreateFileA
[~] Resolved symbol kernel32.dll!ReadProcessMemory
[~] Resolved symbol kernel32.dll!MapViewOfFile
[~] Resolved symbol kernel32.dll!VirtualProtectEx
[#] Found trampoline hook in symbol: MessageBoxW . Restored original bytes from file.
[~] Resolved symbol user32.dll!MessageBoxW

¿Cómo usarlo?

Hay un total de 5 archivos de código fuente/encabezados C++ que tu solución necesita incluir. Sin embargo, tu archivo principal del programa solo necesita incluir dos encabezados obligatorios, como se detalla a continuación.

  • resolver.h - encabezado que contiene la mayor parte de la implementación de UnhookingImportResolver y prácticas macrodefiniciones.
  • resolver.cpp - código fuente con las opciones globales definidas.
  • usings.h - un archivo de encabezado grande y engorroso que contiene decenas de definiciones de tipo using para WinAPIs de uso común.
  • PE.cpp - archivo de código fuente del analizador PE personalizado.
  • PE.h - archivo de encabezado del analizador PE personalizado.

Encabezados requeridos

Tu programa solo requerirá la inclusión de dos encabezados:

root@kitploit:~
#include "usings.h"
#include "resolver.h"

Opciones globales

Hay un par de opciones globales que se pueden cambiar y que afectan la forma en que el Resolvedor funciona o informa su actividad. Estas se definen al principio del archivo resolver.cpp:

Opciones globales del Resolvedor:

  • globalQuietOption - establecer a true si no deseas ningún tipo de salida.
  • globalVerboseOption - establecer a true si deseas una salida detallada.
  • globalAntiSplicingOption - desenganchar las funciones resueltas si están enganchadas.
  • globalLogFilePath - dónde redirigir las líneas de registro de salida. Si está vacío, se usa stdout.
root@kitploit:~
bool globalQuietOption = false;
bool globalVerboseOption = true;
bool globalAntiSplicingOption = true;

wchar_t globalLogFilePath[MAX_PATH] = L"";

Especificación de tipo de API personalizada

Para usar el Resolvedor, primero se debe declarar un tipo de puntero a función con una declaración using de forma estricta:

root@kitploit:~
    using fn_FunctionName = ReturnType WINAPI (
        ParamType1 paramName1,
        ...,
        ParamTypeN paramNameN,
    );

Este repositorio viene con un archivo de encabezado usings.h que contiene definiciones de tipo using predefinidas para decenas de APIs populares de Windows.

El FunctionName corresponderá a la WinAPI que queremos que ImportResolver resuelva y ese puntero a función debe estar marcado con la convención de llamada WINAPI ( __stdcall en x86 y __fastcall en x64). El ReturnType debe preceder al modificador de tipo WINAPI.

Resolución y uso de funciones

Una vez que se haya definido el tipo de puntero a función como se especificó anteriormente, podremos usarlo de la siguiente manera:

root@kitploit:~
    RESOLVE(libraryName, FunctionName);
    ReturnType output = _FunctionName(param1, ..., paramN);

La macro RESOLVE se encarga de instanciar el objeto plantilla ImportResolver y ajustar el nombre de la librería especificada.

El Resolvedor introduce varias macrodefiniciones más que ofrecen una invocación de constructor fácil de usar en diversas circunstancias:

root@kitploit:~
#define RESOLVE(mod, func)                    RESOLVE_PARAMETERIZED(mod, func, ::globalVerboseOption, ::globalAntiSplicingOption)
#define RESOLVE_NO_UNHOOK(mod, func)          RESOLVE_PARAMETERIZED(mod, func, ::globalVerboseOption, false)

#define RESOLVE_VERBOSE_UNHOOK(mod, func)     RESOLVE_PARAMETERIZED(mod, func, true, true)
#define RESOLVE_VERBOSE_NOUNHOOK(mod, func)   RESOLVE_PARAMETERIZED(mod, func, true, false)
#define RESOLVE_NOVERBOSE_UNHOOK(mod, func)   RESOLVE_PARAMETERIZED(mod, func, false, true)
#define RESOLVE_NOVERBOSE_NOUNHOOK(mod, func) RESOLVE_PARAMETERIZED(mod, func, false, false)

Constructor del Resolvedor:

root@kitploit:~
    template<typename Ret, typename ...Args>
    ImportResolver<Ret WINAPI(Args...)>(
            std::string dllName,
            std::string funcName,
            bool _verbose = false,
            bool _unhook = false,
            bool *_wasItHooked = nullptr
        )

¿Cómo funciona?

El resolvedor subyacente utiliza un analizador de encabezados PE personalizado que procesa cada módulo DLL referenciado para mapear sus exportaciones y verificar la integridad de los encabezados PE del módulo, así como la integridad de los bytes de código auxiliar de la función referenciada.

La idea es la siguiente:

  1. Primero emitimos LoadLibrary para cargar la librería referenciada por el usuario (la especificada como primer parámetro de la macro RESOLVE) si no se puede alcanzar mediante GetModuleHandle.
  2. Luego procesamos los encabezados PE de la librería cargada/referenciada, mapeamos sus exportaciones, recuperamos la matriz de direcciones de exportación y también calculamos esas direcciones nosotros mismos para una verificación cruzada.
  3. Si la dirección de una rutina definida en la Tabla de Direcciones de Exportación de la DLL no corresponde a lo que esperaríamos, la exportación se considera enganchada por EAT. Lo mismo ocurre si la entrada de nuestra Tabla de Direcciones de Importación (IAT) del ejecutable para esa función ha sido alterada y ya no apunta al lugar correcto en la sección de código de la DLL; entonces la función se considera enganchada por IAT.
  4. Suponiendo que no se encontraron enganches hasta ahora, obtenemos los primeros N bytes del prólogo de la función y los comparamos con lo que está almacenado en el archivo DLL en disco. Si hay discrepancia entre los bytes obtenidos de la memoria y los del archivo, consideramos que la función ha sido parcheada en línea (parcheada en caliente).
  5. Si la función se considera enganchada, devolvemos la dirección de exportación original (la que calculamos nosotros mismos) y/o desenganchamos la entrada. Si había bytes de parche, los restauraremos.
  6. Finalmente, para optimizar el impacto en el rendimiento del resolvedor, almacenamos en caché todas las bases de imagen de los módulos cargados y las direcciones de funciones resueltas, y las devolvemos desde un caché (siendo std::map) durante hits posteriores.

Entre los problemas que enfrenta un resolvedor de desenganche dinámico de este tipo están los problemas al recorrer APIs reenviadas (una DLL puede contener un código de exportación que indique que esta función no está implementada en este módulo, sino en otro), lo que, aunque esta implementación tiene soporte para ello, a veces rompe su lógica de recorrido.


☕ Apoya el proyecto ☕

Este y otros proyectos son el resultado de noches sin dormir y mucho trabajo duro. Si te gusta lo que hago y aprecias que siempre devuelvo algo a la comunidad, Considera invitarme un café (o mejor una cerveza) solo para darme las gracias! 💪


Autor

root@kitploit:~
   Mariusz Banach / mgeeky, 21
   <mb [at] binary-offensive.com>
   (https://github.com/mgeeky)
Descargar herramienta