Post

Stage1 (22.exe) Loader Reversing, Part I: Stage Decryption, Evasion, and Attribution

Reverse engineering a staged loader that patches AMSI and ETW, decrypts an AES-256-CBC payload in memory, and hands off to a Vidar-like credential stealer.

Stage1 (22.exe) Loader Reversing, Part I: Stage Decryption, Evasion, and Attribution

A binary called 22.exe arrived from a delivery URL on cloudaxis[.]cc, wrapped in enough layers to suggest the author wanted to buy time before anyone looked inside. What followed was a fairly deliberate kill chain: silence the telemetry, decrypt a second-stage PE from an embedded blob, and reflectively load it without ever touching disk. This post walks through each of those steps – the evasion patches, the AES decryption internals, the anti-sandbox gating – and lays out the evidence that ties Stage2 to the Vidar stealer family. Part I stays on Stage1; Part II will pick up with the Stage2 config and C2 behavior.

Sample acquisition source: hXXps://cloudaxis[.]cc/gsmft/yueu/fkvqld/tvqqwh/ushu/22.exe

Summary

What is confirmed in this sample set:

  • staged payload decryption from an embedded encrypted blob,
  • in-memory AMSI and ETW patching before stage handoff,
  • anti-sandbox/anti-analysis checks,
  • reflective loading of a decrypted Stage2 PE,
  • and working YARA coverage for stage1 and stage2 artifacts.

Attribution status:

  • Observed: Stage2 is classified as Vidar by multiple commercial AV detections in sandbox telemetry.
  • Inferred: Based on that plus the behavior in this write-up, I refer to the chain as Vidar-like for now.
  • Confidence is medium until Stage2 config and command handling are fully decoded.

Quick Primer

  • AMSI is a Windows scanning interface used by security products to inspect scripts/content before execution.
  • ETW is a Windows telemetry pipeline used to log behavior (process/runtime events).
  • A patch here means overwriting a few bytes in memory so a security-related function returns immediately.
  • Reflective loading means loading a PE from memory directly instead of writing a normal file to disk and launching it.

In plain English: this loader appears to reduce security visibility first, then unpack and run the next stage in memory.

Stage Flow

22.exe stage workflow diagram

Sample Scope

Artifacts (SHA-256)

Artifact SHA-256
22.exe (Stage1 sample) 0cb5a2e3c8aa7c80c8bbfb3a5f737c75807aa0e689dd4ad0a0466d113d8a6b9d
stage2_dec_unpadded.bin (Decrypted Stage2) 5fa52aa9046334c86da1e9746dfe9d7bb23ec69a8b2ab77d98efd2cb1af012f3

Downloads

Public analysis bundle:

YARA rules:

Stage1 Technical Findings

AMSI and ETW Patch Path

The loader uses dedicated routines to patch telemetry/scanning APIs in memory:

  • AMSI patch bytes: B8 57 00 07 80 C3 (mov eax, 0x80070057 ; ret)
  • ETW patch bytes (primary): 31 C0 C3 (xor eax, eax ; ret)
  • ETW patch bytes (fallback): C2 14 00 (ret 0x14)

Note: C2 14 00 (ret 0x14) is an x86-style stdcall stack-cleanup pattern. In this x64 sample, it appears as a fallback patch buffer and may not be exercised on the primary path.

Relevant functions:

  • sub_140002EA0 for AMSI target selection (AmsiScanBuffer, fallback AmsiOpenSession)
  • sub_140002F00 for ETW target selection (EtwEventWrite, EtwEventWriteTransfer, NtTraceEvent)
  • patch helper logic around memory-protection change + write + instruction cache handling

AMSI path

First, the dispatcher logic shows the AMSI targets and fallback behavior in a compact C-style view.

AMSI dispatcher C view at 0x140002EA0

Under that dispatcher, the patch primitive performs memory-protection change and byte overwrite on the resolved export.

AMSI patch primitive around VirtualProtect at 0x1400041A0

ETW path

The ETW dispatcher follows the same pattern, but fans out across multiple trace APIs (EtwEventWrite, EtwEventWriteTransfer, NtTraceEvent) and keeps a fallback branch.

ETW dispatcher at 0x140002F00

Decompiler view of the same function confirms the call ordering and fallback composition clearly.

ETW dispatcher C view at 0x140002F00

Finally, the data view shows the exact patch byte payloads used by the ETW routine.

ETW patch bytes at 0x1400A3570 and 0x1400A3580

Operationally:

  • AMSI: it is often where decoded script/content gets inspected before execution. Short-circuiting these calls reduces that inspection window.
  • ETW: it is heavily used by EDR/telemetry pipelines. Returning early from ETW writers reduces behavioral event visibility during the critical unpack/execute period.
  • Together: one weakens in-line content scanning and the other weakens runtime logging, which increases the chance that the next stage runs with less detection pressure.

Anti-Analysis / Anti-Sandbox Logic

Observed markers include:

  • \\.\pipe\cuckoo
  • cuckoomon.dll
  • SbieDll.dll
  • SOFTWARE\Wine
  • SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Sandboxie
  • user/host/process markers such as joe sandbox, SANDBOX, maltest, ProcessHacker.exe, injector.exe

This is not one single “if sandbox then quit” check. It looks more like a collection of environment checks that feed gating decisions.

Stage2 Decryption

Offsets used in this sample (sample-specific)

  • encrypted blob VA: 0x140005140
  • encrypted blob size VA: 0x1400A3560 (observed 0x9E410)
  • AES IV VA: 0x1400A3590
  • AES key VA: 0x1400A35A0

Encrypted stage2 blob region at 0x140005140

Decrypt wrapper path

The wrapper at sub_140002FF0 allocates memory, copies encrypted bytes, expands key material, decrypts, and trims PKCS#7 padding before handoff.

Stage2 decrypt wrapper at 0x140002FF0

Why AES is a defensible conclusion

I am not calling this AES because of naming alone. The disassembly behavior matches AES-256-CBC traits:

  • key schedule routine (sub_140002D00) operates on a 32-byte key, uses lookup tables in AES-like expansion style, and runs expansion rounds to 60 words (AES-256 schedule shape).
  • IV is handled as a separate 16-byte input and stored with context.
  • decrypt core (sub_140002820) processes 16-byte blocks and contains GF(2^8)-style arithmetic patterns (including 0x1B reduction behavior typical in AES round math) plus block chaining behavior.
  • wrapper applies PKCS#7 unpadding semantics from the final plaintext byte.

Key expansion routine at 0x140002D00

AES-CBC decrypt core callsite at 0x140002820 path

Bottom line: the code is doing modern block-cipher style decrypt with a 32-byte key and IV, not a simple XOR/rolling key obfuscator.

Stage2 Findings (Current State)

Stage2 was recovered consistently through both script and notebook workflows.

Notable strings/import context observed:

  • ChromeBuildTools
  • \\Network\\Cookies
  • long %DOWNLOADS% token-like string
  • imports such as CreateDesktopA, OpenDesktopA, EnumDisplayDevicesA, GetCurrentHwProfileA
  • single-byte XOR recoverable Mozilla/5.0 markers at offsets 0x410 (key 0x33), 0x22F1 (key 0xD5), 0x2331 (key 0x63)
  • this behavior reproduces the community THOR/signature-base heuristic SUSP_XORed_Mozilla_Oct19 from gen_xor_hunting.yar
  • running upstream gen_xor_hunting.yar directly against this decrypted Stage2 also returns SUSP_XORed_Mozilla_Oct19

Stage2 strings with collection-oriented indicators

These are consistent with a credential/data collection stage. In the current static Stage2 artifact, I still do not observe cleartext C2 URLs/domains/IPs, which suggests network/config material is likely decoded at runtime or stored in a transformed form.

Reference links used for this check:

Notebook and Script Guide

Notebook

  • notebooks/vidar_22exe_deobfuscation_walkthrough.ipynb
    • Previously named with a spectralviper_ prefix. That label was an internal working name for this dataset/cluster during early triage and does not reflect a malware-family attribution. The notebook has been renamed to match the Vidar-like classification used throughout this analysis.
  • What it does:
    • lays out the stage flow and constants,
    • extracts and decrypts Stage2,
    • supports decrypt-from-hex for extracted stage/key material,
    • reproduces XORed Mozilla/5.0 hunting on Stage2,
    • surfaces AMSI/ETW patch evidence,
    • pulls Stage2 IOC/config triage output.

Why this matters: it gives you one repeatable path from raw sample to evidence artifacts without hand-clicking every step in IDA.

Python scripts

  • scripts/extract_stage2_from_22.py
    • deterministic extractor/decryptor using fixed sample offsets.
    • outputs decrypt artifacts and stage2_extract_report.json.
  • scripts/analyze_stage1_evasion.py
    • extracts AMSI/ETW patch bytes and anti-sandbox evidence from IDA sqlite.
    • outputs stage1_evasion_report.json.
  • scripts/hunt_stage2_iocs.py
    • Stage2 import/string triage for fast IOC surfacing (includes XORed Mozilla/5.0 hit extraction).
    • outputs stage2_ioc_report.json.
  • scripts/hunt_stage2_xor_mozilla.py
    • reproduces THOR-style XORed Mozilla/5.0 detection with per-hit offsets/keys.
    • outputs stage2_xor_mozilla_report.json.
  • scripts/assess_vidar_similarity.py
    • compares behavioral/string overlap against expected marker sets.
    • outputs vidar_similarity_report.json.
  • scripts/sv_analysis_lib.py
    • shared parsing/decrypt/util functions used across the other scripts and notebook. The sv_ prefix is a legacy artifact from the original internal working name.

IDA Python Helpers

ida_python/sv_stage_decrypt_annotator.py

What it attempts to do on Stage1 (22.exe):

  • rename core stage/decrypt/evasion functions,
  • rename key globals (encrypted blob, key, IV, patch byte buffers),
  • apply comments/types/frame var names,
  • apply Hex-Rays local variable names where API support exists.

Net effect: faster orientation for the Stage1 to Stage2 handoff path.

ida_python/sv_stage2_hunt_annotator.py

What it attempts to do on decrypted Stage2:

  • heuristic tagging of possible PEB-walk and API-hash style routines,
  • suspicious string tagging,
  • single-byte XOR Mozilla/5.0 hit tagging with per-hit XOR keys,
  • likely config blob/data candidate tagging by xref density,
  • best-effort renaming/comments/struct+enum setup.

Important note: Stage2 tagging is heuristic, not ground truth. It should be treated as a triage accelerator, then validated manually function-by-function.

Detection Engineering

Prepared ruleset:

  • VIDAR_LIKE_22_STAGE1_HighFidelity
  • VIDAR_LIKE_22_STAGE2_HighFidelity
  • VIDAR_LIKE_22_STAGE1_Variant_Heuristic
  • VIDAR_LIKE_22_STAGE2_Variant_Heuristic

Public rule location:

  • https://github.com/taogoldi/YARA/tree/main/stealers/vidar

Rule set note:

  • this post includes all four rules (two high-fidelity and two broader heuristic hunting rules).

Validation snapshot:

  • stage1 rule matches 22.exe
  • stage2 rule matches decrypted Stage2 outputs
  • encrypted blob does not match (expected)

Attribution Status

Threat assessment

Current working assessment is Vidar-like with medium confidence. Multiple commercial AV vendors identify this Stage2 variant as associated with Vidar, and the collection-oriented Stage2 strings/imports are consistent with that direction. Confidence stays medium because we still need a fully decoded Stage2 config and verified end-to-end C2 behavior.

YARA Rules

Copy/paste-ready rules (single file):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
import "pe"

