Skip to content

Instantly share code, notes, and snippets.

@hexkyz
Created May 30, 2020 18:38
Show Gist options
  • Save hexkyz/7bcef98fa103cfab74781243a5223ed7 to your computer and use it in GitHub Desktop.
Save hexkyz/7bcef98fa103cfab74781243a5223ed7 to your computer and use it in GitHub Desktop.
###############################################
# TX SX OS unpacker - by hexkyz and naehrwert #
###############################################
from Crypto.Cipher import AES
from Crypto.Util import Counter
import os
import struct
"""
typedef struct boot_dat_hdr
{
unsigned char magic[0x8];
unsigned char version[0x8];
unsigned char stage2_sha256[0x20];
unsigned int stage2_dst;
unsigned int stage2_size;
unsigned int stage2_enc;
unsigned char pad[0x10];
unsigned int app_offset;
unsigned int boot_dat_hdr_sig_offset;
unsigned int se_keyslot_mask;
unsigned int unk;
unsigned int boot_dat_hdr2_offset;
unsigned char pad2[0x80];
unsigned char boot_dat_hdr_sha256[0x20];
} boot_dat_hdr_t;
"""
"""
typedef struct boot_dat_hdr2
{
unsigned int stage3_size;
unsigned int stage3_dst;
unsigned char rnd[0x8];
unsigned char stage3_ctr[0x10];
unsigned char stage3_sha256[0x20];
unsigned char rnd2[0xA0];
unsigned char boot_dat_hdr2_sha256[0x20];
unsigned char stage3_sig[0x100];
} boot_dat_hdr2_t;
"""
def aes_ctr_dec(buf, key, iv):
ctr = Counter.new(128, initial_value=long(iv.encode('hex'), 16))
return AES.new(key, AES.MODE_CTR, counter=ctr).encrypt(buf)
def get_ver_int(boot_ver):
if (boot_ver[1] == 0x30) and (boot_ver[0] == 0x2E302E33): # TX BOOT 3.0.0
return 300
else:
return 0
f = open("boot.dat", "rb")
b = f.read()
f.close()
version = get_ver_int(struct.unpack("II", b[0x08:0x10]))
s2_base, s2_size = struct.unpack("II", b[0x30:0x38])
s3_size, s3_base = struct.unpack("II", b[0x10D00:0x10D08])
s2_key = "47E6BFB05965ABCD00E2EE4DDF540261".decode("hex")
s2_ctr = "8E4C7889CBAE4A3D64797DDA84BDB086".decode("hex")
s3_key = "D548D48DBA299604CED1AE5B47D8429C".decode("hex")
s3_ctr = "928EB1B748DCBAFE8A126652C4FC1E07".decode("hex")
if version == 300:
fb_key = "6C29FE5E6963F14599B444501B242E70".decode("hex")
fb_ctr = "098E05B944A507C158879DAF6EA3A434".decode("hex")
fb_off = 0x1F7FF0
fb_size = 0x3C0000
fb_base = 0xF0000000
payload81_key = "B014D988F29B66AE3BAA3D405976475F".decode("hex")
payload81_ctr = "FB8A0A8D9768718791C3AB92037A82CC".decode("hex")
payload81_off = 0x213F0
payload81_size = 0x1D6C00
payload81_base = 0x81000000
payload90_key = "BD1178804099CC6B30480210BBB361F1".decode("hex")
payload90_ctr = "12C81E0C4E048E19B3E00C4A3A1B429D".decode("hex")
payload90_off = 0xC95AE0
payload90_size = 0x124B40
payload90_base = 0x90000000
payload90dbg_key = "6364C5C17C5B1CE8B088E9037C52C562".decode("hex")
payload90dbg_ctr = "E195207627D5561D9284DD3D2AEF6F67".decode("hex")
payload90dbg_off = 0xADD3F0
payload90dbg_size = 0xF5D70
payload90dbg_base = 0x90000000
payload98_key = "0ACC721CE4EEADA7FB74F98B3AB45B08".decode("hex")
payload98_ctr = "AC070C079E3587F3514A85277CC47264".decode("hex")
payload98_off = 0xDBA620
payload98_size = 0x17B410
payload98_base = 0x98000000
payloadA0_key = "D26498CB2C9D4E433C8AD6EC00104894".decode("hex")
payloadA0_ctr = "907260F59568FC28EB59D420191222ED".decode("hex")
payloadA0_off = 0xF35A30
payloadA0_size = 0x27B150
payloadA0_base = 0xA0000000
bootloader_key = "F614AB3D734F2DADF2E13EDF508ED384".decode("hex")
bootloader_ctr = "E73CDCE7620DF0154BF110508C14D406".decode("hex")
bootloader_off = 0x5B7FF0
bootloader_size = 0x49000
bootloader_base = 0x88000000
assets_key = "466415715075F928304B55E971DDD294".decode("hex")
assets_ctr = "B80834A0BB0955A8B3CDBFE678F3FD3A".decode("hex")
assets_off = 0x600FF0
assets_size = 0x4DC400
assets_base = 0x88049000
fw_key = "43DB6A9C95B7ACE8F2342D2B066BB265".decode("hex")
fw_ctr = "7E460353C80828BD68F64795D64E8A5C".decode("hex")
fw_off = 0
fw_size = 0x27B150
else:
exit()
# Create main folder
if not os.path.exists("./sxos/"):
os.mkdir("./sxos/")
os.chdir("./sxos/")
# Create folder for the initial boot files
if not os.path.exists("./init/"):
os.mkdir("./init/")
os.chdir("./init/")
# Decrypt Stage2 IRAM payload
f = open("stage2_{0:08X}.bin".format(s2_base), "wb")
f.write(aes_ctr_dec(b[0x100:0x100+s2_size], s2_key, s2_ctr))
f.close()
# Decrypt Stage3 IRAM payload
f = open("stage3_{0:08X}.bin".format(s3_base), "wb")
f.write(aes_ctr_dec(b[0x10F00:0x10F00+s3_size], s3_key, s3_ctr))
f.close()
# Decrypt initial framebuffer binary
f = open("fb_{0:08X}.bin".format(fb_base), "wb")
f.write(aes_ctr_dec(b[fb_off:fb_off+fb_size], fb_key, fb_ctr))
f.close()
# Create folder for the obfuscation payloads
if not os.path.exists("../payloads/"):
os.mkdir("../payloads/")
os.chdir("../payloads/")
# Decrypt first layer's obfuscation payload
f = open("payload_{0:08X}.bin".format(payload81_base), "wb")
f.write(aes_ctr_dec(b[payload81_off:payload81_off+payload81_size], payload81_key, payload81_ctr))
f.close()
# Decrypt second layer's obfuscation payload
f = open("payload_{0:08X}.bin".format(payload90_base), "wb")
f.write(aes_ctr_dec(b[payload90_off:payload90_off+payload90_size], payload90_key, payload90_ctr))
f.close()
# Decrypt third layer's obfuscation payload
f = open("payload_{0:08X}.bin".format(payload98_base), "wb")
f.write(aes_ctr_dec(b[payload98_off:payload98_off+payload98_size], payload98_key, payload98_ctr))
f.close()
# Decrypt fourth layer's obfuscation payload
f = open("payload_{0:08X}.bin".format(payloadA0_base), "wb")
f.write(aes_ctr_dec(b[payloadA0_off:payloadA0_off+payloadA0_size], payloadA0_key, payloadA0_ctr))
f.close()
# Create folder for the bootloader
if not os.path.exists("../bootloader/"):
os.mkdir("../bootloader/")
os.chdir("../bootloader/")
# Decrypt SX OS bootloader's code and assets
f = open("bootloader_{0:08X}.bin".format(bootloader_base), "wb")
f.write(aes_ctr_dec(b[bootloader_off:bootloader_off+bootloader_size], bootloader_key, bootloader_ctr))
f.write(aes_ctr_dec(b[assets_off:assets_off+assets_size], assets_key, assets_ctr))
f.close()
os.chdir("../payloads/")
# Open final firmware binary (encrypted)
f = open("payload_A0000000.bin", "rb")
d = f.read()
f.close()
# Decrypt final firmware binary
f = open("payload_A0000000_dec.bin", "wb")
f.write(aes_ctr_dec(d[fw_off:fw_off+fw_size], fw_key, fw_ctr))
f.close()
# Open final firmware binary (decrypted)
f = open("payload_A0000000_dec.bin", "rb")
d = f.read()
f.close()
# Create folder for the patcher binaries
if not os.path.exists("../patcher/"):
os.mkdir("../patcher/")
os.chdir("../patcher/")
patcher_size = struct.unpack("I", d[0x00:0x04])[0]
patcher_off = struct.unpack("I", d[0x04:0x08])[0]
patcher_base = struct.unpack("I", d[0x08:0x0C])[0]
patcher_crc = struct.unpack("I", d[0x0C:0x10])[0]
patcher_hash = struct.unpack("8I", d[0x10:0x30])
# Parse and store the first patcher
f = open("patcher_{0:08X}.bin".format(patcher_base), "wb")
f.write(d[patcher_off:patcher_off+patcher_size])
f.close()
patcher_size = struct.unpack("I", d[0x30:0x34])[0]
patcher_off = struct.unpack("I", d[0x34:0x38])[0]
patcher_base = struct.unpack("I", d[0x38:0x3C])[0]
patcher_crc = struct.unpack("I", d[0x3C:0x40])[0]
patcher_hash = struct.unpack("8I", d[0x40:0x60])
# Parse and store the second patcher
f = open("patcher_{0:08X}.bin".format(patcher_base), "wb")
f.write(d[patcher_off:patcher_off+patcher_size])
f.close()
patcher_size = struct.unpack("I", d[0x60:0x64])[0]
patcher_off = struct.unpack("I", d[0x64:0x68])[0]
patcher_base = struct.unpack("I", d[0x68:0x6C])[0]
patcher_crc = struct.unpack("I", d[0x6C:0x70])[0]
patcher_hash = struct.unpack("8I", d[0x70:0x90])
# Parse and store the third patcher
f = open("patcher_{0:08X}.bin".format(patcher_base), "wb")
f.write(d[patcher_off:patcher_off+patcher_size])
f.close()
# Create folder for the actual firmware binaries
if not os.path.exists("../firmware/"):
os.mkdir("../firmware/")
os.chdir("../firmware/")
kip_size = struct.unpack("I", d[0x90:0x94])[0]
kip_off = struct.unpack("I", d[0x94:0x98])[0]
kip_base = struct.unpack("I", d[0x98:0x9C])[0]
kip_crc = struct.unpack("I", d[0x9C:0xA0])[0]
kip_hash = struct.unpack("8I", d[0xA0:0xC0])
# Parse and store the Loader KIP1
f = open("kip_{0:08X}.bin".format(kip_base), "wb")
f.write(d[kip_off:kip_off+kip_size])
f.close()
# Extract application files
app_region_off = struct.unpack("I", b[0x4C:0x50])[0]
app_region_size = (len(b) - app_region_off)
app_region = aes_ctr_dec(b[app_region_off:app_region_off+app_region_size], fw_key, fw_ctr)
app_header_off = 0
app_header_size = 0x310
app_header = app_region[app_header_off:app_header_size]
app_entry_count = struct.unpack("I", app_header[0x00:0x04])[0]
app_entry_size = 0x30
# Create folder for the application binaries
if not os.path.exists("../apps/"):
os.mkdir("../apps/")
os.chdir("../apps/")
# Store the application region header
f = open("app_header.bin", "wb")
f.write(app_header)
f.close()
# Parse and store the applications
for i in xrange(app_entry_count):
app_magic = struct.unpack("2I", app_header[0x10 + i * app_entry_size:0x18 + i * app_entry_size])
app_hash = struct.unpack("8I", app_header[0x18 + i * app_entry_size:0x38 + i * app_entry_size])
app_off = struct.unpack("I", app_header[0x38 + i * app_entry_size:0x3C + i * app_entry_size])[0]
app_size = struct.unpack("I", app_header[0x3C + i * app_entry_size:0x40 + i * app_entry_size])[0]
# ROMMENU
if ((app_magic[0] == 0x4D454E55) and (app_magic[1] == 0x00524F4D)):
f = open("ROMMENU.bin", "wb")
f.write(app_region[app_off:app_off+app_size])
f.close()
# HBMENU
if ((app_magic[0] == 0x4D454E55) and (app_magic[1] == 0x00004842)):
f = open("HBMENU.bin", "wb")
f.write(app_region[app_off:app_off+app_size])
f.close()
# MLBIN
if ((app_magic[0] == 0x4C42494E) and (app_magic[1] == 0x0000004D)):
f = open("MLBIN.bin", "wb")
f.write(app_region[app_off:app_off+app_size])
f.close()
# MLMETA
if ((app_magic[0] == 0x4D455441) and (app_magic[1] == 0x00004D4C)):
f = open("MLMETA.bin", "wb")
f.write(app_region[app_off:app_off+app_size])
f.close()
# HBLBIN
if ((app_magic[0] == 0x4C42494E) and (app_magic[1] == 0x00004842)):
f = open("HBLBIN.bin", "wb")
f.write(app_region[app_off:app_off+app_size])
f.close()
# HBLMETA
if ((app_magic[0] == 0x4D455441) and (app_magic[1] == 0x0048424C)):
f = open("HBLMETA.bin", "wb")
f.write(app_region[app_off:app_off+app_size])
f.close()
# FTLBIN
if ((app_magic[0] == 0x4C42494E) and (app_magic[1] == 0x00004654)):
f = open("FTLBIN.bin", "wb")
f.write(app_region[app_off:app_off+app_size])
f.close()
# FTLMETA
if ((app_magic[0] == 0x4D455441) and (app_magic[1] == 0x0046544C)):
f = open("FTLMETA.bin", "wb")
f.write(app_region[app_off:app_off+app_size])
f.close()
# CREPBIN
if ((app_magic[0] == 0x5042494E) and (app_magic[1] == 0x00435245)):
f = open("CREPBIN.bin", "wb")
f.write(app_region[app_off:app_off+app_size])
f.close()
# CREPMETA
if ((app_magic[0] == 0x4D455441) and (app_magic[1] == 0x43524550)):
f = open("CREPMETA.bin", "wb")
f.write(app_region[app_off:app_off+app_size])
f.close()
# ECLBIN
if ((app_magic[0] == 0x4C42494E) and (app_magic[1] == 0x00004543)):
f = open("ECLBIN.bin", "wb")
f.write(app_region[app_off:app_off+app_size])
f.close()
# ECLMETA
if ((app_magic[0] == 0x4D455441) and (app_magic[1] == 0x0045434C)):
f = open("ECLMETA.bin", "wb")
f.write(app_region[app_off:app_off+app_size])
f.close()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment