写在前面

Python反反爬系列

  1. JS混淆---源码乱码
  2. JS混淆---动态Cookie
  3. 访问逻辑---推心置腹
  4. CSS加密---样式干扰

题目


题目网站,点我去刷题

采集全部5页的彩票数据,计算全部中奖的总金额(包含一、二、三等奖)

分析网页

老规矩,我们还是首先打开刷题网站,接着打开谷歌调试工具
查看【XHR】里面的内容

可以发现通过Ajax的方式,返回了一串数据
对比网页的数字,不难发现,这些返回的数据,是页面的三等奖金额

但是,这道题让我们求的是一等奖,二等奖,三等奖的总金额
做了几道猿人学的题目,我们可以猜测,总金额可能会通过JS代码生成
这个问题,我们先留着,继续分析一下返回数据的这个URL


通过观察,我们可以发现,URL中带了两个参数,一个【 m 】的加密参数,还有一个【 q 】的参数,【 q 】的参数值与时间戳类似

所以说,要想正常访问,肯定要先算出【 m 】和【 q 】的值

m 和 q的值,从何而来

我们知道上面提到的请求是通过Ajax请求返回的,这里可以分享给大家一个小技巧
在谷歌浏览器的调试工具中,我们可以通过下图的方式,直接进入到AJax的代码部分


来到这个页面,Ajax上面定义的一个数组,引起了我的注意,我们将它扣下来

var list = {
    "page": window.page,
    "m": r(t, window.o),
    "q": window.i += window.o + '-' + t + "|",
};

看到这段代码,我相信大家已经大概明白了 m 和 q的值,从何而来

  • page:页码,因为我们请求的第一页,所以在URL中省略了,但是从第二页开始page就会出现
  • m:通过调用r函数,并传入,【t】和【window.o】参数,得到一串加密后的密文
  • q:通过 += 的方式,可以累加得到一串字符串

我们截取一段 【q】的值分析一下,window.i和window.o已经 t 是什么

q: 1-1610261917000|

通过对比我们可以发现

  1. window.i就是这串值
  2. window.o是1
  3. t就是时间戳

但是,这个 += 符号有什么用呢?
我们多次请求一下第二页和第三页,你就明白啦

q: 1-1610261917000|2-1610264287000|3-1610264288000|

这是我请求第一页,第二页,第三页后,【q】的值
这个时候,可能就有小伙伴会说,window.o就是页码的值

但是,当我在第三页回到第一页的时候,【q】的值是这样的

q: 1-1610261917000|2-1610264287000|3-1610264288000|4-1610264345000|

所以说,window.o的值其实很简单,并不是页码的值,而是点击页面发起Ajax请求的次数罢了


明白了【t】和【window.o】的含义,我们就明白了通过调用r函数传入的参数是什么了

现在我们知道了【q】值的生成过程,而【m】只知道一部分,所以我们的下一步就是寻找r函数

寻找r函数

我们在r函数出现的哪一行代码,打一个断点

接着按CTRL+R,刷新一下页面
我们会发现代码卡在了769这一行,我们点击右上角的箭头继续往下运行

这个时候,代码就卡在了r函数这一行,我们点击右上角如下图的这个按钮,就可以进入到包含r函数的JS文件中,这也是题目所提到的 【 回溯


就是这么简单,我们很顺利的找到了r函数,接着我们将代码扣下来分析一下

分析r函数

function r(param1, param2) {
    if (window.o >= 6) {
        alert('不要戳这么多下,人家好痛嘛~');
        location.reload();
    }
    return z(param1, param2);
}

代码中,出现了一个if判断, 而判断中的内容就是,如果Ajax请求(也就是点击页面中页码的次数,当然首次进入页面是默认发起了一次请求的)大于或者等于6次的话,页面就会出现如下图的弹窗,紧接着执行location.reload()函数,而这个函数,我们在前面的题目中也碰到过,就是刷新整个页面,window.o的值也会重置为1

接着将传入的参数,二次传递给z函数并执行
我们来看一下z函数中的代码

function z(pwd, time) {
    var n = _n("jsencrypt");
    var g = (new n);
    var r = g.encode(pwd, time);
    return r;
}

这段代码的注意内容是:

  • 实例化了jsencrypt对象
  • 使用jsencrypt中的encode方法,进行了加密,并且返回,也就是【m】的值

jsencrypt就是一个基于rsa加解密的js库,反正就是一段操作,返回一段密文,感兴趣的同学可以自行百度,我这里就不展开讲了
我们就可以通过运行这个JS文件,从而得出m的值

到这里,相信大家已经明白了m 和 q的值,都是怎样产生的了

但是,在包含r函数的JS文件中,是有混淆过的代码的
大致浏览一下文件中的代码你就会发现这样三处混淆代码


所以说, 要想通过JS文件得出m的值,就需要先将混淆的代码翻译过来

翻译混淆过的代码

通常像这种混淆过的代码,如果不是一个函数的话,我们是可以通过谷歌调试工具中的Console,运行出结果的
比如说这段混淆代码,其实就是JS中的一些运算

[][(![] + [])[!+[] + !![] + !![]] + ([] + {})[+!![]] + (!![] + [])[+!![]] + (!![] + [])[+[]]][([] + {})[!+[] + !![] + !![] + !![] + !![]] + ([] + {})[+!![]] + ([][[]] + [])[+!![]] + (![] + [])[!+[] + !![] + !![]] + (!![] + [])[+[]] + (!![] + [])[+!![]] + ([][[]] + [])[+[]] + ([] + {})[!+[] + !![] + !![] + !![] + !![]] + (!![] + [])[+[]] + ([] + {})[+!![]] + (!![] + [])[+!![]]]((+!![] + []) + (!+[] + !![] + !![] + !![] + !![] + []) + (!+[] + !![] + !![] + !![] + !![] + !![] + !![] + []) + (+!![] + []) + (!+[] + !![] + !![] + !![] + !![] + []) + (+[] + []) + (!+[] + !![] + !![] + !![] + !![] + !![] + !![] + []) + (+[] + []))(!+[] + !![] + !![] + !![] + !![] + !![] + !![]) == ([][(![] + [])[!+[] + !![] + !![]] + ([] + {})[+!![]] + (!![] + [])[+!![]] + (!![] + [])[+[]]][([] + {})[!+[] + !![] + !![] + !![] + !![]] + ([] + {})[+!![]] + ([][[]] + [])[+!![]] + (![] + [])[!+[] + !![] + !![]] + (!![] + [])[+[]] + (!![] + [])[+!![]] + ([][[]] + [])[+[]] + ([] + {})[!+[] + !![] + !![] + !![] + !![]] + (!![] + [])[+[]] + ([] + {})[+!![]] + (!![] + [])[+!![]]]((+!![] + []) + (!+[] + !![] + !![] + !![] + !![] + !![] + []) + (!+[] + !![] + !![] + !![] + !![] + !![] + !![] + []) + (!+[] + !![] + !![] + !![] + !![] + !![] + !![] + []) + (!+[] + !![] + !![] + !![] + !![] + !![] + !![] + []) + (!+[] + !![] + []) + (+!![] + []) + (!+[] + !![] + !![] + !![] + !![] + []))(!+[] + !![] + !![] + !![] + !![] + !![]) & [][(![] + [])[!+[] + !![] + !![]] + ([] + {})[+!![]] + (!![] + [])[+!![]] + (!![] + [])[+[]]][([] + {})[!+[] + !![] + !![] + !![] + !![]] + ([] + {})[+!![]] + ([][[]] + [])[+!![]] + (![] + [])[!+[] + !![] + !![]] + (!![] + [])[+[]] + (!![] + [])[+!![]] + ([][[]] + [])[+[]] + ([] + {})[!+[] + !![] + !![] + !![] + !![]] + (!![] + [])[+[]] + ([] + {})[+!![]] + (!![] + [])[+!![]]]((+[] + []) + [][(![] + [])[!+[] + !![] + !![]] + ([] + {})[+!![]] + (!![] + [])[+!![]] + (!![] + [])[+[]]][([] + {})[!+[] + !![] + !![] + !![] + !![]] + ([] + {})[+!![]] + ([][[]] + [])[+!![]] + (![] + [])[!+[] + !![] + !![]] + (!![] + [])[+[]] + (!![] + [])[+!![]] + ([][[]] + [])[+[]] + ([] + {})[!+[] + !![] + !![] + !![] + !![]] + (!![] + [])[+[]] + ([] + {})[+!![]] + (!![] + [])[+!![]]]((!![] + [])[+!![]] + ([][[]] + [])[!+[] + !![] + !![]] + (!![] + [])[+[]] + ([][[]] + [])[+[]] + (!![] + [])[+!![]] + ([][[]] + [])[+!![]] + ([] + {})[!+[] + !![] + !![] + !![] + !![] + !![] + !![]] + ([][[]] + [])[+[]] + ([][[]] + [])[+!![]] + ([][[]] + [])[!+[] + !![] + !![]] + (![] + [])[!+[] + !![] + !![]] + ([] + {})[!+[] + !![] + !![] + !![] + !![]] + (+{} + [])[+!![]] + ([] + [][(![] + [])[!+[] + !![] + !![]] + ([] + {})[+!![]] + (!![] + [])[+!![]] + (!![] + [])[+[]]][([] + {})[!+[] + !![] + !![] + !![] + !![]] + ([] + {})[+!![]] + ([][[]] + [])[+!![]] + (![] + [])[!+[] + !![] + !![]] + (!![] + [])[+[]] + (!![] + [])[+!![]] + ([][[]] + [])[+[]] + ([] + {})[!+[] + !![] + !![] + !![] + !![]] + (!![] + [])[+[]] + ([] + {})[+!![]] + (!![] + [])[+!![]]]((!![] + [])[+!![]] + ([][[]] + [])[!+[] + !![] + !![]] + (!![] + [])[+[]] + ([][[]] + [])[+[]] + (!![] + [])[+!![]] + ([][[]] + [])[+!![]] + ([] + {})[!+[] + !![] + !![] + !![] + !![] + !![] + !![]] + (![] + [])[!+[] + !![]] + ([] + {})[+!![]] + ([] + {})[!+[] + !![] + !![] + !![] + !![]] + (+{} + [])[+!![]] + (!![] + [])[+[]] + ([][[]] + [])[!+[] + !![] + !![] + !![] + !![]] + ([] + {})[+!![]] + ([][[]] + [])[+!![]])(+!![]))[!+[] + !![] + !![]] + ([][[]] + [])[!+[] + !![] + !![]])(!+[] + !![] + !![] + !![] + !![])([][(![] + [])[!+[] + !![] + !![]] + ([] + {})[+!![]] + (!![] + [])[+!![]] + (!![] + [])[+[]]][([] + {})[!+[] + !![] + !![] + !![] + !![]] + ([] + {})[+!![]] + ([][[]] + [])[+!![]] + (![] + [])[!+[] + !![] + !![]] + (!![] + [])[+[]] + (!![] + [])[+!![]] + ([][[]] + [])[+[]] + ([] + {})[!+[] + !![] + !![] + !![] + !![]] + (!![] + [])[+[]] + ([] + {})[+!![]] + (!![] + [])[+!![]]]((!![] + [])[+!![]] + ([][[]] + [])[!+[] + !![] + !![]] + (!![] + [])[+[]] + ([][[]] + [])[+[]] + (!![] + [])[+!![]] + ([][[]] + [])[+!![]] + ([] + {})[!+[] + !![] + !![] + !![] + !![] + !![] + !![]] + ([][[]] + [])[!+[] + !![] + !![]] + (![] + [])[!+[] + !![] + !![]] + ([] + {})[!+[] + !![] + !![] + !![] + !![]] + (+{} + [])[+!![]] + ([] + [][(![] + [])[!+[] + !![] + !![]] + ([] + {})[+!![]] + (!![] + [])[+!![]] + (!![] + [])[+[]]][([] + {})[!+[] + !![] + !![] + !![] + !![]] + ([] + {})[+!![]] + ([][[]] + [])[+!![]] + (![] + [])[!+[] + !![] + !![]] + (!![] + [])[+[]] + (!![] + [])[+!![]] + ([][[]] + [])[+[]] + ([] + {})[!+[] + !![] + !![] + !![] + !![]] + (!![] + [])[+[]] + ([] + {})[+!![]] + (!![] + [])[+!![]]]((!![] + [])[+!![]] + ([][[]] + [])[!+[] + !![] + !![]] + (!![] + [])[+[]] + ([][[]] + [])[+[]] + (!![] + [])[+!![]] + ([][[]] + [])[+!![]] + ([] + {})[!+[] + !![] + !![] + !![] + !![] + !![] + !![]] + (![] + [])[!+[] + !![]] + ([] + {})[+!![]] + ([] + {})[!+[] + !![] + !![] + !![] + !![]] + (+{} + [])[+!![]] + (!![] + [])[+[]] + ([][[]] + [])[!+[] + !![] + !![] + !![] + !![]] + ([] + {})[+!![]] + ([][[]] + [])[+!![]])(+!![]))[!+[] + !![] + !![]] + ([][[]] + [])[!+[] + !![] + !![]])(!+[] + !![] + !![] + !![] + !![] + !![] + !![] + !![])(([] + {})[+[]])[+[]] + (!+[] + !![] + !![] + !![] + !![] + !![] + !![] + []) + (!+[] + !![] + !![] + !![] + !![] + !![] + !![] + !![] + [])) + ([][[]] + [])[!+[] + !![]] + ([][[]] + [])[!+[] + !![] + !![]] + (+{} + [])[+!![]] + ([][[]] + [])[!+[] + !![]] + ([] + {})[!+[] + !![]] + ([][[]] + [])[!+[] + !![] + !![]] + ([][[]] + [])[!+[] + !![] + !![]] + ([][[]] + [])[!+[] + !![] + !![] + !![]] + ([] + {})[!+[] + !![] + !![] + !![] + !![]] + (+{} + [])[+!![]] + ([][[]] + [])[!+[] + !![] + !![] + !![]] + ([][[]] + [])[!+[] + !![] + !![]])(!+[] + !![] + !![]));

