GitHub Repository: https://github.com/JSREI/js-cookie-monitor-debugger-hook
簡體中文| English
資料無價的時代,爬蟲與反爬的對抗已經進入白熱化狀態,其中Cookie反爬是最常见之一
的反爬類型, 網站方透過混淆得親媽都不認識的JS程式碼設定Cookie(通常是瀏覽器指紋、請求時必須帶上的Cookie之類的), 面對請求時必須帶上但是又不知道在哪裡產生的Cookie,你在幾萬行混淆的親媽都不認識的JS屎海中苦苦掙扎希望能找到生成Cookie的地方(要是逆向思路不科學興許還會嗆上幾口...), 甚至幾度想找個藉口騙自己放棄,或要不乾脆用Selenium之類的瀏覽器模擬方式算了? 慫個球,此腳本就是來助你一臂之力的! (你我都知道,這段只是撐場面的廢話,你可以略過,如果你沒有不幸讀完的話...)
本腳本的功能大致分為兩個部分:
本腳本是透過將自己的JS程式碼注入到頁面,Hook住document.cookie
來完成各種功能, 因此在使用本腳本之前要先確定要搞的Cookie確實是透過JS產生的(後面介紹了一種非常簡單的確定Cookie是JS產生還是伺服器回傳的方式)。
目前許多Hook腳本Hook姿勢並不對,本腳本採用的是一次性、重複Hook,對瀏覽器自備的Cookie管理無影響:
除了cookie斷點功能之外,增加了Cookie修改監控功能,能夠在更宏觀的角度分析頁面上的Cookie:
(算了,放棄打碼了...)
顏色是用來區分操作類型:
每個操作都會跟著一個code location,點擊可以定位到做了這個動作的JS程式碼的位置。
從v0.6開始引入了更強大且配置更靈活的斷點規則,引入事件機制, 將Cookie修改細分為增加、刪除、更新三個事件,支援更細微的打斷點, 關於Cookie事件,詳情請參閱本文第五部分。
關於為什麼要這樣設計? 比較常見的情況,目標網站有反爬的Cookie是JS設定的, 但是JS程式碼的邏輯是先瘋狂的刪除,然後刪除好多次之後才加入真正的值, 這種方式設定Cookie剛好能反制一般的Cookie Hook調試。
這裡是其中一個例子,例如F5的Cookie保護,有一個Cookie TS51c47c46075
,它就是先被刪除好多次,然後再被加一次:這種情況下可以針對新增名為TS51c47c46075
的Cookie事件打一個斷點, 就可以避免那些紅色的刪除事件混淆視聽。
理論上只要本腳本的JS程式碼能夠注入到頁面上即可,這裡採用的是油猴插件來將JS程式碼注入到頁面上。
油猴插件可從Chrome商店安裝:
https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo
如果無法翻牆,可以在百度搜尋「Tampermonkey」字樣尋找第三方網站下載,但請注意不要安裝了虛假的惡意插件,建議從官方商店安裝。
其它工具亦可,只要能夠將本腳本的JS程式碼注入到頁面最頭部執行即可。
安裝油猴腳本可以從官方商店,也可以拷貝程式碼自己在本地創建。
推薦此方式,從油猴商店安裝的油猴腳本有後續版本更新時能夠自動更新,本腳本已經在油猴商店上架:
https://greasyfork.org/zh-CN/scripts/419781-js-cookie-monitor-debugger-hook
如果您覺得自動更新太煩,或者有其它的顧慮,可以在這裡複製本腳本的程式碼:
https://github.com/CC11001100/js-cookie-monitor-debugger-hook/blob/main/js-cookie-monitor-debugger-hook.js
review確認沒問題之後在油猴的管理面板添加即可。
注意,監控是為了在宏觀上有一個全局的認識,並不是為了定位細節(通常情況下正確的使用工具才能提高效率哇,當然一個人的認知是有限的,歡迎大家反饋更有意思的玩法) , 例如開啟一個頁面時:
根據這張圖,我們就能夠對這個網站上哪些cookie是JS操作的,以及什麼時間如何操作的有個大致的了解。
再例如借助monitor觀察cookie的變化規律,例如這個頁面,根據時間能夠看出這個cookie每隔半分鐘會被改變一次:
(2021-1-7 18:27:49更新v0.4新增此功能): 如果控制台列印的資訊過多, 可以藉助Chrome瀏覽器自帶的過濾來篩選,列印的日誌的格式已經統一,只需要cookieName = Cookie名字
即可, 例如:
請注意,搜尋時要確保你的搜尋資訊是URL解碼了的,否則可能會不匹配, 因為控制台的列印資訊都是先URL解碼再列印的。
如果你不確定要搞的Cookie是本地產生的還是某個請求伺服器set-cookie
回傳的, 則可以把本腳本打開,然後刷新目標網站的頁面,然後在控制台搜尋Cookie名字即可, 方法與上一節類似,當Cookie的名字比較短沒有識別性的時候可以加cookieName
輔助定位,例如:
cookieName = v
有時候目標網站可能會反覆設定一個cookie,還都是同樣的值,這個變數用來忽略這類事件:
一般保持預設即可。
@since v0.6
此部分的文檔適用於v0.6+版本,如果您本地的版本小於0.6,請升級版本後再來閱讀文檔。
從v0.6開始,在Cookie的值改變時打斷點變得很複雜,也變得很簡單, 複雜是因為引入了事件機制,簡單是因為簡化了斷點規則配置更靈活。
斷點規則可以分為标准规则
和简化规则
,標準規則是程序底層方便實現處理的, 簡化規則是為了用戶更方便地配置,通常情況下您只需要了解簡化規則就可以了, 當簡化規則配置無法滿足需求時再來查閱標準規則如何配置。
所有的規則都是配置在debuggerRules
數組中的,在腳本的頭部有一個變數:如果找不到的話,可以按Ctrl+F按變數的名字搜尋:
debuggerRules
這個變數是數組類型,裡面存放著一些規則條件,來決定什麼情況下會進入斷點。
請注意,這是一個數組,數組中的規則是或的關係,觸發Cookie修改事件時, 會順序匹配每條規則, 只要有一條規則匹配成功就會進入一次斷點。
當名為foo
的Cookie發生變化時進入斷點:
const debuggerRules = [ "foo" ] ;
上面這種方式指定一個字串,會按照Cookie名字等於給定的字串去匹配。
注意,此處的完全匹配如果有被URL編碼的部分也需要先URL解碼再粘貼到這裡, 其它涉及到字符串的地方都一樣後面不再贅述。
如果Cookie的名字中包含一直變化的部分,例如時間戳、UUID之類的, 透過名字已經無法定位,則透過正規比對:
const debuggerRules = [ / foo.+ / ] ;
絕大多數情況只需要這兩種配置就夠了。
下面來實踐一下,當打開這個頁面
https://www.ishumei.com/trial/captcha.html
能看到腳本偵測到了一些Cookie操作:
其中有個smidV2
很可疑,於是我們為它加上一個斷點:
修改完debuggerRules
數組要注意按Ctrl+S保存腳本,然後因為油猴是在頁面加載的時候注入JS代碼的, 所以要刷新頁面重新註入,當刷新頁面的時候就自動進入了斷點:
上圖的紅色框A中是專門傳進來的一些變量,透過將滑鼠移到這些變數上查看值, 我們能夠大概知道當前斷點的一些情況:
然後就是紅色框B,我們打Cookie斷點就是為了追蹤呼叫棧定位產生Cookie的地方, 紅色方框內是本腳本的呼叫棧,有很明顯的userscript.html
標識, 忽略此部分的呼叫棧即可。
然後追溯調用棧,能夠看到設定Cookie的地方:
當然看這個棧對我們沒用,我們要做的就是逐步往前定位, 直到定位到真正生成Cookie的地方,但是呢,本腳本只能幫你打個斷點, 後面星辰大海的征程就要靠你自己啦!
在名為foo
的Cookie被添加
時進入斷點:
const debuggerRules = [ { "add" : "foo" } ] ;
在名為foo
的Cookie被删除
時進入斷點:
const debuggerRules = [ { "delete" : "foo" } ] ;
在名為foo
的Cookie已經存在但是值被更新
時進入斷點:
const debuggerRules = [ { "update" : "foo" } ] ;
條件可以同時指定多個,在添加和更新
時進入斷點,相當於把刪除排除在外:
const debuggerRules = [ { "add|update" : "foo" } ] ;
牽涉到Cookie名字符合的地方都可以使用字串或正規:
const debuggerRules = [ { "add" : / foo_d+ / } ] ;
上面的簡化規則會被轉換為標準規則,您也可以直接在debuggerRules
陣列中配置標準規則, 一條標準的規則的格式:
{
"events": "{add|delete|update}",
"name": {"cookie-name" | /cookie-name-regex/},
"value": {"cookie-value" | /cookie-value-regex/}
}
字串類型,表示此條規則匹配的事件類型,可以是單一事件,例如add
, 也可以是多個事件,多個事件之間使用|
來分隔,例如add|update
, 如果覺得擠的話還可以在|
兩側加空格,例如add | update
當配置了事件類型時只會匹配給定的事件類型,當不配置此選項時,預設匹配所有事件類型。
可以是字串,也可以是正規則,當Cookie的名字符合給定的字串或正規則時為true, 此條不可忽略必須配置。
可以是字串,也可以是正規則,當Cookie的值符合給定的字串或正規時此規則為true, 可以不配置,不配置則會忽略此選項。
前面介紹斷點規則的配置,多次提到了事件類型, 我們只知道每個事件對應的名字的字符串是啥了, 但是還不知道每種事件意味著底層發生了啥, 本部分就是解釋每種事件的實作機制。
Cookie發生了變化細分為增加Cookie、刪除Cookie、更新已有的Cookie的數值,其中每個事件對應著一個事件名稱:
Cookie之前在本地不存在,這是第一次添加。 有可能是第一次訪問這個網站,也有可能是清除了Cookie重新訪問,或者是每次訪問網站都會生成新的Cookie, 甚至可能是網站自己的代碼把Cookie刪了重新添加,這都會觸發增加Cookie事件。
例如執行下面的程式碼,這裡為了確保Cookie之前不存在,在cookie的名字中加了時間戳:
document . cookie = "foo_" + new Date ( ) . getTime ( ) + "=bar; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/" ;
當我們在控制台執行這行程式碼的時候,就會觸發Cookie新增事件:
當一個Cookie在本地已經存在,然後嘗試為它設定值,就會觸發更新Cookie事件。
例如下面的程式碼:
document . cookie = "foo_10086=blabla; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/" ;
document . cookie = "foo_10086=wuawua; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/" ;
第一條設定Cookie的語句會觸發Cookie新增事件, 第二條設定Cookie的語句因為要設定的Cookie已經存在了, 所以觸發了Cookie更新事件。
如果前端開發者在設定Cookie的時候,給了一個早於目前時間的expires, 則表示要刪除這個Cookie,例如一種常見的刪除Cookie的方式:
const expires = new Date ( new Date ( ) . getTime ( ) - 1000 * 30 ) . toGMTString ( ) ;
document . cookie = "foo=; expires=" + expires + "; path=/"
當我們在控制台運行這段程式碼時,就會觸發Cookie刪除事件:
由上面也可以看出來,觸發Cookie刪除事件純粹是偵測expires, 並不會真的去檢查這個Cookie之前是否存在。
前面介紹了在配置Cookie斷點規則的時候有個事件類型, 事實上每個事件類型都對應著一個此事件類型的斷點是否開啟的標誌位, 這個標誌位的優先級是最高的,例如沒有開啟刪除Cookie斷點的情況下,觸發了Cookie刪除事件, 會先檢查Cookie刪除斷點是否開啟標誌位,如果是關閉的,則直接忽略本次事件不再嘗試符合斷點規則(開發者工具控制台上是仍會列印本次刪除事件的日誌的)。
所以現在的情況就變得非常複雜了,讓我們再捋一下這一個小小的Cookie斷點要走的流程:
預設只開啟了Cookie增加事件和Cookie修改事件的斷點:
因為一般情況下,增加Cookie和更新Cookie可以混為一談,它們都是為Cookie賦了一個值, 而大部分情況下我們不會關注Cookie被刪除的事件,所以這裡就這麼設置了,如果無法滿足你的需求, 可以自行修改enableEventDebugger
對應的值。
在使用的過程中遇到任何問題,可以在GitHub的Issues
中反饋, 也可以在油猴腳本的評論區反饋,還可以給我發郵件,我看到之後會盡快處理。
從v0.6版本開始增加了一個變數用於調整本腳本在控制台列印的日誌的字體大小,單位為px:
隨著版本迭代,可能不在這個位置了,如果一下找不到,就在程式碼搜尋:
consoleLogFontSize
然後修改這個變數的值即可。
或者另一個方案,可以在開發者工具控制台按住Ctrl+滑鼠滾輪縮放調整整體大小, 這是Chrome瀏覽器自帶的功能。
在本文的最開始就交代了,本腳本要能夠成功的注入到頁面的開頭部分並且執行才能夠Hook成功, 對於類似於加速樂第一層那種整個頁面只返回一個script,裡面是這種邏輯:
< script >
document . cookie = 这里是一些奇奇怪怪的JS用于计算出Cookie ;
location . href = "跳转走了" ;
</ script >
設定了Cookie並且立刻就重定向到了新的頁面,對於這種操作,有可能會Hook不到,這是油猴腳本的問題,如果堅持要Hook, 可以採用掛代理將本腳本注入到這個URL的響應的頭部。
此頁面下方是一些使用此腳本逆向的實戰範例摘要:
點我進入導覽頁
本項目拆分自: https://github.com/CC11001100/crawler-js-hook-framework-public/tree/master/001-cookie-hook#%E7%9B%91%E6%8E%A7%E5 %AE%9A%E4%BD%8Djavascript%E6%93%8D%E4%BD%9Ccookie
更改了namespace,可能安裝量要清零了,截圖紀念下,截止到目前(2022-7-29 21:40:01)安裝量破三百了,感覺對於這麼窄領域的一個小工具來說很不容易了...
感謝熱心網友回饋問題,謝謝支持。
js-cookie-monitor-debugger-hook 現已加入404星鏈計劃
掃碼加入逆向技術交流群:
如群二維碼過期,可以加我個人微信,發送【逆向群】拉你進群:
點此或掃碼加入TG交流群: