Registry Parser
Tous les articles

UserAssist et l'historique de lancement de programmes en ROT13

8 min de lecture

UserAssist est l'artefact qui, dans un bon jour, vous dit exactement quel exécutable un utilisateur a lancé, combien de fois, quand pour la dernière fois, et combien de temps le focus a duré. Dans un mauvais jour, il ne vous dit rien parce que le lancement venait d'un service ou d'une tâche planifiée. Savoir quel jour vous avez est la différence entre citer UserAssist comme preuve et le citer comme indice.

La clé, les GUID, l'encodage

Les données vivent sous HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist\ dans le NTUSER.DAT de l'utilisateur. Sous ce chemin vous obtenez une poignée de sous-clés GUID, chacune suivant une catégorie d'activité différente. Sur Windows moderne, les deux qui comptent sont :

  • {CEBFF5CD-ACE2-4F4F-9178-9926F41749EA} : exécutables lancés.
  • {F4E57C4B-2036-45F0-A9AB-443BCFE33D9F} : fichiers raccourcis (LNK) invoqués.

Chacun de ces GUID a une sous-clé Count dont les valeurs enregistrent les lancements. Le nom de la valeur est le chemin complet vers l'élément lancé, encodé en ROT13. Oui, ROT13. En 2026. L'encodage n'est pas une mesure de sécurité ; c'est un vestige d'une vieille politique destinée à rendre les lectures occasionnelles moins bruyantes. Quiconque a déjà regardé les données brutes l'a maudit une fois puis a écrit le décodeur de quatre lignes.

HRZR_PGYFRFFNVA décode en UEME_CTLSESSION. P:\Cebtenz Svyrf\Vagrearg Rkcybere\VRKCYBER.RKR décode en C:\Program Files\Internet Explorer\IEXPLORE.EXE. Le décodeur est littéralement un décalage ±13 sur les alphabétiques, laissant les chiffres et la ponctuation tels quels.

Deux noms de valeurs non-lancement vivent aussi dans les clés Count et confondent régulièrement les analystes :

  • UEME_CTLSESSION : un compteur incrémenté au démarrage de session.
  • UEME_CTLCUACount:ctor : un compteur générique lié à l'activation de contrôle. Ignorez pour la plupart.

Tout le reste avec un chemin est un véritable enregistrement de lancement.

Ce qui est dans la donnée de la valeur

La donnée de valeur est une structure binaire de taille fixe. Sur Windows 7 et ultérieur, elle fait 72 octets :

  • Décalage 0x00 (4 octets) : ID de session où l'entrée a été touchée pour la dernière fois.
  • Décalage 0x04 (4 octets) : compte d'exécutions.
  • Décalage 0x08 (4 octets) : compte de focus (nombre de fois que la fenêtre a gagné le focus).
  • Décalage 0x0C (4 octets) : temps de focus total en millisecondes, sommé sur les exécutions.
  • Décalage 0x10 à 0x3B : champs liés au timer, principalement à zéro en pratique.
  • Décalage 0x3C (8 octets) : FILETIME de la dernière exécution.
  • Décalage 0x44 (4 octets) : champ final, dépendant de la version.

Sur Windows XP la structure faisait 16 octets avec une disposition différente. Si vous tombez encore sur des ruches XP en 2026, vous avez un autre ensemble de problèmes, mais la logique d'encodage est la même.

Un enregistrement décodé pourrait ressembler à :

Chemin :          C:\Users\jdoe\AppData\Local\Programs\PuTTY\putty.exe
Compte exécutions : 12
Compte focus :     27
Temps focus :      3621542 ms (environ 60 minutes)
Dernière exécution : 2026-05-21 14:22:11 UTC

Cet enregistrement seul vous dit : cet utilisateur a lancé PuTTY douze fois via Explorer, avec environ une heure de focus de fenêtre cumulé, avec l'invocation la plus récente à l'horodatage donné.

Pourquoi c'est l'un des meilleurs artefacts d'exécution par

utilisateur

UserAssist vit dans le NTUSER.DAT de l'utilisateur. Il n'y a aucune ambiguïté sur l'attribution. Quand vous trouvez un enregistrement là, la réponse à "qui a lancé cela" est l'utilisateur dont le profil détient la ruche.

Comparez à Shimcache, qui dit "ce binaire était sur le système" mais ne peut vous dire qui l'a lancé. Comparez à AmCache, qui enregistre l'exécution mais est à l'échelle système. Comparez à Prefetch, qui suit l'exécution mais n'attribue pas à un utilisateur. UserAssist est le seul artefact courant qui cloue l'attribution depuis une source unique.

Il suit aussi les comptes d'exécution et le temps de focus. Un binaire lancé 47 fois avec deux heures de focus n'est pas le genre de chose qu'un attaquant insère pour une session unique. Un binaire lancé une fois avec trois secondes de focus peut être exactement cela.

Ce qui n'est pas suivi

C'est la partie qui piège les enquêteurs qui s'appuient sur UserAssist comme s'il était un journal d'exécution complet.

UserAssist enregistre les lancements via :

  • Explorer (double-clic, simple clic sur un raccourci épinglé).
  • Le menu Démarrer.
  • La boîte de dialogue Exécuter (Win+R).
  • La barre des tâches.
  • Une invocation de fichier LNK.

UserAssist n'enregistre pas les lancements via :

  • La ligne de commande (cmd.exe, powershell.exe).
  • Les tâches planifiées.
  • Les services.
  • La création de processus WMI.
  • Toute invocation non-GUI par un autre processus.
  • L'exécution distante (PsExec, WinRM, RDP-via-rundll).

C'est par conception. Le shell enregistre les lancements pilotés par GUI. Les lancements programmatiques ne passent pas par le shell. Les attaquants qui connaissent cette règle traitent UserAssist comme une fenêtre sur le comportement manuel de l'utilisateur, pas sur le comportement du malware. La même logique s'applique à vous.

Une charge utile malware que l'utilisateur a double-cliquée depuis un lien de phishing apparaîtra dans UserAssist. La même charge utile exécutée par une tâche planifiée créée par le malware n'apparaîtra pas.

Le LastRun qui clignote

Le FILETIME LastRun est mis à jour à chaque lancement. Utile et volatil. Si vous prélevez une ruche à 14:00 et que l'utilisateur ouvre le binaire à 14:05, le LastRun a bougé et le précédent a disparu de la ruche vivante. Acquérez la ruche une fois, à une heure connue. Traitez cela comme votre état de référence.

Les snapshots VSS sont le filet de sécurité. Si VSS est activé, prenez NTUSER.DAT de chaque snapshot et reconstruisez les changements de LastRun au fil du temps. C'est la différence entre "dernière exécution mardi" et "dernière exécution mardi mais utilisée quotidiennement pendant deux semaines avant cela".

Compte de focus versus compte d'exécution

Le compte d'exécution s'incrémente au lancement. Le compte de focus s'incrémente quand la fenêtre gagne le focus, ce qui peut arriver plusieurs fois par lancement (clics ailleurs, clics retour). Le temps de focus s'accumule pendant que la fenêtre est active.

Un binaire avec compte d'exécution élevé et temps de focus faible a été lancé et écarté : un wrapper de tâche planifiée qui ouvre une fenêtre de console pour une demi-seconde correspond à ce schéma. Compte d'exécution modéré avec temps de focus très élevé est quelque chose que l'utilisateur utilise activement : un tableur, un éditeur, une app de chat.

Cherchant des lancements malicieux, le schéma intéressant est "compte d'exécution élevé, temps de focus faible" combiné à un chemin dans %APPDATA% ou %LOCALAPPDATA%\Temp. C'est un binaire lancé à répétition avec chaque invocation se terminant rapidement.

L'exemple de décodage, de bout en bout

Un nom de valeur UserAssist brut pourrait ressembler à :

P:\Hfref\wqbr\NccQngn\Ybpny\Grzc\flgrk.rkr

Décodage ROT13 : C:\Users\jdoe\AppData\Local\Temp\sytex.exe

Un binaire dans \AppData\Local\Temp\ est suspect par défaut. Un binaire là qui a des entrées UserAssist en est un que l'utilisateur a double-cliqué, ce qui signifie qu'il est arrivé via téléchargement, pièce jointe d'email, ou dépôt USB, et a été ouvert délibérément.

Regardez la donnée de valeur. Supposons que le compte d'exécution est 1, le compte de focus est 1, le temps de focus est 412 ms. Le binaire a tourné une fois, brièvement. Cela correspond à un dropper. Pivotez vers le fichier LNK dans Recent\, l'entrée Prefetch pour sytex.exe, l'enregistrement AmCache pour le fichier. Récupérez le fichier lui-même depuis le MFT.

Cette séquence reconstruit le moment où un utilisateur a été infecté, avec des horodatages à la seconde et l'attribution complète.

Outils

  • Le plugin userassist de RegRipper. Gère le ROT13, le FILETIME, le compte d'exécution, le temps de focus. Sortie : une ligne par entrée. Lisez la source du plugin si vous voulez voir exactement ce qui est extrait.
  • RECmd d'Eric Zimmerman avec le fichier de lot UserAssist. Mêmes sorties en CSV.
  • L'analyseur de ce site décode UserAssist dans sa vue arbre standard.

La plupart des décodeurs ad-hoc sautent le compte de focus et le temps de focus. Assurez-vous que votre outil fait remonter les quatre nombres, pas seulement le chemin et le LastRun.

Cas limites à connaître

Les profils itinérants peuvent traîner UserAssist entre hôtes. Un utilisateur qui se connecte sur plusieurs hôtes avec un profil itinérant aura un ensemble unique d'entrées UserAssist reflétant l'activité sur l'hôte qui a écrit le profil en dernier. Recoupez avec Prefetch (qui est par hôte) pour déterminer où chaque lancement s'est réellement passé.

Effacer UserAssist est possible mais laisse des traces. Certains outils de "confidentialité" et utilitaires de classe CCleaner effacent les valeurs UserAssist. L'effacement lui-même laisse la sous-clé GUID présente avec un LastWrite récent et un Count vide. Ce schéma seul est révélateur. Les cellules non allouées récupérées dans la ruche peuvent encore contenir les valeurs effacées ; le plugin del de RegRipper et les modes de récupération de yarp les trouveront.

Les pseudo-chemins Software Distribution et Backup. Certaines entrées référencent des GUID de dossier shell plutôt que des chemins réels. {1AC14E77-02E7-4E5D-B744-2EB1AE5198B7} est System32. {F38BF404-1D43-42F2-9305-67DE0B28FC23} est Windows. Un décodeur qui ne les résout pas montrera des GUID dans le chemin, déroutant mais techniquement correct.

Pour aller plus loin

UserAssist ne résoudra pas votre dossier seul. Combiné à Prefetch, AmCache et les fichiers LNK dans Recent\, il répond à "qui a lancé quoi, quand, et pendant combien de temps" avec une confiance que presque rien d'autre sur le système n'égale. Rappelez-vous juste ce qu'il ne voit pas, et pivotez vers d'autres artefacts pour couvrir le manque.