[ol]
[/ol]
本文是第四篇, 也是最后一篇,网上大部分针对极验的绕过方法大都是模拟手工滑动滑块的方式,但是通过上面几篇文章的分析,我们是能知道Geetest已经对目前市面上大多自动化测试的工具进行了监测,包括 Selenium甚至electron等。所以基于这些工具的破解不是不行,只是人家官方没有严查,不长久的,稳妥之计还是要直接从封包入手。下面进入正文~
背景图片乱序的还原
如《极验反爬虫防护分析之交互流程分析》第五步的分析,得到的 bg和fullbg图片都是乱序处理后的图片,要判断滑动的距离及轨迹需要将图片进行还原。如下图:
1.jpg (392.1 KB, 下载次数: 2)
下载附件
2020-4-22 17:29 上传
还原后的代码为:
function SEQUENCE() {
var e = "6_11_7_10_4_12_3_1_0_5_2_9_8".split("_");
for (var t, n = [], r = 0; r
至此,我们知道它是通过两次折叠构建出来52个元素的散列表。通过固定的公式将图片上下、左右互换并根据散列表的值进行乱序。通过分析代码中的字符串常亮6_11_7_10_4_12_3_1_0_5_2_9_8是在slide.7.6.0.js文件中,一开始的方法中定义的:$_DAEAF = decodeURI('N-%60%13)nN-%60%1C%1...,decodeURI解码后的数组第911位就是此字符串常量, 如下图:
2.jpg (310.06 KB, 下载次数: 1)
下载附件
2020-4-22 17:30 上传
继续跟进绘图的代码:
3.jpg (178.33 KB, 下载次数: 2)
下载附件
2020-4-22 17:32 上传
将混淆的代码还原之后,如下:
function $_GEN(t, e) {
var $_CJDIX = $_AB.$_Ei()[4][26];
for (; $_CJDIX !== $_AB.$_Ei()[8][24];) {
switch ($_CJDIX) {
case $_AB.$_Ei()[16][26]:
t = t[$_DEAo(65)], e = e[$_DDJm(65)];
var n = t["width"], r = t["height"], i = document[$_DDJm(27)]($_DEAo(91));
i["width"] = n, i["height"] = r;
var CanvasRenderingContext2D = i["getContext"]("2d");
$_CJDIX = $_AB.$_Ei()[8][25];
break;
case $_AB.$_Ei()[8][25]:
CanvasRenderingContext2D["drawImage"](t, 0, 0);
var CanvasRenderingContext2D = e["getContext"]("2d");
e["height"] = r, e["width"] = WIDTH;
for (var a = r / 2, u = 0; u
将以上JS编写为还原图片的Python代码如下:
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
def sequence():
t = 0
n = []
e = "6_11_7_10_4_12_3_1_0_5_2_9_8".split("_")
for r in range(0, 52):
t = 2 * int(e[int(r%26/2)]) + r % 2
if 0 == int(r/2)%2:
t += -1 if (r%2) else 1
t += 26 if (r
找一个待处理的图片:https://static.geetest.com/pictures/gt/6edec3cc1/6edec3cc1.jpg,测试结果如下图:
4.jpg (221.1 KB, 下载次数: 2)
下载附件
2020-4-22 17:35 上传
至此,图片乱序还原的问题搞定。
滑动轨迹的加密方法
同样的方法跟踪滑块失败后的请求,分析回溯来到如下代码:
"$_CHBV": function (t, e, n) {
var $_CABJD = $_AB.$_Ds, $_CABIQ = ['$_CACCE'].concat($_CABJD), $_CACAM = $_CABIQ[1];
$_CABIQ.shift();
var $_CACBm = $_CABIQ[0];
var r = this, i = r[$_CABJD(78)];
var o = {
"lang": i[$_CABJD(172)] || $_CACAM(161), // 语言固定为 zh-hk || zh-cn
"userresponse": $_CEI(t, i[$_CABJD(139)]), // t=滑动的距离,用户响应的内容, $_CABJD(139) = "challenge" 的值
"passtime": n, // 滑块消耗的时间=鼠标轨迹每个点耗时相加
"imgload": r[$_CABJD(744)],
"aa": e, // 滑动轨迹的加密字符
"ep": r[$_CABJD(764)]()
};
i[$_CABJD(118)] && (o[$_CABJD(221)] = t);
o["rp"] = $_DCj(i[$_CACAM(159)] + i[$_CABJD(139)][$_CACAM(151)](0, 32) + o[$_CABJD(736)]);
var s = r[$_CACAM(791)](); // rsa加密的aes密钥
var a = AES[$_CABJD(389)](gjson[$_CACAM(160)](o), r[$_CABJD(751)]()); // 将上面的json用aes加密
var u = Base64[$_CACAM(739)](a), c = {
"gt": i[$_CABJD(159)],
"challenge": i[$_CACAM(139)],
"lang": o[$_CABJD(172)],
"pt": r[$_CACAM(686)],
"w": u + s
};
...
}
至此,我们找到了移动滑块后提交的参数w的来历。根据以往经验s是aes加密用到密钥,用rsa加密后的密文。重点分析u的来历。向上回溯可知u的组成,将代码还原为:
/**
* 用于计算 rp 的hash值
*/
function $_DCj(t) {
function u(t, e) {
return t >> 32 - e;
}
function c(t, e) {
var n, r, i, o, s;
return i = 2147483648 & t, o = 2147483648 & e, s = (1073741823 & t) + (1073741823 & e), (n = 1073741824 & t) & (r = 1073741824 & e) ? 2147483648 ^ s ^ i ^ o : n | r ? 1073741824 & s ? 3221225472 ^ s ^ i ^ o : 1073741824 ^ s ^ i ^ o : s ^ i ^ o;
}
function e(t, e, n, r, i, o, s) {
return c(u(t = c(t, c(c(function a(t, e, n) {
return t & e | ~t & n;
}(e, n, r), i), s)), o), e);
}
function n(t, e, n, r, i, o, s) {
return c(u(t = c(t, c(c(function a(t, e, n) {
return t & n | e & ~n;
}(e, n, r), i), s)), o), e);
}
function r(t, e, n, r, i, o, s) {
return c(u(t = c(t, c(c(function a(t, e, n) {
return t ^ e ^ n;
}(e, n, r), i), s)), o), e);
}
function i(t, e, n, r, i, o, s) {
return c(u(t = c(t, c(c(function a(t, e, n) {
return e ^ (t | ~n);
}(e, n, r), i), s)), o), e);
}
function o(t) {
var n = "", r = "";
for (var e = 0; e >> 8 * e & 255)["toString"](16))["substr"](r["length"] - 2, 2);
}
return n;
}
var s, a, _, l, f, h, d, p, g, m;
for (s = function v(t) {
var e, n = t["length"], r = n + 8, i = 16 * (1 + (r - r % 64) / 64), o = Array(i - 1), s = 0, a = 0;
while (a >> 29, o;
}(t = function w(t) {
t = t["replace"](/\r\n/g, "\n");
for (var e = "", n = 0; n > 6 | 192) : (e += String["fromCharCode"](r >> 12 | 224), e += String["fromCharCode"](r >> 6 & 63 | 128)), e += String["fromCharCode"](63 & r | 128));
}
return e;
}(t)), d = 1732584193, p = 4023233417, g = 2562383102, m = 271733878, a = 0; a
其中 SlideObject["$_CEAQ"]方法依赖于浏览器的windows环境,如下图:
5.jpg (105.76 KB, 下载次数: 2)
下载附件
2020-4-22 17:47 上传
继续跟进,发现"ep"值,是windows的Performance.timing中的值。
6.jpg (330.97 KB, 下载次数: 1)
下载附件
2020-4-22 17:47 上传
因此,可以根据 Performance.timing 时间产生的先后顺序以及时间间隔,用当前的时间戳减去相应的值来模拟。由于相关代码比较简单,为了节省篇幅就不给出了。
滑动轨迹生成的思路
由于极验采用人工智能的方式对滑动的轨迹进行的验证,因此如果我们比较随意的生成鼠标滑动轨迹基本是肯定被封的,因此我们要详细分析一下鼠标轨迹的规律,
通之前介绍的调试手段,手工滑动滑块,获取到鼠标滑动轨迹的集合数组如下:
[[-37,-41,0], [0,0,0], [3,0,251], [6,0,266], [9,0,283], [13,0,300], [15,0,316], [17,0,333], [19,0,349], [20,0,366], [20,0,383], [20,0,400], [20,0,430]]
每个点的组成为:[x, y, timestamp],含义如下:
经反复测试得知还有如下规律:
因为y的取值比较简单,只考虑x坐标与z坐标的关系,将手工调试取10个坐标,以时间为X坐标,滑动距离为Y坐标,打印出来绘图为:
7.jpg (35.66 KB, 下载次数: 2)
下载附件
2020-4-22 17:49 上传
图像的轨迹有点儿像 tanh 和 arctan的混合体,如下图:
8.jpg (20.1 KB, 下载次数: 3)
下载附件
2020-4-22 17:51 上传
我们将两个图像整合、移动并添加一些噪点,最终生成的图像为:
9.jpg (18.75 KB, 下载次数: 2)
下载附件
2020-4-22 18:03 上传
这样的图像很像我们之前采集的鼠标轨迹图像了。至此,鼠标滑动轨迹的X坐标生成方法就告一段落。剩下的是滑动时间的分配。
对上面十次滑动的坐标集合,计算出每个坐标点消耗的时间,对时间进行汇总,如下表:
[table]
[tr]
[td]序号[/td]
[td]0-15(ms)[/td]
[td]15-20(ms)[/td]
[td]20-200(ms)[/td]
[td]200-400(ms)[/td]
[/tr]
[tr]
[td]1