Malware

Inside Silver Fox, Part 3: From DLL Sideloading to ValleyRAT

The final user-mode link of the Silver Fox chain: the legitimately signed NtHandleCallback.exe side-loads a malicious log.dll that spawns three parallel threads — RC4-decrypting and running ValleyRAT in memory, deploying two kernel drivers for anti-forensics, and maintaining the Defender exclusion. ValleyRAT hides its C2 config and plugins in the HKCU\Console registry, so deleting files or blocking the C2 alone does not clean the host.

Dinlon · Published

Regions China · Taiwan · Japan · Korea · India · APAC Industries Government · Critical infrastructure · Healthcare · Education & research · Finance · E-commerce · Gaming · Cybersecurity Platforms Windows Attribution high
actor Silver Fox malware ValleyRAT malware Gh0st RAT technique DLL side-loading

Key findings

  • NtHandleCallback.exe is a legitimately signed application used as the white-plus-black sideload host; on disk the whole chain looks like a legitimate program running a legitimate DLL.
  • The malicious log.dll spawns three parallel threads: T1 decrypts and runs ValleyRAT in memory, T2 deploys two anti-forensic kernel drivers, and T3 maintains the Defender exclusion path.
  • Server.log is the RC4-encrypted ValleyRAT core; decrypting it with the key ??Bid@locale@std yields the online module (internally named 上线模块.dll), which runs via memory mapping and never touches disk.
  • ValleyRAT uses two registry values as persistence containers: HKCU\Console\IpDate can dynamically override the C2 config, and a 32-hex value under HKCU\Console\0 stores the plugin binary — so deleting files or blocking the C2 alone does not clean the host.
  • The logic layer still follows Gh0st RAT's first-byte token plus CManager dispatch design, but the network layer has dropped the Gh0st magic and zlib, switching to a 14-byte custom header with a UDP/ARQ backup channel to evade traditional Gh0st detection.

Silver Fox Part 3 cover (AI-generated)

Background

In the previous part we saw Silver Fox use men.exe to set the stage: landing components, killing AV via BYOVD, modifying DACLs to resist deletion, and arranging a logon trigger. This part explains how, once the stage is set, the real ValleyRAT is loaded, checked in, maintained, and protected. The sample analyzed here:

Sample information for this analysis

This report is intended only for defensive research, incident response, threat hunting, and educational purposes. The samples and tools referenced may be dangerous; test them only in isolated, restorable environments that are not connected to a production network. This report does not encourage, assist, or condone any unauthorized activity; third-party platform and product names are used only for technical description.

NtHandleCallback.exe: the DLL side-load host

NtHandleCallback.exe is not malware in itself. It is a PE32 application compiled 2020-03-11, signed by Hangzhou Shunwang Technology Co.,Ltd via DigiCert. Shunwang is a Chinese company making internet-café management software and game cloud services; after 2023 this certificate flowed heavily into the Chinese-language crime ecosystem and has been used to sign many malicious programs. Here, though, the attacker did not crack the signature — they simply copied this legitimate program over to use as the host that side-loads log.dll.

This EXE is legitimately signed, has a GUI resource section (.rsrc ~43 KB), and enables Control Flow Guard — an unremarkable commercial application. Its real tie to the attack chain is in the Import Table:

NtHandleCallback.exe's Import Table, pointing at log.dll!GenericLogImpl

Note the third-party function log.dll!GenericLogImpl in its IAT. Windows’ DLL search order puts the application directory ahead of the system directory, so as long as log.dll sits in the same folder as the host, on launch the PE loader maps it into the process, resolves the import, and calls GenericLogImpl. From here on, the flow is log.dll’s logic and has little to do with the legitimate EXE.

log.dll: the three-thread orchestrator

log.dll is a PE32 with 5 sections, ImageBase 0x10000000, and a size of about 176 KB. Its export table has a single symbol:

log.dll export table, with only GenericLogImpl

All the malicious behavior sits in GenericLogImpl. The disassembly is easy to follow:

GenericLogImpl disassembly

Three threads run in parallel, and the main caller does not return until all three are joined. This makes NtHandleCallback.exe look like it is merely stuck in a logging function, while in the background three things are already done:

log.dll spawning three parallel threads T1/T2/T3

We dissect these three threads below, but most of the length goes to the ValleyRAT analysis in T1. The entry points and duties of the three threads:

