本文最后更新于 364 天前,其中的信息可能已经有所发展或是发生改变。
前言
本文章仅做安全学习交流用途,严禁作其他用途,如果侵犯您的权益请联系我删除。
所用工具: WeChatOpenDevTools
分析
搜索关键字如signcid无法定位到生成位置。通过分析堆栈一步一步向前确定sign生成位置,最终分析出生成逻辑。




目前AES加解密函数,sign生成函数均已实现,现在只看AES的key能否持久使用,如果不能,则需要探索其来源。
相关代码
//version 5.3.22
const crypto = require('crypto');
function t(r) {
for (var t = function(r) {
for (var e, t, n = [], a = 0; a < r.length; a++) {
e = r.charCodeAt(a),
t = [];
do {
t.push(255 & e),
e >>= 8
} while (e);
n = n.concat(t.reverse())
}
return n
}(r), n = new Array, a = e / 8, s = 0; s < a; s++)
t.length > s ? n.push(t[s]) : n.push(0);
return n
}
function stringToBytes(val) {
const result = [];
for (let i = 0; i < val.length; i++) {
result.push(val.charCodeAt(i));
}
return result;
}
function bytesToWords(t) {
for (var n = [], r = 0, o = 0; r < t.length; r++,
o += 8)
n[o >>> 5] |= (255 & t[r]) << 24 - o % 32;
return n
}
function wordsToBytes(t) {
for (var n = [], r = 0; r < 32 * t.length; r += 8)
n.push(t[r >>> 5] >>> 24 - r % 32 & 255);
return n
}
function endianConversion(value) {
if (typeof value === 'number') {
// 32位整数字节交换
return ((value & 0xFF) << 24) |
((value & 0xFF00) << 8) |
((value >> 8) & 0xFF00) |
((value >> 24) & 0xFF);
}
if (Array.isArray(value)) {
return value.map(endianConversion);
}
return value;
}
function endian(t) {
if (t.constructor == Number)
return 16711935 & r.rotl(t, 8) | 4278255360 & r.rotl(t, 24);
for (var n = 0; n < t.length; n++)
t[n] = endianConversion(t[n]);
return t
}
function de(e, n, a) {
// 1. URL安全的Base64转换(如果a为true)
if (a) {
e = e.replace(/-/g, '+').replace(/_/g, '/');
}
// 2. Base64解码
const encryptedData = Buffer.from(e, 'base64');
// 3. 假设t(n)是密钥处理函数,这里简化为直接使用n作为密钥
// 注意:实际实现中t(n)可能有特定处理,需要根据实际情况调整
const key = Buffer.from(n, 'utf8');
// 4. 创建解密器 - ECB模式通过不提供IV实现
const decipher = crypto.createDecipheriv('aes-128-ecb', key, null);
// 5. 设置PKCS7填充(Node.js默认就是PKCS7)
decipher.setAutoPadding(true);
// 6. 执行解密
let decrypted = decipher.update(encryptedData);
decrypted = Buffer.concat([decrypted, decipher.final()]);
// 7. 返回解密后的字节数组
return decrypted;
}
const decrypted = de('密文', 'CJQjAc1hYieC4QYb', true);
console.log('解密结果:', decrypted.toString('utf8'));
function encryptAesEcb(e, n, a) {
// 将密钥处理为16/24/32字节长度(AES-128/192/256)
const key = Buffer.from(n, 'utf8');
// 创建加密器 (ECB模式在Node.js中通过不提供IV来实现)
const cipher = crypto.createCipheriv('aes-128-ecb', key, null);
// 设置自动PKCS7填充
cipher.setAutoPadding(true);
// 加密数据
let encrypted = cipher.update(e, 'utf8', 'base64');
encrypted += cipher.final('base64');
// 如果需要URL安全的Base64
if (a) {
encrypted = encrypted
.replace(/\+/g, '-')
.replace(/\//g, '_'); // 移除末尾的等号
}
return encrypted;
}
// 使用示例
const plaintext = '{"deptId":325525,"supportTakeout":0,"miniversion":"5347"}';
const key = 'CJQjAc1hYieC4QYb'; // AES-256需要32字节密钥
// 标准Base64输出
const encrypted1 = encryptAesEcb(plaintext, key, false);
console.log('标准Base64:', encrypted1);
// URL安全Base64输出
const encrypted2 = encryptAesEcb(plaintext, key, true);
console.log('URL安全Base64:', encrypted2);
function _md5(r) {
r.constructor == String && (r = stringToBytes(r));
for (var n = bytesToWords(r), i = 8 * r.length, s = 1732584193, u = -271733879, a = -1732584194, f = 271733878, c = 0; c < n.length; c++)
n[c] = 16711935 & (n[c] << 8 | n[c] >>> 24) | 4278255360 & (n[c] << 24 | n[c] >>> 8);
n[i >>> 5] |= 128 << i % 32,
n[14 + (i + 64 >>> 9 << 4)] = i;
var g = function(r, t, n, e, i, o, s) {
var u = r + (t & n | ~t & e) + (i >>> 0) + s;
return (u << o | u >>> 32 - o) + t
}
, _ = function(r, t, n, e, i, o, s) {
var u = r + (t & e | n & ~e) + (i >>> 0) + s;
return (u << o | u >>> 32 - o) + t
}
, y = function(r, t, n, e, i, o, s) {
var u = r + (t ^ n ^ e) + (i >>> 0) + s;
return (u << o | u >>> 32 - o) + t
}
, d = function(r, t, n, e, i, o, s) {
var u = r + (n ^ (t | ~e)) + (i >>> 0) + s;
return (u << o | u >>> 32 - o) + t
};
for (c = 0; c < n.length; c += 16) {
var v = s
, h = u
, T = a
, l = f;
s = g(s, u, a, f, n[c + 0], 7, -680876936),
f = g(f, s, u, a, n[c + 1], 12, -389564586),
a = g(a, f, s, u, n[c + 2], 17, 606105819),
u = g(u, a, f, s, n[c + 3], 22, -1044525330),
s = g(s, u, a, f, n[c + 4], 7, -176418897),
f = g(f, s, u, a, n[c + 5], 12, 1200080426),
a = g(a, f, s, u, n[c + 6], 17, -1473231341),
u = g(u, a, f, s, n[c + 7], 22, -45705983),
s = g(s, u, a, f, n[c + 8], 7, 1770035416),
f = g(f, s, u, a, n[c + 9], 12, -1958414417),
a = g(a, f, s, u, n[c + 10], 17, -42063),
u = g(u, a, f, s, n[c + 11], 22, -1990404162),
s = g(s, u, a, f, n[c + 12], 7, 1804603682),
f = g(f, s, u, a, n[c + 13], 12, -40341101),
a = g(a, f, s, u, n[c + 14], 17, -1502002290),
s = _(s, u = g(u, a, f, s, n[c + 15], 22, 1236535329), a, f, n[c + 1], 5, -165796510),
f = _(f, s, u, a, n[c + 6], 9, -1069501632),
a = _(a, f, s, u, n[c + 11], 14, 643717713),
u = _(u, a, f, s, n[c + 0], 20, -373897302),
s = _(s, u, a, f, n[c + 5], 5, -701558691),
f = _(f, s, u, a, n[c + 10], 9, 38016083),
a = _(a, f, s, u, n[c + 15], 14, -660478335),
u = _(u, a, f, s, n[c + 4], 20, -405537848),
s = _(s, u, a, f, n[c + 9], 5, 568446438),
f = _(f, s, u, a, n[c + 14], 9, -1019803690),
a = _(a, f, s, u, n[c + 3], 14, -187363961),
u = _(u, a, f, s, n[c + 8], 20, 1163531501),
s = _(s, u, a, f, n[c + 13], 5, -1444681467),
f = _(f, s, u, a, n[c + 2], 9, -51403784),
a = _(a, f, s, u, n[c + 7], 14, 1735328473),
s = y(s, u = _(u, a, f, s, n[c + 12], 20, -1926607734), a, f, n[c + 5], 4, -378558),
f = y(f, s, u, a, n[c + 8], 11, -2022574463),
a = y(a, f, s, u, n[c + 11], 16, 1839030562),
u = y(u, a, f, s, n[c + 14], 23, -35309556),
s = y(s, u, a, f, n[c + 1], 4, -1530992060),
f = y(f, s, u, a, n[c + 4], 11, 1272893353),
a = y(a, f, s, u, n[c + 7], 16, -155497632),
u = y(u, a, f, s, n[c + 10], 23, -1094730640),
s = y(s, u, a, f, n[c + 13], 4, 681279174),
f = y(f, s, u, a, n[c + 0], 11, -358537222),
a = y(a, f, s, u, n[c + 3], 16, -722521979),
u = y(u, a, f, s, n[c + 6], 23, 76029189),
s = y(s, u, a, f, n[c + 9], 4, -640364487),
f = y(f, s, u, a, n[c + 12], 11, -421815835),
a = y(a, f, s, u, n[c + 15], 16, 530742520),
s = d(s, u = y(u, a, f, s, n[c + 2], 23, -995338651), a, f, n[c + 0], 6, -198630844),
f = d(f, s, u, a, n[c + 7], 10, 1126891415),
a = d(a, f, s, u, n[c + 14], 15, -1416354905),
u = d(u, a, f, s, n[c + 5], 21, -57434055),
s = d(s, u, a, f, n[c + 12], 6, 1700485571),
f = d(f, s, u, a, n[c + 3], 10, -1894986606),
a = d(a, f, s, u, n[c + 10], 15, -1051523),
u = d(u, a, f, s, n[c + 1], 21, -2054922799),
s = d(s, u, a, f, n[c + 8], 6, 1873313359),
f = d(f, s, u, a, n[c + 15], 10, -30611744),
a = d(a, f, s, u, n[c + 6], 15, -1560198380),
u = d(u, a, f, s, n[c + 13], 21, 1309151649),
s = d(s, u, a, f, n[c + 4], 6, -145523070),
f = d(f, s, u, a, n[c + 11], 10, -1120210379),
a = d(a, f, s, u, n[c + 2], 15, 718787259),
u = d(u, a, f, s, n[c + 9], 21, -343485551),
s = s + v >>> 0,
u = u + h >>> 0,
a = a + T >>> 0,
f = f + l >>> 0
}
return endian([s, u, a, f])
}
//uid就是cookie+key,那么key从哪来?
var t =wordsToBytes(_md5('cid=230101;dk=1;q=加密后密文+cookie'))
function n(r, e) {
return (255 & r[e]) << 24 | (255 & r[e + 1]) << 16 | (255 & r[e + 2]) << 8 | 255 & r[e + 3]
}
var a = Math.abs(n(t, 0))
, s = Math.abs(n(t, 4))
, o = Math.abs(n(t, 8))
, c = Math.abs(n(t, 12));
var final_sign = a.toString() + s.toString() + o.toString() + c.toString()
console.log('final_sign => ',final_sign);
2025.5.7发现更新了算法
依旧跟栈找到sign出现位置

进入后来到这里

步进然后扣出关键代码,在本地实现即可

具体实现代码这里就不贴了,有需要的可以私我
5.8优化算法
如果已知utf8编码的明文,则可以使用 CryptoJS.enc.Utf8.parse 方法生成 WordArray 对象。
const CryptoJS = require("crypto-js");
// 生成 WordArray 对象
const keyWordArray = CryptoJS.enc.Utf8.parse("wm0!@w-s#ll1flo(");
console.log(keyWordArray);
