JS逆向|某音滑块captchaBody分析
字数 1104 2025-08-22 12:22:15
某音滑块captchaBody逆向分析教学文档
逆向目标
目标网址:aHR0cHM6Ly9jcmVkaXQuaGQuZ292LmNuL3h5eHhncy8= (Base64编码)
主要目标:
- 分析接口参数加密(queryContent和sign)
- 分析响应数据解密(application/octet-stream格式)
抓包分析
-
点击"加载更多"时发送
xzxkfr数据包 -
需要逆向的参数:
queryContent:请求参数sign:请求签名nonceStr:随机字符串(无需逆向)
-
响应头为
application/octet-stream,需要逆向解密
加密参数分析
queryContent加密
-
加密流程:
- 条件判断:
"1" === r.headers["C-GATEWAY-QUERY-ENCRYPT"]且n.encryptType === P.ENCRYPT_TYPE.SM4 - 使用SM4加密:
i = ls.sm4.encrypt(i, e.encryptKey)
- 条件判断:
-
SM4加密实现:
const sm4 = require('sm-crypto').sm4
const msg = '' // 明文
const key = '' // 密钥
let encryptData = sm4.encrypt(msg, key) // 默认输出16进制字符串,使用pkcs#7填充
// 可选参数:
let encryptData = sm4.encrypt(msg, key, {
padding: 'none', // 不使用padding
output: 'array', // 输出字节数组
mode: 'cbc', // CBC模式
iv: 'fedcba98765432100123456789abcdef' // IV向量
})
- 本地模拟验证:
const sm4 = require('sm-crypto').sm4
const msg = '' // 明文
const key = '' // 密钥
console.log(sm4.encrypt(msg, key, {output: 'string'}))
sign签名
-
签名流程:
n.sign = o- 签名类型为SM2:
o = ls.sm2.signature(a, e.appSignPrivateKey, e.appSignPublicKey, e.appId) a是由queryContent等参数组成的字符串
-
SM2签名实现:
const sm2 = require('sm-crypto').sm2
const msgString = '' // 待签名数据
const privateKey = '' // 私钥
const publicKey = '' // 公钥
const userId = '' // 用户ID(默认为1234567812345678)
let sigValueHex6 = sm2.doSignature(msgString, privateKey, {
hash: true,
publicKey,
userId
})
- 签名后处理函数
Zi:
const BigInteger = require('jsbn').BigInteger;
function Xi(t) {
return new BigInteger(t, 10).toString(16)
}
function Yi(t) {
return new BigInteger(t, 16).toString(10)
}
响应数据解密
-
解密流程:
- 响应拦截器
interceptors.response.use中找到解密逻辑 - 主要解密函数
oo,使用SM4解密
- 响应拦截器
-
响应数据处理:
// 处理响应数据
var r = t.data // arraybuffer
var n = new DataView(r)
var i = new Uint8Array(r)
var s = {}
var a = 40
// 分割数据
D.forEach(function(t, e) {
var r = n.getInt32(4 * e)
s[t] = i.subarray(a, a + r)
a += r
})
// 验证签名
var o = ao(s, e)
var u = o[0]
var h = o[1]
if (!u) return Promise.reject(new Error("验签失败"))
// SM4解密
var c = "{}"
c = kr.sm4.decrypt(
function(t) {
for (var e = "", r = 0; r < t.length; r++) {
var n = t[r].toString(16)
1 === n.length && (n = "0" + n)
e += n
}
return e
}(s.body),
e.encryptKey,
{output: "string"}
)
- 辅助函数实现:
// Qi函数(MD5哈希)
const CryptoJS = require('crypto-js')
function Qi(t) {
var e = CryptoJS.lib.WordArray.create(t)
return CryptoJS.MD5(e).toString(CryptoJS.enc.Hex)
}
// ji函数(Base64编码)
var ji = function(t) {
return Buffer.from(t).toString("base64")
}
// SM2验签
const sm2 = require('sm-crypto').sm2
sm2.doVerifySignature(a, Zi(i), e.platformPublicKey, {
hash: !0,
userId: e.appId
})
// SM4解密
const sm4 = require('sm-crypto').sm4
sm4.decrypt(
function(t) {
for (var e = "", r = 0; r < t.length; r++) {
var n = t[r].toString(16)
1 === n.length && (n = "0" + n)
e += n
}
return e
}(s.body),
e.encryptKey,
{output: "string"}
)
- Base64转ArrayBuffer:
function base64ToArrayBuffer(base64) {
var binary_string = atob(base64)
var len = binary_string.length
var bytes = new Uint8Array(len)
for (var i = 0; i < len; i++) {
bytes[i] = binary_string.charCodeAt(i)
}
return bytes.buffer
}
关键点总结
-
加密算法:
- 请求参数使用SM4加密
- 签名使用SM2算法
- 响应数据使用SM4解密
-
依赖库:
sm-crypto:SM2/SM4算法实现jsbn:大整数处理crypto-js:MD5哈希
-
注意事项:
- SM2每次加密会生成新的随机数,本地验证需通过接口返回判断
- 响应数据需要先分割处理后再解密
- 注意各种数据格式转换(ArrayBuffer、Hex、Base64等)
-
验证方法:
- 对比网页和本地加密结果是否一致
- 通过实际接口请求验证参数是否正确
- 检查解密后的数据是否符合预期格式
完整流程
- 构造请求参数
- 使用SM4加密queryContent
- 使用SM2生成签名sign
- 发送请求获取加密响应
- 处理响应数据(分割、验签)
- 使用SM4解密响应体
- 解析最终数据
通过以上步骤,可以完整实现该接口的加密请求和响应解密流程。