1. 首页 > 电脑知识

spa2.scrape.center&webpack&js逆向,新人福音

作者:admin 更新时间:2025-07-18
摘要:基于一位“传统”掌握C.V大法的CURD编码人员,空余时间对webpack,js逆向,python爬虫的实践日志,感谢 “Python爬虫案例 | Scrape Center” 作者老师提供的案例,关于“Scrape | Movie” 逆向,刚开始接触的时候网上的老师,spa2.scrape.center&webpack&js逆向,新人福音

 

        基于一位“传统”掌握C.V 的CURD编码人员,空余 时刻对 webpack,js逆向,python爬虫的 操作日志,感谢  “Python爬虫案例 | Scrape Center” 作者老师提供的案例,关于 “Scrape | Movie” 逆向,刚开始接触的时候网上的老师都是基于 crypto-js,或者python算法去对 请求 token 做加密操作,对于小白或 传统后端人员很难入门, 因此做一个 从扣代码开始的程序员入门讲解。

一:webpack 初探

        webpack为js应用的打包工具,将ES Modules、CommonJS、AMD 代码转换成浏览器可执行的 js 文件,使用 webpack 构建的源码项目,打包后会生成 

  app.e9fbf43f…..js,

  chunk-vendors.77daf991.js,

  chunk-10192a00.243cb8b7.js

  ……  

js文件。app.x.js 文件为 应用的入口文件/加载器文件。chunk-10192a00.243cb8b7.js,则是为 模块文件

1、app.js、chunk-10192a00.243cb8b7.js 文件结构初探

主要文件 app.js

