![]() 文 | 崔慶才 出品 | 進(jìn)擊的Coder(ID:FightingCoder) 已獲得原公眾號(hào)的授權(quán)轉(zhuǎn)載 “ 現(xiàn)在越來越多的網(wǎng)站也已經(jīng)應(yīng)用了這些技術(shù)對(duì)其數(shù)據(jù)接口進(jìn)行了保護(hù),在做爬蟲時(shí)如果我們遇到了這種情況,我們可能就不得不硬著頭皮來去想方設(shè)法找出其中隱含的關(guān)鍵邏輯了,這個(gè)過程我們可以稱之為 JavaScript 逆向。 既然我們要做 JavaScript 逆向,那少不了要用到瀏覽器的開發(fā)者工具,因?yàn)榫W(wǎng)頁(yè)是在瀏覽器中加載的,所以多數(shù)的調(diào)試過程也是在瀏覽器中完成的。 工欲善其事,必先利其器。本節(jié)我們先來基于 Chrome 瀏覽器介紹一下瀏覽器開發(fā)者工具的使用。但由于開發(fā)者工具功能十分復(fù)雜,本節(jié)主要介紹對(duì) JavaScript 逆向有一些幫助的功能,學(xué)會(huì)了這些,我們?cè)谧?JavaScript 逆向調(diào)試的過程會(huì)更加得心應(yīng)手。 本節(jié)我們以一個(gè)示例網(wǎng)站 https://spa2./ 來做演示,用這個(gè)示例來介紹瀏覽器開發(fā)者工具各個(gè)面版的用法。 1. 面板介紹首先我們用 Chrome 瀏覽器打開示例網(wǎng)站,頁(yè)面如圖所示: 接下來打開開發(fā)者工具,我們會(huì)看到類似圖 xx 所示的結(jié)果。 這里可以看到多個(gè)面板標(biāo)簽,如 Elements、Console、Sources 等,這就是開發(fā)者工具的一個(gè)個(gè)面板,功能豐富而又強(qiáng)大,先對(duì)面板作下簡(jiǎn)單的介紹:
了解了這些面板之后,我們來深入了解幾個(gè)面板中對(duì) JavaScript 調(diào)試很有幫助的功能。 2. 查看節(jié)點(diǎn)事件之前我們是用 Elements 面板來審查頁(yè)面的節(jié)點(diǎn)信息的,我們可以查看當(dāng)前頁(yè)面的 HTML 源代碼及其在網(wǎng)頁(yè)中對(duì)應(yīng)的位置,查看某個(gè)條目的標(biāo)題對(duì)應(yīng)的頁(yè)面源代碼,如圖所示。 點(diǎn)擊右側(cè)的 Styles 選項(xiàng)卡,可以看到對(duì)應(yīng)節(jié)點(diǎn)的 CSS 樣式,我們可以自行在這里增刪樣式,實(shí)時(shí)預(yù)覽效果,這對(duì)網(wǎng)頁(yè)開發(fā)十分有幫助。 在 Computed 選項(xiàng)卡中還可以看到當(dāng)前節(jié)點(diǎn)的盒子模型,比如外邊距、內(nèi)邊距等,還可以看到當(dāng)前節(jié)點(diǎn)最終計(jì)算出的 CSS 的樣式,如圖所示。 接下來切換到右側(cè)的 Event Listeners 選項(xiàng)卡,這里可以顯示各個(gè)節(jié)點(diǎn)當(dāng)前已經(jīng)綁定的事件,都是 JavaScript 原生支持的,下面簡(jiǎn)單列舉幾個(gè)事件。
通常,我們會(huì)給按鈕綁定一個(gè)點(diǎn)擊事件,它的處理邏輯一般是由 JavaScript 定義的,這樣在我們點(diǎn)擊按鈕的時(shí)候,對(duì)應(yīng)的 JavaScript 代碼便會(huì)執(zhí)行。比如在圖 xx 中,我們選中切換到第 2 頁(yè)的節(jié)點(diǎn),右側(cè) Event Listeners 選項(xiàng)卡下會(huì)看到它綁定的事件。 這里有對(duì)應(yīng)事件的代碼位置,內(nèi)容為一個(gè) JavaScript 文件名稱 所以,利用好 Event Listeners,我們可以輕松地找到各個(gè)節(jié)點(diǎn)綁定事件的處理方法所在的位置,幫我們?cè)?JavaScript 逆向過程中找到一些突破口。 3. 代碼美化剛才我們已經(jīng)通過 Event Listeners 找到了對(duì)應(yīng)的事件處理方法所在的位置并成功跳轉(zhuǎn)到了代碼所在的位置。 但是,這部分代碼似乎被壓縮過了,可讀性很差,根本沒法閱讀,這時(shí)候應(yīng)該怎么辦呢? 不用擔(dān)心,Sources 面板提供了一個(gè)便捷好用的代碼美化功能。我們點(diǎn)擊代碼面板左下角的格式化按鈕,代碼就會(huì)變成如圖所示的樣子。 此時(shí)會(huì)新出現(xiàn)一個(gè)叫作 這個(gè)功能在調(diào)試過程中非常常用,用好這個(gè)功能會(huì)給我們的 JavaScript 調(diào)試過程帶來極大的便利。 4. 斷點(diǎn)調(diào)試接下來介紹一個(gè)非常重要的功能——斷點(diǎn)調(diào)試。在調(diào)試代碼的時(shí)候,我們可以在需要的位置上打斷點(diǎn),當(dāng)對(duì)應(yīng)事件觸發(fā)時(shí),瀏覽器就會(huì)自動(dòng)停在斷點(diǎn)的位置等待調(diào)試,此時(shí)我們可以選擇單步調(diào)試,在面板中觀察調(diào)用棧、變量值,以更好地追蹤對(duì)應(yīng)位置的執(zhí)行邏輯。 那么斷點(diǎn)怎么打呢?我們接著以上面的例子來說。首先單擊如圖所示的代碼行號(hào)。 這時(shí)候行號(hào)處就出現(xiàn)了一個(gè)藍(lán)色的箭頭,這就證明斷點(diǎn)已經(jīng)添加好了,同時(shí)在右側(cè)的 Breakpoints 選項(xiàng)卡下會(huì)出現(xiàn)我們添加的斷點(diǎn)的列表。 由于我們知道這個(gè)斷點(diǎn)是用來處理翻頁(yè)按鈕的點(diǎn)擊事件的,所以可以在網(wǎng)頁(yè)里面點(diǎn)擊按鈕試一下,比如點(diǎn)擊第 2 頁(yè)的按鈕,這時(shí)候就會(huì)發(fā)現(xiàn)斷點(diǎn)被觸發(fā)了,如圖所示。 這時(shí)候我們可以看到頁(yè)面中顯示了一個(gè)叫作 此時(shí)代碼停在了第 4446 行,回調(diào)參數(shù) 另外我們關(guān)注到有一個(gè)方法 我們可以看到, 在 Scope 面板還有多個(gè)域,這里就不再展開介紹了。總之,通過 Scope 面板,我們可以看到當(dāng)前執(zhí)行環(huán)境下的變量的值和方法的定義,知道當(dāng)前代碼究竟執(zhí)行了怎樣的邏輯。 接下來切換到 Watch 面板,在這里可以自行添加想要查看的變量和方法,點(diǎn)擊右上角的 + 號(hào)按鈕,我們可以任意添加想要監(jiān)聽的對(duì)象,如圖所示。 比如這里我們比較關(guān)注 我們還可以切換到 Console 面板,輸入任意的 JavaScript 代碼,便會(huì)執(zhí)行、輸出對(duì)應(yīng)的結(jié)果,如圖所示。 如果我們想看看變量 此時(shí)我們還可以選擇單步調(diào)試,這里有 3 個(gè)重要的按鈕,如圖所示。 這 3 個(gè)按鈕都可以做單步調(diào)試,但功能不同。
用得較多的是第一個(gè),相當(dāng)于逐行調(diào)試,比如點(diǎn)擊 Step Over Next Function Call 這個(gè)按鈕,就運(yùn)行到了 4447 行,高亮的位置就變成了這一行,如圖所示。 5. 觀察調(diào)用棧在調(diào)試的過程中,我們可能會(huì)跳到一個(gè)新的位置,比如點(diǎn)擊上述 Step Over Next Function Call 幾下,可能會(huì)跳到一個(gè)叫作 那究竟是怎么跳過來的呢?我們可以觀察一下右側(cè)的 Call Stack 面板,就可以看到全部的調(diào)用過程了。比如它的上一步是 有時(shí)候調(diào)用棧是非常有用的,利用它我們可以回溯某個(gè)邏輯的執(zhí)行流程,從而快速找到突破口。 6. 恢復(fù) JavaScript 執(zhí)行在調(diào)試過程中,如果想快速跳到下一個(gè)斷點(diǎn)或者讓 JavaScript 代碼運(yùn)行下去,可以點(diǎn)擊 Resume script execution 按鈕,如圖所示。 這時(shí)瀏覽器會(huì)直接執(zhí)行到下一個(gè)斷點(diǎn)的位置,從而避免陷入無窮無盡的調(diào)試中。 當(dāng)然,如果沒有其他斷點(diǎn)了,瀏覽器就會(huì)恢復(fù)正常狀態(tài)。比如這里我們就沒有再設(shè)置其他斷點(diǎn)了,瀏覽器直接運(yùn)行并加載了下一頁(yè)的數(shù)據(jù),同時(shí)頁(yè)面恢復(fù)正常,如圖所示。 7. Ajax 斷點(diǎn)上面我們介紹了一些 DOM 節(jié)點(diǎn)的 Listener,通過 Listener 我們可以手動(dòng)設(shè)置斷點(diǎn)并進(jìn)行調(diào)試。但其實(shí)針對(duì)這個(gè)例子,通過翻頁(yè)的點(diǎn)擊事件 Listener 是不太容易找到突破口的。 接下來我們?cè)俳榻B一個(gè)方法—— Ajax 斷點(diǎn),它可以在發(fā)生 Ajax 請(qǐng)求的時(shí)候觸發(fā)斷點(diǎn)。對(duì)于這個(gè)例子,我們的目標(biāo)其實(shí)就是找到 Ajax 請(qǐng)求的那一部分邏輯,找出加密參數(shù)是怎么構(gòu)造的??梢韵氲剑ㄟ^ Ajax 斷點(diǎn),使頁(yè)面在獲取數(shù)據(jù)的時(shí)候停下來,我們就可以順著找到構(gòu)造 Ajax 請(qǐng)求的邏輯了。 怎么設(shè)置呢? 我們把之前的斷點(diǎn)全部取消,切換到 Sources 面板下,然后展開 XHR/fetch Breakpoints,這里就可以設(shè)置 Ajax 斷點(diǎn),如圖所示。 要設(shè)置斷點(diǎn),就要先觀察 Ajax 請(qǐng)求。和之前一樣,我們點(diǎn)擊翻頁(yè)按鈕 2,在 Network 面板里面觀察 Ajax 請(qǐng)求是怎樣的,請(qǐng)求的 URL 如圖所示。 可以看到 URL 里面包含 這時(shí)候我們?cè)冱c(diǎn)擊翻頁(yè)按鈕 3,觸發(fā)第 3 頁(yè)的 Ajax 請(qǐng)求。會(huì)發(fā)現(xiàn)點(diǎn)擊之后頁(yè)面走到斷點(diǎn)停下來了,如圖所示。 格式化代碼看一下,發(fā)現(xiàn)它停到了 Ajax 最后發(fā)送的那個(gè)時(shí)候,即底層的 接下來切換到 可以發(fā)現(xiàn),可能使用了 因此在某些情況下,我們可以在比較容易地通過 Ajax 斷點(diǎn)找到分析的突破口,這是一個(gè)常見的尋找 JavaScript 逆向突破口的方法。 要取消斷點(diǎn)也很簡(jiǎn)單,只需要在 XHR/fetch Breakpoints 面板取消勾選即可,如圖所示。 8. 改寫 JavaScript 文件我們知道,一個(gè)網(wǎng)頁(yè)里面的 JavaScript 是從對(duì)應(yīng)服務(wù)器上下載下來并在瀏覽器執(zhí)行的。有時(shí)候,我們可能想要在調(diào)試的過程中對(duì) JavaScript 做一些更改,比如說有以下需求:
這時(shí)候我們可以試著在 Sources 面板中對(duì) JavaScript 進(jìn)行更改,但這種更改并不能長(zhǎng)久生效,一旦刷新頁(yè)面,更改就全都沒有了。比如我們?cè)?JavaScript 文件中寫入一行 JavaScript 代碼,然后保存,如圖所示。 這時(shí)候可以發(fā)現(xiàn) JavaScript 文件上出現(xiàn)了一個(gè)感嘆號(hào)標(biāo)志,提示我們做的更改是不會(huì)保存的。這時(shí)候重新刷新頁(yè)面,再看一下更改的這個(gè)文件,如圖所示。 有什么方法可以修改呢?其實(shí)有一些瀏覽器插件可以實(shí)現(xiàn),比如 ReRes。在插件中,我們可以添加自定義的 JavaScript 文件,并配置 URL 映射規(guī)則,這樣瀏覽器在加載某個(gè)在線 JavaScript 文件的時(shí)候就可以將內(nèi)容替換成自定義的 JavaScript 文件了。另外,還有一些代理服務(wù)器也可以實(shí)現(xiàn),比如 Charles、Fiddler,借助它們可以在加載 JavaScript 文件時(shí)修改對(duì)應(yīng) URL 的響應(yīng)內(nèi)容,以實(shí)現(xiàn)對(duì) JavaScript 文件的修改。 其實(shí)瀏覽器的開發(fā)者工具已經(jīng)原生支持這個(gè)功能了,即瀏覽器的 Overrides 功能,它在 Sources 面板左側(cè),如圖所示。 我們可以在 Overrides 面板上選定一個(gè)本地的文件夾,用于保存需要更改的 JavaScript 文件,我們來實(shí)際操作一下。 首先,根據(jù)上文設(shè)置 Ajax 斷點(diǎn)的方法,找到對(duì)應(yīng)的構(gòu)造 Ajax 請(qǐng)求的位置,根據(jù)一些網(wǎng)頁(yè)開發(fā)知識(shí),我們可以大體判斷出 我們打算在 Ajax 請(qǐng)求成功獲得 Response 的時(shí)候,在控制臺(tái)輸出 Response 的結(jié)果,也就是通過 再切回 Overrides 面板,點(diǎn)擊 + 按鈕,這時(shí)候?yàn)g覽器會(huì)提示我們選擇一個(gè)本地文件夾,用于存儲(chǔ)要替換的 JavaScript 文件。這里我選定了一個(gè)我任意新建的文件夾 ChromeOverrides,注意,這時(shí)候可能會(huì)遇到如圖所示的提示,如果沒有問題,直接點(diǎn)擊“允許”即可。 這時(shí),在 Overrides 面板下就多了一個(gè) ChromeOverrides 文件夾,用于存儲(chǔ)所有我們想要更改的 JavaScript 文件,如圖所示。 我們可以看到,現(xiàn)在所在的 JavaScript 選項(xiàng)卡是
接著把修改后的內(nèi)容替換到原來的 JavaScript 文件中。這里要注意,切換到 替換完畢之后保存,這時(shí)候再切換回 Overrides 面板,就可以發(fā)現(xiàn)成功生成了新的 JavaScript 文件,它用于替換原有的 JavaScript 文件,如圖所示。 好,此時(shí)我們?nèi)∠袛帱c(diǎn),然后刷新頁(yè)面,就可以在控制臺(tái)看到輸出的 Reponse 結(jié)果了,如圖所示。 正如我們所料,我們成功將變量 我們還可以增加一些 JavaScript 邏輯,比如直接將變量 修改 JavaScript 文件有很多用途,此方案可以為我們進(jìn)行 JavaScript 的逆向帶來極大的便利。 9. 總結(jié)本節(jié)總結(jié)了一些瀏覽器開發(fā)者工具中對(duì) JavaScript 逆向非常有幫助的功能,熟練掌握了這些功能會(huì)對(duì)后續(xù) JavaScript 逆向分析打下堅(jiān)實(shí)的基礎(chǔ),請(qǐng)大家好好研究。 |
|