免责声明
本文章中所有内容仅供学习交流使用,不用于其他任何目的,不提供完整代码,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!
本文章未经许可禁止转载,禁止任何修改后二次传播,擅自使用本文讲解的技术而导致的任何意外,作者均不负责,若有侵权,请在微信公众号【小白逆向】联系作者立即删除!
前言
本篇文章会对AES加密的另外加密方式进行讲解,让大家熟悉不同的加密参数。同时这次逆向目标会使用xhr断点进行堆栈分析。
逆向目标
-
某一艺术:payload中的sig -
逆向首页:aHR0cHM6Ly93d3cudGhlb25lLmFydC9tYXJrZXQ/dHlwZT1jb3B5cmlnaHQ= -
逆向接口:aHR0cHM6Ly9hcGkudGhlb25lLmFydC9tYXJrZXQvYXBpL3NhbGVSZWNvcmQvbGlzdC92Mg== -
脱敏处理(base64编码) -
涉及知识点:AES加解密
网络抓包分析
1、打开网页并打开开发者工具后,刷新网页,进入xhr过滤器中。
在payload中的sig参数
2、可以看到参数中有/ + =
这些特殊字符,就有7成的把握排除Base64编码了。接下来正式开始分析sig加密参数。
sig逆向分析思路
1、首先截取url,将其填入xhr断点中;
2、刷新网页,获取最新数据,使其断住;
3、根据指定url进行堆栈分析;
4、找到加密点,进行参数采取;
5、本地还原。
sig逆向分析
1、将接口urlaHR0cHM6Ly9hcGkudGhlb25lLmFydC9tYXJrZXQvYXBpL3NhbGVSZWNvcmQvbGlzdC92Mg==
填入XHR/fetch Breakpoints
中
然后刷新网页
2、成功断住
向上分析d.send(h)
中的h参数。在当前文件中的30643行为h参数的定义位置。在此行断下断点。
3、在控制台中打印t.data,可以看到其中就是sig参数
再打印t参数,其中就是当前发起请求的参数对象
4、那么接下来开始向下分析堆栈
此时分析堆栈,需要一个一个的观看,并且需要注意t参数的传递位置,一旦错过,就得重新来过
5、当来到b
这个函数时就会发现此时看见了咋们的逆向对象sig
但是在当前位置没有看见其赋值位置,所以继续向下分析堆栈。
来到getLGoddsListGo
函数时,不注意的读者可能会直接跳过当前函数。导致错过了加密点。在let
位置断下断点,然后再次刷新网页。发现在之前的断点var h = t.data;
位置断下了,所以这里把它给取消,因为已经不需要了。然后点击执行到下一断点。成功在最新断点断住。
6、控制台打印其中的参数let result = await this.$api.service.saleRecord_list_v2(this.goodsListParam, null, sigData);
的this.goodsListParam
与sigData
第一个为一个对象,第二个就是当前的逆向目标。
7、向上查找sigData
参数定义与生成位置。
8、在图中的位置全部断下断点,方便后续的分析。
刷新网页
9、在https://www.theone.art/static/js/chunk-171cc8f8.72eb9f69.js
文件的704行断住,然后点击执行到下一断点,并打印sig参数。
发现它是向一个接口中获取了数据。继续向下。然后打印sig.data.data
得到了一个类似乱码的形式。初步估计是一个加密字符串。
10、继续向下,依次打印dataresult、dataResultFun、dataResultId、sigresult
将断点执行到710行的位置
-
将乱码字符串解密出来得到了一个类似于对象的字符串 -
对字符串进行切割,得到了一个自执行函数 -
再次切割,得到了一个数字加}的字符串 -
对得到的自执行函数进行执行,得到了一串数字。
11、点击跳过当前函数
打印sigData
,得到一个乱码字符串,这就是本次逆向目标
12、取消706、710之外的断点,再次刷新网页
进入解密函数 13、11361行断下断点,执行到此,复制当前解密函数与key=d到本地环境
14、进行函数改写,将o.a
改为CryptoJS
,并在最开始导入const CryptoJS = require("crypto-js");
加密包。
const CryptoJS = require("crypto-js");
function decrypt(e) {
var d = "5opkytHOggKj5utjZOgszg==";
var t = CryptoJS.enc.Base64.parse(d)
, a = CryptoJS.AES.decrypt(e, t, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
return CryptoJS.enc.Utf8.stringify(a).toString()
}
en_data = "truiLeKm7AKyuie+33QCYaLbqi/yiH6B+/2GpMEhhFHG6Q+XNOSqj+H3cIMd4QwlxswaTeHDXGbt8wp7e4Frupdr3bf75d4WtMZLKe1XxBJhW2UCotDRo0gDayBnXNKzJhkZTK0QxlinmU60CGhpPwN/SIr67BeBuq+0B/1PsNPiZ8hE7EPyTtVytusxpaGTYmDw6tIiKBOWFdtfv1urpA=="
console.log(decrypt(en_data));
执行测试,成功解密
15、跳出当前函数,继续扣取if
中的代码
let dataresult = _utils_aes__WEBPACK_IMPORTED_MODULE_3__["a"].decrypt(sig.data.data)
, dataResultFun = dataresult.split(",")[0].substr(4)
, dataResultId = dataresult.split(",")[1].split("=")[1]
, sigresult = eval(dataResultFun);
sigData = _utils_aes__WEBPACK_IMPORTED_MODULE_3__["a"].encryptSelf(dataResultId, sigresult)
然后进入_utils_aes__WEBPACK_IMPORTED_MODULE_3__["a"].encryptSelf
加密函数中,扣取相应代码
16、同样的方法,扣取加密函数的代码,并改写加密函数。
// 加密
function encrypt(t, a) {
var key = CryptoJS.enc.Base64.parse(e);
let word = JSON.stringify({
id: t.substr(0, t.length - 1),
sum: a
});
let srcs = CryptoJS.enc.Utf8.parse(word);
let encrypted = CryptoJS.AES.encrypt(srcs, key, {
mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7
});
return encrypted.toString();
}
17、最后优化整体代码
18、但是在sigresult = eval(dataResultFun);
这个执行代码时,会报错deciphering
未定义,直接全局搜索deciphering
将deciphering
扣出来放到window下即可。
deciphering = function(e, t) {
t = t || 32;
var a = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678"
, n = a.length;
for (let c = 0; c < t; c++)
a.charAt(Math.floor(Math.random() * n));
return e
}
19、测试用例
20、在, sig = await this.$api.service.goods_key();
也就是https://www.theone.art/static/js/chunk-171cc8f8.72eb9f69.js
文件的704行位置,是获取加密的数据的接口,在sig
对象中,就有相关接口,所以这里将在Python中动态请求这个接口获取加密数据然后传入js代码中,进行解密。
21、至此,sig逆向到此结束。
数据获取流程
-
通过接口 aHR0cHM6Ly9hcGkudGhlb25lLmFydC9tYXJrZXQvYXBpL2tleS9nZXQ=
获取加密数据; -
传入js获取加密的sig; -
携带sig获取最终数据。
总结
代码示例,不能直接运行:
# **************************************
# --*-- coding: utf-8 --*--
# @Time : 2022-12-07
# @Author : white
# @FileName: 某一艺术.py
# @Software: PyCharm
# **************************************
import requests
import execjs
from urllib.parse import quote
cookies = dict()
def get_encrypt():
'''
1、发起第一次请求获取加密数据,然后解密;
2、解密获取sig的加密参数;
3、加密获取到的参数
4、发送请求获取最终数据
'''
url = "https://脱敏处理/get"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36"
}
res = requests.get(url=url, headers=headers)
return execjs.compile(open('sig.js', encoding="utf-8").read()).call("tripleAesDecrypt", res.json()["data"])
def get_data():
sig = get_encrypt()
url = 'https://脱敏处理/v2'
headers = {
"Accept": "application/json",
"Content-Type": "application/json;charset=UTF-8",
"Host": "脱敏处理",
"Origin": "https://脱敏处理",
"Referer": "https://脱敏处理/",
"sec-ch-ua": ""Not?A_Brand";v="8", "Chromium";v="108", "Google Chrome";v="108"",
"sig": quote(sig),
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36"
}
data = {
"authorId": None,
"chainContract": None,
"commodityCategoryId": None,
"commodityCategoryIdList": [],
"commodityId": None,
"highPrice": "",
"lowPrice": "",
"pageCount": 1,
"pageSize": 20,
"seriesWorks": None,
"seriesWorksId": None,
"sort": {"field": 2, "upOrDown": 1},
"statusSell": 1,
"topicId": None,
"typeMarket": 1,
"commodityTraitList": [],
"sig": sig
}
res = requests.post(url=url, headers=headers, json=data)
print(res.text)
get_data()
成功截图:
相关代码已在github开源:
https://github.com/puboop/reverse/
原文始发于微信公众号(小白逆向):【逆向系列】5-某一艺术sig加密逆向