Python爬虫JS逆向工程高级教程
本教程将深入讲解Python爬虫中处理JavaScript加密的高级技巧,包括浏览器调试、代码分析、环境模拟和多层加密破解。随着前端技术的不断发展,越来越多的网站采用JavaScript加密参数来防止爬虫,传统的HTTP请求方法已无法满足需求。通过掌握JS逆向技术,爬虫开发者可以突破这些限制,获取网站动态加载的数据。本教程将从基础概念入手,逐步深入到高级实战,帮助您掌握这一复杂而实用的技术。
一、JS逆向基础原理
1.1 什么是JS逆向
JS逆向是指对JavaScript代码进行反向工程,以理解其逻辑、算法或修改其行为的过程。在爬虫领域,JS逆向主要用于破解网站前端加密参数,模拟浏览器环境,从而获取动态加载的数据。许多网站通过JavaScript加密请求参数、响应数据或生成动态签名,使得直接请求接口变得困难。通过逆向分析这些JavaScript加密逻辑,爬虫可以绕过这些限制。
JS逆向的核心目标是获取网站前端加密的参数生成方法,从而在爬虫中模拟生成相同的参数,实现与网站的正常交互。
常见的加密参数包括:请求头中的token、sign、加密的表单数据等。这些参数通常使用对称加密(如AES)、非对称加密(如RSA)或消息摘要(如MD5、SHA)等算法生成。
1.2 常见加密算法类型
在JS逆向中,主要需要处理三种类型的加密算法:
| 加密类型 | 常见算法 | 特点 | 应用场景 |
|---|---|---|---|
| 对称加密 | AES、DES、RC4 | 加密和解密使用相同密钥 | 加密请求数据、响应数据 |
| 非对称加密 | RSA、DSA、ECC | 加密和解密使用不同密钥 | 加密密钥、数字签名 |
| 消息摘要 | MD5、SHA1、SHA256、HMAC | 不可逆,生成固定长度摘要 | 数据完整性校验、签名验证 |
对称加密算法如AES,特点是加密和解密使用相同的密钥,适合加密大量数据。非对称加密算法如RSA,特点是加密和解密使用不同的密钥(公钥和私钥),适合加密密钥或进行身份验证。消息摘要算法如SHA256,特点是不可逆,生成固定长度的摘要,适合验证数据完整性或生成签名。
1.3 加密参数分析与还原方法
在JS逆向过程中,分析加密参数并还原其生成方法是关键步骤。常用的方法包括:
- 网络请求分析:通过浏览器开发者工具的Network面板,捕获目标请求(XHR/fetch),观察加密参数的传输过程。
- 代码静态分析:在Sources面板中搜索加密参数名称或相关关键词,定位加密函数的实现。
- 动态调试:在加密函数处设置断点,观察变量变化,记录加密过程中的中间值。
- 环境模拟:在Python中模拟浏览器环境,执行JavaScript加密代码,生成相同的加密参数。
还原加密参数的核心思路是:找到加密函数的输入参数、密钥和算法,然后在Python中复现相同的加密过程。这需要对JavaScript加密代码有深入的理解,并能够将其转化为Python代码。
二、浏览器调试技术详解
2.1 Chrome开发者工具基础
Chrome开发者工具(Chrome DevTools)是JS逆向分析的核心工具。它提供了完整的前端开发和调试环境,包括Elements、Console、Sources、Network等面板。
打开Chrome开发者工具的方法有三种:
- 点击浏览器右上角的"…"(自定义及控制Google Chrome)>更多工具>开发者工具
- 在页面空白处,鼠标右击,选择"检查"(或"审查元素")
- 使用快捷键F12或Ctrl+Shift+I
开发者工具的布局可以自定义,通常将控制台(Console)放在下方,源代码(Sources)面板放在中间,网络(Network)面板放在上方,以便同时观察多个调试信息。
2.2 断点设置与动态调试
在Sources面板中,可以设置多种类型的断点,用于捕获加密参数的生成过程:
1. 常规断点:点击代码左侧的行号,设置一个简单的断点,当代码执行到该行时暂停。
2. 条件断点**:在代码行号处右键,选择"Add conditional breakpoint",可以设置一个条件表达式,只有满足条件时才会暂停执行。这对于监控特定变量变化非常有用。
3. XHR断点:在Network面板中,点击"Preserve log",然后在"XHR Breakpoints"区域输入要拦截的URL关键字,当发生匹配的请求时,会自动暂停到对应的JavaScript代码处。
4. 事件断点:在Sources面板的"Event Listeners"中,可以查看元素绑定的事件,设置断点来捕获事件触发时的执行路径。
5. 异常断点:在Sources面板的断点设置中,勾选"Pause on exceptions",当代码抛出异常时暂停,有助于发现加密逻辑中的关键点。
6. 调用栈追踪:在Network面板中,点击请求的"Initiator"列下的文件名,可以跳转到触发该请求的JavaScript代码位置,帮助定位加密逻辑。
2.3 代码格式化与美化
大多数网站的JavaScript代码都是经过压缩和混淆的,难以直接阅读。Chrome开发者工具提供了代码格式化的功能:
1. 格式化快捷键:在已打开的JavaScript文件中,使用快捷键Shift+Alt+F(Windows)或Shift+Option+F(Mac),可以自动格式化代码,使其更易于阅读。
2. 手动美化:对于特别复杂的混淆代码,可以使用第三方工具如JSBeautifier进行美化,然后通过"overrides"功能替换网页中的原始代码进行调试。
3. 变量名重命名:对于变量名被混淆的情况,可以在控制台中使用Object.keys和eval等方法尝试恢复变量名。例如:
// 查找以"_"开头的变量
for (var p in window) {
if (psubstr(0, 2) !== "_$") continue;
console.log(p + " >>> " + eval(p));
}
4. 函数劫持:通过重写关键函数(如XMLHttpRequest.send或fetch),可以捕获加密前的参数。例如:
// 劫持 fetch 方法
( function () {
const originalFetch = window.fetch;
window.fetch = function (...args) {
console.log("Intercepted fetch call with args:", args);
return originalFetch.apply(this, args);
}
})();
// 劫持 XOR 方法
var code = function () {
var org = document cookie __lookupSetter__('cookie');
document __defineSetter__('cookie', function (cookie) {
if (cookieindexOf('TSdc75a61a') > -1) {
deb u gger;
}
org = cookie;
});
document __defineGetter__('cookie', function () { return org; });
};
var script = document.createElement('script');
script.textContent = '(' + code + ')()';
(document.head || document.documentElement).appendChild(script);
script.parentNode.removeChild(script);
2.4 网络请求分析与参数捕获
Network面板是分析加密参数的关键工具:
1. 过滤请求:在Network面板中,使用过滤器(Filter)筛选出目标请求,如"XHR"或"fetch"类型的请求。
2. 查看请求详情:点击目标请求,查看其"Headers"(请求头)和"Payload"(请求参数),分析加密参数的传输方式。
3. 查看响应数据:在"Response"标签中查看服务器返回的数据,判断是否需要解密。
4. 复制请求:使用"Copy as cURL"功能,复制请求的命令行格式,便于在Python中模拟发送请求。
5. 查看请求发起点:在"Initiator"列中查看请求是由哪个JavaScript文件发起的,点击文件名可以跳转到对应的代码位置。
6. 查看请求调用栈:在请求的"Initiator"处右键,选择"Open in sources panel",可以查看完整的调用栈信息,帮助理解加密参数的生成流程。
三、加密算法在JS逆向中的应用
3.1 对称加密算法
3.1.1 AES加密
AES(高级加密标准)是最常用的对称加密算法,在JS逆向中经常遇到。常见的AES模式包括CBC、GCM等。
1. AES-CBC模式:在材料[10]的案例中,网站使用AES-CBC模式加密数据:
// JS加密代码
function encrypt(data) {
const key = CryptoJS.enc.Utf8.parse("jo8j9wGw%6HbxfFn");
const iv = CryptoJS.enc.Utf8.parse("0123456789ABCDEF");
const encrypted = CryptoJS.AES.encrypt(
data,
key,
{
iv: iv,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}
);
return encrypted.toString();
}
Python解密代码:
from Crypto.Cipher import AES
import binascii
def decrypt(ciphertext, key, iv):
# 将密钥和IV转换为字节数组
key = key.encode('utf-8')
iv = iv.encode('utf-8')
# 创建AES解密器
cipher = AES.new(key, AES.MODE_CBC, iv)
# 解密并去除填充
decrypted = cipher.decrypt(ciphertext)
return decrypted.rstrip(b'\0').decode('utf-8')
# 示例使用
ciphertext = binascii.unhexlify('c0c2c3c4c5c6c7c8c9ca公布的数据')
key = "jo8j9wGw%6HbxfFn"
iv = "0123456789ABCDEF"
decrypted_data = decrypt(ciphertext, key, iv)
print(decrypted_data)
2. AES-GCM模式:材料[52]展示了AES-GCM模式的加密和解密代码:
// JS加密代码
async function encrypt(secretKey, message) {
let iv = "ddfbccae-b4c4-11";
iv = Uint8Array.from(iv, x => x.charCodeAt(0));
let encoded =getMessageEncoding(message);
ciphertext = await window.crypto.subtle.encrypt(
{
name: "AES-GCM",
iv: iv
},
secretKey,
encoded
);
return ciphertext;
}
Python解密代码:
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
import os
def aes_gcm.decrypt(ciphertext, key, iv):
backend = default_backend()
cipher = Cipher(algorithms.AES(key), modes.GCM(iv), backend=backend)
decryptor = cipher.decryptor()
decrypted = decryptor.update(ciphertext) + decryptor finalize()
return decrypted.decode('utf-8')
# 示例使用
ciphertext = b'\x17O\xadn\x11*I\x94\x99G4G\x90\x8aG4G\x9cG4G='
key = b'jo8j9wGw%6HbxfFn' # 需要16字节对称密钥
iv = b'ddfbccae-b4c4-11' # 需要12字节IV
decrypted_data = aes_gcm.decrypt(ciphertext, key, iv)
print(decrypted_data)
3.1.2 DES加密
DES(数据加密标准)虽然安全性较低,但在一些老系统中仍然使用。材料[10]的案例中使用了DES加密:
// JS加密代码
function descript(data) {
const key = CryptoJS.enc.Utf8.parse("jo8j9wGw");
const encrypted = CryptoJS.DES.encrypt(
data,
key,
{
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
}
);
return encrypted.toString();
}
Python解密代码:
from Crypto.Cipher import DES
import binascii
def des_decrypt(ciphertext, key):
# 将密钥转换为字节数组
key = key.encode('utf-8')
# 创建DES解密器
cipher = DES.new(key, DES.MODE_CBC, iv)
# 解密并去除填充
decrypted = cipher.decrypt(ciphertext)
return decrypted.rstrip(b'\0').decode('utf-8')
# 示例使用
ciphertext = binascii.unhexlify('c0c2c3c4c5c6c7c8c9ca公布的数据')
key = "jo8j9wGw"
decrypted_data = des decrypt(ciphertext, key)
print(decrypted_data)
3.1.3 动态密钥生成(PBKDF2)
许多网站会使用动态密钥生成算法(如PBKDF2)来增加加密的复杂性。材料[48]展示了JS中使用PBKDF2生成密钥的方法:
// JS生成密钥代码
const pbkdf2 = require('pbkdf2');
// 生成密钥
const derivedKey = pbkdf2.pbkdf2Sync(
'password', // 密码
'salt', // 盐值
1, // 迭代次数
32, // 密钥长度(字节)
'sha512' // 哈希算法
);
Python生成密钥代码:
import hashlib
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.backends import default_backend
def generate_key(password, salt, iterations, key_length, digest):
# 将密码和盐值转换为字节数组
password = password.encode('utf-8')
salt = salt.encode('utf-8')
# 创建PBKDF2密钥派生函数
kdf = PBKDF2HMAC(
algorithm=digest,
length[key_length],
salt=salt,
iterations=iterations,
backend=default_backend()
)
# 生成密钥
return kdf衍生化(password)
# 示例使用
password = "password"
salt = "salt"
iterations = 1
key_length = 32
digest = hashes.SHA512()
key = generate_key(password, salt, iterations, key_length, digest)
print(key)
3.2 非对称加密算法
3.2.1 RSA加密
RSA是非对称加密算法,常用于加密对称密钥或生成数字签名。材料[50]展示了RSA-OAEP填充的加密过程:
1. RSA-OAEP填充原理:
// 填充过程
function OAEP(m, r) {
const s = G(r) ^ (m | 0^{k1});
const t = H(s) ^ r;
return s | t;
}
2. RSA加密与解密:
// JS加密代码
const crypto = require('crypto');
// 生成密钥对
const { publickey, privatekey } = crypto.generateRSAKeyPair();
// 加密数据
function encrypt(data, publickey) {
const encrypted = crypto.rsa.encrypt(data, publickey, 'OAEP');
return encrypted.toString('base64');
}
// 解密数据
function decrypt(encryptedData, privatekey) {
const decrypted = crypto.rsa decrypt(encryptedData, privatekey, 'OAEP');
return decrypted.toString();
}
Python解密代码:
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric padding import OAEP, MGF1
from cryptography.hazmat.primitives._ ser vice import load_pem private key
def rsa_decrypt(encrypted_data, private_key_pem):
# 加载私钥
private_key = load_pem_private_key(
private_key_pem.encode('utf-8'),
password=None
)
# 解密数据
decrypted = private_key decrypt(
encrypted_data,
OAEP(
mgf=MGF1 al go r i t h m=hashes.SHA256(),
algorithm=hashes.SHA256(),
label=None
)
)
return decrypted.decode('utf-8')
# 示例使用
encrypted_data = b'U2FsdGVkX1+b公布的数据'
private_key_pem = """
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAjo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFn
jo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFn
jo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFn
jo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFn
jo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFn
jo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFn
jo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFn
jo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFn
jo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFn
jo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFn
jo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFn
jo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFn
jo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFn
jo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFn
jo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFn
jo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFn
jo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFn
jo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFn
jo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFn
jo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFn
jo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFn
jo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFn
jo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFn
jo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFn
jo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFn
jo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFn
jo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFn
jo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFnjo8j9wGw%6HbxfFn
-----END RSA PRIVATE KEY-----
"""
decrypted_data = rsa_decrypt(encrypted_data, private_key_pem)
print(decrypted_data)
3.2.2 RSA密钥交换协议
材料描述了基于RSA-OAEP的密钥交换协议:
// 密钥交换协议
// Alice生成密钥对
const { aPub, aPriv } = generateRSAKeyPair();
// Bob生成密钥对
const { bPub, bPriv } = generateRSAKeyPair();
// Alice向Bob发送加密的r1和r2
const mA = encryptWithRSA(bPub, OAEP(r1, r2));
// Bob向Alice发送加密的r3和r4
const mB = encryptWithRSA(aPub, OAEP(r3, r4));
// Alice和Bob计算会话密钥
const K_AB = J(r1 ^ r3);
const K_BA = J(r2 ^ r4);
Python实现密钥交换:
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric padding import OAEP, MGF1
from cryptography.hazmat.primitives._ service import load_pem public key, load_pem private key
def generate_rsa_key_pair():
# 生成RSA密钥对
private_key = rsa generate private key(
public Exponent=65537,
key_size=2048
)
public_key = private_key public_key
# 导出PEM格式
private_pem = private_key public_bytes(
encoding= serialization EncODING PEM,
format= serialization FORMAT PEM PRIV ATE KEY
)
public_pem = public_key public_bytes(
encoding= serialization ENCODING PEM,
format= serialization FORMAT PEM Public KEY
)
return public_pem, private_pem
def encrypt_with_rsa(data, public_key_pem):
# 加载公钥
public_key = load_pem_public_key(
public_key_pem.encode('utf-8')
)
# 加密数据
encrypted = public_key encrypt(
data.encode('utf-8'),
OAEP(
mgf=MGF1 al go r i t h m=hashes.SHA256(),
algorithm=hashes.SHA256(),
label=None
)
)
return encrypted
# 示例使用
# Alice生成密钥对
aPub_pem, aPriv_pem = generate_rsa_key_pair()
# Bob生成密钥对
bPub_pem, bPriv_pem = generate_rsa_key_pair()
# Alice向Bob发送加密的r1和r2
mA = encrypt_with_rsa('r1r2', bPub_pem)
# Bob向Alice发送加密的r3和r4
mB = encrypt_with_rsa('r3r4', aPub_pem)
# Alice解密mB
decrypted_mB = rsa_decrypt(mB, aPriv_pem)
# Bob解密mA
decrypted_mA = rsa_decrypt(mA, bPriv_pem)
# 计算会话密钥
K_AB = J(decrypted_mA ^ decrypted_mB)
K_BA = J(decrypted_mA ^ decrypted_mB)
3.3 消息摘要算法
3.3.1 MD5与SHA
MD5和SHA系列算法是常用的消息摘要算法,用于验证数据完整性或生成签名。材料的案例中使用了MD5加密:
// JS MD5加密
function hex_md5(s) {
return binl2hex核心 md5(str2binl(s), s.length * chrsz));
}
// 调用示例
const password = "123456";
const encryptedPassword = hex_md5(password);
Python实现MD5加密:
import hashlib
def hex_md5(s):
# 将字符串转换为字节数组
s = s.encode('utf-8')
# 计算MD5哈希值
md5_hash = hashlib.md5(s).hexdigest()
return md5_hash
# 示例使用
password = "123456"
encrypted_password = hex_md5(password)
print(encrypted_password)
3.3.2 HMAC-SHA256
HMAC(基于哈希的消息认证码)是一种结合哈希函数和密钥的消息认证码算法,常用于签名验证。材料展示了JS中使用HMAC-SHA256的代码:
// JS生成HMAC-SHA256签名
function generateHmacSHA256(data, key) {
const和谐算法 = CryptoJS.HmacSHA256(data, key);
return和谐算法.toString();
}
Python验证HMAC-SHA256签名:
import hashlib
import hmac
def verify_hmac-sha256签名(data, key, signature):
# 生成HMAC-SHA256签名
computed signature = mac.new(key.encode('utf-8'), msg=data.encode('utf-8'),消化算法=hashlib.sha256).hexdigest()
# 验证签名
return computed signature == signature
# 示例使用
data = "虚构的订单数据"
key = "秘密密钥"
signature = "生成的HMAC-SHA256签名"
is_valid = verify_hmac-sha256签名(data, key, signature)
print("签名验证结果:", is_valid)
四、复杂JS逆向案例实战
4.1 案例1:多层加密的电商订单系统
4.1.1 案例背景
假设我们有一个虚构的电商网站,其订单提交接口使用多层加密保护参数。加密流程包括:
- 使用SHA512对订单数据进行哈希
- 使用动态生成的AES密钥对哈希结果进行加密
- 使用RSA-OAEP对AES密钥进行加密
- 使用HMAC-SHA256对加密后的数据生成签名
4.1.2 加密流程分析
1. 首先分析网络请求:在Chrome开发者工具的Network面板中,找到订单提交的请求,观察加密参数的传输方式。
2. 定位加密代码:在Sources面板中,搜索"SHA512"、"AES"、"RSA"等关键词,定位加密函数的实现。
3. 设置断点:在找到的加密函数处设置断点,观察加密过程中的变量变化,记录关键参数。
4. 分析加密逻辑:通过动态调试,发现加密流程如下:
// 电商订单加密逻辑
function encryptOrder(orderData) {
// 生成动态盐值
const salt = generateSalt();
// 生成AES密钥
const aesKey = PBKDF2('password', salt, 1000, 32, 'SHA512');
// 使用AES加密订单数据
const encryptedData = AES.encrypt(orderData, aesKey, 'CBC');
// 使用RSA加密AES密钥
const encryptedAesKey = RSA.encrypt(aesKey, rsaPubKey);
// 生成HMAC-SHA256签名
const signature = MAC.generate(encryptedData + encryptedAesKey, macKey);
// 返回加密参数
return {
encryptedData: encryptedData,
encryptedAesKey: encryptedAesKey,
signature: signature,
salt: salt
};
}
4.1.3 Python逆向实现
1. 安装依赖库:
pip install cryptography CryptoJS pbkdf2
2. 实现加密函数:
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from pbkdf2 import PBKDF2
import hashlib
import base64
import os
def generate_salt(length=16):
# 生成随机盐值
return os.urandom(length)
def pbkdf2 Derive Key(password, salt, iterations, key_length, digest):
# 使用PBKDF2派生密钥
pbkdf2 = PBKDF2(password, salt)
return pbkdf2.read(key_length)
def aes encrypt(data, key, iv):
# 创建AES加密器
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
encryptor = cipher.encryptor()
# 加密数据
padded_data = data + (16 - len(data) % 16) * chr(16).encode('utf-8')
encrypted = encryptor.update(padded_data) + encryptor finalize()
return encrypted
def rsa encrypt(data, public_key):
# 使用RSA加密
# 这里需要实现RSA加密逻辑,或使用第三方库如cryptography
# 示例:返回加密后的数据(实际需要实现RSA加密)
return base64.b64encode(data)
def mac generate(data, key):
# 生成HMAC-SHA256签名
mac = hashlib.sha256()
mac.update(key + data)
return mac.hexdigest()
# 示例使用
order_data = "虚构的订单数据"
salt = generate_salt()
password = "password"
# 生成AES密钥
aes_key = pbkdf2 Derive Key(password, salt, 1000, 32, hashlib.sha512)
# 加密订单数据
encrypted_data = aes encrypt(order_data, aes_key, iv)
# 加密AES密钥
encrypted_aes_key = rsa encrypt(aes_key, rsaPubKey)
# 生成签名
signature = mac generate(encrypted_data + encrypted_aes_key, macKey)
# 返回加密参数
encrypted_params = {
'encryptedData': encrypted_data,
'encryptedAesKey': encrypted_aes_key,
'signature': signature,
'salt': salt
}
print(encrypted_params)
3. 模拟发送请求:
import requests
# 构造请求
url = "https://虚构的电商网站.com/api/order"
headers = {
'Content-Type': 'application/json',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36'
}
data = encrypted_params
# 发送请求
response = requests.post(url, headers=headers, json=data)
print(response.json())
4.1.4 案例总结
这个案例展示了多层加密的逆向方法,包括SHA512哈希、AES加密、RSA密钥加密和HMAC签名验证。关键点在于理解各层加密的依赖关系,并在Python中准确复现相同的加密流程。通过这个案例,您可以掌握如何处理复杂的加密参数生成逻辑。
4.2 案例2:基于Canvas指纹的动态签名系统
4.2.1 案例背景
假设我们有一个虚构的金融平台,其登录接口使用Canvas指纹和动态时间戳生成签名。加密流程包括:
- 使用Canvas绘制特定图案,生成指纹值
- 结合时间戳和指纹值生成动态密钥
- 使用HMAC-SHA256对用户名和密码生成签名
- 将签名和指纹一起发送到服务器
4.2.2 加密流程分析
1. 分析Canvas指纹生成:在材料[75]中,我们发现网站使用以下代码生成Canvas指纹:
// Canvas指纹生成代码
function getCanvasFingerprint() {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// 绘制特定图案
ctx.fillStyle = '#FC9630';
ctx.fillRect(0, 0, 8, 8);
ctx.font = '14px Arial';
ctx.fillStyle = '#000000';
ctx.fillText('虚构的指纹数据', 4, 17);
// 获取Base64数据
const base64 = canvas.toDataURL().replace('data:image/png;base64,', '');
const bin = window.atob(base64);
const hex = bin2hex(bin);
// 返回指纹值
return hex;
}
// 将二进制转换为十六进制
function bin2hex(s) {
const _s = s + '';
const f = _s.length;
const a = [];
for (let i = 0; i < f; i++) {
a[i] = _s.charCodeAt(i).toString(16).replace(/^([\da-f])/g, '0$1');
}
return a.join('');
}
2. 分析动态密钥生成:网站使用以下代码结合时间戳和Canvas指纹生成动态密钥:
// 动态密钥生成代码
function generateDynamicKey() {
const timestamp = Date.now();
const canvasFingerprint = getCanvasFingerprint();
// 生成密钥
const key = PBKDF2(
'password',
canvasFingerprint + timestamp,
1000,
32,
'SHA512'
);
return key;
}
3. 分析签名生成:网站使用以下代码生成HMAC-SHA256签名:
// 签名生成代码
function generateSignature(username, password) {
const key = generateDynamicKey();
const data = username + password + Date.now();
// 生成HMAC-SHA256签名
const signature = CryptoJS.HmacSHA256(data, key).toString();
return signature;
}
4.2.3 Python逆向实现
1. 模拟Canvas指纹生成:材料[78]提供了伪造Canvas指纹的方法:
from PIL import Image, ImageDraw
import io
import base64
def generate canvas fingerprint():
# 创建8x8的图像
img = Image.new('RGB', (8, 8), color='white')
d = ImageDraw.Draw(img)
# 绘制特定图案
d.rectangle([0, 0, 8, 8], fill='#FC9630')
d.text((4, 17), '虚构的指纹数据', fill='black', font=ImageFont.truetype('Arial.ttf', 14))
# 转换为Base64
缓冲区 = io.BytesIO()
img.save(缓冲区, format='PNG')
base64_data = base64.b64encode(缓冲区.getvalue())
# 返回十六进制指纹
return bin2hex(base64_data)
2. 实现动态密钥生成:
from pbkdf2 import PBKDF2
import hashlib
import os
def generate dynamic key():
# 生成时间戳
timestamp = int(time.time() * 1000)
# 生成Canvas指纹
canvas_fingerprint = generate canvas fingerprint()
# 生成密钥
salt = canvas_fingerprint + str(timestamp)
key = PBKDF2(
'password',
salt.encode('utf-8'),
iterations=1000,
key_length=32,
digest=hashlib.sha512
).read(32)
return key
3. 实现签名生成:
import hashlib
import base64
def generate signature(username, password):
# 生成动态密钥
key = generate dynamic key()
# 生成签名数据
timestamp = int(time.time() * 1000)
data = f"{username}{password}{timestamp}"
# 生成HMAC-SHA256签名
mac = hashlib.sha256()
mac.update(key + data.encode('utf-8'))
signature = mac.hexdigest()
return signature
4. 模拟发送请求:
import requests
import json
import time
# 构造请求
url = "https://虚构的金融平台.com/api/login"
headers = {
'Content-Type': 'application/json',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36'
}
username = "test_user"
password = "test_password"
# 生成签名
signature = generate signature(username, password)
timestamp = int(time.time() * 1000)
# 构造请求体
data = {
'username': username,
'password': password,
'signature': signature,
'timestamp': timestamp,
'canvasFingerprint': generate canvas fingerprint()
}
# 发送请求
response = requests.post(url, headers=headers, json=data)
print(response.json())
4.2.4 案例总结
这个案例展示了如何处理基于Canvas指纹的动态签名系统。关键点在于准确模拟Canvas指纹的生成过程,并理解动态密钥生成的依赖关系。通过这个案例,您可以掌握如何处理环境依赖型的加密参数生成逻辑。
4.3 案例3:基于WebGL指纹的加密系统
4.3.1 案例背景
假设我们有一个虚构的游戏平台,其API请求使用WebGL指纹和动态时间戳生成加密参数。加密流程包括:
- 使用WebGL渲染特定3D场景,生成指纹值
- 结合时间戳和WebGL指纹生成动态密钥
- 使用AES-GCM加密请求数据
- 使用RSA-OAEP加密动态密钥
- 将加密后的数据和指纹一起发送到服务器
4.3.2 加密流程分析
1. 分析WebGL指纹生成:在材料[84]中,我们发现网站使用以下代码生成WebGL指纹:
// WebGL指纹生成代码
function getWebGLFingerprint() {
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl');
if (!gl) return null;
// 创建着色器
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl ShaderSource(vertexShader, 'void main() { gl glPosition = vec4(0.0, 0.0, 0.0, 1.0); }');
gl compile Shader(vertexShader);
const fragmentShader = gl.createShader(gl FRAGMENT Shader);
gl ShaderSource(fragmentShader, 'void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); }');
gl compile Shader(fragmentShader);
// 创建程序
const program = gl.createProgram();
gl attach Shader(program, vertexShader);
gl attach Shader(program, fragmentShader);
gl link Program(program);
gl use Program(program);
// 渲染并获取指纹
const纹理= gl.createTexture();
gl active Texture(gl TEXTURE0);
gl bind Texture纹理);
gl texture2D纹理, gl TEXTURE_2D, 0, glRGBA, 8, 8, 0, glRGBA, glUNSIGNED BYTES, null);
gl generate Mipmap纹理);
const像素= new Uint8Array(8 * 8 * 4);
gl read pixels(0, 0, 8, 8, glRGBA, glUNSIGNED BYTES, 像素);
// 转换为十六进制
const hex = bin2hex(像素);
// 返回指纹值
return hex;
}
2. 分析动态密钥生成:网站使用以下代码结合时间戳和WebGL指纹生成动态密钥:
// 动态密钥生成代码
function generateDynamicKey() {
const timestamp = Date.now();
const webglFingerprint = getWebGLFingerprint();
// 生成密钥
const key = PBKDF2(
'password',
webglFingerprint + timestamp,
1000,
32,
'SHA512'
);
return key;
}
3. 分析AES-GCM加密:网站使用以下代码进行AES-GCM加密:
// AES-GCM加密代码
async function encryptWithAES(data, key) {
const iv = CryptoJS enc.Utf8.parse('123456789012'); // 12字节IV
// 加密数据
const encrypted = await window.crypto.subtle.encrypt(
{
name: 'AES-GCM',
iv: iv
},
key,
new TextEncoder().encode(data)
);
// 返回加密数据和标签
return {
ciphertext: encrypted,
tag: encrypted
};
}
4. 分析RSA-OAEP加密:网站使用以下代码进行RSA-OAEP加密:
// RSA-OAEP加密代码
function encryptWithRSA(data, public_key) {
// 使用RSA-OAEP加密
const encrypted = CryptoJS.RSA.encrypt(
data,
public_key,
{
padding: CryptoJS padding.OAEP
}
);
return encrypted.toString();
}
4.3.3 Python逆向实现
1. 模拟WebGL指纹生成:由于Python无法直接模拟WebGL渲染,我们可以使用固定值代替:
def generate webgl fingerprint():
# 固定返回特定值,模拟WebGL指纹
return 'GLSL 1.0'
2. 实现动态密钥生成:
from pbkdf2 import PBKDF2
import hashlib
import os
def generate dynamic key():
# 生成时间戳
timestamp = int(time.time() * 1000)
# 生成WebGL指纹
webgl_fingerprint = generate webgl fingerprint()
# 生成密钥
salt = webgl_fingerprint + str(timestamp)
key = PBKDF2(
'password',
salt.encode('utf-8'),
iterations=1000,
key_length=32,
digest=hashlib.sha512
).read(32)
return key
3. 实现AES-GCM加密:
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
import os
def encrypt_with_aes(data, key, iv):
# 创建AES-GCM加密器
cipher = Cipher(algorithms.AES(key), modes.GCM(iv), backend=default_backend())
encryptor = cipher.encryptor()
# 加密数据
encrypted = encryptor.update(data.encode('utf-8')) + encryptor finalize()
# 返回加密数据和标签
return {
'ciphertext': encrypted,
'tag': encryptor finalize()
}
4. 实现RSA-OAEP加密:材料[59]提供了RSA-OAEP加密的原理,但实际实现需要私钥:
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric padding import OAEP, MGF1
from cryptography.hazmat.backends import default_backend
import base64
def rsa encrypt(data, public_key_pem):
# 加载公钥
public_key = load_pem_public_key(
public_key_pem.encode('utf-8'),
backend=default_backend()
)
# 加密数据
encrypted = public_key encrypt(
data,
OAEP(
mgf=MGF1 al go r i t h m=hashes.SHA256(),
algorithm=hashes.SHA256(),
label=None
)
)
return base64.b64encode(encrypted)
5. 模拟发送请求:
import requests
import json
import time
# 构造请求
url = "https://虚构的游戏平台.com/api/login"
headers = {
'Content-Type': 'application/json',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36'
}
username = "test_user"
password = "test_password"
# 生成动态密钥
key = generate dynamic key()
# 生成IV
iv = os.urandom(12) # 12字节IV
# 加密数据
encrypted_data = encrypt_with_aes(username + password, key, iv)
# 加密动态密钥
encrypted_key = rsa encrypt(key, rs
1093

被折叠的 条评论
为什么被折叠?



