某皮 作为东南亚最大的电商平台之一,其移动端应用采用了相当复杂的反爬虫机制。本文基于对 某皮 和 iOS 客户端的算法还原代码,从架构设计、加密体系、算法标准性、指纹采集、防护强度等维度进行深度对比分析。
某皮 的反爬体系主要由两大模块构成:SAP(某皮 Anti-fraud Protocol)请求签名系统和设备指纹系统。两个平台在整体架构上保持一致,但在具体算法选择、加密强度、序列化方式和设备信息采集方面存在显著差异。

SAP 签名系统是整个反爬体系的核心,采用四步签名流程,生成一组动态的 HTTP Header 键值对。整体架构如下图所示:

第一步:生成请求标识(x-sap-ri)
该标识由 4 字节时间戳和 22 字节随机序列组成,其中随机序列基于 MT19937 伪随机数生成器,以时间偏移量异或固定种子派生。Android 和 iOS 在这一环节的核心逻辑完全一致,仅在 UUID 版本标记字节上存在微小差异(Android 使用 0x10,iOS 使用 0x20)。
第二步:生成算法选择签名(Value1)
系统根据 URL 计算出动态 key1,通过基于时间戳的 MT19937 随机洗牌算法从 9 种哈希函数中选出 3 种作为本次请求的算法组合,然后使用类 RC4 流加密对算法选择标识进行加密。这一步的核心目的是将本次请求使用的哈希算法组合隐藏在签名中,迫使分析者必须完整还原所有 9 种算法。
第三步:生成时间验证签名(Value2)
使用第二步选出的第一种哈希算法计算动态 key2,然后通过 ChaCha20 流加密生成签名值。这里 Android 使用 Blake2b 作为密钥派生哈希,而 iOS 使用 Blake3,这是两个平台在加密体系上的第一个重要分歧点。当存在请求体数据时,还会生成第三个签名(Value3)用于保护请求体完整性。
第四步:生成设备指纹长签名(Value4)
这是整个签名体系中最复杂的部分,包含完整的设备指纹信息。系统根据 XOR 密钥的首字节模 4 值,从 ChaCha20、RC6、Threefish-512、Salsa20 四种加密算法中随机选择一种对设备信息进行加密,最后与时间戳异或哈希值组合后再与 XOR 密钥进行逐字节异或操作。
每次签名调用返回一组键值对:
x-sap-ri:请求标识,用于关联请求与服务端记录3a9dea63):对应 Value1,包含算法选择信息65fc4edb):对应 Value2,包含时间验证信息9a9678bf):对应 Value4,包含加密的设备指纹所有动态 key 均通过对 URL 进行特定的多项式哈希计算得出,Android 端使用的乘数为 0x334B,iOS 端使用的乘数为 0x200F1,这种差异使得同一 URL 在两个平台上产生的签名 key 完全不同。
Android 端的 SAP 签名系统支持多个应用版本,代码中明确实现了 3.59.41、3.64.44、3.67.26、3.67.27 四个版本的数据结构。每个版本在设备信息的排列顺序、偏移量计算、固定标识值上都有差异,这表明 某皮 在持续迭代其反爬策略,通过版本差异增加逆向维护成本。
设备指纹信息采用紧凑的自定义二进制格式封装,包含以下关键字段:MMPId(设备唯一标识)、roProductModel(设备型号)、roProductBrand(设备品牌)、gaID(Google 广告 ID)、AfId(第三方归因标识)、Android ID、packageName(包名)、packageMd5(APK MD5 校验值)、packageCharSequence(应用显示名称)、token(推送令牌)等。
数据编码采用了独特的"后向标记"(backward mark)机制。在明文数据的头部,依次写入各个可变字段在数据体中的偏移位置,使得服务端可以通过头部索引快速定位各个字段,同时也增加了逆向分析的难度。这种设计类似于 FlatBuffers 的 vtable 机制,但采用了自定义的偏移计算方式。
Android 端的设备指纹系统拥有独立的上报通道,其核心流程如下:
首先采集原始设备信息并编码为 JSON 格式的键值对,键为数字索引,值为字符串。然后通过自定义的 XOR 加密算法对数据进行逐字节异或混淆,XOR 密钥由固定魔数 0x6484 与随机字符串和时间戳共同派生。
加密后的数据经过 zlib 压缩,再通过自定义的 AES-GCM 变体算法进行加密。这里的 AES-GCM 并非标准实现,而是基于从 native 库中提取的 AES 核心轮函数和 GHASH 实现自行构建的。计数器起始值为 2 而非标准的 1,且最终密文会与 ECDH 协商密钥再次进行 XOR 操作。
ECDH 密钥交换使用固定的服务端公钥(SECP256R1 曲线),客户端生成临时密钥对后计算共享密钥,取共享密钥的偶数索引字节作为 XOR 密钥。最终数据包包含版本号、公钥长度、数据长度、CRC32 校验值和加密数据体。
Android 端 SAP 签名使用的加密算法包括:
哈希算法族:Custom MurmurHash3-32(自定义变体)、标准 MurmurHash3-32、MurmurHash3-x64-128、Hash Mul EOR(自定义乘加异或哈希)、SpookyHash-32、xxHash-32、xxHash-64、Super Fast Hash、FastHash-64、SipHash-24、SipHash-24-64、Blake2b、CityHash-64。其中 CityHash-64 仅在 Android 端使用,用于 Value3 的加密算法选择派生。
流加密/分组加密算法族:ChaCha20、Salsa20、RC6、Threefish-512。四种加密算法在 Value4 中根据随机选择交替使用,增加了静态分析的难度。
伪随机数生成器:MT19937,用于随机序列生成、算法选择洗牌和随机字节生成。
编码算法:某皮 自定义 Base64(标准 Base64 表但可能有填充差异)。
Android 端设备指纹中包含模拟的 Java 栈追踪信息,格式如 85\n#srd(us:12)\n#aai(sf:250)\n#icp(sf:19)\n...。这些栈帧信息模拟了应用运行时的调用链,包含各类缩写标识和行号信息,用于验证请求是否来自真实的应用执行环境。不同应用版本的栈追踪内容也有所变化,表明服务端会校验栈帧与应用版本的匹配性。
iOS 端的 SAP 签名在整体流程上与 Android 高度一致,但在以下方面存在差异:
设备指纹采用 FlatBuffers 序列化格式。这是与 Android 最显著的区别。iOS 端构建了一个完整的 FlatBuffers buffer,包含 vtable(虚表)、table(数据表)和变长数据区。字段编号映射表定义了 31 个字段的位置,包括 iOS 版本号、越狱状态、IDFV、IDFA、时间戳随机值、时间戳 UUID、证书哈希、Keychain 数据、SHA1 哈希、屏幕参数等。
FlatBuffers 的使用使得 iOS 端的设备信息序列化更加规范和高效,但同时也引入了 FlatBuffers 特有的结构特征(如 vtable 大小、table 偏移计算方式),这些特征可以被服务端用来验证请求来源的平台类型。
iOS 端采集的设备信息具有鲜明的平台特征:
ios_version:iOS 系统版本jailbreak:越狱检测标志,格式为 0||1 这种双值表示idfv:供应商标识符,附带时间戳idfa:广告标识符(通常为全零,因用户隐私限制)timestamp_rand:随机时间戳标识timestamp_uuid:UUID 格式时间戳keychain:Keychain 存储数据cert_hash_1/2/3:证书哈希,其中 hash_2 和 hash_3 通过 XOR mask 从 hash_1 派生hash_1/2/3/4:多种哈希值,同样存在 XOR 关联派生关系sha1:SHA1 哈希,同样通过 XOR mask 从 cert_hash_1 派生screen_scale_1/2、screen_flag:屏幕参数值得注意的是,iOS 端的多个哈希字段之间存在确定的 XOR 关联关系。cert_hash_2 由 cert_hash_1 与固定掩码 XOR_MASK_C1_C2 异或得到,cert_hash_3 由 cert_hash_1 的前 32 字节与 XOR_MASK_C1_C3 异或得到。这种设计既保证了字段的表观多样性,又保持了内在的关联可验证性,服务端可以通过交叉验证检测伪造的设备信息。
iOS 端与 Android 端的主要加密差异在于:
Blake3 替代 Blake2b。iOS 端使用 Blake3 作为 Value2 的密钥派生哈希算法,Blake3 是 Blake2 的后继版本,具有更好的并行性能和安全性。iOS 代码中定义了专用的 Blake3 keyed hash 密钥常量。
MurmurHash3-x64-128 替代 CityHash-64。iOS 端在 Value3 的加密算法选择派生中使用 SpookyHash-128 的短输出(spookyhash128_short),而 Android 使用 CityHash-64。
设备指纹内嵌。iOS 端的设备指纹没有独立的上报通道,设备信息直接内嵌在 SAP 签名的 Value4 中,通过 FlatBuffers 序列化后再进行加密。

