Back to Feed
MalwareApr 20, 2026

Chasing an Angry Spark

VM-obfuscated AngrySpark backdoor discovered on single UK machine, operated one year undetected.

Summary

Security researchers discovered AngrySpark, a sophisticated three-stage backdoor featuring a custom virtual machine bytecode interpreter, that operated undetected on a single UK host for approximately one year before disappearing. The malware employed advanced evasion techniques including dual-layer encryption, direct syscalls, hypervisor detection, and runtime payload assembly, making each component independently replaceable through bytecode updates. Despite its engineering complexity and multi-layered architecture, the malware had an unusually limited footprint with no secondary victims identified and no public repository detection during its operational lifetime.

Full text

ResearchChasing an Angry SparkA VM-obfuscated backdoor observed on a single machine in the UK, operated for one year, and vanished without a trace. Threat Research TeamThreat Research TeamPublishedApril 14, 2026Read time29 MinutesWritten byThreat Research TeamThreat Research TeamPublishedApril 14, 2026Read time29 MinutesShare this articleIn the spring of 2022, our anti-rootkit engine flagged something unusual inside a svchost.exe process on a machine in the United Kingdom. A chunk of memory -roughly 32 kilobytes- was making direct NtQuerySystemInformation syscalls, bypassing every usermode hook we had in place. When we pulled the memory dump and opened it in a disassembler, we found not just shellcode, but an entire virtual machine: a custom bytecode interpreter running a 25-kilobyte program, decoding its own payload on the fly, resolving APIs through hash lookups, and checking for hypervisors before executing a single line of its real code.We named it AngrySpark -after the C++ namespace angry_spark found in the RTTI metadata of the DLL that delivered it. Over the following year, we captured three memory snapshots from the same host, watched its C2 infrastructure evolve, and eventually saw the connection get blocked. Then it vanished. No new samples. No new infrastructure. No second victim. Just a single spark in the dark.This is the story of how we took it apart.Why AngrySpark mattersMost backdoors pick a lane. AngrySpark built three of them. The DLL, the VM, and the beacon each carry their own encryption, their own API resolution, and their own C2 channel. Compromise one layer and the other two remain opaque.That architecture is what elevates it from a well-built backdoor to something worth writing up. The payload is assembled from bytecode at runtime, so the operator can swap out the final-stage binary, change the API set, add anti-analysis checks, or rotate C2 URLs by pushing a new 25KB blob. The 6.5KB loader never changes. It behaves more like a deployment framework than a conventional dropper.The other reason it matters is the mismatch between effort and scope. The engineering is serious: a VM-based deployment framework, dual encrypted C2 channels, CET-aware anti-analysis, runtime code patching, and direct syscalls with a 24-build compatibility table. Yet it appeared on a single machine in the United Kingdom, ran for about a year, and disappeared when its infrastructure expired.No public malware repository carried the DLL during its operational life, and no other vendor published on it. That does not make AngrySpark more important than broader campaigns, but it does make it unusually revealing. It looks like high-effort malware deployed with a very limited visible footprint.The architecture at a glanceAngrySpark operates as a three-stage system. A DLL masquerading as a Windows component loads via the Task Scheduler, decrypts its configuration from the registry, and injects position-independent shellcode into svchost.exe. That shellcode implements a virtual machine. The VM processes a 25KB blob of bytecode instructions, decoding and assembling the real payload -- a beacon that profiles the machine, phones home over HTTPS disguised as PNG image requests, and can receive encrypted shellcode for execution.Each stage is designed to be independently replaceable. The VM's bytecode format means the operator can swap out the payload, change the configuration, rotate C2 URLs, or add new anti-analysis checks -- all without modifying a single byte of the loader code.StageRoleKey ideaStage 0, DLL loaderMasquerades as a Windows Task Scheduler extension, decrypts config from the registry, and injects shellcode into svchost.exeA quiet entry point with persistence, configuration handling, and self-removal logicStage 1, VM loaderRuns a custom bytecode interpreter that assembles the real payload at runtimeThe operator can change behavior by updating the blob rather than the loaderStage 2, beaconProfiles the host, communicates over HTTPS disguised as PNG image requests, and can execute encrypted shellcode from the serverA full C2 channel built to blend in and stay adaptable Stage 0, the DLL loaderThe entry point is a 200KB DLL named WptsExtensions.dll, the exact name of a legitimate Windows Task Scheduler extension library. It exports the same five functions (WptsCopyActionData, WptsCreateAction, WptsDestroyAction, WptsFreeActionData, WptsLaunchAction) and spoofs its version info as "Microsoft Corporation, v10.0.19041.1023".After loading its configuration, the DLL injects the VM shellcode into a svchost.exe service host process. The injected code is entirely position-independent. It carries its own API resolver, its own exception handler, and its own syscall dispatcher. No imports, no relocations, no PE headers.Stage 1, the virtual machineThe VM loader is a ~6.5KB position-independent shellcode that implements a bytecode interpreter. It reads instructions sequentially from a 25KB configuration blob, dispatching each to a handler function. The blob contains approximately 190 entries (VM instructions) encoded in a custom format.The May 2022 variant has 23 entries (syscall tables). Between May and June 2022, the operator added a single entry: build 20344, a Windows 10 Insider preview from the Iron/Cobalt development cycle -- the branch that became Windows 11. The June and January variants are identical (24 entries, table at 0x13A2).This tells us two things. First, the operator was actively maintaining the syscall table and testing against Insider builds during May-June 2022. Second, they stopped updating after that. Builds above 22000 (Win11 22H2, 23H2) are not covered, and the entry-point guard rejects them outright. The shellcode was frozen sometime in mid-2022.Stage 2, the beaconThe assembled payload is a 27KB position-independent shellcode with 74 functions. It implements a full C2 beacon with system profiling, encrypted communication, and remote code execution.The beacon disguises its check-in as a request for a PNG image:The encrypted shellcode is decrypted with the second XXTEA key from the configuration, then executed in freshly allocated memory. After execution, the results are XXTEA-encrypted and sent back via HTTP POST to a separate .php endpoint.Stealth, anti-analysis, and self-removalAngrySpark is not only modular, it is also careful about how it appears to defenders. Several design choices look specifically aimed at frustrating clustering, bypassing instrumentation, and limiting the forensic residue left behind.The binary's PE metadata has been deliberately altered to confuse toolchain fingerprinting.Notable defensive friction pointsThe PE timestamp is falsified, and the Rich header was modified after compilation in a way that breaks straightforward toolchain fingerprinting.The shellcode uses direct syscalls to get around usermode API hooks.The VM includes hypervisor checks and CET or Shadow Stack aware anti-analysis logic.The DLL pins the C2 server's TLS certificate fingerprint, rejecting connections to anything that does not match the expected infrastructure. If someone sinkholed the domain or stood up a proxy with a different certificate, the DLL would refuse to talk.The loader can remove itself if contact goes stale or if the server explicitly tells it to do so.The heartbeat serves a second purpose: it is a dead man's switch. On each beacon cycle, the DLL compares the current time against the stored heartbeat. If the difference exceeds a configurable threshold (stored as a day count in the config blob, multiplied by 864000000000 -- one day in 100-nanosecond FILETIME intervals), the DLL enters its self-destruct sequence: it wipes the encrypted registry blob via RegDeleteValue, then schedules its own DLL file for deletion on the next reboot using MoveFileExW with the MOVEFILE_DELAY_UNTIL_REBOOT flag. Before that, it impersonates itself with SeSecurityPrivilege, SeRestorePrivilege, and SeTakeOwnershipPrivilege, moves the DLL to a temp directory, and

Indicators of Compromise

  • malware — AngrySpark
  • malware — WptsExtensions.dll

Entities

Virtual Machine (VM) bytecode interpreter (technology)Position-independent shellcode (technology)HTTPS C2 disguised as PNG requests (technology)