书接上文

第二个w获取流程

第二个w也可以不用去获取,因为第二个w不涉及到验证码的获取和验证,至少现在还没有

点击验证码,获取到如下包,一个 ajax.php 和 get.php

image-20230527114721325

4.第四个请求,携带解密后的w,用于识别验证码是否加载成功

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

image-20230527115052238

image-20230527115101034

第二个w解密流程

第二个w要找到加密位置,需要看html页面,他是通过动态加载script标签,然后通过加载js来完成加密

image-20230527205716028

打上断点,刷新页面,通过观察加载德script标签,发现并不是我们需要的请求,所以直接过掉

image-20230527205824553

image-20230527210024419

过掉所有断点后,我们再点击按钮,将验证码加载出来,这一次断点再次打上,通过观察会发现是我们要请求的接口,然后我们向上追栈

image-20230527210334696

向上追一个栈,我们来到这个函数,我们分析一下 [$_HCDk(340)] 就是调用最上面的那个方法, 他传入了一个new le(f),

image-20230527211333882

image-20230527211503385

往上面看,这里创建了几个参数,其中n已经包含了src,也就是在这里已经完成了w的加密,我们在上面打一下断点,看一下n在这里做了什么

image-20230527212047263

在n下面重新打上断点后,进来发现n里面的script标签里面并没有src属性,第二步将n里面的script标签传给了r,再往下一步,发现是对s进行正则操作,我们看下s是什么,发现s就是我们想要的src,接下来 我们要找到这个s是怎么生成的了

image-20230527212445650

image-20230527212626434

image-20230527212847127

image-20230527212939133

我们在往上看,会发现s是传入进来的,我们也可以在这里打断点看一下是否是传进来就已经加密好,还是就是在这里加密的,经过测试会发现是传进来就加密好了,所以我们要去找是谁调用了这个函数,传入的第一个参数是什么,同样进行追栈

image-20230527213253183

image-20230527213402609

往上追一个栈之后来到这里,同样的,验证一下,发现n在进去函数c之前就已经完成了加密,所以我们追函数C的栈

image-20230527214330057

就在下面,进来之后我们会发现i参数里面包含了该接口请求的所有参数包括加密的w参数,所以我们得去找一下i的来源,再往上看,D函数传入的参数包含i,同样的验证一下是否是传进来就加密好了

image-20230527214605844

image-20230527214925324

答案显而易见,我们再次追栈

image-20230527215036178

来到上一个函数,继续验证,验证结果,继续朔源

image-20230527215519876

image-20230527215612277

发现还是传进来的,继续验证,验证完成,继续朔源

image-20230527215722044

image-20230527215743104

image-20230527215828871

断点来到N的上面,我们会发现n在这里进行了组装

image-20230527221231133

image-20230527221345196

我们再看一下这个代码,t是this,而t[xxx(1481)] 等于我们想要的w,xxx(1481)肯定是做了混淆,但是可以肯定的是,在某一段代码里面肯定执行了,xxx(1481) = w 的操作,xxx我们搜不到做了混淆,但是1481应该是可以搜索得到的

image-20230527221811251

这里一共就三个,去掉本身那个,还有一个是一串数字,剩下的就只有这个符合我们的要求了,我们在这里打上断点。会发现 i[xxxx] = w ,而i在上面, i就是this,而我们要完成的就是 p[$_CGBDY(1303)](_[$_CGBDY(12)](r, i[$_CGBDY(1361)]())) 的解密

image-20230527222112657

image-20230527222241640

image-20230527222331169

第二个w获取流程

我们先把w加密的这一段代码扣下来,前面五行也是跟之前一样毫无意义的循环,都是一样的值,直接删掉

image-20230529201246396

