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.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
BYOVD-DriverKiller — Ingeniería inversa y explotación de controladores | Kitploit
Herramientas/GitHubGitHub/alex3o/byovd-driverkiller
ExplotaciónIngeniería InversaPost-ExplotaciónAnálisis de BinariosAprendizaje y EducaciónRed Teaming
GitHubalex3o/byovd-driverkiller

BYOVD-DriverKiller

Ingeniería inversa y explotación de controladores

Ver Repositorio
831410hace 1 añoRevisado 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

BYOVD-DriverKiller

⚠️ Aviso: Este proyecto es estrictamente educativo y demostrativo. No está pensado para utilizarse en un contexto malintencionado. El objetivo es aprender la metodología de ingeniería inversa y los pasos de explotación de un driver de Windows.


Aquí explico el proceso que seguí para resolver el ejercicio propuesto por d1rk(SaadAhla) https://github.com/SaadAhla, que consiste en realizar ingeniería inversa y explotación sobre un driver legítimo, firmado y no presente en las listas de bloqueo (HVCI, LOLBIN...). Un programa en C que permite terminar cualquier proceso activo del sistema mediante este driver en modo kernel está disponible; detallo su funcionamiento un poco más abajo.

POC-BYOD

📃 Uso: DriverKiller.exe <nom_processus.exe> [-d]

Opción -d: Permite eliminar el servicio y el driver del sistema después de la explotación.

El modo testsigning debe estar activado en la máquina objetivo porque el certificado del driver ha caducado.


Parte 1 - Ingeniería inversa:

El ejercicio proporciona un archivo .sys, nombrado con su hash SHA-256. El primer paso es abrir este archivo con IDA.
IDA está disponible gratuitamente. Solo hay que ir al sitio de Hex-Rays para generar una licencia y descargar el software.

Empezamos enumerando la IAT (Import Address Table) del driver y buscando la llamada a la API que nos interesa: ZwTerminateProcess.

screen1-git

Al hacer doble clic en ZwTerminateProcess, IDA nos redirige al código compilado de esta función. Seleccionando la entrada y mostrando las referencias cruzadas, obtenemos la lista de funciones del driver que la llaman.

screen2-git

Observamos que es la función sub_12EF4, en el offset 1CE, la que utiliza ZwTerminateProcess. Después de un doble clic, IDA muestra su código compilado.

screen11-git

El código descompilado revela las llamadas a ZwOpenProcess (que abre un handle hacia el proceso objetivo) y a ZwTerminateProcess (que termina el proceso a través de ese handle).

Al consultar la documentación de ZwOpenProcess (https://learn.microsoft.com/fr-fr/windows-hardware/drivers/ddi/ntddk/nf-ntddk-zwopenprocess), vemos que el parámetro ClientID corresponde a un puntero que indica el PID del proceso objetivo.

En la línea anterior, ClientId.UniqueProcess se inicializa con la variable v22. Esta última se define justo encima:

v22 = (void )((_QWORD *)i + 10);

Para entender esta asignación, hay que identificar la variable i y el campo +10.

screen3-git

Más arriba en esta función, observamos una llamada a ZwQuerySystemInformation con el parámetro SYSTEM_PROCESS_INFORMATION. También entendemos que i es el iterador sobre las entradas de esta estructura con la variable v6.

Según la documentación de ZwQuerySystemInformation: (https://learn.microsoft.com/en-us/windows/win32/sysinfo/zwquerysysteminformation), esta función devuelve una matriz con una entrada por cada proceso activo en el sistema.

La estructura SYSTEM_PROCESS_INFORMATION se describe aquí: https://learn.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntquerysysteminformation

typedef struct _SYSTEM_PROCESS_INFORMATION {
    ULONG NextEntryOffset;
    ULONG NumberOfThreads;
    BYTE Reserved1[48];
    UNICODE_STRING ImageName;
    KPRIORITY BasePriority;
    HANDLE UniqueProcessId;
    PVOID Reserved2;
    ULONG HandleCount;
    ULONG SessionId;
    PVOID Reserved3;
    SIZE_T PeakVirtualSize;
    SIZE_T VirtualSize;
    ULONG Reserved4;
    SIZE_T PeakWorkingSetSize;
    SIZE_T WorkingSetSize;
    PVOID Reserved5;
    SIZE_T QuotaPagedPoolUsage;
    PVOID Reserved6;
    SIZE_T QuotaNonPagedPoolUsage;
    SIZE_T PagefileUsage;
    SIZE_T PeakPagefileUsage;
    SIZE_T PrivatePageCount;
    LARGE_INTEGER Reserved7[6];
} SYSTEM_PROCESS_INFORMATION;

Recordatorio: tamaños de algunos tipos en Windows x64

  • ULONG = 4 bytes
  • USHORT = 2 bytes
  • HANDLE = 8 bytes
  • PWSTR = 8 bytes
  • KPRIORITY (typedef de un LONG) = 4 bytes
  • UNICODE_STRING = 16 bytes, porque esta es su estructura:
  typedef struct _UNICODE_STRING {
    USHORT Length;        -> 2      
    USHORT MaximumLength; -> + 2 = 4
    PWSTR  Buffer;        -> + 8 = 12 (12 n'est pas un multiple de 8 donc padding de 4 ajouté en amont de Buffer) = 16
} UNICODE_STRING;

Cálculo del offset de UniqueProcessId:

    ULONG NextEntryOffset;        -> 4
    ULONG NumberOfThreads;        -> + 4 = 8
    BYTE Reserved1[48];           -> + 48 = 56
    UNICODE_STRING ImageName;     -> + 16 = 72
    KPRIORITY BasePriority;       -> + 4 = 76 (76 n'est pas un multiple de 8 donc padding de 4 ajouté) = 80
    HANDLE UniqueProcessId;       -> + 8 = 88

El miembro UniqueProcessId está, por tanto, en el offset 0x50 (80 decimal).

Al observar la asignación de nuestra variable v22, vemos que i se convierte (cast) a puntero QWORD (8 bytes)

v22 = (void )((_QWORD *)i + 10);
Por lo tanto, v22 corresponde a la dirección de i + 10 * 8 = 80 bytes. Esta variable contiene, entonces, el PID obtenido de la estructura SYSTEM_PROCESS_INFORMATION.

Para saber qué PID se pasará a ZwTerminateProcess, hay que analizar la condición que rodea esta asignación.

screen4-git

Vemos que primero se recupera el nombre de la imagen del proceso:

v9 = (wchar_t )((_QWORD *)i + 8);
Porque v9 = dirección de i + 8 × 8 = 64 bytes. Esto corresponde al Buffer del miembro ImageName, ya que dicho miembro se encuentra en el offset 56 + 2 (USHORT) + 2 (USHORT) + 4 (padding) = 64

Teniendo en cuenta las manipulaciones y los bucles que aparecen a continuación, se puede plantear la hipótesis de que se realiza una comparación entre el nombre del proceso pasado como argumento (a2) y los procesos activos del sistema v9/String.

sub_1C078(String, v9, (int)v13);
v17 = strupr(a2);
v18 = strupr(String);

Por lo tanto, se supone que el parámetro a2 contiene el nombre del proceso que se terminará mediante ZwTerminateProcess. Observamos que a2 es un parámetro de la función sub_12EF4. Para profundizar, hay que examinar las referencias de esta función (la he renombrado ZwTerminateProcessCaller para una mejor legibilidad).

screen5-git
Descargar herramienta