mirror of
https://github.com/qtmleap/revkit.git
synced 2026-09-16 08:11:51 +02:00
- 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>
126 lines
4.8 KiB
Python
126 lines
4.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Android Frida ログ → Chrome extension 形式変換.
|
|
|
|
Usage:
|
|
python -m tools.transformers.android raws/android/20260404/capture.jsonl
|
|
python -m tools.transformers.android raws/android/20260404/capture.jsonl -o logs/android
|
|
"""
|
|
|
|
import argparse
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
from tools.transformers.base import (
|
|
BaseTransformer,
|
|
load_entries,
|
|
print_summary,
|
|
write_output,
|
|
)
|
|
|
|
|
|
class AndroidTransformer(BaseTransformer):
|
|
"""Android Frida ログ用のイベントハンドラマッピング.
|
|
|
|
Android 固有のイベント (Java MSL / Cronet / ALE BouncyCastle 等) を
|
|
Chrome extension 形式にマッピングする。
|
|
"""
|
|
|
|
def handle_esn_android(self, e: dict):
|
|
"""Android ESN は PRV / PXA を区別する."""
|
|
esn = e.get("esn", "")
|
|
if not esn:
|
|
return
|
|
if "PRV" in esn.upper():
|
|
self.esn["prv"] = esn
|
|
elif "PXA" in esn.upper():
|
|
self.esn["pxa"] = esn
|
|
else:
|
|
self.esn["prv"] = esn
|
|
self.esn["capturedAt"] = e.get("ts", "")
|
|
|
|
_handlers: dict = {
|
|
# MSL
|
|
"msl.message": BaseTransformer.handle_msl_message,
|
|
"msl.api": BaseTransformer.handle_msl_api,
|
|
"msl.api.response": BaseTransformer.handle_msl_api_response,
|
|
"appboot.response": BaseTransformer.handle_msl_api_response,
|
|
# MSL response payload (MessageInputStream / PayloadChunk)
|
|
"msl.response.payload": BaseTransformer.handle_msl_response_payload,
|
|
"msl.payload": BaseTransformer.handle_msl_response_payload,
|
|
# HTTP
|
|
"http.request": BaseTransformer.handle_http_request,
|
|
"http.response": BaseTransformer.handle_http_response,
|
|
# Crypto (Java MSL AesCbcEncryptor)
|
|
"msl.aesCbcEncrypt": BaseTransformer.handle_aes_cbc_encrypt_combined,
|
|
"msl.aesCbcDecrypt": BaseTransformer.handle_aes_cbc_decrypt,
|
|
"msl.hmacSha256": BaseTransformer.handle_hmac,
|
|
"msl.hmacSha256.sign": BaseTransformer.handle_hmac,
|
|
"msl.aesKwUnwrap": BaseTransformer.handle_aes_kw_unwrap,
|
|
"msl.dhSharedSecret": BaseTransformer.handle_dh_shared_secret,
|
|
"msl.rsaEncrypt": BaseTransformer.handle_rsa,
|
|
"msl.rsaDecrypt": BaseTransformer.handle_rsa,
|
|
# Crypto (WidevineCryptoContext / SymmetricCryptoContext)
|
|
"msl.widevine.encrypt": BaseTransformer.handle_noop,
|
|
"msl.widevine.decrypt": BaseTransformer.handle_aes_cbc_decrypt, # CBOR チャンク蓄積 (iOS と同じ)
|
|
"msl.widevine.sign": BaseTransformer.handle_noop,
|
|
"msl.widevine.verify": BaseTransformer.handle_noop,
|
|
"msl.symmetric.encrypt": BaseTransformer.handle_noop,
|
|
"msl.symmetric.decrypt": BaseTransformer.handle_noop,
|
|
"msl.symmetric.sign": BaseTransformer.handle_noop,
|
|
"msl.symmetric.verify": BaseTransformer.handle_noop,
|
|
# Crypto (JWE)
|
|
"msl.jwe.wrap": BaseTransformer.handle_noop,
|
|
"msl.jwe.unwrap": BaseTransformer.handle_noop,
|
|
# DRM
|
|
"drm.keyRequest": BaseTransformer.handle_noop,
|
|
"drm.keyResponse": BaseTransformer.handle_noop,
|
|
"drm.openSession": BaseTransformer.handle_noop,
|
|
"drm.property": BaseTransformer.handle_noop,
|
|
"drm.propertyString": BaseTransformer.handle_noop,
|
|
# ALE (BouncyCastle)
|
|
"ale.keys": BaseTransformer.handle_ale_keys,
|
|
# Keys / ESN / Manifest
|
|
"esn.detected": handle_esn_android,
|
|
"manifest": BaseTransformer.handle_manifest,
|
|
# Auth
|
|
"msl.userauthdata": BaseTransformer.handle_noop,
|
|
# Storage
|
|
"storage.sharedPreferences": BaseTransformer.handle_storage_shared_prefs,
|
|
"storage.file": BaseTransformer.handle_storage_file,
|
|
"storage.appFiles": BaseTransformer.handle_storage_sandbox,
|
|
# Skip
|
|
"manifest.kidTable": BaseTransformer.handle_noop,
|
|
"msl.encrypt.input": BaseTransformer.handle_noop,
|
|
"msl.decrypt.output": BaseTransformer.handle_noop,
|
|
"url": BaseTransformer.handle_noop,
|
|
}
|
|
|
|
|
|
def main():
|
|
p = argparse.ArgumentParser(description="Android → Chrome log transformer")
|
|
p.add_argument("input", help="Android capture.jsonl path")
|
|
p.add_argument("-o", "--output", help="Output directory (default: logs/android)")
|
|
args = p.parse_args()
|
|
|
|
input_path = Path(args.input)
|
|
if not input_path.exists():
|
|
print(f"[-] File not found: {input_path}", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
out_dir = Path(args.output) if args.output else Path("logs") / "android"
|
|
|
|
print(f"[*] Input: {input_path}")
|
|
print(f"[*] Output: {out_dir}")
|
|
|
|
entries = load_entries(input_path)
|
|
print(f"[*] Loaded {len(entries)} entries")
|
|
|
|
t = AndroidTransformer()
|
|
t.transform(entries)
|
|
write_output(t, out_dir)
|
|
print_summary(t, "Android")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|