Files
revkit/docs/spec/unknown_values_report.md
T
tkgstratorandClaude Opus 4.6 69603736d7 docs(spec): appboot sign key は Keychain キャッシュと判明 — ブロッカー1つ解消
- 全アプリデータ削除後のクリーン起動で 38b2030d / 8887ddf1 が出現しないことを確認
- これらは前回セッションの Phase 2 sign key が Keychain にキャッシュされたもの
- フレッシュ appboot では sign_key_1 (Phase 3 KDF) で署名すれば良い
- 残るブロッカーは apphmac の導出ロジックのみ
- Tweak に HKDF (AppleWebCrypto::HKDF @ +0x11900) フックを追加

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 10:16:33 +00:00

14 KiB
Raw Blame History

Unknown Values Investigation Report

Date: 2026-04-09 Updated: 2026-04-09

appboot → MSL 認証の Python 実装に必要な全値の調査結果。 各値について 固定/可変、再利用可否、Python 再現可否 を明記する。


1. バイナリ固定値 (アプリバージョン単位で不変)

これらはすべて Netflix iOS バイナリにハードコードされており、アプリが更新されない限り不変。 Python 定数として埋め込み済みで、追加調査は不要。

kAppBootKey — RSA-4096 SPKI/DER (550B)

項目 値
バイナリ NFWebCrypto.framework
オフセット 0x0020cd31 (__TEXT.__cstring)
格納形式 Base64 エンコード (736 chars)
用途 appboot レスポンスの RSASSA-PKCS1-v1_5 署名検証
ハンドル "ABKP"
Python 定数 constants.IOS_APPBOOT_RSA_KEY_DER
固定/可変 固定 — バイナリ埋め込み
再利用可否 可 — アプリバージョンが同じなら不変
Python 再現 可 — 定数として利用

kAppBootEccKey — ECDSA P-256 SPKI/DER (91B)

項目 値
バイナリ NFWebCrypto.framework
オフセット 0x0020d10c (__TEXT.__cstring)
格納形式 Base64 エンコード (124 chars)
用途 appboot レスポンスの ECDSA 署名検証
ハンドル "ABECCKP"
Python 定数 constants.IOS_APPBOOT_ECC_KEY_DER
固定/可変 固定 — バイナリ埋め込み
再利用可否 可
Python 再現 可

kSharkBootKey (prod) — ECDSA P-256 SPKI/DER (91B)

項目 値
バイナリ NFWebCrypto.framework
オフセット 0x0020d08f
用途 Shark boot 署名検証
Python 定数 constants.IOS_SHARKBOOT_KEY_DER
固定/可変 固定
再利用可否 可
Python 再現 可

DH Prime p — 1024-bit (128B)

項目 値
バイナリ MslClient.framework (NFWebCrypto ではない)
オフセット 0x001265a0 (__TEXT.__const)
ロード関数 IosAdhKeyx::dhKeyGen @ vaddr 0x00079d20
Generator g 5 (vaddr 0x00079d98)
Python 定数 constants.IOS_DH_P, constants.IOS_DH_G
固定/可変 固定 — バイナリ埋め込み
再利用可否 可
Python 再現 可 — cryptography ライブラリで DH 計算
9694e9d8 d93a5ac7 4c509b4b bce85e92
132cd19c ce477d1a 7e47d527 d9ec2915
15f0b8b3 e1eaed50 06e1b1b9 1ea25b91
a01b10e2 e834b8d6 60b2e321 ad644ce1
a83b328d 9014ee7e 16f1e44f fe89579a
c3ee47d6 68b6b766 87c2fe90 a35b5e60
28fd04ef ea882373 ecf60ba2 f637e4cd
aa1b6089 d6c0b561 a8e520e7 96de27df

PSK / KDF Nonce

値 オフセット サイズ
PSK: 027617984f6227539a630b897c017d69 NFWebCrypto @ 0x1ac8f5 16B
Nonce: 809f82a7addf548d3ea9dd067ff9bb91 NFWebCrypto @ 0x1ac905 16B
固定/可変 固定 — バイナリ埋め込み
再利用可否 可
Python 再現 可 — constants.IOS_KDF_PSK / IOS_KDF_NONCE

Device Header (128B CBOR)

項目 値
用途 key 33.6 scheme_data の先頭 128 バイト
Python 定数 constants.IOS_KEY336_DEVICE_HEADER
固定/可変 固定 — iPhone デバイスタイプ共通 (165/180 サンプルで一致)
再利用可否 可
Python 再現 可

AppID

項目 値
値 a2becfec-b286-535c-b884-903a384caee6
固定/可変 固定の可能性が高い — 全キャプチャで同一値。UUIDv5 形式 (deterministic)
再利用可否 可 (要確認: アプリバージョン更新で変わる可能性あり)
Python 再現 可 — 定数として埋め込み
追加調査 複数バージョンで値が同一か確認