将这些代码输入到Console中,就可以得出结果

在这道题目的三处混淆中,都可以通过这种方式翻译出来

而第一张图片的混淆,其实很有意思,看着也挺有意思的
它这种属于JavaScript的表情包加密,我们可以通过这个网站进行翻译解密工具

将需要解密的代码复制过去,然后点击aaencode解密,就可以得到结果【window.o = 1;】

我这里就不放结果的截图,大家可以去试一试

还有一处混淆过的代码,你可以通过Console得出结果,我这里就不再赘述

全部翻译完成后,我们将结果与混淆的代码进行替换,接着道鬼鬼调试工具进行测试验证

验证加密结果

我们将处理过的代码粘贴到鬼鬼调试工具中
然后,在前面的地方,加一行代码 var window = this;
因为在代码中,很多地方用到了 window 变量,不声明的话,会报错

在上面的截图中,可以发现我注释了一行代码 //window = {};
这行代码就是将window变为空,上面我也提到了,很多地方都需window,所以要将其注释

接着,我们加载全部代码,传入参数执行r函数,可以发现得到了我们想要的结果

到目前为止,我们已经可以通过Python代码得到【m】和【q】的值了,但是还有一个问题我们还没有解决

求出中奖总金额

在文章开头,我们还留着一个问题

