> Windows Syscalls
ntoskrnl.exeT1055T1559T1106

NtQueryInformationAtom

Returns metadata about a single atom or the entire global atom table — name, refcount, pin count, usage counts.

Prototype

NTSTATUS NtQueryInformationAtom(
  RTL_ATOM                Atom,
  ATOM_INFORMATION_CLASS  InformationClass,
  PVOID                   AtomInformation,
  ULONG                   AtomInformationLength,
  PULONG                  ReturnLength
);

Arguments

NameTypeDirDescription
AtomRTL_ATOMinAtom ID to query. Ignored when InformationClass = AtomTableInformation (whole-table dump).
InformationClassATOM_INFORMATION_CLASSinAtomBasicInformation (per-atom: name + refcount + pin) or AtomTableInformation (atom count + ID list).
AtomInformationPVOIDoutCaller buffer that receives an ATOM_BASIC_INFORMATION or ATOM_TABLE_INFORMATION structure.
AtomInformationLengthULONGinSize of AtomInformation in bytes.
ReturnLengthPULONGoutReceives the number of bytes actually written. Used to size a follow-up call when STATUS_BUFFER_TOO_SMALL is returned.

Syscall IDs by Windows version

Windows versionSyscall IDBuild
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

Kernel module

ntoskrnl.exeNtQueryInformationAtom

Related APIs

GetAtomNameWGlobalGetAtomNameWGlobalGetAtomNameANtAddAtomNtFindAtomNtDeleteAtom

Syscall stub

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

Undocumented notes

The read-side counterpart to NtAddAtom. The `AtomBasicInformation` class returns an `ATOM_BASIC_INFORMATION` containing the UsageCount (refcount), Flags (pinned vs ordinary), the NameLength (in bytes), and the actual Name bytes — and as with NtAddAtom, the kernel will hand back whatever raw bytes were stored, regardless of UTF-16 validity. The `AtomTableInformation` class returns the count of atoms followed by an array of all RTL_ATOM IDs — effectively a full enumeration primitive. The Win32 wrappers `GlobalGetAtomNameW` and `GetAtomNameW` are the most common path; covert tooling and the !atom WinDbg extension use the raw syscall.

Common malware usage

**Atom Bombing stage 2**: this syscall is the one that performs the cross-process memory write — the malware queues an APC into the target that calls `GlobalGetAtomNameA`, which internally calls NtQueryInformationAtom; the kernel itself writes the atom 'name' bytes (the smuggled shellcode) into the buffer in the target's address space. Because the write is performed by the kernel on behalf of the target, NtWriteVirtualMemory hooks in usermode see nothing. **Stealth IPC inspection**: red-team and persistence-monitoring tools call AtomTableInformation to dump the entire global atom table and look for binary residue, oversized entries, or known-bad atom names — the same primitive used to detect Atom Bomb residuals defensively can be used offensively to read the live IPC state of legitimate apps (Office DDE topics, IME state, Explorer registered class names).

Detection opportunities

AtomBasicInformation calls happen constantly via GlobalGetAtomName — every drag-and-drop, every COM marshalling round, every DDE conversation. Detection has to focus on context: AtomBasicInformation called from a thread that just resumed via NtTestAlert / KiUserApcDispatcher into a freshly-allocated buffer is the canonical Atom Bomb stage-2 indicator (EDRs deploying the 2017-era Atom Bomb rules look for exactly this). AtomTableInformation is rarer in normal code paths and is a slightly stronger signal — a non-system process calling it is worth pivoting on. WinDbg / forensic snapshot of the global atom table (`!atom`) is the orthogonal blue-team primitive — anything in the table with binary bytes or length > a few hundred is suspect.

Direct syscall examples

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);
    }
}

MITRE ATT&CK mappings

Last verified: 2026-05-20