Aller au contenu principal
Xsec

Internals EDR : architecture de télémétrie et limites de l'évasion

Publié le 61 min de lecture

Mis à jour le

Une analyse sourcée des chemins de télémétrie Endpoint Detection and Response (EDR, détection et réponse sur les endpoints, c’est-à-dire les postes et serveurs supervisés) sous Windows, de leurs contrats documentés, de leurs limites d’application et d’une méthodologie de validation en red team.

Ouverture : ce qu’un EDR voit réellement

Considérons une séquence courte : un interpréteur démarre ; au cours de son exécution, des threads et des images mappées apparaissent ; il ouvre un handle vers un autre processus, modifie une clé de registre, écrit un fichier puis établit une connexion réseau. Du point de vue d’un opérateur, il peut s’agir d’une seule action. Windows l’expose comme des transitions d’état séparées, observées à des couches et à des instants différents, avec des garanties différentes. Le système d’exploitation ne transmet pas une « attaque » à l’EDR. Celui-ci la reconstruit à partir d’événements incomplets.

Un processus est une instance en cours d’exécution d’un programme ; un thread est l’un des fils d’exécution à l’intérieur de ce processus. Une image est ici un fichier exécutable ou une bibliothèque mappée en mémoire, ce qui signifie que Windows associe ses pages à des adresses virtuelles du processus. Un objet Windows est une entité gérée par le système, par exemple un processus, un thread ou un fichier ; un handle est la référence accordée à un processus pour manipuler cet objet avec des droits précis.

Cette distinction constitue le fil conducteur de l’article. Un callback est une fonction qu’un composant enregistre auprès de Windows afin que le système l’appelle automatiquement lorsqu’une opération précise se produit. Un hook intercepte au contraire un chemin d’exécution existant pour observer ou modifier l’appel avant de rendre la main au code original. La question utile est donc ce que chaque mécanisme peut connaître à cet instant, ce qu’il peut modifier, ce qui peut disparaître avant l’analyse et quels signaux indépendants subsistent s’il est contourné.

Les solutions Endpoint Detection and Response combinent plusieurs mécanismes d’observation et d’application de politiques. Un schéma d’événement définit les champs, leurs types et leur signification ; la mise en mémoire tampon conserve temporairement les événements lorsque leur producteur et leur lecteur n’avancent pas au même rythme. Aucune architecture unique ne décrit tous les produits : l’emplacement des capteurs, ces schémas et buffers, les dépendances cloud et les capacités de réponse restent spécifiques à chaque implémentation. Les interfaces Windows sur lesquelles de nombreux produits s’appuient sont néanmoins suffisamment documentées pour établir une base technique précise.

La télémétrie désigne les données techniques produites pendant l’exécution, par exemple une création de processus, un accès à un fichier ou une connexion. Un capteur collecte une partie de ces données. L’agent EDR est l’ensemble des composants installés sur l’endpoint ; son service local et le backend, c’est-à-dire le moteur d’analyse placé derrière l’agent, normalisent et relient les événements pour produire une détection.

Avant de suivre cette séquence capteur par capteur, il faut situer les lieux où elle se produit, ceux où elle devient observable et celui où elle est finalement interprétée. Un produit associe généralement un service en mode utilisateur à un ou plusieurs drivers en mode noyau, puis transmet une partie de leurs événements à un moteur d’analyse local ou distant. Cette description est un modèle fréquent, pas une spécification commune à tous les fournisseurs.

Ce modèle traverse d’abord deux domaines d’exécution dont les privilèges déterminent ce qu’un capteur peut observer ou modifier.

Deux domaines d’exécution.

Le mode utilisateur héberge les applications ordinaires et la majorité des services. Chaque processus y dispose de son propre espace d’adressage, l’ensemble isolé des adresses de mémoire virtuelle qu’il peut utiliser, et ne peut pas accéder directement à la mémoire du noyau. Un capteur user-mode peut observer des fonctions qu’un processus appelle, analyser certaines zones de sa mémoire ou recevoir des événements publiés par Windows. Sa visibilité dépend néanmoins du processus instrumenté et du chemin d’exécution réellement emprunté.

Le mode noyau exécute le cœur de Windows et les drivers. Un driver est un module privilégié qui permet au noyau de gérer un périphérique ou d’étendre une fonction du système. Un driver EDR peut s’enregistrer auprès de mécanismes documentés pour observer la création des processus, les accès aux objets, les opérations de fichiers ou une partie du traitement réseau. Cette position offre une observation plus proche de l’état du système, mais elle n’accorde ni une connaissance automatique de l’intention, ni un droit universel de bloquer chaque opération.

Les deux domaines communiquent notamment par des appels système, qui sont les passages contrôlés permettant à un programme de demander un service au noyau. Les requêtes d’entrée/sortie (input/output, I/O) représentent les échanges avec les fichiers, périphériques ou flux réseau. Des objets partagés sous contrôle du noyau et des files d’événements complètent ces échanges. Les deux domaines ne constituent donc pas deux copies complètes de la même réalité : chaque couche expose un contexte et des garanties différents.

La séparation entre user mode et kernel mode situe les privilèges, mais elle ne décrit pas encore la fonction de chaque brique EDR. Il faut maintenant relier ces domaines aux composants qui collectent, transportent et interprètent les événements.

Rôle des composants.

Une interface de programmation d’applications (Application Programming Interface, API) est un point d’entrée accompagné d’un contrat qui permet à un programme de demander un service à un autre composant. Un chemin d’API est la succession de fonctions réellement traversées pour satisfaire cette demande.

Un callback noyau est une fonction enregistrée par un driver que Windows appelle pendant le traitement d’une opération dans le noyau, par exemple la création d’un processus. Un filtre est un composant qui observe, autorise, bloque ou modifie une catégorie d’opérations. Un minifilter est un filtre spécialisé dans les fichiers ; il s’enregistre auprès du Filter Manager, le composant Windows qui organise ces filtres et leur transmet les opérations demandées.

Une altitude est un identifiant numérique écrit sous forme de texte qui positionne certains filtres ou callbacks dans leur chaîne de traitement. Une pré-opération intervient avant que la requête atteigne le système de fichiers ; une post-opération observe son résultat pendant le retour. Pour un minifilter, Windows appelle les pré-opérations des altitudes les plus élevées vers les plus basses, puis les post-opérations dans l’ordre inverse.

Une décision synchrone est prise avant que l’opération surveillée ne se termine, ce qui permet parfois de la bloquer immédiatement. Une action asynchrone intervient plus tard, après mise en file ou corrélation, par exemple lorsqu’un EDR termine un processus déjà démarré.

ComposantCe qu’il représenteRôle techniqueLimite à garder en tête
Application observéeLe processus dans lequel s’exécute le comportement : navigateur, interpréteur de scripts, outil d’administration ou binaire natifProduit les appels, chargements d’images, accès mémoire, fichiers et connexions qui deviendront éventuellement des signauxUne application ne décrit pas elle-même l’intention de l’opérateur
Capteur user-modeUn module chargé ou injecté dans certains processus, ou un composant qui consomme leurs interfacesInstrumente des chemins d’API, collecte du contexte d’appel et peut inspecter du contenu ou de la mémoireUn appel qui ne traverse pas le chemin instrumenté peut échapper à ce capteur précis
Service EDR localLe processus durable qui coordonne l’agent sur l’endpointReçoit les événements, normalise les champs, enrichit les identités, applique une politique locale, maintient des files et dialogue avec le backendUne notification reçue par un driver n’est pas nécessairement conservée, enrichie ou transmise
Driver EDRUn module privilégié chargé dans le noyau WindowsEnregistre des callbacks, des filtres de fichiers ou des composants réseau et peut appliquer certaines décisions synchrones documentéesUn driver n’observe que les mécanismes auxquels il s’est enregistré et les opérations que ces mécanismes exposent
Event Tracing for Windows (ETW)L’infrastructure de télémétrie intégrée à WindowsLe noyau et les applications y publient des événements structurés qu’un EDR peut collecter pour reconstituer une chronologie sans modifier le code observéUne source enregistrée n’est pas forcément activée ; un canal de collecte actif ne garantit pas la conservation de chaque événement
Antimalware Scan Interface (AMSI)Une interface que certaines applications, notamment les moteurs de scripts, utilisent pour présenter du contenu à un antivirusPermet d’analyser un script ou un document après son déchiffrement ou sa reconstruction, au moment choisi par l’applicationAMSI dépend de l’intégration de l’application et n’observe pas universellement tous les processus
Minifilter de fichiersUn driver spécialisé dans la surveillance des opérations sur les fichiersS’enregistre auprès du Filter Manager, le composant Windows qui organise ces filtres, puis reçoit des callbacks avant ou après une ouverture, lecture, écriture, modification ou suppressionIl ne reçoit que les opérations déclarées et sa présence ne garantit pas qu’un événement sera conservé par l’EDR
Windows Filtering Platform (WFP)L’infrastructure de filtrage intégrée au traitement réseau WindowsÉvalue le trafic à plusieurs points de contrôle et permet à un EDR de l’autoriser, le bloquer ou l’enrichir avec l’identité de l’applicationLes informations disponibles et la capacité de blocage dépendent du point de contrôle utilisé
Backend d’analyseUn moteur local ou distant qui reçoit les événements normalisésCorrèle plusieurs capteurs, conserve l’état, enrichit la réputation et produit détections ou réponsesLe transport, la latence, la rétention et la politique peuvent modifier le résultat visible par l’analyste

ETW, AMSI, les callbacks noyau, les minifilters et WFP ne sont donc pas des synonymes d’« EDR ». Ce sont des sources ou des points de contrôle que le produit peut combiner. Le service local et le backend transforment ensuite ces observations en entités, relations, alertes et actions.

Schéma technique
ARCHITECTURE EDR MULTICOUCHE: Des capteurs indépendants deviennent utiles lorsqu'un service corrèle leurs observations partielles.ARCHITECTURE ENDPOINT · PIPELINE DE TÉLÉMÉTRIEARCHITECTURE EDR MULTICOUCHEDes capteurs indépendants deviennent utiles lorsqu'un service corrèle leurs observations partielles.ENDPOINT · MODE UTILISATEURENDPOINT · MODE NOYAUAGENT LOCALANALYSE / CONTRÔLEuserkernelfluxtamponreprisedécisionconfiguration / politiqueCOMPORTEMENTApplicationsProcessus · scriptsSIGNAUX USERHooks API · AMSIETWAppels · contenu · événementsWINDOWSGestionnairessystèmeProcessus · objets · I/OCAPTEURS KERNELCallbacksminifiltersCallouts WFPSERVICE EDRNormaliser+ enrichirIdentité · politique · santéFILE LOCALEBuffer + repriseRétention bornéeANALYSECorrélerle comportementDétecter · investiguerRÉPONSEPolitique + actionAlerter · isoler · contenirUN COMPORTEMENT · PLUSIEURS POINTS D'OBSERVATION · NORMALISATION LOCALE · TRANSPORT BUFFERISÉ · CORRÉLATION BACKEND
Architecture conceptuelle. Le placement des capteurs, le traitement local, le transport, l'analyse backend et la réponse restent propres au produit et à sa politique.Lecture du schémaLes deux lignes de l'endpoint se lisent de gauche à droite : les applications exposent des signaux user-mode, tandis que les gestionnaires Windows exposent des transitions kernel. Les deux alimentent le service EDR local. Celui-ci normalise et met les événements en tampon avant leur corrélation ; la politique revient par un chemin de contrôle distinct du flux de télémétrie.

La liste des composants décrit leurs responsabilités isolées. Leur articulation apparaît plus clairement lorsque la même activité traverse successivement plusieurs capteurs jusqu’au backend.

Parcours concret d’un événement.

Reprenons l’interpréteur de la séquence d’ouverture. Windows peut notifier la création de son processus et de ses threads au driver, signaler les images mappées, distribuer les opérations de handles et de registre aux callbacks inscrits, puis présenter les opérations de fichiers au minifilter. Si l’interpréteur intègre AMSI, le contenu du script peut aussi être soumis à cette interface. ETW peut publier d’autres éléments de contexte, tandis que la connexion atteint une ou plusieurs couches WFP.

Ces notifications n’arrivent pas nécessairement dans le même format, avec le même identifiant temporel ni dans l’ordre où le backend les affichera. Le service local doit les associer à une identité de processus, copier les champs encore valides, résoudre ce qui peut l’être sans bloquer le système, puis décider quoi conserver ou transmettre. Le backend peut enfin relier le contenu du script, l’image chargée, le fichier touché et la destination réseau à une même entité.

Le driver kernel constitue ainsi un capteur parmi plusieurs, et non le propriétaire universel de toute la télémétrie endpoint. La livraison d’un callback ne démontre pas non plus qu’un événement est conservé ou transmis. La pression sur les files, la configuration des sources, l’échantillonnage, les exclusions, la politique locale et l’ingestion backend, l’entrée des événements dans la plateforme d’analyse, peuvent chacun modifier l’ensemble de preuves final.

SummaryUn graphe, pas un flux

L’endpoint se représente plus justement comme un graphe ordonné dans le temps. Processus, threads, fichiers, clés de registre, handles, images, sockets, les extrémités logiques d’une communication réseau, et identités en sont les nœuds. Les opérations observées décrivent les relations entre ces nœuds : un processus crée un thread, ouvre un fichier, charge une image ou initie une connexion. Chaque capteur ne renseigne qu’une partie de ce graphe. L’absence d’un événement dans la télémétrie d’un capteur peut réduire la confiance accordée à une relation ou à une séquence, sans effacer les nœuds déjà établis, leur état antérieur ni les relations rapportées par des capteurs indépendants.

Acte I : naissance et mise en exécution

L’architecture prend maintenant vie avec le premier événement du fil rouge : l’apparition du processus. Les callbacks kernel exposent cette naissance, puis les threads et les images mappées montrent comment une identité devient un environnement capable d’exécuter du code. Aucun de ces signaux ne suffit encore à raconter ce que le processus fera.

Le graphe d’exécution commence par l’apparition d’un processus : cette transition fournit l’identité initiale à laquelle les événements suivants pourront être rattachés.

Création de processus.

PsSetCreateProcessNotifyRoutineEx est une fonction d’enregistrement du Process Manager, le gestionnaire de processus Windows, pas le chemin parcouru par chaque création. Son code est exporté par le module noyau ntoskrnl.exe. Le Windows Driver Kit (WDK), l’ensemble de documentation, d’en-têtes et d’outils destiné aux drivers Windows, fournit son prototype dans le header ntddk.h. Le driver référence ensuite ce point d’entrée au moyen de NtosKrnl.lib, une bibliothèque d’import utilisée par le linker ; lors du chargement du .sys, le loader kernel résout cet import vers l’adresse de l’export dans ntoskrnl.exe.

Le premier argument n’est pas le nom d’une fonction Windows : c’est l’adresse d’un callback PCREATE_PROCESS_NOTIFY_ROUTINE_EX implémenté dans l’image .sys du driver EDR. Avec Remove à FALSE, le Process Manager ajoute cette adresse à son état de notifications de processus ; avec Remove à TRUE, il retire l’inscription et attend la fin des invocations en cours. Le contrat documente une liste de routines inscrites, mais ni son symbole privé, ni sa représentation mémoire, ni son adresse ne sont stables entre les builds. Lors d’une création, le callback reçoit un pointeur PEPROCESS, qui désigne l’objet noyau représentant le processus, ainsi qu’un identifiant de processus et une structure PS_CREATE_NOTIFY_INFO contenant les informations documentées disponibles à cet instant.

Dans la suite, l’identifiant de processus correspond au Process Identifier (PID) et l’identifiant de thread au Thread Identifier (TID). Ces identifiants sont pratiques pour relier des événements, mais leur valeur peut être réutilisée après la fin de l’objet.

L’appel d’enregistrement et le chemin ultérieur de livraison de l’événement sont donc distincts. Le driver appelle une fois l’export de ntoskrnl.exe, puis Windows appelle directement l’adresse du callback située dans le driver à chaque notification correspondante. Les symboles privés représentant le conteneur interne peuvent être utiles au débogage d’un build donné, mais ne doivent pas être présentés comme des interfaces architecturales stables.

Schéma technique
CALLBACK DE CRÉATION DE PROCESSUS: L'enregistrement et la livraison d'événement sont deux chemins distincts.TÉLÉMÉTRIE KERNEL · PROCESS MANAGERCALLBACK DE CRÉATION DE PROCESSUSL'enregistrement et la livraison d'événement sont deux chemins distincts.MODE UTILISATEURMODE NOYAUenregistreroutine stockéeDÉCLENCHEURCréer un processusThread créateurINIT PILOTEPilote EDRImage kernel signéeENREGISTREMENTPsSetCreateProcessNotifyRoutineExCallback ajouté une foisÉVÉNEMENTProcess ManagerChemin de créationDISPATCHNotify routinesPASSIVE_LEVELCALLBACK EDRCorréler la télémétrieObserver ou définirCreationStatusDONNÉES LIVRÉES · PEPROCESS · PID PROCESSUS · PID PARENT · TID CRÉATEUR · IMAGE · LIGNE DE COMMANDE
Chemin documenté du process-notify. Contrairement à une notification passive, le callback Ex peut refuser la création via CreationStatus.Lecture du schémaLe chemin pointillé supérieur représente l'enregistrement unique du pilote ; le chemin inférieur, la livraison de chaque événement processus. PASSIVE_LEVEL décrit les contraintes d'exécution et le contexte du thread créateur indique où le callback s'exécute : aucun des deux ne prouve une intention sans corrélation.

Le callback Ex s’exécute à PASSIVE_LEVEL dans le contexte du thread qui crée le processus. L’Interrupt Request Level (IRQL) est le niveau de priorité d’exécution du noyau ; plus il est élevé, plus les opérations autorisées sont limitées. PASSIVE_LEVEL est son niveau ordinaire et le moins contraignant. Le callback n’est pas purement observationnel : l’affectation d’une erreur à CreationStatus peut empêcher la création. L’utilisation de cette capacité par un EDR dépend du produit et de sa politique. EPROCESS est la structure interne par laquelle le noyau représente un processus ; un pointeur PEPROCESS y fait référence, mais ne constitue pas une copie sérialisée de tous ses champs. Aucun champ ne doit être présenté comme collecté si le capteur ne le lit pas et ne l’enregistre pas explicitement.

Intérêt pour la détection. Les relations parent-enfant, l’identité du créateur, la provenance de l’image et la sémantique de la ligne de commande peuvent alimenter les analyses de chaînes de processus. Un token est l’objet Windows qui porte l’identité et les privilèges de sécurité d’un processus ou d’un thread ; un broker est un processus intermédiaire chargé d’effectuer une opération pour un autre. Aucun de ces éléments n’est concluant isolément. Le parentage modifié, les créations via broker, les images renommées et les lignes de commande incomplètes imposent une corrélation avec les tokens, handles, images et signaux d’exécution.

L’extrait WDK suivant reste volontairement limité au contrat documenté du callback. ProcessNotify, StartProcessProbe et StopProcessProbe sont des fonctions d’exemple compilées dans le .sys, pas des exports Windows. UNREFERENCED_PARAMETER est une macro de compilation fournie par les headers du WDK pour signaler qu’un paramètre est volontairement inutilisé ; aucun code de fonction n’est recherché dans un module au runtime. L’extrait distingue une notification de fin (CreateInfo == NULL) d’une notification de création et traite les chaînes optionnelles comme telles. DbgPrintEx, déclaré dans wdm.h, lié par NtosKrnl.lib et exécuté depuis ntoskrnl.exe dans ce contexte kernel, écrit un message vers le débogueur ; il convient à une sonde dans un lab contrôlé, pas au transport de télémétrie d’un produit.

process-notify.c
#include <ntddk.h>
static VOID ProcessNotify(
PEPROCESS Process,
HANDLE ProcessId,
PPS_CREATE_NOTIFY_INFO CreateInfo)
{
UNREFERENCED_PARAMETER(Process);
// A NULL CreateInfo denotes process exit.
if (CreateInfo == NULL) {
return;
}
// ImageFileName is documented as optional.
if (CreateInfo->ImageFileName != NULL) {
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
"create pid=%p image=%wZ\n", ProcessId, CreateInfo->ImageFileName);
}
}
NTSTATUS StartProcessProbe(VOID)
{
return PsSetCreateProcessNotifyRoutineEx(ProcessNotify, FALSE);
}
VOID StopProcessProbe(VOID)
{
(VOID)PsSetCreateProcessNotifyRoutineEx(ProcessNotify, TRUE);
}

La création du processus établit son identité et sa lignée, mais elle ne décrit pas les chemins d’exécution qui apparaissent ensuite en son sein. Les notifications de threads ajoutent ces unités d’exécution au graphe sans déterminer, à elles seules, ce qu’elles exécutent.

Création et fin de thread.

PsSetCreateThreadNotifyRoutine est exporté par ntoskrnl.exe, déclaré dans ntddk.h et référencé par un driver via NtosKrnl.lib. Il reçoit l’adresse d’un callback situé dans le .sys appelant ; le gestionnaire de threads conserve cette inscription dans un état kernel dont le layout n’est pas documenté. Le callback classique ne reçoit que ProcessId, ThreadId et un booléen Create. ETHREAD est la structure interne qui représente un thread dans le noyau, mais la signature classique du callback ne fournit aucun argument de ce type. Lors de la création, la routine s’exécute dans le contexte du thread ayant créé le nouveau thread, à un IRQL inférieur ou égal à APC_LEVEL. Une Asynchronous Procedure Call (APC) est une fonction placée en attente pour être exécutée dans le contexte d’un thread ; APC_LEVEL impose donc davantage de restrictions que PASSIVE_LEVEL.

Schéma technique
SIGNAL DE CYCLE DE VIE D'UN THREAD: Un notify callback est une primitive de corrélation, pas un verdict d'injection.TÉLÉMÉTRIE KERNEL · THREAD MANAGERSIGNAL DE CYCLE DE VIE D'UN THREADUn notify callback est une primitive de corrélation, pas un verdict d'injection.MODE UTILISATEURMODE NOYAUroutine enregistréecontexteDÉCLENCHEURCréer un threadLocal ou distantENREGISTREMENTPsSetCreateThreadNotifyRoutine[Ex]Initialisation piloteÉVÉNEMENTThread ManagerCréation · finDONNÉES CALLBACKProcessId · ThreadIdCreate = TRUE/FALSECONTEXTE D'EXÉCUTIONThread créateurÀ la créationCORRÉLATION EDRHypothèse d'injectionAutres signaux requisUNE DÉTECTION FIABLE EXIGE UNE CORRÉLATION · CONTEXTE CRÉATEUR · HANDLES · MÉMOIRE · CALL STACK
Le callback classique reçoit ProcessId, ThreadId et Create. Il ne reçoit ni structure ETHREAD ni champ processus source.Lecture du schémaLe callback identifie le processus cible, le nouveau thread et l'état création ou fin. Le contexte créateur ajoute une provenance, mais l'injection distante reste une hypothèse tant que des preuves de handle, mémoire, adresse de départ ou call stack ne la corroborent pas.