rule VIDAR_LIKE_22_STAGE1_HighFidelity
{
  meta:
    author = "taogoldi"
    date = "2026-02-24"
    description = "High-fidelity rule for 22.exe-like stage1 loader/decryptor with AMSI+ETW patching"
    version = 1
    sha256 = "0cb5a2e3c8aa7c80c8bbfb3a5f737c75807aa0e689dd4ad0a0466d113d8a6b9d"
    confidence = "high"

  strings:
    // API patch targets
    $api1 = "AmsiScanBuffer" ascii wide
    $api2 = "AmsiOpenSession" ascii wide
    $api3 = "EtwEventWrite" ascii wide
    $api4 = "EtwEventWriteTransfer" ascii wide
    $api5 = "NtTraceEvent" ascii wide

    // Anti-analysis cluster in this family/build
    $anti1 = "\\\\.\\pipe\\cuckoo" ascii wide
    $anti2 = "cuckoomon.dll" ascii wide
    $anti3 = "SbieDll.dll" ascii wide
    $anti4 = "SOFTWARE\\Wine" ascii wide
    $anti5 = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Sandboxie" ascii wide
    $anti6 = "ProcessHacker.exe" ascii wide
    $anti7 = "injector.exe" ascii wide

    // Patch bytes used by stage1 routines
    $patch_amsi = { B8 57 00 07 80 C3 }
    $patch_etw1 = { 31 C0 C3 }
    $patch_etw2 = { C2 14 00 }

    // Orchestrator / decrypt wrapper / reflective handoff chains
    $sig_orchestrator = {
      53 48 83 EC 40 E8 ?? ?? ?? ??
      C7 44 24 34 00 00 00 00
      48 C7 44 24 38 00 00 00 00
      E8 ?? ?? ?? ?? 85 C0 89 C3 75 ?? 31 DB
    }

    $sig_stage_decrypt_wrapper = {
      41 B8 00 30 00 00
      41 B9 04 00 00 00
      FF 15 ?? ?? ?? ??
      31 D2 48 85 C0 48 89 06 74 ??
      41 89 D8 48 89 FA 48 89 C1 E8 ?? ?? ?? ??
      48 8D 7C 24 20
      4C 8D 05 ?? ?? ?? ??
      48 89 F9
      48 8D 15 ?? ?? ?? ??
      E8 ?? ?? ?? ??
      48 8B 16 41 89 D8 48 89 F9 E8 ?? ?? ?? ??
    }

    $sig_reflective_handoff = {
      E8 ?? ?? ?? ?? 85 C0 74 ??
      48 8B 4C 24 38
      8B 54 24 34
      48 89 4C 24 28
      E8 ?? ?? ?? ??
      31 D2
      48 8B 4C 24 28
      41 B8 00 80 00 00
    }

  condition:
    uint16(0) == 0x5A4D and
    pe.number_of_sections >= 6 and
    all of ($api*) and
    4 of ($anti*) and
    $patch_amsi and $patch_etw1 and $patch_etw2 and
    $sig_orchestrator and $sig_stage_decrypt_wrapper and $sig_reflective_handoff
}

rule VIDAR_LIKE_22_STAGE2_HighFidelity
{
  meta:
    author = "taogoldi"
    date = "2026-02-24"
    description = "High-fidelity rule for decrypted stage2 from 22.exe"
    version = 1
    sha256 = "5fa52aa9046334c86da1e9746dfe9d7bb23ec69a8b2ab77d98efd2cb1af012f3"
    stage2_sha256 = "5fa52aa9046334c86da1e9746dfe9d7bb23ec69a8b2ab77d98efd2cb1af012f3"
    confidence = "high"

  strings:
    $s1 = "ChromeBuildTools" ascii wide
    $s2 = "\\Network\\Cookies" ascii wide
    $s3 = "11111111111111111111111111111111111111111111111111111%DOWNLOADS%" ascii wide

  condition:
    uint16(0) == 0x5A4D and
    pe.number_of_sections == 5 and
    pe.imports("USER32.dll", "CreateDesktopA") and
    pe.imports("USER32.dll", "OpenDesktopA") and
    pe.imports("ADVAPI32.dll", "GetCurrentHwProfileA") and
    pe.imports("USER32.dll", "EnumDisplayDevicesA") and
    all of ($s*)
}

