はじめに
Netflixで画面録画やスクリーンミラーリングを試みると、映像部分だけが真っ黒になる。「自分のPCなのになぜ?」という素朴な疑問を起点に、コンテンツ配信からディスプレイ出力までの技術的な仕組みを掘り下げていく。規約やライセンス契約の話ではなく、ハードウェアとソフトウェアが具体的に何をしているのかに焦点を当てる。
1. 多層防御の全体構成
Netflixのコンテンツ保護は、単一の技術ではなく複数のレイヤーが組み合わさって成立している。
DRM(デジタル著作権管理) が中核にある。プラットフォームごとに Widevine(Android / Chrome等)、FairPlay(Apple系)、PlayReady(Windows / Edge等)が使い分けられている。
暗号化ストリーム として、動画はサーバー側でAES暗号化された状態で配信される。復号鍵はDRMライセンスサーバーから端末に安全に渡され、復号処理はOS内部やハードウェアレベルの保護された領域で行われる。
ハードウェア保護パス が録画防止の要であり、復号からディスプレイ出力までの経路全体がハードウェアで保護される。復号済み映像データはGPUの保護メモリ領域にのみ存在し、OSの通常のフレームバッファには書き出されない。
HDCP(High-bandwidth Digital Content Protection) は外部ディスプレイ出力を保護する。HDMI/DisplayPort経由で映像を出力する際、送受信側がハンドシェイクを行い、非対応デバイスには映像を送らない。
OS APIによるソフトウェア保護 も補助的に機能する。Androidの FLAG_SECURE、iOSの画面キャプチャ検出API、macOS Safariでのスクリーンレコーディング検出などがある。
解像度による戦略的制限 として、ブラウザごとにDRMの保護レベルが異なり、ChromeではWidevine L3(ソフトウェアベース)で最大720p、EdgeではPlayReadyのハードウェア保護で4K再生が可能、というように保護が弱い環境では解像度を下げている。
2. 「自分のPCなのにアクセスできない」メモリ領域
「復号済み映像がGPUの保護メモリにある」と言われて、多くの技術者がまず抱く疑問がある。自分のPCであれば最高権限は自分が持っており、どのメモリでも読めるはずではないか、と。
この直感は、ソフトウェアのレイヤーに限れば正しい。OS管理者(root / Admin)はソフトウェア上の最高権限を持つ。しかし現代のプロセッサやGPUには、OSよりもさらに下のレイヤーに、OSからすら隔離された実行環境がシリコン上に焼き込まれている。
TEE(Trusted Execution Environment)
ARM TrustZoneを例に取ると、CPUは「ノーマルワールド」と「セキュアワールド」の2つの世界に分離されている。Android OSはノーマルワールドで動作し、DRMの復号処理はセキュアワールドで動作する。両者間のメモリ空間は、MMUとメモリアクセスコントローラ(TZASC)によってハードウェアレベルで隔離されている。
信頼の連鎖(Chain of Trust)
この仕組みを支えるのが、チップ製造段階から始まる信頼の連鎖である。
- チップ製造時: GPU/SoCのシリコン内部に秘密鍵が焼き込まれる。外部から読み出す物理的インターフェースは存在しない
- リモート認証: DRMライセンスサーバーが、端末のハードウェアが正規品であることを確認する(attestation)
- 鍵配送: コンテンツ復号鍵はそのハードウェア固有の鍵で暗号化されて送られ、チップ内部でのみ復号される
OSやドライバは「このコンテンツを再生しろ」という命令を出せるが、「復号済みのデータをこのメモリアドレスに書き出せ」とは指示できない。ハードウェアデコーダはコンテンツ保護ポリシーに従って、出力先をディスプレイコントローラ(HDCP付き)に限定する。
それでも完全ではない
Widevine L3(ソフトウェアのみ)は過去に何度も破られている。L1(ハードウェア保護)でも、サイドチャネル攻撃やグリッチングなどの物理攻撃で理論上は突破可能だが、専門設備が必要でコストが非常に高い。また、最終的にディスプレイに映像が表示される以上、カメラで撮影する「アナログホール」はどんなDRMでも原理的に防げない。
3. 再生パイプラインの詳細 ── サーバーからディスプレイまで
Widevine L1対応のAndroid端末を例に、Netflixの再生開始からディスプレイ出力までの具体的な処理の流れを追う。
3-1. マニフェスト取得
再生ボタンが押されると、NetflixアプリはまずCDNからMPD(Media Presentation Description)を取得する。これはDASHプロトコルのマニフェストで、各品質レベルのセグメントURL、DRMの種類(Widevine)、暗号化方式(CENC: Common Encryption)などのメタデータが記述されている。この時点で動画データ自体はまだ来ていない。
3-2. DRMセッション初期化
アプリがAndroidの MediaDrm APIでWidevineセッションを開始すると、OS内部のWidevine CDM(Content Decryption Module)が起動する。L1環境ではCDMの中核処理はTEE内部で動作する。
3-3. ライセンス取得
TEE内のWidevineモジュールがライセンスリクエスト(チャレンジ)を生成する。このリクエストには端末のハードウェア固有の証明書による署名が含まれる。アプリはこの暗号化されたバイナリブロブを中身を理解できないまま、Netflixのライセンスサーバーに転送する。
サーバーは署名を検証して端末が正規のL1ハードウェアであることを確認し、CEK(Content Encryption Key)をその端末固有の公開鍵で暗号化して返す。アプリは暗号化された応答をそのままTEEに渡す。アプリ層を通過するデータはすべて暗号化されており、CEKの平文に一切触れない。
3-4. TEE内部での鍵復号
TEE内部のWidevineモジュールが、シリコンに焼き込まれた秘密鍵でライセンス応答を復号し、CEKを取り出す。このCEKはTEEの保護メモリ内にのみ存在し、Android OS側のメモリ空間にはコピーされない。
3-5. 暗号化された動画セグメントのダウンロード
並行して、アプリはCDNからCENC暗号化済みの動画セグメント(fMP4形式)をHTTPS経由でダウンロードする。AES-128-CTR等で暗号化されたバイナリであり、そのまま見ても映像にはならない。
3-6. 復号とデコード
アプリがAndroidの MediaCodec APIを MediaCrypto 付きで構成し、暗号化セグメントを投入すると、以下の処理が走る。
- 暗号化データがセキュアバッファに転送される。DMAでTEE管理下のメモリ領域に直接書き込まれ、OSはこの領域を読めない。TZASC がハードウェアレベルでアクセスを遮断している
- TEE内でCEKを使ってAES復号が行われる。復号された生のH.264/H.265ストリームは同じ保護メモリ内に留まる
- SoC内蔵のハードウェアビデオデコーダが保護メモリから直接読み出してデコードする。デコーダ自体もTEEから制御され、出力先として保護フレームバッファしか指定できない
3-7. 保護フレームバッファからディスプレイへ
デコード済みフレームは保護サーフェスに書き込まれる。ディスプレイコントローラ(DPU)がこのバッファを読み取って画面に合成するが、このパスもハードウェアで保護されている。外部出力時はHDCPが作動し、認証済みデバイスにのみ映像を送出する。
3-8. 画面録画ツールが黒画面になる理由
Androidの画面録画は SurfaceFlinger 経由でフレームバッファを読み取る。しかしDRM保護サーフェスには GRALLOC_USAGE_PROTECTED フラグが設定されており、SurfaceFlinger はこのレイヤーの中身を読み出せず黒い矩形として扱う。GPUのハードウェアレベルで強制されるため、ソフトウェア的にフラグを書き換えてもメモリアクセスコントローラが物理的にブロックする。
4. 「保護されたサーフェス」の実体 ── OS別比較
Android
バッファ管理を担う Gralloc(Graphics Allocation)HALが GRALLOC_USAGE_PROTECTED フラグ付きでメモリを確保すると、TrustZoneのメモリコントローラがその物理ページを「セキュアワールド専用」にマークする。以降、ノーマルワールドからのバスアクセスはハードウェアが電気的に遮断する。
画面合成を担う SurfaceFlinger は保護バッファの中身を読めず、DPU(Display Processing Unit)に「このバッファをオーバーレイとして直接出力せよ」と指示するだけである。DPUだけがバッファの中身を読める唯一のハードウェアコンポーネントとなる。
Windows
Protected Media Path(PMP)アーキテクチャが中核を担う。Media Foundationフレームワーク内に構築される保護パイプラインでは、すべてのコンポーネントがMicrosoft署名済みの信頼モジュールでなければならない。
復号済み映像はD3D11の保護サーフェス( D3D11_RESOURCE_MISC_HW_PROTECTED フラグ付き)として扱われる。CPU側の Map() 操作はドライバが拒否する。DWM(Desktop Window Manager)はハードウェアオーバーレイで直接ディスプレイに出力し、合成結果のフレームバッファには保護コンテンツが含まれない。
Intelの統合GPU環境では PAVP(Protected Audio Video Path)が追加で動作し、GPU内部のデータパスを暗号化する。VRAMをダンプしても暗号化データしか見えない。
Linux
Linuxデスクトップでは Widevine L1 がサポートされておらず、ChromeのWidevine CDMはL3で動作する。保護サーフェスに相当する標準的な仕組みがX11/Wayland + Mesaのグラフィックスタックに存在しないためである。オープンソースのドライバ開発思想とNDAベースのハードウェア保護仕様が根本的に相容れないことが背景にある。
例外として ChromeOS はGoogleが独自にハードウェア保護パスを実装しており L1 に対応しているが、一般のLinuxデスクトップには適用できない。結果として Netflix の Linux での再生は 720p に制限されている。
5. なぜAndroidがOSSでも保護が成立するのか
AndroidのソースコードはAOSPとして公開されている。Grallocを書き換えて保護フラグを無視するカスタムROMを作ることも、作業としては可能である。それでも保護が崩壊しない理由は2つある。
理由1: ハードウェアがソフトウェアを信用しない
Grallocの PROTECTED フラグは保護の実体ではなく、ハードウェア設定を反映した「ラベル」にすぎない。正規のGrallocがTEEに「このメモリをセキュアに設定してくれ」とリクエストし、TZASCが物理メモリをセキュアワールド専用にマークする。
Grallocを書き換えてこのリクエストを出さなくした場合、TEE側のWidevine Trustletは復号データの書き出し先がセキュアメモリかどうかを確認し、セキュアでなければ出力を拒否する。窓口(Gralloc)を改造しても、金庫(TEE)の中の処理は変わらない。
理由2: 改変した時点でDRM認証が通らなくなる
カスタムROMを焼くにはブートローダーのアンロックが必要で、これによりVerified Bootの信頼の連鎖が途切れる。WidevineのL1認証ではTEE内のアテステーション機構が端末状態を確認し、ブートローダーがアンロックされた端末にはL1証明書を発行せず、L3にフォールバックする。高品質コンテンツがそもそも配信されなくなるという設計である。
AOSPで公開されているのはHALのインターフェース定義と参照実装であり、実機で動くGralloc実装はSoCベンダーのプロプライエタリなバイナリブロブ、TEE内のセキュアOS(Qualcomm QSEE、Trustonic Kinibi等)とWidevine Trustletは完全にクローズドである。OSが全公開でも、金庫の中身が非公開であれば設計は破綻しない。
6. アプリからTEEに至るAPI呼び出しの詳細
最後に、アプリケーションコードからTEEに暗号化データが渡されるまでの各レイヤーを具体的に見ていく。
アプリ層(Java/Kotlin)
アプリが触れるのは3つのクラスである。
-
MediaDrm: Widevineを指定してインスタンスを生成し、ライセンスの取得・管理を行う -
MediaCrypto:MediaDrmセッションに紐づく復号コンテキスト。MediaCodecに渡すブリッジ役 -
MediaCodec: デコーダ本体。暗号化データの投入には通常のqueueInputBuffer()ではなくqueueSecureInputBuffer()を使う
Framework層
queueSecureInputBuffer() が呼ばれると、JNI経由でネイティブの MediaCodec.cpp に到達し、CryptoInfo(暗号化方式、IV、サブサンプル情報)とともに ICrypto インターフェースに渡される。
ICrypto はBinder IPC経由で MediaDrmService(別プロセス)内の CryptoHal に到達する。別プロセスで動作させることで、アプリプロセスが侵害されてもDRM処理に直接触れないようにしている。
HAL層(ベンダー実装)
CryptoHal が呼び出す ICryptoPlugin::decrypt() の概念的なシグネチャは以下の通りである。
decrypt(
mode, // AES-CTR か AES-CBC
keyId, // TEE内の鍵を指すハンドル
iv, // 初期化ベクトル
srcBuffer, // 暗号化データ(共有メモリ上)
subSamples, // クリア/暗号化のサブサンプルマッピング
dstBuffer // 出力先:セキュアバッファハンドル
) → Status
dstBuffer が決定的に重要である。L1環境ではこれはセキュアバッファへの不透明な参照(native_handle_t)であり、アプリやOS側からは単なる整数のハンドルに見える。このハンドルから実際のメモリアドレスを逆引きする手段はノーマルワールドに存在しない。
ベンダー実装の内部
公開されたHALインターフェースの裏側では、以下の処理が行われている。
-
srcBufferをSMC(Secure Monitor Call)命令でTrustZoneに転送 - TEE内のWidevine TrustletがCEKでAES復号を実行
- 復号結果をTEE管理下のセキュアメモリに書き込み
- ハンドル(参照番号)だけをノーマルワールドに返却
このハンドルがそのままハードウェアビデオデコーダに渡され、保護パイプラインが完結する。
アプリから見える世界
アプリの視点では、すべてのAPI呼び出しで一貫して「不透明なハンドルとメタデータ」だけを扱っている。平文の映像データに触れるAPIは設計上存在しない。これはソフトウェアの制限ではなく、平文データが存在する物理メモリ空間がノーマルワールドのアドレス空間に含まれていないという事実の反映である。
おわりに
Netflixの画面録画防止は「ソフトウェアでブロックしている」という単純な話ではなく、チップ製造時の鍵埋め込みから、TEEによるメモリ隔離、ハードウェアデコーダの保護パス、HDCPによる出力制御まで、シリコンレベルから積み上げられた多層防御である。
「自分のPCなのにアクセスできない」という一見矛盾した状態は、チップメーカーがコンテンツ事業者側と契約し、「OSからアクセスできない領域」をシリコン設計に組み込むことで実現されている。ユーザーがPCを「所有」していても、チップ内部の保護領域の設計権限はチップメーカーが握っている ── これが現代のDRM保護の技術的な現実である。