| 对比维度 | Android | iOS |
|---|---|---|
| URL 哈希乘数 | 0x334B | 0x200F1 |
| Value2 密钥派生 | Blake2b | Blake3 |
| Value3 算法选择哈希 | CityHash-64 | SpookyHash-128 Short |
| Value2 加密 Plaintext 魔数 | e903000001000200cdb87f28 | e903000002000300cdb87f28 |
| 设备信息序列化 | 自定义二进制 + 后向标记 | FlatBuffers |
| 独立指纹上报通道 | 是(ECDH + 自定义 AES-GCM) | 否(内嵌于 SAP 签名) |
| UUID 版本标记字节 | 0x10 | 0x20 |
| MT19937 实现变体 | 自定义 MT19937 | StdMt19937(更接近标准) |
Android 端采集的是典型的 Android 设备标识:Android ID、Google 广告 ID、APK 包名和 MD5、设备品牌和型号、推送令牌等。iOS 端采集的是典型的 iOS 设备标识:IDFV、IDFA、越狱状态、Keychain 数据、证书哈希、屏幕参数等。
从加密算法的选择来看,iOS 端在理论上具有略高的加密强度。Blake3 相比 Blake2b 在抗碰撞性和密钥派生安全性上有所提升。然而 Android 端拥有独立的双层加密体系(ECDH 密钥交换 + 自定义 AES-GCM + zlib 压缩),在设备指纹上报环节提供了额外的安全层。
综合评估:Android 端在设备指纹保护上更为复杂和多层,iOS 端在签名哈希算法选择上更为现代。