AppKeyVersion

項目 値
値 1
固定/可変 固定の可能性が高い — 全キャプチャで同一値
再利用可否 可
Python 再現 可
追加調査 サーバー側で拒否される場合はバージョン依存の可能性あり

2. デバイス固有値 (デバイス依存だが再現可能)

Full ESN

項目 値
キャプチャ値 NFAPPL-02-IPHONE9=1-AD0455EF27D3A7B8F0872932FD9837874AF3E6F90157195BD22A8063FEB0B79E
構造 NFAPPL-02-{MODEL}={VARIANT}-{64HEXCHARS}
固定/可変 デバイス固有・固定 — 同一デバイスでは不変。デバイスが変われば変わる
再利用可否 可 — 同一デバイスに対しては永続的に有効
Python 再現 不可 — ESN 生成ロジックは未解明。キャプチャ値をパラメータとして渡す
追加調査 ESN suffix (64 hex chars) の導出元。Keychain/デバイス識別子から生成?

MGK (Model Group Key): enc_key_0 + sign_key_0

項目 値
enc_key_0 0817065e29e6d1c8668473af9e13b3c2 (16B)
sign_key_0 91f752f76d7ab4c2dc6e5b3ec1c0e5a16864421fe449be5457459602e298ebc1 (32B)
導出元 SHA384(ESN) → 3× TFIT-WB-AES-128-ECB → enc_key_0 (16B) + sign_key_0 (32B)
固定/可変 ESN 依存・固定 — 同一 ESN からは常に同一の MGK が導出される
再利用可否 可 — ESN が同じなら毎回同じ値
Python 再現 可 — tools/emulate_tfit.py (Unicorn ARM64 エミュレーション)
追加調査 不要 — Phase 3 KDF のライブ HMAC ログと Python 実装の出力が完全一致を確認済み

3. ランタイム可変値 (要調査)

devicetoken (216B, protobuf)

項目 値
キャプチャ hex 0608a1b7ebdc0312bc01...0a0d00ef (216B)
取得元 Nbp.framework → NRM (Netflix Registration Management) サービスへ HTTP リクエスト
取得関数 -[MslRegistration getDeviceTokensWithCallback:] @ Nbp 0x0005d5c0
protobuf 構造 field 1 (varint): ID, field 2 (188B): opaque payload, field 4: nested proto
固定/可変 可変 — NRM サービスから取得。241 セッション中 41 種のユニーク値を観測
再利用可否 不明 — 有効期限がある可能性。同一セッション内では安定
Python 再現 不可 — NRM サービスとの通信プロトコルが未解明
追加調査が必要 ★ 有効期限の有無。一度取得した値がどのくらい再利用可能か。NRM API の仕様

apphmac (32B, HMAC-SHA256)

項目 値
固定/可変 毎回可変 — appboot リクエストごとに異なる
再利用可否 不可
Python 再現 不可 — 導出ロジック (鍵 + 入力) が未解明
追加調査が必要 ★★ 最大のブロッカー。以下を解明する必要がある:

静的解析の結果: NFWebCrypto の全 6 HMAC call site を静的解析した結果、すべてランタイム導出の鍵を使用。 バイナリに固定の HMAC 鍵は存在しない。

Call site 関数 鍵ソース
0x000101a0 nflxDhDerive SHA384(DH private key) (48B)
0x0000e640 hmacSign AppleNativeKey::getBytes()
0x00011990 HKDF-Extract caller 引数
0x000119b8 HKDF-Expand 前段の PRK
0x0001aa4c HMAC wrapper (SHA256) caller 引数
0x0001aac8 HMAC wrapper (SHA384) caller 引数

Tweak キャプチャの結果: IosMGKAuthData コンストラクタ (MslClient @ base+0xd45c) が発火しなかった。 apphmac は appboot blob (8549B) の暗号化された本体部分に埋め込まれており、 HMAC フックからは個別に特定できなかった。

調査方針:

  1. IosMGKAuthData コンストラクタのオフセットが正しいか再検証 (バイナリバージョン差異)
  2. FpsMgkAppIdAuthData::getAuthData() @ 0x000284bc をフックして apphmac を直接キャプチャ
  3. apphmac が HMAC(PSK, devicetoken) かどうかをテスト (Frida で入力 216B の HMAC コールを監視)

appboot sign key (32B) → 解決済み: Keychain キャッシュ

項目 値
キャプチャ値 38b2030dd55e3367290213ca0d16ee079524ccd24fb7221a52145fb6de016fd8
用途 appboot リクエスト全体 (8549B) の HMAC-SHA256 署名
固定/可変 可変 (導出値) → 前回セッションの Phase 2 sign key (Keychain キャッシュ)
再利用可否 初回 appboot には不要
Python 再現 不要 — フレッシュ appboot では sign_key_1 で署名する
追加調査 不要

