Files
revkit/tools/dump_rsa_key.py
T
tkgstratorandClaude Opus 4.6 a849198fd0 feat(tools): MSLクライアント実装と解析ツールを追加
- src/netflix_msl/: Python MSLクライアント (鍵交換, 暗号化/復号, マニフェスト取得)
- tools/run.py: Fridaフックランナー (@@LOG@@パース, JSONL保存, crypto表示改善)
- tools/find_appboot.js: appboot関連シンボル検索
- tools/check_tweak.js: Tweakロード状態確認
- tools/dump_rsa_key.py, verify_key.py 等の解析ユーティリティ

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

137 lines
4.1 KiB
Python

#!/usr/bin/env python3
"""Chrome Widevine CDM RSA Key Dumper via lldb
lldb で CDM プロセスにアタッチし、BoringSSL の RSA_sign に
ブレークポイントを設定して RSA 秘密鍵をダンプする。
lldb はコードインジェクションを行わないため VerifyCdmHost_0 に検出されない。
使い方:
1. Chrome で DRM コンテンツを再生中に
2. sudo uv run python dump_rsa_key.py
3. Chrome で別の動画を開くか再生を再開して新しいライセンスリクエストを発生させる
"""
import os
import subprocess
import sys
import time
from datetime import datetime
from pathlib import Path
def find_cdm_pid() -> int | None:
result = subprocess.run(["ps", "aux"], capture_output=True, text=True)
for line in result.stdout.splitlines():
if "CdmServiceBroker" in line or "sandbox-type=cdm" in line:
parts = line.split()
if len(parts) >= 2:
try:
return int(parts[1])
except ValueError:
continue
return None
def main():
print("=" * 60)
print("[*] Chrome Widevine CDM RSA Key Dumper (lldb)")
print("[*] %s" % datetime.now().isoformat())
print("=" * 60)
print()
if os.geteuid() != 0:
print("[!] Root required. Run with: sudo uv run python dump_rsa_key.py")
sys.exit(1)
pid = find_cdm_pid()
if not pid:
print("[!] CDM process not found.")
print("[*] Play DRM content in Chrome first.")
sys.exit(1)
print("[+] CDM process: PID %d" % pid)
output_dir = Path("logs") / ("lldb_%s" % datetime.now().strftime("%Y%m%d_%H%M%S"))
output_dir.mkdir(parents=True, exist_ok=True)
# lldb hook script
hook_script = Path(__file__).parent / "scripts" / "lldb_rsa_hook.py"
if not hook_script.exists():
print("[!] %s not found" % hook_script)
sys.exit(1)
# lldb command file
lldb_cmds = output_dir / "lldb_commands.txt"
lldb_cmds.write_text(
"process attach --pid %d\n"
"command script import %s\n"
"command script add -f lldb_rsa_hook.setup_breakpoints setup_bp\n"
"setup_bp\n"
"continue\n" % (pid, hook_script)
)
# Set output dir via env
env = os.environ.copy()
env["RSA_DUMP_DIR"] = str(output_dir)
print("[*] Launching lldb, attaching to PID %d..." % pid)
print("[*] Output dir: %s" % output_dir)
print("[*] Trigger a new license request (open another DRM video).")
print("[*] Press Ctrl+C to stop and detach.")
print()
try:
proc = subprocess.Popen(
["lldb", "--batch", "--source", str(lldb_cmds)],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
env=env,
)
for line in iter(proc.stdout.readline, ""):
line = line.rstrip()
if line:
print(" [lldb] %s" % line)
if "DER saved:" in line:
print()
print("=" * 60)
print("[+] RSA private key extracted!")
print("=" * 60)
proc.wait()
except KeyboardInterrupt:
print("\n[*] Detaching...")
proc.terminate()
try:
proc.wait(timeout=5)
except subprocess.TimeoutExpired:
proc.kill()
# Results
der_files = sorted(output_dir.glob("private_key_*.der"))
json_files = sorted(output_dir.glob("rsa_dump_*.json"))
if der_files:
print()
print("[+] Extracted %d key(s):" % len(der_files))
for f in der_files:
print(" %s (%d bytes)" % (f, f.stat().st_size))
print()
print("[*] Verify: uv run python verify_key.py %s" % der_files[0])
elif json_files:
print()
print("[*] %d RSA dump(s) (DER build may have failed):" % len(json_files))
for f in json_files:
print(" %s" % f)
else:
print()
print("[-] No RSA key data captured.")
print("[*] Try opening a new DRM video to trigger RSA_sign.")
if __name__ == "__main__":
main()