Every fresh build of the Vidar password stealer now hides its own text differently from the last one. Zscaler's ThreatLabz has watched that change since May, and what it found in June is unusual for a commodity stealer: the malware now carries a miniature virtual machine, a bytecode interpreter whose instruction set is reshuffled for every build.
Vidar has been stealing browser credentials, cryptocurrency wallets and session data since 2018. Strings are the soft spot in malware like it. The server addresses it calls, the registry paths it reads and the names of the wallets it hunts for all have to exist somewhere inside the file, and defenders write signatures against them. Vidar's developer has spent years making them harder to reach: single-byte XOR in the earliest versions, then ChaCha20 from internal version 1.5, then a modified ChaCha20 from version 1.8. Version 2.0 is where it turns strange.
A tiny computer inside the malware
The interpreter is about as small as one can be and still work. It runs a fetch, decode and execute loop over a byte array. Each opcode byte indexes a 256-entry dispatch table of which only 14 slots are filled; every other slot is null and simply halts execution. There is no stack and no register beyond a single one-byte accumulator, and the handlers do nothing more exotic than XOR, addition, subtraction, rotation, bitwise negation, multiplication and a substitution table lookup. A hardcoded four-byte XOR key seeds the accumulator and changes with each build, and so do the opcodes, the constants and the substitution tables.
The virtual machine is used two ways. Sometimes the bytecode produces a finished string directly. Sometimes it produces a key and a nonce, which a second layer, a custom stream cipher, then uses to decrypt the real string. Versions 2.0 and 2.1 take the first nine bytes as the key; from 2.2 onward it is the first eight. The last four bytes are the nonce throughout.
Same job, different numbers every time
That second layer varies as well. Versions 2.0 and 2.1 use a ChaCha derivative with a custom 128-bit initial state, an eight-byte key, a four-byte nonce and quarter-round rotations that differ between samples. From version 2.2 the developer switched to an add-rotate-XOR design, folding the key into a 32-bit state with the FNV-1a prime and the nonce with the golden ratio constant 0x9E3779B9, then generating a keystream through a sequence of operations and constants that is different in every build.
None of this is cryptographically strong, and that is not the point. ThreatLabz notes that Vidar keeps the algorithms simple and the interfaces stable while continuously randomising the constants and micro-operations that static signatures depend on. The shape stays the same; the bytes never do.
What the hidden text gives away
The strings ThreatLabz recovered are a reminder of who writes this and what it wants. Several are Russian-language operator messages, including one about a loader failing to start a file and another about a failure to generate a hardware ID. One is a status line that counts what a run actually harvested: browsers, wallets, plugins and grabber results, each with the number of rules that matched. That is the product's own scorecard, and it is the same scorecard behind the campaigns we covered pushing Vidar through fake GTA 6 demo sites in August.
Chase the behaviour, not the strings
For defenders the practical consequence is narrow and clear. String-based detection for Vidar will keep decaying, because the obfuscation is designed to look new in every build while doing the same job, so signatures written against one sample will not survive the next. Detection has to sit on what the malware does: the process lineage, the file access patterns across browser profile directories, and the outbound traffic. Analysts facing a sample have the harder road, and the write-up is worth reading alongside the bytecode deobfuscation work published on JSCEAL last month, because the problem is the same one.
Four sample hashes are published with Ismael Garcia Perez's analysis, covering versions 2.0 through 3.4, among them 1628bb03db87f67661349e169d73ee14ed490bdbf22abfbda08ccc9ebe237974 for version 2.0 and 2d43d592630ad1e012da63ef7279f95dd4a8e94964e12ca2f996051875574fa6 for version 3.1. Zscaler flags the family as Win32.PWS.Vidar.
This briefing is provided by IntelFusions for informational and defensive purposes only. It is based on sources assessed to be reliable at the time of writing, and analytic judgments carry the confidence levels indicated. Indicators of compromise are defanged; re-arm them only in controlled environments. IntelFusions is not affiliated with the organizations named and makes no warranty as to completeness or accuracy.