ThreadEntry pointDuty
T1StartAddress @ 0x10003EE0 → sub_10003BB0Decrypt and run the ValleyRAT body
T2sub_10003DA0Deploy malicious kernel drivers for anti-forensics
T3sub_10003EF0Maintain the Defender exclusion path

T1: Server.log decryption and ValleyRAT check-in

This is the main act. We first dissect how T1 decodes Server.log into ValleyRAT’s PE, then dig into the decrypted PE — its config-hiding tricks, C2 startup path, and relationship to Gh0st RAT.

RC4 decryption and memory loading

sub_10003BB0 (the T1 function) aims to decrypt the encrypted ValleyRAT core file Server.log and execute at that memory location:

strcpy(local_v3, "??Bid@locale@std");        // RC4 key, 16 bytes
fp   = fopen("Server.log", "rb");            // ciphertext in the same folder
size = ftell(fp);
buf  = malloc(size);
fread(buf, 1, size, fp);

rc4_decrypt(buf, size, local_v3, 16);        // FUN_10004CD0 — standard RC4
pe    = map_pe(buf);                          // FUN_100053D0 — custom PE loader
entry = find_export(pe, "NtHandleCallback");  // FUN_10005200
if (entry) entry();                           // call the export directly

The whole chain never lands a file; once decrypted it maps straight to memory, resolves exports, and jumps in. ValleyRAT’s body never exists as a file on disk. Even though the file never lands at runtime, in reversing we can still decrypt the ciphertext with the password — dropping it into CyberChef and using RC4 with ??Bid@locale@std recovers it (this password was reused for nearly five months across the series, while the C2 rotated far more often):

Recovering Server.log to a PE with RC4 in CyberChef

The first 4 bytes are 4D 5A 90 00 (MZ...), essentially confirming a PE; PEBear shows NtHandleCallback among the exports, so we save it as valleyrat.dll for analysis.

The ValleyRAT online module

RC4-decrypting Server.log yields a PE32 DLL whose internal name is 上线模块.dll (“check-in module”), and whose exports include the NtHandleCallback that log.dll looks up and calls. So Server.log hides ValleyRAT’s online module inside it, and the earlier T1 flow simply decrypts it from ciphertext, maps it to memory, and hands off execution.

The decrypted PE's internal name, 上线模块.dll

This “online module” can be understood as ValleyRAT’s control entry point. It first reads communication settings, connects to the C2, then loads later-stage plugins per C2 commands. Because the capabilities actually enabled are decided by the C2, the same ValleyRAT can look different across incidents — some show only a basic connection, others go further with screenshots, keylogging, file operations, remote shell, or other plugin behavior.

The ValleyRAT / Winos 4.0 core still shows Gh0st RAT’s shadow. The logic layer keeps the CManager-based class architecture and the command-token dispatch design; the network layer, though, is clearly reworked — the Gh0st magic and zlib compression are gone, replaced by a custom header, a TCP channel, and an extra UDP/ARQ backup design. This keeps Gh0st’s operating model while sidestepping some detection rules written for the traditional Gh0st protocol.

Abstract flow of 上线模块.dll and the core module

Basic info of the decrypted PE:

FieldValue
TypePE32 DLL (i386, 32-bit)
Size111104 (108 Kb)
MD5016d12cd1f6c68db98fcdde6075e9b71
SHA2565decc64bc4ed4d6ffd3f59be2e839fe5cc70eaa3f07191d77ac241d028e1c6f0

The RTTI classes reveal something interesting:

.?AVCManager@@                        ← CManager (base manager)
.?AVCKernelManager@@                  ← CKernelManager (kernel manager)
.?AVISocketBase@@                     ← ISocketBase (socket base)
.?AVCTcpSocket@@                      ← CTcpSocket
.?AVCUdpSocket@@                      ← CUdpSocket
.?AV?$CArqSessionT@VCUdpSocket@@V1@@@ ← CArqSession<CUdpSocket> (ARQ over UDP)
.?AVCBuffer@@                         ← CBuffer
.?AVCAtlException@ATL@@               ← ATL exception

These names roughly separate three layers: CManager / CKernelManager are the control logic and command-dispatch core; CTcpSocket / CUdpSocket / ISocketBase are the network abstraction; and CArqSessionT<CUdpSocket> hints at a reliable transport built on UDP. This UDP/ARQ class does not exist in native Gh0st RAT and better matches the Winos 4.0 series’ reworked network layer.

