把 Netflix MSL 拆成字节 - 一次协议逆向的路线、踩坑与经验

读完本文,你将获得: 一个常被忽略的问题的答案:既然已经有 HTTPS,Netflix 为什么还要在里面再套一层自研的 MSL 加密信道? MSL 消息的字节级结构:header envelope、encrypted envelope、payload chunk 三层是怎么嵌套的,encrypt-then-MAC 到底对哪段 bytes 签名 CBOR integer key 编码的真实字节,以及为什么 bytes 写成 text、headerdata 内联成 map 会让服务端直接 502 一套不依赖“协议很复杂所以安全”的评估框架:MSL 能保护什么、哪些属性取决于部署配置,以及攻击者会转向哪些边界 〇、摘要 本文记录笔者对 Netflix Message Security Layer (MSL) 的一次协议逆向。 和前几篇 Widevine / FairPlay 文章不同,这次的目标不是攻破某个白盒 AES,而是把一个真实商业客户端的应用层加密协议,拆成能对着字节解释、能复现、能被服务端接受或拒绝的结构。产出是两个最小实现: nfmsl.py:单文件 Python 研究客户端,每一层中间结构都能打印,用来做字段 diff 和错误二分; Android MslClient:用真机 MediaDrm / CryptoSession 复现 Android 侧的 MSL 加密签名链路。 真正的突破点不是拿到某个密钥,而是把下面这些东西对齐到服务端能接受的 wire format: MSL 消息的三层信封结构,以及 encrypt-then-MAC 中「签名签的是密文而非明文」这一点; CBOR integer key 编码里 bytes/text、内联 map/加密 envelope 的字节差异; MasterToken 与 UserIdToken 通过 mtserialnumber 建立的字段级绑定; ASYMMETRIC_WRAPPED 与 WIDEVINE 两类 Key Exchange 的密钥归属差异; licensedManifest 把 Manifest 与 Widevine license challenge 合并进同一个 payload 的结构。 本文同时区分三个容易混淆的对象:Netflix 开源 MSL 框架的能力、本文观测到的 Netflix 客户端协议实例、Netflix 当前生产环境的完整安全策略。前两者可以由公开文档和实验字节支持;第三者还包含服务端风控、密钥托管、撤销策略和设备分级,不能仅凭客户端逆向完整证明。 ...

August 15, 2026 · 18 min · 3744 words · +5

当 Hook 失效的那一刻:SVC 指令级系统调用保护的攻防

