資源下載地址:https://download.csdn.net/download/sheziqiong/85795438
資源下載地址:https://download.csdn.net/download/sheziqiong/85795438
部分網站的 url 構成中含有加密參數,這些參數的加密方法寫在了 JavaScript 中,而 js 通常經過了壓縮,混淆和加密;本項目通過 AJAX 斷點,hook 方法先尋找到加密入口,其次通過 playwright 將加密方法掛載到 window 對象中,達到利用浏覽器環境執行 JavaScript 方法的目的,從而在不需要知道具體加密邏輯的情況下,模擬參數加密。
2.結果
這是一個 AJAX 請求,token 參數是加密參數,limit 參數表示每頁電影展示數量,offset 根據觀察,為(當前頁數-1)*10;
為了觀察 token 的構造,我們打一個 AJAX 斷點,在發出 AJAX 請求的一瞬間停下,用調用棧尋找 token 的生成入口
按個查看調用棧,找到 token 生成入口,
在 169 行打上斷點,刷新網頁,並將 _0x2fa7bd['a']
和 this['$store']['state']['url']['index']
添加到 watch 監控中,
容易發現_0x2fa7bd[‘a’]是一個方法,傳入的參數是 /api/movie
此時有 2 條路徑可以走,1 是點擊 chunk-4dec7ef0.e4c2b130.js:1
,具體的查看這個方法的構造邏輯,並用 python 模擬該方法,第二條路是直接將 _0x2fa7bd['a']
掛載到浏覽器 window 對象中,具體選用哪種方法可以由該方法的復雜程度決定,這裡我們先點進去查看該方法,
在 return 處打上斷點,刷新,察看變量的變化
/api/movie
構造成一個列表,
拼接這個列表形成字符串後,用 SHA1
加密形成 40 個 16 進制數如果用 python 實現的話要導入 hashlib 調用 sha1 方法,導入 base64 調用 encode 方法,還是比較繁瑣的,這裡我們走掛載 window 對象方式
下載改加密方法所在的 js 文件
vscode 打開該文件,在該方法調用後的下一行加上如下一句 window.encrypt = Object(_0x2fa7bd['a']);
意思是將該方法變成 window 對象的一個屬性
調用 playwright 庫改寫 js 加載路徑
from playwright.sync_api import sync_playwright
browser = sync_playwright().start().chromium.launch()
page = browser.new_page()
page.route('https://spa6.scrape.center/js/chunk-19c920f8.c3a1129d.js',
lambda route: route.fulfill(path='./項目6/chunk.js'))
page.route('https://spa6.scrape.center/js/chunk-4dec7ef0.e4c2b130.js',
lambda route: route.fulfill(path='./項目6/chunk-id.js'))
self.page.goto(self.BASEURL)
於是 playwright 的浏覽器實例便具備了調用該加密方法的能力,緊接著我們寫一個方法,讓浏覽器執行這個一個 JavaScript 方法,它的作用是讓傳入進來的參數,實現這個加密方法並返回輸出結果
def get_token(self, params):
result = self.page.evaluate(
'()=>{return window.encrypt("%s")}' % params)
return result
於是列表頁的 url 構造便完成了,接下來用 requests/aiohttp/scrapy 等方法請求該 url 即可
還是一個 AJAX 請求,有 2 個加密參數,2 個都是 64 位的字符串,可能都用了 base64 加密方法,token 的構造方法可能和列表頁的一樣,前面那個,暫且叫 id 不清楚
先調查 token 的加密入口,看方法是否與列表頁一致,如果一致,看加密參數是什麼
可以看出 token 的加密方法是一樣的,但是加密參數不一樣,/api/movie/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWIx
,在/api/movie/的基礎上還拼接了 id,那麼接下來方向就在 id 的加密構造方法上了,但 id 的構造方法通過查看調用棧的方式並沒有找到,所以換一種方法,即然用了 base64 加密方法,那麼很可能是用 btoa 方法實現的,我們可以借助 tampermonkey
插件寫一個簡單的 JavaScript 腳本,進行 hook 捕獲
// ==UserScript==
// @name hookbase64 # 腳本名稱
// @namespace http://tampermonkey.net/
// @version 0.1
// @description try to take over the world!
// @author You
// @match https://spa6.scrape.center/ # 目標網址
// @icon https://www.google.com/s2/favicons?sz=64&domain=greasyfork.org
// @grant none
// ==/UserScript==
(function() {
'use strict';
function hook(object,attr){
var func=object[attr] # 先定義一個方法
object[attr]=function(){
# 隨後改寫這個方法
console.log('hooked',object,attr) # 控制台輸出調用的對象和屬性
var ret=func.apply(object,arguments) # 調用一開始的方法
console.log('ret',ret) # 控制台輸出返回的結果
debugger # 重點:在此處進入調試
return ret # 返回結果
}}
hook(window,'btoa') # 實例化這個方法,對象是window,方法是'btao'
})();
取消網頁所有斷點,刷新
奇怪的是並沒有觸發 js 腳本的運行
可能原因是什麼?可能是這個 id 並不是在這個網頁包含的 AJAX 請求中生成的,可能是在列表頁網址的 AJAX 請求中就生成了,驗證一下
找到了,參數是一串無意義的字符,通過調用棧,一步步找到它的上層調用方法
最終我們找到了構造入口,進入查看其加密方法,為 2 個字符串拼湊後,用 base64 加密成 64 位的字符串,一個字符串是固定的,另外一個是傳入的參數,而這個參數必然是區別每一步電影之間的唯一標識碼,接下來去查看列表頁的 JSON 文件
可以推斷出 ID 字段便是這個傳入進去的參數
那麼接下來就沿用列表頁的方式,將這個方法用 playwright 掛載到 window 對象中,構建 id 生成方法即可
def get_id(self, params):
result = self.page.evaluate(
'()=>{return window.encrypt_id("%s")}' % params)
return result
url 都構造好後,下面就是水到渠成了。
資源下載地址:https://download.csdn.net/download/sheziqiong/85795438
資源下載地址:https://download.csdn.net/download/sheziqiong/85795438