【爬虫实战】使用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

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

进阶使用:

扫描二维码关注公众号,回复: 17365188 查看本文章
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的规则来定位函数只是其中一条路,也不是非走不可。

免责声明

  • 教育和研究用途:本文章提供的信息和示例代码仅供教育和研究用途。它们的目的是帮助读者了解爬虫技术的原理和应用。
  • 合法合规性:请注意,网络爬虫可能会侵犯网站的服务条款或法律法规。在实际应用中,你必须确保你的爬虫活动合法、合规,并遵守所有相关法律。
  • 责任限制:作者对于读者使用文章中提供的信息和代码所导致的任何问题或法律纠纷概不负责。读者应自行承担风险并谨慎操作。
  • 合理使用:请在使用网络爬虫时保持谨慎和礼貌。不要对目标网站造成不必要的干扰或侵害他人利益。请在遵守法律的前提下使用爬虫技术。
  • 变动和更新:作者保留随时更改文章内容的权利,以反映新的法规、技术和最佳实践。
  • 资源和参考文献:本文章中的示例代码和信息可能依赖于第三方资源,作者会尽力提供相关参考文献和资源链接。作者不对这些资源的可用性或准确性负责。
  • 协商:如果您有任何关于本文内容或责任声明的疑虑或疑问,请在使用之前与专业法律顾问协商。

猜你喜欢

转载自blog.csdn.net/u013046615/article/details/134649550