本帖最后由 besterChen 于 2020-4-22 18:23 编辑
本文要分享的内容是去年为了抢鞋而分析 极验(GeeTest)反爬虫防护的笔记,由于篇幅较长(为了多混点CB)我会按照我的分析顺序,分成如下四个主题与大家分享:
本文是第三篇《接口交互的解密方法补遗》,书接上文,上一篇中,我们遗留了两个问题:
- AES的密钥如何生成。
- e的值是什么内容,如何计算得到。
不过在后来的调试过程中发现,geetest并不是简单的使用了base64加密,所以需要进一步分析一下,下面进入正文~
AES加密结果的BASE64编码
在调试的过程中发现AES加密的结果并不是简单的BASE64编码,如下图:
看代码像是BASE64编码后提交给服务器的,但是我将o的值
导出后,自己进行base64编码的结果与这里不一样,因此跟进$_BABW
方法继续分析,如下图:
由此可知,最终的加密结果是由["res"]
+["end"]
拼凑起来的,具体的跟进 $_JJM
方法继续分析, 将混淆的代码还原之后,JS代码如下:
var Base64 = {
"$_JGH": function(e){
var t = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789()";
return e < 0 || e >= t["length"] ? "." : t.charAt(e);
},
"$_JIY": function(e, t) {
return e >> t & 1;
},
"$_JJM": function(e, o) {
var $this = this;
function t(e, t) {
for (var n = 0, r = 24 - 1; 0 <= r; r -= 1) {
1 === $this["$_JIY"](t, r) && (n = (n << 1) + $this["$_JIY"](e, r));
}
return n;
}
for (var n = "", r = "", a = e["length"], s = 0; s < a; s += 3) {
var c;
if (s + 2 < a) {
c = (e[s] << 16) + (e[s + 1] << 8) + e[s + 2];
n += this["$_JGH"](t(c, 7274496)) + this["$_JGH"](t(c, 9483264)) + this["$_JGH"](t(c, 19220)) + this["$_JGH"](t(c, 235));
} else {
var u = a % 3;
2 == u ? (c = (e[s] << 16) + (e[s + 1] << 8), n += this["$_JGH"](t(c, 7274496)) + this["$_JGH"](t(c, 9483264)) + this["$_JGH"](t(c, 19220)), r = ".") : 1 == u && (c = e[s] << 16, n += this["$_JGH"](t(c, 7274496)) + this["$_JGH"](t(c, 9483264)), r = "." + ".");
}
}
return {'res': n, 'end': r}
},
"encode": function (e) {
var t = this["$_JJM"](e);
return t["res"] + t["end"];
}
}
// 使用方法,直接将要加密的字节数组传入方法即可
var result = Base64["encode"]([92,25,128,...])
console.log(result);
导出官网要加密的数组,加密结果如下图:
AES密钥的生成方法
继续向上追溯AES密钥的生成方法,来到如下位置:
跟进wr方法,得到如下代码:
由此,整理出来AES密码的生成方法为:
/**
* AES密钥的获取方法
*/
function create_key() {
return (65536 * (1 + Math.random()) | 0).toString(16).substring(1);
};
function get_aes_key() {
return wr() + wr() + wr() + wr();
}
e值的来历
跟进分析之后,发现e的值是从浏览器的window对象中依次获取,如下内容的信息:
0: "textLength"
1: "HTMLLength"
2: "documentMode"
3: "A"
4: "ARTICLE"
5: "ASIDE"
6: "AUDIO"
7: "BASE"
8: "BUTTON"
9: "CANVAS"
10: "CODE"
11: "IFRAME"
12: "IMG"
13: "INPUT"
14: "LABEL"
15: "LINK"
16: "NAV"
17: "OBJECT"
18: "OL"
19: "PICTURE"
20: "PRE"
21: "SECTION"
22: "SELECT"
23: "SOURCE"
24: "SPAN"
25: "STYLE"
26: "TABLE"
27: "TEXTAREA"
28: "VIDEO"
29: "screenLeft"
30: "screenTop"
31: "screenAvailLeft"
32: "screenAvailTop"
33: "innerWidth"
34: "innerHeight"
35: "outerWidth"
36: "outerHeight"
37: "browserLanguage"
38: "browserLanguages"
39: "systemLanguage"
40: "devicePixelRatio"
41: "colorDepth"
42: "userAgent"
43: "cookieEnabled"
44: "netEnabled"
45: "screenWidth"
46: "screenHeight"
47: "screenAvailWidth"
48: "screenAvailHeight"
49: "localStorageEnabled"
50: "sessionStorageEnabled"
51: "indexedDBEnabled"
52: "CPUClass"
53: "platform"
54: "doNotTrack"
55: "timezone"
56: "canvas2DFP"
57: "canvas3DFP"
58: "plugins"
59: "maxTouchPoints"
60: "flashEnabled"
61: "javaEnabled"
62: "hardwareConcurrency"
63: "jsFonts"
64: "timestamp"
65: "performanceTiming"
66: "internalip"
67: "mediaDevices"
68: "DIV"
69: "P"
70: "UL"
71: "LI"
72: "SCRIPT"
73: "deviceorientation"
74: "touchEvent"
这些key对应的内容为:
A: 337
BUTTON: 1
CPUClass: undefined
DIV: 648
HTMLLength: 185450
IFRAME: 7
IMG: 60
INPUT: 48
LABEL: 9
LI: 222
LINK: 17
P: 75
SCRIPT: 69
SECTION: 1
SPAN: 239
STYLE: 2
UL: 49
browserLanguage: "zh-CN"
browserLanguages: "zh-CN,zh,zh-TW,zh-HK,en-US,en"
canvas2DFP: "805a6cdeadd4f48ade985597f74928cb"
canvas3DFP: "cc03697d39800df1ef0d2229132a62e8"
colorDepth: 24
cookieEnabled: 1
devicePixelRatio: 1
deviceorientation: false
doNotTrack: 1
documentMode: "CSS1Compat"
flashEnabled: -1
hardwareConcurrency: 4
indexedDBEnabled: 1
innerHeight: 443
innerWidth: 1634
internalip: undefined
javaEnabled: 0
jsFonts: "AndaleMono,Arial,ArialBlack,ArialNarrow,ArialRoundedMTBold,ArialUnicodeMS,ComicSansMS,Courier,CourierNew,Geneva,Georgia,Helvetica,HelveticaNeue,Impact,LUCIDAGRANDE,MicrosoftSansSerif,Monaco,Palatino,Tahoma,Times,TimesNewRoman,TrebuchetMS,Verdana"
localStorageEnabled: 1
maxTouchPoints: 0
mediaDevices: -1
netEnabled: 1
outerHeight: 1027
outerWidth: 1634
performanceTiming: "-1,-1,0,0,0,0,0,123,208,1,99154,10,8,403,418,1005,-1,-1,-1,-1"
platform: "MacIntel"
plugins: ""
screenAvailHeight: 1027
screenAvailLeft: 44
screenAvailTop: 23
screenAvailWidth: 1636
screenHeight: 1050
screenLeft: 46
screenTop: 23
screenWidth: 1680
sessionStorageEnabled: 1
systemLanguage: undefined
textLength: 46216
timestamp: 1568186269219
timezone: -8
touchEvent: false
userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:69.0) Gecko/20100101 Firefox/69.0"
将这些值取为数组并用 !!
进行分隔生成字符串,就得到了e的值。因页面内容相对固定,也取固定字符来用,省的每次单独生成了。
至此,接口交互中参数加密解密的部分就都分析完毕了。下一篇也是最后一篇,我们一起来看如何绕过号称基于大数据的智能行为验证组件Slide的内容: 《极验反爬虫防护分析之slide验证方式下图片的处理及滑动轨迹的生成思路》