devirt.re
Create account
Menu

Devirtualization vs deobfuscation vs unpacking

Unpacking, deobfuscation and devirtualization are three different reverse-engineering jobs. Unpacking restores code that a packer compressed or encrypted, deobfuscation simplifies code that was rewritten to be hard to read, and devirtualization translates code that a protector turned into bytecode for its own virtual machine back into a form an analyst can read. One protected file can need all three, and the jobs are easy to confuse because, as Rolf Rolles showed in 2009, virtualized code is never restored to its original form while the program runs, so unpacking alone cannot recover it.

Developers use packing, obfuscation and virtualization to guard license checks and proprietary code. Malware authors use the same protectors to hide what their code does, and the analysis jobs are the same either way. This guide compares them across protectors such as VMProtect, Themida and Code Virtualizer, for authorized work such as malware research, security audits and interoperability, and it explains concepts rather than procedures.

Devirtualization, deobfuscation and unpacking at a glance

Devirtualization, deobfuscation and unpacking differ in the protection they undo, the state they start from and the output they produce.

Diagram: unpacking, deobfuscation and devirtualization compared by what each job starts from and what each gives back.
Devirtualization, deobfuscation and unpacking start from different inputs. Unpacking restores code that was compressed or encrypted until the program starts, deobfuscation simplifies native code rewritten into harder equivalent sequences, and devirtualization recovers logic from bytecode run by an interpreter.
Unpacking, deobfuscation and devirtualization compared
Compared onUnpackingDeobfuscationDevirtualization
GoalRecover the code that the packer hidMake obfuscated native code readableRecover the logic that the virtual machine runs
What it undoesPacking: compression or encryption of the fileObfuscating transformations such as junk code, opaque predicates and flattened control flowVirtualization: native instructions replaced by bytecode for a custom virtual machine
Starts fromA file whose code appears only when the program runsNative code that runs directly but is hard to readBytecode and the interpreter that executes it
ProducesThe program's code, data and imports in a form analysis tools can loadEquivalent code with the obfuscation simplifiedRecovered logic as native code, LLVM IR or pseudocode
Leaves unsolvedObfuscated and virtualized functions inside the programVirtualized functions, whose original instructions are no longer in the fileOriginal source code, names and comments, which compilation discarded
Next job needed whenUnpacked functions are still obfuscated or run through an interpreterA function turns out to be bytecode run by a virtual machineThe code was obfuscated before it was virtualized, as in VMProtect's Ultra mode

Packing is applied to the whole file. Deobfuscation and devirtualization are decided one function at a time, because protectors let a developer choose a different protection for each part of a program. Protectors also add anti-debugging checks and detection of virtual environments; those checks change how a file reacts to being analyzed, not how its code is written, so they sit outside this comparison.

What is unpacking?

Unpacking is the recovery of the code that a packer hid. A packer compresses or encrypts an executable and adds a small routine, often called an unpacking stub, that restores the original code in memory when the program runs. MITRE ATT&CK lists the technique as Software Packing, sub-technique T1027.002 of Obfuscated Files or Information, and names MPRESS and UPX as example packers.

Packers were built to make executables smaller, and then became one of the most common ways malware hides its code. A 2015 study of run-time packers by Xabier Ugarte-Pedrero, Davide Balzarotti, Igor Santos and Pablo Bringas, presented at the IEEE Symposium on Security and Privacy, describes them as used by the vast majority of malicious samples. MITRE ATT&CK adds that packing changes a file's signature in an attempt to avoid signature-based detection.

Unpacking works because packed code has to exist in its original form before the processor can run it. Rolf Rolles described the classical model in 2009: the protector compresses or encrypts the program's sections, the unpacking routine restores them when the program starts, and from then on the program behaves like the unprotected original. The VMProtect manual calls protection by packing alone often insufficient for that reason, and recommends obfuscation and virtualization instead.

Unpacking does not change the code it restores. If a developer obfuscated or virtualized functions before the file was packed, unpacking hands those functions back as they were protected. It can also be incomplete. In the same 2015 study, Ugarte-Pedrero and colleagues describe packers that interleave unpacking with the program's own code, or unpack and re-pack pieces of code on demand, so there may be no moment when the whole program sits unpacked in memory.

What is deobfuscation?

Deobfuscation is the process of turning deliberately obfuscated code into an equivalent form that is easier to analyze. Christian Collberg, Clark Thomborson and Douglas Low defined an obfuscating transformation in a 1997 taxonomy as one that keeps a program's observable behavior while making it harder to understand, and sorted such transformations into layout, data, control and preventive obfuscations. Deobfuscation works back through those transformations without changing what the program does.

In native code, common obfuscating transformations include:

  • Junk instructions and dead code that run to no effect or never run at all.
  • Opaque predicates, conditions whose outcome the obfuscator knows in advance but an analyzer can work out only with great difficulty, as Collberg and colleagues described them in 1997.
  • Control-flow flattening, which routes a function's blocks through a single dispatching switch so that their original order disappears.
  • Mixed boolean-arithmetic (MBA) expressions, which Zhou, Main, Gu and Johnson presented in 2007 as obscuring transforms that mix arithmetic and bitwise operations.
  • Encrypted strings that are decrypted only while the program runs.

Commercial protectors ship the same obfuscating transformations under their own names. VMProtect calls its version mutation: according to its manual, mutation adds garbage commands, dead code and random conditional jumps, and replaces original instructions with equivalent sequences. Oreans lists code mutation and runtime string encryption for Themida and Code Virtualizer.

Deobfuscation of binary code relies on program analysis rather than on reading instructions one by one. Sharath Udupa, Saumya Debray and Matias Madou showed at the 2005 Working Conference on Reverse Engineering that simple combinations of static and dynamic analysis defeat much of the effect of obfuscation aimed at static analysis, including control-flow flattening. Babak Yadegari and colleagues combined taint analysis with semantics-preserving code transformations in a generic approach to obfuscated executables in 2015. Large language models are now being tested on the same problem: in a 2025 evaluation posted on arXiv, Anton Tkachenko, Dmitrij Suskevic and Benjamin Adolphi tested seven commercial models on obfuscated assembly and found results ranging from autonomous deobfuscation to complete failure, with every model failing against combined techniques.

Deobfuscation also applies to scripts such as JavaScript. For interpreted languages, the VMProtect manual notes, obfuscation works on the source code itself, for example by removing comments and giving variables meaningless names. This guide covers compiled executables.

Deobfuscation reaches its limit at virtualized code. A deobfuscator can simplify the interpreter's own instructions, but the function the developer wrote is no longer present as native instructions, only as bytecode for the virtual machine.

What is devirtualization?

Devirtualization, in reverse engineering, is the translation of virtualized code back into a form an analyst can read, such as native instructions, LLVM IR or decompiled pseudocode. Virtualization obfuscation, also called code virtualization, translates selected functions into bytecode for a custom virtual machine and embeds an interpreter that executes that bytecode whenever the program runs.

Protector vendors describe virtualization the same way. The VMProtect manual says virtualized fragments are executed by the interpreter of its virtual machine without being transformed into machine code for the physical processor. Oreans says that Code Virtualizer's virtual opcodes and virtual machine are unique for every protected application, and Themida's help explains that protecting the same application twice with the same virtual machine architecture produces two machines with different registers, instruction handlers and opcode tables.

Virtualization is older than VMProtect, Themida or Code Virtualizer. Collberg, Thomborson and Low listed it in their 1997 taxonomy as table interpretation, a control obfuscation that converts code into a different virtual machine code run by an interpreter shipped with the program, usually at an order-of-magnitude slowdown for each level of interpretation. That makes devirtualization a specialized kind of deobfuscation, and a 2021 systematization of the field by Patrick Kochberger and colleagues is titled “Automatic Deobfuscation of Virtualization-protected Applications”.

Devirtualization methods start either from the interpreter or from what it executes: handler analysis and bytecode translation, trace simplification, symbolic execution, program synthesis, and lifting to an intermediate representation followed by compiler optimization. The devirtualization methods hub describes each family and what its output can prove.

Devirtualization does not recover source code. The names, comments and types that compilation discarded were gone before any protector ran, so a devirtualized function is recovered logic, not the developer's text. The word also names an unrelated compiler optimization: GCC's -fdevirtualize option, for example, converts calls to virtual functions into direct calls.

Does unpacking remove code virtualization?

No. Unpacking removes the file-level layer that a packer adds, but a virtualized function stays virtualized, because the protector replaced its instructions with bytecode when the file was protected, and the interpreter runs that bytecode every time. Rolf Rolles made the point in Unpacking Virtualization Obfuscators at the 2009 USENIX Workshop on Offensive Technologies: generic unpackers assume a moment when the whole program sits unprotected in memory, but instruction virtualizers never restore the program's code to its original form.

VMProtect shows the split in one product. According to the VMProtect product page, its packing option packs the whole application and unpacks it in memory at startup, while its virtualized code stays bytecode that the embedded virtual machine executes. VMProtect research and Themida research explain the difference for each protector.

An unpacked file can look complete in a disassembler and still hide the functions its developer protected most, such as a license check, behind a dispatcher and handlers. Those functions need devirtualization.

Why do unpacking, deobfuscation and devirtualization get confused?

