A Missing Piece in PlushDaemon: Anatomy of a Stealthy Loader
The sample analyzed in this article was not identified through EDR alerts or pre-existing YARA rules. It emerged during a threat hunting campaign conducted by our CTI teams, triggered by several telemetry anomalies: a PE64 DLL submitted from China, claiming to be a legitimate Microsoft binary without respecting their standard PDB path schema, lacking a digital signature, and exhibiting significant malicious characteristics.
What makes this sample particularly interesting is the tension between its three technical identities: the low detection rate of the backdoor, the magic header used and the PDB path, which links it to DaemonicLogistics, a Chinese espionage actor documented by ESET in January 2025. Far from being a mere classification artifact, this dual signal is technically explained, and that is precisely what this article documents.
SHA256 : 993ba9ae2cacf8e6258c20c9cc7cc2fac1a7f0c518b298c19db696d5b691fbe7
Technical Analysis
General structure
The PDB path is the first major IOC: D:\project\vs\zx\NSP64\x64\Release\MemloadX64.pdb. This suggests it may be a sub-project of LittleDaemon, which used the path D:\project\vs\zx\ServiceSvc\Release\NewService.pdb (in file with SHA1 2857bc730952682d39f426d185769938e839a125). This project is named NSP64, potentially standing for “NewServiceProcess,” and the output binary is explicitly named MemloadX64.dll, strongly suggesting process injection.
Name obfuscation
An obfuscation layer was designed into the code but was not fully activated by the attacker.
It targets the resolution of ntdll APIs. Rather than statically importing LdrLoadDll or NtAllocateVirtualMemory, strings that are easily detectable by any static scanner, the binary stores their bitwise-inverted representations (NOT byte) in the .rdata section.
The str_not_copy primitive (at 0x1800014d0) operates as follows: for each byte from the source until the null terminator, it writes the bitwise inverse (NOT operation) of the byte into the destination buffer. Interestingly, the attacker anticipated that this routine, this logical loop, might be detected, and thus created a loop-less function… see for yourself:
Strangely, a second NOT pass in each string initialization function restores the original string in memory, within a global buffer accessible only at runtime. It is highly likely that the attacker performs this “double encoding” during debugging but intended to leave only one pass in production. You can see in the following function an example of a call to str_not_copy, then the second decoding loop (and therefore the return of the string to plain text):
Seven character strings are initialized this way:
- “ntdll”
- “LdrGetProcedureAddress”
- “NtAllocateVirtualMemory”
- “LdrLoadDll”
- “RtlInitAnsiString”
- “RtlAnsiStringToUnicodeString”
- “RtlFreeUnicodeString”
The resolution intentionally utilizes LdrGetProcedureAddress (ntdll) instead of GetProcAddress (kernel32), effectively bypassing userland hooks placed by EDRs on the Win32 layer.
The encrypted GIF loader
The implant implements a specific mechanism for loading an external piece of code, using an environment variable, Apache_Fpath, to point to a GIF file to be read.
hFile = CreateFileA(file_name,0x80000000,0x0,(LPSECURITY_ATTRIBUTES)0x0,0x3,0x0,(HANDLE)0x0);
[...]
file_size = GetFileSize(hFile,(LPDWORD)0x0);
gif_datas = malloc(file_size);
if (gif_datas != (byte *)0x0) {
ReadFile(hFile, gif_datas, file_size, local_res20, 0x0);
CloseHandle(hFile);
is_good_header = _memicmp("GIF89a\x10\x10", gif_datas, 0x8);
if (is_good_header == 0x0) {
xor_key = gif_datas[0x8];
size_no_header = file_size - 0x9;
payload_datas = (byte *)malloc(size_no_header);
if (payload_datas != (byte *)0x0) {
memcpy(payload_datas, gif_datas + 0x9, size_no_header);
free(gif_datas);
gif_datas = payload_datas;
if (0x0 < size_no_header) {
do {
offset_datas = i;
i++;
*gif_datas = *gif_datas ^ offset_datas ^ xor_key;
gif_datas++;
} while (i < size_no_header);
}
The (fake) GIF file is read and decrypted, revealing a sub-structure composed of a size (potentially for a key), the size of the data, and finally the data itself. Once again, we can infer that the first size field was likely intended for a key, though this space remained unused in the program.
In summary, we can establish the following schema:
In-Process Memory Injection
After reading the GIF payload, the loader decrypts a second buffer of 0x44F bytes from the DLL:
dyn_buffer = malloc(0x44f);
do {
i++;
*dyn_buffer = dyn_buffer[&crypted_function - dyn_buffer] ^ i + 0x1 ^ 0x7e;
dyn_buffer++;
} while (i < 0x44f);
This buffer is a function designed to parse a PE binary, load its modules, and execute its entrypoint. This is essentially a custom Windows executable loader.
Three calls to WriteProcessMemory write three stubs: the payload (which is actually an executable file), the PE loader, and the previously resolved APIs. Following these allocations, CreateThread is called to load the binary using the newly mapped APIs.
The Exalyze approach
While this binary has low antivirus detection (4 detections, no attribution), it is a compelling case for analysis via Exalyze, our malware analysis and comparison platform. Several file properties were immediately flagged as highly suspicious:
Specifically, code injection characteristics were instantly identified, alongside an anomaly involving an empty export table (rare for a DLL). Furthermore, the file attempts to masquerade as a Microsoft binary but lacks the necessary technical traits:
- A non-standard debug path.
- A compilation environment inconsistent with Microsoft’s internal standards.
Going deeper, the sequence view automatically highlights the Apache_Fpath environment variable, the GIF file read, its decryption, and the subsequent dynamic loading and execution of code. Essentially, the platform automated the entire reverse engineering process we just described.
Links to PlushDaemon / DaemonicLogistics
Two strong characteristics link this binary to the DaemonicLogistics tool used by PlushDaemon:
- Both binaries share a very similar PDB path; only the tool name has changed.
- Both binaries use the same GIF magic pattern (identification pattern). However, the binary analyzed by ESET appeared to ignore it, leading us to believe their team had not yet encountered this specific sample.
The binary was submitted from China multiple times, aligning with the findings presented in ESET’s research: PlushDaemon compromises network devices for adversary-in-the-middle attacks
Indicators of Compromise (IOCs)
SHA256 : 993ba9ae2cacf8e6258c20c9cc7cc2fac1a7f0c518b298c19db696d5b691fbe7
Yara rule detecting GIF file:
rule DaemonicLogistics_Gif_Apache_Fpath {
meta:
author = "Heurs"
date = "2026-04-30"
description = "Bad GIF loaded with DaemonicLogistics signature."
tlp = "CLEAR"
source = "ExaTrack"
strings:
$h = "GIF89a\x10\x10" ascii
$sign1 = "NETSCAPE" ascii
$sign2 = { 2c 00 00 00 00 }
$sign3 = { 00 2c 00 00 00 }
condition:
$h at 0 and not(1 of ($sign*))
}