| 算法名称 | 标准状态 | 平台 | 说明 |
|---|---|---|---|
| MurmurHash3-32 (标准) | 标准 | Android, iOS | Austin Appleby 原版实现,种子处理一致 |
| MurmurHash3-32 (Custom) | 魔改 | Android, iOS | 自定义变体,混淆多项式和位移操作与标准版不同 |
| MurmurHash3-x64-128 | 标准 | Android, iOS | 64 位平台版本,标准实现 |
| xxHash-32 | 标准 | Android, iOS | Yann Collet 的 xxHash 标准实现 |
| xxHash-64 | 标准 | Android, iOS | 64 位版本标准实现 |
| SpookyHash-32 | 标准 | Android, iOS | Bob Jenkins 的 SpookyHash 32 位输出 |
| SpookyHash-128 Short | 标准 | iOS | SpookyHash 128 位截断输出 |
| SipHash-24 | 标准 | Android, iOS | Aumasson-Bernstein 标准实现,2-4 轮 |
| SipHash-24-64 | 标准 | Android, iOS | 64 位输出版本 |
| Super Fast Hash | 标准 | Android, iOS | Paul Hsieh 的哈希算法标准实现 |
| FastHash-64 | 标准 | Android, iOS | 64 位快速哈希标准实现 |
| Hash Mul EOR | 魔改 | Android, iOS | 自定义乘加异或混合哈希,非公开算法 |
| CityHash-64 | 标准 | Android Only | Google CityHash 标准实现,仅 Android 使用 |
| Blake2b | 标准 | Android Only | RFC 7693 标准实现 |
| Blake3 | 标准 | iOS Only | 标准实现,使用自定义 keyed hash 密钥 |
| 算法名称 | 标准状态 | 说明 |
|---|---|---|
| ChaCha20 | 标准 | RFC 8439 标准实现,nonce 和 counter 处理符合规范 |
| Salsa20 | 标准 | Bernstein 原始设计标准实现 |
| RC6 | 标准 | RSA AES 候选算法标准实现,密钥扩展和轮函数均标准 |
| Threefish-512 | 标准 | Skein 哈希家族配套的标准实现,tweak 处理规范 |
| AES-GCM (Android 指纹) | 魔改 | 计数器从 2 开始(标准为 1),密文额外 XOR ECDH 共享密钥,非标准实现 |
| 自定义 CRC32 (Android 指纹) | 魔改 | 多项式为 0xD5714649(标准为 0xEDB88320),非标准 CRC32 |
| 自定义 XOR 混淆 | 魔改 | 密钥字节序交换 + 轮转 XOR,自定义设计 |
| 组件 | 标准状态 | 说明 |
|---|---|---|
| 某皮 Base64 | 接近标准 | 使用标准 Base64 字符表,可能存在填充差异 |
| FlatBuffers (iOS) | 标准 | Google FlatBuffers 标准序列化格式 |
| 自定义二进制 (Android) | 魔改 | 自创的后向标记偏移机制,类似但不同于 FlatBuffers |
| ECDH 密钥交换 | 标准 | SECP256R1 曲线标准实现 |
| MT19937 (Android) | 魔改 | 自定义变体,输出函数和状态更新与标准版有差异 |
| MT19937 (iOS) | 接近标准 | StdMt19937 更接近标准实现,使用 next_u32 标准方法名 |
Custom MurmurHash3-32:与标准 MurmurHash3 相比,自定义变体在混淆阶段使用了不同的常数和位移量。标准版使用 0x5bd1e995 作为乘数,而自定义版本可能使用了不同的多项式。此外,最终化阶段(finalizer)的异或和移位组合也与标准版不同,这使得相同的输入会产生完全不同的哈希值。
Hash Mul EOR:这是一个完全自定义的哈希算法,结合了乘法、加法和异或操作。其核心逻辑是对输入字节序列进行迭代,每次迭代将累加器乘以一个固定常数,加上当前字节,然后进行位混合。这种设计与 Java 的 String.hashCode() 有相似之处,但位混合更为复杂。
自定义 AES-GCM:Android 指纹加密中的 AES-GCM 实现有三处非标准设计。第一,CTR 计数器起始值为 2 而非标准 GCM 的 1,这会改变整个密钥流。第二,在标准 GCM 的 GHASH 认证标签计算完成后,密文还会与 ECDH 共享密钥的派生字节进行逐字节 XOR,这是标准 GCM 不存在的操作。第三,认证标签的计算可能使用了从 native 库还原的自定义 GHASH 实现,其多项式乘法可能与标准 GF(2^128) 有差异。
自定义 CRC32:标准 CRC32 使用多项式 0xEDB88320( reflected 0x04C11DB7),而 某皮 指纹系统使用 0xD5714649。不同的多项式意味着完全不同的校验值,这使得标准库无法直接复用。
MT19937 变体:Android 端的 MT19937 实现在状态更新和输出生成上与标准版有差异。标准 MT19937 的 tempering 变换使用固定的位移和掩码,而自定义版本可能在某些位移量或掩码常数上做了修改,使得相同的种子产生不同的随机序列。
SAP 签名系统具有以下防护特性:
动态算法选择。每次请求从 9 种哈希函数中随机选出 3 种,并通过 Value1 隐藏选择结果。分析者必须完整还原所有 9 种算法的实现才能正确生成签名。其中包含 2 种魔改算法(Custom MurmurHash3、Hash Mul EOR),增加了使用标准库绕过检测的难度。
四重加密嵌套。Value4 的设备指纹经过四层保护:设备信息序列化、随机算法加密(四选一,均为标准强加密算法)、时间戳异或混淆、XOR 密钥异或。每一层都依赖前一步的计算结果,形成紧密的依赖链。
时间验证机制。签名中的时间戳与服务端时间进行校验,同时 x-sap-ri 中的时间偏移量与 clocks 数组中的单调时钟序列相互印证,使得重放攻击几乎不可能。
平台差异化。Android 和 iOS 使用不同的 URL 哈希乘数、部分不同的哈希算法和不同的设备信息序列化格式,同一请求在两个平台上的签名结果完全不同,防止了跨平台签名复用。
Android 端的设备指纹通过独立的加密通道上报,具有服务端公钥绑定、临时 ECDH 密钥交换、自定义 CRC32 校验等保护措施。自定义 AES-GCM 实现中计数器从 2 开始且密文额外 XOR 处理,增加了标准库复用的难度。魔改的 CRC32 多项式使得攻击者无法使用标准库计算正确的校验值。
iOS 端的设备指纹通过 FlatBuffers 序列化后直接加密嵌入 SAP 签名中,虽然没有独立的加密通道,但 FlatBuffers 的结构化特征和 XOR 关联字段设计使得伪造难度不低。特别是多个哈希字段之间的 XOR 关联关系,服务端可以通过验证这些关系来检测伪造的设备信息。
从逆向工程难度来看,某皮 的移动端反爬机制属于业界较高水平。以下是对各环节难度的评估:
签名算法还原:高难度。需要完整还原 9 种哈希算法(含 2 种魔改)、4 种加密算法(均为标准强加密)、MT19937 随机数生成器(Android 为魔改版)、自定义 Base64 编码等。其中魔改算法必须通过动态调试或静态分析逐字节验证,无法直接复用开源实现。
设备指纹伪造:中高难度。需要了解平台特有的设备信息格式,Android 端需要理解自定义二进制后向标记机制,iOS 端需要正确构造 FlatBuffers 结构。此外,iOS 端 XOR 关联字段的交叉验证增加了伪造的复杂度。
签名重放防护:高强度。时间戳验证 + 单调时钟序列 + 算法随机选择使得每次请求的签名都唯一。MT19937 的伪随机序列虽然可预测,但需要精确还原时间偏移量和种子派生逻辑。
跨平台攻击:高难度。两个平台的签名 key 计算、部分算法选择、设备信息格式和序列化方式完全不同。Android 的魔改算法变体与 iOS 的标准算法实现产生不同的中间值,使得跨平台签名生成几乎不可能。
某皮 的 Android 和 iOS 端反爬系统在架构上高度统一,都采用了多算法组合、动态选择、多层加密的设计思路。两个平台的主要差异体现在哈希算法的选择(Blake2b vs Blake3、CityHash vs SpookyHash)、设备信息的序列化方式(自定义二进制 vs FlatBuffers)、以及设备指纹的上报通道(独立加密 vs 内嵌签名)。
从算法标准性来看,核心的 4 种加密算法(ChaCha20、Salsa20、RC6、Threefish-512)均使用标准实现,保证了加密强度。但在哈希算法层面引入了 2 种魔改变体(Custom MurmurHash3、Hash Mul EOR),在指纹保护层面引入了自定义 AES-GCM 和 CRC32,这些魔改设计有效防止了攻击者使用标准库绕过检测。
从防护强度来看,两个平台都达到了较高的安全级别。Android 端凭借独立的设备指纹加密通道、更多版本的签名格式迭代和魔改算法变体,在纵深防御方面略胜一筹。iOS 端则凭借更现代的哈希算法(Blake3)、结构化的 FlatBuffers 序列化和更接近标准的 MT19937 实现,在算法先进性和可维护性上有所优势。
对于安全研究人员而言,这套系统的设计思路值得参考:通过多算法组合增加逆向成本,通过动态选择防止静态分析,通过平台差异化防止跨平台攻击,通过时间验证防止重放攻击,通过魔改算法防止标准库复用。这些策略的组合运用构成了一个相对完整的移动端 API 防护体系。


本文作者:回锅炒辣椒
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!