案例-七麦数据

发布时间 2023-08-22 20:09:29作者: 屠魔的少年

地址:  https://www.qimai.cn/rank

 

逆向的目标是请求参数:

全局搜索:第三个文件不用看了,因为是CSS文件。

看到下面的请求的是URL有所搜索的关键字,而关键字是来自请求参数的。好像不太对。所以搜索 analysis 没啥用,要用其他方案搜索。

观察到每个请求都有analysis,说明所有请求都要过一个地方,根据经验,应该是axios,应该是一个拦截器做的事。

观察一下启动器,用到了promise。

 

所以应该搜索 interceptors

 

看到下面,这是源码

 

 

 

 

python代码:

import subprocess
from functools import partial

subprocess.Popen = partial(subprocess.Popen, encoding="utf-8")

import execjs
import requests
import json

f = open("七麦.js", mode='r', encoding="utf-8")
js_code = f.read()
f.close()

js = execjs.compile(js_code)

url = "https://api.qimai.cn/rank/index"

params = {
    "brand": "free",
    "device": "iphone",
    "country": "cn",
    "genre": "36",
    "date": "2023-05-15",
    "page": 3,
    "is_rank_index": 1,
    "snapshot": "23:08:06"
}

analysis = js.call("fn", url, params)
params['analysis'] = analysis


resp = requests.get(url, params, headers={
    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36"
})
print(resp.json())

 

七麦.js
 // # 所有请求都有该参数.
// # 用的应该是axios
// # 参数应该是在axios的拦截器上进行的加密
// # 拦截器搜不到...

// haha = 'interc'+'eptors'
//
// axios[haha]['request']["use"](function(){}, function(){})
//
// axios.interceptors.response.use(function(){}, function(){})

// 猜测. 网站的逻辑应该是用上述逻辑完成的.

// 遇到这种情况怎么办?
// 从axios源码下手....
/**
axios.interceptors.request => 里面有个[] <= req_lst
  axios.interceptors.request.use(请求前resolve1, 请求前reject1)
axios.interceptors.request.use(function(){}, function(){})
会改变req_lst
 [{fulfilled: function(){}, rejected: function(){}}]


axios.interceptors.response => 里面有个[] <= resp_lst
 axios.interceptors.response.use(响应后resolve1, 响应后reject1)
会改变resp_lst

 [{fulfilled: function(){}, rejected: function(){}}]

 var t = [请求前resolve1, 请求前reject1, 发请求, undefined, 响应后resolve1, 响应后reject1];
 // 把请求拦截器中所有的对象: {fulfilled, rejected}循环一遍
 this.interceptors.request.forEach((function(e) {
    t.unshift(e.fulfilled, e.rejected)
}
));

 this.interceptors.response.forEach((function(e) {
    t.push(e.fulfilled, e.rejected)
}
));

for ( ;t.length; ){
    n = n.then(响应后resolve2, 响应后reject2);
    debugger;  =>  t => [请求拦截器, 发请求, undefined, 响应拦截器的东西]
    n = n.then(t.shift(), t.shift());
}
return n


 for(第一部分;第二部分;第三部分){

 }
 */

// global就是node的全局
window = global;

function o(n) {
    t = "",
    ['66', '72', '6f', '6d', '43', '68', '61', '72', '43', '6f', '64', '65']['forEach'](function(n) {
        t += window['unescape']('%u00' + n)
    });
    var t, e = t;
    return window['String']["fromCharCode"](n)
}

function v1(t) {
    t = window['encodeURIComponent'](t)["replace"](/%([0-9A-F]{2})/g, function(n, t) {
        return o("0x" + t)
    });
    try {
        return window["btoa"](t); // base64  浏览器的
    } catch (n) {
        return window["Buffer"]["from"](t)["toString"]("base64"); // base64  nodejs的
    }
}

function h(n, t) {

        for (var e = (n = n["split"](""))["length"], r = t["length"], a = "charCodeAt", i = 0; i < e; i++)
            n[i] = o(n[i][a](0) ^ t[(i + 10) % r][a](0));
        return n["join"]("")
    }

function fn(url, params) {
    let baseURL = 'https://api.qimai.cn'
    let s = -56;
    var e, r = +new Date - (s) - 1661224081041, a = [];
    void 0 === params && (params = {}),
        // 循环参数. 然后把参数的值. 塞入 数组a中
    Object.keys(params)["forEach"](function(n) {
        if (n === "analysis")
            return !1;
        params["hasOwnProperty"](n) && a["push"](params[n])
    }),
    a = a["sort"]()["join"](""),
    a = v1(a),
    a = (a += "@#" + url["replace"](baseURL, "")) + ("@#" + r) + ("@#" + 3);
    e = v1(h(a, "xyz517cda96abcd"));
    return e;
}