The online module&#x27;s PE structure and exports, exports include NtHandleCallback

From a detection standpoint, rules written only for the traditional Gh0st magic or fixed TCP behavior may miss such reworked-network variants. Better features to watch include the class architecture, the 14-byte header, the custom handshake, the registry plugin container, and the outbound behavior after injection into tracerpt.exe.

Config read and storage

Once running, ValleyRAT first initializes its communication settings. The sample hardcodes a UTF-16 string, but the whole thing is reversed; on init it flips the string back and parses it into |-separated fields expressed as key:value. This obfuscation only reverses character order and is not hard to analyze, but from 2025 through April 2026 all of Silver Fox’s ValleyRAT stored C2 info this way — showing that even an easy-to-analyze scheme is offset by fast C2 rotation and a multi-stage delivery chain. The config recovered from this sample:

|p1:ydbao8[.]cyou|o1:9000|t1:1|p2:|o2:|t2:1|p3:|o3:|t3:1|dd:1|cl:1
|fz:LINE|bb:1.0|bz:2025. 8.31|jp:1|bh:0|ll:0|dl:1|sh:1|kl:1|bd:0|

How the C2 config fields map to attack-chain behavior

Most notable is that this config can also be overridden from the registry. On init ValleyRAT reads HKCU\Console\IpDate; if that value exists and meets a length condition, the sample uses its contents instead of the hardcoded config. So the C2 can push a new config after the first successful connection, writing it to that value, and the RAT switches to the new target on next start.

This matters for incident response: blocking the domain only handles the current C2 baked into the sample. If HKCU\Console\IpDate already holds updated infrastructure, the RAT may reconnect after a restart. A C2 IOC is a point-in-time snapshot; the dynamic config in the registry reflects the attack state left on the endpoint. In our experience, attackers update the C2 location immediately after the first successful connection to create an analysis breakpoint.

HKCU\Console\IpDate dynamically overriding the C2 config

Registry plugin container: later-stage modules

Beyond the C2 config, ValleyRAT also uses HKCU\Console as local storage for plugins — one of this sample’s most important designs. We observed another registry value:

HKCU\Console\0\d33f351a4aeea5e608853d1a56661059

Note the path is the Console\0 subkey, not Console itself; the single-character subkey name 0 may itself be an anti-forensics touch that an analyst skims past as a placeholder. The value name is a 32-char hex that looks like an MD5 or auto-generated identifier, easy to mistake for an ordinary hash on manual review — but it is actually the plugin binary’s storage container.

On first deployment the C2 pushes a plugin config plus payload; ValleyRAT writes the “config + PE” together into that value, so even after a restart it can reload and execute without re-downloading. The effect goes beyond “in the registry rather than the file system”:

  • The plugin does not appear on disk as a file, lowering visibility for defenses that rely only on file scanning or new-executable monitoring.
  • The attacker reduces dependence on the C2’s immediate availability — as long as the plugin remains in the registry, the RAT can replay later-stage capabilities from the local host after a restart.
  • The plugin may carry victim-binding logic — after reading it back, the sample compares hostname or UID and reports an anomaly to the C2 if it does not match, hinting at per-victim customization.

During response, deleting only NtHandleCallback.exe, log.dll, or blocking the current C2 may still leave this registry plugin container. The HKCU\Console area is therefore a priority to check during cleanup and forensics.

C2 connection: retries and fallback

ValleyRAT’s parameters default to multiple slots and support TCP and KCP (ARQ over UDP). In this sample the main C2 is ydbao8[.]cyou on 9000/tcp. When the main C2 fails, the sample switches to the backup set, and if failures keep accumulating it escalates to a third set — resisting single-domain blocks, sinkholes, analysis-environment blocking, or brief infrastructure outages.

C2 connection retries and fallback

After connecting, the sample sends a very short handshake and enters a receive loop, handing later packets to CKernelManager. From that moment CKernelManager becomes the control core on the victim, deciding per C2 command whether to ignore the heartbeat, install a plugin, read back the registry plugin, or update local settings. Technically Gh0st RAT’s command-dispatch spirit is still here, but the outer protocol has been rewritten — the sample uses a custom 14-byte header in place of Gh0st’s usual magic and compression.

Plugin deployment

