Skip to content

ccjjfdyqlhy/PyRiteJS

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PyRiteJS - VM-Based JavaScript Obfuscator

A VM-based JavaScript obfuscator built on the PyRite compiler. PyRiteJS compiles PyRite source code to JSON bytecode, runs it in a JavaScript VM, and obfuscates the bundle for deployment.

Overview

PyRiteJS implements a complete JavaScript virtual machine that executes bytecode compiled by the PyRite C++ compiler. The system consists of:

  1. Runtime VM - A stack-based bytecode interpreter that executes PyRite bytecode in JavaScript
  2. Bridge System - Bidirectional communication between PyRite and HTML/JS (DOM, events, fetch, reactive state)
  3. Obfuscation Layer - Opcode shuffle, XOR encryption, and AES encryption to protect the bytecode
  4. Build Pipeline - A build tool that compiles .prc.json bytecode files into self-contained .js bundles

Project Structure

PyRiteJS/
├── README.md                           # This file
├── package.json                        # Node.js package config
├── build.js                            # Build pipeline (Section 11)
├── src/
│   ├── index.js                        # Main entry point
│   ├── runtime/
│   │   ├── BigNumber.js                # Arbitrary precision decimals (Section 3)
│   │   ├── PyValue.js                  # Value system (Section 4)
│   │   ├── Env.js                      # Environment & scope chain (Section 5)
│   │   ├── CallFrame.js                # Call frame management (Section 7)
│   │   ├── Upvalue.js                  # Closures & upvalues (Section 8)
│   │   ├── VM.js                       # VM dispatch loop (Section 6)
│   │   ├── builtins.js                 # Built-in functions (Section 9)
│   │   └── opcodes.js                  # Opcode definitions (Section 12)
│   ├── bridge/
│   │   ├── VMBridge.js                 # HTML -> PyRite (Section 13.2)
│   │   ├── JSBridge.js                 # PyRite -> Browser (Section 13.3)
│   │   ├── events.js                   # Event handling (Section 13.4)
│   │   ├── dom.js                      # DOM operations (Section 13.5)
│   │   ├── async.js                    # fetch & async (Section 13.6)
│   │   └── reactive.js                 # Reactive state (Section 13.7)
│   └── obfuscate/
│       ├── opcodeShuffle.js            # Opcode shuffle (Section 10.1)
│       ├── xor.js                      # XOR encryption (Section 10.2)
│       └── aes.js                      # AES encryption (Section 10.3)
├── cpp/
│   └── JsBytecodeSerializer.cpp        # C++ JSON serializer (Section 2)
├── examples/
│   └── counter/
│       ├── counter.pr                  # PyRite source
│       ├── counter.prc.json            # Compiled bytecode
│       ├── counter.bundle.js           # Obfuscated bundle
│       ├── generate_counter_bytecode.js # Bytecode generator
│       └── index.html                  # Demo HTML page
└── tests/
    ├── run_all.js                      # Run all tests
    ├── test_vm_hello.js                # VM basic test
    ├── test_vm_add.js                  # VM arithmetic test
    ├── test_vm_closure.js              # VM closure test
    ├── test_vm_loop.js                 # VM loop/conditional test
    ├── test_vm_list.js                 # VM list test
    ├── test_bridge.js                  # Bridge system test
    └── test_counter.js                 # Counter example test

Quick Start

1. Run the Counter Example

# Build the counter bundle (with opcode shuffle obfuscation)
node build.js examples/counter/counter.prc.json --output examples/counter/counter.bundle.js --shuffle --seed 42

# Open the HTML file in a browser
open examples/counter/index.html

2. Run Tests

# Run all tests
node tests/run_all.js

# Run specific tests
node tests/test_vm_hello.js
node tests/test_vm_add.js
node tests/test_vm_closure.js
node tests/test_vm_loop.js
node tests/test_vm_list.js
node tests/test_bridge.js
node tests/test_counter.js

Build Pipeline

The build.js script compiles a .prc.json bytecode file into a self-contained .js bundle:

node build.js <input.prc.json> [options]

Options:
  --output <file>      Output bundle file (default: <input>.bundle.js)
  --shuffle            Enable opcode shuffle obfuscation
  --xor                Enable XOR encryption
  --xor-key-length N   XOR key length (default: 16)
  --aes                Enable AES encryption
  --aes-password P     AES password (required if --aes)
  --seed N             Random seed for shuffle/xor

Example: Build with all obfuscation layers

node build.js myapp.prc.json \
  --output myapp.bundle.js \
  --shuffle \
  --xor \
  --aes --aes-password "my-secret-key" \
  --seed 12345

Architecture

Bytecode Format (Section 2)

The VM executes JSON-format bytecode:

{
  "magic": "PRC1",
  "version": 1,
  "all_functions": [
    {
      "name": "main",
      "code": [0, 4, 0, 0, 0, ...],
      "constants": [...],
      "arity": 0,
      "max_locals": 4,
      "upvalue_count": 0,
      "upvalue_descriptors": [...],
      "params": [...],
      "local_names": [...],
      "is_method": false,
      "is_exposed": false
    }
  ],
  "entry_function": 0,
  "exports": ["handleClick", "init"]
}

Value System (Section 4)

All values in the VM are represented as PyValue objects with a type tag and raw value:

