https://ying.baichuan-ai.com/
目录
一、发起提问
二、观察发现有两个加密参数:X-Bc-Sig和X-Bc-Ts
三、观察JS调用栈
四、从JS中搜索 X-Bc-Sig和X-Bc-Ts
五、断点并分析参数的生成方式
六、分析入参
七、发现关键的o方法调用了一个i()方法
八、验证结果
九、python执行Node.JS获取参数(本地安装了Node环境)
十、python本地执行JS(本地未安装Node.js环境)
十一、实际应用
一、发起提问

二、观察发现有两个加密参数:X-Bc-Sig和X-Bc-Ts
 三、观察JS调用栈
 

四、从JS中搜索 X-Bc-Sig和X-Bc-Ts
发现只有_app-xxxxx.js文件中存在X-Bc-Sig

五、断点并分析参数的生成方式

六、分析入参
一个参数是固定字符串:"uwlACMuXQApWgO0Q"
一个参数是时间戳组成的动态字符串:"1721891456251retry=3&thread_info=[object Object]"

七、发现关键的o方法调用了一个i()方法
找了一圈没找到i()方法,无奈我只好在控制台打印一下,看看这个i()是什么样的
断点挺住,在去控制台打印一下

七、模拟生成
1、先将关键的o方法复制出来

2、模拟i()方法

3、 调用o方法和截取逻辑复制过来
4、构建参数
我们可以看到n是当前时间戳 n = Date.now()
r是混淆参数 undefined
a是由时间戳组成的固定字符串

 
八、验证结果


九、python执行Node.JS获取参数(本地安装了Node环境)
import execjs
# JavaScript代码
js_code = """
const CryptoJS = require("crypto-js");
function i() {
    return CryptoJS
}
function o(t, e) {
    let r = i().enc.Utf8.parse("0000000000000000");
    return i().AES.encrypt(t, i().enc.Utf8.parse(e), {
        iv: r,
        mode: i().mode.CBC,
        padding: i().pad.Pkcs7
    }).toString()
}
//构建入参
const n = '1721892945291'
const r = undefined
const a = `${n.toString()}retry=3&thread_info=[object Object]`
// 执行关键o函数
let s = o(a, r ? r.substring(0, 16) : "uwlACMuXQApWgO0Q");
// 截取逻辑
s.length > 64 && (s = s.substring(0, 64))
result = {
    "x-bc-sig": s,
    "x-bc-ts": n.toString()
}
return result
"""
# 编译JavaScript代码
ctx = execjs.compile(js_code)
# 执行JavaScript代码并获取结果
result = ctx.call("JSON.stringify")
# 输出结果
print(result)
 

十、python本地执行JS(本地未安装Node.js环境)
将crypto-js下载到本地:
crypto-js.min.js
CRYPTO-JS.MIN.JS: DOWNLOAD - CDNPKG
encryption.js
//encryption.js
//引入本地加密库
const CryptoJS = require('./crypto-js.min.js');
function i() {
    return CryptoJS
}
function o(t, e) {
    let r = i().enc.Utf8.parse("0000000000000000");
    return i().AES.encrypt(t, i().enc.Utf8.parse(e), {
        iv: r,
        mode: i().mode.CBC,
        padding: i().pad.Pkcs7
    }).toString()
}
//构建入参
const n = '1721892945291'
const r = undefined
const a = `${n.toString()}retry=3&thread_info=[object Object]`
// 执行关键o函数
let s = o(a, r ? r.substring(0, 16) : "uwlACMuXQApWgO0Q");
// 截取逻辑
return s.length > 64 && (s = s.substring(0, 64)),
            {
                "x-bc-sig": s,
                "x-bc-ts": n.toString()
            } 
import execjs
# 读取crypto-js库文件和你的JavaScript代码
with open('crypto-js.min.js', 'r', encoding='utf-8') as file:
    crypto_js_code = file.read()
with open('encryption.js', 'r', encoding='utf-8') as file:
    script_code = file.read()
# 整合JavaScript代码
js_code = crypto_js_code + "\n" + script_code
# 编译JavaScript代码
ctx = execjs.compile(js_code)
# 执行JavaScript代码并获取结果
result = ctx.call("JSON.stringify")
# 输出结果
print(result)
 

十一、实际应用
发现runs接口的签名和delete接口的签名有所不同
runs直接对时间戳签名就可以
delete接口需要对时间戳和id进行双重签名
我只能写成两个function来供python调用
//引入本地加密库
const CryptoJS = require('./crypto-js.min.js');
function i() {
    return CryptoJS
}
function o(t, e) {
    let r = i().enc.Utf8.parse("0000000000000000");
    return i().AES.encrypt(t, i().enc.Utf8.parse(e), {
        iv: r,
        mode: i().mode.CBC,
        padding: i().pad.Pkcs7
    }).toString()
}
function runs_sign() {
    //构建入参
    const n = Date.now()
    const r = undefined
    const a = `${n.toString()}retry=3&thread_info=[object Object]`
    // const a = `${n.toString()}id=6042075`
    // 执行关键o函数
    let s = o(a, r ? r.substring(0, 16) : "uwlACMuXQApWgO0Q");
    // 截取逻辑
    return s.length > 64 && (s = s.substring(0, 64)),
        {
            "x-bc-sig": s,
            "x-bc-ts": n.toString()
        }
}
function delete_sign(id) {
    //构建入参
    const n = Date.now()
    const r = undefined
    const a = `${n.toString()}id=${id}`
    // 执行关键o函数
    let s = o(a, r ? r.substring(0, 16) : "uwlACMuXQApWgO0Q");
    // 截取逻辑
    return s.length > 64 && (s = s.substring(0, 64)),
        {
            "x-bc-sig": s,
            "x-bc-ts": n.toString()
        }
}
 
 
    def get_runs_sign_and_timestamp(self):
        """
        获取访问签名和时间戳
        """
        # 读取crypto-js库文件和你的JavaScript代码
        with open('crypto-js.min.js', 'r', encoding='utf-8') as file:
            crypto_js_code = file.read()
        with open('encryption.js', 'r', encoding='utf-8') as file:
            script_code = file.read()
        # 整合JavaScript代码
        js_code = crypto_js_code + "\n" + script_code
        # 编译JavaScript代码
        ctx = execjs.compile(js_code)
        # 执行JavaScript代码并获取结果
        result = ctx.call("runs_sign")
        return result['x-bc-sig'], result['x-bc-ts']
    def get_delete_sign_and_timestamp(self, id):
        """
        获取删除签名和时间戳
        """
        # 读取crypto-js库文件和你的JavaScript代码
        with open('crypto-js.min.js', 'r', encoding='utf-8') as file:
            crypto_js_code = file.read()
        with open('encryption.js', 'r', encoding='utf-8') as file:
            script_code = file.read()
        # 整合JavaScript代码
        js_code = crypto_js_code + "\n" + script_code
        # 编译JavaScript代码
        ctx = execjs.compile(js_code)
        # 执行JavaScript代码并获取结果
        result = ctx.call("delete_sign", id)
        return result['x-bc-sig'], result['x-bc-ts'] 
十二、技巧①
当我们找不到某个函数在哪里的时候,只要在控制台打印这个函数,然后在控制台点击打印的结果,即可跳转到该函数。



![[H并查集] lc100347. 判断矩形的两个角落是否可达(并查集+高质量+周赛408_4)](https://i-blog.csdnimg.cn/direct/c527738e837744aba3298e2272d78ac4.png)
