CKernelManager can be seen as the online module’s minimal dispatcher: it does not provide full RAT features directly, but decides three things per C2 packet — ignore the heartbeat, install a plugin for the first time, or wake an existing plugin from the registry. The real post-exploitation power is not at this layer but in the main control module later injected into tracerpt.exe. The main dispatcher (0x100054C0) takes three paths based on two simple signals, the packet’s first byte and total length:

Path 1: heartbeat — when the first byte is 0xC9 it is treated as a heartbeat and returns immediately. This token is a clear difference between ValleyRAT and Gh0st at the dispatch layer: Gh0st’s heartbeat token is 0x01; here it is deliberately changed to 0xC9 to evade Gh0st detection rules keyed on “first-byte == 0x01”.

Path 2: first deployment — when the packet size is not 101 it goes to first deployment. The front (from pkt+1, 0xA44 = 2628 bytes) is the plugin config, the back is the payload PE. It allocates and memcpy’s the payload PE to memory, then writes the same “config + PE” via RegSetValueExW to HKCU\Console\0\d33f351a4aeea5e608853d1a56661059, and immediately _beginthreadex(sub_100052B0) starts the injector thread — the needle fill, the write to HKLM\SOFTWARE\IpDates_info, and the tracerpt.exe process hollowing are all handled by that thread.

First deployment and injection flow

Path 3: wake / replay — when the packet size is exactly 101 (RAT restart or C2-triggered wake). It opens HKCU\Console\0, reads the previously stored config + PE back to memory, then does host-fingerprint validation: on match it enters the injection flow to push the plugin back into tracerpt.exe; on mismatch (meaning the plugin came from another machine) it returns the response packet prefixed with 0x05 plus the full config to the C2, telling the server this machine rejects it.

All three paths share one plugin staging area and one injector entry (sub_100052B0); CKernelManager itself handles no keylog/shell/screenshot feature commands — those are handled by the plugin’s own dispatcher inside the tracerpt.exe process once the plugin runs.

Plugin injection and startup: self-patch and tracerpt.exe hollowing

Before actually running the plugin, ValleyRAT writes the current config back into it. The sample searches plugin memory for a fixed marker ihziepulgned, which reversed is dengluplugin (pinyin for “register plugin”). Once found, ValleyRAT overwrites the current config block into the plugin — a plugin self-patch: the plugin body need not hardcode the full C2 and victim settings; the online module injects the current config before load.

The sample then chooses an execution mode by the config’s kl flag (offered on the server as a checkbox for “puppet process”). Here kl is 1, so it takes puppet mode: it creates a suspended tracerpt.exe, writes the RAT body into it, and rewrites the main thread context so the payload runs from inside tracerpt.exe — classic process hollowing. tracerpt.exe is a built-in Windows ETW trace-report tool with a Microsoft signature; the attacker picks it as the hollow target so a signed, system-path, plausibly named process carries the RAT behavior.

The sample also has a watchdog: it periodically checks whether the hollowed tracerpt.exe is alive, and if the process is terminated by EDR or by hand, it recreates and re-injects a new tracerpt.exe. Simply killing the process does not end the infection — it triggers a rebuild of the comms process. If kl is 0, the sample runs in-place inside the current NtHandleCallback process; this mode is less stealthy but leaves fewer behaviors, and in long-term observation attackers occasionally choose it, but rarely.

Plugin behavior

After obtaining and analyzing the attacker’s plugin, we found a plugin named “plugin DDDD” being loaded. This plugin is ValleyRAT’s feature core — commonly named “main control module” on the server — providing 27 command tokens including keylog, screenshot, shell, files, shutdown/reboot, and event-log wiping.

We recovered one plugin copy each from the infected host’s memory and registry: the memory version dug out of the infected process’s unbacked region by HollowsHunter, and the registry version taken directly from the REG_BINARY holding the plugin binary under HKCU\Console\0. Of the shared 530,944 bytes, only 74 bytes differ, all in the plugin’s .data section — victim runtime state written after the plugin runs (likely a working directory or session token). So it is the same PE: one a clean sample at deploy time, the other a live sample that has run and carries the victim’s fingerprint.

