Files
hello_js_reverse_skill/references/crypto-patterns.md
T
白夜 b5cf8b121a feat(v2.4.0): JSVMP 双路径攻克体系 + jsdom 环境补丁知识库
基于实战经验重构 JSVMP 分析框架:

- SKILL.md: 新增路径选择决策树(路径 A 算法追踪 vs 路径 B 环境伪装)
- SKILL.md: 路径 B「环境伪装六步法」完整方法论(采集→对比→分级→补丁→验证→端到端)
- SKILL.md: 新增场景 10「JSVMP + jsdom 环境伪装」、解法模式 E
- SKILL.md: 标注三板斧适用边界与局限,经验法则扩充至 22 条
- references/jsdom-env-patches.md: 新增 jsdom 环境补丁知识库(Function.toString 三层防御 / plugins 完整模拟 / DOM 布局属性 / 30+ API 存根)
- cases/: 新增经验库系统(模板 + 首个 JSVMP 实战案例含 58 项环境检测差异全表)
- references/jsvmp-analysis.md: JSVMP 专项分析详细指南
- references/troubleshooting.md: 常见问题排查指南
- templates/python-request/: Python 项目模板
- 各 references 文档增强、Python 技术栈支持、MCP v1.1 适配(44→52 工具)

Made-with: Cursor
2026-04-02 18:10:05 +08:00