rule VIDAR_LIKE_22_STAGE1_Variant_Heuristic
{
  meta:
    author = "taogoldi"
    date = "2026-02-24"
    description = "Variant-oriented stage1 heuristic for this cluster (less strict than high-fidelity)"
    version = 1
    sha256 = "0cb5a2e3c8aa7c80c8bbfb3a5f737c75807aa0e689dd4ad0a0466d113d8a6b9d"
    confidence = "medium"

  strings:
    $api1 = "AmsiScanBuffer" ascii wide
    $api2 = "AmsiOpenSession" ascii wide
    $api3 = "EtwEventWrite" ascii wide
    $api4 = "EtwEventWriteTransfer" ascii wide
    $api5 = "NtTraceEvent" ascii wide

    $anti1 = "\\\\.\\pipe\\cuckoo" ascii wide
    $anti2 = "cuckoomon.dll" ascii wide
    $anti3 = "SbieDll.dll" ascii wide
    $anti4 = "SOFTWARE\\Wine" ascii wide
    $anti5 = "ProcessHacker.exe" ascii wide
    $anti6 = "injector.exe" ascii wide

    $sig_kexp = {
      41 0F B6 4B 1F
      41 B8 08 00 00 00
      41 0F B6 6B 1E
      48 8D 35 ?? ?? ?? ??
      45 0F B6 53 1D
      48 8D 3D ?? ?? ?? ??
      41 0F B6 53 1C
    }

    $sig_stage_decrypt_wrapper = {
      41 B8 00 30 00 00
      41 B9 04 00 00 00
      FF 15 ?? ?? ?? ??
      31 D2 48 85 C0 48 89 06 74 ??
      48 8D 7C 24 20
      4C 8D 05 ?? ?? ?? ??
      48 8D 15 ?? ?? ?? ??
      E8 ?? ?? ?? ??
      48 8B 16 41 89 D8 48 89 F9 E8 ?? ?? ?? ??
      48 8B 16 8D 43 FF 0F B6 04 02
    }

    $patch_amsi = { B8 57 00 07 80 C3 }
    $patch_etw1 = { 31 C0 C3 }

  condition:
    uint16(0) == 0x5A4D and
    4 of ($api*) and
    2 of ($anti*) and
    ($sig_kexp or $sig_stage_decrypt_wrapper) and
    ($patch_amsi or $patch_etw1)
}

rule VIDAR_LIKE_22_STAGE2_Variant_Heuristic
{
  meta:
    author = "taogoldi"
    date = "2026-02-24"
    description = "Variant-oriented stage2 heuristic from this cluster"
    version = 1
    sha256 = "5fa52aa9046334c86da1e9746dfe9d7bb23ec69a8b2ab77d98efd2cb1af012f3"
    stage2_sha256 = "5fa52aa9046334c86da1e9746dfe9d7bb23ec69a8b2ab77d98efd2cb1af012f3"
    confidence = "medium"

  strings:
    $s1 = "ChromeBuildTools" ascii wide
    $s2 = "\\Network\\Cookies" ascii wide
    $s3 = "%DOWNLOADS%" ascii wide

  condition:
    uint16(0) == 0x5A4D and
    pe.imports("USER32.dll", "CreateDesktopA") and
    pe.imports("USER32.dll", "OpenDesktopA") and
    pe.imports("ADVAPI32.dll", "GetCurrentHwProfileA") and
    2 of ($s*)
}

MITRE ATT&CK Mapping

