Files
revkit/docs/spec/msl_crypto_flow.md
T
tkgstratorandClaude Opus 4.6 d91af8380b docs: Netflix仕様書の再構成とiOS解析ドキュメント追加
- 旧ドキュメント (docs/netflix/, docs/instructions/ 等) を削除
- docs/spec/: appboot ピンニング解析、iOS再生フロー、
  MSL CBOR鍵交換解析、キャプチャ復号状況、暗号化フロー図を追加
- docs/netflix_client_spec.md, platform_flow_comparison.md を追加
- iOS認証・マニフェスト・ライセンス取得フロー詳細を追加

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 02:06:14 +00:00

115 lines
5.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Netflix iOS MSL 暗号化フロー解析
キャプチャ日: 2026-04-06
Tweak: NetflixSSLBypass (EVP_CipherInit_ex / EVP_CipherUpdate フック)
## 発見事項
### EVP_Cipher で処理されているもの
Tweak の EVP フックで 264 回の `EVP_CipherInit` と 457 回の `EVP_CipherUpdate` をキャプチャした結果、
**EVP_Cipher は MSL ペイロードの暗号化/復号には使われていない**ことが判明。
EVP_Cipher で処理されているのは以下の3種類のみ:
1. **AES セルフテスト (KAT)**: `key=000102030405060708090a0b0c0d0e0f`, `enc=encrypt`, 48 bytes × 40 回
2. **TFIT ホワイトボックス鍵導出チェーン**: 100以上のユニークな鍵で各 48 bytes を暗号化。出力が次の鍵になるチェーン構造
3. **AES-ECB テスト**: `key=fe70779b...`, 16 bytes × 60 回 (繰り返しパターン)
いずれも **固定サイズ (16 or 48 bytes)** のデータのみ。数百〜数千バイトの MSL ペイロード暗号化は含まれない。
### MSL ペイロード暗号化の実体
Frida の既存キャプチャ (`raws/ios/20260404/capture.jsonl`) では `msl.aesCbcEncrypt` / `msl.aesCbcDecrypt` イベントが
486 / 772 回記録されている。これは MslClient.framework 内の C++ 関数:
```
netflix::msl::crypto::aesCbcEncryptDecrypt(EncryptOrDecrypt, vector<uint8_t>&, vector<uint8_t>&, vector<uint8_t>&, vector<uint8_t>&)
```
が呼ばれている。この関数は内部で `EVP_CipherInit_ex` → `EVP_CipherUpdate` → `EVP_CipherFinal_ex` を呼ぶが、
**ElleKit の MSHookFunction ではこのオフセットのフックに失敗している** (Frida の Interceptor は成功する)。
## 暗号鍵のフロー
```mermaid
graph TD
subgraph "Phase 0: 起動時 (dylib load)"
A[AES Self-Test<br/>KAT: key=000102...0f] -->|48B × 40回| A1[Known Answer Test 結果検証]
end
subgraph "Phase 1: TFIT ホワイトボックス鍵導出"
B[TFIT Seed Key<br/>Irdeto whitebox AES-128-ECB] -->|ECB encrypt 48B| B1[Derived Key 1]
B1 -->|output = next key| B2[Derived Key 2]
B2 -->|output = next key| B3[...]
B3 --> B4[Model Group Key<br/>MGK for ESN generation]
end
subgraph "Phase 2: appboot 鍵交換"
C[DH KeyGen<br/>NFWebCrypto::dhKeyGen] --> C1[Client DH Public Key<br/>464 bytes → CBOR key 33.6]
C1 -->|POST /appboot| C2[Server DH Response<br/>96 bytes ← CBOR key 33.6]
C2 --> C3[DH Shared Secret<br/>dhDerive]
C3 --> C4[HKDF]
C4 --> C5[MSL Session Keys<br/>enc_key: AES-128<br/>hmac_key: SHA-256]
end
subgraph "Phase 3: MSL ペイロード暗号化"
C5 -->|enc_key| D1["aesCbcEncrypt<br/>(MslClient C++)<br/>内部で EVP_CipherInit_ex<br/>→ EVP_CipherUpdate<br/>→ EVP_CipherFinal_ex"]
D1 --> D2[暗号化済みペイロード<br/>CBOR key 33.ciphertext]
D1 --> D3[appboot request payload]
D1 --> D4[manifest request]
D1 --> D5[pbo_license request]
D1 --> D6[logblob request]
D1 --> D7[graphql Cloud/NGP request]
end
subgraph "Phase 4: MSL ペイロード復号"
C5 -->|enc_key| E1["aesCbcDecrypt<br/>(MslClient C++)"]
E1 --> E2[appboot response payload]
E1 --> E3[manifest response<br/>CDN URLs, codec info]
E1 --> E4[pbo_license response<br/>FairPlay license]
E1 --> E5[logblob response]
E1 --> E6[graphql Cloud/NGP response]
end
subgraph "Phase 5: HMAC 検証"
C5 -->|hmac_key| F1["signHmacSha256<br/>(MslClient C++)"]
F1 --> F2[リクエスト署名]
C5 -->|hmac_key| F3["verifyHmacSha256<br/>(MslClient C++)"]
F3 --> F4[レスポンス検証]
end
style A fill:#ffa,stroke:#aa0
style B fill:#ffa,stroke:#aa0
style C5 fill:#f66,stroke:#900,color:#fff
style D1 fill:#6af,stroke:#06a
style E1 fill:#6af,stroke:#06a
```
## 鍵の取得元まとめ
| 鍵 | 取得元 | 用途 |
|----|--------|------|
| TFIT MGK seed | NFWebCrypto.framework にハードコード (per-device-type) | ESN 生成用 Model Group Key 導出 |
| DH 秘密鍵 | NFWebCrypto::dhKeyGen (ランタイム生成) | appboot 鍵交換 |
| kAppBootKey (RSA-4096) | NFWebCrypto.framework にハードコード | DH パラメータの暗号化 (サーバーへ送信) |
| kAppBootEccKey (ECDSA P-256) | NFWebCrypto.framework にハードコード | サーバーレスポンスの署名検証 |
| MSL enc_key (AES-128) | HKDF(DH shared secret) | MSL ペイロードの AES-128-CBC 暗号化/復号 |
| MSL hmac_key (SHA-256) | HKDF(DH shared secret) | MSL ペイロードの HMAC-SHA256 署名/検証 |
| AES self-test key | 固定値 `000102...0f` | OpenSSL KAT (Known Answer Test) |
## 現状の制限
| アプローチ | 状態 | 問題 |
|-----------|------|------|
| Tweak EVP_Cipher フック | ✓ 動作 | TFIT/KAT のみキャプチャ。MSL ペイロードは見えない |
| Tweak MSHookFunction (オフセット) | ✗ コールバック未発火 | ElleKit が MslClient 内部コードのフックに失敗 |
| Frida Interceptor (attach) | ✓ 動作 | 起動後 attach のため appboot に間に合わない |
| Frida enumerateSymbols | ✓ 動作 | アドレス取得可能だが attach タイミングの問題 |
## 次のステップ
1. **Tweak で EVP_CipherInit_ex の IV パラメータを監視** — MSL ペイロード暗号化時に IV が設定されるはずだが、EVP 内部のステートを追跡できていない可能性
2. **Frida + Tweak 併用** — Tweak で appboot 時の鍵導出をキャプチャし、Frida で後続の MSL ペイロードをキャプチャ
3. **ElleKit の代わりに fishhook / substrate を直接使う** — MSHookFunction が失敗する原因を調査