DEVICEID的來源,從此搶票不再掉線(下)


模擬偽裝

現在已經還原了算法的實現邏輯,下一步就是如何更好地偽造自己,本文提供臨時設置的實現方式,方便在不修改之前復現代碼的基礎上實現擴展,當然也可以直接在還原算法源碼中寫入偽造代碼.

值得注意的是,這種 Object.defineProperty 方式只會臨時生效而且僅僅針對使用 js 代碼獲取對象屬性的值,並不會真正修改對象屬性!

  • 設置用戶代理
<code>  /**
* 設置用戶代理,檢測方式: 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
});
}/<code>


  • 設置瀏覽器語言
<code>  /**
* 設置瀏覽器語言,檢測方式: navigator.language

*/
chromeHelper.setLanguage = function(language) {
if (!language) {
language = "zh-CN";
}
Object.defineProperty(navigator, "language", {
value: language,
writable: false
});
}/<code>


  • 設置瀏覽器語言
<code>  /**
* 設置瀏覽器語言,檢測方式: navigator.languages
*/
chromeHelper.setLanguages = function(languages) {
if (!languages) {
languages = ["zh-CN", "zh", "en"];
}
Object.defineProperty(navigator, "languages", {
value: languages,
writable: false
});
}/<code>


  • 設置屏幕顏色深度
<code>  /**
* 設置屏幕顏色深度,檢測方式: screen.colorDepth
*/
chromeHelper.setColorDepth = function(colorDepth) {
if (!colorDepth) {
colorDepth = 24;
}
Object.defineProperty(screen, "colorDepth", {
value: colorDepth,

writable: false
});
}/<code>


  • 設置設備像素比率
<code>  /**
* 設置設備像素比率,檢測方式: window.devicePixelRatio
*/
chromeHelper.setDevicePixelRatio = function(devicePixelRatio) {
if (!devicePixelRatio) {
devicePixelRatio = 24;
}
Object.defineProperty(window, "devicePixelRatio", {
value: devicePixelRatio,
writable: false
});
}/<code>


  • 設置屏幕寬度
<code>  /**
* 設置屏幕寬度,檢測方式: screen.width
*/
chromeHelper.setWidth = function(width) {
if (!width) {
width = 1280;
}
Object.defineProperty(screen, "width", {
value: width,
writable: false
});
}/<code>


  • 設置屏幕高度
<code>  /**
* 設置屏幕高度,檢測方式: screen.height
*/
chromeHelper.setHeight = function(height) {
if (!height) {
height = 800;
}
Object.defineProperty(screen, "height", {
value: height,
writable: false
});
}/<code>


  • 設置屏幕可用寬度
<code>  /**
* 設置屏幕可用寬度,檢測方式: screen.availWidth
*/
chromeHelper.setAvailWidth = function(availWidth) {
if (!availWidth) {
availWidth = 1280;
}
Object.defineProperty(screen, "availWidth", {
value: availWidth,
writable: false
});
}/<code>


  • 設置屏幕可用高度
<code>  /**
* 設置屏幕可用高度,檢測方式: screen.availHeight

*/
chromeHelper.setAvailHeight = function(availHeight) {
if (!availHeight) {
availHeight = 777;
}
Object.defineProperty(screen, "availHeight", {
value: availHeight,
writable: false
});
}/<code>


  • 設置Session存儲
<code>  /**
* 設置Session存儲,檢測方式: !!window.sessionStorage
*/
chromeHelper.setSessionStorage = function(sessionStorage) {
if (!sessionStorage) {
sessionStorage = 1;
}
if (sessionStorage) {
window.sessionStorage = 1
} else {
delete window.sessionStorage
}
}/<code>


  • 設置Local存儲
<code>  /**
* 設置Local存儲,檢測方式: !!window.localStorage
*/
chromeHelper.setLocalStorage = function(localStorage) {
if (!localStorage) {
localStorage = 1;
}
if (localStorage) {
window.localStorage = 1

} else {
delete window.localStorage
}
}/<code>


  • 設置indexedDB存儲
<code>  /**
* 設置indexedDB存儲,檢測方式: !!window.indexedDB
*/
chromeHelper.setIndexedDB = function(indexedDB) {
if (!indexedDB) {
indexedDB = 1;
}
if (indexedDB) {
window.indexedDB = 1
} else {
delete window.indexedDB
}
}/<code>


  • 設置addBehavior存儲
<code>  /**
* 設置addBehavior存儲,檢測方式: !!document.body.addBehavior
*/
chromeHelper.setAddBehavior = function(addBehavior) {
if (!addBehavior) {
addBehavior = 1;
}
if (addBehavior) {
document.body.addBehavior = 1
} else {
delete document.body.addBehavior
}
}/<code>


  • 設置Cpu類型
<code>  /**
* 設置Cpu類型,檢測方式: navigator.cpuClass
*/
chromeHelper.setCpuClass = function(cpuClass) {
if (!cpuClass) {
cpuClass = "unknown";
}
Object.defineProperty(navigator, "cpuClass", {
value: cpuClass,
writable: false
});
}/<code>


  • 設置平臺類型
<code>  /**
* 設置平臺類型,檢測方式: navigator.platform
*/
chromeHelper.setPlatform = function(platform) {
if (!platform) {
platform = "MacIntel";
}
Object.defineProperty(navigator, "platform", {
value: platform,
writable: false
});
}/<code>


  • 設置反追蹤
<code>  /** 

* 設置反追蹤,檢測方式: navigator.doNotTrack
*/
chromeHelper.setDoNotTrack = function(doNotTrack) {
if (!doNotTrack) {
doNotTrack = "unknown";
}
Object.defineProperty(navigator, "doNotTrack", {
value: doNotTrack,
writable: false
});
}/<code>


  • 設置插件
<code>  /**
* 設置插件,檢測方式: navigator.plugins
*/
chromeHelper.setPlugins = function(plugins) {

}/<code>


  • 設置Canvas
<code>  /**
* 設置Canvas,檢測方式: TODO
*/
chromeHelper.setCanvas = function(canvas) {

}/<code>


  • 設置Webgl
<code>  /**
* 設置Webgl,檢測方式: TODO
*/
chromeHelper.setWebgl = function(webgl) {

}/<code>


  • 設置AdBlock
<code>  /**
* 設置AdBlock,檢測方式: TODO
*/
chromeHelper.setAdBlock = function(AdBlock) {

}/<code>


  • 設置AdBlock
<code>  /**
* 設置AdBlock,檢測方式: TODO
*/
chromeHelper.setAdBlock = function(AdBlock) {

}/<code>


  • 設置字體
<code>  /**
* 設置字體,檢測方式: TODO
*/
chromeHelper.setFonts = function(fonts) {

}/<code>


  • 設置最多觸控點
<code>  /**
* 設置最多觸控點,檢測方式: navigator.maxTouchPoints
*/
chromeHelper.setMaxTouchPoints = function(maxTouchPoints) {
if (!maxTouchPoints) {
maxTouchPoints = 0;
}
Object.defineProperty(navigator, "maxTouchPoints", {
value: maxTouchPoints,
writable: false
});
}/<code>


  • 設置ontouchstart事件
<code>  /**
* 設置ontouchstart事件,檢測方式: "ontouchstart" in window
*/
chromeHelper.setTouchEvent = function(ontouchstart) {
if (!ontouchstart) {
ontouchstart = false;
}
if (ontouchstart) {
window.ontouchstart = true
} else {
delete window.ontouchstart
}
}/<code>


  • 設置app代碼名稱代碼
<code>  /**
* 設置app代碼名稱代碼,檢測方式: navigator.appCodeName.toString()
*/
chromeHelper.setAppCodeName = function(appCodeName) {
if (!appCodeName) {
appCodeName = "Mozilla";
}
Object.defineProperty(navigator, "appCodeName", {
value: appCodeName,
writable: false
});
}/<code>


  • 設置app代碼名稱代碼
<code>  /**
* 設置app代碼名稱代碼,檢測方式: navigator.appName.toString()
*/
chromeHelper.setAppName = function(appName) {
if (!appName) {
appName = "Netscape";
}
Object.defineProperty(navigator, "appName", {
value: appName,
writable: false
});
}/<code>


  • 設置Java是否啟用
<code>  /**
* 設置Java是否啟用,檢測方式: navigator.javaEnabled()

*/
chromeHelper.setJavaEnabled = function(javaEnabled) {

}/<code>


  • 設置媒體類型
<code>  /**
* 設置媒體類型,檢測方式: navigator.mimeTypes
*/
chromeHelper.setMimeTypes = function(mimeTypes) {

}/<code>


  • 設置cookie是否啟用
<code>  /**
* 設置cookie是否啟用,檢測方式: navigator.cookieEnabled
*/
chromeHelper.setCookieEnabled = function(cookieEnabled) {
if (!cookieEnabled) {
cookieEnabled = true;
}
Object.defineProperty(navigator, "cookieEnabled", {
value: cookieEnabled,
writable: false
});
}/<code>


  • 設置是否在線
<code>  /** 

* 設置是否在線,檢測方式: navigator.onLine.toString()
*/
chromeHelper.setOnLine = function(onLine) {
if (!onLine) {
onLine = true;
}
Object.defineProperty(navigator, "onLine", {
value: onLine,
writable: false
});
}/<code>


  • 添加歷史記錄
<code>  /**
* 添加歷史記錄,檢測方式: window.history
*/
chromeHelper.pushHistory = function(newUrls) {
for (url in newUrls) {
history.pushState(null, '', url);
}
}/<code>


使用示例

親測構造請求 https://kyfw.12306.cn/otn/HttpZF/logdevice時,關於參數 algID 經常性發生變化,因此無法提供靜態的請求方法,建議根據實際情況實時改變.

通過翻閱源碼實現,最終發現關於發送請求的代碼是這樣的:

<code>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))
})/<code>

其中,參數 a 表示的是加密後的瀏覽器指紋信息,(new Date).getTime() 是當前時間戳,而 algIDx3dmBxuYhGXYRx26hashCodex3d 這部分的 algID 算法參數是暫時性靜態的,比如今天一段時間都是 mBxuYhGXYR 而第二天這個值就變成其他值了.

hashCode 參數的值就是程序運行結果的 value 值,最後面的變量 a 代表的是剩下的瀏覽器指紋信息,即運行結果的 key.

假設此時此刻為例,演示如何使用該 js 文件:

<code>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);
}
});/<code>
只要搞定RAIL_DEVICEID的來源,從此搶票不再掉線(下)

回顧展望

在還原算法實現過程中,充分複習了 web 前端開發的調試技巧,針對通篇無意義的變量命名方式,有效的應對方式就是採用正則表達式精確匹配進行查找.

同時,為了驗證自己的猜想是否正確,還需要結合斷點調試,如果存在反調試手段,那麼只能靠自己硬啃壓縮混淆代碼了,我只能說:考驗真正技術的時候,到了!

本文給我們留下了不少啟發供後續工作學習使用,從開發者的角度上來講:

  • 不完全依靠現成加密技術,哪怕真正加密時沒自己實現也要在加密前後實現自己的混淆邏輯.

例如重新打亂字符串,將字符串分隔成三份,按照首尾中或者尾中首等反人類次序重新排序等.

  • 重複使用同一數據時也不一定要抽象成同一個方法,不同對象調用不同的處理邏輯,更是讓人防不勝防,陷入思維慣性誤區.

例如獲取用戶代理採用不同的正則表達式進行替換,獲取瀏覽器語言時採用另外的途徑驗證上一步獲取結果是否造假等.

  • 無序更勝似有序,看似規整優美的代碼是給開發人員看的而不該給機器看,一定不能使用源碼上線而是要加密處理或者其他處理.

只有要基本的開發經驗很容易一葉知秋,進而判斷相關技術棧,因為技術都是通用的方案,非常容易複製,打造獨特的技術流會增大破解成本,進而嚇退一部分菜鳥小白.

  • 前端和後端需要密切配合協同協作,缺少統一指揮難以避免一方或者多方偷懶進而暴露我方陣地.

重要的業務邏輯肯定是放在後端進行處理,哪怕前端已經處理過相同邏輯也不能偷懶,更要保證前後端處理邏輯的一致性.

攻防是矛與盾,作為攻克方要做的一點就是打鐵還需自身硬,多瞭解不同的技術棧才能做到有的放矢而不至於臨陣脫逃,望而卻步!

最後祝大家搶票愉快,需要買票時人人都有票,再也不需要搶票回家,人生苦短何必浪費生命去搶票?

再次聲明,本文僅供學習研究,一切用作它途的行為均與本人無關,如有侵權,煩請告知,謝謝合作.


  • https://cloud.tencent.com/developer/article/1503476
  • https://segmentfault.com/a/1190000016671687?utm_source=tag-newest#item-1
  • https://www.jianshu.com/p/553d7b7b78fe?utm_source=oschina-app
  • https://www.jianshu.com/p/64ded82068b0
  • https://www.cnblogs.com/tongchuanxing/p/5664379.html
  • https://www.cnblogs.com/liujianshe1990-/p/11059796.html
  • https://www.cnblogs.com/jiechn/p/4080503.html
  • https://www.cnblogs.com/wuyuchang/p/4245621.html

如果你覺得本文對你有所幫助,歡迎點贊評論和轉發,來一波素質三連吧! 偷偷告訴你,點擊瞭解更多可以獲得最佳閱讀體驗喲!


分享到:


相關文章: