爬蟲05 /js加密/js逆向、常用抓包工具、移動端數據爬取


爬蟲05 /js加密/js逆向、常用抓包工具、移動端數據爬取

1. js加密、js逆向:案例1

  • 需求: 將這個網頁中的代理ip和端口號進行爬取

  • 分析:

    1. 爬取的數據是動態加載

    2. 並且我們進行了抓包工具的全局搜索,沒有查找到結果

      意味着:爬取的數據從服務端請求到的是加密的密文數據

    3. 頁面每10s刷新一次,刷新后發現數據更新,但是瀏覽器地址欄的url沒有變,說明加載出的數據是由ajax請求到的

      動態加載出來的數據是由ajax請求到的,並且請求到的數據為加密數據

    4. 定位到ajax數據包,從中可以看到url和動態變化的請求參數和加密的相應數據

    5. 將ajax請求到的密文數據捕獲

      動態的獲取動態變化的請求參數

      基於抓包工具進行了動態變化請求參數taken的全局搜索,定位到了taken產生的源頭,就是如下js代碼:

      var token = md5(String(page) + String(num) + String(timestamp));
      
    6. 對密文數據進行解密

      通過分析找到了解密的js函數:decode_str(encode_str),encode_str就是密文數據

    7. 查找encode_str的實現:

      js逆向:將js代碼轉換成python代碼。開發環境只能執行python代碼

  • js加密/js逆向源碼相關代碼:

    function get_proxy_ip(page, num, click_btn) {
        var timestamp = Date.parse(new Date());
        timestamp = timestamp / 1000;
        var token = md5(String(page) + String(num) + String(timestamp));
        $.get('../proxy?page=' + page + '&num=' + num + '&token=' + token + '&t=' + timestamp, function (result) {
            if (result.status === 'true') {
                var setHtml = "";
                $("#ip-list").html(setHtml);
                var encode_str = result.list;
                var items = str_to_json(decode_str(encode_str));
                for (var index = 0; index < items.length; ++index) {
                    item = items[index];
                    setHtml += "<tr>\n<td>" + (index + 1) + "</td>\n";
                    setHtml += "<td>" + item.ip.toString() + "</td>\n";
                    setHtml += "<td>" + item.port.toString() + "</td>\n";
                    setHtml += "<td>" + item.time.toString() + "</td>\n</tr>\n";
                }
                $("#ip-list").html(setHtml);
                if (click_btn === 'next') {
                    document.getElementById("last-page").disabled = false;
                    if (items.length < 15) {
                        document.getElementById("next-page").disabled = true;
                    }
                } else {
                    document.getElementById("next-page").disabled = false;
                    if (page === 1) {
                        document.getElementById("last-page").disabled = true;
                    }
                }
    
            }
        });
    }
    

    js解密代碼:

    function decode_str(scHZjLUh1) {
        // Base64.decode(scHZjLUh1)
        scHZjLUh1 = Base64["\x64\x65\x63\x6f\x64\x65"](scHZjLUh1);
        key = '\x6e\x79\x6c\x6f\x6e\x65\x72';#key = 'b'nyloner'
        len = key["\x6c\x65\x6e\x67\x74\x68"];
        code = '';
        for (i = 0; i < scHZjLUh1["\x6c\x65\x6e\x67\x74\x68"]; i++) {
            var coeFYlqUm2 = i % len;
            code += window["\x53\x74\x72\x69\x6e\x67"]["\x66\x72\x6f\x6d\x43\x68\x61\x72\x43\x6f\x64\x65"](scHZjLUh1["\x63\x68\x61\x72\x43\x6f\x64\x65\x41\x74"](i) ^ key["\x63\x68\x61\x72\x43\x6f\x64\x65\x41\x74"](coeFYlqUm2))
        }
        return Base64["\x64\x65\x63\x6f\x64\x65"](code)
    }
    

    對以上代碼分析結果如下:

    function decode_str(scHZjLUh1) {
        // Base64.decode(scHZjLUh1)
        scHZjLUh1 = Base64.decode(scHZjLUh1);
        key = 'nyloner';
        len = key.length;
        code = '';
        for (i = 0; i < scHZjLUh1.length; i++) {
            var coeFYlqUm2 = i % len;
            code += window.String.fromCharCode(scHZjLUh1.charCodeAt(i) ^ key.charCodeAt(coeFYlqUm2))
        }
        return Base64.decode(code)
    }
    
    // '\x64\x65\x63\x6f\x64\x65'.encode('utf-8')
    // 注意:遇到類似字節的代碼,一般不是加密數據,通過encode編碼嘗試是否可以得到有效信息
    
  • 代碼實現:

    導入所需模塊:

    import time
    import hashlib
    import requests
    import base64
    

    js逆向/將js代碼轉換成python代碼:

    # js逆向之后的結果
    def decode_str(scHZjLUh1):
        # 解密成字符串
        scHZjLUh1 = base64.decodestring(scHZjLUh1.encode())
        key = 'nyloner'
        lenth = len(key)
        code = ''
        sch_lenth = len(scHZjLUh1)
        for i in range(sch_lenth):
            coeFYlqUm2 = i % lenth
            # chr(0-255)返回對應編碼的字符
            # ord(a-z)返回編碼數值
            code += chr(scHZjLUh1[i] ^ ord(key[coeFYlqUm2]))
        code = base64.decodestring(code.encode())
        code = code.decode('utf-8')
        return code
    

    生成請求所需的token值:

    def getToken():
        page = str(1)
        num = str(15)
        t = str(int(time.time()))
        md5 = hashlib.md5()
        md5.update((page+num+t).encode('utf-8'))
        token = md5.hexdigest()
        return token
    

    對響應數據解密:

    token = getToken()
    url = 'https://nyloner.cn/proxy'
    param = {
        'num':'15',
        'page':'1',
        't':str(int(time.time())),
        'token':token
        
    }
    headers = {
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36',
        'Cookie':'sessionid=20ryihg87smnkko2kx6634jbcf4umhfp'
    }
    code = requests.get(url,headers=headers,params=param).json().get('list')
    str_code = decode_str(code)
    str_code
    

2. js加密、js逆向:案例2

  • 需求: url:https://www.aqistudy.cn/html/city_detail.html ,對其氣象數據進行爬取

  • 分析:

    1. 點擊不同氣象指標的選項卡,發現沒有相關的請求發送,說明當頁面加載出來的時候,所有的氣象數據已經加載完畢。

    2. 分析數據是否為動態加載 ,得出數據是動態加載出來的

    3. 修改查詢的條件(城市的切換,時間的修改),點擊搜索按鈕就會加載出新數據。

    4. 在抓包工具的XHR中捕獲到了兩個數據包

      • url一樣
      • 都有一個d這樣的請求參數
      • 兩個數據包的請求參數d的數據值不同
      • d這個請求參數是經過加密且動態變化
    5. 處理動態變化且加密的請求參數d

      對請求參數d進行全局搜索(不可行)

      點擊頁面中的搜索按鈕后,在抓包工具中就捕獲到了那兩個ajax請求的數據包

      • 點擊按鈕發起ajax請求,找按鈕的點擊事件(click)
      • 借助火狐瀏覽器的開發者工具進行按鈕點擊事件的定位 ,根據點擊事件定位到 getData()js函數
    6. 分析getData這個js函數的實現:目的就是為了找到ajax請求對應的js代碼

      type=="HOUR":按照小時為時間單位進行查詢

      並沒有在該函數的實現中發現ajax請求對應的代碼,但是發現了另外的兩個函數調用getAQIData();getWeatherData();

    7. 分析getWeatherData();和getAQIData()這兩個函數的定義,想要去找到ajax請求對應的代碼:

      這兩個函數實現的區別: method變量賦值的字符串不一樣 GETDETAIL / GETCITYWEATHER

      相同: 都沒有出現ajax請求對應的代碼,但是發現了另一個函數的調用: getServerData(method,param,匿名函數,0.5)

    8. 對getServerData(method,param,匿名函數,0.5) 參數進行分析

      method:GETDETAIL或者GETCITYWEATHER

      param:字典,有四組鍵值對

      • city:查詢城市的名稱
      • type:HOUR
      • startTime:查詢開始的時間
      • endTIme:查詢結束的時間
    9. 對getServerData(method,param,匿名函數,0.5)函數進行分析

      getServerData(method,param,匿名函數,0.5)這個函數的實現,還是為了找到ajax請求對應的代碼:

      基於抓包工具的全局搜索才可以找到

      發現這個函數的實現代碼看不懂,函數的實現好像是一組密文 / 網站對js函數的實現加密

    10. 插入兩個概念/上面密文用到了js混淆:

      JS混淆:對js函數的實現代碼進行加密

      JS反混淆:將加密的js代碼解密成原文

    11. 暴力破解: https://www.bm8.com.cn/jsConfusion/

    12. 分析getServerData函數的實現:

      終於找到了ajax請求對應的代碼

      參數d是由getParam(method, object)函數返回的

      method:method

      object:param字典,四個鍵值分別是城市名稱。type,起始時間,結束時間

    13. 請求到的密文數據的解密方式:

      decodeData(data):data參數就是響應的密文數據,返回值就是解密后的原文數據

    14. JS逆向:python可以使用 PyExecJS 庫來實現模擬JavaScript代碼執行。

    15. 環境的安裝:

    ```python
    pip install PyExecJS
    ```
    
    1. 必須還要安裝nodeJs的開發環境 /PyExecJS是依賴nodeJs環境運行的

    2. 請求到加密的響應數據

    3. 將加密的響應數據進行解密

  • 代碼實現:

    import execjs
    import requests
    headers = {
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'
    }
    node = execjs.get()
    file = 'test.js'
    ctx = node.compile(open(file,encoding='utf-8').read())
    
    # 如何五個變量會作為getPostParamCode的參數
    method = 'GETDETAIL'  # GETCITYWEATHER
    city = '北京'
    type = 'HOUR'
    start_time = '2018-01-25 00:00:00'
    end_time = '2018-01-25 23:00:00'
    # 模擬執行getPostParamCode函數
    js = 'getPostParamCode("{0}", "{1}", "{2}", "{3}", "{4}")'.format(method, city, type, start_time, end_time)
    params = ctx.eval(js)
    # print(params)   # 請求參數d
    url = 'https://www.aqistudy.cn/apinew/aqistudyapi.php'
    data = {
        'd':params
    }
    # 獲取了加密的響應數據
    response_code = requests.post(url=url,headers=headers,data=data).text
    # response_code
    
    # 模擬執行decodeData函數對密文數據進行解密
    js = 'decodeData("{0}")'.format(response_code)
    page_text = ctx.eval(js)
    print(page_text)
    

3. 常用的抓包工具

  • 抓包工具概述:

    本質就是一款代理服務器,適用於實現請求和相應的攔截

  • fiddler

    • 默認只可以抓起http協議的請求和相應
    • https:
      • tools-> options-> https-> detrypt HTTPS traffic
      • 含義:將fiddle的證書安裝到本地
  • 青花瓷

  • miteproxy

4. 移動端數據的爬取

  • 配置相關的環境

    • fiddler的配置:
      • tools-> options-> connections-> Allow remote computers to connect/選中
      • 查看fiddler的端口號,並且記住端口號
    • 測試配置是否生效:
      • 在電腦的瀏覽器訪問:http://localhost:端口號/,如果可以訪問成共則表示配置成功
  • 網絡設置:

    • fiddler所在的電腦開啟一個熱點,手機連接熱點
      • 保證手機和電腦同處於同一個網段
  • 在手機中安裝fiddler的證書

    • 手機瀏覽器訪問:http://電腦的ip:端口號/
      • 點擊下載證書的鏈接,進行證書下載
    • 在手機中新任且安裝證書
  • 開啟手機網絡代理

    • wifi-> 點擊感嘆號-> 開啟代理-> 代理的ip就是電腦的ip,端口就是fiddler的端口
  • appnium是一個基於手機應用自動化的模塊

總結:

  1. js加密:為了防止爬蟲,對js代碼進行加密
  2. js逆向:對於python來說,就是將js代碼轉換成python代碼
  3. js混淆:對js函數的實現代碼進行加密
  4. JS反混淆:將加密的js代碼解密成原文
  5. 看js函數是在定義函數還是在調用函數,查看后面是否跟着;號,有的話就是在調用/沒有的話就是在定義


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM