js逆向分析实战之七麦数据

1.介绍:

  要分析的网站是:https://www.qimai.cn/rank
  ①当你打开网站,打开控制台的时候,网站有debugger检测,会陷入一个死循环,让你无法调试,如下图:
在这里插入图片描述   ②在解决掉debugger问题后(稍后介绍如何解决),点击到你想爬取的数据页面如:https://www.qimai.cn/rank/index/brand/paid/device/iphone/country/cn/genre/36/date/2018-11-11 查看ajax请求,你会看到你想要的数据,but每个重要请求都会有个analysis字段,此字段便是本文的重点分析对象。如下图:
在这里插入图片描述下面正式介绍如何解决检测deugger及其analysis的逆向分析

2.准备工具:

  抓包工具:fiddler、charles
  浏览器:firefox、chrome
  工具凭个人喜好选择

3.解决debugger检测:

  ①回到本文第一张图片,查看黄色框部分。查看堆栈,点击anonymous下的e,跳转到了app.xxxxxxx.js文件的debugger执行函数位置了,如下图:
在这里插入图片描述
②分析下var a = [ 'r','e', 'g','g', 'u', 'b','e', 'd'].reverse().join(''); 这个变量a不就是“debugger”嘛,往下继续自动执行递归函数e调用debugger,造成了无法让我们正常调试。
③ok!我们已经知道是哪个函数在搞的鬼了,也知道这个函数在哪个文件了。下边我们改如何注释掉这段函数呢?我的解决办法是抓包!关于抓包工具如何使用这里就不介绍了,不会的同学请自行百度“抓包https”
打开抓包工具,拦截app.xxxxxx.js文件注释掉以下代码块
!function e(n) { (1 !== ('' + n / n).length || 0 === n) && function () { }.constructor(a) (), e(++n) }(0)
如下图:
在这里插入图片描述在这里插入图片描述已经可以正常调试了

4.analysis逆向分析:

经过以上步骤已经可以正常调试了,下边我们讲下我的分析思路。
①定位analysis字段的生成位置(这一步有很多方法)
  我的第一反应是全项目搜索,command+shift+f(mac版本firefox控制台快捷键) 搜索“analysis”,然而并没有搜到想要的结果。
  尝试第二种办法,添加xhr断点,关键词api,即api.qimai.cn中的api,刷新网页。如下图:
在这里插入图片描述分析下代码h为XMLHttpRequest对象,便是发出请求的最后一步,然后看下图右侧红框堆栈部分,如下图:
在这里插入图片描述自上而下点击堆栈开始做断点吧,看看哪部分代码块有自己想要的线索。
花费好长时间,最终在get堆栈定位到app.xxxxx.js文件的以下位置,找到了“analysis",继续断点得出r便是analysis的值。r由两个函数l.d和l.h处理o后得出
在这里插入图片描述
②解决变量o
分析下代码:这个变量o初始值不就是url的参数值然后做了个排序,然后拼成的字符串嘛,如上图url:
www.qimai.cn/rank/marketRank/market/3/category/-2/country/cn/collection/topselling_free/date/2018-11-13----->[3,-2,cn,topselling_free,2018-11-13],
然后做了个排序拼接在一起就是-22018-11-113cntopselling_free,这里经过一个l.d函数,让我们看下这个函数做了什么事,选中l.d进去h(a)函数:
在这里插入图片描述

        function h(a) {
            return x()(encodeURIComponent(a).replace(/%([0-9A-F]{2})/g, function(a, e) {
                return r("0x" + e)
            }))
        }

又出来个x(),继续步进,到了下图位置,下图命名为图1
在这里插入图片描述
t就是上述的o,也就是url参数;
console下e.from(t.toString(),“binary”)获得了一个数组, 赋值给了n
n.toSting(‘base64’)即是加密后的o

下面我们看看七脉是如何给o进行加密的
首先我们先看看是如何获得的数组,先进入e.from函数,又到s函数,参数依然是url的参数,这网站弄的真够绕的,下图命名为图2
在这里插入图片描述继续进入s函数,传参url参数,下图命名为图3:
在这里插入图片描述
分析下上图中f函数参数,重点来了!!!!!!!
t=null,
e=url参数
n=“binary”,
函数o(t,r)生成了一个长度和url参数等长度且全部为0的数组,并赋值给t
继续进入t.write(e,n)
下图命名为图4
在这里插入图片描述
继续write函数调用k函数,进入k函数传参t=url参数,e=“binary”, n =undefined!
下图命名为图5
在这里插入图片描述
k调C,C调K(W(e)),console下W(e)得到了数组,这个数组就是图1中出现的数组!!!!继续步进进入W看看url参数怎么转成数组的,下图命名为图6
在这里插入图片描述

            function W(t) {
                for (var e = [], n = 0; n < t.length; ++n)
                    e.push(255 & t.charCodeAt(n));
                return e
            }

遍历t,获取每个下标元素对应的 ASCII 数值,然后和255进行&运算。charCodeAt 和python中ord函数作用相同
ok!步出!步进看K函数如何将W得到的数组进行加密的
下图命名为图7
在这里插入图片描述
K函数并没有对此数组进行操作,return i; i的数值和e相同!!
至此我们得到了图1中的数组,将数组进行一个toString(‘base64’)操作即是加密结果

如果至此你已经蒙圈了,可以将断点打到图一中的n.toString(‘base64’),为了防止其他断点的干预,我习惯将其他断点先关闭,然后刷新,重新看数组如何变成base64的。
如果你还很清楚,那直接步出步出步出。。。,到图1所示代码块,如下状态
在这里插入图片描述
继续步进
在这里插入图片描述