425 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 常见加密模式识别与还原(Node.js / Python 双语言)
## 快速识别表
| 特征 | 可能的算法 | 验证方法 |
|------|-----------|---------|
| 32位十六进制 | MD5 | 用已知输入验证 |
| 40位十六进制 | SHA-1 | 同上 |
| 64位十六进制 | SHA-256 | 同上 |
| 128位十六进制 | SHA-512 | 同上 |
| `=` 结尾,含 `A-Za-z0-9+/` | Base64 | 直接 atob 解码 |
| `=` 结尾,含 `A-Za-z0-9-_` | Base64url | 替换 `-_` 为 `+/` 后解码 |
| 固定长度密文,16字节倍数 | AES (128-bit key) | 寻找 key/iv |
| 固定长度密文,8字节倍数 | DES / 3DES | 寻找 8字节 key |
| 超长数字字符串 | RSA | 寻找公钥 |
| `CryptoJS` 关键词 | CryptoJS 库 | 直接搜索源码 |
## 1. MD5
### 标准 MD5
```javascript
// Node.js
const crypto = require('crypto');
function md5(str) {
return crypto.createHash('md5').update(str).digest('hex');
}
```
```python
# Python
import hashlib
def md5(text: str) -> str:
return hashlib.md5(text.encode('utf-8')).hexdigest()
```
### 自定义 MD5(非标准实现)
某些网站修改了 MD5 的内部参数(如 chrsz=16),导致输出与标准 MD5 不同。
**识别方法**:用相同输入对比标准 MD5 输出,如果不一致则为自定义实现。
**还原策略**:必须提取原始 JS 实现,在 Node.js 中直接执行。
### 常见 MD5 签名模式
```javascript
// 模式1:简单拼接
sign = md5(param1 + param2 + timestamp + secret)
// 模式2:排序拼接
params = Object.keys(data).sort().map(k => k + '=' + data[k]).join('&')
sign = md5(params + secret)
// 模式3:嵌套哈希
sign = md5(md5(password) + timestamp)
```
## 2. HMAC
```javascript
// Node.js
const crypto = require('crypto');
function hmacSha256(message, secret) {
return crypto.createHmac('sha256', secret).update(message).digest('hex');
}
```
```python
# Python
import hmac
import hashlib
def hmac_sha256(message: str, secret: str) -> str:
return hmac.new(secret.encode('utf-8'), message.encode('utf-8'), hashlib.sha256).hexdigest()
```
**识别特征**:搜索 `HMAC`、`createHmac`、`CryptoJS.HmacSHA256`
## 3. AES
### AES-CBC
```javascript
// Node.js
const crypto = require('crypto');
function aesEncrypt(plaintext, key, iv) {
const cipher = crypto.createCipheriv('aes-128-cbc', key, iv);
cipher.setAutoPadding(true); // PKCS7
let encrypted = cipher.update(plaintext, 'utf8', 'base64');
encrypted += cipher.final('base64');
return encrypted;
}
function aesDecrypt(ciphertext, key, iv) {
const decipher = crypto.createDecipheriv('aes-128-cbc', key, iv);
decipher.setAutoPadding(true);
let decrypted = decipher.update(ciphertext, 'base64', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
```
```python
# Python
import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
def aes_cbc_encrypt(plaintext: str, key: str, iv: str) -> str:
cipher = AES.new(key.encode('utf-8'), AES.MODE_CBC, iv.encode('utf-8'))
padded = pad(plaintext.encode('utf-8'), AES.block_size)
return base64.b64encode(cipher.encrypt(padded)).decode('utf-8')
def aes_cbc_decrypt(ciphertext_b64: str, key: str, iv: str) -> str:
cipher = AES.new(key.encode('utf-8'), AES.MODE_CBC, iv.encode('utf-8'))
decrypted = cipher.decrypt(base64.b64decode(ciphertext_b64))
return unpad(decrypted, AES.block_size).decode('utf-8')
```
### AES-ECB
```javascript
// Node.js
function aesEcbEncrypt(plaintext, key) {
const cipher = crypto.createCipheriv('aes-128-ecb', key, null);
cipher.setAutoPadding(true);
let encrypted = cipher.update(plaintext, 'utf8', 'base64');
encrypted += cipher.final('base64');
return encrypted;
}
```
```python
# Python
def aes_ecb_encrypt(plaintext: str, key: str) -> str:
cipher = AES.new(key.encode('utf-8'), AES.MODE_ECB)
padded = pad(plaintext.encode('utf-8'), AES.block_size)
return base64.b64encode(cipher.encrypt(padded)).decode('utf-8')
```
### CryptoJS 兼容
```javascript
const CryptoJS = require('crypto-js');
// CryptoJS 默认使用 PKCS7 填充
const encrypted = CryptoJS.AES.encrypt(plaintext, CryptoJS.enc.Utf8.parse(key), {
iv: CryptoJS.enc.Utf8.parse(iv),
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}).toString();
```
**关键参数识别**:
- key 长度:16字节=AES-128,24字节=AES-192,32字节=AES-256
- iv 长度:固定16字节
- 模式:CBC(需要iv)、ECB(不需要iv)、CTR、GCM
- 填充:PKCS7(最常见)、ZeroPadding、NoPadding
- 输出格式:Base64(最常见)、Hex
## 4. DES / 3DES
```javascript
// Node.js
function desEncrypt(plaintext, key) {
const cipher = crypto.createCipheriv('des-ecb', key, null); // 8字节key
cipher.setAutoPadding(true);
let encrypted = cipher.update(plaintext, 'utf8', 'base64');
encrypted += cipher.final('base64');
return encrypted;
}
function tripleDesEncrypt(plaintext, key, iv) {
const cipher = crypto.createCipheriv('des-ede3-cbc', key, iv); // 24字节key
cipher.setAutoPadding(true);
let encrypted = cipher.update(plaintext, 'utf8', 'base64');
encrypted += cipher.final('base64');
return encrypted;
}
```
```python
# Python
from Crypto.Cipher import DES, DES3
from Crypto.Util.Padding import pad
def des_ecb_encrypt(plaintext: str, key: str) -> str:
cipher = DES.new(key.encode('utf-8'), DES.MODE_ECB)
padded = pad(plaintext.encode('utf-8'), DES.block_size)
return base64.b64encode(cipher.encrypt(padded)).decode('utf-8')
def triple_des_cbc_encrypt(plaintext: str, key: str, iv: str) -> str:
cipher = DES3.new(key.encode('utf-8'), DES3.MODE_CBC, iv.encode('utf-8'))
padded = pad(plaintext.encode('utf-8'), DES3.block_size)
return base64.b64encode(cipher.encrypt(padded)).decode('utf-8')
```
## 5. RSA
```javascript
// Node.js
const crypto = require('crypto');
function rsaEncrypt(plaintext, publicKey) {
const buffer = Buffer.from(plaintext, 'utf8');
const encrypted = crypto.publicEncrypt({
key: publicKey,
padding: crypto.constants.RSA_PKCS1_PADDING
}, buffer);
return encrypted.toString('base64');
}
```
**使用 node-forge(更灵活)**:
```javascript
const forge = require('node-forge');
function rsaEncrypt(plaintext, publicKeyPem) {
const publicKey = forge.pki.publicKeyFromPem(publicKeyPem);
const encrypted = publicKey.encrypt(plaintext, 'RSAES-PKCS1-V1_5');
return forge.util.encode64(encrypted);
}
```
```python
# Python
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_v1_5
def rsa_encrypt(plaintext: str, public_key_pem: str) -> str:
key = RSA.import_key(public_key_pem)
cipher = PKCS1_v1_5.new(key)
encrypted = cipher.encrypt(plaintext.encode('utf-8'))
return base64.b64encode(encrypted).decode('utf-8')
# OAEP 模式
from Crypto.Cipher import PKCS1_OAEP
def rsa_encrypt_oaep(plaintext: str, public_key_pem: str) -> str:
key = RSA.import_key(public_key_pem)
cipher = PKCS1_OAEP.new(key)
encrypted = cipher.encrypt(plaintext.encode('utf-8'))
return base64.b64encode(encrypted).decode('utf-8')
```
**常见 RSA 公钥格式**:
- PEM 格式(`-----BEGIN PUBLIC KEY-----`)
- 模数(n) + 指数(e) 格式(需要手动构造 PEM)
## 6. Base64 变体
```javascript
// Node.js
const encoded = Buffer.from(str).toString('base64');
const decoded = Buffer.from(encoded, 'base64').toString();
// Base64url(URL安全变体)
function base64url(str) {
return Buffer.from(str).toString('base64')
.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
}
// 自定义字符表 Base64
function customBase64(str, table) {
const std = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
const b64 = Buffer.from(str).toString('base64');
return b64.split('').map(c => {
const idx = std.indexOf(c);
return idx >= 0 ? table[idx] : c;
}).join('');
}
```
```python
# Python
import base64
encoded = base64.b64encode(text.encode('utf-8')).decode('utf-8')
decoded = base64.b64decode(encoded).decode('utf-8')
# Base64url(URL安全变体)
encoded_url = base64.urlsafe_b64encode(text.encode('utf-8')).decode('utf-8').rstrip('=')
# 自定义字符表 Base64
def custom_base64(text: str, table: str) -> str:
std = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
b64 = base64.b64encode(text.encode('utf-8')).decode('utf-8')
trans = str.maketrans(std, table)
return b64.translate(trans)
```
## 7. 异或加密(XOR)
```javascript
// Node.js
function xorEncrypt(plaintext, key) {
const result = [];
for (let i = 0; i < plaintext.length; i++) {
result.push(plaintext.charCodeAt(i) ^ key.charCodeAt(i % key.length));
}
return Buffer.from(result).toString('hex');
}
```
```python
# Python
def xor_encrypt(plaintext: str, key: str) -> str:
result = bytes([
ord(plaintext[i]) ^ ord(key[i % len(key)])
for i in range(len(plaintext))
])
return result.hex()
```
## 8. RC4
```javascript
// Node.js
function rc4(data, key) {
const s = Array.from({ length: 256 }, (_, i) => i);
let j = 0;
for (let i = 0; i < 256; i++) {
j = (j + s[i] + key.charCodeAt(i % key.length)) % 256;
[s[i], s[j]] = [s[j], s[i]];
}
let i = 0; j = 0;
const result = [];
for (let k = 0; k < data.length; k++) {
i = (i + 1) % 256;
j = (j + s[i]) % 256;
[s[i], s[j]] = [s[j], s[i]];
result.push(data.charCodeAt(k) ^ s[(s[i] + s[j]) % 256]);
}
return Buffer.from(result);
}
```
```python
# Python(使用 pycryptodome)
from Crypto.Cipher import ARC4
def rc4_encrypt(data: str, key: str) -> bytes:
cipher = ARC4.new(key.encode('utf-8'))
return cipher.encrypt(data.encode('utf-8'))
# 或手动实现(与 JS 版本逻辑一致)
def rc4_manual(data: str, key: str) -> bytes:
s = list(range(256))
j = 0
for i in range(256):
j = (j + s[i] + ord(key[i % len(key)])) % 256
s[i], s[j] = s[j], s[i]
i = j = 0
result = []
for char in data:
i = (i + 1) % 256
j = (j + s[i]) % 256
s[i], s[j] = s[j], s[i]
result.append(ord(char) ^ s[(s[i] + s[j]) % 256])
return bytes(result)
```
## 9. 时间戳处理
```javascript
// Node.js
const tsMs = Date.now(); // 毫秒级(13位)
const tsSec = Math.floor(Date.now() / 1000); // 秒级(10位)
```
```python
# Python
import time
ts_ms = int(time.time() * 1000) # 毫秒级(13位)
ts_sec = int(time.time()) # 秒级(10位)
```
**注意**:某些网站使用服务端时间戳,需要从响应头或接口获取。
## 10. 参数签名常见拼接模式
```javascript
// Node.js
// 模式1:固定格式
sign = md5(`page=${page}&t=${timestamp}&key=${secret}`)
// 模式2:所有参数排序拼接
const params = { page: 1, size: 10, t: Date.now() };
const str = Object.keys(params).sort().map(k => `${k}=${params[k]}`).join('&');
sign = md5(str + secret)
// 模式3:JSON 字符串
sign = md5(JSON.stringify(data) + secret)
// 模式4:管道分隔
sign = md5(`${page}|${timestamp}|${secret}`)
```
```python
# Python
import hashlib, json, time
def md5(text: str) -> str:
return hashlib.md5(text.encode('utf-8')).hexdigest()
# 模式1:固定格式
sign = md5(f"page={page}&t={timestamp}&key={secret}")
# 模式2:所有参数排序拼接
params = {"page": 1, "size": 10, "t": int(time.time())}
param_str = "&".join(f"{k}={params[k]}" for k in sorted(params))
sign = md5(param_str + secret)
# 模式3:JSON 字符串(注意 separators 控制格式)
sign = md5(json.dumps(data, separators=(',', ':'), sort_keys=True) + secret)
# 模式4:管道分隔
sign = md5(f"{page}|{timestamp}|{secret}")
```