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.Base64.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; Win64; x64) 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.Base64.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@464w_644h_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@464w_644h_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@464w_644h_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@464w_644h_1e_1c', 'categories': ['剧情', '爱情', '灾难'], 'published_at': '1998-04-03', 'minute': 194, 'score': 9.5, 'regions': ['美国']}, {'id': 5, 'name': '罗马假日', 'alias': 'Roman Holiday', 'cover': 'https://p0.meituan.net/movie/289f98ceaa8a0ae737d3dc01cd05ab052213631.jpg@464w_644h_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/da64660f82b98cdc1b8a3804e69609e041108.jpg@464w_644h_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@464w_644h_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@464w_644h_1e_1c', 'categories': ['剧情', '喜剧', '爱情'], 'published_at': '1999-02-13', 'minute': 85, 'score': 9.5, 'regions': ['中国香港']}, {'id': 9, 'name': '楚门的世界', 'alias': 'The Truman Show', 'cover': 'https://p0.meituan.net/movie/8959888ee0c399b0fe53a714bc8a5a17460048.jpg@464w_644h_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@464w_644h_1e_1c', 'categories': ['动画', '歌舞', '冒险'], 'published_at': '1995-07-15', 'minute': 89, 'score': 9.0, 'regions': ['美国']}]

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

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容