Chat
Ask me anything
Ithy Logo

如何使用 QuickJS

[CTF Write Up] ASIS CTF Final 2023 - isWebP.js : Exploiting QuickJS by ...

简介

QuickJS 是一个小型且可嵌入的 JavaScript 引擎,适合在资源受限的环境中运行 JavaScript 代码。它由 Fabrice Bellard 开发,旨在提供高性能和完整的 ECMAScript 2020 支持。QuickJS 的特点包括体积小、启动快速,并且易于集成到各种应用程序中,使其成为嵌入式系统和轻量级应用的理想选择。

安装 QuickJS

在 Linux 上安装

在 Linux 系统上,可以通过以下步骤从源代码编译并安装 QuickJS:

  1. 下载源代码:
    
    git clone https://github.com/bellard/quickjs.git
    cd quickjs
          
  2. 编译和安装:
    
    make
    sudo make install
          

    上述命令将编译 QuickJS 并将其安装到系统路径中,方便后续使用。

在 macOS 上安装

macOS 用户可以通过 Homebrew 轻松安装 QuickJS:


brew install quickjs
  

安装完成后,可以使用 qjs 命令来验证安装是否成功:


qjs --version
  

在 Windows 上安装

Windows 用户可以从 QuickJS 的 GitHub 发行页面下载预编译的二进制文件,或选择自行编译源代码。请访问 QuickJS GitHub 页面 获取最新的下载链接和编译指南。

从源码编译

无论是在 Linux、macOS 还是 Windows 上,用户都可以通过以下步骤从源码编译 QuickJS:

  1. 克隆仓库并进入目录:
  2. 
    git clone https://github.com/bellard/quickjs.git
    cd quickjs
        
  3. 运行编译命令:
  4. 
    make
        
  5. 安装(可选):
  6. 
    sudo make install
        

    编译成功后,执行 ./qjs 可以启动 QuickJS 交互式环境。

使用 QuickJS

作为独立的 JavaScript 解释器

QuickJS 可以作为一个独立的 JavaScript 解释器使用,适合执行脚本和进行交互式编程。

进入 QuickJS 交互式环境

启动交互式环境非常简单,只需在终端中输入 qjs


qjs
  

在此环境中,你可以直接输入并执行 JavaScript 代码。例如:


console.log(new Date());
  

执行 JavaScript 文件

你可以创建一个包含 JavaScript 代码的文件,例如 hello.js


console.log('Hello, QuickJS!');
  

然后使用以下命令执行该脚本:


qjs hello.js
  

编译 JavaScript 代码为二进制文件

QuickJS 提供了 qjsc 编译器,可以将 JavaScript 代码编译为独立的可执行文件。这在部署应用时非常有用。

例如,将 hello.js 编译为二进制文件:


qjsc -o hello.bin hello.js
  

然后,你可以直接运行生成的二进制文件:


./hello.bin
  

嵌入到 C/C++ 程序中

QuickJS 可以被嵌入到 C 或 C++ 应用程序中,提供 JavaScript 执行环境。以下是基本的集成步骤:

包含 QuickJS 头文件

在你的 C/C++ 代码中,首先需要包含 QuickJS 的头文件:


#include "quickjs.h"
  

初始化 QuickJS 运行时和上下文

创建 QuickJS 运行时和上下文:


JSRuntime *rt = JS_NewRuntime();
JSContext *ctx = JS_NewContext(rt);
  

执行 JavaScript 代码

使用 QuickJS 的 API 执行 JavaScript 代码:


// 执行脚本
JS_Eval(ctx, "console.log('Hello from QuickJS!')", strlen("console.log('Hello from QuickJS!')"), "", JS_EVAL_TYPE_GLOBAL);
  

释放资源

完成所有操作后,记得释放运行时和上下文占用的资源:


JS_FreeContext(ctx);
JS_FreeRuntime(rt);
  

在 Android 上使用 QuickJS

针对 Android 平台,quickjs-android 框架提供了便捷的集成方式,支持自动垃圾回收和简化的使用方法。以下是一个基本示例:


