Python 爬蟲進(jìn)階必備 | Js 逆向之補(bǔ)環(huán)境到底是在補(bǔ)什么?
第一時間關(guān)注Python技術(shù)干貨!

序言
之前我就發(fā)過一篇文章,提了一嘴關(guān)于我理解的爬蟲的本質(zhì)

雖然當(dāng)時的主題寫的是 App 爬蟲,不過并不妨礙的我們理解爬蟲。
今天寫的 Js 逆向之補(bǔ)環(huán)境,就可以理解是在 Js 環(huán)境下精進(jìn)我們的 " 騙術(shù) "
正文
大家在看文章之前應(yīng)該都清楚,Node 環(huán)境和瀏覽器環(huán)境是完全不同的,平臺有很多的檢測點(diǎn)可以發(fā)現(xiàn)我們是在瀏覽器運(yùn)行 Js 還是在 Node 環(huán)境下運(yùn)行 Js
補(bǔ)環(huán)境做的就是盡可能根據(jù)網(wǎng)頁上的 Js 完善本地的 Node 環(huán)境,讓 Js 運(yùn)行在 Node 中像瀏覽器一樣正常運(yùn)行,最高境界當(dāng)然是 Js 拿來套上環(huán)境就跑,不過可以說是道阻且長
。
window
最基本的補(bǔ)環(huán)境是公眾號早期的文章,例如下面的幾篇文章
實(shí)戰(zhàn)案例淺析JS加密 - DES與Base64
實(shí)戰(zhàn)案例淺析JS加密 - RSA與XXTEA
這些個文章中大家經(jīng)常遇到的是
'window' is not defined
那像這樣的報錯提示應(yīng)該如何處理?
Node 環(huán)境下一般如下定義
window = global;
如果只是單單缺少了window這一個變量的定義,像上面這樣報錯自然就消失了。
document
除了window之外,我們經(jīng)常還遇到類似下面這些文章中的情況
Python 爬蟲進(jìn)階必備 | 某采購網(wǎng)站 cookie 加密分析(仿加速樂)
Python 爬蟲進(jìn)階必備 | 某常見 cookie 加密算法邏輯分析 (加速樂 - jsl)
這些網(wǎng)站我們一般稱之為加速樂,因?yàn)闃?biāo)志性的 cookie 參數(shù)是以jsl開頭,而這種類型的加密一般操縱的是document.cookie這個參數(shù)
那像這樣的document應(yīng)該怎么補(bǔ)?
在沒有檢測只是為了能讓 js 運(yùn)行不報錯的情況下,我是這樣寫的
var document = {
cookie:"xxxxxx"
}
或者像下面這樣寫

那么這樣寫就一定保險嗎?
不一定。開頭就已經(jīng)提及,要根據(jù)網(wǎng)頁上的 Js 完善本地的 Node 環(huán)境,所以只要網(wǎng)頁上的 Js 不檢測我們這么寫也沒毛病
那么檢測的 Js 長什么樣?
Object.getOwnPropertyDescriptor
這里可以參考之前寫的關(guān)于某乎的分析文章
Python 爬蟲進(jìn)階必備 | 某著名人均百萬問答社區(qū) header 參數(shù)加密邏輯分析
這里Header中的x-zse-96的加密邏輯之前看過文章的,通過插樁調(diào)試應(yīng)該可以看到下面這樣的代碼

這個東西是個啥?
官方定義是這樣的
“
Object.getOwnPropertyDescriptor()方法返回指定對象上一個自有屬性對應(yīng)的屬性描述符。(自有屬性指的是直接賦予該對象的屬性,不需要從原型鏈上進(jìn)行查找的屬性)
通過描述有點(diǎn)晦澀,你可以這樣理解,如果是自己構(gòu)造的對象,例如
var navigator = {
platform:"win32"
}
就沒辦法通過這樣的檢測
這里是檢測對象的屬性是不是自己賦值給他的
這里打印下剛剛的例子看看有啥不一樣的地方
先是我們自己寫的例子
var navigator1 = {
platform:"win32"
}

再看看瀏覽器里面是啥樣的

所以對于這樣的代碼檢測,我們應(yīng)該如何構(gòu)造呢?
這里站在前人的肩膀上,寫一下
var Navigator = function() {};
Navigator.prototype = {"platform": "win32"};
navigator = new Navigator();
# 只針對檢測 Navigator 原型鏈的寫法
這樣就可以通過上面的原型鏈檢測了
這里的platform是Navigator上的屬性,在使用 new 實(shí)例化后,navigator.platform 取到的是繼承自Navigator的屬性值,而不是直接賦予該對象的屬性,所以得到的結(jié)果和瀏覽器是一樣的。
這樣看是不是很簡單,但是像某乎的校驗(yàn)用到了很多次getOwnPropertyDescriptor,就需要一個個插樁調(diào)試他檢測了什么對象的什么屬性,相當(dāng)惡心。
那么又回到上面的代碼,這里用到的prototype又是個啥?
prototype 與 _ _ proto _ _
首先先上一張圖

是不是很懵逼,懵逼就對了,我們是搞爬蟲的,知道個大概意思就行了。
我們需要知道的是 prototype 與 _ _ proto _ _ 咋對應(yīng)起來就行了
按照官方的說法
“在JS里,萬物皆對象。方法(Function)是對象,方法的原型(Function.prototype)是對象。因此,它們都會具有對象共有的特點(diǎn)。即:對象具有屬性_ _ proto _ _,可稱為隱式原型,一個對象的隱式原型指向構(gòu)造該對象的構(gòu)造函數(shù)的原型,這也保證了實(shí)例能夠訪問在構(gòu)造函數(shù)原型中定義的屬性和方法。
方法(Function)這個特殊的對象,除了和其他對象一樣有上述_proto_屬性之外,還有自己特有的屬性——原型屬性(prototype),這個屬性是一個指針,指向一個對象,這個對象的用途就是包含所有實(shí)例共享的屬性和方法(我們把這個對象叫做原型對象)。原型對象也有一個屬性,叫做constructor,這個屬性包含了一個指針,指回原構(gòu)造函數(shù)。
理解大概的意思(別被搞暈了)可以得出下面這行代碼
xxx.__proto__ == yyy.prototype
這里我們需要用到谷歌瀏覽器驗(yàn)證一下我們的想法

有一定基礎(chǔ)的同學(xué)知道,瀏覽器里 window 是由 Window 實(shí)例而來的,那么 Window 是怎么來的?
我們在瀏覽器的控制臺里看看


可以看到原來我們之前以為簡簡單單就構(gòu)造出來的 window 和 navigator 這么復(fù)雜
這樣一看像我們上面直接使用var定義的方法去補(bǔ)環(huán)境就很容易被識別
例如
var window1 = {
bbb:"xxxx"
};
# 在瀏覽器里沒法定義 window 這里用 window1 做個樣子

這里window1的結(jié)果和我們上面瀏覽器中window的輸出結(jié)果大相徑庭。
不僅僅是window,包括document以及navigator等等囊括DOM、BOM、worker、網(wǎng)絡(luò)請求這些的方方面面都需要我們一個一個分析他的__proto__以及屬性是定義在自己的prototype上還是繼承自上一層。
這里就又涉及了關(guān)于上述各類的繼承關(guān)系,這里分享我找到的一張DOM中各類的繼承關(guān)系圖,希望對大家補(bǔ)環(huán)境有所幫助

總結(jié)
這篇文章我的定義并不是關(guān)于補(bǔ)環(huán)境的總綱或者是總述,只能說是掀起了補(bǔ)環(huán)境神秘面紗的一角,希望大家多多交流,不斷完善自己的環(huán)境框架,做到一鍵通殺~
還有就是關(guān)于文章中如果描述不準(zhǔn)確的地方,歡迎在留言區(qū)指正,大家共同進(jìn)步。
以上就是本次的全部內(nèi)容了,咱們下次再會~
Peace and Love