Unpacking, deobfuscation and devirtualization get confused for four reasons.

  1. One protector can apply all three. VMProtect combines optional packing with mutation, virtualization and Ultra, which mutates code and then virtualizes it, and it lets a developer protect each part of a program with a different method. Oreans' product comparison lists whole-application encryption and compression, code mutation and code virtualization for Themida.
  2. Standards file them together. MITRE ATT&CK places virtual machine software protection inside Software Packing (T1027.002), which is a sub-technique of Obfuscated Files or Information (T1027).
  3. Unpacking is used broadly. Rolles titled his 2009 paper on breaking virtualization “Unpacking Virtualization Obfuscators”, using unpacking in its wide sense of removing a protection before analysis.
  4. The words have other meanings. Devirtualization is also a compiler optimization, deobfuscation is also a routine job for JavaScript, and in MITRE ATT&CK, Deobfuscate/Decode Files or Information (T1140) describes attackers decoding or deobfuscating their own hidden files and payloads, not an analyst's work.

Which one does a protected binary need?

The job a protected binary needs depends on what each function looks like once any packing is removed, and one file can need all three. The VMProtect manual describes protecting one part of a program with virtualization, another with mutation and the most critical fragments with both.

Which job a protected function needs
JobWhat the code shows
Unpacking, firstThe code appears only when the program runs. Packing hides everything else, so it comes off first.
DeobfuscationFunctions run as native code but are buried in junk instructions, opaque predicates or flattened control flow.
DevirtualizationFunctions enter an interpreter: a dispatcher reads bytecode and hands each instruction to a handler, and the expected logic is nowhere in the disassembly.
Devirtualization, then deobfuscationThe recovered logic is still tangled because the code was mutated before it was virtualized, as in VMProtect's Ultra mode.

Devirt's analysis path runs separate stages, from identifying the protection through unpacking, deobfuscation and devirtualization to verification and delivery, and its unpacking stage hands on a recovered image with the remaining layers declared. How Devirt works follows a file from upload to a verified result.

What can each result prove?

Unpacking, deobfuscation and devirtualization each produce a different kind of evidence, and the evidence decides what the output can be trusted for.

  • An unpacked program is a snapshot of what the unpacking routine restored. It is exact for that moment and incomplete when a packer restores code piece by piece.
  • A deobfuscated function should behave like the obfuscated one, because obfuscating transformations preserve observable behavior. Only testing or a formal check shows that the simplification kept it that way.
  • A devirtualized function is recovered logic that needs the same check. Devirt reports every protected function in one of four proof states (proven, runtime-dependent, unresolved or permanently bounded), defined in the methodology note how to read a devirtualization coverage figure. The four states add up to the number of protected functions counted before recovery began, so a function that was not proven cannot drop out of the result.

Frequently asked questions

Is devirtualization a type of deobfuscation?

Yes. Virtualization is one of the obfuscating transformations in Collberg, Thomborson and Low's 1997 taxonomy, so recovering from it is a form of deobfuscation. Devirtualization has its own name because its methods differ: the original instructions are replaced by bytecode rather than disguised, so the analysis has to rebuild them from how the virtual machine behaves.

Is packing the same as obfuscation?

Packing is one form of obfuscation, but the two differ in when the protection ends. A packer hides code until the program runs and then restores it, so the original instructions exist in memory; other obfuscating transformations change the instructions themselves, so the obfuscated code is what runs. MITRE ATT&CK treats Software Packing (T1027.002) as a sub-technique of Obfuscated Files or Information (T1027).

Is devirtualization the same as decompilation?

No. A decompiler turns native machine code into pseudocode. Applied to a virtualized function, it decompiles the interpreter, and Oreans notes that anyone who decompiles code protected by Code Virtualizer finds a new instruction set instead of the original x86 instructions. Devirtualization recovers the function first, so a decompiler has the real logic to work on.

Is deobfuscation illegal?

Deobfuscation is an analysis technique, and whether a particular analysis is appropriate depends on authorization: whether the analyst owns the code or has permission to analyze it. Devirt's acceptable use policy requires that for every submitted binary and notes that a supported protector does not by itself establish permission to analyze a particular file. Rules differ between jurisdictions and licenses, and this guide is not legal advice.

Next steps for an authorized analysis

For files you are authorized to analyze, the next page depends on the protector: VMProtect devirtualization for VMProtect, and Themida devirtualization for Themida and Code Virtualizer. How Devirt works explains the path from upload to a verified result, and the devirtualization methods hub covers the research behind it.

To see where recovery sits in a full analysis, the protected binary analysis workflow walks through all six stages, from identifying the protection to documenting what the evidence proves.