读完本文,你将获得: 理解 ARM64 SVC 指令如何绕过所有 libc 层 hook,以及为什么 Frida 的 Interceptor.attach 对它束手无策 掌握 SVC 在 Android 安全对抗中的五种核心用法:反 ptrace、反 Frida、完整性校验、线程检测、seccomp-BPF 获得一套从"进程莫名崩溃"到"定位 SVC 指令并绕过"的完整排查方法论 对比六种绕过方案(Frida Stalker / seccomp / 内核模块 / eBPF / binary patch / 仿真)的工程权衡 〇、摘要 本文系统分析了 Android 应用中基于 SVC(Supervisor Call)指令的内联系统调用保护技术。笔者在对多个商业级加固方案的逆向分析中,反复遭遇 “hook 了 libc 但保护照样触发” 的困境,最终追溯到 SVC 这个 ARM 指令集的原子操作。 核心发现与贡献: 五种 SVC 对抗模式梳理:从实际样本中提炼出 anti-ptrace、anti-Frida(/proc/self/maps 扫描)、文件完整性校验、线程枚举检测、seccomp-BPF 五种典型用法 🧑‍🔬 六种绕过方案的工程对比:Frida Stalker、seccomp-BPF 拦截、内核模块、eBPF、binary patch、Unicorn 仿真,各有适用场景和代价 🧑‍🔬🔬 完整实战案例:从"Frida attach 就崩溃"到定位 3 处 SVC 指令并逐一绕过的全过程,包含 4 次失败尝试 🔬 SVC 保护的真实边界分析:SVC 不是银弹——它保护的是调用路径而非调用逻辑,一旦攻击者切换到内核视角,保护立即失效 🧑‍🔬 防御建议:从防御者角度给出 SVC 保护与 OLLVM/VMP/服务端验证组合的分层策略 🧑‍🔬 Research Evidence Environment Item Detail Device Pixel 7 (panther), rooted (Magisk 27.0) OS Android 14 (UP1A.231105.001) Target 某商业加固 SO(arm64-v8a, ~2.1 MB),具体应用脱敏处理 Architecture arm64-v8a (AArch64) IDA Pro 9.0 SP1 Ghidra 11.1.2 Frida 16.5.9 (server) + 16.5.2 (client) strace Android NDK r26d bundled seccomp tools libseccomp 2.5.5, seccomp-tools (Ruby gem) Kernel 5.15.148-android14 (custom build with ftrace enabled) Hypothesis H1: 应用检测到调试器是通过 libc 的 ptrace() 调用实现的,hook libc ptrace 即可绕过 H2: Frida attach 后崩溃是因为 /proc/self/maps 中出现了 frida-agent 的映射,应用通过 libc open/read 读取 H3: SVC 指令的 syscall number 硬编码在 .text 段中,可通过模式搜索 D4000001(SVC #0 编码)批量定位 H4: seccomp-BPF 可以拦截所有 SVC 调用,包括从 .text 段直接发出的内联调用 H5: binary patch 将 SVC 指令替换为 NOP 是最简单有效的绕过方式 Experiments ID 实验 验证假设 结果 关键证据 E01 hook libc ptrace 返回 0 H1 ✗ FAIL hook 生效但进程仍然检测到调试器并 exit E02 hook libc openat/read 过滤 maps 读取 H2 ✗ FAIL /proc/self/maps 从未出现在 hook 日志中,但进程仍然崩溃 E03 strace 观察实际 syscall H1, H2 ✓ PASS 发现 ptrace(PTRACE_TRACEME) 和 openat(/proc/self/maps) 直接出现在 strace 输出中,未经 libc E04 IDA 搜索 SVC #0 指令编码 H3 ✓ PASS .text 段找到 17 处 SVC #0(01 00 00 D4),其中 3 处与反调试直接相关 E05 seccomp-BPF 拦截 SVC ptrace H4 ✓ PASS seccomp filter 成功拦截 __NR_ptrace(117),进程不再崩溃 E06 NOP patch SVC 指令 H5 ⚠️ PARTIAL 反 ptrace 绕过成功,但 SO 完整性校验(也是 SVC 实现)检测到 patch 后 abort E07 Frida Stalker 动态替换 SVC 返回值 - ✓ PASS 成功绕过全部 3 处 SVC 保护,进程正常运行 一、路线总览 这一节给出整个分析的宏观路线图。如果你已经熟悉 SVC 的基本概念,可以直接跳到 §四 看实战。 ...

August 8, 2026 · 28 min · 5806 words · +5

当 sign() 函数变成了两万行伪代码

读完本文,你将获得: 理解 Android Native VMP 保护的三代架构演进:从固定 opcode 到状态依赖调度 掌握一套识别 dispatcher、枚举 handler、恢复 opcode 语义的工程方法 经历一个 VMP 保护签名函数的完整分析过程,包括五次失败和三次策略转向 看清 VMP 保护的真实安全边界:它提高了静态分析成本,但 unidbg/Unicorn 黑盒调用可以完全绕过 区分 VMP 与 OLLVM 的适用场景,理解"VMP 了就安全了"为什么是危险的误解 获得一组面向防御者的改进提案,从 P0 到 P3 〇、摘要 Android 生态里,VMP(Virtual Machine Protection / 代码虚拟化保护)是国内加固厂商和头部 App 保护关键 Native 逻辑的主要手段之一。它把原始 ARM/ARM64 指令编译为自定义字节码,由嵌入 SO 的解释器在运行时调度执行,使 IDA Pro 的反编译结果从"可读的 C 伪代码"退化为"两万行 switch-case 状态机"。 笔者在过去两年中分析过多个使用 VMP 保护签名算法的 Android App,积累了以下观察: VMP 的核心价值不是"防逆向",而是"防低成本逆向":它把分析门槛从"F5 一下就能读"提高到"需要恢复 VM 指令集才能理解语义",但不能阻止 unidbg/Unicorn 级别的黑盒调用。 三代 VMP 的区分不在"有没有 VM",而在 dispatcher 的状态依赖程度:一代用固定 handler table,二代用编码 opcode + 动态 dispatch,三代把 dispatch 本身变成状态机输出。 实战中击穿 VMP 保护签名函数的关键转向,往往不发生在 VM 内部:笔者多次在 I/O 边界、JNI 桥接层或 memory pattern 上找到突破口,而不是靠完整还原 VM 指令集。 VMP 与 OLLVM 是互补而非替代关系:OLLVM 保护控制流,VMP 保护指令语义,两者组合才能同时抵御 symbolic execution 和静态反编译。 “VMP 了就安全了"是当前最危险的工程误解:侧信道、I/O 边界、timing pattern 和黑盒模拟器四条路径都不需要理解 VM 内部语义。 本文是一篇原创逆向研究文章(Type A),记录笔者分析 Android Native VMP 的实战过程、失败路径和方法论积累。与本博客的 Chrome VMP 文章 不同,那篇讨论的是 DRM/Widevine 场景下的白盒密码学保护;本文聚焦 Android App 保护厂商(360加固、梆梆、爱加密等)和自研 VMP 实现,目标对象是签名算法、加密模块和反作弊逻辑,分析工具链以 IDA Pro + Frida + unidbg/Unicorn 为主。 ...

July 10, 2026 · 20 min · 4071 words · +5

五个函数,一条链 - Apple FairPlay DRM 的 Frida 逆向全记录

读完本文,你将获得: 掌握从 Java 层追踪到 Native 层的 Frida 动态插桩方法论(分组 hook + 二分定位) 理解 FairPlay DRM 在 Android 上的完整解密调用链:5 个函数、4 个阶段 学会识别 in-place 解密模式(输入输出共用 buffer)——这是 sample-AES 的典型特征 获得一套可复用的"7000+ 导出函数中精确定位目标"的逆向工程流程 〇、摘要 本文记录了笔者对 Apple Music for Android(v3.6.0-beta)FairPlay DRM 实现的完整逆向分析过程。目标是理解 Apple 如何在 Android 平台上保护 ALAC 无损音频,并找到从播放流中提取明文音频数据的技术路径。 核心发现: FairPlay 解密调用链还原:从 Java 层的 FootHillDecryptionKey 追踪到 Native 层的 5 个关键函数,完整还原了「初始化 → 密钥获取 → 解密上下文创建 → 逐样本解密」的四阶段生命周期 白盒 AES 入口定位:在 libandroidappmusic.so 的 7000+ 导出函数中,通过分组 hook + 二分法定位到核心解密函数 NfcRKVnxuKZy04KWbdFu***(混淆函数名),确认其 5 个参数的语义 in-place 解密确认:解密函数的输入和输出共用同一个 buffer 指针,明文直接覆盖密文——这是 sample-AES 的典型实现模式 流式 dump 验证:通过 Frida 拦截 N 函数的调用前后,将加密/解密 buffer 分别 dump 到文件,验证了解密前后数据大小一致,且解密后为裸 ALAC 样本序列 本系列文章分为三部分: ...

May 10, 2026 · 8 min · 1500 words · +5

驯服六头蛇:驾驭希腊诸神 - 抖音六神签名算法的 unidbg 逆向全记录

读完本文,你将获得: 掌握 unidbg 仿真 Android native SO 的完整工作流:从环境搭建到签名输出 学会应对 OLLVM + VM + JIT 三层防护的实战策略:不硬逆混淆,用仿真绕过 理解字节跳动 MetaSec 签名体系(六神)的架构设计和初始化依赖链 获得一套排查"仿真器崩溃"的系统方法:自毁处理器识别、环境变量门、配置字段逆向 〇、摘要 本文记录了对抖音(Douyin)v37.5.0 libmetasec_ml.so 的完整逆向工程过程,目标是通过 unidbg 仿真提取六神签名算法。笔者在两个完整周末内完成了以下突破: 三层防护突破:识别并绕过了 OLLVM 控制流平坦化 + 自定义 VM 字节码解释器 + 运行时 JIT 代码生成的三重保护 自毁处理器中和:定位并 patch 了 3 个自毁处理器(覆盖 ~71 个调用点),防止仿真器跳转到未映射地址导致崩溃 环境变量门发现:通过 JADX 反编译发现了隐藏的环境变量 28d7fdd567361198183fa7b8e=a7,这是 Phase 2 初始化的硬性前置条件 配置 JSON 逆向:还原了 17 字段的配置 JSON 格式,其中 sdkVersion 字段必须使用 Java 层静态字段值(v06.05.40-dy)而非 native 解密值,这是整个研究的核心突破点 六神签名完整提取:X-Gorgon、X-Khronos、X-Argus、X-Ladon、X-Helios、X-Medusa 全部成功生成 难度评估:9/10——笔者遇到的最复杂的商业 Android 保护方案之一。 研究证据概览 (Research Evidence) 实验环境 项目 配置 目标应用 抖音 (Douyin) v37.5.0 (com.ss.android.ugc.aweme) 目标库 libmetasec_ml.so (3.8 MB, ELF64 ARM64) 主机 Ubuntu 22.04 LTS, x86_64, Dual Intel Xeon E5-2673 v4 (80 threads), 96GB RAM 仿真框架 unidbg + Unicorn2 后端, Java 11 (Temurin) 反编译 JADX (DEX → Java), radare2 5.8.9 (SO 静态分析) 辅助工具 Capstone (JIT 反汇编), 自定义 Java hook (指令 trace + CAS 分析) 分析周期 2026-03-27 ~ 2026-03-29 (2 个完整周末) 假设清单 ID 假设 章节 结果 H1 RegisterNatives 注册在 Object 而非 MS 类上是故意的 JNI 混淆手法 §4.2 ✓ 确认 — JADX 追踪发现 MS → i2 → Object 超类遍历逻辑 H2 Phase 2 失败源于缺少某个环境前置条件 §4.5 ✓ 确认 — JADX 发现 Os.setenv("28d7fdd567361198183fa7b8e", "a7") H3 时间戳相关的路径选择是刻意的 anti-analysis 设计 §4.6 ✓ 确认 — 两条路径功能等价,仅在调试环境中表现为非确定性 H4 配置 field[4] sdkVersion 应使用 native 解密出的 SDK 版本字符串 §4.7 ✗ 推翻 — 正确值来自 Java 静态字段 d4.a = "v06.05.40-dy" H5 JIT 中的 CAS (ldaxrh/stxrh) 循环是 anti-emulation 陷阱 §4.8 ✗ 推翻 — 单线程仿真器中 4 次迭代自然收敛 实验记录 ID 实验 步骤 结果 关键证据 E01 JNI 入口定位 RegisterNatives 日志 + JADX 追踪超类遍历 ✓ 入口 SO+0x271938, MS → i2 → Object E02 自毁处理器中和 反汇编 3 个 handler → 统一 patch adr x0, #0; ret ✓ ~71 个调用点全部中和 (0x266a38/0x266b0c/0x266be0) E03 Vtable 校验绕过 cbz → 无条件 b patch ✓ Phase 1 JNI_OnLoad 正常返回 E04 环境变量门发现 JADX 追踪 n3.a() → Os.setenv 调用 ✓ 28d7fdd567361198183fa7b8e=a7 E05 非确定性分发诊断 Phase 2 连续运行 5 次 ✗ 3/5 3 次返回 0 (成功), 2 次返回 -1 (失败) E06 时间戳固定 → 确定性分发 固定 currentTimeMillis + UUID.randomUUID ✓ Phase 2 连续 5 次 100% 返回 0 E07 配置 field[4] = native 解密值 sdkVersion = "v04.09.09.07-bugfix" ✗ VM 进入 ms_config.h:254 无限循环 E08 配置 field[4] = Java 静态字段值 sdkVersion = "v06.05.40-dy" (来自 d4.a) ✓ VM 执行 3173 条指令 → Boolean.TRUE E09 完整 17 字段配置验证 所有字段语义正确 → Phase 3 ✓ Phase 3 → true, Phase 4 → handle 0x12731000 E10 JIT CAS 循环收敛 观察 ldaxrh/stxrh 迭代次数 ✓ 4 次迭代, w20=w12=0x2000 时收敛 E11 六神签名生成 getFeatureHash(0x2000006) ✓ 6 个签名头全部输出 E12 设备注册端到端 TTEncrypt + 六神签名 → POST device_register ✓ HTTP 200, device_id_str + install_id_str 返回 E13 API 验证 Feed + Search API 请求携带六神签名 ✓ HTTP 200, 正常数据返回 一、路线总览 先用一张图说清楚整个初始化到签名输出的完整序列: ...

March 29, 2026 · 18 min · 3685 words · +5
CC BY-NC-SA 4.0 © 2026 +5 · Repost with attribution, non-commercial only
All research is for academic and security purposes only. No functional exploit code or keys provided. For takedown requests contact overkazaf@gmail.com.
本站总访问量 次  ·  访客数 人