Skip to content
KitploitKITPLOIT
FerramentasBlog
Log in
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
BYOVD-DriverKiller — Driver Reverse & Exploitation | Kitploit
Ferramentas/GitHubGitHub/alex3o/byovd-driverkiller
ExploraçãoEngenharia ReversaPós-ExploraçãoAnálise de BináriosAprendizado e EducaçãoRed Teaming
GitHubalex3o/byovd-driverkiller

BYOVD-DriverKiller

Driver Reverse & Exploitation

Ver Repositório
831411há 1 anoRevisado pelo Kitploit

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

BYOVD-DriverKiller

⚠️ Aviso: Este projeto é estritamente educacional e demonstrativo. Não se destina a uso em contexto malicioso. O objetivo é aprender a metodologia de engenharia reversa e as etapas de exploração de um driver do Windows.


Explico aqui a abordagem que segui para resolver o exercício proposto por d1rk(SaadAhla) https://github.com/SaadAhla, que consiste em realizar engenharia reversa e exploração em um driver legítimo, assinado e não presente nas blocklists (HVCI, LOLBIN...). Um programa em C que permite encerrar qualquer processo ativo no sistema por meio deste Kernel-mode Driver está disponível; detalho seu funcionamento um pouco mais abaixo.

POC-BYOD

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

Opção -d: Permite remover o serviço e o Driver do sistema após a exploração.

O modo testsigning deve estar ativado na máquina alvo, pois o certificado do Driver expirou.


Parte 1 - Engenharia reversa:

O exercício fornece um arquivo .sys, nomeado com seu hash SHA-256. O primeiro passo é abrir esse arquivo com o IDA.
O IDA está disponível gratuitamente. Basta acessar o site da Hex-Rays para gerar uma licença e baixar o software.

Começamos listando a IAT (Import Address Table) do Driver e procurando a chamada para a API que nos interessa: ZwTerminateProcess.

screen1-git

Ao clicar duas vezes em ZwTerminateProcess, o IDA nos redireciona para o código compilado dessa função. Selecionando a entrada e exibindo as cross-references, obtemos a lista das funções do Driver que a chamam.

screen2-git

Observamos que é a função sub_12EF4, no offset 1CE, que usa ZwTerminateProcess. Após um duplo clique, o IDA exibe seu código compilado.

screen11-git

O código decompilado revela as chamadas a ZwOpenProcess (que abre um handle para o processo alvo) e a ZwTerminateProcess (que encerra o processo por meio desse handle).

Consultando a documentação de ZwOpenProcess (https://learn.microsoft.com/fr-fr/windows-hardware/drivers/ddi/ntddk/nf-ntddk-zwopenprocess), verificamos que o parâmetro ClientID corresponde a um ponteiro que indica o PID do processo visado.

Na linha acima, ClientId.UniqueProcess é inicializado com a variável v22. Esta é definida logo acima:

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

Para entender essa atribuição, é preciso identificar a variável i e o campo +10.

screen3-git

Mais acima nessa função, observamos uma chamada a ZwQuerySystemInformation com o parâmetro SYSTEM_PROCESS_INFORMATION. Entendemos também que i é o iterador sobre as entradas dessa estrutura com a variável v6.

De acordo com a documentação de ZwQuerySystemInformation: (https://learn.microsoft.com/en-us/windows/win32/sysinfo/zwquerysysteminformation), essa função retorna uma matriz contendo uma entrada para cada processo ativo no sistema.

A estrutura SYSTEM_PROCESS_INFORMATION é descrita aqui: 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;

Lembrete: tamanhos de alguns tipos no Windows x64

  • ULONG = 4 octetos
  • USHORT = 2 octetos
  • HANDLE = 8 octetos
  • PWSTR = 8 octetos
  • KPRIORITY (typedef de um LONG) = 4 octetos
  • UNICODE_STRING = 16 octetos, pois esta é a sua estrutura:
  typedef struct _UNICODE_STRING {
    USHORT Length;        -> 2      
    USHORT MaximumLength; -> + 2 = 4
    PWSTR  Buffer;        -> + 8 = 12 (12 não é múltiplo de 8, portanto padding de 4 adicionado antes de Buffer) = 16
} UNICODE_STRING;

Cálculo do 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ão é múltiplo de 8, portanto padding de 4 adicionado) = 80
    HANDLE UniqueProcessId;       -> + 8 = 88

O membro UniqueProcessId está, portanto, no offset 0x50 (80 decimal).

Observando a atribuição da nossa variável v22, vemos que i é convertido (cast) em ponteiro QWORD (8 octetos)

v22 = (void )((_QWORD *)i + 10);
Portanto, v22 corresponde ao endereço de i + 10 * 8 = 80 octetos. Essa variável contém, de fato, o PID recuperado da estrutura SYSTEM_PROCESS_INFORMATION.

Para saber qual PID será passado para ZwTerminateProcess, é necessário analisar a condição que envolve essa atribuição.

screen4-git

Observamos que o nome da imagem do processo é primeiro recuperado:

v9 = (wchar_t )((_QWORD *)i + 8);
Pois v9 = endereço de i + 8 × 8 = 64 octetos. Isso corresponde ao Buffer do membro ImageName, já que esse membro está no offset 56 + 2 (USHORT) + 2 (USHORT) + 4 (padding) = 64

Diante das manipulações e dos loops abaixo, podemos formular a hipótese de que é feita uma comparação entre o nome do processo passado como argumento (a2) e os processos ativos no sistema v9/String.

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

É, portanto, o parâmetro a2 que deve conter o nome do processo a ser encerrado via ZwTerminateProcess. Notamos que a2 é um parâmetro da função sub_12EF4. Para ir mais longe, é preciso examinar as referências dessa função (renomeei-a para ZwTerminateProcessCaller para melhor legibilidade).

screen5-git

Observamos que ZwTerminateProcessCaller é chamada pela função sub_13624 no offset 61A.

screen6-git
Baixar ferramenta