當你寫爬蟲時遇上Flash+加密的解決方式!
今天在摸魚(劃掉)逛V2EX的時候,有個帖子引起了我的注意
求助一個網站視訊加密方式, 已排除是 base64 加密 - V2EX
https://www.v2ex.com/t/493201
帖子內容:
視訊連結加密之後是這樣的:
lxxt6jIID2Byq541xEB6F3u71bYaE5A/A-1dMFS4o9mx8uzpm81KxH25u1E29:Cl7Wg|lxxt4hGGB6F3u763zGD9i0X_4EBDh7CAC.6Irkx6q7oz7TYOL2uErB25u1E7_:hQ5Ue|lxxt4hGGB6F3u763zGD9i0X_4EBDh7CAC.6Irkx6q7oz7TYOL2uErB25u1E7_/hQ5Ue|lxxt4hGGB6F3u763zGD9i0X_4EBDh7CAC.6Irkx6q7oz7TYOL2uErB25u1E7_hQ5Ue|lxxt4hGGB6F3u763zGD9i0X_4EBDh7CAC.6Irkx6q7oz7TYOL2uErB25u1E7_.hQ5Ue|lxxt4hGGB6F3u763zGD9i0X_4EBDh7CAC.6Irkx6q7oz7TYOL2uErB25u1E7__hQ5Ue|lxxt4hGGB6F3u763zGD9i0X_4EBDh7CAC.6Irkx6q7oz7TYOL2uErB25u1E7_AhQ5Ue|lxxt4hGGB6F3u763zGD9i0X_4EBDh7CAC.6Irkx6q7oz7TYOL2uErB25u1E7bhQW5e|lxxt4hGGB6F3u763zGD9i0X_4EBDh7CAC.6Irkx6q7oz7TYOL2uErB25u1E7ChQW5e|lxxt4hGGB6F3u763zGD9i0X_4EBDh7CAC.6Irkx6q7oz7TYOL2uErB25u1E7dhQW5e
網站連結在這: http://www.tvsky.tv/Industry/Show/278/33875/
請問是什麼加密, 求助。
進群:960410445 即可獲取數十套PDF!
作為一個助人為樂的好青年,當然要順手幫樓主看一下啦
開啟這個網站看看,這是一個用Flash播放器載入並播放視訊的頁面,傳入播放器的引數如帖中所述是有加密的
使用Chrome的開發者工具檢視播放器元素
傳入播放器的引數:
flvurl=lxxt6jIID2Byq541xEB6F3u71bYaE5A/A-1dMFS4o9mx8uzpm81KxH25u1E29:Cl7Wg|lxxt4hGGB6F3u763zGD9i0X_4EBDh7CAC.6Irkx6q7oz7TYOL2uErB25u1E7_:hQ5Ue|lxxt4hGGB6F3u763zGD9i0X_4EBDh7CAC.6Irkx6q7oz7TYOL2uErB25u1E7_/hQ5Ue|lxxt4hGGB6F3u763zGD9i0X_4EBDh7CAC.6Irkx6q7oz7TYOL2uErB25u1E7_hQ5Ue|lxxt4hGGB6F3u763zGD9i0X_4EBDh7CAC.6Irkx6q7oz7TYOL2uErB25u1E7_.hQ5Ue|lxxt4hGGB6F3u763zGD9i0X_4EBDh7CAC.6Irkx6q7oz7TYOL2uErB25u1E7__hQ5Ue|lxxt4hGGB6F3u763zGD9i0X_4EBDh7CAC.6Irkx6q7oz7TYOL2uErB25u1E7_AhQ5Ue|lxxt4hGGB6F3u763zGD9i0X_4EBDh7CAC.6Irkx6q7oz7TYOL2uErB25u1E7bhQW5e|lxxt4hGGB6F3u763zGD9i0X_4EBDh7CAC.6Irkx6q7oz7TYOL2uErB25u1E7ChQW5e|lxxt4hGGB6F3u763zGD9i0X_4EBDh7CAC.6Irkx6q7oz7TYOL2uErB25u1E7dhQW5e&isautoplay=1&adswf=
抓包發現有一個.flv檔案的連結,應該就是播放器加載出來的視訊
私信菜鳥 007 獲取原始碼!
使用Chrome的開發者工具檢視網路請求
全域性搜尋這個URL的部分內容是搜不到的,判斷出這個URL應該是在播放器中對傳入的flvurl引數進行解密,然後再加載出視訊
那麼,遇到這種情況的時候我們應該怎麼做才能破解出這個解密URL的過程呢?
首先,我們需要將這個頁面上的Flash播放器給逆向一下,就像在爬HTML5視訊網站碰到加密引數時逆向JavaScript一樣。
但是Flash播放器是一個被編譯後的.swf檔案,我們並不能像JavaScript那樣直接看到程式碼,需要先進行反編譯。
是時候祭出JPEXS了,在GitHub上可以找到
https://github.com/jindrapetrik/jpexs-decompiler/releases
下載完後啟動它,介面長這樣:
JPEXS啟動介面
預設的語言是英語,可以切換成中文,在Settings – Change language裡選擇
JPEXS切換成中文
然後我們將這個播放器的.swf檔案給下載下來,並使用JPEXS開啟
播放器檔案地址在源頁面的HTML中可以看到
http://www.tvsky.tv/FlvPlay/Playerx.swf
用JPEXS開啟播放器檔案
然後我們有兩種方式快速定位到可能存在解密程式碼的位置
第一種方式:
開啟後找到指令碼組下frame1的DoAction指令碼
點選後窗口右側會反編譯這個指令碼的內容,並展示出反編譯出來的AS原始碼和P程式碼(類似於組合語言),我們只需要看AS原始碼的部分就行了
播放器載入狀態
根據在網頁中播放器的樣子,在載入時會有一個“正在載入Flv檔案”的字樣,直接按Ctrl+F搜尋它
搜尋反編譯出來的程式碼中的字串
找到init函式
第二種:
隨便找一個指令碼開啟,然後按Ctrl+Shift+F開啟全域性搜尋,同樣搜尋“正在載入Flv檔案”
全域性搜尋
快速定位出載入視訊部分後,根據init函式這裡的程式碼可以看出,_loc2_就是被傳進播放器的flvurl
那麼下面的這部分就是它的解密操作了
init部分: _flvurl = _loc2_.split("|"); var _loc1_ = 0; while(_loc1_ < _flvurl.length) { _flvurl[_loc1_] = Pass2Str(_flvurl[_loc1_]); _loc1_ = _loc1_ + 1; } var PwdStr = "AbCdEfGhIjKlMnOpQrStUvWxYzaBcDeFgHiJkLmNoPqRsTuVwXyZ1234509876-_.\/:"; var PwdStrRan = "12345678987654321"; var _PwdLen = 4; var _PwdAddLen = 4; function Pass2Str(Str) { var _loc2_ = ""; var _loc3_ = ""; var _loc4_ = 0; var _loc1_ = 1; while(_loc1_ <= Str.length) { _loc2_ = Str.substr(_loc1_,1); if(_loc1_ % (_PwdLen + 1) != 0) { _loc3_ = _loc3_ + NumS(_loc2_,_loc4_); } else { _loc4_ = parseInt(_loc2_); } _loc1_ = _loc1_ + 1; } return _loc3_; } function NumS(s, _PwdAddLen1) { var _loc1_ = PwdStr.indexOf(s); _loc1_ = _loc1_ - (_PwdAddLen + _PwdAddLen1 - 1); if(_loc1_ <= 0) { return PwdStr.substr(_loc1_ + PwdStr.length,1); } return PwdStr.substr(_loc1_,1); }
然後將反編譯出來的ActionScript程式碼的解密URL部分改寫成Python程式碼:
# http://www.tvsky.tv/Industry/Show/278/33875/ 的視訊url解密部分 # 為方便對照AS程式碼閱讀,這裡只對反編譯出來的AS程式碼直接進行“翻譯”,沒有使用Python的一些更簡潔的寫法 _pwd_len = 4 _pwd_add_len = 4 pwd_str = "AbCdEfGhIjKlMnOpQrStUvWxYzaBcDeFgHiJkLmNoPqRsTuVwXyZ1234509876-_.\/:" def decode(flv_url: str): """ function init() { ...... var _loc2_ = flvurl; ...... _flvurl = _loc2_.split("|"); var _loc1_ = 0; while(_loc1_ < _flvurl.length) { _flvurl[_loc1_] = Pass2Str(_flvurl[_loc1_]); _loc1_ = _loc1_ + 1; } ...... } :param flv_url: flash引數裡的flvurl部分的value :return: 解密後視訊url列表 """ new_flv_url = flv_url.split("|") _loc1_ = 0 while _loc1_ < len(new_flv_url): new_flv_url[_loc1_] = pass2str(new_flv_url[_loc1_]) _loc1_ += 1 return new_flv_url def pass2str(str_: str): """ function Pass2Str(Str) { var _loc2_ = ""; var _loc3_ = ""; var _loc4_ = 0; var _loc1_ = 1; while(_loc1_ <= Str.length) { _loc2_ = Str.substr(_loc1_,1); if(_loc1_ % (_PwdLen + 1) != 0) { _loc3_ = _loc3_ + NumS(_loc2_,_loc4_); } else { _loc4_ = parseInt(_loc2_); } _loc1_ = _loc1_ + 1; } return _loc3_; } :param str_: 加密的url字串 :return: 解密後的url字串 """ _loc1_ = 1 _loc3_ = "" _loc4_ = 0 while _loc1_ <= len(str_): _loc2_ = str_[_loc1_ - 1] if _loc1_ % (_pwd_len + 1) != 0: _loc3_ = _loc3_ + num_s(_loc2_, _loc4_) else: _loc4_ = int(_loc2_) if _loc2_.isdigit() else 0 _loc1_ = _loc1_ + 1 return _loc3_ def num_s(s, _pwd_add_len1): """ function NumS(s, _PwdAddLen1) { var _loc1_ = PwdStr.indexOf(s); _loc1_ = _loc1_ - (_PwdAddLen + _PwdAddLen1 - 1); if(_loc1_ <= 0) { return PwdStr.substr(_loc1_ + PwdStr.length,1); } return PwdStr.substr(_loc1_,1); } """ _loc1_ = pwd_str.index(s) _loc1_ = _loc1_ - (_pwd_add_len + _pwd_add_len1 - 1) if _loc1_ <= 0: return pwd_str[_loc1_ + len(pwd_str) - 1] return pwd_str[_loc1_ - 1] if __name__ == '__main__': url_list = decode( "lxxt6jIID2Byq541xEB6F3u71bYaE5A/A-1dMFS4o9mx8uzpm81KxH25u1E29:Cl7Wg|" "lxxt4hGGB6F3u763zGD9i0X_4EBDh7CAC.6Irkx6q7oz7TYOL2uErB25u1E7_:hQ5Ue|" "lxxt4hGGB6F3u763zGD9i0X_4EBDh7CAC.6Irkx6q7oz7TYOL2uErB25u1E7_/hQ5Ue|" "lxxt4hGGB6F3u763zGD9i0X_4EBDh7CAC.6Irkx6q7oz7TYOL2uErB25u1E7_hQ5Ue|" "lxxt4hGGB6F3u763zGD9i0X_4EBDh7CAC.6Irkx6q7oz7TYOL2uErB25u1E7_.hQ5Ue|" "lxxt4hGGB6F3u763zGD9i0X_4EBDh7CAC.6Irkx6q7oz7TYOL2uErB25u1E7__hQ5Ue|" "lxxt4hGGB6F3u763zGD9i0X_4EBDh7CAC.6Irkx6q7oz7TYOL2uErB25u1E7_AhQ5Ue|" "lxxt4hGGB6F3u763zGD9i0X_4EBDh7CAC.6Irkx6q7oz7TYOL2uErB25u1E7bhQW5e|" "lxxt4hGGB6F3u763zGD9i0X_4EBDh7CAC.6Irkx6q7oz7TYOL2uErB25u1E7ChQW5e|" "lxxt4hGGB6F3u763zGD9i0X_4EBDh7CAC.6Irkx6q7oz7TYOL2uErB25u1E7dhQW5e" ) print(url_list)
執行一下看看效果
BOOM!