AJax返回的数据,是页面的三等奖金额
但是,这道题让我们求的是一等奖,二等奖,三等奖的总金额
所以,我们回到Ajax代码中(截取了部分代码)
如果你对Ajax的内容不是很熟悉的话,可以看看我这篇文章Python反反爬之CSS加密---样式干扰
在这篇文章中,我对ajax部分的代码解释得很详细,这里我就不多赘述

$.ajax({
    url: window.url,
    dataType: "json",
    async: false,
    data: list,
    type: "GET",
    beforeSend: function (request) {},
    success: function (data) {
        if (window.page) {}
        else {
            window.page = 1
        }
        // 请求成功后返回的数据,也就是页面中三等奖的金额
        data = data.data;
        let html = '';
        let arg = 1;
        let puq = `<tr data-week="3"><td>date_twice</td><td>2020-10-date_value</td>caipiaohao<td>total_value</td><td>result_value1</td><td>result_value2</td><td>result_value3</td><td>0</td><td></td></tr>`;
        let arr_arg = [1, 8, 7, 3, 5, 7, 10, 2, 9, 4];
        // 通过循环三等奖的金额,进行一系列的操作,呈现出页面的各种数值
        // 我们可以不用详细的去分析,直接看下面的replace部分
        $.each(data, function (index, val) {
            let caipiao = `<td><span class="rq1">arg1</span><span class="rq1">arg2</span><span class="rq1">arg3</span><span class="rq1">arg4</span><span class="rq1">arg5</span><span class="rq1">arg6</span><span class="rq1">arg7</span></td><td><span class="bq1">arg8</span></td>`;
            let arr = [5, 1, 7, 6, 3, 2, 4, 7];
            for (let c = 1; c <= 8; c++) {
                caipiao = caipiao.replace('arg' + c, (Math.floor(((arr_arg[arg] << 2) / 5 + c) * c / 3) + 1) + arr[window.page])
            }
            // 这里的代码,主要是将页面的数值进行替换
            html += puq.replace('caipiaohao', caipiao)
            .replace('date_twice', arg * window.page + 2020097)
            .replace('date_value', '0' + window.page)
            .replace('result_value3', val.value)
                        // total_value 指的就是页面中的 总销售额字段
                        // val.value 指的就是 三等奖金额
                        // 也就是说,总销售额,就是三等奖的金额乘以24
            .replace('total_value', val.value * 24)
                        // result_value2 二等奖
            .replace('result_value2', val.value * 8)
                        // result_value1 一等奖
            .replace('result_value1', val.value * 15);
            arg += 1
        });
        $('.resbbp').text('').append(html)
    }

通过上述代码的注释,我们可以知道,每一行的总销售额,也就是我们所求的总金额,其实就是三等奖的金额乘以24

所以,我们就可以获取每页的三等奖金额总和,最后乘以24就可以得出这道题目的答案

解出答案

在写Python代码之前,我们还需要完成最后一个步骤

我们知道想要正常请求URL拿到数据,需要三个参数(page,m,q)
而前面我们翻译完成的JS文件,返回的结果只有m的值,而没有返回时间戳
所以,我们只需要将z函数改下一下即可

// 修改前
function z(pwd, time) {
    var n = _n("jsencrypt");
    var g = (new n);
    var r = g.encode(pwd, time);
    return r;
}

// 修改后
function z(pwd, time) {
    var n = _n("jsencrypt");
    var g = (new n);
    var r = g.encode(pwd, time);
    var answer_arr = [r,pwd]
    return answer_arr;
}

现在,我们就可以愉快的写Python代码,爬取数据,解出答案啦
由于JS文件代码太多(2000多行),我就不粘贴出来,点击我可以进行下载

# @BY     :Java_S
# @Time   :2021/1/10 13:36
# @Slogan :够坚定够努力大门自然会有人敲,别怕没人赏识就像三十岁的梵高

import time
import execjs
import requests


def get_cipher(timestamp, page):
    # 导入JS,读取需要的js文件
    with open(r'JS/js6.js', encoding='utf-8', mode='r') as f:
        JsData = f.read()
    # 加载js文件,使用call()函数执行,传入需要执行函数即可获取返回值
    [cipher, limit] = execjs.compile(JsData).call('get_cipher', timestamp, page)
    limit = f'{page}-' + str(timestamp) + '|'

    return cipher, limit


def get_data(page):
    cipher, limit = get_cipher(int(time.time()) * 1000, page)
    url = 'http://match.yuanrenxue.com/api/match/6'
    params = {
        'page': page,
        'm': cipher,
        'q': limit
    }
    headers = {
        'Host': 'match.yuanrenxue.com',
        'Referer': 'http://match.yuanrenxue.com/match/6',
        'User-Agent': 'yuanrenxue.project',
    }
    response = requests.get(url=url, headers=headers, params=params)
    answer = [i['value'] for i in response.json()['data']]
    print(f'第{page}页的三等奖:{answer}')

    return answer

if __name__ == '__main__':
    total = []
    for i in range(1,6):
        total += get_data(i)
    total = sum(total)*24
    print(f'五页中奖的总金额:{total}元')

世界因代码而改变 Peace Out
最后修改:2021 年 01 月 18 日 08 : 02 PM
如果觉得我的文章对你有用,请随意赞赏