如何调用逆向出来的JS代码

为什么需要调用JavaScript代码

在JavaScript逆向中,我们往往是将浏览器的JavaScript代码在本地还原,执行后获取逆向结果。然尔JavaScript逆向基本都是为Python爬虫服务的,所以我们经常需要在Python代码中调用JavaScript代码。本文详细介绍了几种常用的方法。

调用JavaScript代码的方法

使用PyExecJS调用JavaScript函数

前提条件

  1. 安装 PyExecJS

    bash 复制代码
    pip install PyExecJS
  2. 安装一个 JavaScript 运行时 PyExecJS 只是一个"调度员",它需要一个真正的"工人"(JS 运行时)来执行代码。强烈推荐安装 Node.js,因为它最稳定且兼容性最好。

    • 访问Node.js 官网下载并安装。
    • 安装完成后,在你的终端(命令行)输入 node -v,如果能看到版本号,就说明安装成功了。

执行简单的 JavaScript 表达式

这是最基础的用法,可以直接执行一行JS代码并获取返回值。我们使用 execjs.eval() 函数。

javascript 复制代码
import execjs

# 使用 .eval() 执行一个简单的JS字符串操作
js_expression = "'Hello PyExecJS'.split('').reverse().join('')"
result = execjs.eval(js_expression)

print(f"JS 表达式的结果: {result}")
# 输出: JS 表达式的结果: SJcexEyp olleH

调用一个定义好的 JavaScript 函数

当你有一段包含多个函数定义的JS代码时,你需要先"编译"这段代码,然后再通过函数名来调用指定的函数。这需要用到 execjs.compile().call() 假设你有一个 math.js 文件,里面有一个 add 函数。

javascript 复制代码
function add(x, y) {
    return x + y;
}

function subtract(x, y) {
    return x - y;
}

则可以在Python脚本中这样调用:

python 复制代码
import execjs

# 1. 读取你的 JavaScript 文件内容
with open('math.js', 'r', encoding='utf-8') as f:
    js_code = f.read()

# 2. 使用 .compile() 编译JS代码,创建一个JS上下文环境
# PyExecJS 会自动找到你安装的 Node.js 来执行
ctx = execjs.compile(js_code)

# 3. 使用 .call() 调用指定的函数
# 第一个参数是JS中的函数名 "add"
# 后面的参数 (10, 5) 会依次传递给JS的 add(x, y) 函数
result = ctx.call("add", 10, 5)

print(f"调用 JS add(10, 5) 函数的结果是: {result}")
# 输出: 调用 JS add(10, 5) 函数的结果是: 15

优缺点

优点 (Pros) 👍
  • 非常简单:上手快,调用 JS 函数就一两行代码。
  • 安装方便:pip install PyExecJS 就行,库本身很小。
  • 比较灵活:能自动寻找你电脑上安装的 JavaScript 环境(比如 Node.js)。
缺点 (Cons) 👎
  • 必须依赖外部程序:你的电脑上必须安装 Node.js 才能运行,否则会报错,增加了部署的麻烦。
  • 性能很差:每次调用 JS 都像重新打开一个程序,速度慢,不适合频繁调用。
  • 不支持现代 JS:无法运行 Promise 或 async/await 等异步代码,很多新网站的 JS 都跑不了。
  • 已经"过时"了:这个项目自 2018 年以来就没更新过,对于新项目来说,使用它存在风险。

JavaScript 逻辑封装成 Node.js 微服务

安装 Express

bash 复制代码
npm install express

实现Node.js 服务

将以下所有代码保存在一个名为 server.js 的文件中。 这个文件包含了:

  1. 核心的 JS 业务逻辑 (getSign 函数)。
  2. 使用 Express 创建的 HTTP 服务器。
  3. 处理 /get-signature 路由的逻辑。
javascript 复制代码
// server.js

// 1. 导入 express 框架
const express = require('express');

// --------------------------------------------------
// 2. 你的核心 JS 逻辑
// --------------------------------------------------
async function getSign(params) {
    console.log(`[JS Logic] 接收到参数:`, params);
    
    // 模拟异步操作,例如复杂的加密或数据库查询
    await new Promise(resolve => setTimeout(resolve, 50)); 
    
    const sortedValues = Object.values(params).sort();
    const baseString = sortedValues.join('-');
    const signature = `express_signed_${baseString}_${Date.now()}`;
    
    console.log(`[JS Logic] 生成签名为: ${signature}`);
    return signature;
}