删完之后我们来看一下这个控制流,发现会走所有的流程,所以我们直接精简一下

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
function second_w(e) {
t[$_CGBCi(95)](e[$_CGBDY(73)]());
var r = $_CGBCi(226);
!function o(e, t) {
function n(e) {
var $_DEFIu = VIPVz.$_Ds()[4][14];
for (; $_DEFIu !== VIPVz.$_Ds()[8][12];) {
switch ($_DEFIu) {
case VIPVz.$_Ds()[4][14]:
var t = 5381
, n = e[$_CGBHd(54)]
, r = 0;
while (n--)
t = (t << 5) + t + e[$_CGBIC(94)](r++);
$_DEFIu = VIPVz.$_Ds()[0][13];
break;
case VIPVz.$_Ds()[8][13]:
return t &= ~(1 << 31);
break;
}
}
}

100 < new Date()[$_CGBIC(256)]() - t[$_CGBIC(256)]() && (e = $_CGBIC(1474)),
r = $_CGBHd(764) + i[$_CGBIC(1448)] + $_CGBIC(1427) + n(o[$_CGBIC(73)]() + n(n[$_CGBHd(73)]()) + n(e[$_CGBHd(73)]())) + $_CGBHd(1471);
}(t[$_CGBDY(1440)](), new Date()),
i[$_CGBCi(1481)] = p[$_CGBDY(1303)](_[$_CGBDY(12)](r, i[$_CGBDY(1361)]()));
}

image-20230529202254446

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function second_w(e) {
t['push'](e['toString']());
var r = '';
!function o(e, t) {
function n(e) {
var t = 5381, n = e['length'], r = 0;
while (n--)
t = (t << 5) + t + e['charCodeAt'](r++);
return t &= ~(1 << 31);
}
100 < new Date()['getTime']() - t['getTime']() && (e = 'qwe'),
r = '{' + i['$_CEG_'] + '"captcha_token":"' + n(o['toString']() + n(n['toString']()) + n(e['toString']())) + '"}';
}
(t['shift'](), new Date()),
i['$_CEEK'] = p['$_HEf'](_['encrypt'](r, i['$_CCIl']()));
}

我们先看一下 r ,o和n视乎是现成的,就在上面,我们看下 o['toString']()n['toString'](),以及 e['toString']() 怀疑这里是否是做了代码检测,这里的运行结果肯定是会和我们精简话后的代码运行结果不一样的,我们直接把这三个的值给写死,直接复制控制台输出出来的

image-20230530100647305

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function second_w(e) {
t['push'](e['toString']());
o_tostring = "function o(e,t){var..."
n_tostring = 'function ...'
e_tostring = 'bbOy'
var r = '';
!function o(e, t) {
function n(e) {
var t = 5381, n = e['length'], r = 0;
while (n--)
t = (t << 5) + t + e['charCodeAt'](r++);
return t &= ~(1 << 31);
}
100 < new Date()['getTime']() - t['getTime']() && (e = 'qwe'),
r = '{' + i['$_CEG_'] + '"captcha_token":"' + n(o_tostring + n(n_tostring) + n(e_tostring)) + '"}';
}(t['shift'](), new Date()),i['$_CEEK'] = p['$_HEf'](_['encrypt'](r, i['$_CCIl']()));
}

接下来 我们来找一下 i['$_CEG_'] 是怎么生成的,源代码是i[$_CGBIC(1448)],还是那个逻辑,从某个地方取值,那肯定会有从这个地方存值的操作,直接搜索1448,搜索出来的结果有三个我们直接在其它两个地方打断点

image-20230530101948282

可以看到,在最开始的时候,做了一个赋值空字符串的操作,接下来就是一个大号的循环在循环里面做一个拼接的操作,这里一大串都是 for(;;) 也就是for循环里面的判断和自增条件,而真正的循环体只有最后一个拼接的操作

image-20230530103343268

我们把代码复制出来,处理一下

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
function get_i() {
var i = this
, e = i['$_BJJF']['$_BIBF']()
, t = i['$_BJJF']['$_BICQ']()
, n = i['$_BJCr']['$_BICQ']()
, r = i['$_BDHN']['$_BIBF']()
, o = i['$_EIk']
, s = $_GY() - ot;
result = "";
for (var a = [['lang', o['lang'] || 'zh-cn'], ['type', 'fullpage'], ['tt', function (e, t, n) {
if (!t || !n)
return e;
var r, o = 0, i = e, s = t[0], a = t[2], c = t[4];
while (r = n['substr'](o, 2)) {
o += 2;
var _ = parseInt(r, 16)
, l = String['fromCharCode'](_)
, u = (s * _ * _ + a * _ + c) % e['length'];
i = i['substr'](0, u) + l + i['substr'](u);
}
return i;
}(e, o['c'], o['s']) || -1], ['light', r || -1], ['s', V(p['$_HDc'](t))], ['h', V(p['$_HDc'](n))], ['hh', V(n)], ['hi', V(i['$_CCGM'])], ['vip_order', i['vip_order'] || -1], ['ct', i['ct'] || -1], ['ep', i['$_CEHV']() || -1], ['passtime', s || -1], ['rp', V(o['gt'] + o['challenge'] + s)]], c = 0; c < a['length']; c++)
result += '"' + a[c][0] + '":' + ge['stringify'](a[c][1]) + ',';
return result;
}