QuickJS quickJS = QuickJS.createRuntime();
JSContext context = quickJS.createContext();
int result = context.executeIntegerScript("var a = 2 + 10;\n a;", "file.js");
System.out.println("Result: " + result);
context.close();
quickJS.close();
  

上述代码将在 Android 应用中创建一个 QuickJS 运行时环境,执行简单的 JavaScript 代码,并输出结果。

嵌入 QuickJS 到大型应用

对于需要更复杂集成的应用,可以结合多线程、模块加载和自定义函数来扩展 QuickJS 的功能。以下是一些进阶用法:

模块加载

QuickJS 支持 ES6 模块,可以通过模块加载器动态加载 JavaScript 模块:


// main.js
import { add } from './math.js';
console.log(add(5, 3));
  

// math.js
export function add(a, b) {
  return a + b;
}
  

使用 qjs 执行:


qjs main.js
  

自定义 C/C++ 函数供 JavaScript 调用

可以在 C/C++ 中定义自定义函数,并将其导出到 JavaScript 环境中:


// 定义一个简单的加法函数
static JSValue js_add(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
    if (argc < 2)
        return JS_EXCEPTION;
    int a, b;
    JS_ToInt32(ctx, &a, argv[0]);
    JS_ToInt32(ctx, &b, argv[1]);
    return JS_NewInt32(ctx, a + b);
}

int main(int argc, char **argv) {
    JSRuntime *rt = JS_NewRuntime();
    JSContext *ctx = JS_NewContext(rt);
    
    // 创建全局对象
    JSValue global_obj = JS_GetGlobalObject(ctx);
    
    // 添加自定义函数到全局对象
    JS_SetPropertyStr(ctx, global_obj, "add", JS_NewCFunction(ctx, js_add, "add", 2));
    
    JS_FreeValue(ctx, global_obj);
    
    // 执行 JavaScript 代码调用自定义函数
    JS_Eval(ctx, "console.log('5 + 3 =', add(5, 3));", strlen("console.log('5 + 3 =', add(5, 3));"), "", JS_EVAL_TYPE_GLOBAL);
    
    JS_FreeContext(ctx);
    JS_FreeRuntime(rt);
    return 0;
}
  

编译并运行上述代码,将在 JavaScript 环境中添加一个名为 add 的函数,可供调用。

垃圾回收和资源管理

QuickJS 提供了自动垃圾回收机制,但在嵌入式应用中,开发者需要注意手动管理一些资源:

  • 触发垃圾回收: 可以手动触发垃圾回收以释放未使用的内存:
  • 
    JS_RunGC(ctx);
        
  • 监控内存使用: 通过 QuickJS 的 API 监控和限制内存使用,防止应用因内存泄漏而崩溃。

QuickJS 的特点

  • 体积小: QuickJS 设计轻量,适合在资源受限的环境中使用。
  • 启动快速: 启动和执行速度极快,适合需要快速响应的应用场景。
  • 完整的 ECMAScript 2020 支持: 提供全面的 JavaScript 标准支持,确保代码的兼容性和现代性。
  • 可嵌入性: 易于集成到 C/C++ 应用程序中,提供灵活的扩展能力。
  • 交互式环境: 提供命令行工具和交互式环境,方便开发和调试。
  • 模块支持: 支持 ES6 模块化,使代码结构更为清晰和可维护。
  • 跨平台: 支持多种操作系统,包括 Linux、macOS 和 Windows。
  • 活跃的社区和文档: 官方文档详尽,并有活跃的社区支持,帮助用户解决使用中的问题。

进阶用法

错误处理

在执行 JavaScript 代码时,可能会遇到各种错误情况。QuickJS 提供了异常处理机制,开发者可以捕捉并处理这些错误:


// 执行包含错误的 JavaScript 代码
JSValue result = JS_Eval(ctx, "throw new Error('Test Error');", strlen("throw new Error('Test Error');"), "", JS_EVAL_TYPE_GLOBAL);
if (JS_IsException(result)) {
    JSStackTraceEntry stack_trace[16];
    int trace_len = JS_GetStackTrace(ctx, stack_trace, 16, 0);
    for (int i = 0; i < trace_len; i++) {
        printf("Line %d: %s\n", stack_trace[i].line_num, stack_trace[i].func_name);
    }
}
JS_FreeValue(ctx, result);
  

