execjs
是 Python 中一个用于执行 JavaScript 代码的库,它能调用系统中安装的 JavaScript 运行时(如 Node.js、V8 等)来执行 JS 代码。以下是其常用的 API 及用法说明:
1. execjs.compile(source)
-
功能 :编译 JavaScript 代码字符串,返回一个
Context
对象,用于后续执行该代码中的函数或表达式。 -
参数 :
source
为 JavaScript 代码字符串。 -
示例 :
pythonimport execjs # 编译JS代码 js_code = """ function add(a, b) { return a + b; } """ ctx = execjs.compile(js_code)
2. Context.call(name, *args)
-
功能:调用编译后的 JavaScript 代码中的指定函数。
-
参数 :
name
:要调用的 JS 函数名(字符串)。*args
:传递给 JS 函数的参数(支持 Python 基本类型,会自动转换为 JS 类型)。
-
示例 :
python# 调用上面编译的add函数 result = ctx.call("add", 2, 3) # 调用add(2,3) print(result) # 输出:5
3. Context.eval(expression)
-
功能:执行编译后的 JavaScript 代码中的表达式,并返回结果。
-
参数 :
expression
为 JS 表达式字符串(如变量、运算式等)。 -
示例 :
python# 编译包含变量的JS代码 js_code = """ var x = 10; var y = 20; """ ctx = execjs.compile(js_code) # 执行表达式获取结果 result = ctx.eval("x + y") # 计算x+y print(result) # 输出:30
4. execjs.eval(source)
-
功能 :直接执行简单的 JavaScript 代码(无需先编译),相当于
compile(source).eval(source)
的简写。 -
适用场景:执行单行或简单的 JS 表达式(不包含复杂函数定义时更方便)。
-
示例 :
pythonresult = execjs.eval("1 + 2 * 3") # 直接执行JS表达式 print(result) # 输出:7
5. execjs.get_runtime(name=None)
-
功能 :获取指定的 JavaScript 运行时(如 Node.js、V8 等)。若不指定
name
,则返回默认运行时。 -
参数 :
name
为运行时名称(如 "Node"、"V8" 等,取决于系统中安装的运行时)。 -
示例 :
python# 获取Node.js运行时 node_runtime = execjs.get_runtime("Node") print(node_runtime.name) # 输出:Node.js
6. execjs.runtimes
-
功能:返回一个列表,包含当前系统中所有可用的 JavaScript 运行时。
-
示例 :
python# 查看所有可用的JS运行时 for runtime in execjs.runtimes: print(runtime.name) # 可能输出:Node.js、JScript、V8等
注意事项
execjs
依赖系统中已安装的 JavaScript 运行时(如 Node.js 最常用),若未安装会报错。- Python 与 JS 之间的类型转换是自动的(如 Python 的
int
→JS 的Number
,dict
→JS 的Object
等)。 - 复杂场景建议先通过
compile()
编译代码,再用call()
调用函数,效率更高。
通过这些 API,可以方便地在 Python 中集成 JavaScript 代码,适用于处理需要 JS 解析的场景(如加密逻辑、前端代码模拟等)。
使用 Chrome DevTools 逆向分析 JS 加签函数并通过 execjs 模拟执行
一、准备工作
-
安装必要工具
- Chrome 浏览器(自带 DevTools)
- Python 环境
- 安装 execjs 库:
pip install PyExecJS
- 确保系统中安装了 Node.js(execjs 会优先使用 Node.js 作为运行时)
-
基础知识
- 了解基本的 JavaScript 语法
- 了解 HTTP 请求的基本概念
- 熟悉 Python 基础语法
二、使用 Chrome DevTools 定位加签函数
1. 打开开发者工具
- 在 Chrome 中访问目标网站
- 按 F12 或 Ctrl+Shift+I (Windows/Linux) 或 Cmd+Opt+I (Mac) 打开 DevTools
- 切换到 "Network"(网络)面板
2. 找到包含加签参数的请求
- 进行触发加签逻辑的操作(如登录、提交表单等)
- 在网络面板中找到对应的请求(通常是 XHR/fetch 类型)
- 查看请求的参数,识别出疑似加签的字段(如 sign、signature、token 等)
3. 定位加签函数
有几种常用方法:
方法一:搜索关键词
- 切换到 "Sources"(源代码)面板
- 按 Ctrl+Shift+F (Windows/Linux) 或 Cmd+Opt+F (Mac) 打开全局搜索
- 搜索加签参数名(如 "sign"),查看相关代码
方法二:XHR 断点
- 在 Network 面板中,右键点击目标请求,选择 "Copy" → "Copy as cURL"
- 切换到 Sources 面板,在 XHR/fetch 断点处点击 "+",输入包含加签参数的 URL 部分
- 触发请求,程序会在发送请求前暂停,通过调用栈找到加签逻辑
方法三:观察调用栈
- 在 Sources 面板中,找到可能包含加签逻辑的 JS 文件
- 在可疑代码处设置断点(点击行号)
- 触发请求,程序暂停后,通过右侧 "Call Stack"(调用栈)回溯找到加签函数
三、分析并提取加签函数
-
理解加签逻辑
- 观察加签函数的输入参数
- 分析函数内部的加密算法(如 MD5、SHA、HMAC 等)
- 注意是否有时间戳、随机数等动态参数参与计算
-
提取关键代码
- 复制加签函数的完整代码
- 检查该函数是否依赖其他函数或变量,一并提取
- 注意处理可能的代码混淆(如变量名压缩、控制流平坦化等)
-
测试提取的代码
- 在 DevTools 的 "Console"(控制台)中粘贴代码
- 手动调用函数,验证是否能生成正确的签名
四、使用 execjs 执行提取的代码
1. 基本示例
假设我们提取的加签函数如下:
javascript
function generateSign(params, secret) {
// 对参数进行排序
let sortedKeys = Object.keys(params).sort();
let str = '';
for (let key of sortedKeys) {
str += key + '=' + params[key] + '&';
}
str += 'secret=' + secret;
// 使用 MD5 加密
return md5(str);
}
// MD5 函数(假设已提取)
function md5(s) {
// 具体实现...
}
2. Python 代码实现
python
import execjs
import json
# 读取提取的JS代码
with open('sign_code.js', 'r', encoding='utf-8') as f:
js_code = f.read()
# 编译JS代码
ctx = execjs.compile(js_code)
def generate_sign(params, secret):
"""调用JS加签函数生成签名"""
try:
# 调用JS中的generateSign函数
sign = ctx.call('generateSign', params, secret)
return sign
except Exception as e:
print(f"生成签名出错: {e}")
return None
if __name__ == "__main__":
# 测试参数
test_params = {
'username': 'test',
'password': '123456',
'timestamp': 1620000000,
'nonce': 'abc123'
}
test_secret = 'my_secret_key'
# 生成签名
sign = generate_sign(test_params, test_secret)
print(f"生成的签名: {sign}")
# 可以将签名添加到请求参数中发送请求
# request_params = {**test_params, 'sign': sign}
# 发送请求...
3. 处理复杂依赖
如果加签函数依赖较多其他函数或库,可以:
1.** 完整提取依赖 :将所有相关函数和变量都提取到 JS 文件中 2. 使用虚拟环境 **:对于浏览器特定的 API(如 window、document),可以在 JS 代码中添加模拟实现
javascript
// 模拟浏览器环境
var window = {};
var document = {
cookie: ''
};
3.** 分模块处理 **:如果代码量很大,可以拆分成多个 JS 文件,在 Python 中依次加载
五、常见问题及解决方案
1.** 运行时错误 **- 确保 Node.js 已正确安装并配置到环境变量
- 检查 JS 代码是否有语法错误
- 确认所有依赖函数都已被正确提取
2.** 签名不匹配 **- 检查参数排序方式是否与原网站一致
- 确认编码方式(如 UTF-8)是否正确
- 检查是否有隐藏参数参与签名计算
3.** 代码混淆处理 **- 使用在线工具(如 jsbeautifier)格式化代码
- 逐步调试,替换混淆的变量名,提高可读性
- 对于复杂混淆,可考虑使用 AST 工具辅助分析
六、注意事项
1.** 法律与伦理 :确保你的逆向分析行为符合相关法律法规,仅用于合法目的 2. 网站政策 :尊重网站的 robots.txt 和使用条款 3. 动态变化 :网站的加签逻辑可能会定期更新,需要定期维护你的代码 4. 性能考虑 **:对于高频调用,可考虑将 JS 代码转换为 Python 实现以提高性能
通过以上步骤,你可以成功逆向分析网站的加签逻辑并使用 Python 进行模拟执行。实际操作中可能会遇到各种复杂情况,需要结合具体场景灵活运用调试技巧。