进入目标站点

步骤一:识别加密

找接口

打开该接口的 Preview(预览),发现响应内容是一大段乱码字符串,这表明数据被加密或编码。

进一步观察发现:

翻页时每次请求都会出现新的 baseInfo 数据包。

每次响应的字符串长度略有不同(9009、8959、8538 等),说明返回内容确实是动态密文。

推测:网站将真实数据加密后再传给前端,前端再执行解密逻辑展示数据。

找参数

字段名

示例值

含义解析

size

6

分页查询时,每页返回的数据条数,此处为 6 条 / 页

current

1

分页查询的当前页码,此处表示查询第 1 页数据

dimensionTime

"2019"

时间维度参数,通常用于指定统计 / 查询的年份,此处为 2019 年

levelType

2

层级类型参数,值为 2,可能代表行政级别(如市级 / 区级)或数据聚合层级,具体需结合业务文档

propertyCode

["DISTRICT_PROP_GJ025_RJDQSCZZ", "DISTRICT_PROP_GJ117_NMSVGGQDCYYCLS", "DISTRICT_PROP_GJ001_NMHJRK"]

属性编码数组,用于指定需要查询的特定指标 / 维度项(如房产属性、区域统计项等)

结论:

  • 接口参数无加密痕迹,可直接使用。

  • 因此逆向重点转向响应内容的解密逻辑。

步骤二:参数定位

找位置

利用 XHR 断点技术找到接口参数生成的位置

步骤三:参数解析

找逻辑

t.data 生成规则:

  • 基础条件:仅当原始 t.data 是「非空字符串」时才会处理,否则保持原值;

  • 未加密场景:若字符串以 { 开头 → 直接 JSON 解析成对象赋值给 t.data

  • 加密场景:若字符串不以 { 开头 → 用 AES-ECB/Pkcs7 解密 → 解密结果 JSON 解析 → 赋值给 t.data

  • 依赖库:代码中的 u 是 CryptoJS 库(从 u.AES/u.mode.ECB 等特征可判断),l 是解密用的 AES 密钥。

由此得到核心参数:

  • 算法:AES

  • 模式:ECB

  • 填充方式:PKCS7

  • 密钥:QV1f3nHn2qm7i3xrj3Y9K9imDdGTjTu9

步骤四:参数调用

在本地用 Python 实现 AES 解密

    # AES解密密钥(需与服务端保持一致),转换为字节流
    key = 'QV1f3nHn2qm7i3xrj3Y9K9imDdGTjTu9'.encode('utf-8')
    # 创建AES解密器,使用ECB模式(无IV向量)
    aes = AES.new(key, AES.MODE_ECB)
    # 第一步:对加密字符串进行Base64解码,得到AES加密的字节数据
    decrypted_data = base64.b64decode(text)
    # 第二步:使用AES密钥解密字节数据
    decrypted_text = aes.decrypt(decrypted_data)
    # 第三步:去除解密后数据的填充(PKCS7填充),AES块大小固定为16
    decrypted_text = unpad(decrypted_text, AES.block_size)
    # 将解密后的字节数据转换为UTF-8编码的明文字符串并返回
    return decrypted_text.decode('utf-8')

完整的.py代码如下:

# 导入所需的加密解密库
from Crypto.Cipher import AES  # AES加密算法库
from Crypto.Util.Padding import unpad  # AES解填充工具
import base64  # Base64编解码工具
import requests  # HTTP请求库,用于发送接口请求

def get_data(page):
    """
    发送POST请求获取加密的接口数据
    :param page: int/str - 请求的页码,用于分页获取数据
    :return: str - 接口返回的加密字符串数据
    """
    # 请求头配置,模拟浏览器请求,避免被服务端拦截
    headers = {
        'Accept': 'application/json, text/plain, */*',  
        'Content-Type': 'application/json;charset=UTF-8',  
        'Origin': 'https://www.swguancha.com',  
        'Referer': 'https://www.swguancha.com/',  
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)', 
    }

    # POST请求的JSON参数体
    json_data = {
        'size': 6,  
        'current': f'{page}',  
        'propertyCode': [  
            'DISTRICT_PROP_GJ025_RJDQSCZZ',
            'DISTRICT_PROP_GJ117_NMSYGGQDCYYCLS',
            'DISTRICT_PROP_GJ001_NMHJRK',
        ],
        'dimensionTime': '2019',  
        'levelType': 2,  
    }

    # 发送POST请求到目标接口
    response = requests.post(
        'https://app.swguancha.com/client/v1/cPublic/consumer/baseInfo',  
        headers=headers,  
        json=json_data  
    )
    # 返回接口响应的文本内容(加密字符串)
    return response.text

def decrypt(text):
    """
    对接口返回的加密字符串进行AES-ECB解密
    :param text: str - 需要解密的Base64编码加密字符串
    :return: str - 解密后的明文字符串(JSON格式)
    """
    # AES解密密钥(需与服务端保持一致),转换为字节流
    key = 'QV1f3nHn2qm7i3xrj3Y9K9imDdGTjTu9'.encode('utf-8')
    # 创建AES解密器,使用ECB模式(无IV向量)
    aes = AES.new(key, AES.MODE_ECB)
    
    # 第一步:对加密字符串进行Base64解码,得到AES加密的字节数据
    decrypted_data = base64.b64decode(text)
    # 第二步:使用AES密钥解密字节数据
    decrypted_text = aes.decrypt(decrypted_data)
    # 第三步:去除解密后数据的填充(PKCS7填充),AES块大小固定为16
    decrypted_text = unpad(decrypted_text, AES.block_size)
    # 将解密后的字节数据转换为UTF-8编码的明文字符串并返回
    return decrypted_text.decode('utf-8')

# 主程序入口
if __name__ == "__main__":
    # 指定要请求的页码(第一页)
    page = 1
    # 调用接口获取加密数据
    encrypt_data = get_data(page)
    # 打印加密后的原始数据
    print('密文:', encrypt_data)
    # 对加密数据进行解密
    decrypt_data = decrypt(encrypt_data)
    # 打印解密后的明文数据
    print('明文:', decrypt_data)