iOS 逆向分析:东方财富请求头 em-clt-auth 与 qgqp-b-id 算法还原

0x0 前言

  • 分析背景 :在对 iOS 端 App 进行抓包分析时,发现其核心请求头包含 em-clt-auth 和 qgqp-b-id。

  • 目标:通过 IDA 静态分析与逻辑梳理,还原这两个参数的生成机制,为后续的接口自动化或安全研究提供参考。

0x1 em-clt-auth

  • 代码追踪 :展示 SBAppInfoUnit 相关的伪代码

  • 结论 :通过分析发现,em-clt-auth 在当前版本中是写死的固定字符串(202107295071;JprH1p17...),直接复用即可。

0x2 qgqp-b-id

3.1 溯源:请求头入口

在 sub_100358960 函数中,系统为 SBHttpTask 设置了一系列关键 Headers:

  • qgqp-b-id 的取值直接调用了 [[SBAppInfoUnit sharedSBAppInfoUnit] deviceUniqueID]。

  • 若该值为空(Length == 0),则使用硬编码常量 stru_103633988 作为占位符。

  • 同时处理了 em-clt-uiid(由 IDFV 生成)和 em-clt-auth。

3.2 跟踪:异步回调

通过对 deviceUniqueID 的 Setter 方法进行交叉引用(Xref),发现唯一写入该值的地方是 sub_1001FAB28:

  • 回调机制 :这是一个 Block 回调函数。当设备信息采集完成时,回调的第二个参数 a2(NSString 类型)会被写入 SBAppInfoUnit。

  • 结论 :qgqp-b-id 的本质就是 getDeviceInfo: 方法传递给回调的设备特征字符串。

3.3 调度:AQTDDeviceInfo

在 -[AQTDDeviceInfo getDeviceInfo:] 中,程序根据设备状态决定生成哪种指纹:

  • 逻辑判定 :系统会检查 exceed5days(是否超过5天)或读取 NSUserDefaults 中的 AQTDStatusCode。

  • 生成模式:

    • Abstract 模式 :返回由 getAbstractDeviceInfo 生成的摘要信息。

    • Full 模式 :调用 updateDeviceFullInfo 后,由 getFullDeviceInfo 生成完整的指纹。

3.4 算法概要
cpp 复制代码
1. 取:`sdkVersion`、`os`、`currentTime`、`gtoken`、`apkname`,以及 `abstractInfo`(或 fullInfo)。

2. `v6 = abstractInfo`(或 fullInfo)
`v15 = [SecurityUtil encryptAESData: v6]`
`v14 = [SecurityUtil encodeBase64String: v15]`

3. 拼串:

`v13 = stringWithFormat:@"%@|%@|%@|%@|%@|%@", sdkVersion, os, currentTime, gtoken, apkname, v14`
`v12 = [SecurityUtil encodeBase64String: v13]`
`v11 = [MD5Encryption md5EncryptWithString: v13]`
`v10 = v11 + "dxxxxv"`
`v9 = [MD5Encryption md5EncryptWithString: v10]`

5. 取子串并拼接:
`result = substring(v9, 2, 1) + substring(v9, 5, 1) + v12`

0x3 算法总结

1. 设备指纹的预加密 (AES-128-CBC)

算法的第一步是对序列化后的设备信息(fullInfo 或 abstractInfo)进行对称加密。

  • 加密执行 :调用 [SecurityUtil encryptAESData:]。

  • 算法细节 :采用 AES-128-CBC 模式。

  • 编码转换:将加密后的字节流通过 Base64 编码转化为可见字符串,记为 v14​。

2. 原始 Payload 的管道化拼接

将提取的环境变量与 Step 1 生成的密文按照特定顺序进行"管道式"拼接,生成中间变量 v13​。这是整个参数的核心数据载体:

v13​=sdkVersion∣os∣currentTime∣gtoken∣apkname∣v14​

3. 双层 MD5 盐值混淆

为了确保数据的不可篡改性并增加逆向难度,算法引入了双层 MD5 机制:

  • 全串编码:对拼接后的 v13​ 进行整体 Base64 编码,生成 v12​(此为最终数据体)。

  • 初次哈希:计算 v13​ 的 MD5 值,记为 v11​。

  • 注入盐值 :在 v11​ 末尾添加固定混淆盐值 dlxxxxxv,生成混淆串 v10​。

  • 二次哈希:对 v10​ 再次进行 MD5 计算,得到最终校验哈希 v9​。

4. 最终复合结果构造 (Result Construction)

最终生成的 qgqp-b-id 并非单纯的哈希值,而是一个由"校验前缀 + 编码数据体"构成的复合字符串:

  • 校验位提取 :从二次哈希结果 v9​ 中提取下标为 2 和 5 的两个字符(即字符串的第 3 位和第 6 位)。

  • 整体封装:将这两个校验字符拼接在 v12​ 之前。

最终生成的逻辑公式如下:

qgqp_b_id=v9​2+v9​5+Base64(v13​)

0x4 算法验证

通过frida抓包数据演示 qgqp-b-id 的前缀生成逻辑,验证 0x3 节中的算法推导。

校验位提取流程

根据日志输出,二次 MD5 的结果为:

e0 2 8 1 d 8 a 6 9 9 0 0 a 0 5 e e 2 2 2 9 f d 8 d 5 0 e 4 b 0

按照算法要求,提取该字符串的 第 3 位 和 第 6 位:

  • Index 2 (第 3 位) :2

  • Index 5 (第 6 位) :d

  • 组合前缀 :2d

实际抓包请求头:

  • Header : qgqp_b_id: 2dMS4wLjB8aU9TfDE3Njk3NTc3OTg3O...

抓包结果的首两位 2d 与我们通过双层 MD5 算法推导出的校验位完全一致,算法还原成功。

相关推荐
Rebecca_aLi几秒前
码匠教育:零基础学 Python,标识符到循环全套基础语法解析
开发语言·python·正则表达式
wuyk5552 分钟前
Python实战项目05:JSON数据解析与数据可视化小案例|全套实战闭环
python·信息可视化·json
belldeep4 分钟前
python:wps2md
python·wps2md
就叫年华吧丶7 分钟前
长文档点目录定位总是不准?一个 content-visibility + 平滑滚动引发的连环坑(Vue3 实战)
前端·javascript·算法·vue
IvanCodes8 分钟前
Python 正则表达式(十四):文本匹配、查找与替换
开发语言·python·正则表达式
夜雪一千8 分钟前
Python URL编码踩坑:单层编码、双重编码实战解析
开发语言·python
茶栀(*´I`*)11 分钟前
【Python数据可视化】Matplotlib从零到精通:图表核心解剖、多子图布局与三大经典图表实战
python·信息可视化·matplotlib
FPGA信号处理14 分钟前
【信号检测与估计】第四节课:非高斯噪声下的 BLUE、极大似然与 EM 算法
人工智能·算法·机器学习
SelectDB技术团队17 分钟前
Agent Trace 数据底座建设:宽表建模、全文检索与成本聚合的配置与验证步骤
大数据·python·clickhouse·elk·elasticsearch·全文检索·复杂查询
计算机源码社26 分钟前
【27届大数据毕设】基于Spark的TikTok短视频属性分享与互动比率可视化分析系统 基于机器学习的TikTok短视频认证属性与互动效能关联分析
大数据·hadoop·python·机器学习·spark·毕业设计·课程设计