All Posts

Environmental Entropy and Port 0x4F: Dissecting a Petite-Packed VB6 RAT (Part 2)

Environment-keyed transforms, stable 0x99 session tags, and detection opportunities for a modular Windows RAT
Zenyard
8
min read
May 6, 2026
This analysis was generated by Zenyard Agent without human-in-the-loop input and is presented as raw output, as produced by the system. However, in real reversing workflows, the agent is designed to augment the researcher, not replace them. The goal of this experiment is to demonstrate how far a purpose-built AI agent for reversing can go when applied to real-world samples, and the value it can deliver in helping researchers shorten time to insight.

Part 1 covered the packed execution model and anti-analysis surface: Petite reconstruction, self-modifying code, SIDT inspection, user-mode port I/O, interrupt traps, FPU validation, and segment-register abuse. This second part continues from those mechanics into the malware’s decryption layers, C2 protocol, attribution boundaries, and practical hunting pivots.

Technical Findings, Continued

Cryptography and key derivation

The malware protects C2 content with three distinct transformation layers rather than a single conventional cipher. Each layer frustrates recovery in a different way, and all three rely on runtime state that makes detached decryption brittle.

The first layer is an RC4-like stream construction rooted in a custom S-box setup. The implementation uses a base offset of 0x5F and begins its stream logic at 0x78. It is not a standard RC4 implementation, but the scheduling and stream-generation structure is close enough to frame it that way analytically. Key material derives in part from the current stack pointer through XOR with 0x7F99CAC2, which means the same sample can produce different effective state under different loading and instrumentation conditions.

The second layer applies an additive byte transform whose key evolution depends on Binary Coded Decimal adjustment instructions. That is unusual and deliberate. DAA and DAS mutate the running state in a way that looks nonsensical in high-level pseudocode but behaves deterministically on the processor. The function wraps this loop with FPU save and restore operations, binding anti-debug behavior directly to the decryption path.

The third layer uses a 64-bit accumulator and rotates it left or right by an amount derived from the current byte. A parity-controlled branch selects the rotation direction before the result is XORed into the payload byte:

if (parity_flag) {
    acc = ROL64(acc, b);
} else {
    acc = ROR64(acc, b);
}
out[i] = in[i] ^ acc_low_byte;

None of these layers is individually elegant, but together they make partial recovery error-prone and tie successful decoding to the exact runtime conditions the malware expects.

C2 protocol design and network fingerprinting

The implant communicates over raw TCP using newline-delimited framing. It strips, parses the incoming stream through a three-state finite state machine, and routes malformed or out-of-range parser states into deliberate hang behavior rather than graceful failure.

State 1 validates the message header. State 2 processes the body. State 3 validates the trailer. Both the header and trailer paths call the same tag-generation routine, which gives the protocol a stable and analytically useful invariant:

tag = ((interrupt_result + 0x20F645EA) & 0xFFFFFF00) | 0x99;

The low byte is always 0x99. The upper 24 bits vary based on an interrupt-derived value. That gives the protocol a repeatable marker without hardcoding a static session identifier. It also means defenders can hunt for a structural property of the protocol rather than chase rotating infrastructure.

The outbound message queue includes an anti-forensic behavior worth noting. After dispatch, the code overwrites the stored message pointer with a sentinel value instead of retaining the original reference. That shortens the lifetime of useful plaintext in memory and reduces the value of delayed memory acquisition. The design is consistent with an implant intended to survive close inspection rather than merely establish a basic backdoor channel.

Attribution Considerations

The sample shows disciplined engineering, modular payload staging, and a broader anti-analysis surface than typical commodity malware. That supports an assessment of bespoke or tightly maintained tooling. It does not support attribution to a named actor based on the evidence available here.

No actor-specific strings, infrastructure overlaps, compile pipeline artifacts, or code-reuse anchors were recovered from this static analysis alone. The most defensible conclusion is that the malware is inconsistent with low-end commodity crimeware and consistent with custom tooling built for operators who expect competent reverse engineering and instrumented environments.

