Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
BYOVD-DriverKiller — Driver Reverse & Exploitation | Kitploit
Strumenti/GitHubGitHub/alex3o/byovd-driverkiller
ExploitationReverse EngineeringPost-ExploitationBinary AnalysisLearning & EducationRed Teaming
GitHubalex3o/byovd-driverkiller

BYOVD-DriverKiller

Driver Reverse & Exploitation

Vedi Repository
831411 mesi faRevisionato da Kitploit

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

BYOVD-DriverKiller

⚠️ Avvertenza : Questo progetto è strettamente educativo e dimostrativo. Non è destinato a essere utilizzato in un contesto dannoso. L'obiettivo è apprendere la metodologia del reverse engineering e le fasi di exploitation di un driver Windows.


Qui spiego l'approccio che ho seguito per risolvere l'esercizio proposto da d1rk(SaadAhla) https://github.com/SaadAhla, che consiste nell'effettuare reverse engineering ed exploitation su un driver legittimo, firmato e non presente nelle blocklist (HVCI, LOLBIN...). È disponibile un programma C in grado di terminare qualsiasi processo attivo sul sistema tramite questo driver in kernel-mode; ne descrivo il funzionamento più avanti.

POC-BYOD

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

Opzione -d : consente di eliminare il servizio e il driver dal sistema dopo l'exploitation.

La modalità testsigning deve essere attivata sulla macchina target perché il certificato del driver è scaduto.


Parte 1 - Reverse engineering :

L'esercizio fornisce un file .sys, denominato con il suo hash SHA-256. Il primo passo consiste nell'aprire il file con IDA.
IDA è disponibile gratuitamente. Basta andare sul sito di Hex-Rays per generare una licenza e scaricare il software.

Si inizia elencando la IAT (Import Address Table) del driver e cercando la chiamata all'API che ci interessa : ZwTerminateProcess.

screen1-git

Facendo doppio clic su ZwTerminateProcess, IDA ci reindirizza al codice compilato di questa funzione. Selezionando la voce e visualizzando i cross-reference, si ottiene l'elenco delle funzioni del driver che la chiamano.

screen2-git

Si osserva che è la funzione sub_12EF4, all'offset 1CE, a utilizzare ZwTerminateProcess. Dopo un doppio clic, IDA mostra il suo codice compilato.

screen11-git

Il codice decompilato rivela le chiamate a ZwOpenProcess (che apre un handle verso il processo target) e a ZwTerminateProcess (che termina il processo tramite tale handle).

Consultando la documentazione di ZwOpenProcess (https://learn.microsoft.com/fr-fr/windows-hardware/drivers/ddi/ntddk/nf-ntddk-zwopenprocess), si nota che il parametro ClientID corrisponde a un puntatore che indica il PID del processo interessato.

Sulla riga sopra, ClientId.UniqueProcess viene inizializzato con la variabile v22. Quest'ultima è definita poco sopra :

root@kitploit:~
v22 = (void )((_QWORD *)i + 10);

Per comprendere questa assegnazione, bisogna identificare la variabile i e il campo +10.

screen3-git

Più sopra in questa funzione, si osserva una chiamata a ZwQuerySystemInformation con il parametro SYSTEM_PROCESS_INFORMATION. Si comprende inoltre che i è l'iteratore sulle voci di questa struttura insieme alla variabile v6.

Secondo la documentazione di ZwQuerySystemInformation : (https://learn.microsoft.com/en-us/windows/win32/sysinfo/zwquerysysteminformation), questa funzione restituisce un array contenente una voce per ogni processo attivo sul sistema.

La struttura SYSTEM_PROCESS_INFORMATION è descritta qui : https://learn.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntquerysysteminformation

root@kitploit:~
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;

Promemoria : dimensioni di alcuni tipi su Windows x64

  • ULONG = 4 byte
  • USHORT = 2 byte
  • HANDLE = 8 byte
  • PWSTR = 8 byte
  • KPRIORITY (typedef di un LONG) = 4 byte
  • UNICODE_STRING = 16 byte perché la sua struttura è la seguente :
root@kitploit:~
  typedef struct _UNICODE_STRING {
    USHORT Length;        -> 2      
    USHORT MaximumLength; -> + 2 = 4
    PWSTR  Buffer;        -> + 8 = 12 (12 non è un multiplo di 8 quindi padding di 4 aggiunto prima di Buffer) = 16
} UNICODE_STRING;

Calcolo dell'offset di UniqueProcessId :

root@kitploit:~
    ULONG NextEntryOffset;        -> 4
    ULONG NumberOfThreads;        -> + 4 = 8
    BYTE Reserved1[48];           -> + 48 = 56
    UNICODE_STRING ImageName;     -> + 16 = 72
    KPRIORITY BasePriority;       -> + 4 = 76 (76 non è un multiplo di 8 quindi padding di 4 aggiunto) = 80
    HANDLE UniqueProcessId;       -> + 8 = 88

Il membro UniqueProcessId si trova quindi all'offset 0x50 (80 decimale).

Osservando l'assegnazione della nostra variabile v22, si nota che i viene convertito (cast) in un puntatore QWORD (8 byte)

root@kitploit:~
v22 = (void )((_QWORD *)i + 10);
Quindi v22 corrisponde all'indirizzo di i + 10 * 8 = 80 byte. Questa variabile contiene quindi il PID recuperato dalla struttura SYSTEM_PROCESS_INFORMATION.

Per sapere quale PID verrà passato a ZwTerminateProcess, bisogna analizzare la condizione che circonda questa assegnazione.

screen4-git

Si nota che il nome dell'immagine del processo viene prima recuperato :

root@kitploit:~
v9 = (wchar_t )((_QWORD *)i + 8);
Perché v9 = indirizzo di i + 8 × 8 = 64 byte. Ciò corrisponde al Buffer del membro ImageName, dato che tale membro si trova all'offset 56 + 2 (USHORT) + 2 (USHORT) + 4 (padding) = 64

Viste le manipolazioni e i loop qui sotto, si può ipotizzare che venga effettuato un confronto tra il nome del processo passato come argomento (a2) e i processi attivi sul sistema v9/String.

root@kitploit:~
sub_1C078(String, v9, (int)v13);
v17 = strupr(a2);
v18 = strupr(String);

È quindi il parametro a2 a dover contenere il nome del processo da terminare tramite ZwTerminateProcess. Si nota che a2 è un parametro della funzione sub_12EF4. Per andare oltre, bisogna esaminare i riferimenti di questa funzione (l'ho rinominata ZwTerminateProcessCaller per una migliore leggibilità).

screen5-git

Si constata che ZwTerminateProcessCaller viene chiamata dalla funzione sub_13624 all'offset 61A.

screen6-git

Prima di analizzare questo codice decompilato, vado a cercare i riferimenti della funzione sub_13624 (rinominata ZwTerminateProcessCallerCaller) per assicurarmi che questo codice venga effettivamente utilizzato dopo una chiamata API a DeviceIoControl dal UserMode.

screen§-git

Si constata che ZwTerminateProcessCallerCaller viene chiamata dalla funzione sub_14130 (rinominata ZwTerminateProcessCallerCallerCaller ...fortunatamente per noi, è l'ultima prima del punto di ingresso 😅).

screen7-git

Si constata che ZwTerminateProcessCallerCallerCaller viene chiamata dalla funzione sub_1A4A8 all'offset 306.

screen8-git

Si trova l'assegnazione della funzione ZwTerminateProcessCallerCallerCaller :

root@kitploit:~
memset64(DriverObject->MajorFunction, (unsigned __int64)ZwTerminateProcessCallerCallerCaller, 0x1Cu);
Il che significa che questa funzione viene assegnata a tutte le voci della tabella MajorFunction (0x1B = 27, ed esistono 28 IRP principali).

screen9-git

Prima di tornare alla funzione sub_13624 (alias ZwTerminateProcessCallerCaller), si recuperano il Symbolic Name e il Device Name (identici qui) : Viragtlt.

screen12-git

Tornando su ZwTerminateProcessCallerCaller, si nota che il suo secondo parametro (quindi a2) corrisponde a MasterIrp->AssociatedIrp.SystemBuffer.

screen13-git

Subito sopra la chiamata a ZwTerminateProcessCaller si trova il codice IOCTL : -2106392528 (in esadecimale : 0x82730030).

Grazie a queste informazioni, si può dedurre che per sfruttare questo driver è necessario inviare una chiamata API DeviceIoControl al driver con il nome del processo da terminare nel SystemBuffer.


🔷 Informazioni recuperate tramite il reverse engineering :

  • IOCTLCode : 0x82730030
  • Device Name : Viragtlt
  • Symbolic Name : Viragtlt
  • SystemBuffer deve contenere il nome del processo target

Parte 2 - Exploitation

Per sfruttare questo driver (se installato e attivo sulla macchina target), è necessario aprire un handle verso di esso, quindi effettuare una chiamata API DeviceIoControl con un Buffer contenente il nome del processo che si desidera terminare.
Per questo esercizio, ho sviluppato un progetto C che :

  • Verifica se il driver è presente e attivo sul sistema (con un nome di servizio preciso) :
    • Se sì, il programma sfrutta il driver con una chiamata API DeviceIoControl.
    • Se no, il programma estrae il driver dalle proprie risorse, lo rilascia sul desktop dell'utente, crea un servizio attivo e poi sfrutta il driver con una chiamata API DeviceIoControl. (Richiede i diritti di amministratore perché viene effettuata la creazione di un servizio.)
  • Se il driver è presente sul sistema ma il servizio non è avviato, il programma tenta di avviare il servizio e poi lo sfrutta con una chiamata API DeviceIoControl.

Ho anche aggiunto un'opzione -d che consente di eliminare il servizio e il driver dal sistema dopo l'exploitation.

Ecco il comportamento del programma C nel suo ciclo di esecuzione completo :

git

Evasione AV/EDR

In questo caso, DriverKiller.exe non viene rilevato da Microsoft Defender, né in statico né in dinamico. L'evasione non ha davvero senso qui perché il driver sfruttato ha un certificato scaduto, quindi il suo utilizzo in condizioni reali è difficilmente ipotizzabile. Ma per una migliore furtività, si sarebbe potuto implementare :

  • Il mascheramento di alcune chiamate API della tabella IAT tramite implementazioni personalizzate di GetProcAddress e GetModuleHandle
  • Un avvicinamento al Kernel per l'esecuzione delle chiamate API (Direct/Indirect Syscalls)
  • Tecniche Anti-VM / Anti-Debug

Rilevamento sul driver al 29/08/2025 (risultato già esistente, non ho inviato nulla su VirusTotal per ovvie ragioni) :

image

⚠️ Questo progetto è realizzato in un contesto di apprendimento. Può contenere imprecisioni o errori. Ogni suggerimento, correzione o discussione è il benvenuto ! 😃 Grazie a d1rk(SaadAhla) : https://github.com/SaadAhla !

Scarica lo strumento