L’événement identifie le processus cible et le nouveau thread, mais n’établit pas à lui seul une injection de thread distant, qui consiste à provoquer l’exécution d’un thread dans un autre processus pour lui faire exécuter du code choisi. Une conclusion robuste exige d’autres preuves : contexte créateur, accès préalable à un handle de processus, opérations de mémoire virtuelle, provenance de l’adresse de départ ou call stack, la liste des fonctions imbriquées ayant conduit à l’opération.

PsSetCreateThreadNotifyRoutineEx est la variante étendue montrée entre crochets dans le schéma. Elle réside elle aussi dans ntoskrnl.exe, avec la déclaration ntddk.h et l’import NtosKrnl.lib, mais son paramètre de type de notification modifie le contrat et, pour certains types, le contexte d’exécution du callback. La suppression utilise PsRemoveCreateThreadNotifyRoutine, exporté par le même module, déclaré dans le même header et importé par la même bibliothèque. L’API exacte et le type de notification doivent donc être relevés pendant les tests plutôt que déduits du seul nom « thread callback ».

L’existence d’un thread ne révèle pas encore quelles images exécutables sont introduites dans son processus. Les notifications de mapping ajoutent cette provenance de code, tout en restant distinctes de son exécution effective.

Mapping d’images exécutables.

PsSetLoadImageNotifyRoutine est exporté par ntoskrnl.exe, déclaré dans ntddk.h et importé par le driver via NtosKrnl.lib. Il reçoit l’adresse d’une routine PLOAD_IMAGE_NOTIFY_ROUTINE située dans le .sys du driver ; le Process Manager conserve l’inscription dans son état kernel, sans exposer le conteneur interne comme contrat. Le driver doit la retirer avant son déchargement au moyen de PsRemoveLoadImageNotifyRoutine, fourni par le même module, le même header et la même bibliothèque d’import.

Le mapping crée une section image puis en projette une vue dans l’espace d’adressage virtuel du processus. Une page mémoire est une unité de taille fixe que le gestionnaire de mémoire associe à une adresse virtuelle. L’expression « pages d’un fichier » désigne ici des pages virtuelles dont le contenu est adossé aux octets de l’EXE ou de la DLL ; elle ne signifie pas que le fichier est naturellement découpé en pages mémoire ni qu’il est copié intégralement en RAM. Windows peut charger le contenu d’une page à la demande lors de son premier accès. Le mapping prépare donc l’accès au code et aux données sans signifier que ce code a déjà été exécuté.

Windows invoque la routine après ce mapping et avant le point d’entrée, la première adresse à laquelle le chargeur doit transférer l’exécution. Le callback reçoit un nom complet optionnel, l’identifiant du processus cible et des données IMAGE_INFO telles que l’adresse de base et la taille. Dans le schéma, LoadLibrary désigne la famille LoadLibraryA et LoadLibraryW : ces API user-mode sont déclarées dans libloaderapi.h, liées par Kernel32.lib et exportées par Kernel32.dll. Elles demandent le chargement d’un module, mais n’appellent pas directement le callback EDR ; le loader crée le mapping, puis Windows distribue séparément la notification kernel.

Schéma technique
NOTIFICATION DE CHARGEMENT D'IMAGE: La notification arrive après le mapping et avant l'exécution du point d'entrée.TÉLÉMÉTRIE KERNEL · MAPPING D'IMAGENOTIFICATION DE CHARGEMENT D'IMAGELa notification arrive après le mapping et avant l'exécution du point d'entrée.MODE UTILISATEURMODE NOYAUREQUÊTELoadLibraryEXE · DLLLOADERMapper l'imageSection imageÉTATImage mappéeEntrypoint non exécutéNOTIFYCallbacks dechargementPASSIVE_LEVELEDREnrichir+ scorerHash · signer · pathLIMITE SÉMANTIQUECallback VOIDaucun retour Allow/DenyLe blocage exige un autre contrôleDONNÉES CALLBACK · NOM COMPLET (OPTIONNEL) · PROCESS ID · BASE · TAILLE · FLAGS
PLOAD_IMAGE_NOTIFY_ROUTINE retourne void. Il observe le mapping mais ne peut pas renvoyer directement Allow ou Deny.Lecture du schémaLa frontière essentielle est temporelle : le mapping existe déjà quand la notification est livrée, mais son point d'entrée n'a pas encore été exécuté. Le callback rapporte cette transition ; l'enrichissement de réputation et une éventuelle terminaison relèvent d'autres contrôles.

La routine retourne VOID, donc aucune valeur, et ne peut pas renvoyer directement une décision d’autorisation ou de blocage. Une signature numérique relie un fichier à un éditeur et permet de vérifier qu’il n’a pas été modifié depuis sa signature. Un hash est une empreinte calculée à partir du contenu ; la prévalence indique à quelle fréquence ce fichier a été observé dans le parc. Ces informations, comme les catalogues de signatures et la réputation, ne sont pas des champs intrinsèques du callback. Un capteur peut les ajouter par d’autres mécanismes.

La première partie du parcours a établi l’identité du processus, ses unités d’exécution et les images introduites dans son espace d’adressage. Elle ne dit pas encore ce que ce processus demande aux autres objets Windows ni quelles traces il laisse sur le système.

Acte II : interactions avec le système

Le fil rouge quitte donc l’intérieur du processus. Les callbacks du registre décrivent les changements de configuration qui peuvent lui survivre, les callbacks d’objets révèlent les droits demandés sur d’autres processus ou threads, puis les minifilters suivent les effets produits sur le stockage.

Opérations sur le registre.

Le registre est la base de configuration hiérarchique de Windows. Son sous-système noyau, le Configuration Manager, permet à un driver de filtrage de s’enregistrer via CmRegisterCallbackEx. Cette fonction est exportée par ntoskrnl.exe, déclarée dans wdm.h et importée par le driver au moyen de NtosKrnl.lib. Elle reçoit notamment l’adresse du callback présent dans le .sys, l’altitude du filtre, l’objet représentant le driver et un contexte optionnel. Le Configuration Manager conserve l’inscription et retourne un cookie opaque, un identifiant que le driver doit fournir à CmUnRegisterCallback pour se désinscrire. Cette fonction de retrait appartient elle aussi à ntoskrnl.exe et au contrat wdm.h/NtosKrnl.lib. Le conteneur kernel associé au cookie reste un détail d’implémentation.

Les opérations sont identifiées par REG_NOTIFY_CLASS. Dans le schéma, Reg* et Zw* sont des noms de familles, pas deux fonctions uniques. RegSetValueExW constitue un exemple user-mode : winreg.h le déclare, Advapi32.lib fournit son import et Advapi32.dll exporte son point d’entrée. ZwSetValueKey est l’exemple kernel correspondant pour un driver : il est déclaré dans wdm.h, importé via NtosKrnl.lib et exporté par ntoskrnl.exe. Ces appelants atteignent le Configuration Manager ; ils n’appellent pas eux-mêmes le callback EDR, que le gestionnaire distribue selon les inscriptions actives.

Schéma technique
CHEMIN DE FILTRAGE DU REGISTRE: Les appels user-mode et kernel-mode convergent vers le Configuration Manager.TÉLÉMÉTRIE KERNEL · CONFIGURATION MANAGERCHEMIN DE FILTRAGE DU REGISTRELes appels user-mode et kernel-mode convergent vers le Configuration Manager.APPELANTSCHEMIN REGISTRE KERNELstatutUSER MODEAPI Reg*Create · set · queryKERNEL MODERoutines Zw*Appelant piloteDISPATCHConfigurationManagerOpération typéePRE-NOTIFYRegistryCallbackInspecter · bloquerModifierPOST-NOTIFYRegistryCallbackObserver le résultatOPÉRATIONRuche registreExécuter si autoriséEDRCorrélerAlerterDONNÉES SPÉCIFIQUES · KEY OBJECT · VALUE NAME · DATA · CONTEXTE PROCESS/THREAD · STATUT
CmRegisterCallbackEx enregistre une routine recevant des notifications pre/post typées par REG_NOTIFY_CLASS.Lecture du schémaLes deux chemins d'appel convergent vers le même manager. Une pre-notification peut influencer une opération supportée avant son exécution ; la post-notification rapporte le statut obtenu. Les champs et modifications possibles dépendent du REG_NOTIFY_CLASS exact, pas d'un schéma registre universel.

La plupart des classes d’opérations exposent des notifications pre et post accompagnées de structures propres à l’opération. Un pre-callback peut inspecter, bloquer ou, dans les cas supportés, modifier une opération ; un post-callback observe le statut et la sortie obtenus. Les champs disponibles dépendent de la classe de notification. Affirmer que chaque événement contient un chemin complet, un PID, un TID, les données de la valeur et les permissions est donc excessif. La résolution des noms et l’attribution de l’appelant peuvent demander un traitement supplémentaire au capteur.

Intérêt pour la détection. Les clés de persistance, configurations de fournisseurs de sécurité, définitions de services et modifications de politiques prennent leur sens lorsqu’elles sont corrélées à l’identité initiatrice, à la lignée de processus, à la confiance de l’image et à la séquence temporelle. L’accès au registre n’identifie pas à lui seul l’intention.

Le registre expose les changements de configuration, mais pas les droits qu’un processus demande sur un autre processus ou thread. Les callbacks d’objets ajoutent cette relation interprocessus et peuvent, pour les droits supportés, réduire l’accès avant la création du handle.

Opérations sur les objets processus et thread.

Dans ce mécanisme, chaque handle porte un masque de droits sur l’objet Windows visé. ObRegisterCallbacks est exporté par ntoskrnl.exe, déclaré dans wdm.h et importé via NtosKrnl.lib. Le driver lui transmet une structure d’enregistrement contenant l’altitude, les types d’objets et les adresses de ses routines pre-operation et post-operation situées dans son propre .sys. ObjectPreCallback et ObjectPostCallback désignent ces rôles de callback et leurs signatures, pas deux fonctions implémentées par ntoskrnl.exe pour le compte du driver. L’Object Manager conserve l’ensemble inscrit et retourne un handle d’inscription opaque. ObUnRegisterCallbacks, fourni par le même module et le même contrat de compilation, consomme ce handle pour retirer les callbacks avant le déchargement du driver. La structure kernel située derrière ce handle n’est pas publique.

OB_OPERATION_HANDLE_CREATE et OB_OPERATION_HANDLE_DUPLICATE distinguent la création et la duplication de handles. Les fonctions OpenProcess et DuplicateHandle montrées dans le schéma sont des API user-mode exportées par Kernel32.dll et importées avec Kernel32.lib ; leurs déclarations se trouvent respectivement dans processthreadsapi.h et handleapi.h. Elles produisent des requêtes sur des objets Windows. Elles n’appellent pas directement le callback EDR : l’Object Manager reçoit l’opération kernel et distribue les pre/post-callbacks déjà enregistrés.

Schéma technique
CONTRÔLE DES HANDLES PROCESS / THREAD: Les object callbacks interviennent sur la création et la duplication de handles.TÉLÉMÉTRIE KERNEL · OBJECT MANAGERCONTRÔLE DES HANDLES PROCESS / THREADLes object callbacks interviennent sur la création et la duplication de handles.MODE UTILISATEURMODE NOYAUrestreindreSOURCEOpenProcess /DuplicateHandleAccès demandéSYSTEM CALLObject ManagerOpération handlePRE-OPCallback EDRInspecter la requêteACTION SUPPORTÉEDesiredAccess &= allowed_maskRetirer des droits, jamais en ajouterRÉSULTATHandleDroits réduitsPOST-OPTélémétrieStatut finalPRE-OP · TYPE D'OBJET · CREATE/DUPLICATE · ACCÈS ORIGINAL · DESIRED ACCESS MODIFIABLE · KERNEL HANDLE
ObjectPreCallback doit retourner OB_PREOP_SUCCESS. La protection retire des droits supportés de DesiredAccess ; elle ne renvoie pas Deny.Lecture du schémaDesiredAccess représente l'ensemble des capacités demandées pour le futur handle. Le pre-callback peut retirer des bits supportés avant son émission : l'ouverture peut réussir tout en produisant un handle incapable d'écrire en mémoire, de créer un thread ou d'exécuter une autre action protégée.
ImportantContrat d'un ObjectPreCallback

Un ObjectPreCallback doit retourner OB_PREOP_SUCCESS ; il ne renvoie pas un statut arbitraire de refus. DesiredAccess est un masque de bits dans lequel chaque bit représente un droit demandé, par exemple écrire dans la mémoire ou créer un thread. Pour les droits modifiables, le callback peut retirer des bits de ce masque, mais ne peut pas ajouter de droits absents de la requête initiale. Un post-callback peut observer le statut final et l’accès accordé, sans modifier rétroactivement l’opération terminée.

Ce mécanisme permet de protéger des processus sensibles en limitant des droits liés à l’accès mémoire, à la création de threads, à la modification de contexte ou à la duplication de handles. Les handles kernel et les opérations issues de composants de confiance ou protégés doivent être interprétés avec prudence. Un handle bloqué ou réduit ne démontre pas une intention de vol d’identifiants sans le comportement environnant.

Le cœur de politique ci-dessous montre le modèle d’application supporté. ProcessHandlePreOperation est le callback compilé dans le .sys du driver, tandis que IsProtectedTarget est un prédicat d’exemple appartenant à ce même driver, pas une fonction Windows. PsProcessType n’est pas une fonction : c’est un symbole de donnée déclaré par le WDK et exporté par ntoskrnl.exe, dont la valeur identifie le type d’objet processus auprès de l’Object Manager. L’enregistrement, le choix de l’altitude, la durée de vie de la cible et la synchronisation de la politique sont omis, mais l’opération sur le masque utilise les structures pre-operation documentées. Le prédicat de cible doit rester étroit ; appliquer ce masque à tous les processus casserait des logiciels légitimes.

object-pre-callback.c
static OB_PREOP_CALLBACK_STATUS ProcessHandlePreOperation(
PVOID RegistrationContext,
POB_PRE_OPERATION_INFORMATION Info)
{
UNREFERENCED_PARAMETER(RegistrationContext);
if (Info->ObjectType != *PsProcessType ||
!IsProtectedTarget((PEPROCESS)Info->Object)) {
return OB_PREOP_SUCCESS;
}
ACCESS_MASK *desired = NULL;
if (Info->Operation == OB_OPERATION_HANDLE_CREATE) {
desired = &Info->Parameters->CreateHandleInformation.DesiredAccess;
} else if (Info->Operation == OB_OPERATION_HANDLE_DUPLICATE) {
desired = &Info->Parameters->DuplicateHandleInformation.DesiredAccess;
}
if (desired != NULL) {
*desired &= ~(PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE);
}
return OB_PREOP_SUCCESS;
}

Les callbacks d’objets décrivent les droits qu’un processus demande sur un autre objet, mais ils ne couvrent pas les effets produits sur le stockage. Pour observer la création, l’écriture, le renommage ou la suppression de fichiers, puis corréler ces opérations avec leur processus initiateur dans le pipeline EDR, le capteur utilise une autre couche du noyau : les minifilters du système de fichiers.

Effets sur le système de fichiers.

Un minifilter est un driver spécialisé dans la surveillance des opérations sur les fichiers. Il s’enregistre auprès du Filter Manager (FltMgr.sys), le composant Windows qui organise ces filtres, puis reçoit des callbacks avant ou après les opérations qu’il a déclarées. Un volume est la cible logique portant un système de fichiers, par exemple une partition montée ; l’attachement d’un minifilter à un volume constitue une instance.

Cette inscription passe par FltRegisterFilter, une fonction exportée par FltMgr.sys, déclarée dans fltkernel.h et importée au moyen de FltMgr.lib. Le minifilter l’appelle depuis son DriverEntry, la fonction d’entrée compilée dans son propre .sys que Windows invoque au chargement du driver. Il lui fournit une structure FLT_REGISTRATION contenant notamment les adresses de ses callbacks, elles aussi situées dans ce .sys. FltMgr ajoute le filtre à sa liste globale et retourne un pointeur PFLT_FILTER opaque. FltStartFiltering, fourni par le même module, le même header et la même bibliothèque d’import, autorise ensuite l’attachement aux volumes et la livraison des I/O. FltUnregisterFilter, de même provenance, retire l’inscription au déchargement. Les structures internes situées derrière le pointeur opaque ne font pas partie du contrat.

Dans le modèle de drivers Windows, une grande partie de ces opérations est transportée par des paquets de requête d’entrée/sortie (I/O Request Packet, IRP), des structures qui décrivent au noyau l’action demandée et son état. L’altitude attribuée positionne l’instance dans la pile ; le nom d’un produit ne définit aucun ordre fixe entre EDR et antivirus.

Schéma technique
PILE I/O DES MINIFILTERS: L'altitude, et non le nom du produit, détermine l'ordre des callbacks.TÉLÉMÉTRIE KERNEL · FILTER MANAGERPILE I/O DES MINIFILTERSL'altitude, et non le nom du produit, détermine l'ordre des callbacks.UN VOLUME · INSTANCES MINIFILTER ORDONNÉESPOST-OP · LOW → HIGHPRE-OP · HIGH → LOWORIGINERequête I/OCreate · read · writeROUTEURFltMgr.sysOpérations inscritesALTITUDE HAUTEMinifilter APre premierPost dernierALTITUDE BASSEMinifilter BPre après · post avantSTOCKAGEFile systemTerminer l'opérationCOMPLÉTIONStatut · octetsMétadonnéesChemin retourPRE-OP PEUT LAISSER PASSER · DEMANDER UN POST-OP · PENDRE · SYNCHRONISER · OU TERMINER L'I/O
FltMgr appelle les pre-operation de la plus haute à la plus basse altitude ; le retour post-operation suit l'ordre inverse.Lecture du schémaLe chemin supérieur suit la requête : les instances de haute altitude s'exécutent avant les plus basses. Le chemin inférieur suit la complétion : les post-operation remontent dans l'ordre inverse. Seules les opérations enregistrées par chaque minifilter participent à ce trajet.

Pour une opération donnée, les pre-operation callbacks s’exécutent de l’altitude la plus haute vers le système de fichiers. La complétion remonte ensuite dans les post-operation callbacks de l’altitude la plus basse vers la plus haute. Un pre-callback peut laisser passer la requête, demander un post-callback, la mettre en attente, la synchroniser ou la terminer avec un statut final. La présence et le contexte d’exécution du post-callback dépendent du résultat pre-operation et du type d’opération.

Intérêt pour la détection. Les opérations de création, écriture, renommage, disposition, c’est-à-dire la demande de suppression d’un fichier, synchronisation de section et modification de métadonnées peuvent alimenter les détections de ransomware, de staging, la préparation ou le regroupement de données avant leur transfert, d’altération et de collecte. L’entropie mesure ici la dispersion des valeurs d’octets et peut signaler un contenu fortement transformé ou chiffré, sans constituer une preuve à elle seule. Le chemin, le contenu, l’entropie, le contexte du volume et l’attribution au processus sont des enrichissements produit, non un schéma universel d’événement minifilter.

Un minifilter déclare les opérations qu’il souhaite recevoir. Cette table statique ne crée aucun rapport d’ordre fixe avec le filtre d’un autre fournisseur ; FltMgr l’associe à l’altitude de l’instance au runtime. Dans l’extrait, PreCreate, PostCreate, PreWrite, PostWrite, PreSetInformation et PostSetInformation sont des fonctions fournies par le minifilter et compilées dans son .sys, pas des exports de FltMgr.sys.

minifilter-operations.c
CONST FLT_OPERATION_REGISTRATION Operations[] = {
{ IRP_MJ_CREATE, 0, PreCreate, PostCreate },
{ IRP_MJ_WRITE, 0, PreWrite, PostWrite },
{ IRP_MJ_SET_INFORMATION, 0, PreSetInformation, PostSetInformation },
{ IRP_MJ_OPERATION_END }
};

Les interactions avec le registre, les objets et les fichiers montrent ce que le processus touche localement. Elles restent toutefois réparties entre plusieurs contrats et ne décrivent ni toute la chronologie d’exécution ni, à elles seules, la connexion réseau qui clôt la séquence.

Acte III : transport et reconstruction de la télémétrie

ETW ajoute des événements structurés à la chronologie, WFP expose les points de contrôle réseau et l’instrumentation user-mode conserve le contexte des API effectivement traversées. Ces sources complètent le graphe, puis le service EDR doit encore déterminer quelles observations concernent la même entité et dans quel ordre elles se sont produites.

Event Tracing for Windows (ETW) est l’infrastructure de télémétrie intégrée à Windows : le noyau et les applications y publient des événements structurés décrivant leur activité. Un EDR peut les collecter et les corréler pour reconstituer une chronologie, par exemple la création d’un processus ou le chargement d’une Dynamic Link Library (DLL), une bibliothèque de code réutilisable chargée par un processus, sans devoir modifier directement le code observé.

Un provider est le composant qui produit les événements. Un contrôleur démarre une session, c’est-à-dire le canal temporaire et ses buffers mémoire, puis choisit les providers à activer. Un consumer lit les événements de cette session en temps réel ou depuis un fichier Event Trace Log (ETL). Un service EDR peut être à la fois contrôleur et consumer, mais le contrôleur ne relaie pas chaque événement entre le provider et le consumer.

Pour un provider user-mode classique, EventRegister inscrit l’identité du provider et retourne un handle d’inscription, puis EventWrite publie un événement avec ce handle. Ces deux fonctions publiques sont déclarées dans evntprov.h, importées via Advapi32.lib et exportées par Advapi32.dll. Les frameworks ETW générés peuvent les encapsuler, mais les fonctions du provider restent dans son EXE ou sa DLL ; ETW reçoit les données par ces points d’entrée au lieu de copier le code du provider dans le système.

Schéma technique
FLUX D'UNE SESSION ETW: Le contrôleur configure ; les providers écrivent ; les consumers lisent.TÉLÉMÉTRIE SYSTÈME · EVENT TRACING FOR WINDOWSFLUX D'UNE SESSION ETWLe contrôleur configure ; les providers écrivent ; les consumers lisent.PLAN DE CONTRÔLEPLAN DE DONNÉES ÉVÉNEMENTconfigureactivegèreécritlitCONTRÔLEURService EDRDémarrer sessionActiver providersTRACE SESSIONPolicy + buffer poolLevel · keywords · modePROVIDERSApplicationsComposants systèmeUser mode · kernelBUFFERS SESSIONFlux ordonnéTemps réel et/ou ETLCONSUMERService EDRDécoder · filtrerANALYTIQUECorrélerDétection · alerteEVENT RECORD · IDENTIFIANTS PROVIDER / ÉVÉNEMENT / ACTIVITÉ · LEVEL · KEYWORD · TIMESTAMP · PID/TID · PAYLOAD
Le contrôleur appartient au plan de contrôle, pas au flux d'événements. Les providers écrivent dans les buffers lus en temps réel ou via ETL.Lecture du schémaLe plan supérieur configure les providers, classes d'événements, buffers et modes de livraison de la session. Le plan inférieur transporte les données : les providers écrivent les enregistrements dans les buffers et les consumers les lisent. Un même service peut tenir les deux rôles sans faire du contrôleur un relais d'événements.

Les enregistrements peuvent contenir un identifiant de provider, un identifiant et une version d’événement, un niveau représentant sa gravité, un keyword utilisé comme catégorie d’activation, un horodatage, des PID et TID, des identifiants d’activité et un payload, c’est-à-dire les données propres à l’événement. Son schéma provient d’un manifeste, des métadonnées auto-descriptives TraceLogging, du Managed Object Format (MOF) ou d’un fichier Trace Message Format (TMF), qui indiquent au consumer comment décoder les champs. ETW ne garantit ni l’activation d’un provider, ni la conservation de chaque événement, ni sa pertinence en matière de sécurité.

Les événements PowerShell, .NET, kernel et Microsoft-Windows-Threat-Intelligence appartiennent à des familles de providers distinctes, avec des conditions d’activation et d’accès différentes. .NET est la plateforme Microsoft qui exécute du code managé, c’est-à-dire du code dont l’exécution et la mémoire sont supervisées par un environnement dédié. AMSI est une interface séparée d’inspection de contenu. Ses signaux peuvent être corrélés à ETW, mais AMSI ne constitue pas une catégorie de provider ETW.

ETW apporte du contexte d’exécution et une chronologie, mais son rôle de collecte ne fournit pas l’arbitrage propre aux différentes couches réseau. WFP expose ces points de contrôle et peut rattacher une décision au flux, à l’application ou à l’utilisateur selon la couche utilisée.

Télémétrie réseau avec WFP.

Windows Filtering Platform (WFP) est l’infrastructure de filtrage intégrée à la pile réseau Windows. La pile réseau est la succession de composants qui transforme les données d’une application en paquets, puis effectue le trajet inverse à la réception. WFP expose des couches, c’est-à-dire des points de contrôle correspondant à des étapes précises de ce traitement.

À chaque couche, un filtre WFP est une règle composée de conditions et d’une action. Le moteur de filtrage évalue ces règles ; il peut autoriser ou bloquer directement le trafic, ou appeler un callout, une fonction fournie par un driver pour effectuer un traitement spécialisé. Le Base Filtering Engine (BFE, moteur de filtrage de base) gère la configuration de la plateforme. Les couches Application Layer Enforcement (ALE, application des politiques au niveau applicatif) ajoutent notamment l’identité de l’application ou de l’utilisateur à la décision.

Schéma technique
CHEMIN DE CLASSIFICATION WFP: Un filtre correspondant invoque éventuellement un callout à une couche WFP.TÉLÉMÉTRIE RÉSEAU · WINDOWS FILTERING PLATFORMCHEMIN DE CLASSIFICATION WFPUn filtre correspondant invoque éventuellement un callout à une couche WFP.POLITIQUE / GESTIONCLASSIFICATION KERNELgèrefiltresmatchclasseSERVICE EDRPolicy providerAjouter filtres + contexteBFEPolitique filtresConfiguration persistanteTRAFICSocket / flowPacket · streamALESHIMPile TCP/IPExtraireles conditionsCOUCHEFilter engineClasser + arbitrerOPTIONNELCallout EDRInspecter · annoterACTIONPermettreou bloquerDécisionCHEMINRéseauContinuerLES MÉTADONNÉES ALE LIENT LE FLUX AU PROCESSUS · APPLICATION · UTILISATEUR · ADRESSES · PORTS · PROTOCOLE
Un shim amène le trafic à une couche WFP ; le filter engine évalue les conditions et peut invoquer le callout EDR avant d'appliquer l'action.Lecture du schémaLa couche est un point d'observation, le filtre exprime une politique à ce point et le callout est du code spécialisé optionnel sélectionné par un filtre correspondant. Les métadonnées et le pouvoir du callout sont donc bornés par la couche d'invocation et les règles d'arbitrage WFP.

Le BFE coordonne la politique et la configuration persistante en mode utilisateur ; la classification, c’est-à-dire la comparaison du trafic avec les filtres applicables, s’exécute en mode noyau aux couches réseau et transport. Un flux regroupe les échanges appartenant à une même communication. Aux couches ALE, ses métadonnées peuvent inclure l’application, l’utilisateur, le protocole, ainsi que les adresses et ports locaux ou distants. Leur disponibilité dépend de la couche et du type d’événement.

Un callout EDR ne se place donc pas avant WFP. Il est invoqué parce qu’un filtre correspondant à une couche sélectionne ce callout. Sa fonction de classification est un callback compilé dans le .sys du driver EDR, pas une fonction générique dont le code résiderait dans WFP. Le moteur kernel WFP appelle cette adresse enregistrée avec les métadonnées de la couche ; le callback peut les inspecter et contribuer à l’action selon l’arbitrage WFP, les règles de priorité qui déterminent l’action finale lorsque plusieurs filtres correspondent. Le payload réseau est la donnée transportée par les paquets ; le réassemblage de flux reconstruit une suite continue à partir de ces paquets. Leur inspection et la sémantique finale de blocage dépendent de la couche, de l’implémentation du callout et des droits associés à l’action.

WFP décrit la communication et son identité applicative, mais pas nécessairement le chemin d’API qui a conduit le processus à demander cette communication. L’instrumentation user-mode complète cette vue en observant les appels qui traversent effectivement les fonctions instrumentées.

Instrumentation des API en mode utilisateur.

Certains EDR injectent ou chargent un capteur user-mode et détournent des exports, les fonctions qu’un module rend appelables par d’autres composants, dans des bibliothèques telles que ntdll.dll. Un inline hook modifie les premières instructions d’une fonction, appelées son prologue, afin de brancher l’exécution vers le capteur. Un trampoline est un petit bloc de code qui rejoue les instructions déplacées puis ramène l’exécution vers la suite de la fonction originale.

Schéma technique
CHEMIN D'UN APPEL NTDLL HOOKÉ: Un detour ajoute un point d'observation avant le chemin syscall original.TÉLÉMÉTRIE USER-MODE · INLINE DETOURCHEMIN D'UN APPEL NTDLL HOOKÉUn detour ajoute un point d'observation avant le chemin syscall original.MODE UTILISATEURMODE NOYAUévénementAPPELANTApplicationNtCreateThreadExEXPORTEntrée ntdllPrologue patchéDETOURHook EDRInspecter + émettreTRAMPOLINEOctetsoriginauxReprendre l'exécutionTRANSITIONStub syscallIdentifiant propreau buildSORTIE CAPTEURFile locale → service EDRSpécifique au produitSERVICE SYSTÈMEExécution kernelSIGNAUX USER-MODE POSSIBLES · PARAMÈTRES · VALEUR RETOUR · CALL STACK · MODULE APPELANT · INTÉGRITÉ MÉMOIRE
C'est un pattern courant, pas un contrat universel des EDR. Un syscall direct saute seulement ce detour user-mode ; les autres capteurs restent actifs.Lecture du schémaLe detour observe les appels qui traversent l'export patché ; le trampoline rejoue ensuite les instructions déplacées et rejoint le stub normal. Contourner cet export retire ce point d'observation, pas l'opération kernel ni les changements d'état indépendamment observables qu'elle peut produire.

Le schéma emploie NtCreateThreadEx comme exemple concret. Sur les builds Windows modernes observés, ce point d’entrée de la Native API est exporté par ntdll.dll ; son stub user-mode prépare la transition vers le service kernel correspondant. Il ne constitue pas ici une API Win32 documentée comme stable, et son stub comme son identifiant d’appel système doivent être vérifiés sur le build examiné. À un niveau public plus élevé, CreateRemoteThreadEx est déclaré dans processthreadsapi.h, importé via Kernel32.lib et exporté par Kernel32.dll. Ces deux niveaux ne doivent pas être confondus : l’API Win32 fournit un contrat applicatif, tandis que l’export ntdll.dll représente le passage natif montré pour expliquer le detour.

WarningPortée d'un appel système direct

Ce pattern n’est pas universel. Les produits peuvent employer d’autres stratégies d’injection, frameworks d’instrumentation, sources d’événements et règles limitant l’instrumentation de processus sensibles. Un appel système direct demande un service au noyau sans traverser l’export user-mode instrumenté. Il peut donc contourner ce detour précis, mais pas l’appel système lui-même, les callbacks kernel, providers ETW, couches WFP, minifilters, contrôles de handles, scanners mémoire ou corrélations interprocessus indépendants.

Les identifiants et stubs d’appels système dépendent de l’architecture et du build. Un stub est la courte séquence de code machine qui prépare l’identifiant de l’opération puis effectue la transition vers le noyau. La fixation d’un identifiant unique comme technique Windows générale est incorrecte. Microsoft Visual C++ ne supporte pas non plus l’assembleur inline pour l’architecture x86 64 bits (x64) sous la forme souvent présentée dans les exemples simplifiés. Un outil de recherche doit résoudre et valider le build cible au lieu de supposer un stub fixe.

Du signal local à l’événement corrélable

Accumuler des événements provenant de plusieurs capteurs ne suffit pas à reconstruire un comportement. Le pipeline doit d’abord déterminer quelles observations décrivent la même entité, dans quel ordre elles se sont produites et quelles absences relèvent du contrat de l’API ou d’une perte de collecte.

La première difficulté consiste à conserver une identité stable malgré la réutilisation des identifiants Windows et la durée de vie limitée des objets kernel.

Les identifiants Windows constituent des clés de corrélation pratiques, mais pas des identités permanentes. Les PID et TID peuvent être réutilisés après la fin d’un objet. Une époque désigne ici une période d’exécution unique délimitée par le redémarrage de Windows ou du capteur. Un modèle robuste associe donc l’identifiant à l’heure de création et à cette époque, puis idéalement à un identifiant d’entité généré par le produit. La corrélation tardive d’un événement réseau ou fichier avec le propriétaire actuel d’un PID réutilisé peut autrement créer une fausse lignée.

Les callbacks kernel exposent également les objets à des étapes différentes de leur cycle de vie. Un callback de création de processus s’exécute avant que le thread initial commence son exécution, tandis qu’un callback d’image arrive après la création du mapping mais avant l’exécution de son point d’entrée. Un post-callback objet observe une opération de handle terminée. Ces événements décrivent des transitions d’état différentes et ne doivent pas être réduits à un horodatage générique tel que « démarrage du processus ».

Les pointeurs d’objets ne sont valides que selon le contrat et les règles de durée de vie du callback qui les reçoit. Conserver un pointeur brut pour un traitement asynchrone, donc exécuté plus tard, sans prendre une référence appropriée est dangereux. Les capteurs de production copient généralement les champs stables dans un enregistrement borné et délèguent l’enrichissement coûteux à un worker, une tâche ou un thread de fond, ou au service user-mode.

Une identité d’entité stable indique quels événements peuvent être rapprochés, mais elle n’établit pas leur ordre causal. Il faut également préserver le temps de génération et le domaine d’horloge de chaque source.

Temps d’événement et ordre causal.

L’ordre d’invocation des callbacks n’est pas l’ordre d’arrivée au backend. Un buffer est une zone mémoire temporaire qui absorbe un écart de vitesse entre le producteur et le lecteur ; le batching regroupe plusieurs événements pour les transporter ensemble. Les événements peuvent traverser des buffers par unité centrale de traitement (Central Processing Unit, CPU), files kernel-vers-user, stockage local, compression, batching, transport et ingestion cloud avant de devenir interrogeables. Deux enregistrements produits dans un ordre kernel connu peuvent être observés dans l’ordre inverse après des chemins de buffering indépendants.

Plusieurs horloges peuvent coexister : temps système, temps d’interruption, compteur de performance haute résolution, timestamps ETW, temps de réception par le service local et temps d’ingestion backend. Un domaine d’horloge est la source de temps et l’origine utilisées pour produire un timestamp. Un modèle défendable préserve ce domaine original et documente l’incertitude de conversion. Le temps backend ne doit pas remplacer le temps de génération pour reconstruire des séquences subsecondes de processus, threads, handles ou flux réseau.

Même une chronologie correctement ordonnée reste trompeuse si l’absence d’un champ optionnel est interprétée comme une preuve d’altération. La sémantique de chaque callback doit donc précéder toute conclusion sur les données manquantes.

Champs optionnels, résolution des noms et races.

Le caractère optionnel documenté a des conséquences analytiques. ImageFileName et CommandLine dans PS_CREATE_NOTIFY_INFO peuvent être absents. FullImageName dans un callback de chargement peut être nul. Les callbacks registre reçoivent des structures propres à chaque opération plutôt qu’un chemin universel entièrement résolu. Les métadonnées réseau diffèrent selon les couches WFP. L’absence d’une donnée ne démontre donc pas une altération.

Les noms constituent aussi des vues mutables des objets. Une race apparaît lorsque l’objet change entre l’événement et sa résolution ultérieure. Un reparse point peut rediriger la résolution d’un chemin, tandis qu’un hard link donne plusieurs noms au même fichier. Un fichier peut donc être renommé ou résolu différemment après sa création ; les capteurs qui enrichissent de manière asynchrone doivent distinguer les valeurs capturées au moment de l’événement de celles résolues plus tard.

L’optionalité explique pourquoi un événement valide peut manquer d’un champ ; elle n’explique pas la disparition d’enregistrements entiers. À l’échelle du système, le débit, les files et les buffers introduisent une seconde source d’incertitude.

Débit, backpressure et perte d’événements.

La backpressure apparaît lorsqu’un producteur génère des événements plus vite que le composant suivant ne peut les traiter. Chaque chemin de collecte possède donc un budget de performance : les callbacks kernel s’exécutent dans des contextes contraints, les minifilters se trouvent sur des chemins I/O sensibles à la latence, les callouts WFP peuvent affecter le débit réseau et les sessions ETW utilisent des buffers finis. Un capteur peut échantillonner, agréger, supprimer des répétitions ou abandonner des événements de faible priorité. En mode fail-open, il laisse l’opération continuer lors d’une panne ou d’une saturation afin de préserver la disponibilité, au prix d’une visibilité ou d’un contrôle réduit.

Warning« Aucun événement trouvé »

Les contrôleurs ETW peuvent exposer des statistiques de buffers et d’événements perdus. Les files produit peuvent publier des compteurs de santé, des logs de diagnostic ou des heartbeats, de petits messages périodiques confirmant que le capteur fonctionne encore. Un résultat négatif reste ininterprétable sans les indicateurs de santé et de perte. « Aucun événement trouvé » peut signifier une absence d’instrumentation, un filtrage, une perte, un retard d’ingestion, l’expiration de la rétention, une erreur de requête ou un contournement effectif.

Ces contraintes déterminent ce que le produit peut connaître avec confiance, mais pas encore ce qu’il peut empêcher. Il faut séparer la capacité d’observation de chaque source, son éventuelle influence synchrone et les réponses ultérieures du produit.

Acte IV : de l’observation à la décision

Le fil rouge est maintenant corrélé : le produit peut relier le processus, ses images, ses accès aux objets, ses fichiers et sa connexion. Cette connaissance ne dit pas encore à quel moment une action peut être empêchée. L’interface native détermine ce qu’un callback peut effectuer de façon synchrone ; les capacités de réponse du produit peuvent ajouter des actions ultérieures, mais elles ne doivent pas être attribuées à l’API de collecte elle-même.

MécanismeObservation nativeInfluence synchrone nativeFrontière importante
Process notify ExCréation, fin et métadonnées documentéesCreationStatus peut faire échouer la créationLe produit peut employer le callback uniquement pour la télémétrie
Thread notifyPID cible, TID et état création/suppressionAucun retour allow/deny documentéL’injection exige une corrélation au-delà du callback
Image-load notifyMétadonnées du mapping d’imageAucun retour allow/deny ; callback VOIDLe mapping existe déjà lors de la notification
Registry callbackDonnées pre/post typéesLe pre-callback peut bloquer ou modifier les opérations supportéesLes champs et modifications dépendent de REG_NOTIFY_CLASS
Object callbackCréation ou duplication d’un handle process/threadLes bits d’accès supportés peuvent être retirésLe pre-callback retourne success ; ce n’est pas un hook de refus générique
Minifilter pre-opOpérations de système de fichiers enregistréesLaisser passer, pendre, modifier, synchroniser ou terminerL’ordre dépend de l’altitude et des opérations enregistrées
Consumer ETWÉvénements émis vers une session activéeAucune via le rôle de consumerActivation du provider et rétention sont des décisions séparées
Callout WFPMétadonnées de flux, stream, packet ou ALE propres à la coucheLa classification peut contribuer à permit/blockDroits, arbitrage et données disponibles varient selon la couche
Detour user-modeArguments, contexte appelant, retour et données produitLe hook peut altérer ou refuser l’appel user-modeSeuls les appels traversant le chemin instrumenté sont observés

La réponse asynchrone doit être modélisée séparément. Un capteur peut observer un mapping d’image puis terminer le processus quelques millisecondes plus tard, ou enregistrer un flux réseau et isoler l’hôte après corrélation backend. Ce comportement est préventif au niveau produit, sans constituer une prévention synchrone par la source initiale de l’événement.

Une API peut offrir une capacité d’action et rester inutilisable si le composant n’est pas suffisamment approuvé, protégé ou maintenu actif. La confiance plateforme et l’autoprotection déterminent donc si cette capacité reste disponible face à une tentative d’altération.

Confiance plateforme, exécution protégée et autoprotection.

Les API d’enregistrement kernel imposent leurs propres conditions de confiance. Les callbacks processus Ex peuvent par exemple échouer si l’image contenant le callback ne possède pas la caractéristique d’intégrité exigée ; l’enregistrement d’object callbacks peut être refusé si les routines ne résident pas dans une image kernel signée. Ces contrôles augmentent le coût d’un enregistrement arbitraire sans garantir la sécurité de chaque driver signé.

Protected Process Light (PPL) est un niveau de protection Windows qui réserve certaines opérations sur un processus aux composants possédant un niveau de signature suffisant. Il peut limiter l’inspection user-mode ou l’acquisition de handles par un composant EDR dont le niveau de confiance est insuffisant. Certains produits de sécurité emploient des services protégés et des processus antimalware-light ; leur configuration exacte reste propre au fournisseur et au déploiement.

Un hyperviseur est la couche logicielle qui crée et isole des environnements d’exécution sous le système principal. Virtualization-Based Security (VBS, sécurité fondée sur la virtualisation) utilise cette isolation pour protéger certaines décisions et données. Code Integrity est le mécanisme Windows qui vérifie qu’un binaire respecte les exigences de signature et de politique avant son exécution. Hypervisor-Protected Code Integrity (HVCI, intégrité du code protégée par l’hyperviseur) s’appuie sur l’environnement isolé pour renforcer cette vérification du code autorisé à s’exécuter dans le noyau. La blocklist Microsoft refuse des drivers connus comme vulnérables, tandis qu’App Control applique une politique définissant les exécutables et drivers autorisés. Ces mécanismes réduisent l’exposition aux drivers connus ou non fiables, mais ne transforment pas les données kernel non documentées en frontière de sécurité supportée.

Kernel Patch Protection, souvent appelé PatchGuard, vérifie périodiquement l’intégrité de certains codes et structures critiques du noyau sur les systèmes 64 bits supportés. Il ne doit pas être décrit comme un détecteur complet, immédiat et publiquement spécifié de toute manipulation de callbacks ou d’ETW. Sa couverture et sa temporisation sont des détails d’implémentation. Un crash après une modification non supportée démontre une instabilité plateforme, pas un résultat fiable de détection EDR.

L’autoprotection du produit ajoute une couche. Une liste de contrôle d’accès (Access Control List, ACL) indique quelles identités peuvent agir sur un service ou un device de driver. Un watchdog surveille qu’un composant reste actif et tente de le relancer ou de signaler sa défaillance. Services protégés, contrôles de santé des callbacks, intégrité des modules, signature de configuration et état backend peuvent aussi contribuer. L’anti-tamper, qui vise à empêcher ou détecter la modification du produit, reste distinct de la détection comportementale.

Acte V : ce qui disparaît et ce qui reste visible après le contournement d’un capteur

Supposons maintenant que l’un des capteurs rencontrés dans le parcours soit contourné. Pour savoir ce qui change réellement, il faut suivre le signal au-delà de sa collecte et vérifier si les étapes suivantes disposent encore d’autres observations capables de décrire l’activité.

Un pipeline EDR est la succession d’étapes qui transforme des observations brutes de l’endpoint en données exploitables, puis éventuellement en détection ou en action. La collecte n’en constitue que la première étape ; la qualité de détection dépend aussi de la normalisation, de l’enrichissement, de l’état et de la corrélation.

ÉtapeFonction typiquePrincipaux modes de défaillance
CollecteRecevoir callbacks, événements, I/O, flux et signaux user-modeCapteur désactivé, callback absent, provider non activé, perte d’événements
NormalisationConvertir les enregistrements produit dans un schéma interne stableChamps manquants, identité ambiguë, dérive d’horloge ou de schéma
EnrichissementRésoudre signature, hash, réputation, token, chemin, lignée et contexte assetDépendance réseau, cache périmé, races de chemin, objets inaccessibles
CorrélationRelier les événements entre processus, threads, sessions et hôtesRéutilisation d’identifiants, fenêtres incomplètes, échantillonnage, délai backend
DécisionScorer, alerter, bloquer, isoler ou collecter des preuvesDifférences de politique, seuil de modèle, suppression, latence de réponse

La présence d’un callback ne doit pas être assimilée à une visibilité complète. Inversement, le contournement d’un callback ou d’un detour user-mode ne doit pas être assimilé à une perte de détection. L’unité d’analyse pertinente est le graphe complet du comportement et l’ensemble des capteurs indépendants capables de le décrire.

Le pipeline montre que collecte, normalisation, corrélation et décision peuvent échouer indépendamment. Évaluer une évasion consiste donc à localiser précisément l’étape altérée, puis à mesurer les preuves et actions qui subsistent dans les autres couches.

Lire une évasion comme une perte de visibilité.

Une technique d’évasion doit être décrite par le chemin de visibilité précis qu’elle affecte. Les expressions « désactiver l’EDR » ou « devenir invisible » sont trop larges sans identification du produit, de sa version, de sa politique, de l’état des capteurs et du résultat mesuré.

ImportantRègle d'interprétation

Un bypass, c’est-à-dire le contournement effectif d’un contrôle précis, reste limité au chemin de collecte ou d’application testé. La perte d’un signal n’établit pas la perte du graphe comportemental, de la prévention ou de la détection backend.

