web逆向笔记:js逆向案例四 QQ音乐 sign值(webpack打包代码如何扣取)
小爬菜 2024-06-22 08:03:01 阅读 69
web逆向笔记:js逆向案例四 QQ音乐 sign值(webpack打包代码如何扣取)
一、webpack介绍:
Webpack 是一个模块打包工具,主要用于 JavaScript 应用程序。它可以将许多模块打包成一个或多个 bundle,从而优化加载速度和减少加载时间。Webpack 可以处理各种资源,包括 JavaScript、CSS、图片、字体等,并且可以将其转换为浏览器可以理解的格式。
Webpack 的主要特点包括:
模块化:Webpack 允许你将应用程序划分为许多小的、独立的模块,这些模块可以被重复使用,并且可以轻松地进行更改和更新。高效性:Webpack 使用高级优化算法来最小化生成的 bundle 的大小,从而加快加载速度。它还支持代码拆分、懒加载等功能,以便进一步优化性能。灵活性:Webpack 支持各种插件和加载器,可以用于处理不同类型的资源,并且可以扩展其功能以适应不同的项目需求。可配置性:Webpack 提供了丰富的配置选项,可以根据项目需求进行灵活的配置,以满足不同的构建要求。
总的来说,Webpack 是一个强大的工具,可以帮助开发人员更高效地构建和维护 JavaScript 应用程序。它已经成为前端开发中不可或缺的一部分,被广泛使用于各种项目中。
二、webpack
打包简介
1、webpack
数组形式
!function(e) { var t = { }; // 加载器 所有的模块都是从这个函数加载 执行 function n(r) { if (t[r]) return t[r].exports; var o = t[r] = { i: r, l: !1, exports: { } }; return e[r].call(o.exports, o, o.exports, n), o.l = !0, o.exports } n(0)} ([ function () { console.log('123456') }, function () { console.log('模块2') }, ])
2、webpack
对象形式
!function(e) { var t = { }; // 所有的模块 都是从这个加载器 执行的 分发器 function n(r) { if (t[r]) return t[r].exports; var o = t[r] = { i: r, l: !1, exports: { } }; return e[r].call(o.exports, o, o.exports, n), o.l = !0, o.exports } n('xialuo') // 对象 根据KEY 找模块}({ 0: function () { console.log('我是模块1 负责加密') }, 'xialuo': function () { console.log('我是模块2 负责解密') }, 2: function () { console.log('我是模块3 负责爬数据') } });
三、多个JS
文件打包
如果模块比较多,就会将模块打包成JS文件, 然后定义一个全局变量 window[“webpackJsonp”] = [ ],它的作用是存储需要动态导入的模块,然后重写 window[“webpackJsonp”] 数组的 push( ) 方法为 webpackJsonpCallback( ),也就是说 window[“webpackJsonp”].push( ) 其实执行的是 webpackJsonpCallback( ),window[“webpackJsonp”].push( )接收三个参数,第一个参数是模块的ID,第二个参数是 一个数组或者对象,里面定义大量的函数,第三个参数是要调用的函数(可选)
四、QQ音乐sign值获取
1、接口分析
// sign值生成// n方法为加载器o = n(147).default;i = o(e.data);sign = i;
2、扣取加载器方法
window = global;// 加载器!function(e) { function t(t) { for (var r, n, f = t[0], c = t[1], i = t[2], l = 0, u = []; l < f.length; l++) n = f[l], Object.prototype.hasOwnProperty.call(o, n) && o[n] && u.push(o[n][0]), o[n] = 0; for (r in c) Object.prototype.hasOwnProperty.call(c, r) && (e[r] = c[r]); for (b && b(t); u.length; ) u.shift()(); return d.push.apply(d, i || []), a() } function a() { for (var e, t = 0; t < d.length; t++) { for (var a = d[t], r = !0, n = 1; n < a.length; n++) { var c = a[n]; 0 !== o[c] && (r = !1) } r && (d.splice(t--, 1), e = f(f.s = a[0])) } return e } var r = { } , n = { 21: 0 } , o = { 21: 0 } , d = []; function f(t) { if (r[t]) return r[t].exports; var a = r[t] = { i: t, l: !1, exports: { } }; return e[t].call(a.exports, a, a.exports, f), a.l = !0, a.exports } // 定义全局变量介绍加载器方法 window.xiaopacai = f; f.e = function(e) { var t = []; n[e] ? t.push(n[e]) : 0 !== n[e] && { 1: 1, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1, 8: 1, 9: 1, 10: 1, 11: 1, 12: 1, 13: 1, 14: 1, 15: 1, 16: 1, 17: 1, 18: 1, 19: 1, 20: 1, 22: 1, 23: 1, 24: 1, 25: 1, 26: 1 }[e] && t.push(n[e] = new Promise((function(t, a) { for (var r = "css/" + ({ 1: "common", 3: "album", 4: "albumDetail", 5: "album_mall", 6: "category", 7: "cmtpage", 8: "download_detail", 9: "index", 10: "msg_center", 11: "mv", 12: "mvList", 13: "mv_toplist", 14: "notfound", 15: "player", 16: "player_radio", 17: "playlist", 18: "playlist_edit", 19: "profile", 20: "radio", 22: "search", 23: "singer", 24: "singer_list", 25: "songDetail", 26: "toplist" }[e] || e) + "." + { 1: "2e3d715e72682303d35b", 3: "5cf0d69eaf29bcab23d2", 4: "798353db5b0eb05d5358", 5: "df4c243f917604263e58", 6: "20d532d798099a44bc88", 7: "e3bedf2b5810f8db0684", 8: "e3bedf2b5810f8db0684", 9: "ea0adb959fef9011fc25", 10: "020422608fe8bfb1719a", 11: "8bdb1df6c5436b790baa", 12: "47ce9300786df1b70584", 13: "4aee33230ba2d6b81dce", 14: "e6f63b0cf57dd029fbd6", 15: "1d2dbefbea113438324a", 16: "d893492de07ce97d8048", 17: "9484fde660fe93d9f9f0", 18: "67fb85e7f96455763c83", 19: "5e8c651e74b13244f7cf", 20: "3befd83c10b19893ec66", 22: "b2d11f89ea6a512a2302", 23: "c7a38353c5f4ebb47491", 24: "df0961952a2d3f022894", 25: "4c080567e394fd45608b", 26: "8edb142553f97482e00f" }[e] + ".chunk.css?max_age=2592000", o = f.p + r, d = document.getElementsByTagName("link"), c = 0; c < d.length; c++) { var i = (b = d[c]).getAttribute("data-href") || b.getAttribute("href"); if ("stylesheet" === b.rel && (i === r || i === o)) return t() } var l = document.getElementsByTagName("style"); for (c = 0; c < l.length; c++) { var b; if ((i = (b = l[c]).getAttribute("data-href")) === r || i === o) return t() } var u = document.createElement("link"); u.rel = "stylesheet", u.type = "text/css", u.onload = t, u.onerror = function(t) { var r = t && t.target && t.target.src || o , d = new Error("Loading CSS chunk " + e + " failed.\n(" + r + ")"); d.code = "CSS_CHUNK_LOAD_FAILED", d.request = r, delete n[e], u.parentNode.removeChild(u), a(d) } , u.href = o, 0 !== u.href.indexOf(window.location.origin + "/") && (u.crossOrigin = "anonymous"), document.getElementsByTagName("head")[0].appendChild(u) } )).then((function() { n[e] = 0 } ))); var a = o[e]; if (0 !== a) if (a) t.push(a[2]); else { var r = new Promise((function(t, r) { a = o[e] = [t, r] } )); t.push(a[2] = r); var d, c = document.createElement("script"); c.charset = "utf-8", c.timeout = 120, f.nc && c.setAttribute("nonce", f.nc), c.src = function(e) { return f.p + "js/" + ({ 1: "common", 3: "album", 4: "albumDetail", 5: "album_mall", 6: "category", 7: "cmtpage", 8: "download_detail", 9: "index", 10: "msg_center", 11: "mv", 12: "mvList", 13: "mv_toplist", 14: "notfound", 15: "player", 16: "player_radio", 17: "playlist", 18: "playlist_edit", 19: "profile", 20: "radio", 22: "search", 23: "singer", 24: "singer_list", 25: "songDetail", 26: "toplist" }[e] || e) + ".chunk." + { 1: "0b15a31f7bc269ea76ff", 3: "b3395a2d475262b98fa7", 4: "dea94b21a47cdb6d0f65", 5: "f5b6937e84f33133b31d", 6: "6c4ac3718d0230ac3b1c", 7: "ae411fac801093307ebc", 8: "f1c40f6b3a431ca4c9ac", 9: "52f2369df6a4a3649011", 10: "90aef56793aff533bf57", 11: "4c23320d028878580c26", 12: "b43316a48154164d557b", 13: "8adf08693025ab48224f", 14: "89eb6da604ebcf2dda2d", 15: "c9d5d7c9966dea2b213c", 16: "07b3290e08abf8a4e901", 17: "6838a647ca4abb619832", 18: "9d2cbd13db3328dcd357", 19: "ce6940fdeda857506a27", 20: "8af74f665077243ecefa", 22: "5a013d73a1da88cc221e", 23: "469f622f5dffdeee26eb", 24: "9df420e7d63b8d867fd2", 25: "9bea17905ada32dde9b5", 26: "bcb481bd9dd2001370ac" }[e] + ".js?max_age=2592000" }(e), 0 !== c.src.indexOf(window.location.origin + "/") && (c.crossOrigin = "anonymous"); var i = new Error; d = function(t) { c.onerror = c.onload = null, clearTimeout(l); var a = o[e]; if (0 !== a) { if (a) { var r = t && ("load" === t.type ? "missing" : t.type) , n = t && t.target && t.target.src; i.message = "Loading chunk " + e + " failed.\n(" + r + ": " + n + ")", i.name = "ChunkLoadError", i.type = r, i.request = n, a[1](i) } o[e] = void 0 } } ; var l = setTimeout((function() { d({ type: "timeout", target: c }) } ), 12e4); c.onerror = c.onload = d, document.head.appendChild(c) } return Promise.all(t) } , f.m = e, f.c = r, f.d = function(e, t, a) { f.o(e, t) || Object.defineProperty(e, t, { enumerable: !0, get: a }) } , f.r = function(e) { "undefined" !== typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, { value: "Module" }), Object.defineProperty(e, "__esModule", { value: !0 }) } , f.t = function(e, t) { if (1 & t && (e = f(e)), 8 & t) return e; if (4 & t && "object" === typeof e && e && e.__esModule) return e; var a = Object.create(null); if (f.r(a), Object.defineProperty(a, "default", { enumerable: !0, value: e }), 2 & t && "string" != typeof e) for (var r in e) f.d(a, r, function(t) { return e[t] } .bind(null, r)); return a } , f.n = function(e) { var t = e && e.__esModule ? function() { return e.default } : function() { return e } ; return f.d(t, "a", t), t } , f.o = function(e, t) { return Object.prototype.hasOwnProperty.call(e, t) } , f.p = "/ryqq/", f.oe = function(e) { throw e } ; var c = window.webpackJsonp = window.webpackJsonp || [] , i = c.push.bind(c); c.push = t, c = c.slice(); for (var l = 0; l < c.length; l++) t(c[l]); var b = i; a()}([]);
3、模块扣取(可以单个模块扣取,然后运行,缺什么补什么就可以了;也可以扣取整个js文件,然后通过require(‘./webpack.js’)形式引用即可)。这里为了方便采用了引用的方式。运行结果与网页对比,发现不一致情况。
这里出现这种情况,应该是有浏览器环境检测,我们没有补充。可以放到补环境框架中运行,然后补充对应所需环境;也可以手动补几个常见的浏览器环境(如:document、navigator、location等)并给手动补充的环境挂上代理。
// 挂代理代码(主要监听get、set方法)ld = { }; ld.config = { }; ld.config.proxy = true; // 增加属性,用于执行是否开启代理操作// 获取对象类型操作ld.typeObject = function typeObject(obj){ return Object.prototype.toString.call(obj);}// 代理函数ld.proxy = function proxy(obj, objName){ if(!ld.config.proxy){ return obj; } let handler = { get:function(target, prop, receiver){ let result; try{ // 防止报错 result = Reflect.get(target, prop, receiver); // 使用映射的方法,来调用get方法 // 获取对象类型 let type = ld.typeObject(result); if(result instanceof Object){ console.log(`{get操作| 对象名:[${ objName}] -->属性名:[${ prop.toString()}], 返回类型:[${ type}]}`); result = ld.proxy(result, `${ objName}.${ prop.toString()}`); }else if(typeof result === "symbol"){ console.log(`{get操作| 对象名:[${ objName}] -->属性名:[${ prop.toString()}], 返回结果:[${ result.toString()}]}`); }else{ console.log(`{get操作| 对象名:[${ objName}] -->属性名:[${ prop.toString()}], 返回结果:[${ result}]}`); } }catch(e){ console.log(`{get操作| 对象名:[${ objName}] -->属性名:[${ prop.toString()}], 错误信息:[${ e.message}]}`); } return result }, set:function(target, prop, value, receiver){ let result try{ // 防止报错 result = Reflect.set(target, prop, value, receiver); // 使用映射的方法,来调用set方法 // 获取对象类型 let type = ld.typeObject(value); if(value instanceof Object){ console.log(`{set操作| 对象名:[${ objName}] -->属性名:[${ prop.toString()}], 返回类型:[${ type}]}`); }else if(typeof value === "symbol"){ console.log(`{set操作| 对象名:[${ objName}] -->属性名:[${ prop.toString()}], 返回结果:[${ value.toString()}]}`); }else{ console.log(`{set操作| 对象名:[${ objName}] -->属性名:[${ prop.toString()}], 返回结果:[${ value}]}`); } }catch(e){ console.log(`{set操作| 对象名:[${ objName}] -->属性名:[${ prop.toString()}], 错误信息:[${ e.message}]}`); } return result }}; return new Proxy(obj, handler);}
补齐缺少的属性值,结果就和浏览器一样了。
3、构造请求,获取所需数据
import requestsimport timeimport execjsheaders = { 'authority': 'u6.y.qq.com', 'accept': 'application/json', 'accept-language': 'zh-CN,zh;q=0.9', 'cache-control': 'no-cache', 'content-type': 'application/x-www-form-urlencoded', 'pragma': 'no-cache', 'referer': 'https://y.qq.com/', 'sec-ch-ua': '"Chromium";v="122", "Not(A:Brand";v="24", "Google Chrome";v="122"', 'sec-ch-ua-mobile': '?0', 'sec-ch-ua-platform': '"Windows"', 'sec-fetch-dest': 'empty', 'sec-fetch-mode': 'cors', 'sec-fetch-site': 'same-site', 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36',}data = '{"comm":{"cv":4747474,"ct":24,"format":"json","inCharset":"utf-8","outCharset":"utf-8","notice":0,"platform":"yqq.json","needNewCode":1,"uin":0,"g_tk_new_20200303":5381,"g_tk":5381},"req_1":{"module":"vkey.GetVkeyServer","method":"CgiGetVkey","param":{"guid":"5630580667","songmid":["001Xs1NW3wgWpq","0027Z2qE0pDuKY"],"songtype":[0,0],"uin":"0","loginflag":1,"platform":"20"}},"req_2":{"module":"music.musicasset.SongFavRead","method":"IsSongFanByMid","param":{"v_songMid":["0016aXcd24qSC8","001Xs1NW3wgWpq","0027Z2qE0pDuKY","003V4xd80ty2df"]}},"req_3":{"module":"music.musichallSong.PlayLyricInfo","method":"GetPlayLyricInfo","param":{"songMID":"0016aXcd24qSC8","songID":457240977}},"req_4":{"method":"GetCommentCount","module":"music.globalComment.GlobalCommentRead","param":{"request_list":[{"biz_type":1,"biz_id":"457240977","biz_sub_type":0}]}},"req_5":{"module":"music.musichallAlbum.AlbumInfoServer","method":"GetAlbumDetail","param":{"albumMid":"001ln9YB420a7b"}},"req_6":{"module":"vkey.GetVkeyServer","method":"CgiGetVkey","param":{"guid":"9307341590","songmid":["0016aXcd24qSC8"],"songtype":[0],"uin":"0","loginflag":1,"platform":"20","filename":["RS020640pBtN0etKNM.mp3"]}}}'with open('./get_sign.js', 'r', encoding='utf-8') as f: code_js = f.read()sign = execjs.compile(code_js).call('get_sign', data)params = { '_': round(time.time() * 1000), 'sign': sign,}response = requests.post('https://u6.y.qq.com/cgi-bin/musics.fcg', params=params, headers=headers, data=data)print(response.json()["req_1"]['data']["midurlinfo"])
总结:
1、webpack打包的js文件,无论是单文件还是多文件,在逆向时首先需要找到对应的加载器。
2、加载器获取完成后,可以单个模块扣取代码并进调试,也可以扣取整个js文件,进行加载使用。
3、扣取完整代码后,出现结果与网页不一致时,大多数情况下是缺少浏览器环境;可以尝试补充一些常用的环境,并挂上代理方法查看缺少内容。
4、可以将扣取的完整代码,现在浏览器中运行,查看结果是否正确;在将代码在本地运行进行对比,可以快速确认是否是缺少环境导致结果不一样的。
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。