免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
JavaScript 逆向爬取實(shí)戰(zhàn)(下)

閱讀本文大概需要 20 分鐘。

這一篇是 JavaScript 逆向爬取的第二篇。那么接下來我為大家縷順一下學(xué)習(xí)順序。

系列文章的第一篇啟于總結(jié)一些網(wǎng)站加密和混淆技術(shù),這篇文章我們介紹了網(wǎng)頁防護(hù)技術(shù),包括接口加密和 JavaScript 壓縮、加密和混淆。能夠?yàn)閷W(xué)習(xí) JavaScript 逆向爬取奠定堅(jiān)實(shí)的基礎(chǔ)。

接下來就是 JavaScript 逆向爬取的第一篇JavaScript 逆向爬取實(shí)戰(zhàn)。分為上下章發(fā)出是因?yàn)榇_實(shí)寫得太長了(手動(dòng)狗頭)。

那么話不多說,我們開始今天的學(xué)習(xí)吧~

詳情頁加密 id 入口的尋找

好,那么我們觀察下上一步的輸出結(jié)果,我們把結(jié)果格式化一下,看看部分結(jié)果:

{ 'count': 100, 'results': [ { 'id': 1, 'name': '霸王別姬', 'alias': 'Farewell My Concubine', 'cover': 'https://p0.meituan.net/movie/ce4da3e03e655b5b88ed31b5cd7896cf62472.jpg@464w_644h_1e_1c', 'categories': [ '劇情', '愛情' ], 'published_at': '1993-07-26', 'minute': 171, 'score': 9.5, 'regions': [ '中國大陸', '中國香港' ] }, ... ]}

這里我們看到有個(gè) id 是 1,另外還有一些其他的字段如電影名稱、封面、類別等等,那么這里面一定有什么信息是用來唯一區(qū)分某個(gè)電影的。

但是呢,這里我們點(diǎn)擊下第一個(gè)部電影的信息,可以看到它跳轉(zhuǎn)到了 URL 為 https://dynamic6.scrape.cuiqingcai.com/detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWIx 的頁面,可以看到這里 URL 里面有一個(gè)加密 id 為 ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWIx,那么這個(gè)和電影的這些信息有什么關(guān)系呢?

這里,如果你仔細(xì)觀察規(guī)律其實(shí)是可以比較容易地找出規(guī)律來的,但是這總歸是觀察出來的,如果遇到一些觀察不出規(guī)律的那就歇菜了。所以還是需要靠技巧去找到它真正加密的位置。

這時(shí)候我們該怎么辦呢?

分析一下,這個(gè)加密 id 到底是什么生成的。

我們在點(diǎn)擊詳情頁的時(shí)候就看到它訪問的 URL 里面就帶上了 ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWIx 這個(gè)加密 id 了,而且不同的詳情頁的加密 id 是不同的,這說明這個(gè)加密 id 的構(gòu)造依賴于列表頁 Ajax 的返回結(jié)果,所以可以確定這個(gè)加密 id 的生成是發(fā)生在 Ajax 請求完成后或者點(diǎn)擊詳情頁的一瞬間。

那為了進(jìn)一步確定是發(fā)生在何時(shí),我們看看頁面源碼,可以看到在沒有點(diǎn)擊之前,詳情頁鏈接的 href 里面就已經(jīng)帶有加密 id 了,如圖所示。

由此我們可以確定,這個(gè)加密 id 是在 Ajax 請求完成之后生成的,而且肯定也是由 JavaScript 生成的了。

那怎么再去找 Ajax 完成之后的事件呢?是否應(yīng)該去找 Ajax 完成之后的事件呢?

可以是可以,可以試試,我們可以在 Sources 面板的右側(cè),有一個(gè) Event Listener Breakpoints,這里有一個(gè) XHR 的監(jiān)聽,包括發(fā)起時(shí)、成功后、發(fā)生錯(cuò)誤時(shí)的一些監(jiān)聽,這里我們勾選上 readystatechange 事件,代表 Ajax 得到響應(yīng)時(shí)的事件,其他的斷點(diǎn)可以都刪除了,然后刷新下頁面看下,如圖所示。

這里我們可以看到就停在了 Ajax 得到響應(yīng)時(shí)的位置了。

那這里我們怎么再去找這個(gè) id 怎么加密的呢?這里可以選擇一點(diǎn)點(diǎn)斷點(diǎn)找下去,但估計(jì)找的過程會崩潰掉,因?yàn)檫@里可能會會逐漸調(diào)用到頁面 UI 渲染的一些底層實(shí)現(xiàn),甚至可能找著找著都不知道找到哪里去了。

那怎么辦呢?這里我們再介紹一種定位的方法,那就是 Hook。

Hook 技術(shù)中文又叫做鉤子技術(shù),它就是在程序運(yùn)行的過程中,對其中的某個(gè)方法進(jìn)行重寫,在原先的方法前后加入我們自定義的代碼。相當(dāng)于在系統(tǒng)沒有調(diào)用該函數(shù)之前,鉤子程序就先捕獲該消息,鉤子函數(shù)先得到控制權(quán),這時(shí)鉤子函數(shù)既可以加工處理(改變)該函數(shù)的執(zhí)行行為。

通俗點(diǎn)來說呢,比如我要 Hook 一個(gè)方法 a,我可以先臨時(shí)用一個(gè)變量存一下,把它存成 _a,然后呢,我再重新聲明一個(gè)方法 a,里面加點(diǎn)自己的邏輯,比如加點(diǎn)調(diào)試語句、輸出語句等等,然后再調(diào)用下 _a,這里調(diào)用的 _a 就是之前的 a。那這樣就相當(dāng)于新的方法 a 里面混入了我們自己定義的邏輯,同時(shí)又把原來的方法 a 也執(zhí)行了一遍。所以這不會影響原有的執(zhí)行邏輯和運(yùn)行效果,但是我們通過這種改寫就順利在原來的 a 方法前后加上了我們自己的邏輯,這就是 Hook。

那么,我們這里怎么用 Hook 的方式來找到加密 id 的加密入口點(diǎn)呢?

想一下,這個(gè)加密 id 是一個(gè) Base64 編碼的字符串,那么生成過程中想必就調(diào)用了 JavaScript 的 Base64 編碼的方法,這個(gè)方法名叫做 btoa,這個(gè) btoa 方法可以將參數(shù)轉(zhuǎn)化成 Base64 編碼。當(dāng)然 Base64 也有其他的實(shí)現(xiàn)方式,比如利用 crypto-js 這個(gè)庫實(shí)現(xiàn)的,這個(gè)可能底層調(diào)用的就不是 btoa 方法了。

所以,我們其實(shí)現(xiàn)在并不確定是不是調(diào)用的 btoa 方法實(shí)現(xiàn)的 Base64 編碼,那就先試試吧。

要實(shí)現(xiàn) Hook,其實(shí)關(guān)鍵在于將原來的方法改寫,這里我們其實(shí)就是 Hook btoa 這個(gè)方法了,btoa 這個(gè)方法屬于 window 對象,我們將 window 對象的 btoa 方法進(jìn)行改寫即可。

改寫的邏輯如下:

(function () {    'use strict'    function hook(object, attr) {        var func = object[attr]        object[attr] = function () {            console.log('hooked', object, attr, arguments)            var ret = func.apply(object, arguments)            debugger            console.log('result', ret)            return ret        }    }    hook(window, 'btoa')})()

我們定義了一個(gè) hook 方法,傳入 object 和 attr 參數(shù),意思就是 Hook object 對象的 attr 參數(shù)。例如我們?nèi)绻?Hook 一個(gè) alert 方法,那就把 object 設(shè)置為 window,把 attr 設(shè)置為 alert 字符串。這里我們想要 Hook Base64 的編碼方法,那么這里我們就只需要 Hook window 對象的 btoa 方法就好了。

我們來看下,首先一句 var func = object[attr],相當(dāng)于我們先把它賦值為一個(gè)變量,我們調(diào)用 func 方法就可以實(shí)現(xiàn)和原來相同的功能。接著,我們再直接改寫這個(gè)方法的定義,直接改寫 object[attr],將其改寫成一個(gè)新的方法,在新的方法中,通過 func.apply 方法又重新調(diào)用了原來的方法。這樣我們就可以保證,前后方法的執(zhí)行效果是不受什么影響的,之前這個(gè)方法該干啥就還是干啥的。但是和之前不同的是,我們自定義方法之后,現(xiàn)在可以在 func 方法執(zhí)行的前后,再加入自己的代碼,如 console.log 將信息輸出到控制臺,如 debugger 進(jìn)入斷點(diǎn)等等。這個(gè)過程中,我們先臨時(shí)保存下來了 func 方法,然后定義一個(gè)新的方法,接管程序控制權(quán),在其中自定義我們想要的實(shí)現(xiàn),同時(shí)在新的方法里面再重新調(diào)回 func 方法,保證前后結(jié)果是不受影響的。所以,我們達(dá)到了在不影響原有方法效果的前提下,可以實(shí)現(xiàn)在方法的前后實(shí)現(xiàn)自定義的功能,就是 Hook 的完整實(shí)現(xiàn)過程。

最后,我們調(diào)用 hook 方法,傳入 window 對象和 btoa 字符串即可。

那這樣,怎么去注入這個(gè)代碼呢呢?這里我們介紹三種注入方法。

·直接控制臺注入·復(fù)寫 JavaScript 代碼·Tampermonkey 注入

控制臺注入

對于我們這個(gè)場景,控制臺注入其實(shí)就夠了,我們先來介紹這個(gè)方法。

這個(gè)其實(shí)很簡單了,就是直接在控制臺輸入這行代碼運(yùn)行,如圖所示。

執(zhí)行完這段代碼之后,相當(dāng)于我們就已經(jīng)把 window 的 btoa 方法改寫了,可以控制臺調(diào)用下 btoa 方法試試,如:

btoa('germey')

回車之后就可以看到它進(jìn)入了我們自定義的 debugger 的位置停下了,如圖所示。

我們把斷點(diǎn)向下執(zhí)行,點(diǎn)擊 Resume 按鈕,然后看看控制臺的輸出,可以看到也輸出了一些對應(yīng)的結(jié)果,如被 Hook 的對象,Hook 的屬性,調(diào)用的參數(shù),調(diào)用后的結(jié)果等,如圖所示。

那這里我們就可以看到,我們通過 Hook 的方式改寫了 btoa 方法,使其每次在調(diào)用的時(shí)候都能停到一個(gè)斷點(diǎn),同時(shí)還能輸出對應(yīng)的結(jié)果。

好,那接下來怎么用 Hook 找到對應(yīng)的加密 id 的加密入口呢?

由于此時(shí)我們是在控制臺直接輸入的 Hook 代碼,所以頁面一旦刷新就無效了,但由于我們這個(gè)網(wǎng)站是 SPA 式的頁面,所以在點(diǎn)擊詳情頁的時(shí)候頁面是不會整個(gè)刷新的,所以這段代碼依然還會生效。但是如果不是 SPA 式的頁面,即每次訪問都需要刷新頁面的網(wǎng)站,這種注入方式就不生效了。

好,那我們的目的是為了 Hook 列表頁 Ajax 加載完成后的的加密 id 的 Base64 編碼的過程,那怎么在不刷新頁面的情況下再次復(fù)現(xiàn)這個(gè)操作呢?很簡單,點(diǎn)下一頁就好了。

這時(shí)候我們可以點(diǎn)擊第 2 頁的按鈕,這時(shí)候可以看到它確實(shí)再次停到了 Hook 方法的 debugger 處,由于列表頁的 Ajax 和加密 id 都會帶有 Base64 編碼的操作,因此它每一個(gè)都能 Hook 到,通過觀察對應(yīng)的 Arguments 或當(dāng)前網(wǎng)站的行為或者觀察棧信息,我們就能大體知道現(xiàn)在走到了哪個(gè)位置了,從而進(jìn)一步通過棧的調(diào)用信息找到調(diào)用 Base64 編碼的位置。

我們可以根據(jù)調(diào)用棧的信息來觀察這些變量在哪一層發(fā)生變化的,比如最后的這一層,我們可以很明顯看到它執(zhí)行了 Base64 編碼,編碼前的結(jié)果是:

ef34#teuq0btua#(-57w1q5o5--j@98xygimlyfxs*-!i-0-mb1

編碼后的結(jié)果是:

ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWIx

如圖所示。

這里很明顯。

那么核心問題就來了,編碼前的結(jié)果 ef34#teuq0btua#(-57w1q5o5--j@98xygimlyfxs*-!i-0-mb1又是怎么來的呢?我們展開棧的調(diào)用信息,一層層看看這個(gè)字符串的變化情況。如果不變那就看下一層,如果變了那就停下來仔細(xì)看看。

最后我們可以在第五層找到它的變化過程,如圖所示。

那這里我們就一目了然了,看到了 _0x135c4d 是一個(gè)寫死的字符串 ef34#teuq0btua#(-57w1q5o5--j@98xygimlyfxs*-!i-0-mb,然后和傳入的這個(gè) _0x565f18 拼接起來就形成了最后的字符串。

那這個(gè) _0x565f18 又是怎么來的呢?再往下追一層,那就一目了然了,其實(shí)就是 Ajax 返回結(jié)果的單個(gè)電影信息的 id。

所以,這個(gè)加密邏輯的就清楚了,其實(shí)非常非常簡單,就是 ef34#teuq0btua#(-57w1q5o5--j@98xygimlyfxs*-!i-0-mb1 加上電影 id,然后 Base64 編碼即可。

到此,我們就成功用 Hook 的方式找到加密的 id 生成邏輯了。

但是想想有什么不太科學(xué)的地方嗎?剛才其實(shí)也說了,我們的 Hook 代碼是在控制臺手動(dòng)輸入的,一旦刷新頁面就不生效了,這的確是個(gè)問題。而且它必須是在頁面加載完了才注入的,所以它并不能在一開始就生效。

下面我們再介紹幾種 Hook 注入方式

重寫 JavaScript

我們可以借助于 Chrome 瀏覽器的 Overrides 功能實(shí)現(xiàn)某些 JavaScript 文件的重寫和和保存,它會在本地生成一個(gè) JavaScript 文件副本,以后每次刷新的時(shí)候會使用副本的內(nèi)容。

這里我們需要切換到 Sources 選項(xiàng)卡的 Overrides 選項(xiàng)卡,然后選擇一個(gè)文件夾,比如這里我自定了一個(gè)文件夾名字叫做 modify,如圖所示。

然后我們隨便選一個(gè) JavaScript 腳本,后面貼上這段注入腳本,如圖所示。

保存文件。

此時(shí)可能提示頁面崩潰,但是不用擔(dān)心,重新刷新頁面就好了,這時(shí)候我們就發(fā)現(xiàn)現(xiàn)在瀏覽器加載的 JavaScript 文件就是我們修改過后的了,文件的下方會有一個(gè)標(biāo)識符,如圖所示。

同時(shí)我們還注意到這時(shí)候它就直接進(jìn)入了斷點(diǎn)模式,成功 Hook 到了 btoa 這個(gè)方法了。

其實(shí)這個(gè) Overrides 這個(gè)功能非常有用,有了它我們可以持久化保存我們?nèi)我庑薷牡?JavaScript 代碼,所以我們想在哪里改都可以了,甚至可以直接修改 JavaScript 的原始執(zhí)行邏輯也都是可以的。

Tampermonkey 注入

如果我們不想用 Overrides 的方式改寫 JavaScript 的方式注入的話,還可以借助于瀏覽器插件來實(shí)現(xiàn)注入,這里推薦的瀏覽器插件叫做 Tampermonkey,中文叫做油猴。它是一款瀏覽器插件,支持 Chrome。利用它我們可以在瀏覽器加載頁面時(shí)自動(dòng)執(zhí)行某些 JavaScript 腳本。由于執(zhí)行的是 JavaScript,所以我們幾乎可以在網(wǎng)頁中完成任何我們想實(shí)現(xiàn)的效果,如自動(dòng)爬蟲、自動(dòng)修改頁面、自動(dòng)響應(yīng)事件等等。

首先我們需要安裝 Tampermonkey,這里我們使用的瀏覽器是 Chrome。直接在 Chrome 應(yīng)用商店或者在 Tampermonkey 的官網(wǎng) https://www.tampermonkey.net/ 下載安裝即可。

安裝完成之后,在 Chrome 瀏覽器的右上角會出現(xiàn) Tampermonkey 的圖標(biāo),這就代表安裝成功了。

我們也可以自己編寫腳本來實(shí)現(xiàn)想要的功能。編寫腳本難不難呢?其實(shí)就是寫 JavaScript 代碼,只要懂一些 JavaScript 的語法就好了。另外除了懂 JavaScript 語法,我們還需要遵循腳本的一些寫作規(guī)范,這其中就包括一些參數(shù)的設(shè)置。

下面我們就簡單實(shí)現(xiàn)一個(gè)小的腳本,實(shí)現(xiàn)某個(gè)功能。

首先我們可以點(diǎn)擊 Tampermonkey 插件圖標(biāo),點(diǎn)擊「管理面板」按鈕,打開腳本管理頁面。

界面類似顯示如下圖所示。

在這里顯示了我們已經(jīng)有的一些 Tampermonkey 腳本,包括我們自行創(chuàng)建的,也包括從第三方網(wǎng)站下載安裝的。

另外這里也提供了編輯、調(diào)試、刪除等管理功能,我們可以方便地對腳本進(jìn)行管理。

接下來我們來創(chuàng)建一個(gè)新的腳本來試試,點(diǎn)擊左側(cè)的「+」號,會顯示如圖所示的頁面。

初始化的代碼如下:

// ==UserScript==// @name         New Userscript// @namespace    http://tampermonkey.net/// @version      0.1// @description  try to take over the world!// @author       You// @match        https://www.tampermonkey.net/documentation.php?ext=dhdg// @grant        none// ==/UserScript==
(function() { 'use strict';
// Your code here...})();

這里最上面是一些注釋,但這些注釋是非常有用的,這部分內(nèi)容叫做 UserScript Header ,我們可以在里面配置一些腳本的信息,如名稱、版本、描述、生效站點(diǎn)等等。

在 UserScript Header 下方是 JavaScript 函數(shù)和調(diào)用的代碼,其中 'use strict' 標(biāo)明代碼使用 JavaScript 的嚴(yán)格模式,在嚴(yán)格模式下可以消除 Javascript 語法的一些不合理、不嚴(yán)謹(jǐn)之處,減少一些怪異行為,如不能直接使用未聲明的變量,這樣可以保證代碼的運(yùn)行安全,同時(shí)提高編譯器的效率,提高運(yùn)行速度。在下方 // Your code here... 這里我們就可以編寫自己的代碼了。

我們可以將腳本改寫為如下內(nèi)容:

// ==UserScript==// @name HookBase64// @namespace https://scrape.cuiqingcai.com/// @version 0.1// @description Hook Base64 encode function// @author Germey// @match https://dynamic6.scrape.cuiqingcai.com/// @grant none// @run-at document-start// ==/UserScript==(function () { 'use strict' function hook(object, attr) { var func = object[attr] console.log('func', func) object[attr] = function () { console.log('hooked', object, attr) var ret = func.apply(object, arguments) debugger return ret } } hook(window, 'btoa')})()

這時(shí)候啟動(dòng)腳本,重新刷新頁面,可以發(fā)現(xiàn)也可以成功 Hook 住 btoa 方法,如圖所示。

然后我們再順著找調(diào)用邏輯就好啦。

以上,我們就成功通過 Hook 的方式找到加密 id 的實(shí)現(xiàn)了。

詳情頁 Ajax 的 token 尋找

現(xiàn)在我們已經(jīng)找到詳情頁的加密 id 了,但是還差一步,其 Ajax 請求也有一個(gè) token,如圖所示。

其實(shí)這個(gè) token 和詳情頁的 token 構(gòu)造邏輯是一樣的了。

這里就不再展開說了,可以運(yùn)用上文的幾種找入口的方法來找到對應(yīng)的加密邏輯。

Python 實(shí)現(xiàn)詳情頁爬取

現(xiàn)在我們已經(jīng)成功把詳情頁的加密 id 和 Ajax 請求的 token 找出來了,下一步就能使用 Python 完成爬取了,這里我就只實(shí)現(xiàn)第一頁的爬取了,代碼示例如下:

import hashlibimport timeimport base64from typing import List, Anyimport requests
INDEX_URL = 'https://dynamic6.scrape.cuiqingcai.com/api/movie?limit={limit}&offset={offset}&token={token}'DETAIL_URL = 'https://dynamic6.scrape.cuiqingcai.com/api/movie/{id}?token={token}'LIMIT = 10OFFSET = 0SECRET = 'ef34#teuq0btua#(-57w1q5o5--j@98xygimlyfxs*-!i-0-mb'

def get_token(args: List[Any]): timestamp = str(int(time.time())) args.append(timestamp) sign = hashlib.sha1(','.join(args).encode('utf-8')).hexdigest() return base64.b64encode(','.join([sign, timestamp]).encode('utf-8')).decode('utf-8')
args = ['/api/movie']token = get_token(args=args)index_url = INDEX_URL.format(limit=LIMIT, offset=OFFSET, token=token)response = requests.get(index_url)print('response', response.json())
result = response.json()for item in result['results']: id = item['id'] encrypt_id = base64.b64encode((SECRET + str(id)).encode('utf-8')).decode('utf-8') args = [f'/api/movie/{encrypt_id}'] token = get_token(args=args) detail_url = DETAIL_URL.format(id=encrypt_id, token=token) response = requests.get(detail_url) print('response', response.json())

這里模擬了詳情頁的加密 id 和 token 的構(gòu)造過程,然后請求了詳情頁的 Ajax 接口,這樣我們就可以爬取到詳情頁的內(nèi)容了。

總結(jié)

本節(jié)內(nèi)容很多,一步步介紹了整個(gè)網(wǎng)站的 JavaScript 逆向過程,其中的技巧有:

·全局搜索查找入口·代碼格式化·XHR 斷點(diǎn)·變量監(jiān)聽·斷點(diǎn)設(shè)置和跳過·棧查看·Hook 原理·Hook 注入·Overrides 功能·Tampermonkey 插件·Python 模擬實(shí)現(xiàn)

掌握了這些技巧我們就能更加得心應(yīng)手地實(shí)現(xiàn) JavaScript 逆向分析。

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
紅隊(duì)攻防之JS攻防
分析某個(gè)利用云存儲傳播有色視頻的網(wǎng)站(文末附免費(fèi)知識星球加入途徑)
Go語言中如何實(shí)現(xiàn)JWT
就想寫個(gè)爬蟲,我到底要學(xué)多少東西???
JavaScript代碼美化/壓縮/格式化/加密工具
線程知識總結(jié)
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服