// --------------------------------------------------
// 3. 创建并配置 Express 应用
// --------------------------------------------------
const app = express();
const PORT = 3000;

// [关键] 使用 Express 内置的中间件来解析 JSON 请求体
// 这一行代码就代替了原生 http 模块中手动拼接 body 的所有逻辑
app.use(express.json());

// --------------------------------------------------
// 4. 定义 API 路由 (Endpoint)
// --------------------------------------------------
// Express 的路由处理比原生 http 的 if/else 判断清晰得多
app.post('/get-signature', async (req, res) => {
    console.log('[Server] /get-signature 路由被调用');
    try {
        // Express 已经帮你把 JSON body 解析好了,直接通过 req.body 获取
        const params = req.body;

        // 参数校验
        if (!params || Object.keys(params).length === 0) {
            // 使用 .status() 和 .json() 方法链式调用,代码更优雅
            return res.status(400).json({ success: false, error: '请求体不能为空' });
        }

        // 调用核心逻辑
        const signature = await getSign(params);

        // 返回成功响应
        res.json({ success: true, signature: signature });

    } catch (error) {
        console.error('[Server] 处理请求时发生错误:', error);
        res.status(500).json({ success: false, error: '服务器内部错误' });
    }
});

// --------------------------------------------------
// 5. 启动服务器
// --------------------------------------------------
app.listen(PORT, () => {
    console.log(`Express 服务已启动,正在监听 http://127.0.0.1:${PORT}`);
});

在终端中运行:node server.js就可以启动服务。

优缺点

优点 (Pros) 👍
  • 专业级性能与稳定性:Node.js 服务是一个常驻进程,利用 V8 引擎以最高效率执行 JS。它避免了其他方法"每次调用都新建进程"的巨大开销,因此响应极快、吞吐量高,完全能胜任高并发的生产环境。
  • 彻底解决环境与依赖问题:你拥有一个完整的、真实的 Node.js 环境。可以随意使用 npm 安装任何第三方 JS 库(如 crypto-js)。
  • 强大的扩展性:这是一个真正的微服务。未来你可以轻松地增加更多 API 接口(路由),并且可以独立地对这个 JS 服务进行扩容(例如部署到多个服务器或容器),而无需改动 Python 主应用。
缺点 (Cons) 👎
  • 增加了架构复杂度:的项目从一个"单体脚本"变成了"微服务架构"。你需要管理两个独立的服务(Python 应用和 Node.js 服务),并确保它们之间的网络通信是可靠的。

使用Python 内置的 subprocess 模块来运行 JavaScript

这是一种非常强大和灵活的"中量级"方案,它比 PyExecJS 更可靠,又比搭建完整的微服务更简单。Python 程序像一个"指挥官",通过 subprocess 在操作系统中开启一个独立的 node 进程("士兵"),然后通过标准输入/输出(stdin/stdout)给它下达指令并接收报告。

创建 JavaScript 脚本 (worker.js)

这个 JS 脚本需要被设计成一个命令行工具,它必须:

  1. 从**标准输入(stdin)**读取数据。
  2. 执行核心逻辑(可以是异步的)。
  3. 将最终结果打印到标准输出(stdout)
  4. 如果出错,将错误信息打印到标准错误(stderr)
javascript 复制代码
// worker.js

// 你的核心JS逻辑,这里我们还是用一个异步的 getSign 函数
async function getSign(params) {
    // 模拟异步操作
    await new Promise(resolve => setTimeout(resolve, 50)); 
    
    // 简单的处理逻辑
    const baseString = Object.values(params).sort().join('_');
    const signature = `processed_${baseString}`;
    return signature;
}

// --- 以下是与 Python 交互的模板代码 ---

// 完整地读取标准输入
function readStdin() {
    return new Promise((resolve, reject) => {
        let data = '';
        process.stdin.on('data', chunk => data += chunk);
        process.stdin.on('end', () => resolve(data));
        process.stdin.on('error', err => reject(err));
    });
}