window=global; var __webpack_modules__ = {}; function webpackJsonpCallback(data) { var chunkId = data[0]; // 模块 ID var modules = data[1]; // 模块代码 var runtime = data[2]; // 运行时回调(可选) // 将新加载的模块加入 Webpack 内部模块管理 for (var moduleId in modules) { if (Object.prototype.hasOwnProperty.call(modules, moduleId)) { __webpack_modules__[moduleId] = modules[moduleId]; } } if (runtime) runtime(); } window["webpackJsonp"] = [] window["webpackJsonp"].push = webpackJsonpCallback; const myModule = require('./webpackJsonp_demo1.js'); !(function (e) { var c = {} // 已加载的模块 function n(t) { // 如果模块已加载,则直接返回 if (c[t]) {return c[t].exports;} // 创建新模块对象 var a = c[t] = { i: t, // 模块 ID l: !1, // 是否加载完成 exports: {} // 模块导出对象 }; e[t].call(a.exports, a, a.exports, n), // 执行模块函数 a.l = !0; // 标记模块已加载 return a.exports; } n.m = e; // 存储所有模块 // 入口执行模块 114bfd n("114bfd"); })({ "114bfd":function () { console.log('模块 114bfd') }, "113bfd":function () { console.log('模块 113bfd') }, "112bfd":function () { console.log('模块 112bfd') } });

 模块文件 webpackJsonp_demo1.js

(window["webpackJsonp"] = window["webpackJsonp"] || []).push([["chunk-10192a00"], { "5a19": function() { console.log("chunk-10192a00 5a19") } }])

解释 app.js 文件 里面的参数

具体的明确解释,这位老师解释很清楚 js逆向-webpack – peng_boke – 博客园

e :app.js里面定义的模块数组

加载器 (n(t)):

n(114bfd) 被调用,意味着执行模块 114bfd。 n(114bfd) 检查 c[5a19] 是否已加载(最开始是 undefined)。加载直接返回return c[114bfd].exports; c[114bfd] 还未加载,因此创建 c[114bfd], 接着执行e[114bfd].call(a.exports, a, a.exports, n) a.l = !0 表示该模块加载完成。 返回 a.exports(但这里没有 exports, 因此 undefined)。

拓展资料:

这个代码实现了一个简化版的 Webpack 模块加载器。 其中 n(t) 负责按需加载模块,并用 c 缓存已加载模块。 入口 n(114bfd) 直接执行了 console.log('模块 114bfd')

二:浏览器调试分析

         以上关于 webpack 的解释 和文件的描述,接下来准备 开始分析 Scrape | Movie 流程浏览器调试,

1、打开浏览器开发者调试工具,请求目的网址,查看请求头加密数据,由截图可知,当前加密数据为 token,

2、借用浏览器搜索工具检索加密参数 token

3、根据调试工具定位到发送网络请求的地方有在处理 token 的加密,添加断点调试加密函数

根据上面对 webpack 调用的研究,当前网络请求在 d504 模块里面,就能得到,上层存在函数使用类似于 n(d504) 这样的方式调用,当前的网络模块去处理,具体的调用可以在 浏览器的函数堆栈里面查看 ,我们需要解决 token 的 难题,根据代码分析,得到 

this.$store.state.url.index = "/api/movie" var a = (this.page - 1) * this.limit var e = token = Object(i["a"])(this.$store.state.url.index, a);
 4、根据token的生成,可以得出 i[“a”] 为 一个函数,其中i = e(“7d92”),e为一个模块加载器,得到7d92模块,模块里面有一个 a 函数,调用 a 函数 传入两个参数, 最后得到密文

5、上一步 知道 i = e(“7d92”) ,我们可以在这一步添加断点,查看 e的调用, 最后定位到,e的调用是 调用了,app.js 里面的,function c(t) {}

6、定位到 7d92 模块里面得到模块 7d92

"7d92": function(t, e, r) { "use strict"; r("6b54"); var n = r("3452"); //i() 为 token 生成的函数 function i() { for (var t = Math.round((new Date).getTime() / 1e3).toString(), e = arguments.length, r = new Array(e), i = 0; i < e; i++) r[i] = arguments[i]; r.push(t); var o = n.SHA1(r.join(",")).toString(n.enc.Hex) , c = n.enc.Base .stringify(n.enc.Utf8.parse([o, t].join(","))); return c } e["a"] = i },

三:扣 js 代码,还原浏览器运行步骤

        根据以上 浏览器分析和webpack模块的调用可以推理得出,如果想要用,e= Object(i[“a”])(this.$store.state.url.index, a) 类似的 技巧调用 a 函数生成 token,则需要先调用 i 即 i = e(“7d92”) 由于 webpack代码都是做了压缩打包相关的操作,显示e调用模块,最终调用的是 function c(t){}  可以得出 i = c(“7d92”) 调用到 app.js 里面去 即可,在调用的时候,只要吧缺少的模块都补充上去, 最后就能够 原模原样的调用 i[“a”]  函数生成 token ,本篇文章不对 i = c(“7d92”) 环境做说明(….),本篇文章只会直接 模拟 7d92 模块里面的 ,i 函数做补环境操作。

1、新建app.js 加载器, 并且扣app.js 相关的代码
window=global; // 模块js const af3e9bb54 = require('./chunk-4136500c.f3e9bb54.js'); // 模块js const acb8b7 = require('./chunk-10192a00.243cb8b7.js'); // 模块js //const a77daf991 = require('./chunk-vendors.77daf991.js'); // 补充浏览器需要的环境 navigator = {} navigator.userAgent="Mozilla/5.0 (Windows NT 10.0; Win ; x ) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36" //const { JSDOM } = require('jsdom'); //window.document = new JSDOM(''); const { JSDOM } = require('jsdom'); const dom = new JSDOM(); const document = dom.window.document; window.document=document; window.location = {}; var test="https://spa2.scrape.center/"; const event = document.createEvent('Event'); event.initEvent('custom', true, true); event.timestamp = Date.now(); const element = document.createElement('div'); element.addEventListener('custom', (e) => {}); element.dispatchEvent(event); const element1 = document.createElement('div'); element1.addEventListener("keydown",(e)=>{}) element1.dispatchEvent(event); !(function(e) { function t(t) { for (var r, o, i = t[0], c = t[1], s = t[2], l = 0, f = []; l < i.length; l++) o = i[l], Object.prototype.hasOwnProperty.call(a, o) && a[o] && f.push(a[o][0]), a[o] = 0; for (r in c) Object.prototype.hasOwnProperty.call(c, r) && (e[r] = c[r]); d && d(t); while (f.length) f.shift()(); return u.push.apply(u, s || []), n() } function n() { for (var e, t = 0; t < u.length; t++) { for (var n = u[t], r = !0, o = 1; o < n.length; o++) { var i = n[o]; 0 !== a[i] && (r = !1) } r && (u.splice(t--, 1), e = c(c.s = n[0])) } return e } var r = {}, o = { app: 0 }, a = { app: 0 }, u = []; function i(e) { return c.p + "js/" + ({} [e] || e) + "." + { "chunk-4136500c": "f3e9bb54", "chunk-10192a00": "243cb8b7", "chunk-7502f973": "428355cb" } [e] + ".js" } function c(t) { if (r[t]) return r[t].exports; var n = r[t] = { i: t, l: !1, exports: {} }; // 可以做备注,以防模块加载失败的时候,可以看得到那个模块加载失败了,在去扣对应的模块来补充即可 console.log("当前加载的模块:"+t); return e[t].call(n.exports, n, n.exports, c), n.l = !0, n.exports } // 引出内部变量,后续直接 用 window 对象调用内部变量 window.c=c; })({}); // MD5 相关的加密 function getToken() { r = {}; var n = window.c("3452"); for (var t = Math.round((new Date).getTime() / 1e3).toString(), e = arguments.length, r = new Array(e), i = 0; i < e; i++) { r[i] = arguments[i]; } r.push(t); var o = n.SHA1(r.join(",")).toString(n.enc.Hex) , c = n.enc.Base .stringify(n.enc.Utf8.parse([o, t].join(","))); return c } //模拟调用 //var token =getToken("/api/movie",0); //console.log(token)

四:执行相关代码,得到“果子”

limit = 10 offset = self.get_page_number(limit, page) # 调用 js 里面的 getToken 技巧,传入参数, 最后生成token,直接通过token使用requests请求库发起请求,即可获得 结局 app_e9fbf43f_js = execjs.compile(open('./app.e9fbf43f.js', encoding='utf-8').read()) token = app_e9fbf43f_js.call("getToken", '/api/movie', offset) params = { 'limit': limit, 'offset': offset, 'token': token } response = requests.get('https://spa2.scrape.center/api/movie/', params=params, headers=self.headers) if response.status_code == 200: responseData = response.json() pageCount = responseData["count"] results = responseData["results"] 当前为第: 1  页, 总共: 10 页 [{'id': 1, 'name': '霸王别姬', 'alias': 'Farewell My Concubine', 'cover': 'https://p0.meituan.net/movie/ce4da3e03e655b5b88ed31b5cd7896cf62472.jpg@4 w_ 4h_1e_1c', 'categories': ['剧情', '爱情'], 'published_at': '1993-07-26', 'minute': 171, 'score': 9.5, 'regions': ['中国内地', '中国香港']}, {'id': 2, 'name': '这个杀手不太冷', 'alias': 'Léon', 'cover': 'https://p1.meituan.net/movie/6bea9af4524dfbd0b668eaa7e187c3df767253.jpg@4 w_ 4h_1e_1c', 'categories': ['剧情', '动作', '犯罪'], 'published_at': '1994-09-14', 'minute': 110, 'score': 9.5, 'regions': ['法国']}, {'id': 3, 'name': '肖申克的救赎', 'alias': 'The Shawshank Redemption', 'cover': 'https://p0.meituan.net/movie/283292171619cdfd5b240c8fd093f1eb255670.jpg@4 w_ 4h_1e_1c', 'categories': ['剧情', '犯罪'], 'published_at': '1994-09-10', 'minute': 142, 'score': 9.5, 'regions': ['美国']}, {'id': 4, 'name': '泰坦尼克号', 'alias': 'Titanic', 'cover': 'https://p1.meituan.net/movie/b607fba7513e7f15eab170aac1e1400d878112.jpg@4 w_ 4h_1e_1c', 'categories': ['剧情', '爱情', '灾难'], 'published_at': '1998-04-03', 'minute': 194, 'score': 9.5, 'regions': ['美国']}, {'id': 5, 'name': '罗马假日', 'alias': 'Ro n Holiday', 'cover': 'https://p0.meituan.net/movie/289f98ceaa8a0ae737d3dc01cd05ab052213631.jpg@4 w_ 4h_1e_1c', 'categories': ['剧情', '喜剧', '爱情'], 'published_at': '1953-08-20', 'minute': 118, 'score': 9.5, 'regions': ['美国']}, {'id': 6, 'name': '唐伯虎点秋香', 'alias': 'Flirting Scholar', 'cover': 'https://p0.meituan.net/movie/da 660f82b98cdc1b8a3804e69609e041108.jpg@4 w_ 4h_1e_1c', 'categories': ['喜剧', '爱情', '古装'], 'published_at': '1993-07-01', 'minute': 102, 'score': 9.5, 'regions': ['中国香港']}, {'id': 7, 'name': '乱世佳人', 'alias': 'Gone with the Wind', 'cover': 'https://p0.meituan.net/movie/223c3e186db3ab4ea3bb14508c709400427933.jpg@4 w_ 4h_1e_1c', 'categories': ['剧情', '爱情', '历史', '战争'], 'published_at': '1939-12-15', 'minute': 238, 'score': 9.5, 'regions': ['美国']}, {'id': 8, 'name': '喜剧之王', 'alias': 'The King of Comedy', 'cover': 'https://p0.meituan.net/movie/1f0d671f6a37f9d7b015e4682b8b113e174332.jpg@4 w_ 4h_1e_1c', 'categories': ['剧情', '喜剧', '爱情'], 'published_at': '1999-02-13', 'minute': 85, 'score': 9.5, 'regions': ['中国香港']}, {'id': 9, 'name': '楚门的 全球', 'alias': 'The Tru n Show', 'cover': 'https://p0.meituan.net/movie/8959888ee0c399b0fe53a714bc8a5a17460048.jpg@4 w_ 4h_1e_1c', 'categories': ['剧情', '科幻'], 'published_at': None, 'minute': 103, 'score': 9.0, 'regions': ['美国']}, {'id': 10, 'name': '狮子王', 'alias': 'The Lion King', 'cover': 'https://p0.meituan.net/movie/27b76fe6cf3903f3d74963f70786001e1438406.jpg@4 w_ 4h_1e_1c', 'categories': ['动画', '歌舞', '冒险'], 'published_at': '1995-07-15', 'minute': 89, 'score': 9.0, 'regions': ['美国']}]

以上文章是本人对于js逆向的探讨,文章写得很不谨慎,关键在于自己 操作的心得探讨,如果有同学那里不懂的,可以展开交流 进修。