写在前面
Python反反爬系列
1. JS混淆---源码乱码
- JS混淆---动态Cookie
- 访问逻辑---推心置腹
- CSS加密---样式干扰
解题代码Github
个人博客
题目
题目网站,点我去刷题
采集全部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|
通过对比我们可以发现
- window.i就是这串值
- window.o是1
- 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函数传入的参数是什么了
[scode type="blue"]现在我们知道了【q】值的生成过程,而【m】只知道一部分,所以我们的下一步就是寻找r函数[/scode]
寻找r函数
我们在r函数出现的哪一行代码,打一个断点
接着按CTRL+R,刷新一下页面
我们会发现代码卡在了769这一行,我们点击右上角的箭头继续往下运行
这个时候,代码就卡在了r函数这一行,我们点击右上角如下图的这个按钮,就可以进入到包含r函数的JS文件中,这也是题目所提到的 【 回溯 】
[scode type="blue"]就是这么简单,我们很顺利的找到了r函数,接着我们将代码扣下来分析一下[/scode]
分析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函数,可以发现得到了我们想要的结果
[scode type="blue"]到目前为止,我们已经可以通过Python代码得到【m】和【q】的值了,但是还有一个问题我们还没有解决[/scode]
求出中奖总金额
在文章开头,我们还留着一个问题
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)
}
[scode type="blue"]通过上述代码的注释,我们可以知道,每一行的总销售额,也就是我们所求的总金额,其实就是三等奖的金额乘以24[/scode]
[scode type="green"]所以,我们就可以获取每页的三等奖金额总和,最后乘以24就可以得出这道题目的答案 ::aru:pouting:: [/scode]
解出答案
在写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}元')