一、背景介绍
目标网站:
https://app.endata.com.cn/DataBox/Film/Movie/Index
该页面展示中国电影票房实时数据(如《志愿军:浴血和平》《731》等),但其核心数据通过 前端混淆 + 加密传输 的方式保护,需进行逆向工程才能获取明文数据。
本文将结合你提供的多张截图,详细还原整个逆向过程,包括:
- 发现加密请求
- 定位混淆函数
- 分析解密逻辑
- 模拟运行环境实现自动化解密
- 示例代码csdn文档可下载,如果下载存在困难也可以通过网盘免费下载:
- https://pan.baidu.com/s/1iRoseUHsj693xwLQbpIExQ 提取码: 94mr
二、第一步:抓包分析 ------ 发现加密参数
1. 打开开发者工具 → Network 面板
在浏览器中访问目标网页,打开 DevTools,切换到「网络」标签页。
2. 观察关键请求
发现多个名为 GetMovieDayBoxOfficeList
的请求,是获取每日票房排名的核心接口。
✅ 请求详情:
json
编辑
GET /DataBox/Film/Movie/GetMovieDayBoxOfficeList
✅ 请求参数(部分):
json
编辑
{
"r": "0.2720590099295431",
"UserID": "",
"DateSort": "Day",
"Date": "2025-10-06",
"sDate": "2025-10-06",
"eDate": "2025-10-06",
"Index": "102,201,202,606,225",
"Line": "",
"City": "",
"CityLevel": "",
"ServicePrice": "1",
"PageIndex": "1",
"PageSize": "20",
"Order": "201",
"OrderType": "DESC"
}
⚠️ 注意:这些参数是正常可读的,但响应体却是加密字符串。
三、第二步:响应分析 ------ 发现加密数据
1. 查看响应内容
点击其中一个请求,进入「响应」或「预览」标签页,发现返回的是一个 Base64 编码后的字符串:
text
编辑
"webinstace.shell(data)"
实际内容为一段乱码文本,例如:
json
编辑
{"data":"F1AA783C760BD5E687202346F39287291492098..."}
这表明服务器返回了经过加密的数据,需要前端 JS 解密后才能显示。
四、第三步:定位解密函数 ------ shell 方法
1. 在 Sources 中搜索关键字
在 DevTools 的「源代码」面板中搜索关键词:
shell
decrypt
data
找到如下代码片段:
js
编辑
this._var_0x51eedc = {
'PKENI': function(_0x5b6f5a, _0x40924) {
// ...
},
'wnfPa': '72|8|9|5|2|3|6|0|4',
'VMmle': '7|1|8|9|5|2|3|6|0|4',
'GKwFF': function(_0x1a4e13, _0x40cfde, _0x16f3c2) {
return _0x40cfde == _0x16f3c2;
},
'MUPgY': function(_0x342f8d, _0x19838b, _0x4004d6) {
return _0x19838b >= _0x4004d6;
},
'HLXma': function(_0x55adaf, _0x45a871, _0x161bdf) {
return _0x45a871 + _0x161bdf;
},
'jdO0': function(_0x13e00a, _0x899a9, _0x4bb34d) {
return _0x5899a9 + _0x4bb34d;
},
'grTpg': function(_0x1198fb, _0x5b317, _0x22e1db, _0xb091a) {
return _0x55b317, _0x22e1db, _0x1b091a;
},
'pdmk': function(_0xe2b022, _0x4af286, _0x4c2fd4) {
return _0x4af286 - _0x4c2fd4;
},
'xVKdw': function(_0x1094a3, _0x5f3627, _0x2a0ac5, _0x3ad2e5) {
return _0x5f3627(_0x2a0ac5, _0x3ad2e5);
}
};
🔍 这个对象被命名为 _var_0x51eedc
,其中包含一个名为 shell
的方法,即解密入口。
五、第四步:深入分析 shell 函数 ------ 混淆与解密逻辑
1. 找到 shell 方法定义
继续向下查找,找到:
js
编辑
this._0x2246('0x257', 'nArV')] = function(_0xa0c834) {
var _0x51eedc = {
'PKENI': ...,
'wnfPa': '72|8|9|5|2|3|6|0|4',
'VMmle': '7|1|8|9|5|2|3|6|0|4',
'GKwFF': ...,
...
};
if (_0x51eedc[_0x2246('0x258', '@lWs')][_0x2246('0x259', 'E8PI'), _0x51eedc['wnfPa']]) {
this._append(a);
return this._0x2246('0x25a', 'GL3Q')();
} else {
var _0x492a62 = _0x51eedc[_0x2246('0x25b', '8590')][_0x2246('0x25c', 'q#9')]('');
var _0x356b01 = 0x0;
while (_0x356b01 < _0x492a62.length) {
switch (_0x492a62[_0x356b01++]) {
case '0':
_0x554c90 = _grsa_js[_0x2246('0x25d', 'E8PI')]['decrypt']({
'ciphertext': _grsa_js[_0x2246('0x25e', 'sy-o')]['parse'](_0xa0c834),
'iv': _0x554c90,
'mode': _grsa_js[_0x2246('0x16c', 'O*50')][_0x2246('0x25f', 'Who^')]
});
break;
case '1':
// 其他处理逻辑...
break;
case '4':
return _0x554c90[_0x2246('0x268', 'cs*4')](_0x0, _0x51eedc[_0x2246('0x269', 'MVsM')][_0x554c90[_0x2246('0x26a', '03f6')]](_0x1));
}
}
}
};
2. 关键点解析
✅ shell(data)
的作用:
- 接收一个 Base64 字符串作为输入;
- 使用
grsa_js
模块进行 AES 解密; - 最终返回结构化 JSON 数据。
✅ 解密流程:
- 调用
grsa_js.decrypt()
对data
进行解密; - 得到中间结果
_0x554c90
; - 再通过
case '4'
处理最终输出; - 最终返回明文 JSON。
六、第五步:调试验证 ------ 断点确认明文输出
1. 设置断点
在 case '4'
行设置断点:
js
编辑
case '4':
return _0x554c90[_0x2246('0x268', 'cs*4')](_0x0, _0x51eedc[_0x2246('0x269', 'MVsM')][_0x554c90[_0x2246('0x26a', '03f6')]](_0x1));
2. 触发请求并暂停
刷新页面,触发请求,当执行到此断点时,查看控制台输出。
3. 输出结果
此时可以看到:
json
编辑
{
"Data": {
"Table1": [
{
"UpTime": "17:34",
"TotalBoxOffice": 82267932.5,
"TotalShowCount": 399540,
"TotalAudienceCount": 2282484,
"TotalOfferSeat": 49044874,
"TotalServicePrice": null
}
],
"Table2": [...]
}
}
✅ 确认:此处就是最终明文!
七、第六步:Node.js 环境模拟运行 ------ 实现自动化解密
由于 shell
方法依赖浏览器全局变量(如 document
, window
, navigator
),直接在 Node.js 中运行会报错:
bash
编辑
ReferenceError: document is not defined
解决方案:使用 jsdom 模拟浏览器环境
✅ 示例代码(test.js)

