當前位置:
首頁 > 知識 > 當你寫爬蟲時遇上Flash+加密的解決方式

當你寫爬蟲時遇上Flash+加密的解決方式



Linux編程

點擊右側關注,免費入門到精通!

作者丨loco 


https://zhuanlan.zhihu.com/p/45508079

今天在摸魚(劃掉)逛V2EX的時候,有個帖子引起了我的注意

求助一個網站視頻加密方式, 已排除是 base64 加密 - V2EX



https://www.v2ex.com/t/493201

帖子內容:

視頻鏈接加密之後是這樣的: 




網站鏈接在這: 


http://www.tvsky.tv/Industry/Show/278/33875/ 



請問是什麼加密, 求助。


作為一個助人為樂的好青年,當然要順手幫樓主看一下啦??

打開這個網站看看,這是一個用Flash播放器載入並播放視頻的頁面,傳入播放器的參數如帖中所述是有加密的

傳入播放器的參數:


flvurl=lxxt6jIID2Byq541xEB6F3u71bYaE5A/A-

1

dMFS4o9mx8uzpm81KxH25u1E29:

Cl7Wg

|lxxt4hGGB6F3u763zGD9i0X_4EBDh7CAC.6Irkx6q7oz7TYOL2uErB25u1E7_:hQ5Ue|

lxxt4hGGB6F3u763zGD9i0X_4EBDh7CAC.

6

Irkx6q7oz7TYOL2uErB25u1E7

_

/hQ5Ue

|lxxt4hGGB6F3u763zGD9i0X_4EBDh7CAC.6Irkx6q7oz7TYOL2uErB25u1E7_hQ5Ue|

lxxt4hGGB6F3u763zGD9i0X_4EBDh7CAC.

6

Irkx6q7oz7TYOL2uErB25u1E7

_

.hQ5Ue

|lxxt4hGGB6F3u763zGD9i0X_4EBDh7CAC.6Irkx6q7oz7TYOL2uErB25u1E7__hQ5Ue|

lxxt4hGGB6F3u763zGD9i0X_4EBDh7CAC.

6

Irkx6q7oz7TYOL2uErB25u1E7_AhQ5Ue

|lxxt4hGGB6F3u763zGD9i0X_4EBDh7CAC.6Irkx6q7oz7TYOL2uErB25u1E7bhQW5e|

lxxt4hGGB6F3u763zGD9i0X_4EBDh7CAC.

6

Irkx6q7oz7TYOL2uErB25u1E7ChQW5e

|lxxt4hGGB6F3u763zGD9i0X_4EBDh7CAC.6Irkx6q7oz7TYOL2uErB25u1E7dhQW5e&isautoplay=1&adswf=

抓包發現有一個.flv文件的鏈接,應該就是播放器載入出來的視頻

全局搜索這個URL的部分內容是搜不到的,判斷出這個URL應該是在播放器中對傳入的flvurl參數進行解密,然後再載入出視頻


那麼,遇到這種情況的時候我們應該怎麼做才能破解出這個解密URL的過程呢?

首先,我們需要將這個頁面上的Flash播放器給逆向一下,就像在爬HTML5視頻網站碰到加密參數時逆向JavaScript一樣。

但是Flash播放器是一個被編譯後的.swf文件,我們並不能像JavaScript那樣直接看到代碼,需要先進行反編譯。

是時候祭出JPEXS了,在GitHub上可以找到

https://github.com/jindrapetrik/jpexs-decompiler/releases



下載完後啟動它,界面長這樣:

默認的語言是英語,可以切換成中文,在Settings – Change language里選擇

然後我們將這個播放器的.swf文件給下載下來,並使用JPEXS打開

播放器文件地址在源頁面的HTML中可以看到

http://www.tvsky.tv/FlvPlay/Playerx.swf


然後我們有兩種方式快速定位到可能存在解密代碼的位置

第一種方式:



打開後找到腳本組下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!

 推薦↓↓↓ 






??

16個技術公眾號

】都在這裡!


涵蓋:程序員大咖、源碼共讀、程序員共讀、數據結構與演算法、黑客技術和網路安全、大數據科技、編程前端、Java、Python、Web編程開發、Android、iOS開發、Linux、資料庫研發、幽默程序員等。

覺得我們「好看」的點亮它~

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Python開發 的精彩文章:

這個事故告訴我們,該放手時就放手!
菜鳥必收藏,13個Python慣用小技巧

TAG:Python開發 |