百某应JS逆向

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环境)

十一、无Node环境执行

十二、实际应用

十三、技巧①


一、发起提问

二、观察发现有两个加密参数: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环境)

将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)

十一、无Node环境执行

首先修改主文件encryption.js的引入部分

再用python文件进行读取并合并两个js文件即可

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("runs_sign")

# 输出结果
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']

十三、技巧①

当我们找不到某个函数在哪里的时候,只要在控制台打印这个函数,然后在控制台点击打印的结果,即可跳转到该函数。

后记 

当我用第二个账号进行逆向时,竟然失败了,报签名异常,调试发现,以前的逆向还能继续使用,但是现在这个账号用了相同的逆向却失败了。

经分析发现:

另外,通过x-bc-sig搜索关键字,在启动器中没有找到。于是只好去源文件的JS中一个一个的找才找到:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

文子阳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值