23 KiB
Netflix MSL UserAuthData — Android 動的解析レポート
Netflix Android アプリ (com.netflix.mediaclient v9.57.0) の MSL 通信におけるユーザー認証データ (userauthdata) の生成・送信メカニズムを、Frida フックおよびキャプチャ済み暗号平文の CBOR 解析により明らかにした結果をまとめる。
1. 結論サマリー
| 項目 | 内容 |
|---|---|
| userauthdata の送信タイミング | 初回認証時のみ 1 回 (鍵交換 + エンティティ認証) |
| 使用される認証スキーム | NETFLIXID (netflixId + secureNetflixId) |
| 通常リクエストの認証方式 | UserIdToken (key 18) — サーバー発行のトークン |
| CBOR キー | userauthdata = key 47, useridtoken = key 18 |
| apiRequest 第5引数 | 常に null — userauthdata は MslControl 層で MSL MessageHeader に直接埋め込まれ、apiRequest パラメータとしては渡されない |
2. ユーザー認証フロー
%%{init:{'theme':'dark'}}%%
sequenceDiagram
participant App as Netflix App
participant MSL as MslControl
participant WV as WidevineCryptoContext
participant Server as Netflix Server
rect rgba(200,100,100,0.15)
Note over App,Server: 初回認証 (1回のみ)
App->>MSL: apiRequest(url, body, headers, userId, null)
Note over MSL: MslControl が MessageHeader を構築
MSL->>MSL: userauthdata を MessageHeader に埋め込み<br/>(CBOR key 47)
Note over MSL: scheme: NETFLIXID<br/>authdata: {netflixId, secureNetflixId}
MSL->>WV: encrypt(headerdata)
MSL->>Server: MSL Message (暗号化済み)
Server->>MSL: keyresponsedata + mastertoken<br/>+ UserIdToken
end
rect rgba(100,200,100,0.15)
Note over App,Server: 以降のリクエスト (348回観測)
App->>MSL: apiRequest(url, body, headers, userId, null)
Note over MSL: MslControl が MessageHeader を構築
MSL->>MSL: UserIdToken を MessageHeader に埋め込み<br/>(CBOR key 18)
MSL->>WV: encrypt(headerdata)
MSL->>Server: MSL Message (暗号化済み)
end
3. キャプチャデータによる証明
3.1 MSL MessageHeader の認証キー分布
353 件の暗号化前 MSL MessageHeader (Widevine encrypt 平文) を CBOR 解析した結果:
| 認証方式 | CBOR キー | 件数 | 割合 |
|---|---|---|---|
| UserIdToken のみ | key 18 | 348 | 98.6% |
| userauthdata のみ (NETFLIXID) | key 47 | 1 | 0.3% |
| どちらもなし (鍵交換 handshake) | — | 5 | 1.1% |
| 両方 | key 18 + 47 | 0 | 0% |
3.2 userauthdata が送信されたヘッダー (1件)
ファイル: logs/android_20260313/android14.prod.ftl.netflix.com/0052_msl.widevine.encrypt.json
MSL MessageHeader CBOR キー:
[17, 19, 20, 21, 22, 24, 36, 40, 41, 42, 47]
17: mastertoken (list)
19: renewable = false
20: sender = "" (ESN)
21: handshake = true ← 鍵交換ハンドシェイク
22: messageid = 2491352839960294
24: timestamp = 1773392548
40: peer = true
41: nonreplayable_id = 10
42: keyrequestdata (WIDEVINE)
47: userauthdata ← ★ ここにユーザー認証データ
3.3 userauthdata の構造 (CBOR key 47)
{
"30": "NETFLIXID",
"35": {
"56": "<netflixId>",
"60": "<secureNetflixId>"
}
}
| CBOR キー | フィールド名 | 値 |
|---|---|---|
30 |
scheme |
NETFLIXID |
35 |
authdata |
(dict) |
35.56 |
netflixId |
v=3&mac=AQEAEQABABQxZyPp7r91RNj4pmPTIatYcL3jjIUVbvQ.&dt=1773392547767 |
35.60 |
secureNetflixId |
v=3&ct=<暗号化トークン 672文字>&pg=ZEULH5S2GNGCRAABCSG6J2EGGA&ch=<HMAC> |
3.4 netflixId の構造
v=3&mac=AQEAEQABABQxZyPp7r91RNj4pmPTIatYcL3jjIUVbvQ.&dt=1773392547767
| パラメータ | 値 | 意味 |
|---|---|---|
v |
3 |
バージョン |
mac |
AQEAEQABABQxZyPp7r91RNj4pmPTIatYcL3jjIUVbvQ. |
HMAC 署名 (改ざん検知) |
dt |
1773392547767 |
デバイスタイムスタンプ (Unix ms) |
3.5 secureNetflixId の構造
v=3&ct=<暗号化トークン>&pg=ZEULH5S2GNGCRAABCSG6J2EGGA&ch=<HMAC>
| パラメータ | 値 | 意味 |
|---|---|---|
v |
3 |
バージョン |
ct |
BgjHlOvcAxLcA5Nne... (672 文字) |
暗号化された認証トークン (protobuf base64url) |
pg |
ZEULH5S2GNGCRAABCSG6J2EGGA |
プロファイル GUID |
ch |
AQEAEAABABTp79nN9l_2MuRhqTXl0-SjAqcm83QU8vw. |
チャネル HMAC |
4. UserIdToken の構造 (通常リクエスト)
初回認証後、サーバーから発行された UserIdToken が以降の全リクエストで使用される。
4.1 CBOR 構造 (key 18)
{
"15": "<tokendata (bytes, CBOR エンコード)>",
"16": "<signature (bytes, 44B HMAC)>"
}
4.2 tokendata の CBOR 構造
| CBOR キー | フィールド名 | 値 (キャプチャ例) |
|---|---|---|
29 |
userdata |
<bytes 217> (暗号化されたユーザーデータ) |
25 |
serialnumber |
5970945423176003 |
11 |
issuedate |
1773420422 (2026-03-13 16:47:02 UTC) |
12 |
mtserialnum |
8079134327147185 (紐づく MasterToken のシリアル) |
profileid |
profileid |
ZEULH5S2GNGCRAABCSG6J2EGGA |
13 |
expiration |
1774601222 (2026-03-27 08:47:02 UTC, 14日後) |
4.3 トークンライフサイクル
発行: 2026-03-13 16:47:02 UTC
有効期限: 2026-03-27 08:47:02 UTC
有効期間: 14日間
同一セッション内で全 348 リクエストが同じ UserIdToken (serialnumber: 5970945423176003) を使用。
5. 認証スキーム一覧
Netflix MSL は 5 つのユーザー認証スキームをサポート (iOS ドキュメントから):
| スキーム | クラス (Android) | 送信データ | 用途 |
|---|---|---|---|
NETFLIXID |
com.netflix.msl.userauth.NetflixIdAuthenticationData (ProGuard 難読化) |
netflixId, secureNetflixId | 本キャプチャで観測 — Cookie ベース認証 |
EMAIL_PASSWORD |
ProGuard 難読化 (ClassNotFound) | email, password | ログインフォーム認証 |
USER_ID_TOKEN |
com.netflix.msl.userauth.UserIdTokenAuthenticationData |
既存 UserIdToken | トークン再認証 |
SSO_TOKEN |
ProGuard 難読化 (ClassNotFound) | ssoToken | SSO 認証 |
SWITCH_PROFILE |
ProGuard 難読化 (ClassNotFound) | switchGUID, originalGUID | プロファイル切替 |
5.1 ProGuard 難読化の影響
| クラス | 状態 |
|---|---|
UserAuthenticationData (基底クラス) |
存在確認済み — メソッド a(2), b(2), c(2) に難読化 |
UserIdTokenAuthenticationData |
存在確認済み — コンストラクタフック成功 (ただし呼び出しなし) |
EmailPasswordAuthenticationData |
ClassNotFoundException |
NetflixIdAuthenticationData |
ClassNotFoundException |
SsoTokenAuthenticationData |
ClassNotFoundException |
SwitchProfileAuthenticationData |
ClassNotFoundException |
→ 4 つのサブクラスが ProGuard で難読化/インライン化されている。UserIdTokenAuthenticationData のみ元のクラス名が残存。
6. MSL CBOR 整数キーマッピング (固定値)
MSL プロトコルは JSON と CBOR の 2 つのエンコーディングをサポートする。CBOR エンコード時は、JSON の文字列キーがプロトコル仕様で定義された固定の整数キーに置換される。これはメッセージサイズ削減のための最適化であり、Netflix の MSL ライブラリ (MslEncoderFactory) にハードコードされている。
以下のマッピングは、353 件の暗号化前 MSL MessageHeader と 403 件の PayloadChunk の CBOR 解析、および iOS 版 MSL ドキュメント (msl_ios.md §3) の JSON フィールド名との照合により確定したものである。
6.1 MSL メッセージ外殻 (Header / MasterToken / EntityAuthData)
JSON 構造:
{
"headerdata": "<Base64 暗号文>",
"signature": "<Base64 HMAC-SHA256>",
"mastertoken": { "tokendata": "...", "signature": "..." }
}
| CBOR キー | JSON フィールド名 | 型 | 説明 | 観測値例 |
|---|---|---|---|---|
15 |
tokendata |
bytes | トークンデータ (CBOR エンコード、暗号化済み) | <bytes 153〜296> |
16 |
signature |
bytes | HMAC 署名 | <bytes 44> (HMAC-SHA256) |
注: key 15/16 は MasterToken, UserIdToken, keyrequestdata 等のトークン系構造体で共通使用される汎用キー。
6.2 MessageHeader (復号後の headerdata)
JSON 構造 (iOS 版 msl_ios.md §3.2):
{
"messageid": 12345,
"sender": "NFANDROID1-PRV-P-...",
"renewable": false,
"handshake": false,
"timestamp": 1773392401,
"capabilities": { ... },
"useridtoken": { "tokendata": "...", "signature": "..." },
"userauthdata": { "scheme": "NETFLIXID", "authdata": { ... } },
"keyrequestdata": [ ... ],
"nonreplayableid": 10
}
| CBOR キー | JSON フィールド名 | 型 | 説明 | 観測数 |
|---|---|---|---|---|
17 |
mastertoken |
list[dict{15,16}] | MasterToken (tokendata + signature) | 18 件 |
18 |
useridtoken |
dict{15,16} | UserIdToken (tokendata + signature) | 348 件 |
19 |
renewable |
bool | トークン更新可能フラグ | 353 件 (全ヘッダー) |
20 |
sender |
string | 送信者 ESN (空文字列 = デフォルト) | 353 件 |
21 |
handshake |
bool | ハンドシェイクメッセージフラグ | 353 件 |
22 |
messageid |
int | メッセージ一意 ID | 353 件 |
24 |
timestamp |
int | Unix タイムスタンプ (秒) | 353 件 |
36 |
capabilities |
dict | クライアント能力情報 | 353 件 |
40 |
peer |
bool | ピアツーピアフラグ | 353 件 |
41 |
nonreplayableid |
int | リプレイ防止 ID | 2 件 |
42 |
keyrequestdata |
list[dict{30,31}] | 鍵交換リクエストデータ | 3 件 |
47 |
userauthdata |
dict{30,35} | ユーザー認証データ | 1 件 |
6.3 capabilities (key 36 の内部)
| CBOR キー | JSON フィールド名 | 型 | 説明 | 観測値 |
|---|---|---|---|---|
37 |
compressionalgos |
list[string] | サポート圧縮アルゴリズム | [] (空) |
38 |
languages |
list[string] | サポート言語 | ["GZIP", "LZW"] |
39 |
encoders |
list[string] | サポートエンコーダー | ["CBOR"] |
maxpayloadchunksize |
maxpayloadchunksize |
int | 最大ペイロードチャンクサイズ | -1 (無制限) |
注:
maxpayloadchunksizeは文字列キーのまま。Netflix 独自拡張のため整数キーが未割当と推定。
6.4 keyrequestdata / keyresponsedata (key 42 の内部要素)
| CBOR キー | JSON フィールド名 | 型 | 説明 | 観測値 |
|---|---|---|---|---|
30 |
scheme |
string | 鍵交換スキーム | "WIDEVINE" |
31 |
keydata |
dict | 鍵データ | Widevine デバイス証明書 |
31.duid |
keydata.duid |
bytes | デバイス一意 ID (32B) | Widevine deviceUniqueId |
31.50 |
keydata.cdmsg |
bytes | CDM メッセージ (protobuf) | Widevine ライセンスチャレンジ (~2577B) |
6.5 userauthdata (key 47 の内部)
| CBOR キー | JSON フィールド名 | 型 | 説明 | 観測値 |
|---|---|---|---|---|
30 |
scheme |
string | 認証スキーム | "NETFLIXID" |
35 |
authdata |
dict | スキーム固有の認証データ | dict{56, 60} |
NETFLIXID スキームの authdata (key 35 の内部)
| CBOR キー | JSON フィールド名 | 型 | 説明 |
|---|---|---|---|
56 |
netflixid |
string | NetflixId Cookie (URL エンコード) |
60 |
securenetflixid |
string | SecureNetflixId Cookie (URL エンコード) |
6.6 MasterToken tokendata (key 17[].15 を CBOR デコード)
| CBOR キー | JSON フィールド名 | 型 | 説明 | 観測値例 |
|---|---|---|---|---|
25 |
serialnumber |
int | MasterToken シリアル番号 | 5970945423176003 |
26 |
renewable |
bool | 更新可能フラグ | true |
27 |
issuer |
string | 発行者 | "sf", "cad" |
28 |
identity |
bytes | エンティティ ID (暗号化済み) | <bytes 125〜226> |
43 |
sequencenumber |
int | シーケンス番号 | 8079134327147185 |
6.7 UserIdToken tokendata (key 18.15 を CBOR デコード)
| CBOR キー | JSON フィールド名 | 型 | 説明 | 観測値例 |
|---|---|---|---|---|
11 |
issuedate |
int | 発行日 (Unix タイムスタンプ秒) | 1773420422 |
12 |
mtserialnum |
int | 紐づく MasterToken のシリアル番号 | 8079134327147185 |
13 |
expiration |
int | 有効期限 (Unix タイムスタンプ秒) | 1774601222 |
25 |
serialnumber |
int | UserIdToken シリアル番号 | 5970945423176003 |
29 |
userdata |
bytes | 暗号化されたユーザーデータ | <bytes 217> |
profileid |
profileid |
string | プロファイル GUID | "ZEULH5S2GNGCRAABCSG6J2EGGA" |
注:
profileidは文字列キーのまま。Netflix 独自拡張フィールド。
6.8 PayloadChunk (暗号化前)
| CBOR キー | JSON フィールド名 | 型 | 説明 | 観測値例 |
|---|---|---|---|---|
14 |
sequencenumber |
int | チャンクシーケンス番号 | 1, 2, ... 8 |
22 |
messageid |
int | メッセージ ID (MessageHeader と一致) | 1170337128274032 |
44 |
compressionalgo |
string | 圧縮アルゴリズム | "GZIP" |
62 |
data |
bytes | ペイロードデータ (GZIP 圧縮) | <bytes 40〜3497> |
63 |
endofmsg |
bool | 最終チャンクフラグ | true / false |
6.9 全整数キー一覧 (ソート順)
キャプチャデータから観測された全 28 整数キーの完全一覧:
| CBOR キー | JSON フィールド名 | 使用コンテキスト |
|---|---|---|
11 |
issuedate |
UserIdToken.tokendata |
12 |
mtserialnum |
UserIdToken.tokendata |
13 |
expiration |
UserIdToken.tokendata |
14 |
sequencenumber |
PayloadChunk |
15 |
tokendata |
MasterToken, UserIdToken, keyrequestdata (共通) |
16 |
signature |
MasterToken, UserIdToken, keyrequestdata (共通) |
17 |
mastertoken |
MessageHeader |
18 |
useridtoken |
MessageHeader |
19 |
renewable |
MessageHeader |
20 |
sender |
MessageHeader |
21 |
handshake |
MessageHeader |
22 |
messageid |
MessageHeader, PayloadChunk |
24 |
timestamp |
MessageHeader |
25 |
serialnumber |
MasterToken.tokendata, UserIdToken.tokendata |
26 |
renewable |
MasterToken.tokendata |
27 |
issuer |
MasterToken.tokendata |
28 |
identity |
MasterToken.tokendata |
29 |
userdata |
UserIdToken.tokendata |
30 |
scheme |
keyrequestdata, userauthdata |
31 |
keydata |
keyrequestdata |
35 |
authdata |
userauthdata |
36 |
capabilities |
MessageHeader |
37 |
compressionalgos |
capabilities |
38 |
languages |
capabilities |
39 |
encoders |
capabilities |
40 |
peer |
MessageHeader |
41 |
nonreplayableid |
MessageHeader |
42 |
keyrequestdata |
MessageHeader |
43 |
sequencenumber |
MasterToken.tokendata |
44 |
compressionalgo |
PayloadChunk |
47 |
userauthdata |
MessageHeader |
50 |
cdmsg |
keyrequestdata.keydata (Widevine) |
56 |
netflixid |
userauthdata.authdata (NETFLIXID) |
60 |
securenetflixid |
userauthdata.authdata (NETFLIXID) |
62 |
data |
PayloadChunk |
63 |
endofmsg |
PayloadChunk |
6.10 文字列キー (整数マッピングなし)
| キー | 使用コンテキスト | 説明 |
|---|---|---|
maxpayloadchunksize |
capabilities | 最大ペイロードチャンクサイズ |
profileid |
UserIdToken.tokendata | プロファイル GUID |
duid |
keyrequestdata.keydata | デバイス一意 ID |
これらは Netflix 独自の拡張フィールドであり、MSL 標準の整数キーが割り当てられていないと推定される。
7. userauthdata の生成メカニズム
7.1 生成レイヤー
userauthdata は apiRequest のパラメータとしてではなく、MslControl 層で MSL MessageHeader に直接埋め込まれる:
Netflix App
↓ apiRequest(url, body, headers, userId=GUID, auth=null)
ApiHandlerImpl
↓ (auth は null のまま渡される)
MslControl
↓ MessageHeader を構築
↓ ← ここで userauthdata を設定
↓ (初回: NETFLIXID スキーム)
↓ (以降: UserIdToken を使用)
WidevineCryptoContext.encrypt(headerdata)
↓
暗号化された MSL メッセージ → サーバー送信
7.2 netflixId / secureNetflixId の生成元
これらの値は Android の Cookie ストレージ から取得される:
| Cookie 名 | 生成元 | 保存場所 |
|---|---|---|
netflixId (=NetflixId) |
サーバーが Set-Cookie で発行 | Android CookieManager / SharedPreferences |
secureNetflixId (=SecureNetflixId) |
サーバーが Set-Cookie で発行 (Secure 属性) | Android CookieManager / SharedPreferences |
ログイン成功時にサーバーからこれらの Cookie が発行され、MSL ユーザー認証に使用される。
7.3 RenewSSOToken との関係
キャプチャで RenewSSOToken GraphQL オペレーションが観測されている:
{
"operationName": "RenewSSOToken",
"variables": {
"ssoToken": "BgiHtuvcAxL4AWLUqSE7W14gsbHZBDGMle4StHvRnr70..."
}
}
これは MSL レベルの userauthdata とは別の、GraphQL レベルの SSO トークン更新。MSL 層では既に UserIdToken で認証されており、SSO トークン更新は MSL ペイロード内で行われる。
8. 認証フロー全体図
%%{init:{'theme':'dark'}}%%
sequenceDiagram
participant User as ユーザー
participant App as Netflix App
participant MSL as MslControl
participant Server as Netflix Server
rect rgba(255,200,200,0.15)
Note over User,Server: Phase 1: ログイン (本キャプチャ前)
User->>App: メールアドレス + パスワード入力
App->>Server: HTTPS POST /login
Server->>App: Set-Cookie: NetflixId, SecureNetflixId
Note over App: Cookie をローカルに保存
end
rect rgba(200,200,255,0.15)
Note over User,Server: Phase 2: MSL 初回認証 (キャプチャ済み, 1回)
App->>MSL: apiRequest(..., userId=GUID, auth=null)
MSL->>MSL: MessageHeader 構築
Note over MSL: key 42: keyrequestdata (WIDEVINE)<br/>key 47: userauthdata (NETFLIXID)<br/> → netflixId + secureNetflixId
MSL->>Server: 暗号化 MSL メッセージ
Server->>MSL: keyresponsedata + MasterToken<br/>+ UserIdToken (有効期間 14日)
Note over MSL: UserIdToken をメモリに保持
end
rect rgba(200,255,200,0.15)
Note over User,Server: Phase 3: 通常リクエスト (348回観測)
App->>MSL: apiRequest(..., userId=GUID, auth=null)
MSL->>MSL: MessageHeader 構築
Note over MSL: key 18: useridtoken<br/> → serialnumber + profileid + expiration
MSL->>Server: 暗号化 MSL メッセージ
Server->>MSL: MSL レスポンス
end
rect rgba(255,255,200,0.15)
Note over User,Server: Phase 4: トークン更新 (15回観測)
Note over MSL: MasterToken + keyrequestdata で<br/>トークンを更新 (key 17 + key 18)
MSL->>Server: 更新リクエスト
Server->>MSL: 新しい MasterToken
end
9. iOS 版との比較
| 観点 | Android | iOS |
|---|---|---|
| userauthdata スキーム | NETFLIXID (netflixId + secureNetflixId) |
NETFLIXID (同一) |
| CBOR キー | 整数キー (47, 18, etc.) | 整数キー (同一仕様) |
| apiRequest パラメータ | 第5引数 UserAuthenticationData auth = null |
makeUserAuthData() メソッドで生成 |
| userauthdata 設定レイヤー | MslControl (Java) | IosMslClient (C++) |
| 暗号化 | WidevineCryptoContext | AES-CBC (C++) |
| UserIdToken 有効期間 | 14 日 | 不明 |
| ProGuard 影響 | サブクラス 4/5 が難読化で ClassNotFound | N/A (C++ バイナリ) |
10. 解析に使用したコード
10.1 CBOR デコードスクリプト
import base64, json, cbor2
from urllib.parse import unquote
# Widevine encrypt 平文から MSL MessageHeader を解析
with open("logs/android_20260313/android14.prod.ftl.netflix.com/0052_msl.widevine.encrypt.json") as f:
data = json.load(f)
raw = base64.b64decode(data["plaintext_b64"])
header = cbor2.loads(raw)
# userauthdata (key 47) を取得
if 47 in header:
uad = header[47]
scheme = uad[30] # "NETFLIXID"
authdata = uad[35]
netflix_id = unquote(authdata[56])
secure_netflix_id = unquote(authdata[60])
print(f"scheme: {scheme}")
print(f"netflixId: {netflix_id}")
print(f"secureNetflixId: {secure_netflix_id}")
# UserIdToken (key 18) を取得
if 18 in header:
uit = header[18]
tokendata = cbor2.loads(uit[15])
print(f"profileid: {tokendata.get('profileid')}")
print(f"serialnumber: {tokendata.get(25)}")
print(f"expiration: {tokendata.get(13)}")
10.2 Frida フックによるキャプチャ
hook_netflix_android.js に以下のフックを実装:
| フック対象 | 目的 | 結果 |
|---|---|---|
ApiHandlerImpl.apiRequest 第5引数 |
UserAuthenticationData オブジェクト取得 | 常に null (MslControl 層で設定されるため) |
UserIdTokenAuthenticationData.$init |
コンストラクタ監視 | フック成功、呼び出しなし (トークンはサーバーから受信) |
EmailPasswordAuthenticationData.$init |
コンストラクタ監視 | ClassNotFoundException (ProGuard) |
NetflixIdAuthenticationData.$init |
コンストラクタ監視 | ClassNotFoundException (ProGuard) |
SsoTokenAuthenticationData.$init |
コンストラクタ監視 | ClassNotFoundException (ProGuard) |
SwitchProfileAuthenticationData.$init |
コンストラクタ監視 | ClassNotFoundException (ProGuard) |
UserAuthenticationData.getScheme |
スキーム名取得 | メソッド名が難読化 (ProGuard) |
WidevineCryptoContext.encrypt |
暗号化前平文取得 | 成功 — CBOR 解析で userauthdata 発見 |
11. まとめ
-
userauthdataは MSL MessageHeader の CBOR key 47 として暗号化前に埋め込まれる。apiRequestのパラメータ(第5引数)としては渡されず、MslControl層が Cookie ストレージからnetflixId/secureNetflixIdを読み取って設定する。 -
NETFLIXID スキームが使用され、
netflixId(HMAC 付きデバイストークン) とsecureNetflixId(暗号化認証トークン + プロファイル GUID) の 2 つの値で構成される。 -
初回の MSL ハンドシェイク時に 1 回だけ送信される。サーバーは応答として
UserIdToken(有効期間 14 日) を発行し、以降の全リクエストはこのUserIdToken(key 18) で認証される。 -
353 件の MSL ヘッダーのうち userauthdata を含むのは 1 件のみ (0.3%)。残りの 98.6% は
UserIdTokenによる認証。 -
ProGuard 難読化により
NetflixIdAuthenticationData等のサブクラスは直接フックできないが、Widevine encrypt の平文データを CBOR 解析することで userauthdata の完全な内容を取得できた。