關於使用PyExecJS+nodejs使用與js反混淆

 

來源:https://cuiqingcai.com/5024.htmljavascript

梳理這篇博客的時候出問題,我默認的是jscript做爲pyexcJs的引擎,問題很大,大部分的js都沒法加載,各類包用不了,只能處理及其低端的。php

安裝nodejs,環境變量配好後仍是不行,cmd裏能夠就是引擎仍是jscrapy,嘗試卸載jscrapy發現比較難,官網不提供改選引擎的方法,陷入困境。html

再以後修改安裝選項:java

 

以後仍是不行,重啓電腦後能夠了。node

 

本節來講明一下 JavaScript 加密邏輯分析並利用 Python 模擬執行 JavaScript 實現數據爬取的過程。在這裏以中國空氣質量在線監測分析平臺爲例來進行分析,主要分析其加密邏輯及破解方法,並利用 PyExecJS 來實現 JavaScript 模擬執行來實現該網站的數據爬取。jquery

疑難雜症

中國空氣質量在線監測分析平臺是一個收錄全國各大城市天氣數據的網站,包括溫度、溼度、PM 2.五、AQI 等數據,連接爲:https://www.aqistudy.cn/html/city_detail.html,預覽圖以下:git

經過這個網站咱們能夠獲取到各大城市任何一天的天氣數據,對數據分析仍是很是有用的。github

然而不幸的是,該網站的數據接口通訊都被加密了。通過分析以後發現其頁面數據是經過 Ajax 加載的,數據接口地址是:https://www.aqistudy.cn/apinew/aqistudyapi.php,是一個 POST 形式訪問的接口,這個接口的請求數據和返回數據都被加密了,即 POST 請求的 Data、返回的數據都被加密了,下圖是數據接口的 Form Data 部分,可見傳輸數據是一個加密後的字符串:api

下圖是該接口返回的內容,一樣是通過加密的字符串:瀏覽器

遇到這種接口加密的狀況,通常來講咱們會選擇避開請求接口的方式進行數據爬取,如使用 Selenium 模擬瀏覽器來執行。但這個網站的數據是圖表展現的,因此其數據會變得難以提取。

那怎麼辦呢?剛啊!

一剛到底

以前的老法子都行不通了,那就只能上了!接下來咱們就不得不去分析這個網站接口的加密邏輯,並經過一些技巧來破解這個接口了。

首先找到突破口,當咱們點擊了這個搜索按鈕以後,後臺便會發出 Ajax 請求,說明這個點擊動做是被監聽的,因此咱們能夠找一下這個點擊事件對應的處理代碼在哪裏,這裏能夠藉助於 Firefox 來實現,它能夠分析頁面某個元素的綁定事件以及定位到具體的代碼在哪一行,如圖所示:

這裏咱們發現這個搜索按鈕綁定了三個事件,blur、click、focus,同時 Firefox 還幫助咱們列出來了對應事件的處理函數在哪一個代碼的哪一行,這裏能夠看到 click 事件是在 city_detail.html 的第 139 行處理的,並且是調用了 getData() 函數。

接下來咱們就能夠順藤摸瓜,找到 city_detail.html 文件的 getData() 函數,而後再找到這個函數的定義便可,很容易地,咱們在 city_detail.html 的第 463 行就找到了這個函數的定義:

通過分析發現它又調用了 getAQIData() 和 getWeatherData() 兩個方法,而這兩個方法的聲明就在下面,再進一步分析發現這兩個方法都調用了 getServerData() 這個方法,並傳遞了 method、param 等參數,而後還有一個回調函數很明顯是對返回數據進行處理的,這說明 Ajax 請求就是由這個 getServerData() 方法發起的,如圖所示:

因此這裏咱們只須要再找到 getServerData() 方法的定義便可分析它的加密邏輯了。繼續搜索,然而在原始 html 文件中沒有搜索到該方法,那就繼續去搜尋其餘的 JavaScript 文件有沒有這個定義,終於通過一番尋找,竟然在 jquery-1.8.0.min.js 這個文件中找到了:

有的小夥伴可能會說,jquery.min.js 不是一個庫文件嗎,怎麼會有這種方法聲明?嗯,我只想說,最危險的地方就是最安全的地方。

好了,如今終於找到這個方法了,可爲何看不懂呢?這個方法名後面怎麼直接跟了一些奇怪的字符串,並且不符合通常的 JavaScript 寫法。其實這裏是通過 JavaScript 混淆加密了,混淆加密以後,代碼將變爲不可讀的形式,可是功能是徹底一致的,這是一種常見的 JavaScript 加密手段。

那到這裏了該怎麼解呢?固然是接着剛啊!

反混淆

JavaScript 混淆以後,實際上是有反混淆方法的,最簡單的方法即是搜索在線反混淆網站,這裏提供一個:http://www.bm8.com.cn/jsConfusion/,咱們將 jquery-1.8.0.min.js 中第二行 eval 開頭的混淆後的 JavaScript 代碼複製一下,而後粘貼到這個網站中進行反混淆,就能夠看到正常的 JavaScript 代碼了,搜索一下就能夠找到 getServerData() 方法了,能夠看到這個方法確實發出了一個 Ajax 請求,請求了剛纔咱們分析到的接口:

