Python爬虫JS逆向工程高级教程

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逆向过程中,分析加密参数并还原其生成方法是关键步骤。常用的方法包括:

  1. 网络请求分析:通过浏览器开发者工具的Network面板,捕获目标请求(XHR/fetch),观察加密参数的传输过程。
  2. 代码静态分析:在Sources面板中搜索加密参数名称或相关关键词,定位加密函数的实现。
  3. 动态调试:在加密函数处设置断点,观察变量变化,记录加密过程中的中间值。
  4. 环境模拟:在Python中模拟浏览器环境,执行JavaScript加密代码,生成相同的加密参数。

还原加密参数的核心思路是:找到加密函数的输入参数、密钥和算法,然后在Python中复现相同的加密过程。这需要对JavaScript加密代码有深入的理解,并能够将其转化为Python代码。

二、浏览器调试技术详解

2.1 Chrome开发者工具基础

Chrome开发者工具(Chrome DevTools)是JS逆向分析的核心工具。它提供了完整的前端开发和调试环境,包括Elements、Console、Sources、Network等面板。

打开Chrome开发者工具的方法有三种

  1. 点击浏览器右上角的"…"(自定义及控制Google Chrome)>更多工具>开发者工具
  2. 在页面空白处,鼠标右击,选择"检查"(或"审查元素")
  3. 使用快捷键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.keyseval等方法尝试恢复变量名。例如:

// 查找以"_"开头的变量
for (var p in window) {
    if (psubstr(0, 2) !== "_$") continue;
    console.log(p + " >>> " + eval(p));
}

4. 函数劫持:通过重写关键函数(如XMLHttpRequest.sendfetch),可以捕获加密前的参数。例如:

// 劫持 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 案例背景

假设我们有一个虚构的电商网站,其订单提交接口使用多层加密保护参数。加密流程包括:

  1. 使用SHA512对订单数据进行哈希
  2. 使用动态生成的AES密钥对哈希结果进行加密
  3. 使用RSA-OAEP对AES密钥进行加密
  4. 使用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指纹和动态时间戳生成签名。加密流程包括:

  1. 使用Canvas绘制特定图案,生成指纹值
  2. 结合时间戳和指纹值生成动态密钥
  3. 使用HMAC-SHA256对用户名和密码生成签名
  4. 将签名和指纹一起发送到服务器
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指纹和动态时间戳生成加密参数。加密流程包括:

  1. 使用WebGL渲染特定3D场景,生成指纹值
  2. 结合时间戳和WebGL指纹生成动态密钥
  3. 使用AES-GCM加密请求数据
  4. 使用RSA-OAEP加密动态密钥
  5. 将加密后的数据和指纹一起发送到服务器
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
### Python 爬虫与 JavaScript 逆向工程教程 #### 学习目标 掌握 Python 爬虫中的 JavaScript 逆向工程技术,能够解析并模拟复杂的 Web 请求,处理动态加载的内容。 #### 关键概念和技术 - **JavaScript 执行环境**:为了使 Python 能够执行 JavaScript 代码,可以利用 PyExecJS 或者其他似的库创建一个 JavaScript 运行环境[^2]。 - **DOM 对象映射**:理解 Python 和 JavaScript 中的数据结构差异对于成功进行 JS 逆向至关重要。例如,在 Python 中 `dict` 型对应于 JSON 的 object;而 list/tuple 则分别对应 array[^3]。 - **网络请求分析**:使用浏览器开发者工具来捕获和审查 HTTP(S) 流量,特别是关注 POST/GET 请求及其携带的有效载荷(payload),这有助于识别出用于身份验证或其他重要操作的关键参数[^5]。 - **数据解密**:当遇到经过加密传输的信息时,需深入研究其背后的算法原理,并可能借助第三方库完成相应的编码转换工作。 - **模拟登录流程**:通过 requests 库发送带有适当头部信息以及表单字段的 HTTP 请求以实现自动化的账户认证过程。同时要注意管理好 session cookie 来保持会话状态连续性。 - **动态页面抓取**:针对那些依赖 AJAX 技术异步更新部分内容的情况,则可考虑采用 Selenium WebDriver 实现完整的浏览器交互体验,从而获取最终呈现给用户的 HTML 文档片段。 #### 示例代码 下面给出一段简单的例子展示如何用 Python 解析并运行来自网页上的 JavaScript 函数: ```python import execjs def execute_js_function(js_code, func_name, *args): ctx = execjs.compile(js_code) result = ctx.call(func_name, *args) return result ``` 此函数接收三个参数:要编译执行的 js 字符串、待调用的方法名以及该方法所需的任意数量的位置实参。它返回由指定名称所指代的 JavaScript 方法的结果。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZTLJQ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值