Technique ID Technique Name Observed Behavior
T1027 Obfuscated Files or Information Stage2 PE encrypted with AES-256-CBC inside Stage1 resource blob
T1140 Deobfuscate/Decode Files or Information Runtime AES decryption of embedded blob with PKCS#7 unpadding
T1620 Reflective Code Loading Decrypted Stage2 PE loaded directly from memory without disk write
T1562.001 Impair Defenses: Disable or Modify Tools AMSI patch (AmsiScanBuffer, AmsiOpenSession) returns error code to bypass scanning
T1562.006 Impair Defenses: Indicator Blocking ETW patch (EtwEventWrite, EtwEventWriteTransfer, NtTraceEvent) suppresses telemetry events
T1497.001 Virtualization/Sandbox Evasion: System Checks Checks for Cuckoo pipes, Sandboxie DLL, Wine registry keys, analysis tool process names
T1555.003 Credentials from Password Stores: Credentials from Web Browsers Stage2 strings reference ChromeBuildTools, \\Network\\Cookies, browser credential paths
T1082 System Information Discovery Stage2 imports GetCurrentHwProfileA, EnumDisplayDevicesA for host profiling

IOC Appendix

File Hashes (SHA-256)

Artifact SHA-256
22.exe (Stage1) 0cb5a2e3c8aa7c80c8bbfb3a5f737c75807aa0e689dd4ad0a0466d113d8a6b9d
stage2_dec_unpadded.bin (Decrypted Stage2) 5fa52aa9046334c86da1e9746dfe9d7bb23ec69a8b2ab77d98efd2cb1af012f3

Network IOCs

Type Value Context
URL hXXps://cloudaxis[.]cc/gsmft/yueu/fkvqld/tvqqwh/ushu/22.exe Stage1 delivery URL

Registry Keys

Key Context
SOFTWARE\Wine Anti-analysis: Wine environment check
SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Sandboxie Anti-analysis: Sandboxie detection

File / Pipe Paths

Path Context
\\.\pipe\cuckoo Anti-analysis: Cuckoo sandbox named pipe
cuckoomon.dll Anti-analysis: Cuckoo monitor DLL
SbieDll.dll Anti-analysis: Sandboxie injection DLL

Process Names (Anti-Analysis Markers)

Process / String Context
ProcessHacker.exe Analysis tool detection
injector.exe Analysis tool detection
joe sandbox / SANDBOX / maltest User/host environment string checks

Stage1 Patch Byte Signatures

Target Bytes Instruction Semantics
AMSI (AmsiScanBuffer) B8 57 00 07 80 C3 mov eax, 0x80070057 ; ret
ETW (primary) 31 C0 C3 xor eax, eax ; ret
ETW (fallback) C2 14 00 ret 0x14

Stage2 XOR-Encoded Strings

Offset XOR Key Decoded Value
0x410 0x33 Mozilla/5.0
0x22F1 0xD5 Mozilla/5.0
0x2331 0x63 Mozilla/5.0

Conclusion

Stage1 (22.exe) follows a well-structured loader pattern: neutralize AMSI and ETW telemetry through targeted in-memory patches, run a battery of anti-sandbox and anti-analysis checks, then decrypt an AES-256-CBC encrypted Stage2 PE from an embedded blob and reflectively load it – all without writing the payload to disk. The evasion work is not novel in isolation, but the combination of dual-target patching (both content scanning and event tracing), multi-tool sandbox detection, and crypto-backed stage separation puts this above a commodity dropper in terms of operational investment.

Stage2 static analysis shows strong indicators of credential and browser data collection – Chrome tooling strings, cookie paths, download tokens, and host-profiling imports – consistent with the Vidar stealer family. The XOR-encoded Mozilla/5.0 user-agent strings, confirmed independently by the upstream gen_xor_hunting.yar rule, further support that classification. Attribution remains at medium confidence pending full Stage2 config extraction and C2 validation, which Part II will address.

Four YARA rules (two high-fidelity, two heuristic) are published and validated against both stages. The accompanying scripts and notebook provide a reproducible path from the raw sample through decryption to IOC extraction, suitable for integration into broader hunting workflows.

This post is licensed under CC BY 4.0 by the author.