139329dc9992e132f9c8d887ad685660161cefcfb0a18867d616a7d217a0605esilverfox: 139329dc9 — MSVC C++ RC4 loader with zero section names and PEB-walking API resolution
Third observed SilverFox variant: MSVC C++ x64 (not Rust), 173 KB PE32+ with a standard RC4 (not custom stream cipher) encrypted .text payload, zero-named section anti-signature, and PEB-walking runtime API resolution. Chinese-language social engineering lure masquerades as an internal staff disciplinary list.
Executive Summary
A PE32+ Windows dropper compiled with MSVC 14.16 (Visual Studio 2017/2019) on 2025-12-05. The binary carries nine sections, all with empty names, zero static imports at load time, and a deliberately corrupt PE header (zeroed checksum, fabricated LOAD_CONFIG and debug directories). A small position-independent stub in the last executable section decrypts the main .text, .data, and import table at runtime via standard RC4, then walks the PEB to resolve LoadLibraryA, GetProcAddress, and VirtualProtect, and performs its own import table fix-up before transferring control to the decrypted payload. Co-tagged valleyrat by OpenCTI; CAPE detonation was unavailable (no Windows guest).
What It Is
| Attribute | Detail | Evidence |
|---|---|---|
| SHA-256 | 139329dc9992... |
triage.json |
| File name | Chinese-language internal staff disciplinary list lure | triage.json |
| Type | PE32+ executable (GUI) x86-64, 9 sections | file.txt |
| Size | 173 056 bytes | triage.json |
| Compiler | MSVC 14.16 (C/C++) | exiftool.json:18, pefile.txt:49 |
| Timestamp | 2025-12-05 06:31:50 UTC | rabin2-info.txt:11, pefile.txt:38 |
| Stripped | Partial (symbol info absent but sections intact) | pefile.txt:39 |
| Signed | Unsigned | rabin2-info.txt:27 |
| Family | silverfox / valleyrat / trojan/silverfox.bg[qtsc] | metadata.json:8 |
| CAPE | Skipped — no Windows guest available | dynamic-analysis.md |
How It Works
Anti-analysis / build layer
The PE header is deliberately corrupted to frustrate automated parsers: checksum is zeroed, the LOAD_CONFIG directory holds impossible future timestamps (2056), and the debug directory is filled with garbage.^[pefile.txt:68,424,480] All nine section headers carry empty Name fields — a cheap anti-signature trick that breaks many tools that expect .text or .rsrc.^[pefile.txt:80] IMAGE_FILE_RELOCS_STRIPPED is set despite DllCharacteristics declaring HIGH_ENTROPY_VA.^[pefile.txt:43]
Encryption layer
The entry-point stub lives in the final executable section (sect_5, VA 0x2b000, EP at VA 0x2ca40).^[pefile.txt:54, rabin2-info.txt:9] It performs a three-stage decryption:
- Decrypt API strings — Five short strings (e.g.
LoadLibraryA,GetProcAddress,VirtualProtect) are XOR-decrypted in-place using a length-dependent key (key_byte = (index % 0x6d) + 0x92) and a flag-bit gate that triggers the decryption only if the low bit of the last byte is set.^[ghidra:FUN_14002bd60, ghidra:FUN_14002bda0, ghidra:FUN_14002bde0, ghidra:FUN_14002be20] - RC4 decrypt sections — The main code section (sect_0, 0x22C00 bytes, entropy 7.998), the key data section (sect_1), and the import/data section (sect_2) are decrypted with a 28-byte RC4 key derived from the decrypted API strings.^[ghidra:FUN_14002b360, ghidra:FUN_14002b470, r2:fcn.14002b8a0, r2:fcn.14002ba20, r2:fcn.14002bb10]
- Import fix-up — After decryption,
fcn.14002bc00walks the now-readable import directory, resolves every DLL/API pair via the previously resolvedGetProcAddress/LoadLibraryA, and patches the IAT thunk entries in-memory.^[r2:fcn.14002bc00]
API resolution
The stub does not rely on the static IAT for its own operation. It resolves needed APIs at runtime via classic PEB-walking:
GS:[0x60]→PEB.Ldr→InMemoryOrderModuleList→ enumerate entries untilntdll.dllis found by string comparison.^[r2:fcn.14002b5c0, ghidra:FUN_14002b5c0]- Export table traversal on the matched module base (
BaseDllNamecheck at offset+0x58into theLDR_DATA_TABLE_ENTRY).^[r2:fcn.14002b6c0]
Payload
Two strings in the final section suggest the decrypted payload is a DLL loaded from memory rather than a second-stage PE file on disk: Stub.dll at VA 0x14002c4b8 and g_stcParam at VA 0x14002c4cb.^[strings.txt:439-440, ghidra:search] The entry point of the decrypted main section is at offset 0x215B4 relative to the image base.^[pefile.txt:54]
Decompiled Behavior
Entry point (EP at VA 0x14002ca40) — Resides in the last executable section (sect_5, 0x1C00 bytes raw). The decompilation is heavy with opaque predicates and control-flow flattening, but the overall flow is reconstructible:
FUN_14002b5c0— PEB-walk to recoverntdll.dllbase address.^[ghidra:FUN_14002b5c0]FUN_14002b6c0— Walk ntdll exports to findGetProcAddressby comparing name hashes.^[r2:fcn.14002b6c0]FUN_14002bd60/FUN_14002bda0/FUN_14002bde0— Populate encrypted string buffers forLoadLibraryA,VirtualProtect, andGetProcAddress.^[ghidra:FUN_14002bd60, ghidra:FUN_14002bda0, ghidra:FUN_14002bde0]FUN_14002be20/FUN_14002bf40— XOR-decrypt the above strings in-place using the loopbyte = cipher[i] ^ ((i % 0x6d) + 0x92).^[ghidra:FUN_14002be20, ghidra:FUN_14002bf40]FUN_14002b8a0— Allocate stack buffer, perform RC4 KSA (FUN_14002b360) on the 28-byte key, then RC4 PRGA (FUN_14002b470) to decrypt the 0x22C00-byte.textsection in-place.^[r2:fcn.14002b8a0, r2:fcn.14002b360, r2:fcn.14002b470]FUN_14002ba20— Same RC4 routine to decrypt sect_2 (data).^[r2:fcn.14002ba20]FUN_14002bb10— Same RC4 routine to decrypt sect_1 (key / import strings).^[r2:fcn.14002bb10]FUN_14002bc00— Walk decrypted import directory and fix-up every thunk.^[r2:fcn.14002bc00]- Transfer —
call qword [image_base + 0x215B4]into the decrypted payload. Observed via xrefs in the flow.^[pefile.txt:54]
C2 Infrastructure
Static-only (no CAPE detonation). No hardcoded IPs, domains, URLs, mutexes, or named pipes were recovered from the plaintext strings or decrypted sections. The C2 mechanism lives inside the encrypted payload, which was not extracted in this analysis. The RC4 key is runtime-dependent on the decrypted API strings, making static payload extraction non-trivial without full emulation.
Interesting Tidbits
- Empty section names: All nine
IMAGE_SECTION_HEADER.Namefields are null-filled. This breaks many PE parsers and signature tools that expect.text,.rdata, etc.^[pefile.txt:80] - Corrupt metadata: LOAD_CONFIG and debug directories both contain impossible future dates and random data. The security directory is empty.^[pefile.txt:276,424,480]
- Checksum zero: Despite being an executable on a modern Windows subsystem, the optional header checksum is explicitly zeroed.^[pefile.txt:68]
- Social engineering in Chinese: The lure document title claims to be an internal list of "disciplinary violations of internal staff in Q3 2025", a classic pressure tactic in Chinese-language phishing.^[triage.json:5]
- No standard capa output: Capa failed to run (missing signatures directory).^[capa.txt] Floss also failed due to incorrect invocation flags.^[floss.txt]
Stub.dll+g_stcParam: The presence of these two strings strongly implies the decrypted payload is expected to be a DLL with a global parameter structure — a pattern seen in other reflective DLL loaders.^[strings.txt:439-440]
How To Mess With It (Homelab Replication)
Goal: Produce a minimal PE with encrypted .text and PEB-walking API resolution.
- Toolchain: MSVC 2019 (v142) or MSVC 2022 (v143), x64 Release,
/O2 /GS-. - PE layout: Create a PE with two extra sections after
.text:.key(holds the RC4 key) and.stub(holds the decryptor stub). Mark.textasMEM_READ | MEM_WRITEso the stub can decrypt it in-place. - RC4 key: Pick any 28-byte string; store it in
.key. In the stub, copy the key to the stack and run standard KSA + PRGA over.text. - PEB walking: Use
gs:[0x60]chain as documented at peb-walking-api-resolution. ResolveGetProcAddressandLoadLibraryAby walkingntdllexports. - Import fix-up: After decryption, parse the now-readable import directory and patch thunks with resolved addresses.
- Anti-signature: Zero all section names in the header and corrupt the checksum / debug timestamps.
- Verification: Run your reproducer in a debugger, set a breakpoint on the decrypted payload entry-point, and confirm execution flows into decrypted code.
Deployable Signatures
YARA
rule silverfox_rc4_loader_2025 {
meta:
family = "silverfox"
description = "SilverFox RC4 loader: MSVC C++ x64, zero section names, encrypted sections, Stub.dll reference"
author = "PacketPursuit SOC"
date = "2026-06-04"
sha256 = "139329dc9992e132f9c8d887ad685660161cefcfb0a18867d616a7d217a0605e"
strings:
$stub_dll = "Stub.dll" ascii wide nocase
$g_stcParam = "g_stcParam" ascii wide nocase
$pcnypr = "pcnypr" ascii wide nocase
condition:
uint16(0) == 0x5a4d and
pe.number_of_sections == 9 and
for all i in (0 .. pe.number_of_sections - 1) : (
pe.sections[i].name == ""
) and
(
$stub_dll or
$g_stcParam or
$pcnypr
) and
pe.sections[0].entropy > 7.8 and
filesize < 500KB
}
Behavioral fingerprint
This binary has zero section names, zero checksum, and impossible future LOAD_CONFIG/debug timestamps. On execution, a small stub in the final section walks the PEB (
gs:[0x60] → Ldr → InMemoryOrderModuleList) to resolveGetProcAddress, then decrypts three RC4-encrypted sections (the first with entropy >7.9) using a 28-byte key derived from XOR-obfuscated API strings. It then walks its own import directory, resolving every API via the dynamically obtained resolver, and finally jumps to offset0x215B4of the decrypted.text. No disk write of the payload is expected; the DLL is prepared purely in memory.
IOC list
| Indicator | Type | Value | Static evidence |
|---|---|---|---|
| SHA-256 | Hash | 139329dc9992e132f9c8d887ad685660161cefcfb0a18867d616a7d217a0605e |
metadata.json |
| Filename | String | 2025年第三季度内职人员违纪名单信息,请大家使用内网电脑查看(文件切勿外发).exe |
triage.json |
| Compilation | Timestamp | 2025-12-05 06:31:50 UTC |
pefile.txt:38 |
| Decrypted API key pattern | Behavior | XOR loop: plain[i] = cipher[i] ^ ((i % 0x6d) + 0x92) |
ghidra:FUN_14002bf40 |
| RC4 key length | Config | 28 bytes | r2:fcn.14002b8a0 (r8d=0x1c) |
| Payload entry offset | Config | 0x215B4 relative to image base |
pefile.txt:54 |
| Stub DLL marker | String | Stub.dll |
strings.txt:439 |
| Global param marker | String | g_stcParam |
strings.txt:440 |
Detection Signatures / ATT&CK Mapping
| ID | Technique | Implementation | Evidence |
|---|---|---|---|
| T1027.002 | Obfuscated Files / Information | RC4-encrypted .text with entropy 7.998 |
pefile.txt:95 |
| T1055 | Process Injection | Reflective DLL preparation in decrypted .text; imports resolved dynamically |
Static inference from stub strings and flow |
| T1106 | Native API | Direct Nt* API strings (NtClose, NtDuplicateObject, NtQueryInformationProcess, NtRemoveProcessDebug) in decrypted imports |
strings.txt:282-286 |
| T1497.001 | Sandbox Evasion | NtRemoveProcessDebug, DbgUiSetThreadDebugObject, IsDebuggerPresent strings present |
strings.txt:286-288, strings.txt:366 |
| T1027 | Obfuscated Files or Information | Zero section names, fabricated PE directories, zeroed checksum | pefile.txt:43,68,80 |
References
- entities/silverfox — Family page with Rust and C variant fingerprints
- techniques/peb-walking-api-resolution — Detailed PEB-walking implementation
- OpenCTI artifact — Source label
silverfox/valleyrat
Provenance
- File identification:
filev5.45 —^[file.txt] - PE structural analysis:
pefile(Python) —^[pefile.txt] - Header summary:
radare2 rabin2—^[rabin2-info.txt] - Metadata:
exiftoolv12.76 —^[exiftool.json] - String extraction:
strings—^[strings.txt] - Decompilation:
Ghidra(pyghidra MCP) + radare2pdc— provenance markers in-line - Static provenance: all claims trace to
raw/analyses/139329dc9992e132f9c8d887ad685660161cefcfb0a18867d616a7d217a0605e/*