Skip to content

Security1 publisher2 min readPublished

Vidar now hides its strings behind a bytecode VM whose opcodes change with every build

Zscaler tracked three changes to Vidar's string encryption between early May and early September 2026, ending in a per-build virtual machine and stream cipher that make a key pulled from one sample useless against the next.

The Watch · Security desk

Photograph accompanying Vidar now hides its strings behind a bytecode VM whose opcodes change with every build
Photo: wikipedia.org

What happened

  • Zscaler ThreatLabz followed Vidar's string obfuscation from May through early September 2026 as it went from basic XOR to ChaCha20 and then to a bytecode virtual machine with a custom stream cipher.
  • The virtual machine showed up in June 2026, and ThreatLabz says its opcodes differ from one build of the stealer to the next.
  • Alongside the virtual machine the developer added a new custom stream cipher whose operations also vary across builds.
  • The hardcoded XOR key is four bytes, changes with each build, and doubles as the seed that initializes the interpreter's accumulator.

Compiled by The WatchSomething wrong?How this is made

Why it matters

  • constraint Tooling stops being reusable: a working Vidar string decryptor has to have its key and opcode semantics re-derived per build, so each new sample is reversing work rather than a lookup.
  • cost At roughly one algorithm change every six weeks, detection content built on strings lifted from a Vidar build carries a recurring maintenance bill for whoever owns the signatures.
  • decision Teams that cover infostealers with static string signatures have to choose between funding per-build reversing and moving coverage to behaviour that does not depend on recovering strings.

The interpreter is small. It runs a fetch-decode-execute loop with the VM code supplied as a byte array, and the matching handler mutates a one-byte accumulator, emitting an output byte when a specific opcode is reached [9]. Each opcode byte indexes a sparse 256-entry dispatch table that is initialized on first use and filled with 14 handlers, which leaves 242 slots null, and a null slot terminates execution [10][1]. There is no stack and no register beyond the accumulator [11]. The handlers use XOR, addition, subtraction, rotation, bitwise negation, multiplication, and substitution through a lookup table [12].

What moves between builds is exactly the part an analyst would want to reuse. Zscaler says the opcodes, constants and substitution tables all change across builds [14]. A script that decodes strings out of one sample needs its opcode semantics re-derived before it decodes anything out of the next.

The dates are what make that expensive. Early May 2026 to early September 2026 is about 18 weeks [3][2]. Inside that window ThreatLabz recorded three changes to the algorithm: ChaCha20 at internal version 1.5, a modified ChaCha20 at 1.8 that the researchers say was altered to make detection and decryption harder, and the VM plus custom stream cipher at 2.0 [4][5][6]. That averages one change roughly every six weeks [3].

Vidar is not new, and neither is this pattern of iteration. Zscaler dates the stealer to 2018, when it obfuscated strings with XOR [1]. The difference in the current line is that the obfuscation is generated per build rather than chosen per version, and the VM is present in both the 2.x and 3.x code [15].

Zscaler's post documents string obfuscation from version 2.0 to version 3.3 and does not report detection rates before and after the VM appeared, or any change in what the stealer collects [8]. The version numbers are ThreatLabz's own internal ones [16]. The deobfuscation itself still happens on the victim machine, with the interpreter writing plaintext bytes into an output buffer as the malware runs [9]. Zscaler describes the per-build techniques as designed to hinder static and automated analysis [17].

What to watch

  • Whether internal versions after 3.3 change the interpreter structure itself, not only the opcodes, constants and substitution tables inside it.
  • Whether anyone publishes an emulator for the 14 handlers that recovers strings without per-sample reversing.
  • Whether Zscaler or others release detection numbers comparing pre-VM and post-VM Vidar builds.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories