【爬虫实战】使用Python和JS逆向基于webpack的虚拟货币平台

前言

之前遇到过很多常规的网站,接下来尝试一下分析使用webpack的网站。

一、目标整理

https://mytokencap.com/?tab_active=0 为例,获取某个虚拟货币的行情。

对应接口是

二、逻辑分析

首先补充一个webpack的知识点。

1. webpack

一个现代的静态模块打包工具,特点:

  1. 模块化支持
  2. 打包和压缩
  3. 资源加载管理。重点Loader

样式:数字+函数。

一个示例:

!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)
    n(1)
}(
    [
        function () {
            console.log('模块1')
        },

        function () {
            console.log('模块2')
        }
    ]
)
// 输出如下

// 模块1
// 模块2

模块以数组的形式存储,每个元素都是一个函数。

进阶使用:

window = global;
!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
    }

    window.r = n //将模块加载器函数 n 挂载到全局对象 window 上,使其成为全局对象的一个属性
}(
    [
        function () {
            console.log('模块1')
        },

        function () {
            console.log('模块2')
        }
    ]
)
r(0) // 相当于调用的是 n(0)
r(1)
// 分别会输出 模块1和模块2

r(0)表示调用第二个模块。

一般有两种方式:

  1. 数组索引
  2. 对象key

2. 请求分析

请求头里目前是没有什么特殊字段,载荷里面倒是有:

language: en_US
timestamp: 1700815567939
code: 81cc83a67ea69c2133030549296ba3f3
platform: web_pc
v: 0.1.0
legal_currency: USD
international: 1

可以看出有个特殊的code,看来这就是我们要关注的对象。接下来就是定位了,尝试了一下搜索关键字,并没有得到想要的结果,所以只能从启动器入手。

可以看到这个是个Ajax请求,此时的code已经生成,往前找,要记得加上XHR的接口断点,少走很多弯路。

这时候code已经生成了,继续往前找。

但是这时候的e.data的code还是已经生成了,继续往前。

然后到了这里:

  u.prototype.request = function(e) {
            "string" === typeof e ? (e = arguments[1] || {}).url = arguments[0] : e = e || {},
            (e = s(this.defaults, e)).method ? e.method = e.method.toLowerCase() : this.defaults.method ? e.method = this.defaults.method.toLowerCase() : e.method = "get";
            var t = [a, void 0]
              , n = Promise.resolve(e);
            for (this.interceptors.request.forEach((function(e) {
                t.unshift(e.fulfilled, e.rejected)
            }
            )),
            this.interceptors.response.forEach((function(e) {
                t.push(e.fulfilled, e.rejected)
            }
            )); t.length; )
                n = n.then(t.shift(), t.shift());
            return n
        }

通过interceptors可以猜测这是一个拦截器,一个请求拦截器和一个响应拦截器。一个往前放(unshift),一个往后放(push)。接下来就在n这里打断点:

可以看到t这个数组有6个元素,中间两个作为分隔符,前面是请求,后面是响应。追踪第一个函数t[0]

从代码里看到有个code,打上断点,在控制台输出一下,断定是这里生成的code值。而r是由o函数和时间戳的一些处理来组成的。如果这时候光标悬浮到o函数上可以看到 webpack-202d9e5f2ca81e3c.js:1,说明这是webpack构建的。还有一个提示是文件首行(self.webpackChunk_N_E = self.webpackChunk_N_E || []).push([[2888], { 出现的webpack也是原因。

但是需要注意的是o函数的来源是前面的o = n.n(r),r来自r = n(2568),所以这里的关键是n。至于如何找n,有两种方式。首先可以直接从o上去找,因为o=n.n(r),所以点击o就是n.n的位置。

但是一定有一个纯n函数,往上找。

这里就是要找的Loader。

还有一个方式,在var r = n(2568)打断点,刷新后点击n,找到的也是同一个函数。

这是一个自执行函数,且没有参数,所以属于多文件的打包,并没有把功能代码塞到一块,所以得一步一步来。全选文件,复制到一个js文件里,代码不再展示,这里就是核心调度器。

运行后会提示ReferenceError: self is not defined,这里的self只出现了这一行,只是全局性的变量。添加window = global;,并用window替换self。然后再运行,就不会有报错了。但是要记得待会再补代码的时候如果有self也要同样使用window。

调用的代码如下:

复制过来,但是这个n要修改一下,否则可能会和函数中的n冲突:

var timer = Date.now().toString()
var s = timer + "9527" + timer.substr(0, 6)
console.log(o()(s))

但是这时候会提示ReferenceError: o is not defined,o从哪里来呢,就在这里:

var r = n(2568)
, o = n.n(r)

这时候会提示ReferenceError: n is not defined。这时候就需要找n,但是n在闭合环境中,所以需要把n取出来。所以在前面的代码中找个合适地方插入window.nn = n,。原来的代码就可以调整一下:

var r = window.nn(2568) // 代替之前的n
var o = window.nn.n(r) // 代替之前的n.n

接下来提示TypeError: Cannot read property 'call' of undefined。其实也有两种方式找函数,一个是全选o()然后点击,或者全选n(2568)然后点击,两种方式都可以找到。o()其实就是真正的找n(2568)

这时候到最上面可以看到也是webpack,全部复制,放在刚才的文件里,再执行,提示ReferenceError: self is not defined,修改替换成window。再次运行,OK,输出:

f0e0ac2b0c281d790c54220ca9b5188d

其实self就是个全局变量,所以替换成一个自定义的全局变量。

最后写个函数返回:

// 调用

function get_code() {
    var r = window.nn(2568)
    var o = window.nn.n(r)
    timer = Date.now().toString()
    s = timer + "9527" + timer.substr(0, 6)
    return [o()(s), timer]
}

三、代码实现

Python部分

import time

import requests
import execjs

# 获取JS的动态V值
with open('btc.js') as f:
    js_code = f.read()
js_compile = execjs.compile(js_code)
code,ts = js_compile.call("get_code")
print(code,ts)

headers = {
    "authority": "api.mytokenapi.com",
    "accept": "application/json, text/plain, */*",
    "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,la;q=0.7,lv;q=0.6,da;q=0.5,sm;q=0.4",
    "cache-control": "no-cache",
    "content-type": "application/x-www-form-urlencoded;charset=utf-8",
    "dnt": "1",
    "origin": "https://mytokencap.com",
    "pragma": "no-cache",
    "referer": "https://mytokencap.com/",
    "sec-ch-ua": "\"Chromium\";v=\"116\", \"Not)A;Brand\";v=\"24\", \"Google Chrome\";v=\"116\"",
    "sec-ch-ua-mobile": "?0",
    "sec-ch-ua-platform": "\"macOS\"",
    "sec-fetch-dest": "empty",
    "sec-fetch-mode": "cors",
    "sec-fetch-site": "cross-site",
    "user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36"
}
url = "https://api.mytokenapi.com/ticker/index"
params = {
    "language": "en_US",
    "timestamp": ts,
    "code": code,
    "platform": "web_pc",
    "v": "0.1.0",
    "legal_currency": "USD",
    "international": "1"
}
response = requests.get(url, headers=headers, params=params)

print(response.json())

输出结果:

输出

四、总结

本文主要介绍了对于使用webpack打包技术的这一类网站的逆向方法和思路。当然对于逆向JS有很多种方式,根据webpack的规则来定位函数只是其中一条路,也不是非走不可。

免责声明

  • 教育和研究用途:本文章提供的信息和示例代码仅供教育和研究用途。它们的目的是帮助读者了解爬虫技术的原理和应用。
  • 合法合规性:请注意,网络爬虫可能会侵犯网站的服务条款或法律法规。在实际应用中,你必须确保你的爬虫活动合法、合规,并遵守所有相关法律。
  • 责任限制:作者对于读者使用文章中提供的信息和代码所导致的任何问题或法律纠纷概不负责。读者应自行承担风险并谨慎操作。
  • 合理使用:请在使用网络爬虫时保持谨慎和礼貌。不要对目标网站造成不必要的干扰或侵害他人利益。请在遵守法律的前提下使用爬虫技术。
  • 变动和更新:作者保留随时更改文章内容的权利,以反映新的法规、技术和最佳实践。
  • 资源和参考文献:本文章中的示例代码和信息可能依赖于第三方资源,作者会尽力提供相关参考文献和资源链接。作者不对这些资源的可用性或准确性负责。
  • 协商:如果您有任何关于本文内容或责任声明的疑虑或疑问,请在使用之前与专业法律顾问协商。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JavaScript逆向工程涉及到逆向分析、代码还原、代码修改等多个方面,其中Webpack作为一个重要的静态模块打包,也是逆向工程中需要重点关注的一部分。以下是一些关于Webpack逆向的笔记: 1. Webpack的打包过程是从入口文件开始的,Webpack会递归地遍历所有依赖模块,并将它们打包成一个或多个Bundle文件。因此,逆向工程的第一步就是找到Webpack打包的入口文件。 2. 在入口文件中,Webpack会通过require或import语句加载其他模块,并递归地遍历所有依赖模块。因此,我们可以通过分析入口文件来了解应用的整体结构和模块依赖关系。 3. 在Webpack的打包过程中,每个模块都会被赋予一个唯一的标识符,这个标识符可以通过module.id来获取。因此,我们可以通过分析打包后的代码,来了解每个模块的标识符和依赖关系。 4. 在Webpack的打包过程中,Loader和Plugin是非常重要的扩展机制,它们可以对模块进行转换和优化。因此,我们可以通过分析Loader和Plugin的源代码,来了解它们的作用和实现原理。 5. 在逆向工程中,我们可以使用Webpack的source-map选项来生成源映射文件,从而方便我们进行代码还原和调试。同时,我们也可以使用Webpack的devtool选项来设置不同的调试工具,以便更好地进行逆向分析和调试。 总的来说,Webpack作为静态模块打包,在JavaScript逆向工程中扮演着重要的角色。通过对Webpack逆向分析,我们可以更加深入地了解应用的整体结构和模块依赖关系,从而更好地进行代码还原和调试。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值