上述代码将在遇到错误时输出堆栈跟踪信息,帮助开发者定位问题。

性能优化

为了提升 QuickJS 的性能,可以考虑以下优化策略:

  • 减少垃圾回收次数: 尽量减少创建和销毁大量对象,降低垃圾回收的频率。
  • 预编译模块: 对常用的 JavaScript 模块进行预编译,减少运行时的解析和编译开销。
  • 使用快速算法: 在 C/C++ 代码中实现高效的算法和数据结构,提升整体应用性能。
  • 多线程执行: 利用多线程技术并行执行 JavaScript 代码,提高处理效率。

集成第三方库

QuickJS 可以集成第三方 JavaScript 库,扩展其功能。例如,集成一个流行的库如 Lodash:

  1. 下载 Lodash 库:
  2. 
    wget https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js -O lodash.js
        
  3. 在 JavaScript 代码中加载 Lodash:
  4. 
    import _ from './lodash.js';
    console.log(_.capitalize('quickjs'));
        
  5. 执行脚本:
  6. 
    qjs --module your_script.js
        

自定义模块开发

除了使用现有的库,开发者还可以创建自定义模块,满足特定需求:

  1. 定义模块:
  2. 
    // math.js
    export function multiply(a, b) {
        return a * b;
    }
        
  3. 导入并使用模块:
  4. 
    // main.js
    import { multiply } from './math.js';
    console.log(multiply(4, 5));
        
  5. 执行脚本:
  6. 
    qjs --module main.js
        

调试与测试

使用调试工具

QuickJS 支持通过调试协议进行调试,开发者可以使用现有的调试工具进行代码调试。例如,可以使用 Chrome DevTools 连接到 QuickJS 提供的调试端口,进行断点设置、变量监控等操作。

编写单元测试

为了确保 JavaScript 代码的正确性,可以使用测试框架如 Jest 或 Mocha 进行单元测试:

  1. 安装测试框架:
  2. 
    npm install --save-dev jest
        
  3. 编写测试用例:
  4. 
    // add.test.js
    const { add } = require('./math');
    
    test('adds 1 + 2 to equal 3', () => {
        expect(add(1, 2)).toBe(3);
    });
        
  5. 运行测试:
  6. 
    npx jest
        

最佳实践

代码组织与模块化

采用模块化编程,可以提高代码的可维护性和可重用性。将不同功能的代码分离到不同的模块中,避免代码冗余和逻辑混乱。

安全性考量

在嵌入 QuickJS 到应用程序时,需要注意以下安全性问题:

  • 限制执行权限: 对执行的 JavaScript 代码进行权限限制,防止恶意代码执行系统命令或访问敏感资源。
  • 代码审查: 定期审查和测试 JavaScript 代码,确保其不包含安全漏洞。
  • 输入验证: 对用户输入的数据进行严格验证,防止注入攻击。

性能监控与优化

持续监控 QuickJS 的性能表现,并根据监控结果进行优化。例如,可以使用性能分析工具分析 JavaScript 代码的执行时间,找出性能瓶颈并优化相关代码。

获取更多信息

欲了解 QuickJS 的更多详细信息和高级用法,请访问其官方 GitHub 仓库:

https://github.com/bellard/quickjs

该页面包含了完整的文档、示例代码以及社区支持,是学习和使用 QuickJS 的最佳资源。

总结

QuickJS 作为一个高效、轻量级的 JavaScript 引擎,提供了强大的功能和灵活的集成能力。无论是作为独立的脚本执行工具,还是嵌入到复杂的 C/C++ 应用程序中,QuickJS 都能胜任。通过上述步骤和最佳实践,你可以快速上手并充分利用 QuickJS 的优势。在实际应用中,建议结合具体需求和场景,进一步探索 QuickJS 的高级特性和优化策略,以实现最佳的性能和用户体验。


Last updated January 3, 2025
Ask Ithy AI
Download Article
Delete Article