前言
新版的12306大概是前天上線的吧,因?yàn)槲仪疤煜霌屍钡臅r候發(fā)現(xiàn)之前寫的程序已經(jīng)無法正常工作了。
登陸的時候就會報錯——“非法請求”,很納悶,莫非是12306改了邏輯?
不過經(jīng)過這兩天的研究,已經(jīng)又搞出了新版的搶票軟件,嘿嘿,感覺值得驕傲一下~好了,不嘚瑟了
下面先從登陸入手,來剖析新版添加的dynamicJs(這個名字還挺貼切,就這么叫了)
一窺新版貓膩
下面就是新版的12306登陸所傳的參數(shù)
根據(jù)我上版搶票軟件的經(jīng)驗(yàn),其中后三個參數(shù)都是新添加的,并且其中用紅框框中的那個參數(shù)的key和value都是不固定的~另外兩個倒是簡單,也不影響登陸的成功與否。
那么這個動態(tài)變化的參數(shù)到底是怎么來的呢?這大概就是12306這版的“驕傲”了吧——dynamicJs。從12306官網(wǎng)分析找到這個dynamicJs:
該js文件中對于生成動態(tài)key-value的關(guān)鍵片段(以及后面的gc()函數(shù))如下:
function() {(function() {var form = document.forms[0];var oldSubmit;if (null != form && form != 'undefined'&& form.id == 'loginForm') {form.oldSubmit = form.submit;submitForm = function() {var keyVlues = gc().split(':');var inputObj = $('<input type="hidden" name="'+ keyVlues[0]+ '" value="'+ encode32(bin216(Base32.encrypt(keyVlues[1], keyVlues[0])))+ '" />');var myObj = $('<input type="hidden" name="myversion" value="' + window.helperVersion + '" />');inputObj.appendTo($(form));myObj.appendTo($(form));delete inputObj;delete myObj;}} else {submitForm = function() {var keyVlues = gc().split(':');return keyVlues[0]+ ",-,"+ encode32(bin216(Base32.encrypt(keyVlues[1], keyVlues[0])))+ ":::" + 'myversion' + ",-,"+ window.helperVersion;};}})();});
核心就是submitForm這個function,它會在登陸按鈕click的時候觸發(fā)(其實(shí)是提交表單的時候都會觸發(fā),只是這里先只關(guān)注登陸)
可以看到上面的if-else判斷中,對于form是有做區(qū)分的:分成2類,一類是id為loginForm的Form(也就是登陸表單),其余的則歸為另一類。
這兩類處理的邏輯不同,對于loginForm,它會在頁面上插入2個隱藏域來順著表單的提交而提交。而對于另一類,則生成2組key-value并用":::"join起來。
不過這里要小小的 吐槽一下12306一下,就是不管登陸成功與否,這兩個隱藏域都會被插入,我們可以做個試驗(yàn)來看看,下圖就是我N次登陸不成功時所發(fā)生的事情:
呵呵~這不是我們的重點(diǎn)。還是分析那個動態(tài)的key和value是怎么來的。由于我們只關(guān)注登陸,可以將之前的js代碼精簡一下:
if (null != form && form != 'undefined'&& form.id == 'loginForm') {form.oldSubmit = form.submit;submitForm = function() {var keyVlues = gc().split(':');var inputObj = $('<input type="hidden" name="'+ keyVlues[0]+ '" value="'+ encode32(bin216(Base32.encrypt(keyVlues[1], keyVlues[0])))+ '" />');var myObj = $('<input type="hidden" name="myversion" value="'+ window.helperVersion + '" />');inputObj.appendTo($(form));myObj.appendTo($(form));delete inputObj;delete myObj;}}
其中一個參數(shù)的key是keyValues[0],而value則是用keyValue[0]和keyValue[1]經(jīng)過某些加密運(yùn)算得到的值,這就是我們要找的目標(biāo)參數(shù)。
好了,可以看到keyValues這個數(shù)組是通過gc()這個函數(shù)的返回值split出來的,那么看看gc()是什么:
function gc() {var key = 'MTk0MTEw';var value = '';var cssArr = [ 'selectSeatType', 'ev_light', 'ev_light','fishTimeRangePicker', 'updatesFound', 'tipScript','refreshButton', 'fish_clock', 'refreshStudentButton','btnMoreOptions', 'btnAutoLogin', 'fish_button','defaultSafeModeTime', 'ticket-navigation-item' ];var csschek = false;if (cssArr && cssArr.length > 0) {for ( var i = 0; i < cssArr.length; i++) {if ($('.' + cssArr[i]).length > 0) {csschek = true;break;}}}if (csschek) {value += '0';} else {value += '1';}var idArr = [ 'btnMoreOptions', 'refreshStudentButton','fishTimeRangePicker', 'helpertooltable', 'outerbox','updateInfo', 'fish_clock', 'refreshStudentButton','btnAutoRefresh', 'btnAutoSubmit', 'btnRefreshPassenger','autoLogin', 'bnAutoRefreshStu', 'orderCountCell','refreshStudentButton', 'enableAdvPanel', 'autoDelayInvoke','refreshButton', 'refreshTimesBar', 'chkAllSeat' ];var idchek = false;for ( var i = 0; i < idArr.length; i++) {if ($('#' + idArr[i])[0]) {idchek = true;break;}}if (idchek) {value += '0';} else {value += '1';}var attrArr = [ 'helperVersion' ];var attrLen = attrArr ? attrArr.length : 0;var attrchek = false;for ( var p in parent) {if (!attrchek) {for ( var k = 0; k < attrLen; k++) {if (String(p).indexOf(attrArr[k]) > -1) {attrchek = true;break;}}} elsebreak;}for ( var p in window) {if (!attrchek) {for ( var k = 0; k < attrLen; k++) {if (String(p).indexOf(attrArr[k]) > -1) {attrchek = true;break;}}} elsebreak;}var styleArr = [ '.enter_right>.enter_enw>.enter_rtitle', '.objbox td' ];var stylechek = false;if (styleArr && styleArr.length > 0) {for ( var i = 0; i < styleArr.length; i++) {var tempStyle = $(styleArr[i]);if (tempStyle[0]) {for ( var k = 0; k < tempStyle.length > 0; k++) {if (tempStyle.eq(k).attr('style')) {stylechek = true;break;}}}}}if (stylechek) {value += '0';} else {value += '1';}var keywordArr = [ {key : ".enter_right",values : [ "親", "搶票", "助手" ]}, {key : ".cx_form",values : [ "點(diǎn)發(fā)車", "刷票" ]}, {key : "#gridbox",values : [ "只選", "僅選", "checkBox", "checkbox" ]}, {key : ".enter_w",values : [ "助手" ]} ];var keywordchek = false;if (keywordArr && keywordArr.length > 0) {for ( var i = 0; i < keywordArr.length; i++) {var kw = keywordArr[i];if (fw(kw)) {keywordchek = true;break;}}}if (keywordchek) {value += '0';} else {value += '1';}if (value.indexOf('0') > -1) {aj();}return key + ':' + value;}
可以看到key無需計算,而value是經(jīng)過計算得到的:具體的就是在頁面上找是否有指定class的元素,是否有指定id的元素........也不知道12306是要干嘛,反正我是沒理解這塊的真正含義?但是可以分析出來,value最后的結(jié)果必然是“xxxx”,x代表0或1,但是顯然12306不希望value中包含‘0’。那么也許可以直接用‘1111’來代替value吧~
到這里,登陸的邏輯就分析完了,想模擬登陸的童鞋可以自己動手試試了,這里提供一個思路吧,可以把encode32等函數(shù)拿到本地,然后用Java提供的js引擎執(zhí)行一下~那么這個value就出來。
更深入
其實(shí)不僅僅是登陸,在各種提交表單的地方都加入了這么一個動態(tài)的key-value,具體的流程是這樣的:
1、首先在加載頁面時會引入一個動態(tài)的js文件,通過這個主要是拿到動態(tài)的key值,而計算value的邏輯都一樣
2、在提交表單的時候會將這個動態(tài)的key和value也一并提交上去,若該參數(shù)出錯,則會報“非法請求”錯誤
掌握了這些,從老版搶票軟件升級到新版也不是什么難事了吧~
總結(jié)
以上是生活随笔為你收集整理的12306新版抢票之逻辑分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。