cscg is loooong
This commit is contained in:
60
2026/cscg/forensics/slippery-slope/recover_tk.py
Normal file
60
2026/cscg/forensics/slippery-slope/recover_tk.py
Normal file
@@ -0,0 +1,60 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
LDN Data Frame Key Derivation for Mario Maker 2
|
||||
Requires: pip install pycryptodome scapy
|
||||
"""
|
||||
|
||||
import hashlib
|
||||
from Crypto.Cipher import AES
|
||||
|
||||
# ── Nintendo key constants ────────────────────────────────────────────────────
|
||||
MASTER_KEY_00 = bytes.fromhex("c2caaff089b9aed55694876055271c7d")
|
||||
AES_KEK_GENERATION_SRC = bytes.fromhex("4d870986c45d20722fba1053da92e8a9")
|
||||
AES_KEY_GENERATION_SRC = bytes.fromhex("89615ee05c31b6805fe58f3da24f7aa8")
|
||||
DATA_INPUT_KEY = bytes.fromhex("f1e7018419a84f711da714c2cf919c9c")
|
||||
NINTENDO_OUI = bytes([0x00, 0x22, 0xAA])
|
||||
|
||||
# ── Game-specific ─────────────────────────────────────────────────────────────
|
||||
PASSWORD = b"LunchPack2DefaultPhrase"
|
||||
|
||||
# ── AES-ECB helper ───────────────────────────────────────────────────────────
|
||||
def aes_ecb_decrypt(key: bytes, data: bytes) -> bytes:
|
||||
return AES.new(key, AES.MODE_ECB).decrypt(data)
|
||||
|
||||
# ── Step-by-step key derivation ──────────────────────────────────────────────
|
||||
def derive_data_key(server_random: bytes, password: bytes) -> bytes:
|
||||
assert len(server_random) == 16, "server_random must be 16 bytes"
|
||||
|
||||
# Step 1: kek_key
|
||||
kek_key = aes_ecb_decrypt(MASTER_KEY_00, AES_KEK_GENERATION_SRC)
|
||||
print(f"[1] kek_key: {kek_key.hex()}")
|
||||
|
||||
# Step 2: access_key (decrypt the data input key with kek_key)
|
||||
access_key = aes_ecb_decrypt(kek_key, DATA_INPUT_KEY)
|
||||
print(f"[2] access_key: {access_key.hex()}")
|
||||
|
||||
# Step 3: generation_key
|
||||
generation_key = aes_ecb_decrypt(access_key, AES_KEY_GENERATION_SRC)
|
||||
print(f"[3] generation_key: {generation_key.hex()}")
|
||||
|
||||
# Step 4: hash input buffer = server_random || password
|
||||
buf = server_random + password
|
||||
hash_input = hashlib.sha256(buf).digest()[:16]
|
||||
print(f"[4] SHA256(buf)[0:16]: {hash_input.hex()}")
|
||||
|
||||
# Step 4: final data frame key
|
||||
data_key = aes_ecb_decrypt(generation_key, hash_input)
|
||||
print(f"[★] data_frame_key: {data_key.hex()}")
|
||||
|
||||
return data_key
|
||||
|
||||
# ── Main ─────────────────────────────────────────────────────────────────────
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
pcap_path = sys.argv[1] if len(sys.argv) > 1 else "./capture.pcapng"
|
||||
|
||||
server_random = bytes.fromhex("aaec2d390000000005180000796265e7")
|
||||
|
||||
data_key = derive_data_key(bytes.fromhex("d33f9c317a7f2b77a27b68e40f596f89"), PASSWORD)
|
||||
|
||||
print(f"tk: {data_key.hex()}")
|
||||
Reference in New Issue
Block a user