1. 项目背景与目标
最近在研究某金融数据平台的接口加密机制时,遇到了一个名为md5__1038的参数。这个参数从最初的简单MD5演变到现在长达1038位的复杂字符串,其生成过程涉及多重混淆和加密算法。本文将详细记录整个逆向分析过程,包括AST反混淆、算法还原以及补环境的关键点。
2. 初步分析与调试准备
2.1 环境搭建与工具选择
在进行逆向分析前,我准备了以下工具链:
- Chrome开发者工具:用于基础调试和网络请求分析
- Firefox浏览器:某些反调试机制在Firefox上更容易绕过
- Babel AST解析工具:用于JavaScript代码的语法树分析和转换
- Python环境:用于算法还原后的实现验证
提示:在实际操作中发现,该网站对Chrome的调试器检测较为严格,推荐使用Firefox进行初步分析,可以绕过部分反调试机制。
2.2 请求流程分析
通过抓包观察,发现关键请求流程如下:
- 首次访问会返回一段混淆的JavaScript代码
- 该代码在客户端生成md5__1038参数
- 携带此参数再次请求,获取包含xq_a_token的有效响应
- 后续请求都需要携带xq_a_token作为身份凭证
3. 反混淆过程详解
3.1 代码结构与检测机制
获取到的混淆代码主要包含两个文件:
- 主逻辑文件(jquery相关)
- VM虚拟机文件(核心参数生成)
这两个文件都采用了相似的反格式化检测机制:
javascript复制// 典型的代码完整性检测
function checkCodeIntegrity() {
var code = wholeFunction.toString();
var hash = calculateHash(code);
if (hash !== expectedValue) {
// 触发异常路径
enterDeadLoop();
}
}
3.2 AST解混淆步骤
3.2.1 绕过hash校验
首先需要处理的是代码的hash校验机制。通过分析发现校验逻辑依赖三个关键参数:
- 代码字符串的特定前缀
- 中间的数字校验值
- 代码字符串的后缀
解决方案是定位到校验函数,将其硬编码为返回固定值:
javascript复制// 原始混淆代码
var fY = combined_jquery(i, j, P);
// 修改后
var fY = 470; // 硬编码通过校验
3.2.2 字面量还原
混淆代码中包含大量十六进制数值和复杂表达式:
javascript复制// 混淆形态
var a = 0x2f3d ^ 0x45a1;
var b = (123 * 456) % 789;
// 还原后
var a = 19324; // 计算结果
var b = 123; // 简化后
使用Babel的path.evaluate()可以自动计算并替换这些表达式。
3.2.3 字符串解密
代码中使用了多层字符串加密:
javascript复制// 加密形态
var str = _0x12ab(0x123);
// 解密后
var str = "example";
处理方案:
- 提取所有解密函数
- 在AST遍历时识别解密调用
- 预执行解密函数获取明文
- 替换原调用节点
3.2.4 控制流平坦化
混淆代码使用了复杂的分发器模式:
javascript复制// 原始控制流
while(1) {
switch(state) {
case 0: ...; state = 5; break;
case 1: ...; state = 3; break;
// ...
}
}
// 还原后
// 线性执行的实际逻辑
还原步骤:
- 分析状态变量和跳转逻辑
- 构建控制流程图
- 按照执行顺序重组基本块
4. 核心算法分析
4.1 参数生成流程
经过反混淆后,核心参数生成流程如下:
- 收集环境信息(包括UA、屏幕分辨率等)
- 生成随机种子
- 多层哈希计算(SHA1 + 自定义哈希)
- LZ-String风格压缩
- Base64编码最终输出
4.2 关键函数实现
核心的m6函数简化实现:
javascript复制function generate_md5__1038(params) {
// 1. 环境信息收集
var env = collectEnvironment();
// 2. SHA1计算
var hash = sha1(env);
// 3. 自定义混淆
var mixed = customMix(hash);
// 4. 压缩编码
var compressed = lzCompress(mixed);
return base64Encode(compressed);
}
4.3 补环境关键点
在纯Python实现时,需要特别注意以下环境检测:
- 错误堆栈检测
javascript复制Error.stackTraceLimit = 50;
- SVG检测
javascript复制n.B.SVGAnimateString();
- 随机数构造器检测
javascript复制Math.random.constructor("debugger;");
对应的Python实现需要模拟这些浏览器特性:
python复制class BrowserEnvironment:
@staticmethod
def svg_animate_string():
return None
@staticmethod
def get_stack_trace_limit():
return 50
5. Python实现方案
5.1 核心算法移植
将JavaScript算法移植到Python时的主要挑战:
- 无符号整数处理
- 位操作差异
- 浏览器API模拟
解决方案:
python复制def js_unsigned_right_shift(val, n):
return (val % 0x100000000) >> n
def custom_mix(input_str):
# JavaScript风格的哈希混合
h = 157763114
for c in input_str:
h = (h << 4) - h + ord(c)
h = js_unsigned_right_shift(h, 0)
return h
5.2 完整请求流程
python复制import hashlib
import base64
class XueQiuAPI:
def __init__(self):
self.session = requests.Session()
self.xq_a_token = None
def get_md5_param(self):
# 实现md5__1038生成逻辑
env_data = self.collect_env()
hash_str = self.custom_hash(env_data)
return self.encode_result(hash_str)
def request_data(self):
md5_param = self.get_md5_param()
first_resp = self.session.get(
"https://xueqiu.com/api",
params={"md5__1038": md5_param}
)
self.xq_a_token = first_resp.cookies.get("xq_a_token")
# 携带token请求真实数据
data_resp = self.session.get(
"https://xueqiu.com/data",
headers={"X-Token": self.xq_a_token}
)
return data_resp.json()
6. 经验总结与避坑指南
6.1 常见问题排查
-
参数不匹配
- 现象:服务器返回403错误
- 检查:确保环境信息收集完整,特别是屏幕分辨率和时区
-
哈希值不一致
- 现象:本地生成的md5__1038无效
- 检查:无符号整数处理是否正确,JavaScript的>>>运算符需要特殊处理
-
请求顺序错误
- 现象:无法获取xq_a_token
- 检查:必须严格按照先获取参数→再获取token的顺序
6.2 性能优化建议
-
缓存环境信息
- 非必要不重新收集环境数据
- 合理设置参数有效期
-
并行计算
- 将哈希计算等耗时操作并行化
python复制from concurrent.futures import ThreadPoolExecutor
def parallel_hash(inputs):
with ThreadPoolExecutor() as executor:
results = list(executor.map(compute_hash, inputs))
return results
- 算法优化
- 对核心哈希算法进行Cython加速
6.3 长期维护策略
-
监控机制
- 定期检查接口是否变更
- 设置自动化测试用例
-
模块化设计
- 将算法、请求、环境模拟分离
- 便于单独更新某个组件
-
版本兼容
- 保留旧版算法实现
- 根据服务端响应动态选择算法版本
7. 进阶技巧
7.1 动态代码解析
对于不断变化的混淆策略,可以构建自动化解析系统:
python复制class JSParser:
def __init__(self, code):
self.ast = parse(code)
def detect_obfuscation(self):
# 识别常见混淆模式
pass
def auto_deobfuscate(self):
# 应用相应的反混淆策略
pass
7.2 浏览器行为模拟
使用Pyppeteer等工具实现更真实的环境模拟:
python复制async def get_realistic_env():
browser = await launch()
page = await browser.newPage()
# 设置真实浏览器参数
await page.setViewport({'width': 1920, 'height': 1080})
await page.setUserAgent('Mozilla/5.0...')
# 执行环境收集脚本
env = await page.evaluate('''() => {
return {
screen: [window.screen.width, window.screen.height],
// 其他环境信息
};
}''')
await browser.close()
return env
7.3 机器学习辅助分析
对于新型混淆技术,可以训练模型识别代码模式:
-
特征提取:
- 操作码序列
- 控制流图特征
- 字符串使用模式
-
分类模型:
- 识别混淆类型
- 预测最佳反混淆策略
在实际操作中发现,这种复杂的参数生成机制通常会每3-6个月更新一次。保持对代码变化的敏感度,建立快速分析响应流程,是长期稳定的关键。建议将核心算法部分设计为可插拔的模块,当检测到接口失效时,能够快速替换新的算法实现而不影响整体架构。