空气质量监控平台分析反爬虫分析

第一个反爬:禁止右键和快捷键 F12

解决措施

image-20220809214407034

第二个反爬:debuggr

image-20220809213856610

第三个反爬虫 反调试定时器

image-20220809214717694

清除定时任务 注意下面命令的执行时间要在catch 代码执行之前执行 不然加了定时器之后 调试会一直在打转

1
end=setInterval(function(){},10000);for(i=1;i<=end;i++){clearInterval(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
  var debugflag = false;
document.onkeydown = function() {
if ((e.ctrlKey) && (e.keyCode == 83)) {
alert("检测到非法调试,CTRL + S被管理员禁用");
return false;
}
}
document.onkeydown = function() {
var e = window.event || arguments[0];
if (e.keyCode == 123) {
alert("检测到非法调试,F12被管理员禁用");
return false;
}
}
document.oncontextmenu = function() {
alert('检测到非法调试,右键被管理员禁用');
return false;
}

// 这里是一个自执行函数
// window.innerWidth - 返回Window的内容区域宽度,也就是viewport的宽度,不包括scrollbars,toolbars.
// window.outerWidth - 返回Window的外部宽度,包括scrollbars, toolbars等等。
// 大致就是页面页面宽度和宽度大于230时 并且函数的执行时间过长 就认为是反调试
!function () {
const handler = setInterval(() => {
if (window.outerWidth - window.innerWidth > 230 ||
window.outerHeight - window.innerHeight > 230) {
// document.write((window.outerWidth - window.innerWidth) + ',' + (window.outerHeight - window.innerHeight));
document.write('检测到非法调试, 请关闭调试终端后刷新本页面重试!<br/>');
document.write("Welcome for People, Not Welcome for Machine!<br/>");
debugflag = true;
}
const before = new Date();
(function() {}
["constructor"]("debugger")())
const after = new Date();
const cost = after.getTime() - before.getTime();
if (cost > 50) {
debugflag = true;
document.write('检测到非法调试, 请关闭调试终端后刷新本页面重试!<br/>');
document.write("Welcome for People, Not Welcome for Machine!<br/>");
}

}, 2000)
}();

加了一个 定时器 去执行函数的检测脚本,检测到了就不断输出指定字段。

image-20220809220737364

把页面恢复执行之后 页面上的不断打印检测到非法调试, 请关闭调试终端后刷新本页面重试! 和代码分析一致 可以得出结论真正的 检测就在这里

首页中有这个关键字 但是不是我们想要的

image-20220809221321017

无法替换文件来做到

解决方式也很简单 选择把窗口分离出去 再刷新页面 即可

image-20220809221452456

加密点分析

在页面元素上右键 该网页禁止了 空白页面右键 没有禁止在元素上右键

image-20220809221910297

定位到表格的上一个父元素 div.dataggrid-body 右键选择 停止于 > 子树修改

image-20220809222036455

image-20220809222750068

随便点选一个城市让页面元素变动 触发表单修改

image-20220809222222483

image-20220809225611498

调用堆栈

image-20220809230041335

可以看到标准的ajax 请求

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 sCPnj6CA7NLes(mYHdW1XRZ, oQW42PTe6E, cixSit3kG, pPsBAMS) {

const kTgE = hex_md5(mYHdW1XRZ + JSON.stringify(oQW42PTe6E));

const dKHzs = g1doG4NK3KY4GK9i(kTgE, pPsBAMS);
if (!dKHzs) {
var pKmiMOA = pL5LAoYRS(mYHdW1XRZ, oQW42PTe6E);
$.ajax({
url: '../apinew/aqistudyapi.php',
data: { h3Y52gFKE: pKmiMOA },
type: "post",
success: function (dKHzs) {
dKHzs = deyKOtTW1SHYP(dKHzs);
o5yJnf = JSON.parse(dKHzs);
if (o5yJnf.success) {
if (pPsBAMS > 0) {
o5yJnf.result.time = new Date().getTime(); // 添加当前时间
localStorageUtil.save(kTgE, o5yJnf.result);
}
cixSit3kG(o5yJnf.result);
} else {
console.log(o5yJnf.errcode, o5yJnf.errmsg);
}
}
});
} else {
cixSit3kG(dKHzs);
}

deyKOtTW1SHYP 是解密函数

跟到代码里去看

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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
// vm3834.js
const asksvAESlv6Y = "a9tfUJutFN8sjZh1";
const asiy2lKidURh = "bgHf4XlTXywlQarV";
const ackoBEs4yTKN = "dO3WnWluQNHauldH";
const aciOunjFiMyA = "fOsbkMQ7qMMJ4xn5";
const dskyrOQPMrLC = "hRjA29JvTwJXnmZl";
const dsi5ivV1Kf7l = "xeQJT9nq4urlywGp";
const dcktdnp37Lh5 = "ocvRlZoHX7KnKYSx";
const dciROqfwYVC9 = "pl20yMKRdtrBM9D1";
const aes_local_key = 'emhlbnFpcGFsbWtleQ==';
const aes_local_iv = 'emhlbnFpcGFsbWl2';
var BASE64 = {
encrypt: function(text) {
var b = new Base64();
return b.encode(text)
},
decrypt: function(text) {
var b = new Base64();
return b.decode(text)
}
};
var DES = {
encrypt: function(text, key, iv) {
var secretkey = (CryptoJS.MD5(key).toString()).substr(0, 16);
var secretiv = (CryptoJS.MD5(iv).toString()).substr(24, 8);
secretkey = CryptoJS.enc.Utf8.parse(secretkey);
secretiv = CryptoJS.enc.Utf8.parse(secretiv);
var result = CryptoJS.DES.encrypt(text, secretkey, {
iv: secretiv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
return result.toString()
},
decrypt: function(text, key, iv) {
var secretkey = (CryptoJS.MD5(key).toString()).substr(0, 16);
var secretiv = (CryptoJS.MD5(iv).toString()).substr(24, 8);
secretkey = CryptoJS.enc.Utf8.parse(secretkey);
secretiv = CryptoJS.enc.Utf8.parse(secretiv);
var result = CryptoJS.DES.decrypt(text, secretkey, {
iv: secretiv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
return result.toString(CryptoJS.enc.Utf8)
}
};
var AES = {
encrypt: function(text, key, iv) {
var secretkey = (CryptoJS.MD5(key).toString()).substr(16, 16);
var secretiv = (CryptoJS.MD5(iv).toString()).substr(0, 16);
secretkey = CryptoJS.enc.Utf8.parse(secretkey);
secretiv = CryptoJS.enc.Utf8.parse(secretiv);
var result = CryptoJS.AES.encrypt(text, secretkey, {
iv: secretiv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
return result.toString()
},
decrypt: function(text, key, iv) {
var secretkey = (CryptoJS.MD5(key).toString()).substr(16, 16);
var secretiv = (CryptoJS.MD5(iv).toString()).substr(0, 16);
secretkey = CryptoJS.enc.Utf8.parse(secretkey);
secretiv = CryptoJS.enc.Utf8.parse(secretiv);
var result = CryptoJS.AES.decrypt(text, secretkey, {
iv: secretiv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
return result.toString(CryptoJS.enc.Utf8)
}
};
var localStorageUtil = {
save: function(name, value) {
var text = JSON.stringify(value);
text = BASE64.encrypt(text);
text = AES.encrypt(text, aes_local_key, aes_local_iv);
try {
localStorage.setItem(name, text)
} catch (oException) {
if (oException.name === 'QuotaExceededError') {
localStorage.clear();
localStorage.setItem(name, text)
}
}
},
check: function(name) {
return localStorage.getItem(name)
},
getValue: function(name) {
var text = localStorage.getItem(name);
var result = null;
if (text) {
text = AES.decrypt(text, aes_local_key, aes_local_iv);
text = BASE64.decrypt(text);
result = JSON.parse(text)
}
return result
},
remove: function(name) {
localStorage.removeItem(name)
}
};
function dBSZMW3M6n3(pmK6Hbv) {
pmK6Hbv = DES.decrypt(pmK6Hbv, dskyrOQPMrLC, dsi5ivV1Kf7l);
return pmK6Hbv
}
function dG8KpaYpqH(pmK6Hbv) {
pmK6Hbv = AES.decrypt(pmK6Hbv, asksvAESlv6Y, asiy2lKidURh);
return pmK6Hbv
}
function gXUbeHrdoLCeYe0p(key, period) {
if (typeof period === 'undefined') {
period = 0
}
var d = DES.encrypt(key);
d = BASE64.encrypt(key);
var data = localStorageUtil.getValue(key);
if (data) {
const time = data.time;
const current = new Date().getTime();
if (new Date().getHours() >= 0 && new Date().getHours() < 5 && period > 1) {
period = 1
}
if (current - (period * 60 * 60 * 1000) > time) {
data = null
}
if (new Date().getHours() >= 5 && new Date(time).getDate() !== new Date().getDate() && period === 24) {
data = null
}
}
return data
}
function ObjectSort(obj) {
var newObject = {};
Object.keys(obj).sort().map(function(key) {
newObject[key] = obj[key]
});
return newObject
}
function dvsQWNF2EO4XbKrd(data) {
data = AES.decrypt(data, asksvAESlv6Y, asiy2lKidURh);
data = DES.decrypt(data, dskyrOQPMrLC, dsi5ivV1Kf7l);
data = BASE64.decrypt(data);
return data
}
var pSeoLCOw4K = (function() {
function ObjectSort(obj) {
var newObject = {};
Object.keys(obj).sort().map(function(key) {
newObject[key] = obj[key]
});
return newObject
}
return function(method, obj) {
var appId = 'e75f0f57c0ec8c9a0f7b1312302ef91f';
var clienttype = 'WEB';
var timestamp = new Date().getTime();
var param = {
appId: appId,
method: method,
timestamp: timestamp,
clienttype: clienttype,
object: obj,
secret: hex_md5(appId + method + timestamp + clienttype + JSON.stringify(ObjectSort(obj)))
};
param = BASE64.encrypt(JSON.stringify(param));
param = DES.encrypt(param, dcktdnp37Lh5, dciROqfwYVC9);
return param
}
}
)();
function getData(mWZUgc23O, orY2UnQfh7, coGmO8LCQ, pz0mxss) {
// mWZUgc23O= "GETDATA" orY2UnQfh7={city: '上海'} coGmO8LCQ 回调 pz0mxss =0.5 变量名每次都会变化
const k6Bx = hex_md5(mWZUgc23O + JSON.stringify(orY2UnQfh7)); // 加密值 变量是地点
const dSQsW = gXUbeHrdoLCeYe0p(k6Bx, pz0mxss); // 判断缓存有没有该城市的数据
if (!dSQsW) { // 如果没有再发请求
var pmK6Hbv = pSeoLCOw4K(mWZUgc23O, orY2UnQfh7); // 对城市名和方法进行加密传值 发送ajax
$.ajax({
url: '../apinew/aqistudyapi.php',
data: {
hu8slNEvY: pmK6Hbv
},
type: "post",
success: function(dSQsW) {
dSQsW = dvsQWNF2EO4XbKrd(dSQsW);
onMCfI = JSON.parse(dSQsW);
if (onMCfI.success) {
if (pz0mxss > 0) {
onMCfI.result.time = new Date().getTime();
localStorageUtil.save(k6Bx, onMCfI.result)
}
coGmO8LCQ(onMCfI.result)
} else {
console.log(onMCfI.errcode, onMCfI.errmsg)
}
}
})
} else {
coGmO8LCQ(dSQsW)
}
}


空气质量监控平台分析反爬虫分析
https://kingjem.github.io/2022/08/10/空气质量监控平台分析反爬虫分析/
作者
Ruhai
发布于
2022年8月10日
许可协议