#javascript #compiler #obfuscation #virtualization #virtual-machine

jsyc-compiler

Rusty-JSYC(JavaScript字节码编译器)是一个用Rust编写的JavaScript到字节码的编译器。该字节码旨在与提供的虚拟机(https://github.com/jwillbold/rusty-jsyc/blob/master/vm/vm.js)一起使用,该虚拟机是用JavaScript编写的。组合它们形成了虚拟化混淆的组件。

1 个不稳定版本

0.1.0 2019年6月17日

#573 in 编程语言

LGPL-3.0-only

89KB
1.5K SLoC

Build Status codecov

Rusty-JSYC

Rusty-JSYC(JavaScript字节码编译器)是一个用Rust编写的JavaScript到字节码的编译器。该字节码旨在与提供的虚拟机一起使用,该虚拟机是用JavaScript编写的。组合它们形成了虚拟化混淆的组件。

如何使用本工具

您必须首先编译给定的JavaScript代码。之后,您可以使用提供的虚拟机执行它。

编译您的JavaScript代码

您可以使用提供的命令行工具

cargo run </path/to/javascript.js> </path/to/vm-template.js> </output/dir> -d

或者将编译器作为库使用,并在自己的Rust代码中调用它

extern crate jsyc_compiler;

use jsyc_compiler::{JSSourceCode, BytecodeCompiler};

fn main() {
  let js_code = JSSourceCode::new("console.log('Hello World');".into());
  let mut compiler = BytecodeCompiler::new();

  let bytecode = compiler.compile(&js_code).expect("Failed to compile code");
  println!("Bytecode: {}", bytecode);

  let depedencies = compiler.decl_dependencies();
  println!("Depedencies: {:?}", depedencies);

  let base64_bytecode = bytecode.encode_base64();
  println!("Base64-encoded bytecode: {}", base64_bytecode);
}

在您的Cargo.Toml中

[dependencies]
jsyc_compiler = "~0.1"

运行虚拟机

// include vm.js
// ...
var vm = new VM();
vm.init(Base64EncodedBytecode);
requestIdleCallback(() => vm.run());
// ...

Base64EncodedBytecode替换为实际编码的字节码。

示例演示

一个演示编译器和虚拟机都能使用的示例可以在playground/snake中找到。它包含一个小型的Snake游戏(snake.js)。您可以使用以下方式编译它:

cargo run "playground/snake/unobfuscated/snake.js" "vm/vm.js" "playground/snake/obfuscated" "playground/snake/unobfuscated/index.html"

编译后,在您的浏览器中打开index.html文件。

/path/to/rusty-jsyc/playground/snake/obfuscated/index.html

这已在Chrome 74和Firefox 67上进行了测试。然而,任何支持ES6的浏览器都应该是兼容的。

虚拟化混淆

虚拟化混淆是一种最先进的混淆方案。它通过将代码编译成字节码来混淆代码,然后由虚拟机(VM)执行。因此,VM与编译的字节码一起分发。然后,使用此字节码调用它并执行它,从而执行实际代码。

由于字节码是逐条指令执行的,原始代码在任何地方都没有被恢复。因此,任何潜在的攻击者必须首先逆向工程VM,这可能是高度混淆的。然后必须理解底层架构和指令集,然后才能分析实际的字节码。由于任何两种虚拟化混淆都可能不同,因此自动工具的使用受到限制。[1][2]

兼容性

虚拟和真实JavaScript上下文之间的交互

可以将虚拟JavaScript上下文中定义的函数提供给真实JavaScript上下文。

// Compiled JavaScript
function secret_function(a, b, c) { return a*b+c; }
window.secret_function = secret_function;
// Non-Compiled JavaScript
var secret_function = window.secret_function;
secret_function(10, 20, 1337);

它不需要是 window,任何两个上下文都知晓的对象实例都适用。当调用 secret_function 时,虚拟机将开始执行相应的字节码块。因此,以这种方式调用函数并不会比在编译后的 JavaScript 中调用它透露更多关于实现的信息。

当前的不稳定属性

这些属性在字节码中不会像在真实 JavaScript 中那样反映。

  • 外部非成员函数的 'this' 指针简单地是 'void 0'
  • 赋值表达式不会返回值,因此它们实际上不是表达式
  • 如果你声明了一个没有赋值的变量,它的值将是未知的。因此,它可能是或可能不是 undefined (void 0)。(它将是 undefined,但不是 JavaScript 的 undefined (void 0))
  • letconst 声明被视为 var 声明

不支持的 JavaScript 语法

当前,此编译器只支持 JavaScript 功能的子集。目前缺少的是

  • 与对象相关的符号 ({}, new, this, super)
  • for-of 和 for-in 循环
  • try 和 throw 结构
  • break, continue, with, await, class 和 switch 关键字
  • 标签
  • 函数表达式和箭头函数(允许常规函数)
  • 函数表达式和箭头函数可以用以下方式实现
var func_expr = eval("0, function(x) {return x*x;}");

然而,它们不支持对编译后的 JavaScript 中定义的变量的引用。

  • 标记模板表达式
  • 展开、剩余和序列符号

如何运行测试

该项目中有几个测试集

  1. Cargo 测试: cargo test
  2. Node (mocha) 测试:npm install && npm test

1: Rolf Rolles. Unpacking virtualization obfuscators. USENIX Workshop on Offensive Technologies (WOOT), 2009。

2: Johannes Kinder. Towards static analysis of virtualization-obfuscated binaries. Reverse Engineering (WCRE), 2012 19th Working Conference。

依赖项

~7–17MB
~217K SLoC