从上到下,我们先处理前面一大段赋值的操作,先看 i['$_BJJF']['$_BIBF'](),进去打断点,我们先看一下

this[$_CDDIY(34)] 很显然 这是一段轨迹的操作,下一段代码又将其制空,这一步操作没有意义,后面肯定是要对轨迹进行处理的,而这一段轨迹我们可以用算法生成,也可以直接复制,这一段轨迹来自于你对点击生成验证码的操作进行鼠标轨迹检测,这也就是为什么用selenium无法成功的原因

image-20230530113517383

image-20230530113555853

我们往下执行一步,输出一下 this[$_CDDIY(917)](e),这里传递的值应该是上一步的时间差值,并没有对时时的时间进行计算,所以我们可以直接复制这个轨迹。到了这里,e参数的加密入口就处理好了,现在要解决的是 this['$_BHIh'](e) 方法

image-20230530151150040

1
2
3
4
5
6
7
8
9
10
11
12
13
function $_BIBF_e() {
var e = [
[
"move",
1098,
239,
1685417642390,
"pointermove"
],
..... // 这里是复制的轨迹
];
return this['$_HDc'](this['$_BHIh'](e));
}

接下来就是扣代码的环节,把没必要的给删掉,需要注意的是 ce(['down', 'move', 'up', 'scroll'])['$_EHc'](p) 这里需要在ce的原型链上添加新的方法,需要注意

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
function ce(e) {
this['$_BAEJ'] = e || [];
}

ce.prototype = {
"$_EHc": function (e) {
var t = this['$_BAEJ'];
if (t['indexOf'])
return t['indexOf'](e);
for (var n = 0, r = t['length']; n < r; n += 1)
if (t[n] === e)
return n;
return -1;
}
}


function $_BHHD(e) {
var t = 32767;
return 'number' != typeof e ? e : (t < e ? e = t : e < -t && (e = -t),
Math['round'](e));
}

function $_BHJu(e) {
var t = ''
, n = 0;
(e || [])['length'];
while (!t && e[n])
t = e[n] && e[n][4],
n++;
if (!t)
return e;
for (var r = '', o = ['mouse', 'touch', 'pointer', 'MSPointer'], i = 0, s = o['length']; i < s; i++)
0 === t['indexOf'](o[i]) && (r = o[i]);
for (var a = e['slice'](), c = a['length'] - 1; 0 <= c; c--) {
var _ = a[c]
, l = _[0];
if (-1 < new ce(['move', 'down', 'up'])['$_EHc'](l))
0 !== (_[4] || '')['indexOf'](r) && a['splice'](c, 1);
}
return a;
}

// 处理轨迹
function $_BHIh(e) {
var t = 0
, n = 0
, r = []
, i = 0;
if (e['length'] <= 0)
return [];
for (var s = null, a = null, c = $_BHJu(e), _ = c['length'], l = _ < 300 ? 0 : _ - 300; l < _; l += 1) {
var u = c[l]
, p = u[0];
-1 < new ce(['down', 'move', 'up', 'scroll'])['$_EHc'](p) ? (s || (s = u),
a = u,
r['push']([p, [u[1] - t, u[2] - n], $_BHHD(i ? u[3] - i : i)]),
t = u[1],
n = u[2],
i = u[3]) : -1 < new ce(['blur', 'focus', 'unload'])['$_EHc'](p) && (r['push']([p, $_BHHD(i ? u[1] - i : i)]),
i = u[1]);
}
// o[aa(922)] = s,o[aa(978)] = a, 两个不不需要,直接删除
return r;
}

然后运行一下 this['$_BHIh'](e) 控制台输出和我们的运行结果一致,这个方法就解决了,现在要解决的是 this['$_HDc']($_BHIh(e)) 方法,这个方法比较长,需要扣仔细一点

image-20230530155302966

抠出来的代码会精简很多,极验的控制流都是假的,直接把控制流的主体部分拿出来就可以了,抠出来运行一下看一下是否和浏览器的一样,验证结果没有问题,e参数解密完成。那就回到加密入口,去看一下t参数

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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
function $_HDc(e) {
var p = {
"\u006d\u006f\u0076\u0065": 0,
"\u0064\u006f\u0077\u006e": 1,
"\u0075\u0070": 2,
"\u0073\u0063\u0072\u006f\u006c\u006c": 3,
"\u0066\u006f\u0063\u0075\u0073": 4,
"\u0062\u006c\u0075\u0072": 5,
"\u0075\u006e\u006c\u006f\u0061\u0064": 6,
"\u0075\u006e\u006b\u006e\u006f\u0077\u006e": 7
};

function h(e, t) {
for (var n = e['toString'](2), r = '', o = n['length'] + 1; o <= t; o += 1)
r += '0';

return n = r + n;

}

function f(e) {
var t = []
, n = e['length']
, r = 0;
while (r < n) {
var o = e[r]
, i = 0;
while (1) {
if (16 <= i)
break;
var s = r + i + 1;
if (n <= s)
break;
if (e[s] !== o)
break;
i += 1;
}
r = r + 1 + i;
var a = p[o];
0 != i ? (t['push'](8 | a),
t['push'](i - 1)) : t['push'](a);
}
for (var c = h(32768 | n, 16), _ = '', l = 0, u = t['length']; l < u; l += 1)
_ += h(t[l], 4);
return c + _;
}

function _(e, t) {
for (var n = [], r = 0, o = e['length']; r < o; r += 1)
n['push'](t(e[r]));
return n;
}
function g(e, t) {
e = function c(e) {
var t = 32767
, n = (e = _(e, function (e) {
return t < e ? t : e < -t ? -t : e;
}))['length']
, r = 0
, o = [];
while (r < n) {
var i = 1
, s = e[r]
, a = Math['abs'](s);
while (1) {
if (n <= r + i)
break;
if (e[r + i] !== s)
break;
if (127 <= a || 127 <= i)
break;
i += 1;
}
1 < i ? o['push']((s < 0 ? 49152 : 32768) | i << 7 | a) : o['push'](s),
r += i;
}
return o;
}(e);
var n, r = [], o = [];
_(e, function (e) {
var t = Math['ceil'](function n(e, t) {
return 0 === e ? 0 : Math['log'](e) / Math['log'](t);
}(Math['abs'](e) + 1, 16));
0 === t && (t = 1),
r['push'](h(t - 1, 2)),
o['push'](h(Math['abs'](e), 4 * t));
});
var i = r['join']('')
, s = o['join']('');
return n = t ? _(function a(e, t) {
var n = [];
return _(e, function (e) {
t(e) && n['push'](e);
}),
n;
}(e, function (e) {
return 0 != e && e >> 15 != 1;
}), function (e) {
return e < 0 ? '1' : '0';
})['join']('') : '',
h(32768 | e['length'], 16) + i + s + n;
}

return function (e) {
for (var t = [], n = [], r = [], o = [], i = 0, s = e['length']; i < s; i += 1) {
var a = e[i]
, c = a['length'];
t['push'](a[0]),
n['push'](2 === c ? a[1] : a[2]),
3 === c && (r['push'](a[1][0]),
o['push'](a[1][1]));
}
var _ = f(t) + g(n, !1) + g(r, !0) + g(o, !0)
, l = _['length'];
return l % 6 != 0 && (_ += h(0, 6 - l % 6)),
function u(e) {
for (var t = '', n = e['length'] / 6, r = 0; r < n; r += 1)
t += '()*,-./0123456789:?@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~'['charAt'](parseInt(e['slice'](6 * r, 6 * (r + 1)), 2));
return t;
}(_);
}(e);
}

image-20230530161827587

t = i[$_CFJHp(1378)][$_CFJHp(1419)]() 我们看一下这个函数,直接看return this[$_CDEDb(34)] 是一个空数组,this[$_CDEDb(942)] 就在上面,也就是我们上一步做的轨迹加密,但是这一次它传入了一个空数组,所以这个方法我们可以这样写,验证一下,没有问题,这一步其实也可以写死