Opening the plugin’s RTTI strings shows CLoginManager, CManager, CTcpSocket, ISocketBase, but CKernelManager is absent. This suggests the plugin is not an extension of the online module but a separate manager class inheriting from CManager, parallel to the online module. Its work loop has its own socket construction (dual TCP + UDP/ARQ), its own 200-cycle p1/p2/p3 C2 failover, its own registry persistence (this time the key is HKCU\Console\Ipcial, alongside the online module’s IpDate), and even its own reimplemented C2 handshake and recv loop. Architecturally the plugin is more like a “main control module” — a standalone, fully featured next-stage RAT rather than a traditional capability plugin loaded later.

Pulling the first vtable slot of CLoginManager reveals a clean switch-table dispatcher:

movzx eax, byte ptr [esi]         ; take the packet's first byte as the token
sub   eax, 0x14                   ; offset to 0
cmp   eax, 0xb6                   ; range check
ja    default_handler
movzx eax, byte [eax+0x100262a8]  ; index table -> case slot
jmp   dword [eax*4+0x10026238]    ; dispatch via jump table

Disassembly of the plugin&#x27;s command-dispatch switch table

The token range is [0x14, 0xCA], and the jump table has 28 slots (27 valid commands + 1 default) — Gh0st’s command dispatch is written the same way. Compared with the previous section’s CKernelManager, which had only 3 paths, the plugin’s dispatcher returns fully to Gh0st-style full-feature design. The interesting part: ValleyRAT already stripped Gh0st’s original 5-byte magic and zlib at the online-module layer, replacing them with a 14-byte custom header and dual comms channels (presumably to add a backup connection and incidentally bypass old WAF rules for Gh0st); yet at the command-execution layer, the dispatch mechanism and class architecture are fully preserved, with modern additions like BCrypt AES — so this plugin is a heavily reworked Gh0st architecture.

In practice the 27 commands group into 8 feature categories, each mapping to 1–3 token variants:

The 27 commands grouped into 8 feature categories

In other words, this plugin covers every basic capability a modern RAT should have — from reconnaissance, control, and persistence to post-op cleanup. Once the attacker has a machine, anything they want to do needs no new plugin; this alone is enough. Five commands I find especially notable:

Five especially notable commands

0x22 ClearEventLogs is especially painful for response. It passes three wide strings Application / Security / System to OpenEventLogW in turn and then immediately ClearEventLogW, wiping the three most-examined event sources at once. What the IR team sees afterward is a clean EventLog — no logon records, no service-start records, no system errors. To counter it, use Sysmon to forward key events to a central SIEM in real time, or enable Windows Event Forwarding, so even if local logs are wiped, IR can still see the full timeline from the remote copy.

T2: anti-forensic kernel driver deployment and PID protection

This thread’s goal is to enable malicious kernel drivers and add the ValleyRAT and other tools that T1 runs to the protection list, blocking forensic tools from analyzing them. It does, in order:

  • Wait for NtHandleCallback.exe to get a PID
  • Deploy Cndom6.sys, create a service, and open \\.\Cndom6 (for IOCTL registration)
  • Send Cndom6 an IOCTL to register the loader group’s protected PIDs
  • Wait for tracerpt.exe to get a PID
  • Deploy XiaoH.sys, create a service
  • Send Cndom6 an IOCTL to register the tracerpt group’s protected PIDs
  • Check whether NVIDIA.exe is running, and WinExec it if not
  • Send Cndom6 an IOCTL to register the NVIDIA group’s protected PIDs

The three protected groups map to three key roles in the chain: NtHandleCallback.exe is this part’s white-plus-black host, tracerpt.exe is the puppet process ValleyRAT injects into, and NVIDIA.exe is the BYOVD AV-killer component from Part 2.

The two drivers carved out of log.dll

Tracing the write-file callers locates two binaries embedded in log.dll:

DriverRVAFile OffsetSizeBits
Cndom6.sys0x230900x2129023,616 bytes (0x5C40)x86-64 (PE32+)
XiaoH.sys0x28CD00x26ED010,808 bytes (0x2A38)x86-64 (PE32+)

Both start as standard PE+ files. log.dll is 32-bit, but it carries two 64-bit drivers; this is not a problem for user-mode loading — the service just uses CreateService plus StartService, the system completes the load, and the driver file is then deleted. For analysis we only need to dump them. How these drivers complete anti-forensics via kernel mechanisms is detailed in the next chapter.

Cndom6 IOCTL interface

