Skip to content

AtomBombing

Malware stores shellcode in the Windows global Atom Table via GlobalAddAtom, then uses NtQueueApcThread to force a target process to copy and execute it, bypassing traditional injection defences.

AtomBombing exploits the Windows global Atom Table — a system-wide string store used by applications to share small pieces of data across process boundaries. Unlike traditional injection paths that require VirtualAllocEx + WriteProcessMemory (both commonly monitored by EDR products), this technique uses GlobalAddAtom to place shellcode into the Atom Table and then abuses the APC mechanism to trigger GlobalGetAtomName inside the target process, causing the target itself to copy the shellcode into its own memory.

Because the technique does not call WriteProcessMemory, some EDR products that rely solely on that API as an injection indicator miss it.

Attack flow

  1. Store shellcode in atom(s) with GlobalAddAtomW.
  2. Find an alertable thread in the target process.
  3. Queue an APC for GlobalGetAtomNameW to copy the atom data into a RW buffer inside the target.
  4. Use ROP chains to call VirtualProtect inside the target (making the buffer executable) — bypassing DEP without a direct WriteProcessMemory write to an executable region.
  5. Queue a second APC pointing to the shellcode to execute it.

How it works

c
#include <windows.h>

// Step 1: store shellcode as atom (max 255 chars; use multiple atoms for larger payloads)
ATOM atom = GlobalAddAtomW((LPCWSTR)shellcode);  // shellcode treated as wide string

// Step 2: queue APC to copy atom data into target process address pDst
// NtQueueApcThread(hThread, GlobalGetAtomNameW, atom, pDst, cbDst)
typedef NTSTATUS (NTAPI *pfnNtQueueApcThread)(HANDLE, PVOID, ULONG_PTR, ULONG_PTR, ULONG_PTR);
pfnNtQueueApcThread NtQueueApc = (pfnNtQueueApcThread)
    GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtQueueApcThread");

NtQueueApc(hTargetThread,
           GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "GlobalGetAtomNameW"),
           (ULONG_PTR)atom,
           (ULONG_PTR)pRemoteBuffer,
           (ULONG_PTR)cbShellcode);

// Step 3: ROP chain to flip buffer to PAGE_EXECUTE_READ (bypasses DEP)
// Step 4: queue APC to pRemoteBuffer to execute shellcode

Because atoms are limited to 255 UTF-16 characters, larger payloads must be split across multiple atoms and reassembled in the target.

Detection & analysis

During analysis:

  • API monitor: unusual GlobalAddAtomW calls with binary content (not printable strings), followed by NtQueueApcThread referencing GlobalGetAtomNameW.
  • Memory forensics: search for anonymous executable pages in legitimate processes; the ROP chain and shellcode will appear there.

Static / automated detection:

  • YARA: co-occurrence of GlobalAddAtomW, GlobalGetAtomNameW, and NtQueueApcThread / QueueUserAPC in the same PE.
  • ETW: cross-process APC queuing targeting GlobalGetAtomNameW is rare in benign software.
  • Unprotect ID U1220; MITRE ATT&CK T1055.
Votes

Comments(0)