Detection Opportunities

Network

The strongest network-level hunting pivot is the session tag property. The malware generates tags whose low byte is always 0x99, and it validates those tags at both protocol boundaries. That creates a durable signature at the packet and stream level even when the upper bytes vary by session and the C2 address itself is derived only at runtime.

A second useful protocol-level indicator is the combination of raw TCP transport, repeated structured tag fields, and newline-delimited framing. Those traits are less specific than the 0x99 invariant, but they become useful when correlated with the fixed tag behavior and short-lived message lifetimes visible in memory or traffic captures.

Host and Behavioral

On the host, the best hunting opportunities come from behavior that should be rare in legitimate user-mode software. Prioritize 32-bit Windows processes that execute SIDT, perform IN or OUT instructions against port 0x4F, or use VirtualProtect immediately before self-referential code writes. That combination is highly unusual and maps directly to the sample’s anti-analysis and unpacking path.

Also hunt for processes that import or load msvbvm60.dll while exhibiting Petite-style packing artifacts, runtime API resolution through GetProcAddress, reflective PE mapping, or memory-adjacent use of the constants 0x7F99CAC2 and 0x8B8F9397. Any one of those artifacts may be noisy in isolation. In combination, they describe the family with high confidence.

Conclusion

This sample is architecturally distinct because its decryption path depends on the execution environment rather than treating evasion as a separate layer wrapped around a stable payload. Debugger interference, altered stack state, and emulator behavior can all change what the malware decrypts and how it proceeds. The most important detection priorities are the 0x99 beacon property, user-mode SIDT, port 0x4F I/O, and runtime API resolution around self-modifying code. Those traits are behavioral and protocol-level, which makes them more durable hunting pivots than infrastructure that can be rotated cheaply.

Annex A: Indicators of Compromise

Indicator Type Value Description
Packer marker @petite repeated 9 times Strong indicator of the packed outer loader and embedded inner PE images
Runtime dependency msvbvm60.dll Visual Basic 6 runtime linked or loaded by the sample
Protocol constant Low byte 0x99 Fixed low byte in every generated session tag
Tag constant 0x20F645EA Added to interrupt-derived value during tag generation
Session key constant 0x7F99CAC2 XORed with stack pointer to derive session state
Anti-analysis canary 0x8B8F9397 Used in LOCK-prefixed integrity write/check logic
Emulator check I/O port 0x4F with mask 0x478CCC4B High-confidence anti-emulation primitive
CLI pattern -I Likely carries encrypted C2 material or server identifier
C2 storage location DAT_00430280 Runtime destination for decrypted C2 address
Encrypted seed locations 0x0042b7b8, 0x0042b7c0 Source data used to derive C2 material
Reflective loading Runtime mapping of up to 5 embedded PEs Modular payload staging without normal loader registration

Annex B: YARA Hunting Rules

The following rule targets the packed outer loader. It relies on the repeated Petite marker, the VB6 runtime string, and multiple stable constants recovered during analysis. It is intentionally narrow to keep false positives down.

rule Zenyard_Petite_VB6_Paranoiac_RAT_Wrapper
{
    meta:
        description = "Detects the packed outer wrapper of the analyzed Petite-packed VB6 RAT family"
        author = "Zenyard Agent"
        date = "2026-03-21"

    strings:
        $petite = "@petite" ascii
        $vb6 = "msvbvm60.dll" ascii nocase
        $xorkey = { C2 CA 99 7F }
        $tagconst = { EA 45 F6 20 }
        $canary = { 97 93 8F 8B }
        $iomask = { 4B CC 8C 47 }

    condition:
        uint16(0) == 0x5A4D and
        #petite >= 5 and
        $vb6 and
        3 of ($xorkey, $tagconst, $canary, $iomask)
}

Join Our Newsletter

This field is required
Thank you for subscribing!
Oops! Something went wrong while submitting the form.
By subscribing to our newsletter, you consent to the collection and use of your information as described in this Privacy Policy.