爬虫逆向-空中网登路密码逆向
背景:
本文只是为了学习逆向技术,与爬取数据无关, 所以文中没有数据爬取,只是叙述了JS逆向思路及步骤
请勿对目标网站进行大规模爬取
网址: https://passport.kongzhong.com
开始 - 步骤
1、 抓包得到登录操作为ajax请求,1.1、密码是密文2、获取密码加密js代码得知加密需要:dc['dc']3、根据js代码得知dc['dc']为ajax请求返回,4、发送ajax请求得dc, 注意 _ 为时间戳5、根据密码和dc,js逆向加密,得密码加密后得字符串6、发送登录请求,成功PS: vcode 为验证码, 需要接入验证码识别
import requests import time import json import re import execjs
headers = { 'Accept': '*/*', 'Accept-Language': 'zh-CN,zh;q=0.9,en-GB;q=0.8,en-US;q=0.7,en;q=0.6', 'Connection': 'keep-alive', # 'Cookie': 'KZLBS=d08e6bcdc9f0cbe5; Hm_lvt_1287c2225a527abe3386233dd9316f99=1671177724; SSO-KGZLT=f272a541-e40e-44ce-ad05-2645a0b2101a; SSO-KGZIT=c42c3494-2394-40ca-9583-a8c1058f67e7; Hm_lpvt_1287c2225a527abe3386233dd9316f99=1671418222; SSO-KGZQRT=D71FF8C57C8312613E6100C22D769B69', 'Referer': 'https://passport.kongzhong.com/', 'Sec-Fetch-Dest': 'script', 'Sec-Fetch-Mode': 'no-cors', 'Sec-Fetch-Site': 'same-site', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36', 'sec-ch-ua': '"Not?A_Brand";v="8", "Chromium";v="108", "Google Chrome";v="108"', 'sec-ch-ua-mobile': '?0', 'sec-ch-ua-platform': '"Windows"', } params = { 'j': 'j', 'jsonp': 'j', 'service': 'https://passport.kongzhong.com/', '_': round(time.time() * 1000) } response = requests.get( url='https://sso.kongzhong.com/ajaxLogin', headers=headers, params=params ) # 获取dc dc_list = re.findall('\((.*)\)', response.text) dc = json.loads(dc_list[0]) print(dc['dc']) # 获取password 加密串 node = execjs.get() ctx = node.compile(open("./1-空中网逆向.js", "r", encoding="utf-8").read()) # 执行js函数 resp = ctx.eval( 'KZLoginHandler.encrypt("{}", "{}")'.format('123456', dc['dc'])) print(resp) url = 'https://sso.kongzhong.com/ajaxLogin' params2 = { 'j': 'j', 'type': '1', 'service': 'https://passport.kongzhong.com/', '_': round(time.time() * 1000), 'vcode': '', 'toSave': '0', 'username': '13102520219', 'password': resp } response2 = requests.get( url='https://sso.kongzhong.com/ajaxLogin', headers=headers, params=params2 ) print(response2.text)
JS代码
1 /** 2 * SSO 登录控制器 3 * @author: qiaozheng@kongzhong.com 4 * @version: 3.0 5 */ 6 var KZLoginHandler = 7 { 8 'id': 'kongzhong-login-agent', 9 'loginServer': 'http://sso.kongzhong.com', 10 //'loginURL': 'sso/userLogin', 11 'service': '', 12 'targetService': '', 13 //'dc': '', 14 'j_data': null, 15 'f_call_back': null, 16 'timestamp': 0, 17 'completed': false, 18 'renew': false, 19 'init': function () { 20 this.j_data = null; 21 this.f_call_back = null; 22 this.timestamp = 0; 23 this.completed = true; 24 //this.dc = ''; 25 }, 26 'check': function (call_back) { 27 this.init(); 28 this.f_call_back = call_back; 29 var param = "jsonp=j"; 30 if (this.service != null && jQuery.trim(this.service) != "") { 31 param += "&service=" + decodeURIComponent(this.service); 32 } 33 if (this.targetService != null && jQuery.trim(this.targetService) != "") { 34 param += "&targetService=" + decodeURIComponent(this.targetService); 35 } 36 if (this.renew) { 37 param += "&renew=1"; 38 } 39 40 this.exec_login(param); 41 }, 42 'exec_login': function (param) { 43 if (this.completed == false) { 44 return false; 45 } 46 if (this.j_data != null && this.j_data["state"] == "1") { 47 var data = {}; 48 data["user"] = this.j_data["user"]; 49 data["service"] = this.j_data["service"]; 50 data["logged"] = true; 51 data["dc"] = this.j_data["dc"]; 52 this.f_call_back(data); 53 return false; 54 } 55 var url = this.loginServer + "/ajaxLogin"; 56 57 jQuery.ajax({ 58 async: false, 59 url: url, 60 type: 'post', 61 dataType: 'jsonp', 62 jsonp: 'j', //jsoncallback 63 data: param, 64 jsonpCallback: "j", 65 timeout: 5000, 66 cache: false, 67 success: function (json) { 68 }, 69 error: function (xhr) { 70 console.log("error:", xhr.status); 71 if (xhr.status === 403 || xhr.status === 445) { 72 window.location.href = "https://www.zhanhuo.com/waf/index.html?src=passport"; 73 } 74 } 75 }); 76 77 //当WAF拦截出现445错误时,ajax的error方法不能捕获jsonp的异常,必须通过ajax创建的第一个script的onerror来捕获异常 78 var head = document.head || $('head')[0] || document.documentElement; 79 var script = $(head).find('script')[0]; 80 script.onerror = function (evt) { 81 window.location.href = "https://www.zhanhuo.com/waf/index.html?src=passport"; 82 } 83 }, 84 'jsonpCallbackKongZ': function (vData) { 85 this.j_data = vData; 86 //this.dc = vData["dc"]; 87 this.timestamp = Date.parse(new Date()); 88 if (this.f_call_back != null) { 89 var data = {}; 90 if (vData["state"] == "0") { 91 data["service"] = vData["service"]; 92 data["logged"] = false; 93 data["errors"] = vData["kzmsg"]; 94 if (vData["requirevcode"] != null 95 && vData["requirevcode"] == "1") { 96 data["requirevcode"] = true; 97 } else { 98 data["requirevcode"] = false; 99 } 100 //data["message"] = vData["kzmsg"]; 101 } else if (vData["state"] == "1") { 102 data["user"] = vData["user"]; 103 data["service"] = vData["service"]; 104 data["logged"] = true; 105 } 106 data["dc"] = this.j_data["dc"]; 107 this.f_call_back(data); 108 } 109 this.completed = true; 110 }, 111 'login': function (user, pwd, to_save, vcode, call_back) { 112 var tempTime = Date.parse(new Date()) - this.timestamp; 113 if ((tempTime / 1000) >= 180) { 114 this.j_data = null; 115 } 116 if (this.j_data == null 117 || this.j_data == "") { 118 this.check(function (data) { 119 this.f_call_back = call_back; 120 var param = ""; 121 param += "&type=1"; 122 //param += "<="+this.j_data["lt"]; 123 if (this.service != null && jQuery.trim(this.service) != "") { 124 //param += "&service="+this.service; 125 param += "&service=" + decodeURIComponent(this.service); 126 } 127 param += "&username=" + user; 128 param += "&password=" + this.encrypt(pwd, data["dc"]); 129 //param += "&password="+pwd; 130 param += "&vcode=" + vcode; 131 if (to_save) { 132 param += "&toSave=1"; 133 } else { 134 param += "&toSave=0"; 135 } 136 if (this.targetService != null && jQuery.trim(this.targetService) != "") { 137 param += "&targetService=" + decodeURIComponent(this.targetService); 138 } 139 if (this.renew) { 140 param += "&renew=1"; 141 } 142 143 this.exec_login(param); 144 }); 145 } else { 146 this.f_call_back = call_back; 147 var param = ""; 148 param += "&type=1"; 149 //param += "<="+this.j_data["lt"]; 150 if (this.service != null && jQuery.trim(this.service) != "") { 151 //param += "&service="+this.service; 152 param += "&service=" + decodeURIComponent(this.service); 153 } 154 param += "&username=" + user; 155 param += "&password=" + this.encrypt(pwd, this.j_data["dc"]); 156 //param += "&password="+pwd; 157 param += "&vcode=" + vcode; 158 if (to_save) { 159 param += "&toSave=1"; 160 } else { 161 param += "&toSave=0"; 162 } 163 if (this.targetService != null && jQuery.trim(this.targetService) != "") { 164 param += "&targetService=" + decodeURIComponent(this.targetService); 165 } 166 if (this.renew) { 167 param += "&renew=1"; 168 } 169 170 this.exec_login(param); 171 } 172 173 }, 174 'login_sms': function (user, smscode, to_save, vcode, call_back) { 175 var tempTime = Date.parse(new Date()) - this.timestamp; 176 if ((tempTime / 1000) >= 180) { 177 this.j_data = null; 178 } 179 if (this.j_data == null 180 || this.j_data == "") { 181 this.check(function () { 182 this.f_call_back = call_back; 183 var param = ""; 184 param += "&type=2"; 185 //param += "<="+this.j_data["lt"]; 186 param += "&service=" + this.service; 187 param += "&username=" + user; 188 param += "&vcode=" + vcode; 189 param += "&smscode=" + smscode; 190 //param += "&tcode="+tm; 191 if (to_save) { 192 param += "&toSave=1"; 193 } else { 194 param += "&toSave=0"; 195 } 196 if (this.targetService != null) { 197 param += "&targetService=" + decodeURIComponent(this.targetService); 198 } 199 if (this.renew) { 200 param += "&renew=1"; 201 } 202 203 this.exec_login(param); 204 }); 205 } else { 206 this.f_call_back = call_back; 207 var param = ""; 208 param += "&type=2"; 209 //param += "<="+this.j_data["lt"]; 210 param += "&service=" + this.service; 211 param += "&username=" + user; 212 param += "&vcode=" + vcode; 213 param += "&smscode=" + smscode; 214 //param += "&tcode="+tm; 215 if (to_save) { 216 param += "&toSave=1"; 217 } else { 218 param += "&toSave=0"; 219 } 220 if (this.targetService != null) { 221 param += "&targetService=" + decodeURIComponent(this.targetService); 222 } 223 if (this.renew) { 224 param += "&renew=1"; 225 } 226 227 this.exec_login(param); 228 } 229 230 }, 231 'login_reg': function (user, pwd, to_save, call_back) { 232 var tempTime = Date.parse(new Date()) - this.timestamp; 233 if ((tempTime / 1000) >= 180) { 234 this.j_data = null; 235 } 236 if (this.j_data == null 237 || this.j_data == "") { 238 this.check(function () { 239 this.f_call_back = call_back; 240 var param = ""; 241 param += "&type=101"; 242 //param += "<="+this.j_data["lt"]; 243 param += "&service=" + this.service; 244 param += "&username=" + user; 245 param += "&password=" + pwd; 246 if (to_save) { 247 param += "&toSave=1"; 248 } else { 249 param += "&toSave=0"; 250 } 251 if (this.renew) { 252 param += "&renew=1"; 253 } 254 255 this.exec_login(param); 256 }); 257 } else { 258 this.f_call_back = call_back; 259 var param = ""; 260 param += "&type=101"; 261 //param += "<="+this.j_data["lt"]; 262 param += "&service=" + this.service; 263 param += "&username=" + user; 264 param += "&password=" + pwd; 265 if (to_save) { 266 param += "&toSave=1"; 267 } else { 268 param += "&toSave=0"; 269 } 270 if (this.renew) { 271 param += "&renew=1"; 272 } 273 274 this.exec_login(param); 275 } 276 277 }, 278 'encrypt': function (str, pwd) { 279 if (pwd == null || pwd.length <= 0) { 280 return null; 281 } 282 var prand = ""; 283 for (var i = 0; i < pwd.length; i++) { 284 prand += pwd.charCodeAt(i).toString(); 285 } 286 var sPos = Math.floor(prand.length / 5); 287 var mult = parseInt(prand.charAt(sPos) + prand.charAt(sPos * 2) + prand.charAt(sPos * 3) + prand.charAt(sPos * 4) + prand.charAt(sPos * 5)); 288 var incr = Math.ceil(pwd.length / 2); 289 var modu = Math.pow(2, 31) - 1; 290 if (mult < 2) { 291 return null; 292 } 293 var salt = Math.round(Math.random() * 1000000000) % 100000000; 294 prand += salt; 295 while (prand.length > 10) { 296 var a = prand.substring(0, 1); 297 var b = prand.substring(10, prand.length); 298 if (b.length > 10) { 299 prand = b; 300 } else { 301 prand = (parseInt(a) + parseInt(b)).toString(); 302 } 303 } 304 prand = (mult * prand + incr) % modu; 305 var enc_chr = ""; 306 var enc_str = ""; 307 for (var i = 0; i < str.length; i++) { 308 enc_chr = parseInt(str.charCodeAt(i) ^ Math.floor((prand / modu) * 255)); 309 if (enc_chr < 16) { 310 enc_str += "0" + enc_chr.toString(16); 311 } else enc_str += enc_chr.toString(16); 312 prand = (mult * prand + incr) % modu; 313 } 314 salt = salt.toString(16); 315 while (salt.length < 8) salt = "0" + salt; 316 enc_str += salt; 317 return enc_str; 318 } 319 320 }; 321 322 dc = "85B887AF9E8325D4243383AFC66997C9" 323 console.log(KZLoginHandler.encrypt('123456', dc))
PS: 文中只是分析了密码逆向部分, 关于验证码识别API接入 未处理