爬取js加密和混淆的例子


作業講解:js逆向

概述

url:https://nyloner.cn/proxy

需求:將這個網頁中的代理ip和端口號進行爬取
難點:
	動態變化的請求參數
	js加密
	需要js逆向

分析

  • 爬取的數據是動態加載

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

    • 意味着:爬取的數據從服務端請求到的是加密的密文數據
  • 頁面每10s刷新一次,刷新后發現數據更新,但是瀏覽器地址欄的url沒有變,說明加載出的數據是由ajax請求到的。

    • 動態加載出來的數據是由ajax請求到的,並且請求到的數據為加密數據
  • 定位到ajax數據包,從中可以看到url和動態變化的請求參數和加密的響應數據

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

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

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

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

      • 通過解析找到了解密的js函數:decode_str(encode_str)
    • 查找encode_str的實現:

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

答案

動態參數解析

# 我們抓包看到 token是加密生成 則把它進行加密
var token = md5(String(page) + String(num) + String(timestamp));
# 進行加密
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

網頁源碼js

# 動態參數加密
function get_proxy_ip(page, num, click_btn) {
    var timestamp = Date.parse(new Date());
    timestamp = timestamp / 1000;
    # token的加密
    var token = md5(String(page) + String(num) + String(timestamp));
    # ajax 請求 類似這樣的url:https://nyloner.cn/proxy?page=1&num=15&token=26c8bc7&t=1575
    $.get('../proxy?page=' + page + '&num=' + num + '&token=' + token + '&t=' + timestamp, function (result) {
        # 判斷 是否為True 
        if (result.status === 'true') {
            var setHtml = "";
            $("#ip-list").html(setHtml);
            var encode_str = result.list;
            # decode_str 是解密函數
            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;
                }
            }

        }
    });
}

獲取token 向url發請求獲取加密的數據

import time
import hashlib
import requests
import base64
#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')
    code
#     return code

將加密的數據進行解析

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=2cj1fd0hpbv64qrxe6xckek8tu3uad4m'
}
code = requests.get(url,headers=headers,params=param).json().get('list')
str_code = decode_str(code)
str_code

js加密,混淆

需求概述

url:https://www.aqistudy.cn/html/city_detail.html
獲取該url的數據

需求分析

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

  • 2.數據是否為動態加載

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

  • 4.在抓包工具的xhr中獲取到了兩個數據包

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

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

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

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

      • type=='HOUR',按照小時為時間單位進行查詢
      • 並沒有在該函數的實現中發現ajax請求對應的代碼,但是發現了另外的兩個函數調用getAQIData();getWeatherData();
        • 分析getWeatherData();和getAQIData()這兩個函數的定義,想要去找到ajax請求對應的代碼:
          • 這兩個函數實現的區別
            • method變量賦值的字符串不一樣
              • GETDETAIL
              • GETCITYWEATHER
          • 相同:
            • 都沒有出現ajax請求對應的代碼,但是發現另一個函數的調用:
              • getServerData(method,param,匿名函數,0.5)
                • method:GETDETAIL或者GETCITYWEATHER
                • param:字典,有四組鍵值對
                  • city:查詢城市的名稱
                  • type:HOUR
                  • starttime:查詢開始時間
                  • endTIME:查詢結束的時間
                    • 分析getServerDate(method,param,匿名函數,0.5)這個函數的實現,還是為了找到ajax請求對應的代碼

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

                    • 發現這個函數的實現代碼看不懂,函數的實現好像是一組密文

                    • 說明:網站對js函數的實現加密

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

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

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

                    • 分析getServerData函數的實現:

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

                    • 參數d的構成:getParam(method,object)返回

                    • method:method

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

                    • 請求到的密文數據的解密方式

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

                    • JS逆向

                    • 使用PyExecJS庫來實現模擬JavaScript代碼執行

                    • 環境的安裝:

                    • pip install PyExecJS

                    • 必須還要安裝nodeJS的開發環境

          • 請求到加密的響應數據
          • 將加密的響應數據進行解密
  • 獲取動態變化且加密的請求參數(d)

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)


免責聲明!

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



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