1. 项目背景与核心挑战
农机购置补贴是国家重要的惠农政策之一,各省市都建立了专门的在线申请系统。这类系统通常涉及大量敏感数据和资金流转,因此安全防护等级较高。近期在分析某省农机购置补贴系统时,发现其采用了国密SM4算法进行响应数据加密,并部署了动态验证码机制,给自动化数据处理带来了挑战。
这个逆向工程案例的典型性在于:它代表了当前政务系统安全防护的普遍水平——既采用了国家密码标准,又结合了常规的验证码防御。对于需要对接此类系统的开发者而言,理解其安全机制的工作原理和突破方法具有现实意义。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术方案选型与工具准备
2.1 逆向分析环境搭建
工欲善其事必先利其器。针对这个项目,我准备了以下工具链:
- Chrome DevTools + Fiddler:用于网络请求捕获和分析
- Node.js环境:用于模拟浏览器执行JavaScript
- Python 3.8+:主要开发语言,配合requests库处理HTTP请求
- 国密SM4算法实现库(gmssl或pycryptodome)
特别提示:在实际操作中,建议使用虚拟机或独立环境进行测试,避免对生产系统造成影响。同时要严格遵守相关法律法规,逆向分析仅限授权范围内的系统。
2.2 验证码识别方案对比
系统采用了常见的字符验证码,经过测试发现有以下特征:
- 4位数字+字母组合
- 简单干扰线和噪点
- 无动态扭曲效果
针对这种验证码,我们评估了三种方案:
- 第三方打码平台(成本高但稳定)
- 深度学习模型(需要标注数据)
- 传统图像处理(开发快但泛化性差)
最终选择了方案2+3的混合模式:先用OpenCV进行预处理(二值化+降噪),再使用轻量级CNN模型识别。实测准确率可达92%以上,完全满足业务需求。
3. 核心逆向过程详解
3.1 加密响应分析
通过抓包发现,系统关键接口的响应数据都是加密的,格式如下:
code复制{
"code": 200,
"data": "3D5E...(加密字符串)",
"msg": "success"
}
进一步分析发现:
- 加密算法为国密SM4(ECB模式)
- 密钥通过前端JavaScript动态生成
- 每次会话会更新加密密钥
3.2 密钥获取方案
密钥生成逻辑位于一个混淆过的JS文件中。通过以下步骤还原:
- 使用AST解析工具反混淆
- 定位关键函数
generateSM4Key() - 分析其依赖的浏览器指纹参数
最终提取出的密钥生成逻辑为:
javascript复制function getKey() {
const t = new Date().getTime();
return md5(navigator.userAgent + t.toString().slice(0,8)).slice(8,24);
}
3.3 Python实现解密
基于上述分析,解密流程的Python实现如下:
python复制from gmssl.sm4 import CryptSM4, SM4_DECRYPT
import hashlib
import time
def decrypt_response(encrypted_data):
# 生成密钥(模拟浏览器逻辑)
t = int(time.time() * 1000)
key_seed = f"{user_agent}{str(t)[:8]}"
key = hashlib.md5(key_seed.encode()).hexdigest()[8:24]
# SM4解密
crypt_sm4 = CryptSM4()
crypt_sm4.set_key(key.encode(), SM4_DECRYPT)
decrypted_data = crypt_sm4.crypt_ecb(bytes.fromhex(encrypted_data))
return decrypted_data.decode()
4. 验证码处理实战
4.1 验证码获取与预处理
验证码接口分析:
- GET /api/captcha?t=时间戳
- 返回二进制图片数据
预处理流程代码示例:
python复制import cv2
import numpy as np
def preprocess_captcha(img_data):
# 转为灰度图
gray = cv2.cvtColor(img_data, cv2.COLOR_BGR2GRAY)
# 二值化
_, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 降噪(中值滤波)
denoised = cv2.medianBlur(binary, 3)
return denoised
4.2 识别模型训练
使用CNN模型进行识别,网络结构如下:
python复制from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense
model = Sequential([
Conv2D(32, (3,3), activation='relu', input_shape=(30, 90, 1)),
MaxPooling2D((2,2)),
Conv2D(64, (3,3), activation='relu'),
Flatten(),
Dense(64, activation='relu'),
Dense(36, activation='softmax') # 26字母+10数字
])
训练技巧:人工标注500张验证码作为训练集,使用数据增强(旋转±10度、添加轻微噪声)提升模型泛化能力。
5. 完整请求流程实现
5.1 会话维持机制
系统采用JWT进行会话管理,需要处理以下流程:
- 首次访问获取CSRF Token
- 带Token请求验证码
- 提交登录表单时携带Token和验证码
关键实现代码:
python复制class SubsidySystem:
def __init__(self):
self.session = requests.Session()
self.headers = {
'User-Agent': 'Mozilla/5.0...'
}
def get_token(self):
response = self.session.get(
'https://xxx.com/login',
headers=self.headers
)
# 从HTML中提取CSRF Token
self.token = re.search(r'name="_token" value="(.+?)"', response.text).group(1)
def get_captcha(self):
response = self.session.get(
f'https://xxx.com/api/captcha?t={int(time.time())}',
headers=self.headers
)
img = cv2.imdecode(np.frombuffer(response.content, np.uint8), cv2.IMREAD_COLOR)
return self.recognize_captcha(img)
5.2 异常处理策略
在实际运行中需要处理以下异常情况:
- 验证码识别失败(自动重试3次)
- Token过期(重新初始化会话)
- 请求频率限制(添加随机延迟)
建议的健壮性处理代码:
python复制def safe_request(self, url, retry=3):
for i in range(retry):
try:
response = self.session.get(url)
if '验证码错误' in response.text:
raise CaptchaError
return self.decrypt_response(response.json()['data'])
except Exception as e:
if i == retry - 1:
raise
time.sleep(random.uniform(1, 3))
self.refresh_session()
6. 关键问题与解决方案
6.1 动态密钥跟踪问题
问题现象:密钥每小时变化,导致解密失败。
解决方案:
- 在每次请求前获取当前时间戳
- 精确模拟浏览器密钥生成算法
- 添加时间容错机制(±5分钟)
6.2 验证码识别率波动
问题现象:在不同时段识别准确率差异大。
优化措施:
- 收集不同时段的验证码样本
- 对样本进行聚类分析,发现存在3种不同风格
- 训练集成模型,针对不同风格使用不同预处理参数
6.3 请求指纹检测
问题现象:高频请求被拦截。
反检测策略:
- 模拟完整浏览器指纹(navigator对象属性)
- 保持合理的请求间隔
- 随机化User-Agent中的次要参数
7. 法律合规与伦理考量
需要特别强调的是,此类技术研究必须遵守以下原则:
- 仅限授权范围内的系统测试
- 不得用于绕过正常业务流程
- 不获取、不存储敏感用户数据
- 测试数据及时清理
在实际项目中,我们仅使用测试账号进行操作,所有技术方案都经过法律合规审查。建议开发者在类似项目中也要建立完善的风控机制。
