Tutoriales

Un solucionador y desenganchador de API de Windows universal

Desengancharme es un solucionador y desenganchador de API de Windows universal que aborda el problema de invocar llamadas al sistema no supervisadas de su malware Red Teams

En la era de los AV y EDR intrusivos que introducen parches calientes en los procesos en curso debido a sus mayores requisitos ópticos, los oponentes modernos deben tener una herramienta robusta para deslizarse a través de estos guardias. La implementación planificada de un solucionador para importaciones dinámicas, que podría separar las funciones utilizadas durante la operación, es un paso más hacia el fortalecimiento de la resistencia de los oponentes.

La solución que propongo aquí es cambiar el uso de importaciones de WinAPI resueltas por vinculador y permanecer visible en los encabezados PE del ejecutable compilado (especialmente la tabla de direcciones de importación) para preferir un enfoque completamente dinámico, que insiste en solo resolver dinámicamente las importaciones. Un resolutor dinámico de este tipo puede equiparse con una lógica de desenganche que se ejecuta en segundo plano, sin instrucciones del operador.

Escaparate

Aquí es cómo UnhookMe Obras de ejemplo:

  1. Nos presenta la primera MessageBoxW ese no es el tema de enganchar
  2. Entonces nos enganchamos MessageBoxW Prólogo a nosotros mismos para que siempre devuelva 0 sin mostrar su mensaje
  3. Eventualmente rompemos MessageBoxW dinámica con el UnhookingImportResolver Resolver, que detecta los parches de Prolog aplicados y restaura los bytes originales, desengancha de forma eficaz MessageBoxW Funcionalidad.

Mientras tanto, cuando aparecen los cuadros de mensaje, estas son las líneas del registro que se imprimirán en la salida estándar de la consola:

[~] Icono fijo kernel32.dll! CreateFileA
[~] Icono fijo kernel32.dll! ReadProcessMemory
[~] Icono fijo kernel32.dll! MapViewOfFile
[~] Icono fijo kernel32.dll! VirtualProtectEx
[#] Gancho de trampolín que se encuentra en el símbolo: MessageBoxW. Bytes originales restaurados del archivo.
[~] Icono fijo user32.dll! MessageBoxW

Cómo usarlo

Hay un total de 5 archivos fuente / encabezado de C ++ que su solución debe contener. Sin embargo, su ejecutable principal solo necesita contener dos encabezados obligatorios, como se describe a continuación.

  • resolver.h – encabezado con la mayoría UnhookingImportResolver Implementación y definiciones de macros prácticas
  • resolver.cpp – Código fuente con opciones globales definidas
  • usings.h – un archivo de encabezado grande y desagradable con docenas de using Definiciones de tipos para WinAPI de uso frecuente
  • PE.cpp – archivo de código fuente del analizador PE personalizado
  • PE.h – archivo de encabezado del analizador PE personalizado

Encabezados obligatorios

Solo se requieren dos encabezados para su programa:

# incluye «usings.h»
# Incluir «resolver.h»

Opciones globales

Hay algunas opciones globales que se pueden cambiar para afectar la forma en que funciona el solucionador o para informar de su actividad. Estos están al comienzo de. Están definidos resolver.cpp Archivo:

Opciones de resolución global:

  • globalQuietOption – configúrelo en verdadero si no desea ninguna salida
  • globalVerboseOption – configúrelo en verdadero si desea una salida exhaustiva detallada
  • globalAntiSplicingOption – Desenganche las funciones resueltas si están conectadas.
  • globalLogFilePath – dónde se deben redirigir las líneas de registro de salida. Si está vacío, elija stdout.

bool globalQuietOption = false;
bool globalVerboseOption = true;
bool globalAntiSplicingOption = true;
wchar_t globalLogFilePath[MAX_PATH] = L «»;

Especificación del tipo de API personalizado

Para poder utilizar resolutores, primero se debe declarar un tipo de puntero de función using Declaración de forma estricta:

using fn_FunctionName = ReturnType WINAPI (
    ParamType1 paramName1,
    ...,
    ParamTypeN paramNameN,
);

Este repositorio viene con usings.h Archivo de encabezado con tipos de uso predefinidos para docenas de API de Windows populares.

Ese Nombre de la función corresponde a la WinAPI que ImportResolver debería resolver, y este puntero de función debe estar marcado con la convención de llamada WINAPI ( __stdcall en x86 y __fastcall en x64). Ese Tipo de retorno debe seguir adelante WINAPI Tipo de cambio.

Resolución y uso de funciones

Después de definir el tipo de puntero de función como se indica arriba, podemos usarlo de la siguiente manera:

RESOLVE (nombre de la biblioteca, nombre de la función);
ReturnType output = _FunctionName (param1,…, paramN);

La macro RESOLVE se encarga de la instanciación ImportResolver Plantilla de objeto y personalice el nombre de la biblioteca especificada.

Resolver introduce varias definiciones de macros más que son fáciles de usar en diversas circunstancias, llamadas de constructor:

#definir 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 de resolución:

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

¿Como funciona?

El solucionador subyacente usa un analizador de encabezado PE personalizado que procesa cada módulo DLL referenciado para mapear sus exportaciones y verificar la integridad de los encabezados PE de ese módulo, así como la integridad de los bytes stub de la función referenciada.

La idea es esta:

  • Primero exhibimos LoadLibrary referenciado por la biblioteca de usuario (el que se utiliza como el primer parámetro para RESOLVE Macro) si no fue accesible a través de GetModuleHandle.
  • Luego procesamos los encabezados PE de la biblioteca cargada / referenciada, asignamos sus exportaciones, recuperamos una matriz de direcciones de exportación y calculamos estas direcciones nosotros mismos para la verificación cruzada.
  • Si la dirección de una rutina definida en la tabla de direcciones de exportación de la DLL no cumple con nuestras expectativas, la exportación se trata como un gancho EAT. Lo mismo se aplica si nuestra entrada en la tabla de direcciones de importación ejecutable (IAT) para esta función se ha cambiado y ya no se refiere a la posición correcta en la sección de código de la DLL, entonces la función se considera vinculada a IAT.
  • Suponiendo que no se han encontrado enlaces hasta ahora, buscamos los primeros N bytes del prólogo de la función y los comparamos con lo que está almacenado en el archivo DLL en el disco duro. Si hay una discrepancia entre los bytes recuperados de la memoria y del archivo, consideramos que la función está parcheada en línea (parcheada en caliente).
  • Si se ha marcado la función, devolvemos la dirección de exportación original (que calculamos nosotros mismos) y / o verificamos la entrada. Si hubiera bytes de parche, los restauraremos.
  • Finalmente, para optimizar el impacto en el rendimiento del resolutor, almacenamos en caché todas las bases de imágenes del módulo cargadas y las direcciones de funciones resueltas y las devolvemos desde una caché (donde std::map ) en aciertos posteriores.

Algunos de los problemas a los que se enfrenta un solucionador desmontado tan dinámico incluyen los problemas de iteración a través de API enrutadas (una DLL puede contener Exportthunk que dice que esta función no está implementada en este módulo sino en otro); lo que, si bien admite esta implementación, a veces rompe su lógica transversal.

Publicaciones relacionadas

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Botón volver arriba