2026-04-09 解決: Netflix アプリの全データ (Library/, Documents/, tmp/) を削除してクリーンな初回起動を キャプチャした結果、38b2030d も 8887ddf1 も出現しなかった。 これらは前回の appboot で Phase 2 KDF から導出されたセッション鍵が iOS Keychain に キャッシュされていた値に過ぎないことが確定。

フレッシュ appboot (初回起動 / キャッシュなし) では:

  1. Phase 3 KDF → sign_key_1 で appboot リクエストに署名
  2. DH 鍵交換 → Phase 2 KDF → 新セッション鍵を導出
  3. 新セッション鍵を Keychain にキャッシュ
  4. 以降のリクエストはキャッシュされたセッション鍵で署名

4. appboot blob の構造 (8549B)

Tweak (NetflixEntityAuth) で HMAC 署名対象として 8549B の完全な blob をキャプチャ。

[0:20]    ESN prefix (ASCII: "NFAPPL-02-IPHONE9=1-")
[20]      NULL terminator
[21:28]   Header/flags (00 00 01 00 00 00 00)
[28:8210] Encrypted body (8182B — 暗号化された entity_auth + key_exchange)
[8210:8212] Length prefix (73 15)
[8212:8296] Full ESN (ASCII, 84 chars)
[8296:8332] AppID UUID (ASCII, 36 chars)
[8332]    AppKeyVersion (ASCII "1")
[8333:8549] DeviceToken protobuf (216B)

暗号化本体 (8182B) には entity_auth_data の apphmac、key_exchange の DH 公開鍵、 key 33.6 scheme_data 等が含まれるが、暗号化されているため直接読めない。

保存先: raws/ios/captures/appboot_blob.bin


5. HMAC 署名チェーン (ライブキャプチャ)

appboot 時の HMAC 呼び出し順序と使用鍵:

順序 鍵 データ 用途
1-6 PSK / 前段出力 / Nonce MGK (48B) の各パーツ Phase 3 KDF (6 段チェーン)
7-9 sign_key_1 (d45443fa...) 76B / 92B / 76B チャンク key exchange データ署名
10 session sign key (8887ddf1...) 389B CBOR MSL ヘッダー署名
11 appboot sign key (38b2030d...) 8549B appboot blob appboot リクエスト署名
12+ session sign key (8887ddf1...) CBOR メッセージ各種 通常の MSL 通信
  • Phase 3 KDF (順序 1-6) は Python 実装と完全一致を確認済み
  • session sign key (8887ddf1...) は Phase 2 KDF (DH 共有秘密) から導出されたもの
  • appboot sign key (38b2030d...) の導出元が最大の未解明事項

6. 全値の分類まとめ

Python 単体で再現可能 (追加調査不要)

値 固定/可変 Python 定数/関数
kAppBootKey (RSA-4096) バイナリ固定 constants.IOS_APPBOOT_RSA_KEY_DER
kAppBootEccKey (P-256) バイナリ固定 constants.IOS_APPBOOT_ECC_KEY_DER
kSharkBootKey (P-256) バイナリ固定 constants.IOS_SHARKBOOT_KEY_DER
DH prime p / g バイナリ固定 constants.IOS_DH_P / IOS_DH_G
PSK / Nonce バイナリ固定 constants.IOS_KDF_PSK / IOS_KDF_NONCE
Device header (128B) バイナリ固定 constants.IOS_KEY336_DEVICE_HEADER
TFIT tables バイナリ固定 tools/emulate_tfit.py
MGK (enc_key_0 + sign_key_0) ESN 依存・決定的 emulate_tfit.py で導出
Phase 3 KDF 全出力 MGK 依存・決定的 crypto.kdf_renew()
DH 鍵生成/共有秘密 毎回ランダム crypto.generate_dh_keypair()
Phase 2 KDF 全出力 DH 依存・決定的 crypto.derive_initial_session_keys()
AppID 固定の可能性高 定数として埋め込み
AppKeyVersion 固定の可能性高 定数として埋め込み

デバイスから1回取得すれば再利用可能

値 固定/可変 取得方法 再利用条件
Full ESN デバイス固有・固定 Tweak/Frida でキャプチャ 同一デバイスなら永続

要追加調査 (Python 実装のブロッカー)

値 固定/可変 再利用 調査優先度 調査内容
apphmac (32B) 毎回可変 不可 ★★ 最高 導出ロジック (鍵 + 入力) の解明
devicetoken (216B) 可変 不明 ★ 高 有効期限。NRM API 仕様。再利用可能期間
appboot sign key 解決済み — — Keychain キャッシュ。初回は sign_key_1 で代替

結論: apphmac の導出ロジックが残る唯一の最大ブロッカー。 appboot sign key は Keychain キャッシュと判明し、フレッシュ appboot では sign_key_1 (Phase 3 KDF 出力) で署名すればよい。 devicetoken は有効期限が不明だが、キャプチャ値を渡すことで当面は動作する可能性がある。