发送加密逆向

这里可以查看发送的拦截器函数,打断点后刷新页面:

在发送请求的拦截器函数里打断点,发现和发送参数里一样的 data 变量,找到处理 data 变量的两个步骤,分别打断点:

进入对应的函数查看,并打断点,其实到这里就可以发现发送参数的加密过程了:

依然是 CryptoJS 库!!!

最后得到的加密 JS 如下:

var jt = {}
jt.a = require("crypto-js")


var Zt = "$shanghaidianqi$"
var Vt = "2023050814260000"

var sr = function(Xe) {
    Xe = JSON.stringify(Xe);
    var ot = jt.a.enc.Utf8.parse(Zt)
      , Kt = jt.a.enc.Utf8.parse(Vt)
      , kt = jt.a.AES.encrypt(Xe, ot, {
        iv: Kt,
        mode: jt.a.mode.CBC,
        padding: jt.a.pad.Pkcs7
    });
    return JSON.stringify({
        data: kt.toString()
    });
}

返回解密逆向

和上面不同的是,接受拦截器的回调函数代码如下图,就是红色框起来的地方:

这里调用了他本身,传入一个叫 arguments 的参数,这里需要注意的是,cr 里面是一个异步框架的原型,他的大概写法如下:

for(;;)
    switch (Jt.prev = Jt.next) {
    case 0:
        return xxx()
    case 1:
        Jt.sent
        return yyy()
    return 2:
        kt = xxx.sent
        abrupt("return", kt)
    }

这里的每一个 case 都是一个 await,这里的 return 也不是返回,而是到下一个 case。这里 return 后在下一个 case 的地方会使用 sent 接收。这种异步框架就不要单步调试了,他是一个死循环,具体调试步骤看下面的断点。

image.png

做好以上工作之后,开始调试:

image.png

可以发现,到 case3 的地方,密文出现了,注意下面这一行代码:

kt = JSON.parse(mr(kt))

在图片中可以清晰的看到他是密文,但是他经过了 mr 函数之后,就可以 JOSN.parse 转成字符串了,那么这个函数不就是解密函数吗,点进函数查看如下:

很熟悉的格式,一看就是 CryptoJS 库。

最后得到的解密代码如下:

var jt = {}
jt.a = require("crypto-js")

var Zt = "$shanghaidianqi$"
var Vt = "2023050814260000"

var mr = function(Xe) {
    var ot = jt.a.enc.Utf8.parse(Zt)
      , Kt = jt.a.enc.Utf8.parse(Vt)
      , kt = jt.a.AES.decrypt(Xe, ot, {
        iv: Kt,
        mode: jt.a.mode.CBC,
        padding: jt.a.pad.Pkcs7
    })
      , yr = kt.toString(jt.a.enc.Utf8);
    return JSON.parse(yr.toString());
}

上爬虫

import execjs
import requests
import json

from docutils.nodes import organization

url = "https://www.hfhuizhan.com/prod-api/hfhz-exhibition/back/exhibition/listExhibitionNotPage"

accept_file = open("accept_js.js", "r", encoding="utf-8")
accept_code = accept_file.read()
accept_file.close()

send_file = open("send_js.js", "r", encoding="utf-8")
send_code = send_file.read()
send_file.close()

accept_fn = execjs.compile(accept_code)
send_fn = execjs.compile(send_code)

headers = {
    "accept": "application/json",
    "accept-encoding": "gzip, deflate, br, zstd",
    "accept-language": "zh-CN,zh;q=0.9",
    "authorization": "null",
    "cache-control": "no-cache",
    "content-length": "55",
    "content-type": "application/json;charset=UTF-8, application/json;charset=UTF-8",
    "origin": "https://www.hfhuizhan.com",
    "pragma": "no-cache",
    "priority": "u=1, i",
    "referer": "https://www.hfhuizhan.com/schedule",
    "sec-ch-ua": "\"Chromium\";v=\"135\", \"Not-A.Brand\";v=\"8\"",
    "sec-ch-ua-mobile": "?0",
    "sec-ch-ua-platform": "\"Windows\"",
    "sec-fetch-dest": "empty",
    "sec-fetch-mode": "cors",
    "sec-fetch-site": "same-origin",
    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36"
}

ming_data = {
    "yyyyMM":"2024-04"
}

payload_data = send_fn.call("sr", ming_data)

resp = requests.post(url=url, data=payload_data, headers=headers)
ming = accept_fn.call("mr", resp.text)

data = ming["data"]

for item in data:
    name = item["name"]
    organizer = item["organizer"]
    startTime = item["startTime"]
    endTime = item["endTime"]

    print(f"name:{name},organizer:{organizer},startTime:{startTime},endTime:{endTime}")