> Windows Syscalls
ntoskrnl.exeT1055T1559T1106

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

NameTypeDirDescription
AtomRTL_ATOMinID atome à interroger. Ignoré quand InformationClass = AtomTableInformation (dump complet de la table).
InformationClassATOM_INFORMATION_CLASSinAtomBasicInformation (par atome : nom + refcount + pin) ou AtomTableInformation (compte + liste d'IDs).
AtomInformationPVOIDoutBuffer de l'appelant qui reçoit une structure ATOM_BASIC_INFORMATION ou ATOM_TABLE_INFORMATION.
AtomInformationLengthULONGinTaille de AtomInformation en octets.
ReturnLengthPULONGoutReç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 WindowsID de syscallBuild
Win10 15070x12Fwin10-1507
Win10 16070x135win10-1607
Win10 17030x13Awin10-1703
Win10 17090x13Dwin10-1709
Win10 18030x13Fwin10-1803
Win10 18090x140win10-1809
Win10 19030x141win10-1903
Win10 19090x141win10-1909
Win10 20040x147win10-2004
Win10 20H20x147win10-20h2
Win10 21H10x147win10-21h1
Win10 21H20x148win10-21h2
Win10 22H20x148win10-22h2
Win11 21H20x14Ewin11-21h2
Win11 22H20x150win11-22h2
Win11 23H20x150win11-23h2
Win11 24H20x152win11-24h2
Server 20160x135winserver-2016
Server 20190x140winserver-2019
Server 20220x14Dwinserver-2022
Server 20250x152winserver-2025

Module noyau

ntoskrnl.exeNtQueryInformationAtom

APIs liées

GetAtomNameWGlobalGetAtomNameWGlobalGetAtomNameANtAddAtomNtFindAtomNtDeleteAtom

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 ENDP

cAtom 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