书接上文

5.第五个请求,返回生成验证码的三张图片

接口地址:https://api.geetest.com/get.php

image-20230527115446875

image-20230527115504124

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 第五次请求,获取各项图片
params = {
'is_next': 'true',
'type': 'slide3',
'gt': gt,
'challenge': challenge,
'lang': 'zh-cn',
'https': 'true',
'protocol': 'https://',
'offline': 'false',
'product': 'popup',
'api_server': 'api.geetest.com',
'isPC': 'true',
'autoReset': 'true',
'width': '100%',
'callback': 'geetest_{}'.format(get_time()),
}

response = requests.get('https://api.geetest.com/get.php', params=params, headers=headers)
res_img = jsonp_data(response.text)
bg_url = "https://static.geetest.com/" + res_img['bg']
fullbg_url = "https://static.geetest.com/" + res_img['fullbg']
slice_url = "https://static.geetest.com/" + res_img['slice']
new_challenge = res_img['challenge']
download_img('./imgs/bg.jpg',bg_url)
download_img('./imgs/fullbg.jpg',fullbg_url)
download_img('./imgs/slice.jpg',slice_url)
print(bg_url)
print(fullbg_url)
print(slice_url)

图片处理

乱序处理

图片下载下来后,发现是乱序的,在页面里面发现canvas, 怀疑可能是对图片进行了重绘image-20230531114025800

image-20230531114124412

我们直接在 事件侦听器断点中,监控一下画布,

image-20230531114910905

刷新一下验证码,断点进来,我们看一下代码,可以看到 i和e都是canvas,却别在于一个显示,一个不显示,o和s是两个画笔,i中写入乱序的背景图,而s画笔会在此基础上对乱序的图片进行重绘

image-20230531114938335

image-20230531115309764

我们直接看他是怎么重绘的

c的值是x轴的坐标,图片分成52份,上面26份,下面26份,

u 的值 是判断数组中的值是否大于25,否则返回80或者0 ,这里代表的是画布的高度是160,然后分成上下两部分,上面26份,下面26份,80和0都是y轴的坐标

l的值,是从画布o,就是乱序的图里面 c ,u xy轴坐标,10指图片的宽度,a,指图片的高度

s[$_CJEq(95)](l, _ % 26 * 10, 25 < _ ? a : 0); 这里代表的意思是 s[“putImageData”] 就是从乱序的图片里面获取l,然后再通过顺序写入到新的图片里面去, Ut就是图片还原的正确顺序

我们直接用python 还原就好了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def true_back(path):
old_img = Image.open(path)
new_img = Image.new('RGB', (260, 160)) # 创建一张新的图片
Ut = [
5,
19,
18,
16,
17
]
r = 160 # 图片的高度
a = r // 2 # 上下切成两份
for _ in range(52): # 处理成52张图片
c = Ut[_] % 26 * 12 + 1
u = a if 25 < Ut[_] else 0
# 获取一个区域,这个地方不一样,js是前两个参数是左上角的坐标,后面两个参数是宽和高
# python 里面 前两个参数是左上角的坐标,后面两个参数是右下角的坐标
l = old_img.crop((c, u, c + 10, u + a))
x1 = _ % 26 * 10
y1 = a if 25 < _ else 0
new_img.paste(l, (x1, y1))
new_img.save("./imgs/new_full_img.jpg")

滑动距离

使用opencv进行处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def get_slice():
# 读取图片
bg = cv2.imread("./imgs/new_bg.jpg") # 获取带缺口的背景图片
slice = cv2.imread("./imgs/slice.jpg") # 滑块图片

# 灰度处理,将rgb三原色全部改成灰色,这样便于图片亮度处理
bg = cv2.cvtColor(bg, cv2.COLOR_BGR2GRAY) # 蓝绿红改灰色
slice = cv2.cvtColor(slice, cv2.COLOR_BGR2GRAY)

# 边缘检测,图片边缘处理
bg_cam = cv2.Canny(bg, 255, 255)
slice = cv2.Canny(slice, 255, 255)

# 将两张图片进行匹配,会对每一个像素点进行匹配
r = cv2.matchTemplate(bg_cam, slice, cv2.TM_CCOEFF_NORMED)
# print(r)

# 获取最佳匹配多,返回四个结果
# 第一个是最不匹配的值,第三个是最不匹配的坐标
# 第二个是最匹配的值,第四个是最匹配的坐标
minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(r)
# print(minVal, maxVal, minLoc, maxLoc)
distance = maxLoc[0] # maxLoc是最匹配的坐标,第一个值就代表着横向的距离
return distance

滑动轨迹

使用随机算法生成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
def __ease_out_expo(sep):
if sep == 1:
return 1
else:
return 1 - pow(2, -10 * sep)


def get_slide_track(distance):
"""
根据滑动的距离生成轨迹
return 滑动轨迹,x 已滑动的横向距离, y已滑动的纵向距离,除起点完 都为0 t:滑动过程消耗的时间
"""
if not isinstance(distance, int) or distance < 0:
raise ValueError(f'distance类型必须是大于等于0的整数')
# 初始化轨迹列表,正常开始是000,但是网页可能会记录鼠标干了什么导致第一个是随机的,第二个是000
slide_track = [[random.randint(-50, -10), random.randint(-50, -10), 0], [0, 0, 0], ]
# 共记录count此滑块位置信息
count = 30 + int(distance / 2)
# 初始滑块时间
t = random.randint(50, 100)
# 记录上一次滑动的距离
_x = 0
_y = 0
for i in range(count):
# 已滑动的横向距离,用离散法获取随机的横向距离
x = round(__ease_out_expo(i / count) * distance)
# 滑动过程消耗的时间
t += random.randint(10, 20)
# 如果与上次一相同 就不记录
if x == _x:
continue
slide_track.append([x, _y, t])
_x = x
slide_track.append([distance, 0, t])
return slide_track, t

6.第六个请求,验证验证码是否成功

滑动滑块,生成最后一个请求,该请求同样需要携带w,唯一的区别在于这个w相比较之前的w更短一点,而且challenge参数会和之前不一样,多了个cb,这个参数是来自于第五个请求返回的新的challenge,需要注意

image-20230531151118913

第三个W解密流程:

从发起程序中进入堆栈,同样按照最开始的流程 搜索”\u0077”,只有一个,直接断点进来,其中h + u就是我们要的w

image-20230531151325564

image-20230531151556237

我们把这里一大段代码都扣下来,看一下具体代码流程, 传入的参数通过调试可知分别为滑动的距离、被加密后的轨迹、滑动的时间,o当中的参数的最后一个, 他其实跟之前写的那个鼠标移动的轨迹时间是一样的,我们也同样可以用之前算差值的方法算tm的参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
// t:滑动的距离 e:被加密后的轨迹 n: 滑动的时间
function get_3w(t, e, n) {
var r = this
, i = r['$_CJV']
, o = {
"\u006c\u0061\u006e\u0067": 'zh-cn',
"\u0075\u0073\u0065\u0072\u0072\u0065\u0073\u0070\u006f\u006e\u0073\u0065": H(t, i['challenge']),
"\u0070\u0061\u0073\u0073\u0074\u0069\u006d\u0065": n,
"\u0069\u006d\u0067\u006c\u006f\u0061\u0064": 21,
"\u0061\u0061": e,
"\u0065\u0070": $_CCCP()
};
try {
if (window['_gct']) {
var s = {
"\u006c\u0061\u006e\u0067": o['lang'],
"\u0065\u0070": o['ep']
}
, a = window['_gct'](s);
if (a['lang']) {
var _ = function d(t) {
for (var e in t)
if ('ep' !== e && 'lang' !== e)
return e;
}(s)
, c = function p(t, e, n) {
for (var r = new t[('gg')][('f')](e, n), i = ['n', 's', 'e', 'es', 'en', 'w', 'wn', 'ws'], o = i['length'] - 2, s = 0; s < n['length']; s++) {
var a, _ = Math['abs'](n[s]['charCodeAt']() - 70)['toString']()[1];
a = o < _ ? t['gg'][i[1 + o]](r) : t['gg'][i[_]](r);
for (var c = Math['abs'](n[s]['charCodeAt']() - 70)['toString']()[0], u = 0; u < c; u++)
a['cc']();
}
return r['random']['join']('')['slice'](0, 10);
}(a, s, _);
s[_] = c;
}
!function g(t) {
if (aa(3) == typeof Object['assign'])
return Object['assign']['apply'](Object, arguments);
if (null == t)
throw new Error('Cannot convert undefined or null to object');
t = Object(t);
for (var e = 1; e < arguments['length']; e++) {
var n = arguments[e];
if (null !== n)
for (var r in n)
Object['prototype']['hasOwnProperty']['call'](n, r) && (t[r] = n[r]);
}
return t;
}(o, s);
}
} catch (v) {
}
i['offline'] && (o['x'] = t),
o['rp'] = X(i['gt'] + i['challenge']['slice'](0, 32) + o['passtime']);
var u = r['$_CCDf']()
, l = V['encrypt'](gt['stringify'](o), r['$_CCEV']())
, h = m['$_FEE'](l)
, f = {
"\u0067\u0074": i['gt'],
"\u0063\u0068\u0061\u006c\u006c\u0065\u006e\u0067\u0065": i['challenge'],
"\u006c\u0061\u006e\u0067": o['lang'],
"\u0024\u005f\u0042\u0043\u0058": r['$_BHIK'],
"\u0063\u006c\u0069\u0065\u006e\u0074\u005f\u0074\u0079\u0070\u0065": r['$_BHJU'],
"\u0077": h + u
};
return f;
}

image-20230531163529174

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
function $_CCCP() {
var now = new Date().getTime()
return {
"v": "7.9.0",
"$_BIo": false,
"me": true,
"tm": {
"a": now - 2367,
"b": now - 2367 + 168,
"c": now - 2367 + 168,
"d": 0,
"e": 0,
"f": now - 2367 + 168 + 167,
"g": now - 2367 + 168 + 167 + 4,
"h": now - 2367 + 168 + 167 + 4 + 6,
"i": now - 2367 + 168 + 167 + 4 + 6,
"j": now - 2367 + 168 + 167 + 4 + 6 + 35,
"k": now - 2367 + 168 + 167 + 4 + 6 + 15,
"l": now - 2367 + 168 + 167 + 4 + 6 + 35,
"m": now - 2367 + 168 + 167 + 4 + 6 + 35 + 16,
"n": now - 2367 + 168 + 167 + 4 + 6 + 35 + 16 + 2,
"o": now - 2367 + 168 + 167 + 4 + 6 + 35 + 16 + 2 + 12,
"p": now - 2367 + 168 + 167 + 4 + 6 + 35 + 16 + 2 + 12 + 3,
"q": now - 2367 + 168 + 167 + 4 + 6 + 35 + 16 + 2 + 12 + 3,
"r": now - 2367 + 168 + 167 + 4 + 6 + 35 + 16 + 2 + 12 + 3 + 18,
"s": now - 2367 + 168 + 167 + 4 + 6 + 35 + 16 + 2 + 12 + 3 + 18 + 65,
"t": now - 2367 + 168 + 167 + 4 + 6 + 35 + 16 + 2 + 12 + 3 + 18 + 65,
"u": now - 2367 + 168 + 167 + 4 + 6 + 35 + 16 + 2 + 12 + 3 + 18 + 65
},
"td": -1
}
}

H方法是一个控制流,和之前的一样,它的控制判断流程没有意义,直接拿循环的主题部分就好了

image-20230531163816356

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function H(t, e) {
for (var n = e['slice'](-2), r = [], i = 0; i < n['length']; i++) {
var o = n['charCodeAt'](i);
r[i] = 57 < o ? o - 87 : o - 48;
}
n = 36 * r[0] + r[1];
var s, a = Math['round'](t) + n, _ = [[], [], [], [], []], c = {}, u = 0;
i = 0;
for (var l = (e = e['slice'](0, -2))['length']; i < l; i++)
c[s = e['charAt'](i)] || (c[s] = 1,
_[u]['push'](s),
u = 5 == ++u ? 0 : u);
var h, f = a, d = 4, p = '', g = [1, 2, 5, 10, 50];
while (0 < f)
0 <= f - g[d] ? (h = parseInt(Math['random']() * _[d]['length'], 10),
p += _[d][h],
f -= g[d]) : (_['splice'](d, 1),
g['splice'](d, 1),
d -= 1);
return p;

}

然后我们再处理一下轨迹,即找一下e的加密过程,看一下这边轨迹做了哪些处理,我们把堆栈往上走一步,这个l呢就是轨迹处理,第一个参数就是轨迹, l = n[$_DAAAt(912)][$_CJJJp(1034)](n[$_DAAAt(912)][$_CJJJp(1069)](), n[$_DAAAt(71)][$_DAAAt(1091)], n[$_CJJJp(71)][$_DAAAt(389)]) 这段代码有点长,我们先看,我们处理一下

image-20230531174448247

外层的括号代表是一个方法 n[$_DAAAt(912)][$_CJJJp(1034)] ,该方法传入三个参数,第一个参数是 n[$_DAAAt(912)][$_CJJJp(1069)](),可以很明显的看到,[$_CJJJp(1069)]n[$_DAAAt(912)] 原型链上的一个方法,其实就相当于 `[$_CJJJp(1069)].n[$_DAAAt(912)] 我们把外层的方法和里面的方法一起抠出来

1
2
3
4
5
l = n[$_DAAAt(912)][$_CJJJp(1034)](
n[$_DAAAt(912)][$_CJJJp(1069)](),
n[$_DAAAt(71)][$_DAAAt(1091)],
n[$_CJJJp(71)][$_DAAAt(389)]
)

image-20230531175231123

抠出来整理后的代码是这样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
function $_CJJJp_34(t, e, n) {
if (!e || !n)
return t;
var r, i = 0, o = t, s = e[0], a = e[2], _ = e[4];
while (r = n['substr'](i, 2)) {
i += 2;
var c = parseInt(r, 16)
, u = String['fromCharCode'](c)
, l = (s * c * c + a * c + _) % t['length'];
o = o['substr'](0, l) + u + o['substr'](l);
}
return o;
}

function $_CJJJp_69(guiji) {
function n(t) {
var e = '()*,-./0123456789:?@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqr'
, n = e['length']
, r = ''
, i = Math['abs'](t)
, o = parseInt(i / n);
n <= o && (o = n - 1),
o && (r = e['charAt'](o));
var s = '';
return t < 0 && (s += '!'),
r && (s += '$'),
s + r + e['charAt'](i %= n);

}
var t = function (t) {
for (var e, n, r, i = [], o = 0, s = 0, a = t['length'] - 1; s < a; s++)
e = Math['round'](t[s + 1][0] - t[s][0]),
n = Math['round'](t[s + 1][1] - t[s][1]),
r = Math['round'](t[s + 1][2] - t[s][2]),
0 == e && 0 == n && 0 == r || (0 == e && 0 == n ? o += r : (i['push']([e, n, r + o]),
o = 0));
return 0 !== o && i['push']([e, n, o]),
i;
}(guiji)
, r = []
, i = []
, o = [];
return new ct(t)['$_CAd'](function (t) {
var e = function (t) {
for (var e = [[1, 0], [2, 0], [1, -1], [1, 1], [0, 1], [0, -1], [3, 0], [2, -1], [2, 1]], n = 0, r = e['length']; n < r; n++)
if (t[0] == e[n][0] && t[1] == e[n][1])
return 'stuvwxyz~'[n];
return 0;
}(t);
e ? i['push'](e) : (r['push'](n(t[0])),
i['push'](n(t[1]))),
o['push'](n(t[2]));
}),
r['join']('') + '!!' + i['join']('') + '!!' + o['join']('');
}

需要注意的几个地方,一个是这里,括号里面的那个方法没有传递参数,但实际上这个就相当于参数传递,这个东西是轨迹,this.轨迹,这就是为什么是原型链上的方法,因为this的作用域指向的就是轨迹,所以这里我们直接从函数上面把轨迹传递进来就好了

image-20230531181250554

还有一个地方是这里, 这里新建了一个ct方法,然后再ct方法的原型链上又添加了一个新方法,我们需要把这两个方法找出来

image-20230531181458174

1
2
3
4
5
6
7
8
9
10
11
12
function ct(t) {
this['$_BCAc'] = t || [];
}

ct.prototype.$_CAd = function (t) {
var e = this['$_BCAc'];
if (e['map'])
return new ct(e['map'](t));
for (var n = [], r = 0, i = e['length']; r < i; r += 1)
n[r] = t(e[r], r, this);
return new ct(n);
}

接下来我们就可以测试一下括号内的方法有没有什么问题了,运行结果没什么问题,那我们就看下一个外层的方法

image-20230531182741153

我们先看下外层方法的另外两个参数 n[$_DAAAt(71)][$_DAAAt(1091)], n[$_CJJJp(71)][$_DAAAt(389)]

简化一下就是 n[$_DAAAt(71)]["c"], n[$_CJJJp(71)]["s"] 而c 和 s就是上一个请求,即获取验证码图片的那个请求中的c和s参数,我们这里先固定写死,后面再传参

image-20230531185533521

运行一下,和浏览器里面一样,这样l参数我们就解决了,也就是被加密的轨迹 e参数解决了

image-20230531185812219

现在 我们回到最开始的try 条件那里,看一下这一部走了什么,我们的最终目的是为了完成 h + u,在这里还需要知道o是什么,而o是在try之前就做了定义,这一段try catch会非常头疼,所以我们直接看结果,看一下进try之后做了什么

image-20230601110259671

将断点设在上下两个地方,可以很明显看到就多了一个 h9s9的值,而其它值我们已经搞定了,而这个值好像又和random值有关系,那么我们也可以直接在这里用定值,直接把try catch这一段给删掉,然后给o加定值,而且这个值并没有在后续进行校验,所以可以随便给,再整理一下,就是这样了

image-20230601110640578

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
function get_3w(t, guiji, n) {
var c = [12, 58, 98, 36, 43, 95, 62, 15, 12];
var s = "7546376a";
var guiji = [[
-24,
-23,
0
],]
var l = get_l(guiji)
var r = this
, i = r['$_CJV']
, o = {
'lang': 'zh-cn',
'userresponse': H(t, i['challenge']),
'passtime': n,
'imgload': 21,
'aa': l,
'ep': $_CCCP(),
"h9s9": "1816378497",
};
// i['offline'] && (o['x'] = t),
o['rp'] = X(i['gt'] + i['challenge']['slice'](0, 32) + o['passtime']);
var u = r['$_CCDf']()
, l = V['encrypt'](gt['stringify'](o), r['$_CCEV']())
, h = m['$_FEE'](l)
, f = {
"\u0067\u0074": i['gt'],
"\u0063\u0068\u0061\u006c\u006c\u0065\u006e\u0067\u0065": i['challenge'],
"\u006c\u0061\u006e\u0067": o['lang'],
"\u0024\u005f\u0042\u0043\u0058": r['$_BHIK'],
"\u0063\u006c\u0069\u0065\u006e\u0074\u005f\u0074\u0079\u0070\u0065": r['$_BHJU'],
"\u0077": h + u
};
return f;
}

然后我们再看一下X方法,X方法比较长,它其实是一个md5方法,随便抠一下就好了,运行结果和md5方法一样就可以了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
function X(t, e) {
function _(t, e) {
return t << e | 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(_(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(_(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(_(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(_(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 e, n = '', r = '';
for (e = 0; e <= 3; e++)
n += (r = '0' + (t >>> 8 * e & 255)['toString'](16))['substr'](r['length'] - 2, 2);
return n;

}

var s, a, u, l, h, f, d, p, g, v;
for (s = function m(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 < n)
s = a % 4 * 8,
o[e = (a - a % 4) / 4] = o[e] | t['charCodeAt'](a) << s,
a++;
return s = a % 4 * 8,
o[e = (a - a % 4) / 4] = o[e] | 128 << s,
o[i - 2] = n << 3,
o[i - 1] = n >>> 29,
o;
}(t = function y(t) {
t = t['replace'](/\r\n/g, '\n');
for (var e = '', n = 0; n < t['length']; n++) {
var r = t['charCodeAt'](n);
r < 128 ? e += String['fromCharCode'](r) : (127 < r && r < 2048 ? e += String['fromCharCode'](r >> 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,
v = 271733878,
a = 0; a < s['length']; a += 16)
p = i(p = i(p = i(p = i(p = r(p = r(p = r(p = r(p = n(p = n(p = n(p = n(p = e(p = e(p = e(p = e(l = p, g = e(h = g, v = e(f = v, d = e(u = d, p, g, v, s[a + 0], 7, 3614090360), p, g, s[a + 1], 12, 3905402710), d, p, s[a + 2], 17, 606105819), v, d, s[a + 3], 22, 3250441966), g = e(g, v = e(v, d = e(d, p, g, v, s[a + 4], 7, 4118548399), p, g, s[a + 5], 12, 1200080426), d, p, s[a + 6], 17, 2821735955), v, d, s[a + 7], 22, 4249261313), g = e(g, v = e(v, d = e(d, p, g, v, s[a + 8], 7, 1770035416), p, g, s[a + 9], 12, 2336552879), d, p, s[a + 10], 17, 4294925233), v, d, s[a + 11], 22, 2304563134), g = e(g, v = e(v, d = e(d, p, g, v, s[a + 12], 7, 1804603682), p, g, s[a + 13], 12, 4254626195), d, p, s[a + 14], 17, 2792965006), v, d, s[a + 15], 22, 1236535329), g = n(g, v = n(v, d = n(d, p, g, v, s[a + 1], 5, 4129170786), p, g, s[a + 6], 9, 3225465664), d, p, s[a + 11], 14, 643717713), v, d, s[a + 0], 20, 3921069994), g = n(g, v = n(v, d = n(d, p, g, v, s[a + 5], 5, 3593408605), p, g, s[a + 10], 9, 38016083), d, p, s[a + 15], 14, 3634488961), v, d, s[a + 4], 20, 3889429448), g = n(g, v = n(v, d = n(d, p, g, v, s[a + 9], 5, 568446438), p, g, s[a + 14], 9, 3275163606), d, p, s[a + 3], 14, 4107603335), v, d, s[a + 8], 20, 1163531501), g = n(g, v = n(v, d = n(d, p, g, v, s[a + 13], 5, 2850285829), p, g, s[a + 2], 9, 4243563512), d, p, s[a + 7], 14, 1735328473), v, d, s[a + 12], 20, 2368359562), g = r(g, v = r(v, d = r(d, p, g, v, s[a + 5], 4, 4294588738), p, g, s[a + 8], 11, 2272392833), d, p, s[a + 11], 16, 1839030562), v, d, s[a + 14], 23, 4259657740), g = r(g, v = r(v, d = r(d, p, g, v, s[a + 1], 4, 2763975236), p, g, s[a + 4], 11, 1272893353), d, p, s[a + 7], 16, 4139469664), v, d, s[a + 10], 23, 3200236656), g = r(g, v = r(v, d = r(d, p, g, v, s[a + 13], 4, 681279174), p, g, s[a + 0], 11, 3936430074), d, p, s[a + 3], 16, 3572445317), v, d, s[a + 6], 23, 76029189), g = r(g, v = r(v, d = r(d, p, g, v, s[a + 9], 4, 3654602809), p, g, s[a + 12], 11, 3873151461), d, p, s[a + 15], 16, 530742520), v, d, s[a + 2], 23, 3299628645), g = i(g, v = i(v, d = i(d, p, g, v, s[a + 0], 6, 4096336452), p, g, s[a + 7], 10, 1126891415), d, p, s[a + 14], 15, 2878612391), v, d, s[a + 5], 21, 4237533241), g = i(g, v = i(v, d = i(d, p, g, v, s[a + 12], 6, 1700485571), p, g, s[a + 3], 10, 2399980690), d, p, s[a + 10], 15, 4293915773), v, d, s[a + 1], 21, 2240044497), g = i(g, v = i(v, d = i(d, p, g, v, s[a + 8], 6, 1873313359), p, g, s[a + 15], 10, 4264355552), d, p, s[a + 6], 15, 2734768916), v, d, s[a + 13], 21, 1309151649), g = i(g, v = i(v, d = i(d, p, g, v, s[a + 4], 6, 4149444226), p, g, s[a + 11], 10, 3174756917), d, p, s[a + 2], 15, 718787259), v, d, s[a + 9], 21, 3951481745),
d = c(d, u),
p = c(p, l),
g = c(g, h),
v = c(v, f);
return (o(d) + o(p) + o(g) + o(v))['toLowerCase']();
}

在接下来就剩下最后的 u l h 的计算了 , u = r['$_CCDf']() u其实是RSA加密,跟第一个W那里的RSA加密一模一样,把相关的函数和参数挪过来就好了, $_CCIl 就是aeskey

image-20230601115041534

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function te() {
return e() + e() + e() + e();
}

function e() {
return (65536 * (1 + Math['random']()) | 0)['toString'](16)['substring'](1);
}

function $_CCIl(e) {
return quanjv['$_EIk']['aeskey'] && !e || (quanjv['$_EIk']['aeskey'] = te()), // 如果aeskey 有 并且 e没有 返回 this['$_EIk']['aeskey']
quanjv['$_EIk']['aeskey']; // 如果上述是否 那就重新创建一个aeskey,从te()方法创建
}

function $_CCDf(e) {
var rsa = new RSAKey(); // 因为源码中,没有下面这一步,所以我们得手动补上设置公钥的步骤
var public_key = "00C1E3934D1614465B33053E7F48EE4EC87B14B95EF88947713D25EECBFF7E74C7977D02DC1D9451F79DD5D1C10C29ACB6A9B4D6FB7D0A0279B6719E1772565F09AF627715919221AEF91899CAE08C0D686D748B20A3603BE2318CA6BC2B59706592A9219D0BF05C9F65023A21D2330807252AE0066D59CEEFA5F2748EA80BAB81"
rsa.setPublic(public_key, '10001')

var t = rsa['encrypt']($_CCIl(e)); // 对aeskey进行加密
while (!t || 256 !== t['length']) // 如果t的长度不等于256就一直加密
t = rsa['encrypt']($_CCIl(!0));
return t;
}

接下来就是l参数了 l = V['encrypt'](JSON['stringify'](o), r['$_CCEV']()) 这里就是个aes加密,和第一个w里面扣的时候一模一样

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function encrypt1(e, t, n) {
var key = t;
var iv = "0000000000000000";

key = CryptoJS.enc.Utf8.parse(key);
iv = CryptoJS.enc.Utf8.parse(iv);
var r = CryptoJS.AES.encrypt(e, key, {
iv: iv,
mode: CryptoJS.mode.CBC
});

var o = r['ciphertext']['words'];
var i = r['ciphertext']['sigBytes'];
var s = [];
for (var a = 0; a < i; a++) {
var c = o[a >>> 2] >>> 24 - a % 4 * 8 & 255;
s['push'](c);
}
return s;
}

然后 h = m['$_FEE'](l) 这里和第一个w那里的 i = $_HEf(o) 处理方式一模一样,同样呢直接挪过来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
function $_HEf(e) {
var t = $_HCh(e);
return t['res'] + t['end'];

}

function $_HCh(e, o) {
var i = this;
o || (o = i);
for (var t = function (e, t) {
for (var n = 0, r = 24 - 1; 0 <= r; r -= 1)
1 === $_HBw(t, r) && (n = (n << 1) + $_HBw(e, r));
return n;
}, n = '', r = '', s = e['length'], a = 0; a < s; a += 3) {
var c;
if (a + 2 < s)
c = (e[a] << 16) + (e[a + 1] << 8) + e[a + 2],
n += $_GJz(t(c, 7274496)) + $_GJz(t(c, 9483264)) + $_GJz(t(c, 19220)) + $_GJz(t(c, 235));
else {
var _ = s % 3;
2 == _ ? (c = (e[a] << 16) + (e[a + 1] << 8),
n += $_GJz(t(c, 7274496)) + $_GJz(t(c, 9483264)) + $_GJz(t(c, 19220)),
r = '.') : 1 == _ && (c = e[a] << 16,
n += $_GJz(t(c, 7274496)) + $_GJz(t(c, 9483264)),
r = '.' + '.');
}
}
return {
"\u0072\u0065\u0073": n,
"\u0065\u006e\u0064": r
};
}

function $_HBw(e, t) {
return e >> t & 1;
}

function $_GJz(e) {
var t = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789()';
return e < 0 || e >= t['length'] ? '.' : t['charAt'](e);
}

最终将代码整理一下,看一下跑出来的结果,看上去好像没什么问题,然后我们回去处理一下轨迹和滑动距离

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// t:滑动的距离 e:被加密后的轨迹 n: 滑动的时间
function get_3w(gt,challenge,s,c,t, guiji, n) {
var l = get_l(guiji,c,s)
var i = {
"gt" :gt,
"challenge":challenge,
}
var o = {
'lang': 'zh-cn',
'userresponse': H(t, i['challenge']),
'passtime': n,
'imgload': 21,
'aa': l,
'ep': $_CCCP(),
"h9s9": "1816378497",
};
// i['offline'] && (o['x'] = t),
o['rp'] = X(i['gt'] + i['challenge']['slice'](0, 32) + o['passtime']);

var u = $_CCDf() // RSA加密
, l = encrypt1(JSON['stringify'](o), $_CCIl()) // AES加密
, h = $_HEf(l) // 数据处理
, f = {
"gt": i['gt'],
"challenge": i['challenge'],
"lang":'zh-cn',
'$_BCX': 0,
'client_type': 'web',
"w": h + u
};
return f;
}

var c = [12, 58, 98, 36, 43, 95, 62, 15, 12];
var s = "7546376a";
var guiji = [
[
-24,
-23,
0
]

]
var gt = '019924a82c70bb123aae90d483087f94'
var challenge = 'a2968f8eecf3aec14f1a741af0f08ad89x'

console.log(get_3w(gt,challenge,s,c,159, guiji, 590))

image-20230601121140334

提交验证

最后也是没有任何问题,验证成功

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# 第五次请求,获取各项图片
params = {
'is_next': 'true',
'type': 'slide3',
'gt': gt,
'challenge': challenge,
'lang': 'zh-cn',
'https': 'true',
'protocol': 'https://',
'offline': 'false',
'product': 'popup',
'api_server': 'api.geetest.com',
'isPC': 'true',
'autoReset': 'true',
'width': '100%',
'callback': 'geetest_{}'.format(get_time()),
}

response = session.get('https://api.geetest.com/get.php', params=params)
# print(response.text)
res_img = jsonp_data(response.text)
bg_url = "https://static.geetest.com/" + res_img['bg']
# fullbg_url = "https://static.geetest.com/" + res_img['fullbg']
slice_url = "https://static.geetest.com/" + res_img['slice']
resp = session.get(bg_url)
with open("./imgs/bg.jpg", 'wb') as f:
f.write(resp.content)
resp = session.get(slice_url)
with open("./imgs/slice.jpg", 'wb') as f:
f.write(resp.content)
print(challenge)
new_challenge = res_img['challenge']
print(new_challenge)
c = res_img['c']
s = res_img['s']
# 还原图片顺序
# true_back("fullbg.jpg")
true_back("bg.jpg")
# 获取滑动距离
distance = get_slice()
print(distance)
# 获取轨迹,和滑动时间
slide_track, t = get_slide_track(distance)
# console.log(get_3w(gt, challenge, s, c, 159, guiji, 590))

print(gt, new_challenge, s, c, distance, slide_track, t)
W3 = three_js.call('get_3w', gt, new_challenge, s, c, distance, slide_track, t)


# 第六次请求,获取验证码结果
params3 = W3
params3['callback'] = 'geetest_{}'.format(get_time())
print(params3)
response = session.get('https://api.geetest.com/ajax.php', params=params3)
# res_img = jsonp_data(response.text)
print(response.text)

image-20230601162115557