鄭重聲明:
本文僅供學習使用,禁止用於非法用途,不然後果自負,若有侵權,煩請告知刪除,謝謝合做!
如今已經還原了算法的實現邏輯,下一步就是如何更好地僞造本身,本文提供臨時設置的實現方式,方便在不修改以前復現代碼的基礎上實現擴展,固然也能夠直接在還原算法源碼中寫入僞造代碼.html
值得注意的是,這種 Object.defineProperty
方式只會臨時生效並且僅僅針對使用 js 代碼獲取對象屬性的值,並不會真正修改對象屬性!前端
/** * 設置用戶代理,檢測方式: navigator.userAgent */ chromeHelper.setUserAgent = function(userAgent) { if (!userAgent) { userAgent = "Mozilla5.0 (Macintosh; Intel Mac OS X 10_15_2) AppleWebKit537.36 (KHTML, like Gecko) Chrome80.0.3987.87 Safari537.36"; } Object.defineProperty(navigator, "userAgent", { value: userAgent, writable: false }); }
/** * 設置瀏覽器語言,檢測方式: navigator.language */ chromeHelper.setLanguage = function(language) { if (!language) { language = "zh-CN"; } Object.defineProperty(navigator, "language", { value: language, writable: false }); }
/** * 設置瀏覽器語言,檢測方式: navigator.languages */ chromeHelper.setLanguages = function(languages) { if (!languages) { languages = ["zh-CN", "zh", "en"]; } Object.defineProperty(navigator, "languages", { value: languages, writable: false }); }
/** * 設置屏幕顏色深度,檢測方式: screen.colorDepth */ chromeHelper.setColorDepth = function(colorDepth) { if (!colorDepth) { colorDepth = 24; } Object.defineProperty(screen, "colorDepth", { value: colorDepth, writable: false }); }
/** * 設置設備像素比率,檢測方式: window.devicePixelRatio */ chromeHelper.setDevicePixelRatio = function(devicePixelRatio) { if (!devicePixelRatio) { devicePixelRatio = 24; } Object.defineProperty(window, "devicePixelRatio", { value: devicePixelRatio, writable: false }); }
/** * 設置屏幕寬度,檢測方式: screen.width */ chromeHelper.setWidth = function(width) { if (!width) { width = 1280; } Object.defineProperty(screen, "width", { value: width, writable: false }); }
/** * 設置屏幕高度,檢測方式: screen.height */ chromeHelper.setHeight = function(height) { if (!height) { height = 800; } Object.defineProperty(screen, "height", { value: height, writable: false }); }
/** * 設置屏幕可用寬度,檢測方式: screen.availWidth */ chromeHelper.setAvailWidth = function(availWidth) { if (!availWidth) { availWidth = 1280; } Object.defineProperty(screen, "availWidth", { value: availWidth, writable: false }); }
/** * 設置屏幕可用高度,檢測方式: screen.availHeight */ chromeHelper.setAvailHeight = function(availHeight) { if (!availHeight) { availHeight = 777; } Object.defineProperty(screen, "availHeight", { value: availHeight, writable: false }); }
/** * 設置Session存儲,檢測方式: !!window.sessionStorage */ chromeHelper.setSessionStorage = function(sessionStorage) { if (!sessionStorage) { sessionStorage = 1; } if (sessionStorage) { window.sessionStorage = 1 } else { delete window.sessionStorage } }
/** * 設置Local存儲,檢測方式: !!window.localStorage */ chromeHelper.setLocalStorage = function(localStorage) { if (!localStorage) { localStorage = 1; } if (localStorage) { window.localStorage = 1 } else { delete window.localStorage } }
/** * 設置indexedDB存儲,檢測方式: !!window.indexedDB */ chromeHelper.setIndexedDB = function(indexedDB) { if (!indexedDB) { indexedDB = 1; } if (indexedDB) { window.indexedDB = 1 } else { delete window.indexedDB } }
/** * 設置addBehavior存儲,檢測方式: !!document.body.addBehavior */ chromeHelper.setAddBehavior = function(addBehavior) { if (!addBehavior) { addBehavior = 1; } if (addBehavior) { document.body.addBehavior = 1 } else { delete document.body.addBehavior } }
/** * 設置Cpu類型,檢測方式: navigator.cpuClass */ chromeHelper.setCpuClass = function(cpuClass) { if (!cpuClass) { cpuClass = "unknown"; } Object.defineProperty(navigator, "cpuClass", { value: cpuClass, writable: false }); }
/** * 設置平臺類型,檢測方式: navigator.platform */ chromeHelper.setPlatform = function(platform) { if (!platform) { platform = "MacIntel"; } Object.defineProperty(navigator, "platform", { value: platform, writable: false }); }
/** * 設置反追蹤,檢測方式: navigator.doNotTrack */ chromeHelper.setDoNotTrack = function(doNotTrack) { if (!doNotTrack) { doNotTrack = "unknown"; } Object.defineProperty(navigator, "doNotTrack", { value: doNotTrack, writable: false }); }
/** * 設置插件,檢測方式: navigator.plugins */ chromeHelper.setPlugins = function(plugins) { }
/** * 設置Canvas,檢測方式: TODO */ chromeHelper.setCanvas = function(canvas) { }
/** * 設置Webgl,檢測方式: TODO */ chromeHelper.setWebgl = function(webgl) { }
/** * 設置AdBlock,檢測方式: TODO */ chromeHelper.setAdBlock = function(AdBlock) { }
/** * 設置AdBlock,檢測方式: TODO */ chromeHelper.setAdBlock = function(AdBlock) { }
/** * 設置字體,檢測方式: TODO */ chromeHelper.setFonts = function(fonts) { }
/** * 設置最多觸控點,檢測方式: navigator.maxTouchPoints */ chromeHelper.setMaxTouchPoints = function(maxTouchPoints) { if (!maxTouchPoints) { maxTouchPoints = 0; } Object.defineProperty(navigator, "maxTouchPoints", { value: maxTouchPoints, writable: false }); }
/** * 設置ontouchstart事件,檢測方式: "ontouchstart" in window */ chromeHelper.setTouchEvent = function(ontouchstart) { if (!ontouchstart) { ontouchstart = false; } if (ontouchstart) { window.ontouchstart = true } else { delete window.ontouchstart } }
/** * 設置app代碼名稱代碼,檢測方式: navigator.appCodeName.toString() */ chromeHelper.setAppCodeName = function(appCodeName) { if (!appCodeName) { appCodeName = "Mozilla"; } Object.defineProperty(navigator, "appCodeName", { value: appCodeName, writable: false }); }
/** * 設置app代碼名稱代碼,檢測方式: navigator.appName.toString() */ chromeHelper.setAppName = function(appName) { if (!appName) { appName = "Netscape"; } Object.defineProperty(navigator, "appName", { value: appName, writable: false }); }
/** * 設置Java是否啓用,檢測方式: navigator.javaEnabled() */ chromeHelper.setJavaEnabled = function(javaEnabled) { }
/** * 設置媒體類型,檢測方式: navigator.mimeTypes */ chromeHelper.setMimeTypes = function(mimeTypes) { }
/** * 設置cookie是否啓用,檢測方式: navigator.cookieEnabled */ chromeHelper.setCookieEnabled = function(cookieEnabled) { if (!cookieEnabled) { cookieEnabled = true; } Object.defineProperty(navigator, "cookieEnabled", { value: cookieEnabled, writable: false }); }
/** * 設置是否在線,檢測方式: navigator.onLine.toString() */ chromeHelper.setOnLine = function(onLine) { if (!onLine) { onLine = true; } Object.defineProperty(navigator, "onLine", { value: onLine, writable: false }); }
/** * 添加歷史記錄,檢測方式: window.history */ chromeHelper.pushHistory = function(newUrls) { for (url in newUrls) { history.pushState(null, '', url); } }
親測構造請求 /otn/HttpZF/logdevice時,關於參數 algID
常常性發生變化,所以沒法提供靜態的請求方法,建議根據實際狀況實時改變.java
經過翻閱源碼實現,最終發現關於發送請求的代碼是這樣的:web
e = c.hashAlg(m, a, e); a = e.key; e = e.value; a += "\x26timestamp\x3d" + (new Date).getTime(); $a.getJSON("https://kyfw.12306.cn/otn/HttpZF/logdevice" + ("?algID\x3dmBxuYhGXYR\x26hashCode\x3d" + e + a), null, function(a) { var b = JSON.parse(a); void 0 != mb && mb.postMessage(a, r.parent); for (var d in b) "dfp" == d ? G("RAIL_DEVICEID") != b[d] && (V("RAIL_DEVICEID", b[d], 1E3), c.deviceEc.set("RAIL_DEVICEID", b[d])) : "exp" == d ? V("RAIL_EXPIRATION", b[d], 1E3) : "cookieCode" == d && (c.ec.set("RAIL_OkLJUJ", b[d]), V("RAIL_OkLJUJ", "", 0)) })
其中,參數 a
表示的是加密後的瀏覽器指紋信息,(new Date).getTime()
是當前時間戳,而 algID\x3dmBxuYhGXYR\x26hashCode\x3d
這部分的 algID
算法參數是暫時性靜態的,好比今天一段時間都是 mBxuYhGXYR
而次日這個值就變成其餘值了.ajax
hashCode
參數的值就是程序運行結果的 value
值,最後面的變量 a
表明的是剩下的瀏覽器指紋信息,即運行結果的 key
.正則表達式
假設此時此刻爲例,演示如何使用該 js 文件:算法
function ajax(req){ var xhr=new XMLHttpRequest(); xhr.onreadystatechange=function(){ if(xhr.readyState===4){ req.success&&req.success(xhr.responseText,xhr.status); } } req.method=req.method?req.method.toUpperCase():'GET'; var data=null; var url=req.url; if(req.data){ var arg=''; for(var n in req.data){ arg+=n+'='+encodeURIComponent(req.data[n])+'&' } arg=arg.slice(0,-1); if(req.method==='GET'){ url=url+'?'+arg; }else{ data=arg; } } if(req.headers){ for(var h in req.headers){ var v=req.headers[h]; xhr.setRequestHeader(h,v); } } xhr.open(req.method,url); xhr.send(data); } e = chromeHelper.prototype.encryptedFingerPrintInfo(); a = e.key; e = e.value; a += "\x26timestamp\x3d" + (new Date).getTime(); ajax({ url:"https://kyfw.12306.cn/otn/HttpZF/logdevice" + ("?algID\x3dmBxuYhGXYR\x26hashCode\x3d" + e + a), success:function(data){ console.log("data",data); startIndex = "callbackFunction('".length; endIndex = data.lastIndexOf("')"); jsonStrData = data.substring(startIndex,endIndex); console.log("jsonStrData",jsonStrData); jsonData = JSON.parse(jsonStrData); console.log("jsonData",jsonData); exp = jsonData.exp; cookieCode = jsonData.cookieCode; dfp = jsonData.dfp; console.log("RAIL_DEVICEID::: "+dfp+" RAIL_EXPIRATION::: " + exp +" RAIL_OkLJUJ::: " + cookieCode); } });
在還原算法實現過程當中,充分複習了 web 前端開發的調試技巧,針對通篇無心義的變量命名方式,有效的應對方式就是採用正則表達式精確匹配進行查找.chrome
同時,爲了驗證本身的猜測是否正確,還須要結合斷點調試,若是存在反調試手段,那麼只能靠本身硬啃壓縮混淆代碼了,我只能說:考驗真正技術的時候,到了!數據庫
本文給咱們留下了很多啓發供後續工做學習使用,從開發者的角度上來說:json
例如從新打亂字符串,將字符串分隔成三份,按照首尾中或者尾中首等反人類次序從新排序等.
例如獲取用戶代理採用不一樣的正則表達式進行替換,獲取瀏覽器語言時採用另外的途徑驗證上一步獲取結果是否造假等.
只有要基本的開發經驗很容易一葉知秋,進而判斷相關技術棧,由於技術都是通用的方案,很是容易複製,打造獨特的技術流會增大破解成本,進而嚇退一部分菜鳥小白.
重要的業務邏輯確定是放在後端進行處理,哪怕前端已經處理過相同邏輯也不能偷懶,更要保證先後端處理邏輯的一致性.
攻防是矛與盾,做爲攻克方要作的一點就是打鐵還需自身硬,多瞭解不一樣的技術棧才能作到有的放矢而不至於臨陣脫逃,望而卻步!
最後祝你們搶票愉快,須要買票時人人都有票,不再須要搶票回家,人生苦短何須浪費生命去搶票?
再次聲明,本文僅供學習研究,一切用做它途的行爲均與本人無關,若有侵權,煩請告知,謝謝合做.
若是本文對你有所幫助,不用讚揚,也沒必要轉發,直接點贊留言告訴鼓勵一下就能夠啦!