In this deployment log.dll T2 used only four IOCTLs (0x222180, 0x22218C, 0x221E60, 0x222190), but fully reversing Cndom6 shows it exposes six IOCTLs plus a master flag:

Cndom6&#x27;s exposed IOCTL interface

From the call order we can infer Cndom6’s external design: the attacker splits PID-protection needs into several independent IOCTLs, each mapping to a different kernel hook or PID list. Another notable anti-forensics touch — after loading, Cndom6 dereferences its own image page sections to delete its .sys file from disk, so after log.dll T2 deploys it, no matching Cndom6.sys file can be found on the endpoint.

XiaoH IOCTL interface

XiaoH also exposes an IOCTL interface: Cndom6 hides and protects PIDs (so a process is invisible and unkillable), while XiaoH hides network connections (so netstat/EDR cannot see the C2 connection). Together they form a complete Ring 0 anti-forensics chain. XiaoH exposes four IOCTLs:

XiaoH&#x27;s exposed IOCTL interface

Its hide-list and whitelist are complementary containers:

XiaoH&#x27;s hide-list and whitelist containers

“Replace with a fake connection” is where XiaoH is more special than a typical rootkit: it rewrites the target connection into a structurally valid fake one, swapping the target IP for 1.2.3.4 and the port for a placeholder 0x3930 (14640), so queries return a list with the right count but no C2 in it. Another detail: XiaoH’s 0x221E60 IOCTL code is the same as Cndom6’s 0x221E60 (protect the tracerpt group) but maps to a different device namespace, so the T2 caller must open the right \\.\XiaoH or \\.\Cndom6 for the IOCTL to reach the correct driver.

T3: maintaining the Defender exclusion

T3’s code is short — the core is a PowerShell loop:

while (!is_terminate_signal()) {
    run_powershell(
        "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe",
        "-NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -Command "
        "\"if ((Get-MpPreference).ExclusionPath -notcontains "
        "'C:\\\\Users\\\\Public\\\\Pictures') "
        "{ Add-MpPreference -ExclusionPath 'C:\\\\Users\\\\Public\\\\Pictures' }\""
    );
    Sleep(500);
    if (is_terminate_signal()) break;
    Sleep(1000);
}

The goal is simple: even if someone manually removes C:\Users\Public\Pictures from the Defender exclusion list, this loop re-adds it within 500 to 1000 ms. The if ... -notcontains is an idempotent guard, so repeated runs produce no extra system events.

There is an interesting case: if the Defender service has already been killed outright by NVIDIA.exe with NSecKrnl, Get-MpPreference fails and the loop spins in the background — occupying a thread but doing nothing. T3 is usually of no real use, meaningful only while Defender is alive. Recall that in Part 1 the Inno Setup install script already added the entire C: drive to the exclusion list, so why do it again? Presumably because this tool is reused in other delivery chains, or simply as double insurance.

Technique summary

We break the behavior of NtHandleCallback.exe, log.dll, the ValleyRAT online module, and the later-stage plugin into multiple techniques; the per-technique mapping to ATT&CK is in the MITRE ATT&CK mapping table at the end. The plugin command handlers represent capabilities the sample possesses, which does not necessarily mean every command was triggered in this incident.

Technique summary matrix for NtHandleCallback.exe / log.dll / ValleyRAT

Conclusion

Looking back at this chain, NtHandleCallback.exe has little role of its own — more of a chosen legitimate shell. The real executor is log.dll!GenericLogImpl: a very short function that pulls three threads at once — T1 decrypts Server.log and wakes ValleyRAT, checking in and obtaining plugins; T2 deploys kernel drivers to protect key processes; T3 keeps re-adding the Defender exclusion. That is why this chain is easy to underestimate: the entry looks ordinary, yet loading, persistence, and anti-forensics are already handled separately behind it.

The most distinctive thing here is the HKCU\Console activity: IpDate stores the C2 config, and that 32-char hex value under Console\0 stores the plugin body. Even after deleting NtHandleCallback.exe and log.dll and blocking the hardcoded C2, as long as the config and plugin remain in the registry, the machine cannot be considered clean.

Recovering the later-stage plugin also clarifies ValleyRAT’s relationship to Gh0st: the outer online module has dropped the Gh0st magic and zlib for a custom 14-byte header with a UDP/ARQ backup; but in the command-executing plugin, the first-byte token, CManager, and switch table — Gh0st-style designs — remain. It keeps Gh0st’s control habits, then repackages delivery, comms, and survival once more.

