分析过程
1.打开网站,我们只看xhr,点击翻页,只有一个数据包,载荷以及响应数据都是加密的,并且请求的header头也有两个加密的参数Requestid和Sign.
2.我们先解决请求头的参数,我们通过搜索大法,搜索requestid,看一下只有第一个是在生成,其他地方的都是在使用,直接点进第一个,下断点翻页成功断住,requestId = getUuid(),我们抠出来getUuid函数即可。
function getUuid() {
var s = [];
var hexDigits = "0123456789abcdef";
for (var i = 0; i < 32; i++) {
s[i] = hexDigits.substr(Math.floor(Math.random() * 16), 1)
}
s[14] = "4";
s[19] = hexDigits.substr(s[19] & 3 | 8, 1);
s[8] = s[13] = s[18] = s[23];
var uuid = s.join("");
return uuid
}
3.我们接着看,sign = MD5(data + requestId + timestamp),经过测试是标准的MD5算法,各个字段内容如下第二个图。
4.我们看看options.data,就是我们请求时,载荷的加密的字符串,通过encrypt.encryptLong对=='{“limit”:“20”,“page”:“3”}'明文进行加密,我们扣一下encrypt.encryptLong==代码。
JSEncrypt.prototype.encryptLong = function(str) {
try {
var encrypted = this.getKey().encryptLong(str) || "";
var uncrypted = this.getKey().decryptLong(encrypted) || "";
var count = 0;
var reg = /null$/g;
while (reg.test(uncrypted)) {
count++;
encrypted = this.getKey().encryptLong(str) || "";
uncrypted = this.getKey().decryptLong(encrypted) || "";
if (count > 10) {
break
}
}
return encrypted
} catch (ex) {
return false
}
}
//这段代码分析,看到中间 this.getKey().decryptLong 这个函数还要进入内层进行扣取
RSAKey.prototype.encryptLong = function(text) {
var _this = this;
var maxLength = (this.n.bitLength() + 7 >> 3) - 11;
try {
var ct_1 = "";
if (text.length > maxLength) {
var lt = text.match(/.{1,117}/g);
lt.forEach(function(entry) {
var t1 = _this.encrypt(entry);
ct_1 += t1
});
return hex2b64(ct_1)
}
var t = this.encrypt(text);
var y = hex2b64(t);
return y
} catch (ex) {
return false
}
}
//进入后可以看到这个是RSA的非对称加密中间还有个hex2b64这个算法,为了保险起见也将这个算法一起copy
function hex2b64(h) {
var i;
var c;
var ret = "";
for (i = 0; i + 3 <= h.length; i += 3) {
c = parseInt(h.substring(i, i + 3), 16);
ret += b64map.charAt(c >> 6) + b64map.charAt(c & 63)
}
if (i + 1 == h.length) {
c = parseInt(h.substring(i, i + 1), 16);
ret += b64map.charAt(c << 2)
} else if (i + 2 == h.length) {
c = parseInt(h.substring(i, i + 2), 16);
ret += b64map.charAt(c >> 2) + b64map.charAt((c & 3) << 4)
}
while ((ret.length & 3) > 0) {
ret += b64pad
}
return ret
}
//这些参数也要带上不然报错
var b64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var b64pad = "=";
5.看上面使用了var encrypt = new JSEncrypt;这个算法库,到时候直接导入算法库。
6.这里我们用const JSEncrypt = require(“jsencrypt”);这个库,安装命令npm install jsencrypt,整合一下代码如下:
const JSEncrypt = require("jsencrypt");
const CryptoJS = require("crypto-js");
function MD5(data) {
return CryptoJS.MD5(data).toString();
}
function getUuid() {
var s = [];
var a = "0123456789abcdef";
for (var i = 0; i < 32; i++) {
s[i] = a.substr(Math.floor(Math.random() * 0x10), 1)
}
s[14] = "4";
s[19] = a.substr((s[19] & 0x3) | 0x8, 1);
s[8] = s[13] = s[18] = s[23];
var b = s.join("");
return b
}
var b64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var b64pad = "=";
function hex2b64(h) {
var i;
var c;
var ret = "";
for (i = 0; i + 3 <= h.length; i += 3) {
c = parseInt(h.substring(i, i + 3), 16);
ret += b64map.charAt(c >> 6) + b64map.charAt(c & 63);
}
if (i + 1 == h.length) {
c = parseInt(h.substring(i, i + 1), 16);
ret += b64map.charAt(c << 2);
}
else if (i + 2 == h.length) {
c = parseInt(h.substring(i, i + 2), 16);
ret += b64map.charAt(c >> 2) + b64map.charAt((c & 3) << 4);
}
while ((ret.length & 3) > 0) {
ret += b64pad;
}
return ret;
}
JSEncrypt.prototype.encryptLong = function(str) {
try {
var encrypted = this.getKey().encryptLong(str) || "";
var uncrypted = this.getKey().decryptLong(encrypted) || "";
var count = 0;
var reg = /null$/g;
while (reg.test(uncrypted)) {
count++;
encrypted = this.getKey().encryptLong(str) || "";
uncrypted = this.getKey().decryptLong(encrypted) || "";
if (count > 10) {
break
}
}
return encrypted
} catch (ex) {
return false
}
}
function sort_ASCII(obj) {
var arr = new Array;
var num = 0;
for (var i in obj) {
arr[num] = i;
num++
}
var sortArr = arr.sort();
var sortObj = {};
for (var i in sortArr) {
sortObj[sortArr[i]] = obj[sortArr[i]]
}
return sortObj
}
function get_params(page) {
var paramPublicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCvxXa98E1uWXnBzXkS2yHUfnBM6n3PCwLdfIox03T91joBvjtoDqiQ5x3tTOfpHs3LtiqMMEafls6b0YWtgB1dse1W5m+FpeusVkCOkQxB4SZDH6tuerIknnmB/Hsq5wgEkIvO5Pff9biig6AyoAkdWpSek/1/B7zYIepYY0lxKQIDAQAB";
var encrypt = new JSEncrypt();
encrypt.setPublicKey(paramPublicKey);
var c = Date.parse(new Date());
var d = getUuid();
var e = JSON.stringify(sort_ASCII({"limit": "20", "page":page}));
var data = encrypt.encryptLong(e);
var f = MD5(e + d + c);
return {
"timestamp": c,
"requestId": d,
"sign": f,
"data": data,
}
}
console.log(get_params(1))
7.结果发现data是false,我们在扣的代码,加一句看一下是什么错误,发现this.getKey().decryptLong不是一个函数
8.我们打上断点并去网页也打上断点做一下比较,网页是一个函数,我们这是undefined,我们点进去看一下,
8.它是在RSAKey的原型链上添加函数,我们直接这样添加,发现本地和网页的不一样,修改一下就可以了,修改玩的代码如下:。
var encrypt = new JSEncrypt();
encrypt.setPublicKey(paramPublicKey);
encrypt.encryptLong = function(text) {
var _this = this.getKey();//改动了
var maxLength = (_this.n.bitLength() + 7 >> 3) - 11;
try {
var ct_1 = "";
if (text.length > maxLength) {
var lt = text.match(/.{1,117}/g);
lt.forEach(function(entry) {
var t1 = _this.encrypt(entry);
ct_1 += t1
});
return hex2b64(ct_1)
}
var t = _this.encrypt(text);
var y = hex2b64(t);
return y
} catch (ex) {
console.log(ex)
return false
}
}
encrypt.decryptLong = function(text) {
var _this = this.getKey();//改动了
var maxLength = _this.n.bitLength() + 7 >> 3;
text = b64tohex(text);
try {
if (text.length > maxLength) {
var ct_2 = "";
var lt = text.match(/.{1,256}/g);
lt.forEach(function(entry) {
var t1 = _this.decrypt(entry);
ct_2 += t1
});
return ct_2
}
var y = _this.decrypt(text);
return y
} catch (ex) {
return false
}
}
9.到此,header以及载荷的加密已经完成了,完整代码如下:
const JSEncrypt = require("jsencrypt");
const CryptoJS = require("crypto-js");
function MD5(data) {
return CryptoJS.MD5(data).toString();
}
function getUuid() {
var s = [];
var a = "0123456789abcdef";
for (var i = 0; i < 32; i++) {
s[i] = a.substr(Math.floor(Math.random() * 0x10), 1)
}
s[14] = "4";
s[19] = a.substr((s[19] & 0x3) | 0x8, 1);
s[8] = s[13] = s[18] = s[23];
var b = s.join("");
return b
}
var b64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var b64pad = "=";
function hex2b64(h) {
var i;
var c;
var ret = "";
for (i = 0; i + 3 <= h.length; i += 3) {
c = parseInt(h.substring(i, i + 3), 16);
ret += b64map.charAt(c >> 6) + b64map.charAt(c & 63);
}
if (i + 1 == h.length) {
c = parseInt(h.substring(i, i + 1), 16);
ret += b64map.charAt(c << 2);
}
else if (i + 2 == h.length) {
c = parseInt(h.substring(i, i + 2), 16);
ret += b64map.charAt(c >> 2) + b64map.charAt((c & 3) << 4);
}
while ((ret.length & 3) > 0) {
ret += b64pad;
}
return ret;
}
JSEncrypt.prototype.encryptLong = function(str) {
try {
var encrypted = this.getKey().encryptLong(str) || "";
var uncrypted = this.getKey().decryptLong(encrypted) || "";
var count = 0;
var reg = /null$/g;
while (reg.test(uncrypted)) {
count++;
encrypted = this.getKey().encryptLong(str) || "";
uncrypted = this.getKey().decryptLong(encrypted) || "";
if (count > 10) {
break
}
}
return encrypted
} catch (ex) {
console.log(ex)
return false
}
}
function sort_ASCII(obj) {
var arr = new Array;
var num = 0;
for (var i in obj) {
arr[num] = i;
num++
}
var sortArr = arr.sort();
var sortObj = {};
for (var i in sortArr) {
sortObj[sortArr[i]] = obj[sortArr[i]]
}
return sortObj
}
function get_params(page) {
var paramPublicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCvxXa98E1uWXnBzXkS2yHUfnBM6n3PCwLdfIox03T91joBvjtoDqiQ5x3tTOfpHs3LtiqMMEafls6b0YWtgB1dse1W5m+FpeusVkCOkQxB4SZDH6tuerIknnmB/Hsq5wgEkIvO5Pff9biig6AyoAkdWpSek/1/B7zYIepYY0lxKQIDAQAB";
var encrypt = new JSEncrypt();
encrypt.setPublicKey(paramPublicKey);
encrypt.encryptLong = function(text) {
var _this = this.getKey();
var maxLength = (_this.n.bitLength() + 7 >> 3) - 11;
try {
var ct_1 = "";
if (text.length > maxLength) {
var lt = text.match(/.{1,117}/g);
lt.forEach(function(entry) {
var t1 = _this.encrypt(entry);
ct_1 += t1
});
return hex2b64(ct_1)
}
var t = _this.encrypt(text);
var y = hex2b64(t);
return y
} catch (ex) {
console.log(ex)
return false
}
}
encrypt.decryptLong = function(text) {
var _this = this.getKey();
var maxLength = _this.n.bitLength() + 7 >> 3;
text = b64tohex(text);
try {
if (text.length > maxLength) {
var ct_2 = "";
var lt = text.match(/.{1,256}/g);
lt.forEach(function(entry) {
var t1 = _this.decrypt(entry);
ct_2 += t1
});
return ct_2
}
var y = _this.decrypt(text);
return y
} catch (ex) {
return false
}
}
var c = Date.parse(new Date());
var d = getUuid();
var e = JSON.stringify(sort_ASCII({"limit": "20", "page":page}));
var data = encrypt.encryptLong(e);
var f = MD5(e + d + c);
return {
"timestamp": c,
"requestId": d,
"sign": f,
"data": data,
}
}
console.log(get_params(1))
10.接下来,我们对响应数据进行解密,我们通过启动器进去,前两个直接跳过,在疑似位置打上断点,点击下一页,成功断住,发现了解密函数,我们继续;
11.进去之后发现关键词decode,下断点,进去;
12.进去之后发现是混淆,我们还原一下,就是AES;接下来就可以写代码了。
_0x48989b[_0x1b50('0x20')]['decode'] = function(_0x291626) {
var _0x3c6fa1 = CryptoJS['enc'][_0x1b50('0xc')][_0x1b50('0x5')](this[_0x1b50('0x27')](this[_0x1b50('0x28')]))
, _0x3ec027 = CryptoJS[_0x1b50('0xe')][_0x1b50('0xc')][_0x1b50('0x5')](this[_0x1b50('0x27')](this['iv']));
return CryptoJS[_0x1b50('0x13')][_0x1b50('0x1b')](_0x291626, _0x3c6fa1, {
'iv': _0x3ec027,
'mode': CryptoJS[_0x1b50('0x3d')]['CBC'],
'padding': CryptoJS['pad'][_0x1b50('0x24')]
})['toString'](CryptoJS['enc']['Utf8']);
}
最终代码
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
'''
@Project :pythonProject
@File :test.py
@IDE :PyCharm
@Author :haozaispider(微信同号)
@Date :2024/12/10 下午3:59
'''
import execjs
import requests
from loguru import logger
headers = {
"Accept": "application/json, text/javascript, */*; q=0.01",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
"Origin": "https://www.birdreport.cn",
"Pragma": "no-cache",
"Referer": "https://www.birdreport.cn/",
"Sec-Fetch-Dest": "empty",
"Sec-Fetch-Mode": "cors",
"Sec-Fetch-Site": "same-site",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36 Edg/129.0.0.0",
# "requestId": "9aec1349d68bad4316d975cdf4a41eb8",
"sec-ch-ua": "\"Microsoft Edge\";v=\"129\", \"Not=A?Brand\";v=\"8\", \"Chromium\";v=\"129\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"Windows\"",
# "sign": "e8b0161b00e9acafdfd1b26b7cc4da6d",
# "timestamp": "1727145614000"
}
url = "https://api.birdreport.cn/front/activity/search"
params_obj = execjs.compile(open("./test.js", 'r', encoding="utf-8").read()).call('get_params',3)
logger.info('params_obj ===》》》 '+ str(params_obj))
headers["sign"] = params_obj["sign"]
headers["timestamp"] = str(params_obj["timestamp"])
headers["requestId"] = params_obj["requestId"]
data = params_obj["data"]
response = requests.post(url, headers=headers,
data=data
)
logger.info('密文数据===》》》 ' + response.text)
decrypt_data = execjs.compile(open("./test.js", 'r', encoding="utf-8").read()).call('decrypt_data',response.json()["data"])
logger.info('明文数据===》》》 ' + decrypt_data)
const JSEncrypt = require("jsencrypt");
const CryptoJS = require("crypto-js");
function MD5(data) {
return CryptoJS.MD5(data).toString();
}
function getUuid() {
var s = [];
var a = "0123456789abcdef";
for (var i = 0; i < 32; i++) {
s[i] = a.substr(Math.floor(Math.random() * 0x10), 1)
}
s[14] = "4";
s[19] = a.substr((s[19] & 0x3) | 0x8, 1);
s[8] = s[13] = s[18] = s[23];
var b = s.join("");
return b
}
var b64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var b64pad = "=";
function hex2b64(h) {
var i;
var c;
var ret = "";
for (i = 0; i + 3 <= h.length; i += 3) {
c = parseInt(h.substring(i, i + 3), 16);
ret += b64map.charAt(c >> 6) + b64map.charAt(c & 63);
}
if (i + 1 == h.length) {
c = parseInt(h.substring(i, i + 1), 16);
ret += b64map.charAt(c << 2);
}
else if (i + 2 == h.length) {
c = parseInt(h.substring(i, i + 2), 16);
ret += b64map.charAt(c >> 2) + b64map.charAt((c & 3) << 4);
}
while ((ret.length & 3) > 0) {
ret += b64pad;
}
return ret;
}
JSEncrypt.prototype.encryptLong = function(str) {
try {
var encrypted = this.getKey().encryptLong(str) || "";
var uncrypted = this.getKey().decryptLong(encrypted) || "";
var count = 0;
var reg = /null$/g;
while (reg.test(uncrypted)) {
count++;
encrypted = this.getKey().encryptLong(str) || "";
uncrypted = this.getKey().decryptLong(encrypted) || "";
if (count > 10) {
break
}
}
return encrypted
} catch (ex) {
console.log(ex)
return false
}
}
function sort_ASCII(obj) {
var arr = new Array;
var num = 0;
for (var i in obj) {
arr[num] = i;
num++
}
var sortArr = arr.sort();
var sortObj = {};
for (var i in sortArr) {
sortObj[sortArr[i]] = obj[sortArr[i]]
}
return sortObj
}
function get_params(page) {
var paramPublicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCvxXa98E1uWXnBzXkS2yHUfnBM6n3PCwLdfIox03T91joBvjtoDqiQ5x3tTOfpHs3LtiqMMEafls6b0YWtgB1dse1W5m+FpeusVkCOkQxB4SZDH6tuerIknnmB/Hsq5wgEkIvO5Pff9biig6AyoAkdWpSek/1/B7zYIepYY0lxKQIDAQAB";
var encrypt = new JSEncrypt();
encrypt.setPublicKey(paramPublicKey);
encrypt.encryptLong = function(text) {
var _this = this.getKey();
var maxLength = (_this.n.bitLength() + 7 >> 3) - 11;
try {
var ct_1 = "";
if (text.length > maxLength) {
var lt = text.match(/.{1,117}/g);
lt.forEach(function(entry) {
var t1 = _this.encrypt(entry);
ct_1 += t1
});
return hex2b64(ct_1)
}
var t = _this.encrypt(text);
var y = hex2b64(t);
return y
} catch (ex) {
console.log(ex)
return false
}
}
encrypt.decryptLong = function(text) {
var _this = this.getKey();
var maxLength = _this.n.bitLength() + 7 >> 3;
text = b64tohex(text);
try {
if (text.length > maxLength) {
var ct_2 = "";
var lt = text.match(/.{1,256}/g);
lt.forEach(function(entry) {
var t1 = _this.decrypt(entry);
ct_2 += t1
});
return ct_2
}
var y = _this.decrypt(text);
return y
} catch (ex) {
return false
}
}
var c = Date.parse(new Date());
var d = getUuid();
var e = JSON.stringify(sort_ASCII({"limit": "20", "page":page}));
var data = encrypt.encryptLong(e);
var f = MD5(e + d + c);
return {
"timestamp": c,
"requestId": d,
"sign": f,
"data": data,
}
}
function decrypt_data(data) {
const key = "C8EB5514AF5ADDB94B2207B08C66601C"
const iv = "55DD79C6F04E1A67"
var b = CryptoJS.enc.Utf8.parse(key);
var c = CryptoJS.enc.Utf8.parse(iv);
var d = CryptoJS.AES.decrypt(data, b, {
iv: c,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
return d.toString(CryptoJS.enc.Utf8)
}
// console.log(get_params(1))
结果展示
完结撒花!!!!!有不懂的欢迎私信看到都会回复,大佬可以直接跳过。欢迎+v一起交流学习。
标签:function,web,return,逆向,text,var,encrypt,观鸟,data From: https://blog.csdn.net/qq_45703821/article/details/144352479