那麼到這裏咱們又能夠發現一個很關鍵的方法,那就是 getParam(),它接受了 method 和 object 參數,而後返回獲得的 param 結果就做爲 POST Data 參數請求接口了,因此 param 就是加密後的 POST Data,一些加密邏輯都在 getParam() 方法裏面,其方法實現以下:

 

 

能夠看到這裏使用了 Base64 和 AES 加密。加密以後的字符串便做爲 POST Data 傳送給服務器了,而後服務器再進行解密處理,而後進行邏輯處理,而後再對處理後的數據進行加密,返回了加密後的數據,那麼 JavaScript 再接收到以後再進行一次解密,再渲染才能獲得正常的結果。

因此這裏還須要分析服務器傳回的數據是怎樣解密的。順騰摸瓜,很容易就找到一個 decodeData() 方法,其定義以下:

 

 

嗯,這裏又通過了三層解密,才把正常的明文數據解析出來。

因此一切都清晰了,咱們須要實現兩個過程才能正常使用這個接口,即實現 POST Data 的加密過程和 Response Data 的解密過程。其中 POST Data 的加密過程是 Base64 + AES 加密,Response Data 的解密是 AES + DES + Base64 解密。加密解密的 Key 也都在 JavaScript 文件裏能找到,咱們用 Python 實現這些加密解密過程就能夠了。

因此接下來怎麼辦?接着剛啊!

接着剛纔怪!

何須去費那些事去用 Python 重寫一遍 JavaScript,萬一兩者裏面有數據格式不統一或者兩者因爲語言不兼容問題致使計算結果誤差,上哪裏去 Debug?

那怎麼辦?這裏咱們藉助於 PyExecJS 庫來實現 JavaScript 模擬就行了。

PyExecJS

PyExecJS 是一個可使用 Python 來模擬運行 JavaScript 的庫。你們可能據說過 PyV8,它也是用來模擬執行 JavaScript 的庫,但是因爲這個項目已經不維護了,並且對 Python3 的支持很差,並且安裝出現各類問題,因此這裏選用了 PyExecJS 庫來代替它。

首先咱們來安裝一下這個庫:

 

 

使用 pip 安裝便可。

在使用這個庫以前請確保你的機器上安裝瞭如下其中一個JS運行環境:

  • JScript
  • JavaScriptCore
  • Nashorn
  • Node
  • PhantomJS
  • PyV8
  • SlimerJS
  • SpiderMonkey

PyExecJS 庫會按照優先級調用這些引擎來實現 JavaScript 執行,這裏推薦安裝 Node.js 或 PhantomJS。

接着咱們運行代碼檢查一下運行環境:

 

 

運行以後,因爲我安裝了 Node.js,因此這裏會使用 Node.js 做爲渲染引擎,結果以下:

 

 

接下來咱們將剛纔反混淆的 JavaScript 保存成一個文件,叫作 encryption.js,而後用 PyExecJS 模擬運行相關的方法便可。

首先咱們來實現加密過程,這裏 getServerData() 方法其實已經幫咱們實現好了,並實現了 Ajax 請求,但這個方法裏面有獲取 Storage 的方法,Node.js 不適用,因此這裏咱們直接改寫下,實現一個 getEncryptedData() 方法實現加密,在 encryption.js 裏面實現以下方法:

 

 

接着咱們模擬執行這些方法便可:

 

 

這裏咱們首先定義一些參數,如 method、city、start_time 等,這些均可以經過分析 JavaScript 很容易得出其規則。

而後這裏首先經過 execjs(即 PyExecJS)的 get() 方法聲明一個運行環境,而後調用 compile() 方法來執行剛纔保存下來的加密庫 encryption.js,由於這裏麪包含了一些加密方法和自定義方法,因此只有執行一遍才能調用。

接着咱們再構造一個 js 字符串,傳遞這些參數,而後經過 eval() 方法來模擬執行,獲得的結果賦值爲 params,這個就是 POST Data 的加密數據。

接着咱們直接用 requests 庫來模擬 POST 請求就行了,也不必用 jQuery 自帶的 Ajax 了,固然後者也是可行的,只不過須要加載一下 jQuery 庫。

接着咱們用 requests 庫來模擬 POST 請求:

 

 

這樣 response 的內容就是服務器返回的加密的內容了。

接下來咱們再調用一下 JavaScript 中的 decodeData() 方法便可實現解密:

 

 

這樣 decrypted_data 就是解密後的字符串了,解密以後,其實是一個 JSON 字符串:

 

 

大功告成!

這樣咱們就能夠成功獲取溫度、溼度、風力、天氣等信息了。

另外這部分數據其實不全,還有 PM 2.五、AQI 等數據須要用另一個 method 參數 GETDETAIL,修改一下便可獲取這部分數據了。

再日後的數據就是解析和存儲了,這裏再也不贅述。

相關文章
相關標籤/搜索