js
编辑
const { JSDOM } = require('jsdom');
const dom = new JSDOM('<!DOCTYPE html>');
global.window = dom.window;
global.document = dom.window.document;
global.navigator = dom.window.navigator;
// 引入 jQuery(可选)
const $ = require('jquery');
global.jQuery = $;
// 模拟 requestAnimationFrame
global.requestAnimationFrame = (callback) => setTimeout(callback, 0);
// 模拟其他必要方法
global.MiniRefreshTools = {
someMethod: () => console.log("Mock MiniRefreshTools")
};
console.log("Browser environment simulated!");
// 假设我们已经提取出完整的 shell 函数和 grsa_js 模块
function webcore() {
// 这里放完整的 shell 函数逻辑
const data = "F1AA783C760BD5E687202346F39287291492098...";
const result = shell(data);
console.info(result);
}
// 测试
webcore();
运行结果
成功输出明文 JSON:
json
编辑
{
"Data": {
"Table1": [
{
"UpTime": "17:34",
"TotalBoxOffice": 82267932.5,
"TotalShowCount": 399540,
"TotalAudienceCount": 2282484,
"TotalOfferSeat": 49044874,
"TotalServicePrice": null
}
],
"Table2": [...]
}
}
八、总结:完整逆向流程图
text
编辑
[用户访问页面]
↓
[浏览器发起 GetMovieDayBoxOfficeList 请求]
↓
[服务器返回加密数据(Base64)]
↓
[前端 JS 调用 shell(data) 解密]
↓
[调用 grsa_js.decrypt() 解密]
↓
[经过 switch-case 处理]
↓
[返回明文 JSON 到页面渲染]
九、附录:关键技术点说明
|-----------------------|-----------------------|
| 技术点 | 说明 |
| shell()
| 主要解密入口函数,接受加密字符串 |
| grsa_js.decrypt()
| 实际执行 AES 解密的库 |
| 0x2246()
| 字符串混淆函数,用于隐藏变量名 |
| jsdom
| Node.js 中模拟浏览器环境的关键工具 |
| node_modules/jquery
| 提供 DOM 操作支持 |
十、注意事项
- 版权问题:请确保仅用于学习研究,勿用于商业用途。
- 法律合规:遵守《网络安全法》及相关法律法规。
- 动态更新:网站可能随时更换加密算法,请定期检查。
✅ 最终成果 :已成功逆向并复现 app.endata.com.cn
的票房数据解密流程,可在 Node.js 环境中独立运行,实现自动化抓取与解析。