mirror of
https://github.com/qtmleap/revkit.git
synced 2026-10-01 23:41:50 +02:00
Hooks HKDF_extract and HKDF_expand in the tweak for improved key derivation logging and analysis. Removes Frida and Proxyman skills, references, and documentation for a more focused Agent Team workflow. Adds several Python tools for systematic HKDF and session key derivation analysis, supporting ongoing reverse engineering and debugging efforts. Switches all agent workflow documentation and prompts to English, clarifies inter-agent communication constraints, and updates the workflow to reflect current team structure and supported features. Relates to the need for more accurate key extraction and simplified agent usage.
272 lines
9.7 KiB
Python
272 lines
9.7 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
HKDF パラメータ探索 最終版
|
|
ログから正確に特定したペアリング:
|
|
DH_compute_key → shared_secret = 052a8b...
|
|
直後の AES_set_encrypt_key bits=128 key=97b99f4e88e8e73779aa20ac11877c5d
|
|
直後の HMAC key=d45443fa11efec622c83b27c55f7a73143bdfa0d51820ac597b9e3fb5c28dbb0
|
|
|
|
また別のペアリング:
|
|
DH_generate_key の後
|
|
AES_set_encrypt_key bits=128 key=0817065e29e6d1c8668473af9e13b3c2
|
|
HMAC key=91f752f76d7ab4c2dc6e5b3ec1c0e5a16864421fe449be5457459602e298ebc1
|
|
→ ただし DH_compute_key より前なのでこれは前回セッションのキャッシュ鍵か?
|
|
"""
|
|
|
|
import hashlib
|
|
import hmac as hmac_mod
|
|
import struct
|
|
|
|
from cryptography.hazmat.primitives import hashes
|
|
from cryptography.hazmat.primitives.kdf.hkdf import HKDF, HKDFExpand
|
|
|
|
# 正確なペアリング (ログから特定)
|
|
PAIRS = [
|
|
# (name, shared_secret, enc_key, hmac_key)
|
|
(
|
|
"Pair-A: DH_compute_key → 直後のAES-128/HMAC",
|
|
bytes.fromhex(
|
|
"052a8bfe9f1a1a9becdd67672338191b"
|
|
"d7b5aff7fffe1f4cfbd97a0b14f8d59a"
|
|
"f54697a0bc1cf96ad6f6e84af98ffd1c"
|
|
"ebbc0fb5b04360878710f215f1261129"
|
|
"652808fd11f5164d84a501b40e63ba91"
|
|
"fcdcc932b6dbd61017099673f552db6e"
|
|
"94ff19934bfdef21b9701c4c9d312b78"
|
|
"f2cb8911e446313c2cb0567f7de865b3"
|
|
),
|
|
bytes.fromhex("97b99f4e88e8e73779aa20ac11877c5d"),
|
|
bytes.fromhex("d45443fa11efec622c83b27c55f7a73143bdfa0d51820ac597b9e3fb5c28dbb0"),
|
|
),
|
|
# HMAC key として別のペアリング候補
|
|
(
|
|
"Pair-B: DH_compute_key → HMAC直後",
|
|
bytes.fromhex(
|
|
"052a8bfe9f1a1a9becdd67672338191b"
|
|
"d7b5aff7fffe1f4cfbd97a0b14f8d59a"
|
|
"f54697a0bc1cf96ad6f6e84af98ffd1c"
|
|
"ebbc0fb5b04360878710f215f1261129"
|
|
"652808fd11f5164d84a501b40e63ba91"
|
|
"fcdcc932b6dbd61017099673f552db6e"
|
|
"94ff19934bfdef21b9701c4c9d312b78"
|
|
"f2cb8911e446313c2cb0567f7de865b3"
|
|
),
|
|
bytes.fromhex("97b99f4e88e8e73779aa20ac11877c5d"),
|
|
bytes.fromhex("a4333e99a34eef3663f8e38e217e696949cd3bf57598c5c260fedb8997afa82b"), # 最初のHMAC
|
|
),
|
|
]
|
|
|
|
found: list[str] = []
|
|
|
|
|
|
def check(label: str, derived: bytes, target_enc: bytes, target_hmac: bytes) -> bool:
|
|
if len(derived) >= 48 and derived[:16] == target_enc and derived[16:48] == target_hmac:
|
|
msg = f"[FULL MATCH] {label}"
|
|
print(msg)
|
|
found.append(msg)
|
|
return True
|
|
if len(derived) >= 16 and derived[:16] == target_enc:
|
|
msg = f"[ENC_ONLY] {label} rest={derived[16:32].hex()}"
|
|
print(msg)
|
|
found.append(msg)
|
|
return False
|
|
|
|
|
|
HASH_ALGS = {
|
|
"SHA1": hashes.SHA1(),
|
|
"SHA256": hashes.SHA256(),
|
|
"SHA384": hashes.SHA384(),
|
|
"SHA512": hashes.SHA512(),
|
|
}
|
|
|
|
SALTS: list[tuple[str, bytes | None]] = [
|
|
("None", None),
|
|
("empty", b""),
|
|
("z16", b"\x00" * 16),
|
|
("z20", b"\x00" * 20),
|
|
("z32", b"\x00" * 32),
|
|
("z64", b"\x00" * 64),
|
|
]
|
|
|
|
INFOS: list[tuple[str, bytes]] = [
|
|
("empty", b""),
|
|
("00", b"\x00"),
|
|
("01", b"\x01"),
|
|
("MSL", b"MSL"),
|
|
("enc", b"enc"),
|
|
("hmac", b"hmac"),
|
|
("session", b"session"),
|
|
("Netflix", b"Netflix"),
|
|
("AES", b"AES"),
|
|
("HMAC", b"HMAC"),
|
|
("scheme5", b"scheme5"),
|
|
("key_exp", b"key_expansion"),
|
|
("NF", b"Netflix MSL"),
|
|
("sign", b"sign"),
|
|
("encryption", b"encryption"),
|
|
("signing", b"signing"),
|
|
("ENC", b"ENC"),
|
|
("SIGN", b"SIGN"),
|
|
]
|
|
|
|
LENGTHS = [16, 32, 48, 64, 80, 128]
|
|
|
|
for pair_name, shared_secret, target_enc, target_hmac in PAIRS:
|
|
print("=" * 70)
|
|
print(f"Pair: {pair_name}")
|
|
print(f" shared: {shared_secret.hex()[:20]}...")
|
|
print(f" enc: {target_enc.hex()}")
|
|
print(f" hmac: {target_hmac.hex()}")
|
|
print("=" * 70)
|
|
|
|
ss_int = int(shared_secret.hex(), 16)
|
|
INPUT_VARIANTS: list[tuple[str, bytes]] = [
|
|
("raw", shared_secret),
|
|
("pad128", ss_int.to_bytes(128, "big")),
|
|
("sha256", hashlib.sha256(shared_secret).digest()),
|
|
("sha384", hashlib.sha384(shared_secret).digest()),
|
|
("sha512", hashlib.sha512(shared_secret).digest()),
|
|
("rev", shared_secret[::-1]),
|
|
]
|
|
|
|
print("HKDF 全組み合わせ:")
|
|
for inp_name, inp_val in INPUT_VARIANTS:
|
|
for hash_name, hash_alg in HASH_ALGS.items():
|
|
for salt_name, salt_val in SALTS:
|
|
for info_name, info_val in INFOS:
|
|
for out_len in LENGTHS:
|
|
try:
|
|
hkdf = HKDF(
|
|
algorithm=hash_alg,
|
|
length=out_len,
|
|
salt=salt_val,
|
|
info=info_val,
|
|
)
|
|
derived = hkdf.derive(inp_val)
|
|
label = f"HKDF({inp_name},h={hash_name},s={salt_name},i={info_name},L={out_len})"
|
|
check(label, derived, target_enc, target_hmac)
|
|
except Exception:
|
|
pass
|
|
|
|
print("HKDFExpand 全組み合わせ:")
|
|
for inp_name, inp_val in INPUT_VARIANTS:
|
|
for hash_name, hash_alg in HASH_ALGS.items():
|
|
for info_name, info_val in INFOS:
|
|
for out_len in LENGTHS:
|
|
try:
|
|
hkdf_expand = HKDFExpand(
|
|
algorithm=hash_alg,
|
|
length=out_len,
|
|
info=info_val,
|
|
)
|
|
derived = hkdf_expand.derive(inp_val)
|
|
label = f"HKDFExpand({inp_name},h={hash_name},i={info_name},L={out_len})"
|
|
check(label, derived, target_enc, target_hmac)
|
|
except Exception:
|
|
pass
|
|
|
|
print("直接ハッシュ:")
|
|
for inp_name, inp_val in INPUT_VARIANTS:
|
|
for hash_fn, hash_name in [
|
|
(hashlib.sha256, "SHA256"),
|
|
(hashlib.sha384, "SHA384"),
|
|
(hashlib.sha512, "SHA512"),
|
|
]:
|
|
h = hash_fn(inp_val).digest()
|
|
dummy = h + b"\x00" * 64
|
|
check(f"hash_{hash_name}({inp_name})", dummy, target_enc, target_hmac)
|
|
|
|
print("部分列比較:")
|
|
for offset in range(0, len(shared_secret) - 15):
|
|
if shared_secret[offset : offset + 16] == target_enc:
|
|
print(f" [MATCH] shared[{offset}:{offset+16}] == enc_key")
|
|
|
|
print()
|
|
|
|
print()
|
|
print("=" * 70)
|
|
print("追加: ログの HMAC 直後の値からの逆算")
|
|
print("=" * 70)
|
|
|
|
# ログの時系列:
|
|
# 19:02:57.669 [HMAC] key=a4333e99a34eef3663f8e38e217e696949cd3bf57598c5c260fedb8997afa82b
|
|
# 19:02:57.672 [AES bits=128 key=97b99f4e...
|
|
# 19:02:57.674 [HMAC] key=d45443fa...
|
|
#
|
|
# a4333e... は DH 直後の最初の HMAC 呼び出し
|
|
# 97b99f... は AES-128
|
|
# d45443... は最終的な hmac_key
|
|
#
|
|
# 仮説: a4333e... = HKDF の中間 PRK?
|
|
# HMAC が HKDF の一部として呼ばれているなら...
|
|
|
|
HMAC_AFTER_DH = bytes.fromhex("a4333e99a34eef3663f8e38e217e696949cd3bf57598c5c260fedb8997afa82b")
|
|
ENC_AFTER_DH = bytes.fromhex("97b99f4e88e8e73779aa20ac11877c5d")
|
|
HMAC_FINAL = bytes.fromhex("d45443fa11efec622c83b27c55f7a73143bdfa0d51820ac597b9e3fb5c28dbb0")
|
|
DH_SHARED_CORRECT = bytes.fromhex(
|
|
"052a8bfe9f1a1a9becdd67672338191b"
|
|
"d7b5aff7fffe1f4cfbd97a0b14f8d59a"
|
|
"f54697a0bc1cf96ad6f6e84af98ffd1c"
|
|
"ebbc0fb5b04360878710f215f1261129"
|
|
"652808fd11f5164d84a501b40e63ba91"
|
|
"fcdcc932b6dbd61017099673f552db6e"
|
|
"94ff19934bfdef21b9701c4c9d312b78"
|
|
"f2cb8911e446313c2cb0567f7de865b3"
|
|
)
|
|
|
|
print(f"HMAC after DH: {HMAC_AFTER_DH.hex()}")
|
|
print(f"ENC after DH: {ENC_AFTER_DH.hex()}")
|
|
print(f"HMAC final: {HMAC_FINAL.hex()}")
|
|
print()
|
|
|
|
# Tweak の HMAC フックは key_len=32 のとき記録
|
|
# HKDF の内部では HMAC-SHA256(key=salt, ikm) = PRK となる
|
|
# PRK = HMAC-SHA256(salt, IKM)
|
|
# この HMAC 呼び出しが a4333e... = PRK?
|
|
|
|
# verify: HMAC-SHA256(salt=?, DH_SHARED) = a4333e...
|
|
# salt が zeros_32 の場合:
|
|
for salt_val, salt_name in [(b"\x00" * 32, "z32"), (None, "None"), (b"", "empty")]:
|
|
actual_salt = salt_val if salt_val is not None else b"\x00" * 32 # HKDF-Extract のデフォルト
|
|
prk = hmac_mod.new(actual_salt, DH_SHARED_CORRECT, hashlib.sha256).digest()
|
|
print(f"HMAC-SHA256(salt={salt_name}, DH_SHARED) = {prk.hex()}")
|
|
if prk == HMAC_AFTER_DH:
|
|
print(f" [MATCH] PRK == HMAC_AFTER_DH with salt={salt_name}")
|
|
|
|
print()
|
|
# HMAC_AFTER_DH が PRK なら、expand で enc_key を導出
|
|
prk_candidate = HMAC_AFTER_DH
|
|
for info_name, info_val in [("empty", b""), ("01", b"\x01"), ("enc", b"enc")]:
|
|
for out_len in [16, 32, 48]:
|
|
try:
|
|
hkdf_expand = HKDFExpand(
|
|
algorithm=hashes.SHA256(),
|
|
length=out_len,
|
|
info=info_val,
|
|
)
|
|
derived = hkdf_expand.derive(prk_candidate)
|
|
if derived[:16] == ENC_AFTER_DH:
|
|
print(f"[PRK→ENC MATCH] HKDFExpand(prk=HMAC_AFTER_DH, info={info_name}, L={out_len})")
|
|
except Exception:
|
|
pass
|
|
|
|
print()
|
|
print("=" * 70)
|
|
print("結果サマリー")
|
|
print("=" * 70)
|
|
if found:
|
|
print(f"一致 ({len(found)}件):")
|
|
for f in found:
|
|
print(f" {f}")
|
|
else:
|
|
print("一致なし")
|
|
print()
|
|
print("仮説: HKDF 以外の鍵導出を使用している可能性")
|
|
print(" または: NFWebCrypto 内部で独自の鍵導出実装を使用")
|
|
print()
|
|
print(" 次のアプローチ:")
|
|
print(" 1. NFWebCrypto の HKDF 関数を直接フック")
|
|
print(" 2. AES_set_encrypt_key の直前の call stack を調べる")
|
|
print(" 3. key33.6 (96B) の構造 [IV(16)+CT(48)+HMAC(32)] を仮定して")
|
|
print(" appboot レスポンスから enc_key を復号する")
|