Schéma technique
ÉVASION ≠ INVISIBILITÉ: Chaque bypass vise un chemin de collecte ; des contrôles indépendants voient encore le comportement.MODÈLE RED TEAM · COUVERTURE CAPTEURSÉVASION ≠ INVISIBILITÉChaque bypass vise un chemin de collecte ; des contrôles indépendants voient encore le comportement.VISIBILITÉ CIBLÉEANGLE MORT TENTÉVISIBILITÉ / CONTRÔLE RÉSIDUELHOOKS USER-MODEDetours ntdllBYPASSUnhook / syscall directENCORE VISIBLECallbacks · ETW · WFP · mémoireCHEMIN ETW LOCALÉcriture providerBYPASSPatcher un seul processusENCORE VISIBLEKernel / autres providers · intégritéCALLBACK KERNELNotify routineBYPASSPrimitive d'écriture kernelRISQUE / CONTRÔLEIntégrité · crash · télémétrieCONFIANCE KERNELPolitique pilotes signésATTAQUEPilote signé vulnérableMITIGATIONHVCI · blocklist · App ControlRÈGLE DE VALIDATION · NOMMER LE CAPTEUR · TESTER LE BUILD CIBLE · MESURER LES SIGNAUX RÉSIDUELS · INTÉGRITÉ
Le modèle utile relie la technique au capteur précisément affecté, puis inventorie la visibilité résiduelle et les mitigations plateforme.Lecture du schémaChaque ligne se lit de gauche à droite : identifier le capteur visé, formuler précisément l'angle mort tenté, puis mesurer les signaux et contrôles restants. La colonne centrale ne démontre jamais à elle seule une invisibilité globale de l'endpoint.

Le premier cas concerne les capteurs les plus proches du code user-mode : contourner leur detour modifie un chemin d’observation, sans supprimer les transitions que Windows expose ailleurs.

Unhooking user-mode et appels système directs.

Ces techniques peuvent supprimer ou contourner certains detours user-mode. Les preuves résiduelles peuvent inclure les callbacks de processus et de threads, opérations de handles, mappings d’images, ETW, WFP, les minifilters, l’état de la mémoire distante et les corrélations backend. La restauration des hooks peut elle-même produire des anomalies d’intégrité ou de protection mémoire.

Une fois le detour contourné, ETW demeure un chemin distinct pour les providers activés. Son altération doit être qualifiée par processus, fonction de publication, session et famille de providers affectés.

Altération d’ETW.

Microsoft décrit EtwEventWrite dans ntetw.h comme une fonction interne au système, susceptible de changer entre les versions. Sur les builds modernes observés, son point d’entrée user-mode est exporté par ntdll.dll ; cette localisation décrit l’implémentation examinée, pas un contrat applicatif stable. Un provider compatible doit préférer l’API publique EventWrite, déjà située plus haut dans Advapi32.dll. Lorsque son chemin de publication atteint ntdll.dll!EtwEventWrite, modifier cette copie dans un processus n’affecte que les appels qui la traversent dans ce processus. Cela ne désactive ni les providers kernel, ni les autres processus, ni les autres points d’entrée ETW, ni les capteurs non ETW. La modification de pages exécutables peut également constituer un signal d’intégrité observable. L’état ETW kernel est spécifique à l’implémentation et ne doit pas être représenté par un offset ou un layout stable.

L’altération d’un chemin ETW user-mode ne modifie pas les callbacks enregistrés séparément dans le noyau. Agir sur ces callbacks déplace donc le problème vers une primitive privilégiée et des structures propres au build.

Manipulation des callbacks kernel.

La suppression d’un callback par des structures internes non documentées exige une primitive d’écriture kernel, c’est-à-dire une capacité permettant de modifier une adresse choisie dans la mémoire du noyau, ainsi qu’une découverte propre au build. Un code de contrôle d’entrée/sortie (Input/Output Control, IOCTL) est une commande structurée envoyée par un processus à un driver ; il n’existe pas d’IOCTL Windows générique « remove callback ». Un driver vulnérable expose son propre protocole de device et ses propres primitives. L’exploitation doit donc implémenter sa sémantique de lecture/écriture et résoudre l’état kernel cible sur le build exact. Les mécanismes d’intégrité, l’autoprotection du produit, le réenregistrement des callbacks, les crashs et les autres capteurs modifient le résultat.

Une primitive kernel peut notamment provenir de l’abus d’un driver légitimement signé mais vulnérable. La question passe alors du contournement d’un capteur à la chaîne de confiance qui autorise le chargement et l’accès au device de ce driver.

Drivers signés vulnérables.

Une signature valide établit une relation de confiance pour le chargement ; elle ne garantit pas l’absence de vulnérabilité exploitable. Les contrôles modernes comprennent HVCI, la blocklist Microsoft des drivers vulnérables, les politiques App Control et une règle Attack Surface Reduction (ASR, réduction de la surface d’attaque) contre l’abus de drivers signés vulnérables. Leur couverture n’est pas absolue et l’état de la blocklist doit être vérifié sur l’endpoint testé.

Épilogue : mesurer la couverture dans un lab

Le parcours a montré qu’une activité unique devient plusieurs observations, puis que la disparition d’une source n’a de sens qu’en comparaison avec les preuves restantes. Le lab sert à mesurer ces différences sans confondre le contrat de Windows, l’implémentation d’un build et le comportement d’un produit.

Établir la portée de chaque preuve

Trois classes de preuves doivent rester séparées :

  1. Les contrats documentés regroupent les interfaces de programmation d’applications (Application Programming Interface, API) Windows publiques, que les programmes appellent, et les interfaces de drivers de périphériques (Device Driver Interface, DDI), qui définissent comment un driver dialogue avec le noyau, la partie centrale et privilégiée du système. Les signatures de callbacks et les règles d’ordonnancement font aussi partie de ces contrats. Ils constituent le fondement le plus solide des affirmations architecturales.
  2. Les détails d’implémentation observés comprennent les symboles privés, tableaux internes, offsets de structures et routines produit obtenues par reverse engineering, c’est-à-dire par l’étude du binaire et de son exécution lorsque le code source n’est pas disponible. Un symbole de débogage associe une adresse machine à un nom de fonction, de variable ou de type ; privé signifie ici qu’il ne constitue pas une interface publique supportée. Un offset est la distance d’un champ par rapport au début d’une structure ; son layout est l’organisation complète des champs en mémoire. Un build est une version compilée précise du logiciel. Ces détails ne sont valides que pour les builds Windows et produit examinés.
  3. Le comportement produit correspond à la télémétrie réellement conservée, enrichie, transmise et évaluée par une version et une politique EDR précises. Il doit être mesuré et ne peut pas être déduit de la seule disponibilité d’un callback.

La sixième édition de Windows Internals couvre Windows 7 et Windows Server 2008 R2. Elle reste utile pour les concepts architecturaux, mais ne constitue pas un contrat d’implémentation actuel. Le Windows Driver Kit (WDK) regroupe la documentation, les en-têtes et les outils nécessaires au développement de drivers Windows ; sa documentation courante et la septième édition de Windows Internals prévalent pour les interfaces supportées. WinDbg est le débogueur Microsoft utilisé pour observer l’état et l’exécution de Windows. Les symboles privés, offsets et observations tirés du débogage restent propres au build examiné.

WarningFrontière de preuve

Une structure non documentée observée dans WinDbg établit seulement le comportement du build examiné. Une DDI supportée établit un contrat ; l’implémentation d’un fournisseur exige encore une mesure directe.

Le débogage kernel et le reverse engineering deviennent nécessaires lorsque la question de recherche porte sur l’implémentation réelle plutôt que sur le contrat documenté. Ils exigent un format de preuve plus strict.

ETW Threat Intelligence (ETW-TI) est un provider Windows orienté sécurité qui publie certaines opérations sensibles ; il reste une source ETW parmi d’autres, soumise à ses propres conditions d’accès et d’activation. Le désassemblage traduit le code machine d’un binaire en instructions processeur ; le pseudocode en propose une représentation de plus haut niveau, plus lisible mais reconstruite par l’outil. Les routines de notification, enregistrements de callbacks, nœuds de minifilters et entrées encodées peuvent ainsi être étudiés à l’aide de symboles privés et du désassemblage. Une primitive issue d’un driver vulnérable peut parfois permettre de modifier la mémoire kernel. Ces techniques établissent seulement que le build examiné peut être inspecté ou altéré ; elles ne rendent pas portables les noms de symboles internes, tailles de tableaux, encodages de pointeurs, layouts de listes ou offsets de champs.

Chaque affirmation portant sur un internal devrait enregistrer :

  • les versions, hashes, timestamps et adresses de chargement exacts du kernel et des drivers ;
  • la source du symbol server, le dépôt qui fournit les informations de débogage, l’identité du fichier Program Database (PDB) associé au binaire et l’état de chargement de ces symboles, qui relient des adresses machine à des noms, fonctions et types ;
  • l’emplacement de désassemblage ou de pseudocode établissant l’utilisation de l’objet interne ;
  • le layout de structure dérivé des symboles ou du code, et non copié depuis un autre build ;
  • les hypothèses de synchronisation et de durée de vie requises avant toute lecture ou modification ;
  • les états avant et après, y compris réenregistrement du callback, santé du capteur, stabilité système et télémétrie résiduelle.

La propriété d’une routine par un driver de sécurité doit être établie en résolvant son adresse vers le module chargé, puis en examinant l’appel d’enregistrement ou l’implémentation de la routine. Des labels tels que WdFilter.sys, MsSecFlt.sys ou un driver tiers sont des observations sur un système, pas des membres obligatoires d’une liste de callbacks Windows. L’ordre de chargement, la version produit, les fonctions activées et les composants de sécurité plateforme en modifient la population.

Les expériences de suppression sont particulièrement faciles à surinterpréter. Effacer une entrée, détacher un nœud, désactiver un flag d’activation ou restaurer des octets peut produire un état temporaire tout en laissant intacts des pointeurs en cache, des références de rundown, qui empêchent la destruction d’un objet encore utilisé, des files de workers, watchdogs, callbacks alternatifs et preuves backend. Une écriture réussie n’équivaut pas à un contournement propre ou durable du capteur.

WarningPortée d'une observation interne

Un symbole privé, un layout ou un offset doit être redécouvert et prouvé sur le build cible. Une valeur observée sur une autre version ne constitue pas un fait actuel.

Ces exigences de preuve fournissent également une grille de lecture des erreurs récurrentes. Lorsqu’une analyse saute une étape entre enregistrement, livraison, enrichissement et décision, elle transforme une observation locale en conclusion générale injustifiée.

Erreurs d’analyse fréquentes.

  1. Confondre enregistrement et flux d’événements. Une API de setup appelée lors de l’initialisation du driver n’est pas traversée par chaque événement.
  2. Présenter des symboles privés comme architecture publique. Les noms et offsets internes exigent un build et une source de preuve explicites.
  3. Inventer des champs d’événement universels. Arguments du callback, enrichissement produit et schéma backend sont des couches séparées.
  4. Assimiler notification et prévention. Les callbacks VOID ne retournent pas de décision ; la réponse asynchrone du produit doit être décrite séparément.
  5. Assimiler un bypass à la désactivation de l’EDR. Le hook, provider, callback, callout ou la file affectés doivent être nommés précisément.
  6. Prendre une alerte pour seule vérité terrain. La télémétrie brute, la prévention, la santé du capteur et l’ingestion backend doivent aussi être examinées.
  7. Omettre les contrôles négatifs. Un test exige une baseline, c’est-à-dire une capture de référence comparable, et une opération bénigne de même forme API.
  8. Ignorer la récupération. Watchdogs, réenregistrement, redémarrage, actualisation de politique et mises à jour peuvent annuler une modification transitoire.
  9. Supposer un numéro d’appel système fixe. L’architecture et le build Windows déterminent le stub ; les exemples codés en dur, donc fixés directement dans le code, vieillissent immédiatement.
  10. Assimiler driver signé et comportement fiable. Signature, état de vulnérabilité, couverture de blocklist et politique runtime sont des propriétés distinctes.

Comparer les preuves résiduelles

Ces erreurs d’analyse se corrigent par un protocole qui sépare le comportement généré, le chemin de collecte testé et le résultat observé. Un plan de test exhaustif doit faire varier ces dimensions indépendamment ; le tableau décrit les preuves minimales et non une alerte attendue propre à un produit.

Famille de testSource primaire testéeSources résiduelles indépendantesPreuves de résultat requises
Création de processusCallback Process ExMapping image, ETW, lignée de handles, audit de serviceStatut de création, événement brut, lignée d’entité, action de politique
Comportement de thread distantThread callback ou hook user-modeObject callbacks, mémoire, ETW-TI, état de la cibleIdentités source/cible, droits, adresse de départ, résultat du thread
Introduction d’imageCallback image-loadMinifilter, Code Integrity, scan mémoire, ETWÉtat du mapping, signature/hash enrichis, résultat d’exécution
Persistance registreRegistry callbackLignée processus, télémétrie services/tâches, activation ultérieureStatut pre/post, clé et valeur résolues, identité initiatrice
Accès à un processus sensibleObject callbackÉvénements d’audit, lectures mémoire, threads, état de protectionAccès original/réduit, handle accordé, opération suivante
Transformation de fichiersMinifilterProcess/thread, analyse de contenu ou entropie, volumeRésultat pre/post, identité chemin/octets, débit et périmètre
Script ou code managéChemin ETW/AMSIProcessus, images, fichiers, réseau, mémoireActivation provider, contenu disponible, comportement aval
Connexion réseauWFP/ALEDomain Name System (DNS), lignée processus, proxy, Transport Layer Security (TLS), logs distantsCouche, direction, endpoints, identité application, action
Contournement d’un hook userExport instrumentéCallbacks kernel, ETW, WFP, minifilter, mémoireIntégrité du hook, chemin réel, graphe d’événements résiduel
Chargement ou abus d’un driverCode Integrity et politique driversCallback image, registre service, minifilter, télémétrie kernelDécision de chargement, politique, hash/version, accès device

La conclusion la plus solide est rarement binaire. La couverture peut être complète, partielle, retardée, échantillonnée, enrichie seulement dans le backend ou suffisante pour détecter malgré l’absence d’un événement primaire. Chaque dimension doit être enregistrée séparément.

La matrice indique quelles sources comparer et quelles preuves conserver. Elle ne rend cependant pas l’expérience reproductible sans un protocole qui fige l’environnement, contrôle les variables et distingue les couches du résultat.

Protocole de recherche reproductible.

Une expérimentation défendable sur les internals EDR doit enregistrer assez d’état pour permettre la reproduction ou la contestation de la conclusion.

  1. Figer l’environnement. Relever l’édition, le build et le niveau de servicing Windows, l’état de la virtualisation et de VBS, les versions du produit et du capteur EDR, l’identifiant de politique et la connectivité réseau.
  2. Définir le comportement. Décrire l’opération de processus, thread, image, registre, objet, fichier, ETW ou réseau générée. Un nom d’alerte ne constitue pas une définition du comportement.
  3. Inventorier les capteurs attendus. Relever les callbacks documentés, drivers chargés, instances et altitudes des minifilters, sessions ETW et providers activés, filtres et callouts WFP et modules user-mode.
  4. Capturer une référence. Exécuter un contrôle bénin de même forme API et collecter les enregistrements locaux et backend avant toute condition d’évasion.
  5. Modifier une seule variable. Désactiver, contourner ou altérer un seul chemin de collecte en conservant le comportement et la politique.
  6. Mesurer la visibilité résiduelle. Comparer les événements bruts, enregistrements normalisés, alertes, actions de prévention, temps d’arrivée backend et effets de bord endpoint.
  7. Répéter après redémarrage et mise à jour. Les callbacks, sessions de providers, blocklists et protections peuvent être restaurés ou modifiés par le servicing et les contrôles de santé du capteur.

Les résultats doivent distinguer au minimum quatre situations : l’événement ciblé disparaît ; l’événement reste mais perd des champs ; la télémétrie reste tandis que l’alerte disparaît ; ou la prévention change tandis que la télémétrie demeure. Ces résultats appartiennent à des couches différentes du pipeline et ne doivent pas être regroupés sous une affirmation unique de « bypass réussi ».

L’inventaire en lecture seule suivant fournit un point de départ reproductible sur un hôte Windows du lab. Il n’identifie pas chaque capteur EDR, mais capture trois surfaces fréquemment mal comprises avant le début du test.

Collect-EdrSurface.ps1
$EvidenceRoot = Join-Path $env:TEMP ("edr-surface-" + (Get-Date -Format "yyyyMMdd-HHmmss"))
New-Item -ItemType Directory -Path $EvidenceRoot | Out-Null
fltmc filters | Out-File (Join-Path $EvidenceRoot "minifilters.txt")
logman query -ets | Out-File (Join-Path $EvidenceRoot "etw-sessions.txt")
logman query providers | Out-File (Join-Path $EvidenceRoot "etw-providers.txt")
$WfpState = Join-Path $EvidenceRoot "wfp-state.xml"
netsh wfp show state "file=$WfpState"
Get-CimInstance Win32_SystemDriver |
Select-Object Name, State, StartMode, PathName |
Export-Csv (Join-Path $EvidenceRoot "drivers.csv") -NoTypeInformation

Cet instantané doit être associé aux versions du système d’exploitation (operating system, OS) et du capteur, à l’état de la politique, aux timestamps et aux preuves backend. fltmc confirme les instances et altitudes des minifilters, logman distingue les providers enregistrés des sessions actives et netsh wfp exporte l’état WFP et Internet Protocol Security (IPsec) courant.

Revenir au fil rouge

Reprenons la séquence d’ouverture. La création du processus établit une identité et une lignée. Les notifications d’image décrivent des mappings exécutables, sans décider si ces mappings sont malveillants. Un callback objet peut observer ou réduire l’accès demandé à un autre processus, tandis qu’une notification de thread ultérieure indique seulement qu’un thread est apparu. Les télémétries fichier et WFP ajoutent des effets extérieurs au processus. ETW peut apporter du contexte d’exécution. La valeur analytique naît de leur relation temporelle, pas d’un événement isolé.

Une affirmation sur la visibilité EDR peut donc être examinée avec quatre questions :

  1. Quelle transition d’état Windows s’est produite ? Un comportement doit être décrit indépendamment d’une alerte ou d’un nom d’outil.
  2. Quel mécanisme documenté peut l’observer ou l’influencer ? Enregistrement, livraison, normalisation, détection et réponse sont des étapes distinctes.
  3. Quels champs et quelles garanties d’ordre existent réellement ? Les données optionnelles, la réutilisation des PID, le transport asynchrone et les pertes d’événements limitent chaque conclusion.
  4. Quelles preuves indépendantes subsistent si ce mécanisme disparaît ? Les signaux résiduels de processus, handles, images, fichiers, ETW ou réseau déterminent si une évasion retire un événement ou rompt réellement la chaîne analytique.
ImportantL'idée transférable

Les internals EDR se comprennent plus justement comme un système distribué partiellement observable. L’endpoint produit des preuves fragmentées, les composants locaux les transforment, le transport peut les retarder ou les perdre, puis un backend reconstruit le comportement sous le contrôle d’une politique. Ce modèle reste utile d’un produit et d’un build Windows à l’autre, car il sépare la sémantique stable du système d’exploitation de l’implémentation propre au fournisseur.

Ce modèle mental ne vaut que si chaque affirmation conserve le niveau de preuve qui la supporte. La dernière étape consiste donc à distinguer contrat public, observation propre à un build et comportement mesuré d’un produit.

Hiérarchie des sources et limites.

Le WDK public définit les interfaces supportées, mais pas toutes les structures internes de dispatch utilisées par un build Windows particulier. Windows Internals explique les concepts d’implémentation tout en avertissant explicitement que les internals non documentés peuvent évoluer. Les symboles de débogage kernel et le reverse engineering peuvent établir le fonctionnement d’un build donné ; ils n’établissent pas un contrat compatible avec les versions futures. Le comportement d’un fournisseur exige une observation directe, car l’existence d’une fonction OS ne permet pas de déduire la manière dont un produit la configure ou la consomme.

Une expérimentation fondée sur les symboles WinDbg et une primitive de lecture ou d’écriture issue d’un driver vulnérable peut établir le comportement du système examiné. Elle ne prouve pas que les offsets, layouts internes, propriétaires de callbacks ou états de providers sont identiques sur un autre build.

Comprendre où se trouvent les fonctions Windows citées

Une fonction citée dans l’article peut intervenir à trois endroits différents. Son module d’exécution contient le code chargé, ou le stub qui transfère vers l’étape suivante. Son header fournit au compilateur le prototype et les types. Sa bibliothèque d’import .lib permet au linker d’inscrire la dépendance dans l’image Portable Executable (PE), mais elle ne contient pas le code utilisé au runtime. Le loader résout cette dépendance et écrit l’adresse obtenue dans l’Import Address Table (IAT), la table d’adresses d’import de l’application ou du driver.

Fonctions citéesModule d’exécutionDéclaration et importRôle dans le parcours
PsSetCreateProcessNotifyRoutineEx, fonctions de notification de threads et d’images, CmRegisterCallbackEx, ObRegisterCallbacks, DbgPrintEx, ZwSetValueKeyntoskrnl.exentddk.h ou wdm.h, puis NtosKrnl.libEnregistrement, retrait ou service fourni par le noyau
FltRegisterFilter, FltStartFiltering, FltUnregisterFilterFltMgr.sysfltkernel.h, puis FltMgr.libInscription et activation d’un minifilter
EventRegister, EventWrite, RegSetValueExWAdvapi32.dllevntprov.h ou winreg.h, puis Advapi32.libPublication ETW ou accès user-mode au registre
LoadLibraryA/W, OpenProcess, DuplicateHandle, CreateRemoteThreadExKernel32.dllHeader Win32 correspondant, puis Kernel32.libContrat public appelé par une application
NtCreateThreadEx, et EtwEventWrite sur les builds observésntdll.dllInterface Native API ou interne à qualifier selon le buildStub user-mode proche de la transition native ; il ne doit pas être présenté comme un contrat Win32 stable

Les callbacks ProcessNotify, ProcessHandlePreOperation, PreCreate, PostCreate et les autres routines fournies par l’EDR ne résident dans aucun de ces modules Windows. Leur code est compilé dans le fichier .sys, l’EXE ou la DLL du produit. L’API Windows reçoit leur adresse lors de l’enregistrement, puis le gestionnaire concerné conserve une inscription qui permet de les rappeler. Le contrat peut garantir ce rappel sans publier le tableau, la liste ou le layout kernel qui matérialise l’inscription sur un build donné.

Cette distinction permet de relire chaque fonction sans interrompre le fil principal : le module indique où se trouve le code Windows, le header et la bibliothèque expliquent comment le composant y fait référence, et l’adresse du callback indique où commence réellement le code du capteur.

L’article établit ici les contrats des capteurs et leurs limites de corrélation. Neutralisation d’EDR poursuit l’analyse sur un cas plus étroit : déterminer quand l’interface IOCTL d’un driver signé accorde réellement une primitive de terminaison non autorisée, sans confondre import kernel, atteignabilité et vulnérabilité.

Références

Utiliser avec une IA

Actions