今日網(wǎng)站
aHR0cHM6Ly93d3cuemhpaHUuY29tL3NlYXJjaD90eXBlPWNvbnRlbnQmcT0lRTYlQkIlQjQlRTYlQkIlQjQ=
加密定位
需要分析的請(qǐng)求是下面這個(gè)

aHR0cHM6Ly93d3cuemhpaHUuY29tL3NlYXJjaD90eXBlPWNvbnRlbnQmcT0lRTYlQkIlQjQlRTYlQkIlQjQ= 需要分析的請(qǐng)求是下面這個(gè)今日網(wǎng)站
加密定位
這個(gè)請(qǐng)求的 header 中帶有加密的參數(shù) x-zse-96
我們今天就是要分析這個(gè)參數(shù)的生成邏輯
簡(jiǎn)單的請(qǐng)求定位有三個(gè)方法,之前講過(guò)了,可以找找之前的文章看看
這個(gè) header 加密參數(shù)的名字比較特殊,我們可以直接全局檢索這個(gè)名字的來(lái)定位參數(shù)
檢索的結(jié)果如下
只有一個(gè)結(jié)果,在結(jié)果里再次檢索有兩個(gè)結(jié)果
分別是
全部打上斷點(diǎn),然后刷新請(qǐng)求可以看到斷點(diǎn)斷在下面這個(gè)位置
說(shuō)明我們定位這個(gè)值賦值的位置了,接下來(lái)可以繼續(xù)分析他的邏輯了。
上面我們找到了參數(shù)賦值的位置,接下來(lái)要看看怎么生成這個(gè)參數(shù)
由頁(yè)面可以知道,這個(gè)參數(shù)的加密邏輯是這樣的
T = (0,i.default)(t, b.body, {zse93: m,dc0: y,xZst81: E}); _ = T.signature; v.set("x-zse-96", "2.0_" + _);
我們把斷點(diǎn)打在 T 上看看,我們需要的是 T.signature
目前未知的參數(shù)/方法有 t 、 i.default 、 b.body 、 m 、 y 、 E
下面一個(gè)一個(gè)分析
這里可以看到 y 是一串加密的亂碼
var y = (0,r.getDC0Cookie)()
進(jìn)一步分析可以得到, y 是當(dāng)前的 cookie 中 key 為`d_c0的值
參數(shù) t 是當(dāng)前請(qǐng)求的 url
參數(shù) m 是固定值
O = o.ZSE_83_VERSION.webm = u + "_" + O
參數(shù) E 的值是個(gè) null , b.body 是個(gè) undefine
接下來(lái)就只剩下 i.default 未知了,所以單步進(jìn)去分析可以看到在這個(gè) i.default 方法中最終返回了 signature ,這個(gè) signature 就是我們需要的加密值
這個(gè) signature 的邏輯如下
signature = (0,o.default)((0,r.default)(d))
這里傳入的 d 就是上面的參數(shù)拼接起來(lái)的
這里又多了兩個(gè)未知的方法, o.default 與 r.default
先看看第一個(gè)方法 r.default
單步進(jìn)去的邏輯如下
function m(e, t, n) {return t ? n ? O(t, e) : h(O(t, e)) : n ? v(e) : h(v(e))}
這里是一些三元表達(dá)式,最終返回的是 h(v(e))
這個(gè)方法比較簡(jiǎn)單的,其實(shí)就是將上面的 d 取 md5 hash 的操作
得到 r.default 的結(jié)果后傳入 o.default
進(jìn)入的是下面這個(gè)邏輯
var b = function(e) {return __g._encrypt(encodeURIComponent(e))};
這里用到了 r() 方法
分析這個(gè)方法我們可以自己慢慢把全部的邏輯摳出來(lái),也可以像我一樣把這個(gè) js 文件復(fù)制到本地,會(huì)發(fā)現(xiàn)全部的邏輯都在一個(gè) function 中。
把這段代碼拿到瀏覽器中運(yùn)行
是可以正常得到結(jié)果的,那我們要把這個(gè)代碼在 node 中運(yùn)行看看
在 node 里運(yùn)行結(jié)果我改了改了,保證他可以運(yùn)行不報(bào)錯(cuò)
首先直接將代碼復(fù)制過(guò)來(lái)運(yùn)行是會(huì)報(bào)錯(cuò)的
簡(jiǎn)單修改下,聲明 window ,并把最后的 exports 修改為 window.exports
修改后調(diào)用發(fā)現(xiàn)報(bào)錯(cuò) atob 未定義
這個(gè)應(yīng)該大家都會(huì)吧,其實(shí)就是 base64,補(bǔ)的方法有很多種
方法 1 :
_keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";function _utf8_encode (string) { var string = string.replace(/\r\n/g,"\n"); var utftext = ""; for (var n = 0; n < string.length; n++) { var c = string.charCodeAt(n); if (c < 128) { utftext += String.fromCharCode(c); } else if((c > 127) && (c < 2048)) { utftext += String.fromCharCode((c >> 6) | 192); utftext += String.fromCharCode((c & 63) | 128); } else { utftext += String.fromCharCode((c >> 12) | 224); utftext += String.fromCharCode(((c >> 6) & 63) | 128); utftext += String.fromCharCode((c & 63) | 128); } } return utftext;}function _utf8_decode (utftext) { var string = ""; var i = 0; var c = 0; var c1 = 0; var c2 = 0; var c3 = 0; while ( i < utftext.length ) { c = utftext.charCodeAt(i); if (c < 128) { string += String.fromCharCode(c); i++; } else if((c > 191) && (c < 224)) { c2 = utftext.charCodeAt(i+1); string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); i += 2; } else { c2 = utftext.charCodeAt(i+1); c3 = utftext.charCodeAt(i+2); string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); i += 3; } } return string;}var xazxBase64 = { 'decode': function (input){ output = ""; var chr1, chr2, chr3; var enc1, enc2, enc3, enc4; i = 0; input = input.replace(/[^A-Za-z0-9+\/=]/g, ""); while (i < input.length) { enc1 = _keyStr.indexOf(input.charAt(i++)); enc2 = _keyStr.indexOf(input.charAt(i++)); enc3 = _keyStr.indexOf(input.charAt(i++)); enc4 = _keyStr.indexOf(input.charAt(i++)); chr1 = (enc1 << 2) | (enc2 >> 4); chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); chr3 = ((enc3 & 3) << 6) | enc4; output = output + String.fromCharCode(chr1); if (enc3 !== 64) { output = output + String.fromCharCode(chr2); } if (enc4 !== 64) { output = output + String.fromCharCode(chr3); } } output = _utf8_decode(output); return output; }, 'encode': function (input){ output = ""; var chr1, chr2, chr3, enc1, enc2, enc3, enc4; i = 0; input = _utf8_encode(input); while (i < input.length) { chr1 = input.charCodeAt(i++); chr2 = input.charCodeAt(i++); chr3 = input.charCodeAt(i++); enc1 = chr1 >> 2; enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); enc4 = chr3 & 63; if (isNaN(chr2)) { enc3 = enc4 = 64; } else if (isNaN(chr3)) { enc4 = 64; } output = output + _keyStr.charAt(enc1) + _keyStr.charAt(enc2) + _keyStr.charAt(enc3) + _keyStr.charAt(enc4); } return output; }};
方法 2 :
global.Buffer = global.Buffer || require('buffer').Buffer;if (typeof btoa === 'undefined') { global.btoa = function (str) { return new Buffer.from(str, 'binary').toString('base64'); }; }if (typeof atob === 'undefined') { global.atob = function (b64Encoded) { return new Buffer.from(b64Encoded, 'base64').toString('binary'); }; }
方法 3 :
var atob = function(r) { e = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; var o = String(r).replace(/=+$/, ""); if (o.length % 4 == 1) throw new t("'atob' failed: The string to be decoded is not correctly encoded."); for (var n, a, i = 0, c = 0, d = ""; a = o.charAt(c++); ~a && (n = i % 4 ? 64 * n + a : a, i++ % 4) ? d += String.fromCharCode(255 & n >> (-2 * i & 6)) : 0) a = e.indexOf(a); return d }
這個(gè)是網(wǎng)上流傳最多的版本,其實(shí)也沒(méi)有毛病,直接用 jsdom 套個(gè)環(huán)境就完事了
使用方法也非常簡(jiǎn)單
npm install jsdom
在代碼開頭加上下面的代碼
const jsdom = require("jsdom"); const { JSDOM } = jsdom; const dom = new JSDOM(`<!DOCTYPE html><p>Hello world</p>`); window = dom.window; document = window.document; XMLHttpRequest = window.XMLHttpRequest;
直接運(yùn)行可以得到下面的結(jié)果
# 輸入值127927b6d4c1814afa22cdea9c7d7be9# 正確結(jié)果aHt0c6Lyn9Ox28S8K0OqNJuqb0FYoXYBG8F0b7uySRYf# jsdom的結(jié)果aHt0c6Lyn9Ox28S8K0OqNJuqb0FYoXYBG8F0b7uySRYf
如果要使用 node 生成正確的加密 結(jié)果
推薦采用方法 2,可以直接得到近似結(jié)果,但是結(jié)果是多了最后的 4 位,偷懶一點(diǎn)直接截掉后四位就行了
# 輸入值c06829267e17d3941f5c4cf33db9d509# 正確結(jié)果aHt0c6Lyn9Ox28S8K0OqNJuqb0FYoXYBG8F0b7uySRYf# 我們自己的結(jié)果aHt0c6Lyn9Ox28S8K0OqNJuqb0FYoXYBG8F0b7uySRYf9Tuw# 截掉后四位就完事了
想知道一步到位的方法就需要一點(diǎn)點(diǎn)分析分析他的加密了
接下來(lái)主要會(huì)告訴你分析插樁的點(diǎn)在哪里
先看加密的入口
__g._encrypt(encodeURIComponent(e))
這里的 __g._encrypt 是 r()
r 是在下面這里調(diào)用的
這里用到了 o.v 這里的 o.v 是由 new G.v 生成的
就是代碼里的一長(zhǎng)串 base64 編碼
傳入這一串編碼之后就在 G.prototype.D 和 G.prototype.v 來(lái)回跳轉(zhuǎn),并且在這兩個(gè)方法做一些判斷,移位的操作最后生成最后的結(jié)果
能插樁看到信息的點(diǎn)在哪里呢?
全局檢索 var k
在這里把 charCodeAt 的結(jié)果打印出來(lái),得到的結(jié)果如下
__g _encryptwindowundefinedwindownavigatorObjectname nodejs userAgent headless userAgent toLowerCase indexOf callPhantom _phantom __phantomas buffer Buffer emit spawn webdriver domAutomation domAutomationController getOwnPropertyDescriptor userAgent getOwnPropertyDescriptor webdriver getOwnPropertyDescriptor [native code] getOwnPropertyDescriptorFunctionprototype toString call indexOf length RuPtXwxpThIZ0qyz_9fYLCOV8B1mMGKs7UnFHgN3iDaWAJE-Qrk2ecSo6bjd4vl5 length charCodeAt .. charAt ... charCodeAt .. charAt ... charCodeAt .. charAt ... charCodeAt ... charAt ... charCodeAt ... charAt ... charCodeAt ... charAt ... charCodeAt ... charAt ... charCodeAt ... charAt ... charCodeAt ... charAt ... charCodeAt ... charAt ... charCodeAt ... charAt ...
除了這上面一個(gè)點(diǎn)外就是打印的 this.C 的值
這兒的值可以觀察到是第幾次循環(huán)遍歷,有需要注意的值自己記錄下來(lái),下次自己加個(gè)判斷 debugger 直接斷在這個(gè)位置
除了上面的兩個(gè)位置外還有兩個(gè)位置需要注意,不需要斷點(diǎn)
在分析的時(shí)候要知道有這個(gè)邏輯存在
第一個(gè)是 eval 代碼在的地方,這里會(huì)執(zhí)行代碼,那么執(zhí)行的代碼就需要注意了
就像開頭的 __g 還有 window 還有 navigator 這些都經(jīng)過(guò)了這里
如果生成的值和頁(yè)面生成的值完全不一樣或者干脆就得出一個(gè)空串,恭喜你,這才是我寫這個(gè)文章的目的。
知乎我一直以為是沒(méi)有環(huán)境檢測(cè)的,特別是我用上面的代碼跑出了近乎一樣的代碼的時(shí)候我感覺(jué)這個(gè)加密也太簡(jiǎn)單了,之后當(dāng)我認(rèn)真研究的時(shí)候,我發(fā)現(xiàn)是我天真了。
第一個(gè)是運(yùn)算得出的代碼結(jié)果完全不一樣
你需要關(guān)注的是 this.C 的值是 99 的時(shí)候
“
就是遍歷到第 99 次的時(shí)候你可以打印 this.C 的值確認(rèn)位置
會(huì)檢測(cè) window 對(duì)象是否有 Buffer 這個(gè)方法,沒(méi)有的話就會(huì)跳過(guò) 101-105 ,直接將 this.C 賦值為 106
如果 window 中有 Buffer 對(duì)象會(huì)順序執(zhí)行 101-105 這幾個(gè)步驟中會(huì)給傳入的 hash 值的前方加上一個(gè)字符,這樣傳入的值都不一樣了,得到的結(jié)果當(dāng)然也不一樣了
還有一個(gè)是輸出是個(gè)空值,這個(gè)檢測(cè)的比較多了
需要關(guān)注的點(diǎn)是這個(gè)方法代碼檢測(cè)了方法的 toString 還有 getOwnPropertyDescriptor
這個(gè)檢測(cè)的比較寬泛,在 this.C 等于 106-199 之間都有
如果你嫌麻煩的話直接使用上面提示可以生成值的代碼直接跑就可以了
聯(lián)系客服