2019年末逆向复习系列之Boss直聘Cookie加密字段__zp_stoken__逆向分析
鄭重聲明:本項目的所有代碼和相關文章, 僅用于經驗技術交流分享,禁止將相關技術應用到不正當途徑,因為濫用技術產生的風險與本人無關。
這篇文章是公眾號《云爬蟲技術研究筆記》的《2019年末逆向復習系列》的第七篇:《Boss直聘Cookie加密字段__zp_stoken__逆向分析》
本次案例的代碼都已上傳到Review_Reverse上面,后面會持續更新,大家可以Fork一波。
具體加密JS可以在Review_Reverse.boss_zp.encrypt.js中看到,替換具體的參數即可。
背景分析
Boss直聘大概可以算是目前最火的招聘軟件了!幾個月前的火遍全網的“找工作,馬上,和老板談!”真的是洗腦了一遍又一遍啊!
鑒于Boss直聘的職位更新速度快,職位發布多的兩大特點,很多做行業分析和各地崗位差距報告的人都會利用Boss直聘的數據來做分析,幾個月前,Boss直聘對他們的接口在Cookie方面做了__zp_stoken__字段的加密,這次案例就用它來做逆向分析。
__zp_stoken__字段生成流程分析
我們需要分析的是__zp_stoken__字段,我們看看它是在哪里生成的
我們要獲取Cookie生成的過程,先把網站的數據清空,在Chrome中的Application中利用Clear site data清理好該網站下的數據,然后我們重新刷新網站(我們需要在具體的職位信息頁面刷新,因為首頁不需要Cookie就能直接訪問),看看Network列表中的Cookie生成過程
我們首先會訪問具體的職位信息頁面,得到一個HttpCode是302的響應,代表這次的訪問重定向到一個新的URL了,這個URL可以在響應中的Location中看到,拿到的是security-check.html這個URL,我們看看這個URL是什么內容
這個URL具體有幾個Param
- seed
- name
- ts
- callbackUrl
- srcReferer
根據多次的訪問測試,發現變化的字段主要是seed、name、ts,也就是這個URL是動態的,我們直接下斷點是打不到具體的地方的,在Chrome里我們也拿不到這個鏈接的具體響應內容,我們利用Fiddler來獲取鏈接內容
var Cookie = {get: function(name) {var arr,reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)");if ((arr = document.cookie.match(reg))) {return unescape(arr[2]); } else {return null;}},set: function(name, value, time, domain, path) {var str = name + "=" + encodeURIComponent(value);if (time) {var date = new Date(time).toGMTString();str += ";expires=" +date;}str = domain ? str + ";domain=" + domain : str;str = path ? str + ";path=" + path : str;document.cookie = str;} };var url = window.location.href; var seed = decodeURIComponent(getQueryString("seed")) || ""; var ts = getQueryString("ts"); var fileName = getQueryString("name"); var callbackUrl = docodeURIComponent(getQueryString("callbackUrl")); var srcReferer = decodeURIComponent(getQueryString("srcReferer")||'');if (seed && ts && fileName) {seriesLoadScripts("security-js/" + fileName + ".js", function() {var expiredate = new Date().getTime() + 32 * 60 * 60 * 1000 * 2;var code = "";var nativeParams = {};var ABC = window.ABC || frame.contentWindow.ABC;try {code = new ABC().z(seed, parseInt(ts) + (480+new Date().getTimezoneOffset())*60*1000);} catch (e) {}if (code && callbackUrl) {Cookie.set("__zp_stoken__", code, expiredate, COOKIE_DOMAIN, "/");// 據說iOS 客戶端存在有時寫cookie失敗的情況,因此調用客戶端提供的方法,交由客戶端額外寫一次cookieif (typeof window.wst != "undefined" && typeof wst.postMessage == "function") {nativeParams = {name: "setWKCookie",params: {url: COOKIE_DOMAIN,name: "__zp_stoken__",value: encodeURIComponent(code),expiredate: expiredate,path: "/"}};window.wst.postMessage(JSON.stringify(nativeParams));}if(srcReferer && isSeo(srcReferer) && srcReferer != 'https://m.baidu.com/'){window.location.replace(srcReferer);} else {window.location.replace(callbackUrl);}} else {window.history.back();}}); } else {if(srcReferer && isSeo(srcReferer) && srcReferer != 'https://m.baidu.com/'){window.location.replace(srcReferer);}else if (callbackUrl) {window.location.replace(callbackUrl);} else {window.history.back();} }可以看出__zp_stoken__是這么生成的
var seed = decodeURIComponent(getQueryString("seed")) || ""; var ts = getQueryString("ts"); code = new ABC().z(seed, parseInt(ts)+(480+new Date().getTimezoneOffset())*60*1000);簡化一下
480+new Date().getTimezoneOffset() = 0 code = ABC.z(seed, ts)如上所示,通過傳入的seed和ts參數調用實例ABC的c方法來生成,給當前的document對象加上帶__zp_stoken__的Cookie之后重新加載頁面,這樣整個從無Cookie訪問到生成Cookie重載頁面訪問的流程就分析到這里。
__zp_stoken__生成逆向分析
1. 切中關鍵語句,下斷點
剛才說了security-check.html這個問題是動態的,我們不能去給這個文件下斷點,重新看看security-check.html的頁面源碼
var fileName = getQueryString("name"); seriesLoadScripts("security-js/" + fileName + ".js", function())加載了包含加密方法的Js文件,fileName是security-check.html的params中的name字段,我們在Sources的Page中找到該Js文件,整個文件一共是5381行
上面我們分析了加密使用ABC的z方法,在文件中搜索ABC字符,找到了z方法
先對最后的return語句打斷點
重新刷新頁面,可以獲取的整個方法中各個參數的值,接下來開始分析整個加密方法的流程
2. 刪繁就簡,核心流程分析
分析z加密方法
ABC[_0x56ae('0x3e')]['z'] = function(_0x172aa3, _0x38e762) {//_0x56ae('0x3e') 是 prototype,ABC[_0x56ae('0x3e')]['z']也就是給ABC方法的prototype屬性中的鍵z指定方法,_0x172aa3是seed,ts是時間戳var _0x3a1f8c = {};_0x3a1f8c[_0x56ae('0x3f')] = function(_0x5362ae, _0x320fdd) {return _0x5362ae == _0x320fdd;};_0x3a1f8c[_0x56ae('0x40')] = _0x56ae('0x41');_0x3a1f8c[_0x56ae('0x42')] = _0x56ae('0x43');_0x3a1f8c[_0x56ae('0x44')] = function(_0x2e473d, _0x3ad5ac, _0x1c497c) {return _0x2e473d(_0x3ad5ac, _0x1c497c);};_0x3a1f8c[_0x56ae('0x45')] = function(_0x1f3c2a, _0x218684) {return _0x1f3c2a + _0x218684;};try {if (document && _0x3a1f8c[_0x56ae('0x3f')](_0x3a1f8c[_0x56ae('0x40')], typeof document[_0x56ae('0x46')])) {_0x5606fe = _0x172aa3;_0x2f4975 = _0x38e762;} else {_0x5606fe = _0x3a1f8c[_0x56ae('0x42')];}} catch (_0x1f6304) {_0x5606fe = _0x3a1f8c[_0x56ae('0x42')];}//上面幾項是給dict賦值,沒有什么難度,可以不做修改,重點是最后的return語句中的方法return _0x3a1f8c[_0x56ae('0x44')](_0x49423a, this, _0x3a1f8c[_0x56ae('0x45')](_0x172aa3[_0x56ae('0x47')], 0xb));return 語句是這樣
我們在console中獲取每個參數的值,看看能不能做精簡
- _0x3a1f8c[_0x56ae('0x44')]
_0x3a1f8c[_0x56ae(‘0x44’)]是這樣一個方法,參數一是方法,參數二、三作為參數一方法的參數
- this
this就是ABC實例,可以在同一個文件中搜索ABC可以得到
_0x3a1f8c[_0x56ae('0x45')](_0x172aa3[_0x56ae('0x47')], 0xb)這個參數直接放在console里面運行,得到固定值是55,
從上面分析來看,return語句可以直接簡化成
_0x49423a(ABC, 55)3. 還是那句話,缺啥補啥
我們現在拿到了關鍵語句,接下來就是調試_0x49423a方法,往js文件里面添加缺少的變量和函數方法,出于簡單的考慮,直接使用node來調用js文件
直接拿node運行js文件得出來的結果和在Chrome的console里運行js內容得到的結果不一樣,看來還需要調試在哪個地方引用的windows,this之類的值兩個運行環境有差別
__zp_stoken__參數生成注意要求
- Js加密函數所在文件不同,不過文件內容邏輯基本相同,只用分析一個文件即可
- __zp_stoken__參數生成之后,不能直接在Cookie中使用,需要做URLencode才能使用
復習要點
1.這個案例也是很好的動態Cookie生成的案例,和之前的努比亞論壇的Cookie生成案例類似,利用window.location.href/replace/reload()這些機制來重載頁面,具體如下圖所示:
2.在調試的時候注意關鍵詞的值,比如this,document,windows這些,往往有時候雖然方法走通了,但是值不對,很有可能就是上面這些值在賦值時發生了錯誤。
?
總結
以上是生活随笔為你收集整理的2019年末逆向复习系列之Boss直聘Cookie加密字段__zp_stoken__逆向分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2019年末逆向复习系列之今日头条WEB
- 下一篇: python-css反爬之svg映射