一個案例入門補環境

七夜魔手發表於2024-10-16

此分享只用於學習用途,不作商業用途,若有冒犯,請聯絡處理

反爬前置資訊

站點:aHR0cDovLzEyMC4yMTEuMTExLjIwNjo4MDkwL3hqendkdC94anp3ZHQvcGFnZXMvaW5mby9wb2xpY3k=
介面:/xjzwdt/rest/xmzInfoDeliveryRest/getInfoDeliveryList

破解結果預覽

image

何謂補環境

補環境其實是補瀏覽器有而Node沒有的環境,即補BOM和DOM的物件,一切環境補的結果都是向瀏覽器實際結果靠齊,入門補環境只需要記住缺啥補啥這個技巧,當執行提示缺少某個環境,則直接在瀏覽器執行該環境是啥結果然後補上該結果。
image

反爬機制

站點有兩個反爬項,請求體加密響應結果解密
image
image

逆向研究

補環境處理的是關鍵程式碼,所以我們還是要定位到關鍵程式碼位置拿到關鍵程式碼

找到介面的呼叫堆疊,點選下面圖片標識的堆疊進入程式碼
image

1處打上斷點重新發起請求,在2處可以看到這裡已經實現請求體加密了,那我們得往前走,也就是點選3處標識堆疊
image

然後在1處打上斷點重新發起請求,點選2處進入函式引用程式碼位置
image

這裡就能看到關鍵程式碼了,它重新封裝了ajax,請求體加密響應結果解密都是在這裡進行的
image

但是我們要補的不是這個位置,因為我們又不是直接構建請求的,我們要破解的只是加解密,所以要補的是window.sm2Util.encryptwindow.sm2Util.aesDecrypt,而它們都在sm2Util.js檔案中,接下來我們直接補sm2Util.js程式碼就行。

注意直接拿全部程式碼,不要格式化
image

補環境

先直接執行程式碼,發現報下面所示錯誤,這是因為window不是Node的環境,這裡一般是使用global替代
image

補好window環境後再次執行報下面所示錯誤,繼續補navigator
image

補好navigator環境後再次執行報下面所示錯誤,這是因為在瀏覽器環境下exportsundefined,但是在Node環境下exportsObject物件,所以這裡我們要處理
image

補好exports後就不會報錯了
image

列印看看發現能拿到想要的方法了,這裡就初步補好環境了,要確定是否真正補全了還得執行一下程式碼,我們就拿除錯時拿到的請求體加密原文進行除錯
image

ok,這裡測試的請求體加密成功了,說明encrypt補好了,接下來測試下響應文字解密,直接拿介面返回的響應文字測試即可
image

ok,這裡測試的響應文字解密也成功了,也就說明咱們補好了。
image

補環境補充-掛代理

上面講解遇到缺失的環境都是物件本身,所以我們能直接知道缺了啥,然後對此做出處理。

但是如果缺失的是物件某個引數呢,比如下圖所示我們是沒法知道是哪個物件哪個引數出的問題的。大家可能會說不是有程式碼位置提示嗎,但是這是在明文程式碼下能清楚提示,遇到混淆程式碼就不行了,所以我們需要掛代理
image

所謂掛代理其實就是使用Proxy物件,它是一種物件代理機制,可以在物件和函式之間新增一箇中間層,從而實現對物件和函式的攔截和控制。

使用下面這段程式碼,新增想要監控的物件

function getEnv(proxy_array) {
    for (var i = 0; i < proxy_array.length; i++) {
        handler = `{\n
            get: function(target, property, receiver) {\n
                   console.log('方法:get','    物件:${proxy_array[i]}','    屬性:',property,'    屬性型別:',typeof property,'    屬性值型別:',typeof target[property]);
                   return target[property];
            },
            set: function(target, property, value, receiver){\n
                    console.log('方法:set','    物件:${proxy_array[i]}','    屬性:',property,'    屬性型別:',typeof property,'    屬性值型別:',typeof target[property]);
                    return Reflect.set(...arguments);
            }
        }`;
        eval(`
            try {
                ${proxy_array[i]};
                ${proxy_array[i]} = new Proxy(${proxy_array[i]}, ${handler});
            } catch (e) {
                ${proxy_array[i]} = {};
                ${proxy_array[i]} = new Proxy(${proxy_array[i]}, ${handler});
            }
        `)
    }
}

這樣它會列印出呼叫的物件、屬性等資訊,這不就清晰多了。
image

我們在本案例測試,可以看到程式碼使用的全部環境了,然後看到哪些環境或者屬性是undefined就把它補好。
image

程式碼展示

import json
import subprocess
from functools import partial

import requests

subprocess.Popen = partial(subprocess.Popen, encoding="utf-8")  # 修改全域性編碼
import execjs  # 必須在修改編碼後引入

with open('sm2Utils.js', 'r', encoding='utf8') as fr:
    str_data = fr.read()
js_code = execjs.compile(str_data)

headers = {
    "Accept": "application/json, text/javascript, */*; q=0.01",
    "Accept-Language": "zh-CN,zh;q=0.9",
    "Content-Type": "application/json;charset=UTF-8",
    "Origin": "Origin",
    "Referer": "Referer",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36",
    "X-Requested-With": "XMLHttpRequest",
    "encrypt": "1"
}
url = "url "
params = {
    "foreSessionClusterIntercept": "true"
}
post_data = {"token": "Epoint_WebSerivce_**##0601",
             "params": {"categuids": "4bcbbec7-2428-403a-8eed-b0db5c0e01a5", "titlename": "", "currpage": 0,
                        "pagesize": 10}}
print('encrypt data: ', json.dumps(post_data, separators=(',', ':')))
data = js_code.call('encrypt', json.dumps(post_data, separators=(',', ':')))
print('encrypt result: ', data)
response = requests.post(url, headers=headers, params=params, data=data, verify=False)
print('request result: ', response.text)
decrypt_result = js_code.call('decrypt', response.text)
print('decrypt result: ', decrypt_result)

相關文章