image-20230530162730718

1
2
3
4
// 参数t的加密入口
function $_BICQ_t(){
return $_HDc([]);
}

image-20230530163052621

接下来看参数n n = i[$_CFJIy(1313)][$_CFJIy(1419)](),其中 n[$_CEAIT(34)] 其实就是上一个w里面那两个数组中包含浏览器指纹的那个,而下面的 n[$_CEAHd(1374)]() 就是另一个数组,所以 这两个数组都可以直接复制过来,里面的时间戳改成 new Date().getTime() 就好了

image-20230530174631647

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
function $_BICQ_n(e, t) {
var n = this,
r = {
"STYLE": 2,
"SCRIPT": 6,
"A": 1,

"hardwareConcurrency": 16,
"jsFonts": "Arial,ArialBlack,ArialNarrow,Calibri,Cambria,CambriaMath,ComicSansMS,Consolas,Courier,CourierNew,Georgia,Helvetica,Impact,LucidaConsole,LucidaSansUnicode,MicrosoftSansSerif,MSGothic,MSPGothic,MSSansSerif,MSSerif,PalatinoLinotype,SegoePrint,SegoeScript,SegoeUI,SegoeUILight,SegoeUISemibold,SegoeUISymbol,Tahoma,Times,TimesNewRoman,TrebuchetMS,Verdana,Wingdings",
"mediaDevices": -1,
"timestamp": new Date().getTime(),
"touchEvent": -1,
"performanceTiming": -1,
"internalip": -1
}, // 浏览器指纹
o = [];
array1 = [
"textLength",
"HTMLLength",

]
return new ce(array1)['$_EAz'](function (e) {
var t = r[e];
o['push'](n['$_BIHL'](t) ? n['$_BIDi'] : t);}),
o['join']('magic data');
}

然后我们再看一下 new ce(n[$_CEAHd(1374)]())[$_CEAHd(19)] 是个什么东西,ce方法 我们在前面已经扣下来了,现在我们在ce方法的原型方法上再加上一个 $_EAz方法

image-20230530175301839

1
2
3
4
5
6
7
8
"$_EAz": function (e) {
var t = this["$_BAEJ"];
if (t["map"])
return new ce(t["map"](e));
for (var n = [], r = 0, o = t["length"]; r < o; r += 1)
n[r] = e(t[r], r, this);
return new ce(n);
}

这下n也加密完成了,我们看一下加密结果, 没什么问题

image-20230530180525444

接下来就看一下r参数 r = i['$_BDHN']['$_BIBF']() ,看一下这个是什么方法,简化一下

image-20230530180849252

1
2
3
4
5
6
7
8
function  $_BIBF_r(e) {
var e = this['$_BGq'] || [];
return this['$_BGq'] = [],
this['$_BGIZ'] = 0,
this['$_BGJn'] = [],
(y || b || w) && (e = e['slice'](0, 10)),
e['join']('|');
}

我们先看一下 this['$_BGq'] 是什么,这里貌似是一个固定的值, 这里也可以直接写死,

image-20230530183918615

image-20230530184002052

再看一下 o = i[$_CFJIy(336)] 参数o,发现其实是上面两个请求的返回值,所以这边也是可以直接写死的

image-20230530184253163

image-20230530184337456

最后一个参数 s s = $_GY() - ot 参数s应该是两个时间戳相减,这个差值可以随机,也可以写死,这里我们直接写死

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
, o = {
"$_CAAU": 1685443121545,
"protocol": "https://",
"gt": "019924a82c70bb123aae90d483087f94",
"challenge": "cb2ede0a2b0a27d9559714c0ce37dc5b",
"offline": false,
"new_captcha": true,
"product": "popup",
"width": "300px",
"https": true,
"api_server": "api.geetest.com",
"type": "fullpage",
"static_servers": [
"static.geetest.com",
"dn-staticdown.qbox.me"
],
"beeline": "/static/js/beeline.1.0.1.js",
"voice": "/static/js/voice.1.2.3.js",
"click": "/static/js/click.3.0.9.js",
"fullpage": "/static/js/fullpage.9.1.4.js",
"slide": "/static/js/slide.7.9.0.js",
"geetest": "/static/js/geetest.6.0.9.js",
"aspect_radio": {
"slide": 103,
"click": 128,
"voice": 128,
"beeline": 50
},
"cc": 16,
"supportWorker": true,
"$_FF_": {
"pt": 0
},
"aeskey": "77266c508c6b7371",
"theme": "wind",
"theme_version": "1.5.8",
"logo": true,
"feedback": "https://www.geetest.com/contact#report",
"c": [
12,
58,
98,
36,
43,
95,
62,
15,
12
],
"s": "302a3836",
"i18n_labels": {
"copyright": "由极验提供技术支持",
"error": "网络不给力",
"error_content": "请点击此处重试",
"error_title": "网络超时",
"fullpage": "智能检测中",
"goto_cancel": "取消",
"goto_confirm": "前往",
"goto_homepage": "是否前往验证服务Geetest官网",
"loading_content": "智能验证检测中",
"next": "正在加载验证",
"next_ready": "请完成验证",
"read_reversed": false,
"ready": "点击按钮进行验证",
"refresh_page": "页面出现错误啦!要继续操作,请刷新此页面",
"reset": "请点击重试",
"success": "验证成功",
"success_title": "通过验证"
}
} // 先写死
, s = 67777;

然后接着往下面看,中间这一部分就让他自己算,我们直接看下面的,直接看 V和p方法

image-20230530184734204

image-20230530184755299

这是p方法,精简一下, 我们把 this['$_HCh']this['$_GHz'] 补一下,其实这一步的操作和第一个w获取参数i的操作一模一样,正常扣代码就行

image-20230530195315039

1
2
3
4
function p(e) {
var t = this['$_HCh'](this['$_GHz'](e));
return t['res'] + t['end'];
}
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 $_HBw(e, t) {
return e >> t & 1;
}

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

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 $_GHz(e) {
for (var t = [], n = 0, r = e['length']; n < r; n += 1)
t['push'](e['charCodeAt'](n));
return t;
}

接着 我们来扣V方法,这个V方法其实是个md5加密,我们可以导包或者直接扣也i选哪个

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

}

接着 我们把后面的一些参数补起来

image-20230530201940323

i['$_CCGM'] 是第一个w里面算的指纹,我们直接把它给拿过来,这里可能会有问题,因为如果我们拿第一个w里面指纹生成的方式,就相当于是重新生成的,而这里应该是拿上一次的指纹

image-20230530202325058

接着来看 i['$_CEHV'](),看一下ren的值,搜一下,这里有可能是GPU的信息,这里应该也是指纹信息,fp和lp这里有两个时间戳,有可能又是检测鼠标移动,我们可以把一个值设定成当前时间,另一个值把这两个值得差值给加上,而下面的tm的值,我们也算和当前时间的时间差,完成是这样的

image-20230530205438042

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
var now = new Date().getTime();
$_CEHV = {
"v": "9.1.4",
"te": false,
"$_BBy": true,
"ven": "Google Inc. (Google)",
"ren": "ANGLE (Google, Vulkan 1.3.0 (SwiftShader Device (Subzero) (0x0000C0DE)), SwiftShader driver)",
"fp": [
"move",
413,
59,
now,
"pointermove"
],
"lp": [
"up",
224,
303,
now + 992,
"pointerup"
],
"em": {
"ph": 0,
"cp": 0,
"ek": "11",
"wd": 1,
"nt": 0,
"si": 0,
"sc": 0
},
"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
},
"dnf": "dnf",
"by": 0
}

o参数里面没用的参数可以删掉,后面还有个 ge['stringify'] ,这个和第一个w里面一样,这里就是个json,stringify,然后运行一下,看上去好像没什么问题,我们回到最开始的加密入口,看下r值能不能获取到,这样r也能获取到,接下来就是加密算法的部分了

image-20230530211649323

image-20230530212158586

最后一段 return p['$_HEf'](_['encrypt'](r, i['$_CCIl']())) 我们将断点打到下面来,然后去看一下 i[$_CGBDY(1361)] 是什么, 点进去之后能很明显的看到 又是判断aeskey是否存在,不存在就生成,如果存在就直接返回,那么我们可以直接用上一次加密的aeskey,而这里应该要的就是上一次生成的aeskey,而不是重新生成,那么我们就需要通过传参获取上一次的aeskey

image-20230530212500902

image-20230530212601815

我们直接把第一次w加密的aes算法拿过来

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;
}

接下来 p['$_HEf'] 的操作也是和第一w生成时 $_HEf(o) 的操作一样,所以我们也可以直接拿过来,最后我们将浏览器中的aeskey拿过来,测试一下, 没什么问题,我们再将代码整理一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function second_w(aeskey) {  // 通过传参获取上一次加密的aeskey
// t['push'](e['toString']());
o_tostring = "function o(e,t){var $_CGBHd=VIPVz.$_CG,$_CGBGV=['$_CGCAl'].concat($_CGBHd),$_CGBIC=$_CGBGV[1];$_CGBGV.shift();var $_CGBJu=$_CGBGV[0];function n(e){var $_DEFIu=VIPVz.$_Ds()[4][14];for(;$_DEFIu!==VIPVz.$_Ds()[8][12];){switch($_DEFIu){case VIPVz.$_Ds()[4][14]:var t=5381,n=e[$_CGBHd(54)],r=0;while(n--)t=(t<<5)+t+e[$_CGBIC(94)](r++);$_DEFIu=VIPVz.$_Ds()[0][13];break;case VIPVz.$_Ds()[8][13]:return t&=~(1<<31);break;}}}100<new Date()[$_CGBIC(256)]()-t[$_CGBIC(256)]()&&(e=$_CGBIC(1474)),r=$_CGBHd(764)+i[$_CGBIC(1448)]+$_CGBIC(1427)+n(o[$_CGBIC(73)]()+n(n[$_CGBHd(73)]())+n(e[$_CGBHd(73)]()))+$_CGBHd(1471);}"
n_tostring = 'function n(e){var $_DEFIu=VIPVz.$_Ds()[4][14];for(;$_DEFIu!==VIPVz.$_Ds()[8][12];){switch($_DEFIu){case VIPVz.$_Ds()[4][14]:var t=5381,n=e[$_CGBHd(54)],r=0;while(n--)t=(t<<5)+t+e[$_CGBIC(94)](r++);$_DEFIu=VIPVz.$_Ds()[0][13];break;case VIPVz.$_Ds()[8][13]:return t&=~(1<<31);break;}}}'
e_tostring = 'bbOy'
var r = '';
!function o(e, t) {
function n(e) {
var t = 5381, n = e['length'], r = 0;
while (n--)
t = (t << 5) + t + e['charCodeAt'](r++);
return t &= ~(1 << 31);
}

100 < new Date()['getTime']() - t['getTime']() && (e = 'qwe'),
r = '{' + get_i() + '"captcha_token":"' + n(o_tostring + n(n_tostring) + n(e_tostring)) + '"}';
}('bbOy', new Date());

return $_HEf(encrypt1(r, aeskey));
}

console.log(second_w("691f5ba419b5614f"))

image-20230530214145454

我们修改一下第一个w生成的结果,将里面的指纹信息和aeskey一起返回出来

image-20230530215924185

我们前面的o参数里面,还有一个s的值,该值是从上一个请求结果里面获取的,我们还需要把它给拿过来

image-20230530220559036

image-20230530220643792

这段指纹呢,我们也直接用上一次加密完成的

image-20230530222043837

最终的结果也是没有问题,

image-20230530223855182

python代码:

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
import json
import re
import time
import execjs
import requests

f = open("1..js", 'r', encoding="utf-8")
first_js = execjs.compile(f.read())
f2 = open("2...js", 'r', encoding="utf-8")
second_js = execjs.compile(f2.read())


def jsonp_data(text):
# jsonp_re = re.compile(r"\((?P<code>.*)\)", re.S)
# jsonp_str = jsonp_re.search(text, re.S).group("code")
# return json.loads(jsonp_str)
jsonp_re = re.findall(r"\((.*)\)", text)
return json.loads(jsonp_re[0])


def get_time():
return int(time.time() * 1000)


session = requests.session()
headers = {
'authority': 'www.geetest.com',
'accept': 'application/json, text/javascript, */*; q=0.01',
'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
'cache-control': 'no-cache',
# 'cookie': '461cca3146ff093d059dee9439aa6b26=6eee86d2-3925-4a45-9ad3-cf28de229505',
'pragma': 'no-cache',
'referer': 'https://www.geetest.com/demo/slide-popup.html',
'sec-ch-ua': '"Microsoft Edge";v="113", "Chromium";v="113", "Not-A.Brand";v="24"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'same-origin',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36 Edg/113.0.1774.50',
'x-requested-with': 'XMLHttpRequest',
}