// 主执行函数
async function main() {
    try {
        const inputStr = await readStdin();
        const params = JSON.parse(inputStr);

        // 调用核心逻辑
        const result = await getSign(params);

        // 将成功的结果(JSON字符串)打印到 stdout
        // 这是Python捕获数据的唯一方式
        console.log(JSON.stringify({ success: true, data: result }));

    } catch (error) {
        // 将错误信息(JSON字符串)打印到 stderr
        console.error(JSON.stringify({ success: false, error: error.message }));
        process.exit(1); // 以非零状态码退出,表示执行失败
    }
}

main();

创建 Python脚本 (main.py)

python 复制代码
import subprocess
import json

def run_js_with_subprocess(params: dict) -> str | None:
    """
    通过 subprocess 调用 worker.js 并传递参数。
    """
    command = ['node', 'worker.js']
    
    # 将Python字典转换为JSON字符串,以便通过stdin传递
    input_data = json.dumps(params)
    
    print(f"[Python] 准备运行命令: {' '.join(command)}")
    print(f"[Python] 将通过 stdin 发送数据: {input_data}")

    try:
        # subprocess.run 是最推荐的方式
        result = subprocess.run(
            command,
            input=input_data,       # 将数据传递给子进程的 stdin
            capture_output=True,  # 捕获 stdout 和 stderr
            text=True,              # 使用文本模式(自动编解码)
            check=True              # 如果返回码非0(即JS出错),则抛出 CalledProcessError 异常
        )

        # 解析从JS的stdout返回的JSON字符串
        output_data = json.loads(result.stdout)
        
        if output_data.get('success'):
            return output_data.get('data')
        else:
            # 这种情况比较少见,因为JS出错会抛出异常
            print(f"[Python] JS 脚本报告了一个逻辑错误: {output_data.get('error')}")
            return None

    except FileNotFoundError:
        print("[Python] 错误: 'node' 命令未找到。请确保 Node.js 已经安装并加入到系统 PATH。")
        return None
    except subprocess.CalledProcessError as e:
        # 当JS脚本以非零状态码退出时,会捕获此异常
        print("[Python] 错误: JS 脚本执行失败!")
        print(f"  - 返回码: {e.returncode}")
        # e.stderr 中包含了JS通过 console.error 输出的信息
        print(f"  - 错误输出 (stderr): {e.stderr.strip()}")
        return None

if __name__ == "__main__":
    order_data = {
        "orderId": "TYO-20250919-001",
        "amount": 15000,
        "currency": "JPY"
    }
    
    signature = run_js_with_subprocess(order_data)
    
    if signature:
        print(f"\n[Python] 成功从JS获取结果: {signature}")

优缺点

优点 (Pros) 👍
  • 无Python第三方依赖:subprocess 是内置库。
  • 完整的Node.js环境:可以随意使用 npm 包和所有 Node.js 功能,包括 async/await
  • 模式通用:这种通过 stdin/stdout 通信的模式可以用于调用任何命令行程序。
缺点 (Cons) 👎
  • 需要手动序列化数据:所有数据交换都需要通过 JSON 等格式进行转换。
  • 进程开销:每次调用都会启动新进程,不适合超高频次的场景。
相关推荐
Asort2 小时前
JavaScript设计模式(六)——适配器模式 (Adapter)
前端·javascript·设计模式
weixin_457126053 小时前
分享几个免费下载抖音、小红书、快手高清图片和视频的在线网站
javascript·python·html
Mintopia3 小时前
🚗💨 “八缸” 的咆哮:V8 引擎漫游记
前端·javascript·v8
Z_ One Dream3 小时前
React 和 Vue 如何选择?(2026 年)
javascript·vue.js·react.js
Restart-AHTCM3 小时前
前端核心框架vue之(路由核心案例篇3/5)
前端·javascript·vue.js
用户6120414922133 小时前
jsp+servlet做的医院挂号看诊管理系统
java·javascript·mysql
Dxy12393102163 小时前
Python对图片进行加密,js前端进行解密
前端·javascript·python
江城开朗的豌豆4 小时前
路由对决:Vue Router vs React Router,谁是你的菜?
前端·javascript·react.js