Created
May 30, 2020 18:38
-
-
Save hexkyz/7bcef98fa103cfab74781243a5223ed7 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
############################################### | |
# 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