params = {
't': get_time(),
}
# 第一个请求,用于注册验证码,获取贯穿始终的gt
register_res = session.get('https://www.geetest.com/demo/gt/register-slide', params=params, headers=headers).json()
challenge = register_res['challenge']
gt = register_res['gt']

# 第二个请求,用于返回加载验证码的js
params = {
'gt': gt,
'callback': 'geetest_{}'.format(get_time()),
}
load_res = session.get('https://apiv6.geetest.com/gettype.php', params=params, headers=headers)

# 第三个请求,需要解密第一个w,该请求同样也是为了加载验证码

params_dict = first_js.call('cul_first_w', gt, challenge)

load_res2 = session.get(
'https://apiv6.geetest.com/get.php', headers=headers, params=params_dict['msg']
)
# 获取到第一个w里面返回的各种值,然后放到第二个w里面进行参与加密
aeskey = params_dict['aeskey']
finger_print = params_dict['finger_print']
sec = jsonp_data(load_res2.text)['data']['s']

W = second_js.call('second_w', aeskey, gt, challenge, sec, finger_print)
# print(W)
params = {
'gt': gt,
'challenge': challenge,
'lang': 'zh-cn',
'pt': '0',
'client_type': 'web',
'w': W,
'callback': 'geetest_{}'.format(get_time()),
}
# 第四次请求,需要解密第二个w,同样用于加载验证码
load_res3 = session.get(
'https://api.geetest.com/ajax.php', headers=headers, params=params
)
print(load_res3.text)

js代码太长了,这里只给出入口的js代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function second_w(aeskey,gt,challenge,sec,finger_print) {  // 通过传参获取上一次加密的aeskey
// t['push'](e['toString']());
o_tostring = "function o(e,t){var $_CGBHd=VIPVz.$_CG,$_CGBGV=['$_CGCAl'].concat($_CGBHd),$_CGBIC=$_CGBGV[1];$_CGBGV.shift();var $_CGBJu=$_CGBGV[0];function n(e){var $_DEFIu=VIPVz.$_Ds()[4][14];for(;$_DEFIu!==VIPVz.$_Ds()[8][12];){switch($_DEFIu){case VIPVz.$_Ds()[4][14]:var t=5381,n=e[$_CGBHd(54)],r=0;while(n--)t=(t<<5)+t+e[$_CGBIC(94)](r++);$_DEFIu=VIPVz.$_Ds()[0][13];break;case VIPVz.$_Ds()[8][13]:return t&=~(1<<31);break;}}}100<new Date()[$_CGBIC(256)]()-t[$_CGBIC(256)]()&&(e=$_CGBIC(1474)),r=$_CGBHd(764)+i[$_CGBIC(1448)]+$_CGBIC(1427)+n(o[$_CGBIC(73)]()+n(n[$_CGBHd(73)]())+n(e[$_CGBHd(73)]()))+$_CGBHd(1471);}"
n_tostring = 'function n(e){var $_DEFIu=VIPVz.$_Ds()[4][14];for(;$_DEFIu!==VIPVz.$_Ds()[8][12];){switch($_DEFIu){case VIPVz.$_Ds()[4][14]:var t=5381,n=e[$_CGBHd(54)],r=0;while(n--)t=(t<<5)+t+e[$_CGBIC(94)](r++);$_DEFIu=VIPVz.$_Ds()[0][13];break;case VIPVz.$_Ds()[8][13]:return t&=~(1<<31);break;}}}'
e_tostring = 'qwe'
var r = '';
!function o(e, t) {
function n(e) {
var t = 5381, n = e['length'], r = 0;
while (n--)
t = (t << 5) + t + e['charCodeAt'](r++);
return t &= ~(1 << 31);
}

// 100 < new Date()['getTime']() - t['getTime']() && (e = 'qwe'),
r = '{' + get_i(gt,challenge,sec,finger_print) + '"captcha_token":"' + n(o_tostring + n(n_tostring) + n(e_tostring)) + '"}';
}('bbOy', new Date());

return $_HEf(encrypt1(r, aeskey));
}