ed1a00479fe2ea2555882c67719abc86e98b512f122aea79adacf37355cab996silverfox: ed1a0047 — Rust x64 dropper with LZSS payload extraction and ntdll unhooking
Executive Summary
A 150 KB Rust-compiled x64 PE dropper masquerading as a Chinese-language PDF. The binary embeds multiple encrypted/compressed payloads in PE .rsrc, extracts them with a custom stream cipher seeded by runtime image hash, decompresses via an LZSS-like algorithm, then injects the decrypted payload into a suspended clone of itself using process hollowing. Prior to injection it restores ntdll .text from disk to evade user-mode EDR hooks. No CAPE detonation available (no x64 Windows guest); all behavior inferred from static reversing.
What It Is
| Attribute | Value |
|---|---|
| SHA-256 | ed1a00479fe2ea2555882c67719abc86e98b512f122aea79adacf37355cab996 |
| File on disk | 2026年 第一季度违纪违规人员名单pdf.exe |
| Type | PE32+ executable (GUI) x86-64^[file.txt] |
| Size | 150 528 bytes |
| Compiler | Rust (rustc) — evidenced by .buildid section and standard library linkage^[pefile.txt:117] |
| Linker | MSVC 14.0 (LinkerVersion: 14.0)^[pefile.txt:45] |
| Stripped | Yes (external PDB, debug stripped)^[pefile.txt:39] |
| Signed | No, unsigned^[rabin2-info.txt:27] |
| Timestamp | Fri 2026-05-22 01:10:24 UTC (fabricated or recent)^[pefile.txt:34] |
| VersionInfo | Randomized: JUjflFmsWFlu.exe, CompanyName=JECIHgAuilWg^[pefile.txt:321] |
The OpenCTI labels on this artifact include both silverfox and valleyrat^[triage.json:11] — these may be overlapping campaigns or dual-track attribution. The codebase is Rust with heavy use of unsafe NT APIs via ntdll and hand-rolled crypto; the build is stripped and uses randomized version strings.^[pefile.txt:321]
How It Works
1. Entry point decoy & real start
entry() calls Sleep(0) and immediately exits.^[ghidra:entry] The true initializer is FUN_140001030() (the Rust main or a pre-main hook) which builds obfuscated strings on the stack, resolves APIs dynamically, and begins the payload chain.
2. Anti-analysis: library & process check
The binary builds a DLL name on the stack via FUN_140004ee0() and loads it.^[ghidra:FUN_140001030:90] If the library is absent or a queried export returns 0, it triggers ShellExecuteExW() followed by ExitProcess()^[ghidra:FUN_140001030:95], suggesting an environment-gate that either detects the wrong sandbox or attempts to elevate/re-launch.
3. Process enumeration & target selection
CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS) walks processes looking for a specific module name.^[ghidra:FUN_140001030:120] If found, the target PID is harvested for later injection.
4. Resource extraction (encrypted payload)
Embedded resources in .rsrc are accessed by numeric IDs (200, 51, 103, 107, 129, 67, 198, 150, 27)^[pefile.txt:438] — no FindResourceA/LoadResourceA imports are visible in the static IAT because the binary maps its own PE sections directly via CreateFileMappingW + MapViewOfFile and parses the resource directory manually.^[ghidra:FUN_140001030:1800]
5. Decryption stream cipher
A custom stream cipher is seeded with a key derived from bVar4 ^ 0xcaaafe23, runs a non-linear transformation over each byte using constants 0x3d57aa23, 0x44d9bb23, and 0x9e37cb23^[ghidra:FUN_140001030:1250], then XORs the payload in place. The same routine feeds a decompression pass.
6. Decompression (LZSS-like)
FUN_140004000() implements an LZSS decompressor^[ghidra:FUN_140004000]: bit-packed literal/copy flags, 12-bit back-reference length/offset fields, and sliding-window copy loops using FUN_1400065c0(). The compressed payload appears to sit inside .rsrc resources immediately following an initial length byte.
7. EDR evasion: restore ntdll .text from disk
Before injection, the binary:
- Resolves its own
ntdllbase viaGetModuleHandleW("ntdll").^[ghidra:FUN_140001030:1520] - Opens
System32\ntdll.dllread-only viaCreateFileW.^[ghidra:FUN_140001030:1530] - Maps it with
CreateFileMappingW+MapViewOfFile.^[ghidra:FUN_140001030:1540] - Walks the in-memory PE header and matches each section by name hash against the disk-mapped copy.
- For any matching
.textsection, itVirtualProtects the in-memory page toPAGE_EXECUTE_READWRITE (0x40), copies the on-disk bytes over it, then reverts protection.^[ghidra:FUN_140001030:1580]
This is a direct unhooking of EDR user-mode patches in ntdll.
8. Injection — process hollowing into self
If the earlier target PID lookup succeeded:
NtOpenProcesswith access mask0x478(PROCESS_VM_OPERATION | READ | WRITE | QUERY_INFORMATION).^[ghidra:FUN_140001030:1650]NtAllocateVirtualMemoryin the remote process for decrypted payload.NtWriteVirtualMemoryto deposit the PE.- If the process handle can be duplicated and a named pipe handle discovered via
NtQuerySystemInformation(SystemHandleInformation, 0x10), the binary usesDuplicateHandleand either callsNtAlertThreadorZwSetIoCompletionto trigger execution.^[ghidra:FUN_140001030:1720]
If no suitable target exists, it falls back to hollowing itself:
GetModuleFileNameWto obtain its own path.^[ghidra:FUN_140001030:1850]CreateProcessWwithCREATE_SUSPENDED (0x8000000)and no new window.^[ghidra:FUN_140001030:1860]- Writes the decrypted PE into the suspended child and exits the parent.
9. Self-deletion
On failure of all injection paths, it self-destructs using DeleteFileW, MoveFileW, and MoveFileExW with MOVEFILE_DELAY_UNTIL_REBOOT.^[ghidra:FUN_140001030:1900]
Decompiled Behavior
Entry point (entry): decoy — Sleep(0), RtlExitUserProcess.^[ghidra:entry]
Notable functions (called from FUN_140001030):
FUN_140006280— optimizedmemset(byte then qword loop).^[ghidra:FUN_140006280]FUN_140004000— LZSS decompressor with sliding window and length/offset bit parsing.^[ghidra:FUN_140004000]FUN_140004ee0/FUN_140004ae0/FUN_140004530— stack-based string builders / decoders.FUN_1400038c0— enablesSeDebugPrivilegeviaAdjustTokenPrivileges.^[ghidra:FUN_1400038c0]FUN_140003f80— mirrorsFUN_1400038c0; appears to be a duplicate privilege-escalation stub.
Control flow patterns:
- Heavy
do { ... } whileloops for bitstream parsing in decompression. - Repeated
Sleep(1000)retry loops (10 iterations) when a target process handle cannot be obtained.^[ghidra:FUN_140001030:1100]
C2 Infrastructure
- No hardcoded IPs, domains, or URLs found in strings or
.rsrc.^[strings.txt] - Communication appears to be runtime-resolved via named pipe discovered through handle enumeration (
NtQuerySystemInformation+NtQueryObject(ObjectNameInformation)).^[ghidra:FUN_140001030:1700] - The pipe name is compared via
lstrcmpiWagainst a dynamically built string, so the pipe name itself is not static. - No PCAP available (CAPE skipped).
Mutex / named objects: None found statically.
Interesting Tidbits
- Rust
.buildidsection is present (28 bytes, entropy 0.1) with a truncated compiler-generated hash5db6a0e5.^[pefile.txt:117] This is a reliable Rust-PE detection heuristic. - VersionInfo randomization uses 11-character gibberish strings (
JECIHgAuilWg) — likely auto-generated per build.^[pefile.txt:321] - No TLS callbacks are visible (
.tlssize 0x10 and zeroed).^[pefile.txt:177] - High-entropy
.rsrc(7.66) with 10 anonymous numeric resource IDs but no RT_STRING or RT_DIALOG — consistent with encrypted blob storage.^[pefile.txt:198] - Import table is tiny — only 47 imports across 5 DLLs — the rest resolved at runtime via
GetProcAddresson stack-decrypted names.^[pefile.txt:341] - Padding artifact:
PADDINGXXPADDING...string at offset ~0xA8C0 into.textsuggests the PE was padded or post-processed after linking.^[strings.txt:168]
How To Mess With It (Homelab Replication)
Goal: replicate a dropper that extracts an embedded resource, decrypts it with a custom stream cipher, decompresses LZSS, and hollows itself.
- Toolchain: Rust stable (x86_64-pc-windows-msvc),
cargo build --release, add#![windows_subsystem = "windows"]. - Embed payload: use
include_bytes!()or a custom build script to AES/RC4 encrypt a DLL and store it as a.rsrcblob. - LZSS: implement a simple LZ77 with 12-bit length/offset fields (matching
FUN_140004000structure). - Privilege: call
OpenProcessToken+LookupPrivilegeValueW("SeDebugPrivilege")+AdjustTokenPrivileges. - Injection:
CreateProcessW(self_path, CREATE_SUSPENDED)→NtUnmapViewOfSection(or VirtualAllocEx) →WriteProcessMemory→NtResumeThread. - ntdll unhooking: read
C:\Windows\System32\ntdll.dll, walk sections,VirtualProtect+ overwrite.textfrom disk.
Verification: run under x64dbg with ScyllaHide disabled; confirm suspended child process creation and memory write to child at offset matching PE ImageBase.
Deployable Signatures
YARA rule
rule SilverFox_Rust_x64_Build {
meta:
author = "PacketPursuit"
date = "2026-05-29"
description = "SilverFox Rust x64 dropper with .buildid section and resource extraction"
sha256 = "ed1a00479fe2ea2555882c67719abc86e98b512f122aea79adacf37355cab996"
strings:
// .buildid section header in PE section table
$buildid = ".buildid"
// Custom crypto constants observed in decrypted payload routine
$k1 = { 23 FE AA CA }
$k2 = { 23 AA 57 3D }
$k3 = { 23 BB D9 44 }
$k4 = { 23 CB 37 9E }
// Padding artifact
$pad = "PADDINGXXPADDING"
condition:
uint16(0) == 0x5A4D and
filesize < 500KB and
uint16(uint32(0x3c)+0x14) == 0xF0 and // PE32+
$buildid and
2 of ($k1, $k2, $k3, $k4) and
$pad
}
Behavioral hunt query (Splunk/EQL)
(process where
(event.type:"start" or event.action:"Process Create") and
process.parent.executable == process.executable and
process.command_line_length == 0 and
process.startup_info.dwFlags : "1"
)
or
(file where
file.path : "C:\\Windows\\System32\\ntdll.dll" and
event.action:"Open" and
process.name : "*.exe"
)
IOC list
| Type | Indicator | Note |
|---|---|---|
| Hash | ed1a00479fe2ea2555882c67719abc86e98b512f122aea79adacf37355cab996 |
SHA-256 |
| Hash | 9eac33d20fe68f7700ff55e8dd63b032f64621fe6e8d3e4b94dd7e737c6c5272 |
.text section MD5 |
| File name | 2026年 第一季度违纪违规人员名单pdf.exe |
Original lure |
| Version string | JUjflFmsWFlu.exe |
Randomized per build |
| Constants | 0xcaaafe23, 0x3d57aa23, 0x44d9bb23, 0x9e37cb23 |
Stream cipher seed + round constants |
| Imports | NtAllocateVirtualMemory, NtWriteVirtualMemory, NtQuerySystemInformation, NtQueryObject, ZwSetIoCompletion |
Native API set |
| Registry | None observed | |
| Mutex/Pipe | Runtime-resolved | Discovered via NtQuerySystemInformation(SystemHandleInformation) |
Behavioral fingerprint
This executable is a stripped x64 Rust PE with a .buildid section and randomized VersionInfo strings. On launch it resolves ntdll and kernel32 APIs dynamically from stack-decrypted names, enables SeDebugPrivilege, then enumerates processes via CreateToolhelp32Snapshot. It extracts an encrypted payload from its own PE resources, decrypts it with a stream cipher seeded by image-derived entropy, decompresses via LZSS, and either injects the resulting PE into a discovered target process using NtAllocateVirtualMemory/NtWriteVirtualMemory or hollows a suspended clone of itself. Prior to injection, it restores ntdll .text from disk to remove EDR user-mode hooks.
Detection Signatures
| MITRE ATT&CK ID | Technique | Evidence |
|---|---|---|
| T1055 | Process Injection | NtAllocateVirtualMemory, NtWriteVirtualMemory, suspended child process^[ghidra:FUN_140001030:1650] |
| T1055.012 | Process Hollowing | Self-CreateProcessW with CREATE_SUSPENDED, memory write, no command line^[ghidra:FUN_140001030:1850] |
| T1562.001 | Impair Defenses: Disable or Modify Tools | ntdll .text restoration from disk via VirtualProtect + copy^[ghidra:FUN_140001030:1580] |
| T1497.001 | Virtualization/Sandbox Evasion: System Checks | Initial library load + export call gate that exits if 0^[ghidra:FUN_140001030:90] |
| T1070.004 | File Deletion | MoveFileExW with MOVEFILE_DELAY_UNTIL_REBOOT for self-deletion^[ghidra:FUN_140001030:1900] |
| T1027.002 | Obfuscated Files or Information: Software Packing | LZSS-like decompression + custom stream cipher^[ghidra:FUN_140004000] |
| T1106 | Native API | Direct Nt* syscall wrappers used throughout; no VirtualAllocEx for remote allocation^[ghidra:FUN_140001030:1650] |
References
- Artifact ID:
f8697ba9-64a6-4eb1-864c-42558acdb22b(OpenCTI) - MalwareBazaar entry: search
ed1a00479fe2ea2555882c67719abc86e98b512f122aea79adacf37355cab996 - OpenCTI labels:
silverfox,valleyrat,trojan/silverfox.bg[qtsc] - Related wiki: silverfox
Provenance
| Source | Tool | Version |
|---|---|---|
| file.txt | file |
5.45 |
| exiftool.json | exiftool |
12.76 |
| pefile.txt | pefile (Python) |
2024.8.26 |
| strings.txt | strings |
binutils 2.42 |
| rabin2-info.txt | radare2 |
5.9.8 |
| capa.txt | capa |
— (signature path error; skipped) |
| binwalk.txt | binwalk |
2.4.2 |
| Decompilation | Ghidra + r2/pdc |
Ghidra 11.2.1, radare2 5.9.8 |
| dynamic-analysis.md | CAPE | skipped (no x64 guest) |