87 lines
2.9 KiB
Python
87 lines
2.9 KiB
Python
#!/usr/bin/env python3
|
|||
|
|
"""
|
||
|
|
Refined search: decrypt ay64.dat using AES-CBC with iv=ay64.dat5
|
||
|
|
and check for structured output.
|
||
|
|
"""
|
||
|
|
from Crypto.Cipher import AES
|
||
|
|
import hmac, hashlib
|
||
|
|
|
||
|
|
base = "L3/"
|
||
|
|
cdm = open("libwvdrmengine.so", "rb").read()
|
||
|
|
ct = open(base + "ay64.dat", "rb").read() # 128 bytes ciphertext
|
||
|
|
iv = open(base + "ay64.dat5", "rb").read() # 16 bytes IV
|
||
|
|
mac = open(base + "ay64.dat6", "rb").read() # 32 bytes MAC
|
||
|
|
|
||
|
|
print(f"Ciphertext ({len(ct)}B): {ct.hex()}")
|
||
|
|
print(f"IV ({len(iv)}B): {iv.hex()}")
|
||
|
|
print(f"MAC ({len(mac)}B): {mac.hex()}")
|
||
|
|
print()
|
||
|
|
|
||
|
|
def score(data):
|
||
|
|
"""Return a score > 0 for data that looks like key material."""
|
||
|
|
s = 0
|
||
|
|
# Widevine keybox magic
|
||
|
|
if data[:4] in (b"KBOX", b"KBPI", b"kbpi", b"KEYB"):
|
||
|
|
s += 100
|
||
|
|
# ASN.1 SEQUENCE (DER-encoded RSA key)
|
||
|
|
if data[0] == 0x30 and data[1] in (0x81, 0x82):
|
||
|
|
# Extra check: length should be plausible
|
||
|
|
if data[1] == 0x81 and data[2] > 64:
|
||
|
|
s += 10
|
||
|
|
elif data[1] == 0x82 and 0 < data[2] < 10:
|
||
|
|
s += 10
|
||
|
|
# Null-terminated string header
|
||
|
|
if all(0x20 <= b < 0x7f or b == 0 for b in data[:16]):
|
||
|
|
s += 5
|
||
|
|
# Low entropy check: real key material should not be all-zero or all-same
|
||
|
|
if len(set(data)) > 20:
|
||
|
|
s += 2
|
||
|
|
# Protobuf-like: field 1 or 2 as first byte
|
||
|
|
if data[0] in (0x08, 0x0a, 0x10, 0x12) and data[1] != 0:
|
||
|
|
s += 3
|
||
|
|
return s
|
||
|
|
|
||
|
|
hits = []
|
||
|
|
print(f"Scanning {len(cdm):,} bytes with CBC (iv=ay64.dat5)...")
|
||
|
|
|
||
|
|
for i in range(0, len(cdm) - 16, 1):
|
||
|
|
key = cdm[i:i+16]
|
||
|
|
if len(set(key)) < 5:
|
||
|
|
continue
|
||
|
|
try:
|
||
|
|
pt = AES.new(key, AES.MODE_CBC, iv).decrypt(ct)
|
||
|
|
s = score(pt)
|
||
|
|
if s >= 5:
|
||
|
|
# Verify HMAC-SHA256(key, ct) == mac if possible
|
||
|
|
h = hmac.new(key, ct, hashlib.sha256).digest()
|
||
|
|
hmac_ok = hmac.compare_digest(h, mac)
|
||
|
|
print(f"offset={i:#010x} score={s} hmac={'OK' if hmac_ok else '--'} key={key.hex()}")
|
||
|
|
print(f" pt: {pt.hex()}")
|
||
|
|
hits.append((s, i, key, pt, hmac_ok))
|
||
|
|
if hmac_ok:
|
||
|
|
print(" *** HMAC VERIFIED — THIS IS THE KEY ***")
|
||
|
|
except Exception:
|
||
|
|
pass
|
||
|
|
|
||
|
|
# Also try: HMAC-SHA256(key, iv+ct) == mac
|
||
|
|
try:
|
||
|
|
key = cdm[i:i+16]
|
||
|
|
h2 = hmac.new(key, iv + ct, hashlib.sha256).digest()
|
||
|
|
if hmac.compare_digest(h2, mac):
|
||
|
|
pt = AES.new(key, AES.MODE_CBC, iv).decrypt(ct)
|
||
|
|
print(f"*** HMAC(iv+ct) VERIFIED at offset {i:#010x} key={key.hex()} ***")
|
||
|
|
print(f" pt: {pt.hex()}")
|
||
|
|
except Exception:
|
||
|
|
pass
|
||
|
|
|
||
|
|
if i % 500000 == 0:
|
||
|
|
print(f" {i:,}/{len(cdm):,} ({100*i/len(cdm):.1f}%)")
|
||
|
|
|
||
|
|
print(f"\nDone. {len(hits)} candidate(s) found.")
|
||
|
|
if hits:
|
||
|
|
hits.sort(key=lambda x: (-x[0], -x[4]))
|
||
|
|
print("\nTop candidates by score:")
|
||
|
|
for s, i, k, pt, hok in hits[:10]:
|
||
|
|
print(f" score={s} offset={i:#010x} hmac={'OK' if hok else '--'} key={k.hex()}")
|
||
|
|
print(f" pt={pt.hex()}")
|