Type Tag Description
Null 0 Null value
Number 1 BigNumber (arbitrary precision)
String 2 String
Boolean 3 Boolean (stored as Number 0/1)
List 4 Array
Dict 5 Object
Function 6 Closure
Native 7 JS function wrapper
Class 8 Class definition
Instance 9 Class instance
Binary 10 Uint8Array
Module 11 Module proxy
Exception 12 Exception
BoundMethod 13 Bound method
Promise 14 Promise
PyElement 15 DOM element wrapper
State 16 Reactive state

Opcodes (Section 12)

The VM supports 58 opcodes covering:

  • Constants (OP_CONSTANT, OP_NULL, OP_TRUE, OP_FALSE)
  • Stack manipulation (OP_POP, OP_DUP)
  • Variables (OP_GET_LOCAL, OP_SET_LOCAL, OP_GET_GLOBAL, OP_SET_GLOBAL, OP_DEFINE_GLOBAL)
  • Upvalues (OP_GET_UPVALUE, OP_SET_UPVALUE, OP_CLOSE_UPVALUE)
  • Properties (OP_GET_PROPERTY, OP_SET_PROPERTY, OP_GET_INDEX, OP_SET_INDEX, OP_GET_LEN)
  • Arithmetic (OP_ADD, OP_SUB, OP_MUL, OP_DIV, OP_MOD, OP_POW, OP_NEGATE)
  • Comparison (OP_EQUAL, OP_NOT_EQUAL, OP_LESS, OP_LESS_EQUAL, OP_GREATER, OP_GREATER_EQUAL)
  • Control flow (OP_JUMP, OP_JUMP_IF_FALSE, OP_JUMP_IF_TRUE, OP_LOOP)
  • Functions (OP_CALL, OP_CLOSURE, OP_RETURN, OP_RETURN_NULL)
  • I/O (OP_PRINT, OP_INPUT)
  • Misc (OP_CAST, OP_SWAP, OP_RAISE, OP_TRY_BEGIN, OP_TRY_END, OP_CATCH, OP_HALT)

Bridge System (Section 13)

The bridge system enables bidirectional communication between PyRite and the browser:

VMBridge (HTML -> PyRite, Section 13.2)

// Call an exported PyRite function
window.__PyRite_Bridge.call('handleClick', arg1, arg2);

// Register a JS callback that PyRite can call
window.__PyRite_Bridge.expose('myCallback', (data) => {
  console.log('Called from PyRite:', data);
});

// Subscribe to VM events
window.__PyRite_Bridge.onEvent('statechange', (data) => {
  console.log('State changed:', data);
});

JSBridge (PyRite -> Browser, Section 13.3)

PyRite code can call browser functions:

  • createElement(tag), getElementById(id), querySelector(sel)
  • setText(el, text), getText(el)
  • setAttribute(el, name, value), getAttribute(el, name)
  • appendChild(parent, child), removeChild(parent, child)
  • alert(msg), setTimeout(fn, ms), setInterval(fn, ms)

Events (Section 13.4)

  • elem_on(el, eventType, handler) - Bind event
  • elem_off(el, eventType, handler) - Unbind event
  • elem_once(el, eventType, handler) - Bind one-time event

DOM (Section 13.5)

Full DOM manipulation API including:

  • Element creation & querying
  • Content & attribute manipulation
  • Class manipulation
  • Form values
  • Styles
  • Tree manipulation
  • Dimensions & scrolling

Async (Section 13.6)

  • fetch(url, options) - HTTP requests
  • await(promise) - Await a promise (Node.js only with deasync)
  • Response helpers: response_text, response_json, response_status, response_ok

Reactive (Section 13.7)

  • state(initial) - Create reactive state
  • state_get(state), state_set(state, value) - Read/write state
  • bindText(el, state) - One-way binding (state -> element text)
  • bindInput(el, state) - Two-way binding (input element <-> state)
  • computed(state, fn) - Computed state
  • watch(state, fn) - Watch for changes

Obfuscation (Section 10)

Opcode Shuffle (Section 10.1)

Randomly permutes opcode numbers. The permutation is embedded in the bundle header and applied in reverse at load time.

XOR Encryption (Section 10.2)

XOR-encrypts bytecode bytes with a random key. The key is embedded in the bundle header.

AES Encryption (Section 10.3)

AES-128-ECB encrypts bytecode bytes with a password-derived key. The password is not embedded (must be provided at load time).

Counter Example

The counter example demonstrates the full PyRiteJS workflow:

  1. Source (counter.pr): PyRite source code with reactive state and event handlers
  2. Bytecode (counter.prc.json): JSON bytecode (generated by generate_counter_bytecode.js)
  3. Bundle (counter.bundle.js): Obfuscated, self-contained JS bundle
  4. HTML (index.html): Demo page that loads the bundle

The counter features:

  • Reactive state (state(0))
  • Event binding (elem_on)
  • State binding (bindText)
  • State updates (state_set)

C++ Integration (Section 2)

The cpp/JsBytecodeSerializer.cpp file shows how to add JSON output to the PyRite C++ compiler. Add this file to the compiler project and call serializeToJson(program) to emit JSON bytecode.

License

MIT

About

PyRite VM in JS runtime & VM-Based JavaScript Obfuscator

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Packages

 
 
 

Contributors