I grabbed a fresh ValleyRAT sample off MalwareBazaar last week and it turned out to be one of the more elegant Silver Fox builds I've taken apart this year. The shipping container is an encrypted self-extracting RAR. What's inside is a tiny masterclass in abusing legitimacy: a signed launcher from a famous Electron toolchain, a signed binary from one of China's biggest game studios, and a 111 megabyte Rust DLL that does direct syscalls through a hardware-breakpoint exception handler.
Here's how it all clicks together, plus a few RE tricks worth stealing.
| Attribute | Value |
|---|---|
| Sample | renamed to sample.exe from the SHA256-named MalwareBazaar drop |
| Source | MalwareBazaar (password infected) |
| SHA-256 | 6847e6a295e8312a990752c9a0662f5757e096d3ecde43776caa2943f016da11 |
| File type | PE32 (i386) GUI, 3,947,694 bytes. Stock WinRAR SFX with a RAR5 encrypted-headers overlay starting at 0x5fc00. |
| Family | ValleyRAT (a.k.a. Winos 4.0; Silver Fox / Valley Thief / UTG-Q-1000 cluster) |
| Lab | Kali for static work, isolated Windows 10 in a snapshotted Hyper-V box for any dynamic step. No outbound network. |
TL;DR
sample.exe (encrypted RAR-SFX, password "123")
└── wudfhost.exe Squirrel.Windows StubExecutable (LetsGo Network signed)
└── app-3.16.5/wudfhost.exe NetEase Unity game player (NetEase signed)
└── app-3.16.5/UnityPlayer.dll MALICIOUS Rust loader (loader_dll.dll, 111 MB)
├── service.exe.mui custom-base64 + padded payload chunk
└── (overlay) ~83 MB AES-encrypted final-stage shellcodePersistence depends on whether you have Tencent QQ PC Manager installed. If yes, you get a quiet .lnk in Startup. Otherwise you get a system-wide scheduled task pretending to be MicrosoftEdgeUpdateHelpUpdates running on every logon as administrators. The decoy is an English-language "Professional Farm Transfer Plan.pdf" that pops up so the victim doesn't get suspicious when nothing else seems to happen.
Now the fun part.
0. Lab and rules of engagement
Before unzipping, the rules:
- Kali for static, snapshotted Windows VM for dynamic. Static covered everything below:
7z,rar2john,hashcat,binwalk,pestudiovia wine,die,radare2,LnkParse3,yara. The malicious DLL was never executed. - No outbound network on the analysis VM. Static analysis shouldn't need it, but ValleyRAT droppers happily kick off the chain on auto-extract or shell-handler bugs. Keep DHCP off and block the host firewall outbound for the VM's IP.
- Defang on disk.
chmod -xon the unpacked PE, store inside a directory calledinfected/, and resist the urge to drag-and-drop the malicious DLL into a tool that runs registered file-handlers. - Snapshot before the first byte. Every parser you point at this thing has a chance of triggering something. Roll back rather than clean.
MalwareBazaar packages samples in AES-encrypted zips with the password infected. Plain unzip(1) on Linux can't decrypt AES-zip; 7z x -pinfected file.zip does. Same trick if you grab it from a mirror that re-zips the sample.
1. The outer SFX: encrypted, but not really
The file looks like an installer. PE info gives it away as a stock WinRAR self-extractor:
PDB: D:\Projects\WinRAR\sfx\build\sfxrar32\Release\sfxrar.pdb
Resources: Lang ID 2052 (Chinese, PRC)
Compiled: 2024-02-26The RAR signature Rar!\x1a\x07\x01\x00 shows up at file offset 0x5fc00. Carve the overlay and feed it to unrar:
$ unrar l -p- payload.rar
Incorrect password for payload.rar
Archive: payload.rar
Details: RAR 5, encrypted headers
0 filesEncrypted headers means we can't even list filenames without the key. Annoying. But this is the point where the malware author and I have drastically different views on what their crypto is doing:
- They think it's protecting the payload from AV scanning at the gateway.
- I think it's a 16-byte salt and a single PBKDF2 round.
rar2john exports the hash, hashcat eats it instantly:
$ rar2john payload.rar > rar.hash
$ hashcat -m 13000 rar.hash short_pwds.txt
$rar5$16$c953d2...$15$59144421...$8$abefc909a9bd0c00:123Password: 123. Total time-to-crack: under one second on a single CPU core.
Silver Fox-family droppers have used 123, 888, 123456, 888888, Server8888, and the like for years. Whatever password is in their phishing email body is what's hardcoded in the crypto. The encryption is purely AV evasion at the perimeter, not confidentiality. Always start with hashcat -m 13000 against rockyou.txt and a 6-digit numeric mask.
The SFX comment script then reveals the entry point:
Setup=wudfhost.exe
Silent=1
Overwrite=1
Update=USilent=1 means the SFX shows zero UI, extracts to %TEMP%\RarSFX0\, auto-runs wudfhost.exe. The user just sees a PDF open on their desktop a moment later.
2. The dropped tree
RarSFX0/
├── 1.bat 2.bat 3.bat auto.vbs orchestration
├── pdf.ico 图标3.ico icon decoration
├── db.log timestamp ("2026-04-21 21:01:55")
├── service.exe.mui payload chunk #1 (181 KB)
├── 2026il5s.dat decoy PDF #2 (financial allocation)
├── 2026od8a.dat decoy PDF #3 (401k/IRA optimisation)
├── Professional Farm Transfer Plan.pdf decoy PDF #1 (the visible one)
├── WUDFHost.exe Squirrel stub
├── WUDFHost.lnk persistence shortcut
└── app-3.16.5/
├── WUDFHost.exe Unity binary (signed!)
├── UnityPlayer.dll THE LOADER (111 MB!)
├── msvcp140.dll vcruntime140*.dll real MS runtimes
├── bootfix.bat WUDFHost.lnkThe batch files are all you need to understand the orchestration:
:: the social engineering payload
copy "Professional Farm Transfer Plan.pdf" "%USERPROFILE%\Desktop\"
start "" "%USERPROFILE%\Desktop\Professional Farm Transfer Plan.pdf":: keep the implant resident
taskkill /f /im "WUDFHost.exe"
WUDFHost.exe:: AV-aware persistence
tasklist /FI "IMAGENAME eq QQPCTray.exe" | find "QQPCTray.exe" >nul
if %errorlevel% equ 0 (
copy wudfhost.lnk "%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup"
) else (
schtasks /create /tn "MicrosoftEdgeUpdateHelpUpdates" ^
/tr "C:\Windows\en-US\WUDFHost.exe" ^
/sc onlogon /ru "administrators" /rl highest /f
)That 3.bat is genuinely thoughtful tradecraft. If Tencent QQ PC Manager is running, the malware downgrades persistence to a low-noise user-Startup .lnk (which QQ tends to ignore). On clean boxes, it goes for the elevated scheduled task with a Microsoft-Edge-flavoured name. The same author shipping the same dropper to a hundred victims gets to look like two completely different threats depending on who their AV vendor is.
3. The signed-binary chain
This is where it gets clever. Three WUDFHost.exe copies, all 64-bit, all valid PEs, all signed, and only one of them is malicious. And that one isn't named WUDFHost.exe.
WUDFHost.exe (root, 32-bit)
Signer: LetsGo Network Incorporated (Squirrel.Windows owners)
PDB: C:\Users\ani\code\squirrel\squirrel.windows\build\Release\Win32\StubExecutable.pdb
Job: "Find latest app-X.Y.Z folder, run the binary inside it"
app-3.16.5/WUDFHost.exe (64-bit)
Signer: NetEase (Hangzhou) Network Co., Ltd
PDB: F:\jenkins\workspace\YaoTaiWin\artifacts\WindowsPlayer\...\WindowsPlayer_Master_il2cpp_x64.pdb
Job: "LoadLibrary UnityPlayer.dll, GetProcAddress UnityMain, jump"
app-3.16.5/UnityPlayer.dll (64-bit)
Signer: NONE
Internal name: loader_dll.dll
Job: do crimeThe first stage is Squirrel.Windows StubExecutable, exactly the same launcher you'll find in your %LocalAppData%\GitHubDesktop\ or %LocalAppData%\slack\. It's signed by LetsGo Network because LetsGo are the people who ship Squirrel. Valid Authenticode.
The second stage is a real Unity Player from a real NetEase game (YaoTaiWin in the PDB, NetEase's gaming platform), signed by NetEase with a now-expired DigiCert code-signing cert. Valid Authenticode.
The third stage, UnityPlayer.dll, is not signed. But of course it doesn't have to be. app-3.16.5/WUDFHost.exe does LoadLibrary("UnityPlayer.dll") with no path, and Windows resolves the DLL from the EXE's own directory. The malicious DLL drops in alongside. Forwarding DLL hijack via two different signed binaries from two different vendors.
A Squirrel StubExecutable.pdb PDB string plus an app-X.Y.Z\ sibling directory is one of the most reliable signals you have for sideload-driven malware in 2025 to 2026. Build a YARA rule for it and grep your corpus.
The app-3.16.5/ folder name is also pure misdirection. It looks like a Squirrel app version directory, but the wrapped Unity game has nothing to do with Squirrel. The version number is a coincidence the malware author engineered. The dropper places the Squirrel stub above an app-3.16.5/ it doesn't actually belong to, and Squirrel happily picks it up.
4. The 111-megabyte Rust loader
UnityPlayer.dll is 116,391,936 bytes. That's 111 MB of "DLL". The PE itself only has 280 KB worth of sections:
.text 0x36a00 0x37000
.data 0xa00 0x1000
.rdata 0x6c00 0x7000
...
.reloc 0x400 0x1000So 110 MB of overlay. Whatever's in there is the actual party.
The visible code is Rust:
library\std\src\panicking.rs
library\alloc\src\raw_vec\mod.rs
/rustc/ded5c06cf21d2b93bffd5d884aa6e96934ee4234/...
/rust/deps\gimli-0.32.0\src\read\abbrev.rs
/rust/deps\hashbrown-0.15.5\src\raw\mod.rs
/rust/deps\object-0.37.3\src\read\coff\section.rs
/rust/deps\addr2line-0.25.0\src\line.rs
obf_lit produced invalid UTF-8That obf_lit produced invalid UTF-8 is a panic from the popular obfstr / litcrypt family of crates that XOR-decrypt string literals at runtime. So nearly every interesting string in the binary is encrypted on disk and only materialises in RAM when a particular function is called.
The exports list is the most diagnostic thing in the whole DLL:
1 UnityMain the sideload hook
2 AddHwBp installs a hardware breakpoint
3 HandlerHwBp Vectored Exception HandlerUnityMain is what NetEase's Unity binary calls. The other two are the punchline.
The hardware-breakpoint syscall trampoline
HandlerHwBp looks like this when you skim it:
cmp dword [rdx], 0xc0000005 ; EXCEPTION_ACCESS_VIOLATION ?
jne not_us
mov rax, [rdx + 0x80] ; ContextRecord->Rip
movzx r10, byte [rax] ; opcode byte
movzx r9, byte [rax + 1] ; next byte
xor r10b, 0x0f ; if 0x0F, r10 == 0
xor r9b, 0x05 ; if 0x05, r9 == 0
or r11b, r10b
je found_syscallIt's scanning instruction bytes at the faulting address looking for 0F 05, the syscall instruction. The whole DLL's syscall dispatch goes through this VEH:
- Set DR0 to the address of a benign instruction inside
ntdll.dll. - Hit it, take a
STATUS_BREAKPOINTexception. - Walk forward from
Ripto find the realsyscallopcode. - Resolve the SSN dynamically, set up registers, and let execution continue.
This pattern (sometimes called "Hell's Hall" or "HWBP syscall") is direct-syscall delivery without ever calling the Nt* stubs that EDRs hook in user space. It's a documented Silver Fox capability and it's perfectly fingerprintable. Any DLL exporting AddHwBp and HandlerHwBp is from this family. Cheap YARA win.
Anti-sandbox: waitable timers, not Sleep
The very first thing UnityMain does is not read a file or check the environment. It does this:
QueryPerformanceCounter(&t0);
HANDLE timer = CreateWaitableTimerExW(...);
LARGE_INTEGER due = { .QuadPart = -2000000000 }; // 200 s, relative
SetWaitableTimer(timer, &due, 0, NULL, NULL, FALSE);
WaitForSingleObject(timer, INFINITE);
Sleep(2000);
QueryPerformanceCounter(&t1);
if (delta(t1, t0) too small) ExitProcess();A lot of sandboxes patch Sleep() to return immediately. Almost none of them patch SetWaitableTimer and QueryPerformanceCounter consistently. The QPC is a hardware tick counter, hard to lie about. So if the wallclock didn't advance, the loader noses out of the sandbox before doing anything compromising. Cute.
The custom base64
In the small clear-text region of .rdata, just before the path C:\Windows\en-US\service.exe.mui, there's an alphabet:
0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-/That's base64 with the digits moved to the front and + replaced by -. A standard base64.b64decode() will fail on it. A url-safe base64.urlsafe_b64decode() will fail on it. You have to translate the alphabet first or, easier, do the lookup table by hand.
This is the alphabet the loader uses to decode both service.exe.mui and its own 110 MB overlay. The overlay decodes to 87 MB of high-entropy ciphertext (entropy ~7.94). The plaintext is almost certainly an AES blob: the loader imports bcryptprimitives.dll!ProcessPrng which usually accompanies a BCryptDecrypt-class call lurking inside the obfuscated code path.
The fake .mui
service.exe.mui looks like this in a hex viewer:
00000000 50 4e 50 58 50 30 30 30 30 30 30 30 30 30 30 30 PNPXP00000000000
00000010 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 0000000000000000
... (1000+ bytes of '0' padding) ...
00000408 68 63 59 6b 4d 41 76 38 68 5a 59 6b 41 41 75 4f hcYkMAv8hZYkAAuO
00000418 68 57 59 6a 2d 41 74 65 68 54 59 6a 6f 41 73 75 hWYj-AtehTYjoAsu
...A 5-byte magic PNPXP, runs of literal ASCII 0, then chunks of custom-base64 separated by more padding. Real Windows MUI files never look like this. It's pure cosmetic disguise. file(1) reports it as ASCII text, strings shows nothing alarming, and at a glance in Explorer it looks like an obscure localisation resource you shouldn't touch.
After stripping the magic and the 0-padding, decoding with the custom base64, and decrypting with whatever key the obfuscated Rust assembles in memory, you get the next-stage shellcode. The 110 MB overlay carries the bulk RAT (the leaked-builder ValleyRAT plugin set: keylogger, audio/screen capture, BYOVD kernelquick rootkit, etc.). I haven't fully decrypted it for this writeup. The techniques and capabilities are well documented by Check Point, Zscaler and Fortinet for the post-leak builder.
When you see a "DLL" that's over 100 MB and the IMAGE_NT_HEADERS say only ~300 KB of sections, the PE structure is a fig leaf. The whole game is in the overlay. Calculate last_section.PointerToRawData + last_section.SizeOfRawData and dd skip= past it; analyse the bottom half on its own. Don't run strings on the whole 116 MB, the overlay is base64 ciphertext and you'll find nothing useful but spend minutes pretending to.
5. Pivots from the LNK
This is the part everybody forgets. WUDFHost.lnk (the persistence shortcut, dropped twice in the package) is a goldmine. LnkParse3 rips it open in three lines:
import LnkParse3
with open("WUDFHost.lnk", "rb") as f:
LnkParse3.lnk_file(f).print_lnk_file()What falls out:
| Field | Value |
|---|---|
| Target path | C:\Windows\en-US\WUDFHost.exe |
| Working directory | C:\Windows\en-US |
| Build host (NetBIOS) | desktop-jpresuo |
| Build MAC OUI | 00:0c:29: (VMware default) |
| Builder SID | S-1-5-21-4148627571-40687702-2776887718-500 |
| Volume label | 系统 ("System"), Chinese-locale Windows |
| Drive serial | 0x122dcedf |
The 00:0c:29: MAC OUI is VMware Workstation's. The -500 RID is local Administrator. The volume label is Mandarin. The malware author was operating from a Chinese-locale Windows VM, building shortcuts as Administrator, and didn't sanitise the LNK metadata before shipping.
Hashes change every build. desktop-jpresuo and that SID don't. They pivot beautifully on VirusTotal and MalwareBazaar to find sibling samples that were built on the same machine.
I can't remember the last time I analysed a malicious LNK and didn't get the build host name out of it. Threat actors keep forgetting that Windows fingerprints their dev box every time they hit Save.
What it is
This is ValleyRAT, the Winos 4.0 Remote Access Trojan, distributed by the Silver Fox / UTG-Q-1000 / "Valley Thief" cluster. It's a Chinese cybercrime operation that has been distributing through trojanised installers, SEO-poisoned downloads, and tax-themed phishing since 2022. The post-March-2025 leak of their builder kicked off an explosion of variants. Check Point counted ~6,000 new samples in the year following the leak, with ~85% in the second half of that period.
What's interesting about this sample isn't the RAT itself (the leaked ValleyRAT plugin set is well documented), it's the delivery layer. Every choice is calibrated to spend AV/EDR analyst attention on something signed and legitimate. Squirrel and Unity are not exotic. They're so boring that the typical EDR triage workflow whitelists them on sight.
Stand-out fingerprints for this build:
- Rust loader (the older Picus-documented .NET / MSBuild / 3DES delivery is gone).
- Two-tier signed-binary sideload (Squirrel + Unity, two different vendors, both expired certs).
- Hardware-breakpoint VEH syscall trampoline for direct syscalls.
- AV-aware persistence branching based on Tencent QQ Manager presence.
- Fake
.muicarrier withPNPXPmagic and0-padding cosmetic. - Custom base64 alphabet (
0-9 A-Z a-z - /).
RE tips you can steal
A summary of things that saved me time on this one and will save you time on the next:
- Crack first, RE second. Silver Fox uses trivial passwords for its encrypted SFXs. Don't waste an evening trying to decrypt headers or hunting for the password in the binary.
rar2john+hashcat -m 13000is faster than reading the SFX stub. PDB:squirrel.windows\...\StubExecutable.pdbis a smoking gun. Build a YARA rule on it andapp-X.Y.Z\directory siblings.- Check Authenticode on every PE in the drop. Knowing what's signed by whom tells you in 30 seconds which binaries are payload and which are camouflage.
.muifiles inC:\Windows\en-US\that aren't structured MUI resources are payloads. Real MUI files are tiny, structured, and never start withPNPXP.- Carve the overlay before disassembling. A 116 MB DLL with 280 KB of sections is all overlay. Don't make
stringsslog through 110 MB of ciphertext. - Rust binaries leak the toolchain, the crates, and often the obfuscation library.
obf_lit produced invalid UTF-8means they're usingobfstr/litcrypt-style string XOR; that gives you the function shape to look for. AddHwBp/HandlerHwBpis a family signature. Three Silver Fox variants in 2025 ship those exact export names. If you see them, you know what you're looking at.- VEH that does
xor reg, 0x0ffollowed byxor reg, 0x05is asyscall-finder. That's a 5-line YARA hex pattern that catches every HWBP-syscall implementation I've seen. - Run
LnkParse3on every.lnkyou find. Build host, MAC OUI, SID, volume label: all free pivots. Setup=andSilent=in a WinRAR SFX comment script tell you the entry point before you open Ghidra. Always read the SFX script first.
IOCs
Detection
A Sigma sketch (pseudo):
title: ValleyRAT WUDFHost Sideload Chain
detection:
selection_path:
Image|endswith: '\WUDFHost.exe'
Image|contains:
- '\AppData\'
- '\Temp\RarSFX'
- '\Windows\en-US\'
filter_legit:
Image|startswith: 'C:\Windows\System32\'
schtasks_persistence:
CommandLine|contains:
- 'MicrosoftEdgeUpdateHelpUpdates'
- '\Windows\en-US\WUDFHost.exe'
condition: (selection_path and not filter_legit) or schtasks_persistenceA YARA sketch:
rule ValleyRAT_HWBP_SyscallTrampoline_2026 {
meta:
author = "0xfuzz"
family = "ValleyRAT/Winos4.0"
tlp = "white"
strings:
$alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-/" ascii
$hwbp_pair = { 41 80 F? 0F 4? ?? ?? 41 80 F? 05 }
$obfstr = "obf_lit produced invalid UTF-8" ascii
$mui_path = "C:\\Windows\\en-US\\service.exe.mui" ascii
$exp1 = "AddHwBp" ascii
$exp2 = "HandlerHwBp" ascii
condition:
uint16(0) == 0x5a4d
and ($alphabet and $hwbp_pair)
and 2 of ($obfstr, $mui_path, $exp1, $exp2)
}EDR correlations worth wiring up:
- Process
WUDFHost.exerunning outsideC:\Windows\System32\, especially underC:\Windows\en-US\or%TEMP%\RarSFX*\. The legitimateWUDFHost.exeonly ever lives in System32. - Scheduled task creation for any task name beginning
MicrosoftEdgeUpdate...whose action targets a path underC:\Windows\en-US\. cmd.exerunningtasklist /FI "IMAGENAME eq QQPCTray.exe"followed within seconds byschtasks /createfrom the same parent.- Process tree where signed
WUDFHost.exe(NetEase signature) loadsUnityPlayer.dllfrom a non-Unity install path. - The final RAT plugin set beacons over HTTP/HTTPS. Once the in-memory blobs are decrypted you'll see the standard ValleyRAT
GetOnlineSize_<n>andIpDates-style traffic. Out of scope for static analysis here, but worth wiring up on a dynamic VM if you go that next step.
References
- Check Point, Cracking ValleyRAT: From Builder Secrets to Kernel Rootkits
- Nextron Systems, Thor vs. Silver Fox: Uncovering and Defeating a Sophisticated ValleyRat Campaign
- Picus Security, Dissecting ValleyRAT: From Loader to RAT Execution in Targeted Campaigns
- Zscaler ThreatLabz, Technical Analysis of the Latest Variant of ValleyRAT
- FortiGuard, A Deep Dive into a New ValleyRAT Campaign Targeting Chinese Speakers
- Malpedia, ValleyRAT (Malware Family)