NtQueryInformationAtom
Renvoie des métadonnées sur un atome unique ou sur toute la table d'atomes globale — nom, refcount, pin count, compteurs d'usage.
Prototype
NTSTATUS NtQueryInformationAtom( RTL_ATOM Atom, ATOM_INFORMATION_CLASS InformationClass, PVOID AtomInformation, ULONG AtomInformationLength, PULONG ReturnLength );
Arguments
| Name | Type | Dir | Description |
|---|---|---|---|
| Atom | RTL_ATOM | in | ID atome à interroger. Ignoré quand InformationClass = AtomTableInformation (dump complet de la table). |
| InformationClass | ATOM_INFORMATION_CLASS | in | AtomBasicInformation (par atome : nom + refcount + pin) ou AtomTableInformation (compte + liste d'IDs). |
| AtomInformation | PVOID | out | Buffer de l'appelant qui reçoit une structure ATOM_BASIC_INFORMATION ou ATOM_TABLE_INFORMATION. |
| AtomInformationLength | ULONG | in | Taille de AtomInformation en octets. |
| ReturnLength | PULONG | out | Reçoit le nombre d'octets effectivement écrits. Sert à dimensionner un appel suivant en cas de STATUS_BUFFER_TOO_SMALL. |
IDs de syscalls par version de Windows
| Version de Windows | ID de syscall | Build |
|---|---|---|
| Win10 1507 | 0x12F | win10-1507 |
| Win10 1607 | 0x135 | win10-1607 |
| Win10 1703 | 0x13A | win10-1703 |
| Win10 1709 | 0x13D | win10-1709 |
| Win10 1803 | 0x13F | win10-1803 |
| Win10 1809 | 0x140 | win10-1809 |
| Win10 1903 | 0x141 | win10-1903 |
| Win10 1909 | 0x141 | win10-1909 |
| Win10 2004 | 0x147 | win10-2004 |
| Win10 20H2 | 0x147 | win10-20h2 |
| Win10 21H1 | 0x147 | win10-21h1 |
| Win10 21H2 | 0x148 | win10-21h2 |
| Win10 22H2 | 0x148 | win10-22h2 |
| Win11 21H2 | 0x14E | win11-21h2 |
| Win11 22H2 | 0x150 | win11-22h2 |
| Win11 23H2 | 0x150 | win11-23h2 |
| Win11 24H2 | 0x152 | win11-24h2 |
| Server 2016 | 0x135 | winserver-2016 |
| Server 2019 | 0x140 | winserver-2019 |
| Server 2022 | 0x14D | winserver-2022 |
| Server 2025 | 0x152 | winserver-2025 |
Module noyau
APIs liées
Stub du syscall
4C 8B D1 mov r10, rcx B8 52 01 00 00 mov eax, 0x152 F6 04 25 08 03 FE 7F 01 test byte ptr [0x7FFE0308], 1 75 03 jne short +3 0F 05 syscall C3 ret CD 2E int 2Eh C3 ret
Notes non documentées
Le pendant en lecture de NtAddAtom. La classe `AtomBasicInformation` renvoie une `ATOM_BASIC_INFORMATION` contenant le UsageCount (refcount), les Flags (épinglé vs ordinaire), le NameLength (en octets) et les octets de nom — et comme pour NtAddAtom, le noyau rend les octets bruts stockés, indépendamment de la validité UTF-16. La classe `AtomTableInformation` renvoie le compte d'atomes suivi d'un tableau de tous les RTL_ATOM IDs — une primitive d'énumération complète. Les wrappers Win32 `GlobalGetAtomNameW` et `GetAtomNameW` sont la voie la plus courante ; les outils discrets et l'extension WinDbg !atom utilisent le syscall brut.
Usage courant par les malwares
**Atom Bombing étape 2** : c'est ce syscall qui effectue l'écriture mémoire cross-process — le malware enqueue un APC dans la cible qui appelle `GlobalGetAtomNameA`, qui appelle en interne NtQueryInformationAtom ; le noyau lui-même écrit les octets de 'nom' d'atome (le shellcode contrebandé) dans le buffer de l'espace d'adresses de la cible. Comme l'écriture est faite par le noyau pour le compte de la cible, les hooks NtWriteVirtualMemory usermode ne voient rien. **Inspection IPC discrète** : les outils red-team et de monitoring de persistance appellent AtomTableInformation pour dumper toute la table d'atomes globale et y chercher des résidus binaires, entrées surdimensionnées ou noms connus mauvais — la même primitive qui sert défensivement à détecter les résiduels Atom Bomb peut servir offensivement pour lire l'état IPC live d'apps légitimes (topics DDE Office, état IME, noms de classes Explorer).
Opportunités de détection
Les appels AtomBasicInformation surviennent constamment via GlobalGetAtomName — chaque drag-and-drop, chaque round de marshalling COM, chaque conversation DDE. La détection doit se concentrer sur le contexte : AtomBasicInformation appelé depuis un thread qui vient de reprendre via NtTestAlert / KiUserApcDispatcher dans un buffer fraîchement alloué est l'indicateur canonique d'étape 2 Atom Bomb (les EDR déployant les règles Atom Bomb de l'ère 2017 cherchent exactement ça). AtomTableInformation est plus rare dans les chemins de code normaux et est un signal légèrement plus fort — un processus non-système qui l'appelle mérite un pivot. Le snapshot WinDbg / forensique de la table d'atomes globale (`!atom`) est la primitive blue-team orthogonale — toute entrée avec des octets binaires ou une longueur > quelques centaines est suspecte.
Exemples de syscalls directs
asmx64 direct stub (Win11 24H2)
; Direct syscall stub for NtQueryInformationAtom (SSN 0x152 on Win11 24H2)
NtQueryInformationAtom PROC
mov r10, rcx ; syscall convention
mov eax, 152h ; SSN — varies per build
syscall
ret
NtQueryInformationAtom ENDPcAtom Bombing — stage 2 (kernel writes shellcode into target)
// Inside the victim process, the queued APC runs this code.
// GlobalGetAtomNameA -> NtQueryInformationAtom -> kernel copies the atom
// 'name' bytes (which were our shellcode in stage 1) into 'buf'.
#include <windows.h>
VOID StageTwoApc(PVOID context) {
USHORT atomId = (USHORT)(ULONG_PTR)context;
char buf[4096]; // attacker-sized to fit the shellcode
UINT got = GlobalGetAtomNameA(atomId, buf, sizeof(buf));
// Stage 3 (not shown): NtSetContextThread / ROP -> NtProtectVirtualMemory
// flips buf to RWX and pivots execution into it.
(void)got;
}cAtom table enumeration (defensive or offensive)
// Dump every atom in the global table and print its name + refcount.
// Use defensively: hunt for binary residue (Atom Bomb IOC).
// Use offensively: read live IPC state (DDE topics, IME state).
#include <windows.h>
#include <winternl.h>
#include <stdio.h>
typedef struct _ATOM_TABLE_INFORMATION {
ULONG NumberOfAtoms;
USHORT Atoms[1];
} ATOM_TABLE_INFORMATION;
typedef struct _ATOM_BASIC_INFORMATION {
USHORT UsageCount;
USHORT Flags;
USHORT NameLength;
WCHAR Name[1];
} ATOM_BASIC_INFORMATION;
typedef NTSTATUS (NTAPI *pNtQueryInformationAtom)(
USHORT, ULONG, PVOID, ULONG, PULONG);
VOID DumpAtomTable(VOID) {
pNtQueryInformationAtom NtQueryInformationAtom = (pNtQueryInformationAtom)
GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueryInformationAtom");
BYTE tbuf[8192]; ULONG ret = 0;
if (NtQueryInformationAtom(0, /*AtomTableInformation*/ 1,
tbuf, sizeof(tbuf), &ret) < 0) return;
ATOM_TABLE_INFORMATION* ti = (ATOM_TABLE_INFORMATION*)tbuf;
for (ULONG i = 0; i < ti->NumberOfAtoms; ++i) {
BYTE abuf[1024]; ULONG ar = 0;
if (NtQueryInformationAtom(ti->Atoms[i], /*AtomBasicInformation*/ 0,
abuf, sizeof(abuf), &ar) < 0) continue;
ATOM_BASIC_INFORMATION* ai = (ATOM_BASIC_INFORMATION*)abuf;
wprintf(L"0x%04X rc=%u name=%.*s\n",
ti->Atoms[i], ai->UsageCount, ai->NameLength / 2, ai->Name);
}
}Mappings MITRE ATT&CK
Last verified: 2026-05-20