T22对称加密

一:抓包分析

下一页抓包分析,可以看到请求体有两个加密参数,aes_sign、des_sign

image-20260511134404945

汲取上一题的教训,查看下请求头,可以看到有两个加密参数X-Aes-Token、X-Des-Token

image-20260511134600651

二:加密参数分析

直接搜索大法

image-20260511134755860

可以看到四个参数都在这里

image-20260511134852045

1、X-Aes-Token

直接让AI帮我改写,发现不对后调整

image-20260511142237176

第一次生成的代码出入很大,加限制条件和结果

image-20260511142312675image-20260511142329876

最后代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import base64
from Crypto.Cipher import AES


def cryptojs_aes_ctr_encrypt(plaintext: str) -> str:
"""
完全模拟 CryptoJS 的 AES-128-CTR 加密
- 密钥、IV 均以 UTF-8 字符串传入
- IV 不足 16 字节右补 0x00,超出截断
- 使用 CTR 模式,计数器初始值为 IV 的 **小端整数**(与 CryptoJS 内部一致)
- 输出 Base64 字符串
"""
key = "1234567890123456".encode('utf-8')
iv_bytes = "abcdefghijklmnop".encode('utf-8')

# IV 补齐/截断至 16 字节
if len(iv_bytes) < 16:
iv_bytes += b'\x00' * (16 - len(iv_bytes))
elif len(iv_bytes) > 16:
iv_bytes = iv_bytes[:16]

plain = plaintext.encode('utf-8')
block_size = 16
counter = int.from_bytes(iv_bytes, 'big') # 大端整数初始计数器
cipher_ecb = AES.new(key, AES.MODE_ECB) # 用于加密计数器

ciphertext_parts = []
for i in range(0, len(plain), block_size):
# 当前计数器的 16 字节大端表示
counter_bytes = counter.to_bytes(16, 'big')
# 加密计数器 -> 密钥流
keystream = cipher_ecb.encrypt(counter_bytes)
# 当前明文块(可能不足 16 字节)
block = plain[i:i + block_size]
# 异或生成密文块
encrypted_block = bytes(a ^ b for a, b in zip(keystream, block))
ciphertext_parts.append(encrypted_block)
# 计数器加 1
counter += 1

ciphertext = b''.join(ciphertext_parts)
return base64.b64encode(ciphertext).decode('ascii')

image-20260511152149539

image-20260511142448890

测试结果一致

2、X-Des-Token

进入函数实际上是

image-20260511143150381

代码改写验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def l(e: str) -> str:
"""
对应 JS 中的 l 函数:
var n = CryptoJS.enc.Utf8.parse("6f726c64");
var t = CryptoJS.enc.Utf8.parse("01234567");
return CryptoJS.DES.encrypt(e, n, { iv: t, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }).toString()
"""
key = b"6f726c64" # UTF-8 字节,恰好 8 字节
iv = b"01234567" # UTF-8 字节,恰好 8 字节

# 明文转 UTF-8 字节
plaintext = e.encode('utf-8')

# PKCS7 填充(DES 块大小 8 字节)
padded = pad(plaintext, DES.block_size)

# DES CBC 加密
cipher = DES.new(key, DES.MODE_CBC, iv)
ciphertext = cipher.encrypt(padded)

# 返回 Base64 字符串(与 CryptoJS 的 .toString() 一致)
return base64.b64encode(ciphertext).decode('ascii')

image-20260511143252728

image-20260511143302373

一致

3、aes_sign

image-20260511143747359

也就是如上函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def p(e: str) -> str:
"""
对应 JS 中的:
var n = CryptoJS.enc.Utf8.parse("12345678901234567890123456789012");
var t = CryptoJS.enc.Utf8.parse(u); // u = "abcdefghijklmnop"
return CryptoJS.AES.encrypt(e, n, {
iv: t,
mode: CryptoJS.mode.OFB,
padding: CryptoJS.pad.NoPadding
}).toString()
"""
# 密钥(32 字节 → AES-256)
key = b"12345678901234567890123456789012"
# IV(16 字节,AES 块大小)
iv = b"abcdefghijklmnop"

# 明文转 UTF-8
plaintext = e.encode('utf-8')

# 创建 OFB 模式密码器(无需填充)
cipher = AES.new(key, AES.MODE_OFB, iv=iv)
ciphertext = cipher.encrypt(plaintext)

# 返回 Base64 字符串(与 CryptoJS 的 .toString() 一致)
return base64.b64encode(ciphertext).decode('ascii')

image-20260511143950751

image-20260511143927205

一致

4、des_sign

略,实际为l(i + "_param")

三:编码与验证

image-20260511151848928

更多内容也在公众号更新:码字的秃猴

tuhou