Part 3 stops at the user-mode line for now. The next part pulls the view down to the Cndom6.sys and XiaoH.sys deployed by T2, to see how the malicious drivers do PID protection, connection hiding, and anti-forensics in the kernel.

Sample and references

MITRE ATT&CK mapping

TacticTechniqueProcedure
Defense evasionT1574.002NtHandleCallback.exe is a legitimately signed host that side-loads same-folder log.dll!GenericLogImpl via its Import Table
Defense evasionT1553.002Uses a legitimately signed third-party commercial program (Hangzhou Shunwang
Defense evasionT1036.005Names like log.dll
Defense evasionT1027The online module is stored as RC4 ciphertext in Server.log
Defense evasionT1027.009log.dll embeds two 64-bit drivers
Defense evasionT1140T1 decrypts Server.log with the RC4 key ??Bid@locale@std to recover the online module 上线模块.dll
Defense evasionT1620The decrypted online module never lands on disk; log.dll allocates
Command and controlT1105After connecting to C2 it receives later-stage plugins and writes them to the Registry; DownloadExec-style commands can pull and run arbitrary binaries
Defense evasionT1112Uses HKCU\Console\IpDate to override the C2 config and HKCU\Console\0\d33f351a4aeea5e608853d1a56661059 to store the plugin binary
Defense evasionT1055.012When the kl flag enables puppet mode
Command and controlT1095The online module and plugin use a custom 14-byte-header packet format for C2
Command and controlT1571Hardcoded main C2 uses ydbao8[.]cyou on the non-standard port 9000/tcp
PersistenceT1543.003T2 deploys Cndom6.sys and XiaoH.sys
Defense evasionT1014Cndom6.sys protects and hides processes/handles while XiaoH.sys hides network connections
Defense evasionT1562.001T3 repeatedly uses PowerShell to check and maintain the Defender exclusion for C:\Users\Public\Pictures
ExecutionT1059.001Runs powershell.exe hidden with Get-MpPreference/Add-MpPreference to manipulate Defender settings
DiscoveryT1007The later-stage plugin can enumerate services to collect host service state
CollectionT1056.001The plugin has a keylogging command handler and can start a thread to capture input
CollectionT1113The plugin can capture the screen on C2 command
ExecutionT1059.003The plugin can run background shell commands on C2 command
Defense evasionT1070.001The plugin's 0x22 ClearEventLogs clears the Application
ImpactT1529The plugin can force reboot
Defense evasionT1564Spreads malicious state across an encrypted file

Indicators of compromise

TypeIndicatorFirst seenLast verifiedConfidenceStatus
Domainydbao8[.]cyouValleyRAT main C2 (ydbao8[.]cyou on 9000/tcp)2025-092026-05-14HighACTIVE
SHA2565decc64bc4ed4d6ffd3f59be2e839fe5cc70eaa3f07191d77ac241d028e1c6f0上线模块.dll — the ValleyRAT online module recovered by RC4-decrypting Server.log (memory-loaded, never on disk)2025-092026-05-14High
MD5016d12cd1f6c68db98fcdde6075e9b71上线模块.dll — same file as above, ValleyRAT online module (MD5)2025-092026-05-14High
RegistryHKCU\Console\IpDateDynamic C2-config override; after the first successful connection the C2 can push a new config here, and the RAT uses it on next start2025-092026-05-14High
RegistryHKCU\Console\0\d33f351a4aeea5e608853d1a56661059REG_BINARY container for the plugin (config + PE); a priority location for cleanup and forensics2025-092026-05-14High
RegistryHKCU\Console\IpcialThe later-stage plugin's (main control module) own persistence key2025-092026-05-14High
RegistryHKLM\SOFTWARE\IpDates_infoMarker value written by the injector thread2025-092026-05-14Moderate
String??Bid@locale@stdRC4 key that decrypts Server.log (reused across the series for nearly five months)2025-092026-05-14High
StringihziepulgnedPlugin self-patch marker (reverses to dengluplugin); once found, the current config is overwritten in2025-092026-05-14High
PathC:\Users\Public\PicturesWindows Defender exclusion path that T3 keeps re-adding2025-092026-05-14High

OIS-2026-007 · TLP:CLEAR under FIRST TLP 2.0 · Cite this research with its report ID and permalink.