見出し画像

ラスバレのアプリ内配信動画をアプリ外で視聴する方法

ゲーム『アサルトリリィ Last Bullet』(以下ラスバレ)では時折アプリ内でHTTP Live Streaming(以下HLS)でVOD配信が行われることがある。動画はH264でエンコードされ、ストリーミング配信用に複数のTSファイルに分割された上で、その再生時間とファイル名がHLS M3U形式のマニフェストファイルに記述されている。そのファイルのURLを割り出し、動画をアプリ外で視聴する方法について書き留める。

注意点

ここから紹介する内容は解析でしか得られない情報となる。ゲームをプレイする上で有利・不利が発生したり、他者の権利を侵害する情報ではないと考えて公にしているが、利用規約に抵触する可能性があるため、自己責任かつモラルの範囲内で利用して欲しい。

手順

今回はAndroid版アプリから動画URLを解析するため、Android OS 7.0~10に対応したパケットキャプチャアプリ『HttpCanary』を利用する。

1. まずはHttpCanary初回起動時のウィザードに従って自己署名ルートCA証明書のインストールを済ませる。

2. 非root環境の場合はHttpCanaryの設定メニューからParallel Spaceと同64bitをインストールする。

3. Parallel Spaceを起動し、ラスバレアプリをサブ垢アプリとして追加する。

4. ラスバレを起動し、チュートリアルまで済ませる。このとき数GBのリソースをダウンロードすることになるため、高速回線とストレージの空き容量が必要となる。

5. ラスバレのホーム画面が表示できるようになったらHttpCanaryに切り替え、パケットキャプチャを開始する。

6. ラスバレのホーム画面に戻り、ストリーミング再生を開始する。

7. 数秒ほど再生を確認したところでキャプチャを停止し、動画再生の端緒となるマニフェスト(*.m3u8)を見つける。

マニフェストURL

現在は『Edel Lilie+』の動画が計3本配信されており、それぞれ次のようなURLが得られた。

  • 昼公演の一部 ​

https://static-files-allb.pokelabo.jp/production/streaming/video3/video3.m3u8?__gda__=st=1626013680~exp=1626014280~acl=%2F%2A~hmac=13b02a972824f23072c09a86b29de05b3cfd60a7c88085cec76b71ba21e8e57f
  • 出演者特別コメント第1段

https://static-files-allb.pokelabo.jp/production/streaming/video4/video4.m3u8?__gda__=st=1626013680~exp=1626014280~acl=%2F%2A~hmac=13b02a972824f23072c09a86b29de05b3cfd60a7c88085cec76b71ba21e8e57f
  • ライブ映像第2段

https://static-files-allb.pokelabo.jp/production/streaming/video5/video5.m3u8?__gda__=st=1626013680~exp=1626014280~acl=%2F%2A~hmac=13b02a972824f23072c09a86b29de05b3cfd60a7c88085cec76b71ba21e8e57f

このマニフェストのURLをVLC Media PlayerなどのHLS対応アプリで開くと、動画をアプリ外で視聴できる。

youtube-dlFFmpegなどのメディアダウンローダー兼コンバーターに有効なマニフェストのURLを渡せば動画をローカルに保存することもできる。

youtube-dl -i https://static-files-allb.pokelabo.jp/production/streaming/video3/video3.m3u8?__gda__=st=1626013680~exp=1626014280~acl=%2F%2A~hmac=13b02a972824f23072c09a86b29de05b3cfd60a7c88085cec76b71ba21e8e57f
ffmpeg -protocol_whitelist "file,http,https,tcp,tls,crypto" -i https://static-files-allb.pokelabo.jp/production/streaming/video3/video3.m3u8?__gda__=st=1626013680~exp=1626014280~acl=%2F%2A~hmac=13b02a972824f23072c09a86b29de05b3cfd60a7c88085cec76b71ba21e8e57f -c copy -movflags faststart video3.mp4

ただし、得られたマニフェストURLの有効期限は最大10分間となる。上記URLの有効期間は本記事公開時点ですでに失効しており、こうして得たURLを誰かと共有して楽しむといったことは期待できない。どうやらこれはAkamaiが提供する簡易的なアクセス制限のようだ。

上記マニフェストのURLパラメータの意味だが、

?__gda__=st=1626013680~exp=1626014280~acl=%2F%2A~hmac=13b02a972824f23072c09a86b29de05b3cfd60a7c88085cec76b71ba21e8e57f

を例に取ると、「__gda__」がトークン名を、「st=1626013680」が2021-07-11 14:28:00(UTC)から有効であることを、「exp=1626014280」が2021-07-11 14:38:00(UTC)まで有効であることを、「acl=%2F%2A」が「/*」へのアクセスを許可していることを意味し、それらのパラメータを連結した文字列「st=1626013680~exp=1626014280~acl=%2F%2A」に対するHMAC-SHA256の鍵付きハッシュ値が「13b02a972824f23072c09a86b29de05b3cfd60a7c88085cec76b71ba21e8e57f」であるということになる。

不正に有効期限を延ばそうとexpパラメータを書き換えても、HMAC-SHA256の算出に用いているハッシュ鍵が不明のため、正しい鍵付きハッシュ値を導出できず、マニフェストURLへのアクセスは失敗する。

マニフェストファイル

マニフェストファイルについてもう少し掘り下げてみよう。今回取得した昼公演の一部のマニフェストファイル(video3.m3u8)の中身は次のようになっている。

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:8
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-KEY:METHOD=AES-128,URI="video.key",IV=0x894BC56F3DD4A85248AE3BC7EF012928
#EXTINF:2.168833,
video0000.ts
#EXTINF:2.068733,
video0001.ts
#EXTINF:2.936267,
video0002.ts
...(中略)...
#EXTINF:4.371033,
video0255.ts
#EXT-X-ENDLIST

暗号を復号するキーファイルとTSファイルのURLが相対パスで記述されているため、その部分を次のようにフルパスに書き換えると、TSファイルが公式サーバに置かれている限り、ローカル上に保存したマニフェストファイルを使って動画を視聴することができる。

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:8
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-KEY:METHOD=AES-128,URI="https://static-files-allb.pokelabo.jp/production/streaming/video.key",IV=0x894BC56F3DD4A85248AE3BC7EF012928
#EXTINF:2.168833,
https://static-files-allb.pokelabo.jp/production/streaming/video0000.ts
#EXTINF:2.068733,
https://static-files-allb.pokelabo.jp/production/streaming/video0001.ts
#EXTINF:2.936267,
https://static-files-allb.pokelabo.jp/production/streaming/video0002.ts
...(中略)...
#EXTINF:4.371033,
https://static-files-allb.pokelabo.jp/production/streaming/video0255.ts
#EXT-X-ENDLIST

この改変マニフェストを使って動画をローカルに保存する場合のFFmpegコマンドは次のようになる。

ffmpeg -protocol_whitelist "file,http,https,tcp,tls,crypto" -i video3_fullpath.m3u8 -c copy -movflags faststart video3.mp4

マニフェストファイルのURLには有効期限が存在したが、TSファイルのURLは配信期間中は有効で、実は配信期間を幾日も過ぎた本記事執筆時点でもまだダウンロード可能となっている。当然のことながらローカルに保存した動画に再生期限はない。

応用

ここまで得られた情報からラスバレの動画配信サーバのディレクトリツリー構造を整理すると次のようになる。

static-files-allb.pokelabo.jp.
└─production
  └─streaming
      ├─video (Edel Lilie)
      ├─video2 (League of Gardens)
      ├─video3 (Edel Lilie+ 昼公演)
      │      video3.m3u8video.keyvideo0000.tsvideo0001.tsvideo0002.ts
      │      ...
      │
      ├─video4 (Edel Lilie+ 第1弾)
      │      video4.m3u8video.keyvideo0000.tsvideo0001.tsvideo0002.ts
      │      ...
      │
      ├─video5 (Edel Lilie+ 第2弾)
      │      video5.m3u8video.keyvideo0000.tsvideo0001.tsvideo0002.ts
      │      ...
      │
      ├─video6 (未公開・ふるーつ第1話?)

ここまでツリー構造やファイル名規則が判明していれば、いちいちマニフェストのURLを割り出す必要はなく、手元にあるマニフェストファイルに記載されたパスの一部を推測で書き換える(例:/video3/ → /video6/ )だけで、アプリ内配信動画をアプリ外で視聴できてしまいそうである。

それどころか、下手するとアプリ内で配信が開始される前に、サーバ上にTSファイルがアップロードされた段階で動画視聴が可能になる可能性が高い。(18時公開の場合、15時半ごろにはアップロードが完了しているようだ。)

ただしその場合、マニフェスト内の#EXTINFタグで示されるTSファイル毎の再生時間(秒単位)は推測不可能のため、改変マニフェストを頼りに再生するとシーク位置がおかしくなってシーンが飛んでしまう。が、一度FFmpegで動画をローカルに保存してから再生することでシーク問題は解決できる。