JavaScript Virtual Machine Pattern Decompilation: Instruction Set and Stack Operations

JavaScript Virtual Machine Pattern Architecture

JavaScript Virtual Machine (JSVMP) implementations encode JavaScript code into a sequence of numeric instructions that are interpreted at runtime. Understanding how to decompile these patterns requires analyzing the instruction set, stack manipulation, and control flow mechanisms.

Core Instruction Set Analysis

The instruction set consists of numeric opcodes that perform specific operations. Each instruction interacts with an operand stack and modifies the execution context.

Stack Manipulation Instructions

// Opcode 23: Push current context onto operand stack
case 23:
    all = cbbb;
    duei.push(cbbb);
    break;

// Opcode 47: Push this context onto operand stack
case 47:
    duei.push(allthis);
    break;

// Opcode 10: Push constant from pool
case 10:
    a1 = shuz[start++];
    duei.push(constantPool[a1]);
    break;

// Opcode 11: Push immediate value
case 11:
    a1 = shuz[start++];
    duei.push(a1);
    break;

Arithmetic Operations

// Opcode 19: Subtraction
case 19:
    a1 = duei.pop();
    a2 = duei.pop();
    a1 = a2 - a1;
    duei.push(a1);
    break;

// Opcode 20: Addition
case 20:
    a1 = duei.pop();
    a2 = duei.pop();
    a1 = a2 + a1;
    duei.push(a1);
    break;

// Opcode 27: Multiplication
case 27:
    a1 = duei.pop();
    a2 = duei.pop();
    a1 = a1 * a2;
    duei.push(a1);
    break;

// Opcode 28: Modulo
case 28:
    a1 = duei.pop();
    a2 = duei.pop();
    a1 = a2 % a1;
    duei.push(a1);
    break;

// Opcode 29: Bitwise XOR
case 29:
    a1 = duei.pop();
    a2 = duei.pop();
    a1 = a2 ^ a1;
    duei.push(a1);
    break;

Comparison Operations

// Opcode 36: Less than or equal
case 36:
    a1 = duei.pop();
    a2 = duei.pop();
    a1 = a2 <= a1;
    duei.push(a1);
    break;

// Opcode 37: Greater than or equal
case 37:
    a1 = duei.pop();
    a2 = duei.pop();
    a1 = a2 >= a1;
    duei.push(a1);
    break;


// Opcode 38: Greater than
case 38:
    a1 = duei.pop();
    a2 = duei.pop();
    a1 = a2 > a1;
    duei.push(a1);
    break;

// Opcode 39: Equality
case 39:
    a1 = duei.pop();
    a2 = duei.pop();
    a1 = a2 == a1;
    duei.push(a1);
    break;

// Opcode 53: Strict equality
case 53:
    a1 = duei.pop();
    a2 = duei.pop();
    a1 = a2 === a1;
    duei.push(a1);
    break;

// Opcode 54: Strict inequality
case 54:
    a1 = duei.pop();
    a2 = duei.pop();
    a1 = a2 !== a1;
    duei.push(a1);
    break;

Bitwise Shift Operations

// Opcode 31: Left shift
case 31:
    a1 = duei.pop();
    a2 = duei.pop();
    a1 = a2 << a1;
    duei.push(a1);
    break;

// Opcode 33: Right shift (signed)
case 33:
    a1 = duei.pop();
    a2 = duei.pop();
    a1 = a2 >> a1;
    duei.push(a1);
    break;

// Opcode 34: Unsigned right shift
case 34:
    a1 = duei.pop();
    a2 = duei.pop();
    a1 = a2 >>> a1;
    duei.push(a1);
    break;

Control Flow Intsructions

// Opcode 190: Unconditional jump
case 190:
    a1 = shuz[start++];
    start += a1;
    break;

// Opcode 192: Conditional jump (if true)
case 192:
    a1 = duei.pop();
    a3 = shuz[start++];
    if (a1) {
        start += a3;
    }
    break;

// Opcode 25: Conditional jump (if false)
case 25:
    a1 = duei.pop();
    a2 = shuz[start++];
    if (!a1) {
        start += a2;
    }
    break;

Variable Assignment

// Opcode 22: Property assignment
case 22:
    a1 = shuz[start++];
    a2 = duei.pop();
    a3 = duei.pop();
    a2[constantPool[a1]] = a3;
    break;

Object and Array Operations

// Opcode 104: Create empty object
case 104:
    duei.push({});
    break;

// Opcode 105: Create empty array
case 105:
    duei.push([]);
    break;

// Opcode 181: Property access
case 181:
    a1 = duei.pop();
    a2 = duei.pop();
    try {
        a1 = a2[a1];
    } catch (e) {
        a1 = window[a1];
    }
    all = a2;
    duei.push(a1);
    break;

Function Calls and Context Management

// Opcode 46: Constructor call
case 46:
    a1 = shuz[start++];
    a3 = duei.pop();
    args = [];
    for (a2 = 0; a2 < a1; a2++) {
        args.splice(0, 0, duei.pop());
    }
    offnew = 1;
    if (a3 == RegExp) {
        a4 = new RegExp(args[0], args[1]);
    } else {
        a4 = new a3(...args);
    }
    offnew = 0;
    duei.push(a4);
    break;

// Opcode 150: Method call
case 150:
    a1 = shuz[start++];
    a3 = duei.pop();
    args = [];
    for (a2 = 0; a2 < a1; a2++) {
        args.splice(0, 0, duei.pop());
    }
    // Handle built-in functions
    if (a3 == window.setTimeout) {
        a4 = setTimeout(...args);
    } else if (a3 == window.atob) {
        a4 = atob(...args);
    } else {
        a4 = a3.apply(all, args);
    }
    duei.push(a4);
    break;

Exception Handling

// Opcode 195: Try-catch-finally block
case 195:
    a2 = shuz[start++];
    a3 = shuz[start++];
    a4 = shuz[start++];
    try {
        a6 = cbb_jsvmp(a3, start, start, duei, args.length, 1, {...});
        start = a2 + start;
        if (a6 == "-90_cbb") {
            return a6;
        }
    } catch (e) {
        a7 = e;
        start = a2 + start;
        a6 = cbb_jsvmp(a1, start, start, duei, args.length, 1, {...});
        if (a6 == "-90_cbb") {
            return a6;
        }
    } finally {
        // Finally block handling
    }
    break;

Stack Frame Menagement

The virtual machine maintains a call stack to handle nested function executions:

// Opcode 1812: Save current stack frame
case 1812:
    a1 = {
        "a1": shuz,
        "a2": start,
        "a3": duei,
        "a4": cbbb
    };
    baoChen.push(a1);
    break;


// Opcode 1813: Restore stack frame
case 1813:
    a1 = duei.pop();
    shuz = codeOfmyfun;
    start = a1['fg'];
    cbbb = a1;
    break;

Decompilation Strategy

  1. Identify constant pool: Extract all string and numeric constants from the pool
  2. Map instruction sequences: Build a mapping of opcode to operation
  3. Reconstruct control flow: Identify loops, conditionals, and jumps
  4. Restore function definitions: Extract function bodies and their variable scopes
  5. Generate readable output: Convert the interpreted code back to JavaScript

The decompilation process involves tracing the execution path through the instruction stream, reconstructing the original source code structure based on the operations performed.

Tags: javascript decompilation virtual-machine jsvmp

Posted on Sat, 09 May 2026 03:44:17 +0000 by rooky