进入到M
在这里插入图片描述console下Q.fromByteArray(t),即是加密后的o,进入到fromByteArray
在这里插入图片描述
以下函数是对数组加密,即生成base64字符串的过程,直接调用u即可

//对数组进行加密
        function u(t) {  //用到了s
            for (var e, n = t.length, r = n % 3, i = "", o = [], a = 16383, u = 0, c = n - r; u < c; u += a)
                o.push(s(t, u, u + a > c ? c : u + a));
            return 1 === r ? (e = t[n - 1],
            i += l[e >> 2],
            i += l[e << 4 & 63],
            i += "==") : 2 === r && (e = (t[n - 2] << 8) + t[n - 1],
            i += l[e >> 10],
            i += l[e >> 4 & 63],
            i += l[e << 2 & 63],
            i += "="),
            o.push(i),
            o.join("")
        }
        
	function s(t, e, n) { //用到了a
	    for (var r, i = [], o = e; o < n; o += 3) r = (t[o] << 16 & 16711680) + (t[o + 1] << 8 & 65280) + (255 & t[o + 2]),
	        i.push(a(r));
	    return i.join('')
	}
	
	function a(t) { //用到了l
    return l[t >> 18 & 63] + l[t >> 12 & 63] + l[t >> 6 & 63] + l[63 & t]
}

//l就是A-Za-z0-9+ 生成l
for (var l = [], c = [], f = 'undefined' != typeof Uint8Array ? Uint8Array : Array, d = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', h = 0, p = d.length; h < p; ++h) l[h] = d[h],
    c[d.charCodeAt(h)] = h;
c['-'.charCodeAt(0)] = 62,
    c['_'.charCodeAt(0)] = 63

综合W函数得到以下js
命名为qimai.js
经过u函数后,便拿到了加密后的o了,剩下的分析便大同小异了,没什么难度了。


    function W(t) {
    //t为排序后url参数值,返回数组
    t = t.toString()
    for (var e = [], n = 0; n < t.length; ++n) e.push(255 & t.charCodeAt(n));
    return e
}


//u函数将数组转化为base64
for (var l = [], c = [], f = 'undefined' != typeof Uint8Array ? Uint8Array : Array, d = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', h = 0, p = d.length; h < p; ++h) l[h] = d[h],
    c[d.charCodeAt(h)] = h;
c['-'.charCodeAt(0)] = 62,
    c['_'.charCodeAt(0)] = 63

function a(t) {
    return l[t >> 18 & 63] + l[t >> 12 & 63] + l[t >> 6 & 63] + l[63 & t]
}

function s(t, e, n) {
    for (var r, i = [], o = e; o < n; o += 3) r = (t[o] << 16 & 16711680) + (t[o + 1] << 8 & 65280) + (255 & t[o + 2]),
        i.push(a(r));
    return i.join('')
}

function u(t) {

    for (var e, n = t.length, r = n % 3, i = '', o = [], a = 16383, u = 0, c = n - r; u < c; u += a) o.push(s(t, u, u + a > c ? c : u + a));
    return 1 === r ? (e = t[n - 1], i += l[e >> 2], i += l[e << 4 & 63], i += '==') : 2 === r && (e = (t[n - 2] << 8) + t[n - 1], i += l[e >> 10], i += l[e >> 4 & 63], i += l[e << 2 & 63], i += '='),
        o.push(i),
        o.join('')
}

//直接传入排序后的url参数值,返回base64
function decrypt(o) {
    return u(W(o))
}


下边附上Python完整版本Demo

import execjs
import time

class Decrypt():
    def __init__(self):
        js_path = ’qimai.js'  #上述的qimai.js
        f = open(js_path)
        js = f.read()
        self.ctx = execjs.compile(js)
        f.close()

    def __f(self, a):
        '''
        解密第一步
        :param a:
        :return:
        '''
        e = 'a12c0fa6ab9119bc90e4ac7700796a53'
        a = list(a)
        for i in range(len(a)):
            a[i] = chr(ord(a[i]) ^ ord(e[i % len(e)]))
        return ''.join(a)

    @classmethod
    def decrypt(cls, tags, params={}):
        '''
        :param tags: url的分类 如应用宝页面 https://www.qimai.cn/rank/marketRank  则传入rank/marketRank
        :return:
        '''
        self = cls()

        now_date = round(time.time() * 1000)

        t = now_date - 708 - 1515125653845

        params_ = ''
        if params:
            o_ = ''.join(sorted(list(params.values())))
            params_ = self.ctx.call('decrypt', o_)
        o = '%s@#/%s@#%s@#1' % (params_, tags, t)
        s = self.__f(o)
        analysis = self.ctx.call('decrypt', s)
        return analysis
 if __name__ == '__main__':
    now_date = time.strftime('%Y-%m-%d', time.localtime())
    params_list = [
        ('rank/marketRank', {'market': '1', 'category': '6', 'date': now_date}, '360'),
        ('rank/marketRank', {'market': '3', 'category': '-2', 'date': now_date}, '应用宝'),
        ('rank/marketRank', {'market': '5', 'category': '5', 'date': now_date}, '豌豆荚'),
        ('rank/marketRank', {'market': '4', 'category': '6', 'date': now_date}, '小米'),
        ('rank/marketRank', {'market': '7', 'category': '7', 'date': now_date}, '魅族'),
        ('rank/marketRank', {'market': '9', 'category': '4', 'date': now_date}, 'oppo'),
        ('rank/release', {'genre': '36', 'date': now_date}, 'App Store')
    ]
    for tags, params, store in params_list:
        analysis = Decrypt.decrypt(tags, params)
        print(analysis)

明天附上github

猜你喜欢

转载自blog.csdn.net/qq_32735511/article/details/83964353