Skip to content

Instantly share code, notes, and snippets.

@GoobyCorp
Last active June 23, 2018 00:04
Show Gist options
  • Save GoobyCorp/e9f99b97a81ac5e6c501cac1b8f6ec09 to your computer and use it in GitHub Desktop.
Save GoobyCorp/e9f99b97a81ac5e6c501cac1b8f6ec09 to your computer and use it in GitHub Desktop.
A script to unpack Team Xecuter's SX OS. THIS IS NOT A CRACK!
#!/usr/bin/python3
#built-in's
from ctypes import *
from os import mkdir
from io import BytesIO
from os.path import isdir, isfile, join
from binascii import hexlify as _hexlify, unhexlify
#packages
#pycryptodome
#pip install pycryptodome
from Cryptodome.Cipher import AES
from Cryptodome.Hash import SHA256
from Cryptodome.Util import Counter
from Cryptodome.PublicKey import RSA
from Cryptodome.Util.number import bytes_to_long
#pillow
#pip install pillow
from PIL import Image
#directories
OUTPUT_DIR = "output"
LICENSE_DIR = "license"
#files
BOOT_FILE = "boot_v1_1.dat"
RSA_KEY_FILE = "key.pem"
FB_IMG_FILE = "fb.png"
class rsa_pub_key_2048(Structure):
_fields_ = [
("pub_exp", c_uint32),
("modulus", (c_ubyte * 0x100))
]
class boot_dat_ident(Structure):
_fields_ = [
("name", (c_ubyte * 0x07)),
("pad", (c_ubyte * 0x05)),
("version", (c_ubyte * 0x04))
]
class boot_dat_hdr(Structure):
_fields_ = [
("ident", boot_dat_ident),
("s2_sha", (c_ubyte * 0x20)),
("s2_dst", c_uint32),
("s2_size", c_uint32),
("s2_enc", c_uint32),
("pad", (c_ubyte * 0x10)),
("s3_size", c_uint32),
("pad2", (c_ubyte * 0x90)),
("hdr_sha", (c_ubyte * 0x20))
]
def hexlify(b: (bytes, bytearray)) -> str:
return _hexlify(b).decode("utf8")
def aes_ctr_dec(data: (bytes, bytearray), key: (bytes, bytearray), ctr_val: (bytes, bytearray)) -> (bytes, bytearray):
ctr = Counter.new(128, initial_value=bytes_to_long(ctr_val))
return AES.new(key, AES.MODE_CTR, counter=ctr).decrypt(data)
if __name__ == "__main__":
#create the output folder if it's not there
if not isdir(OUTPUT_DIR):
mkdir(OUTPUT_DIR)
print("Created \"output\" directory.")
#make sure the boot file exists
assert isfile(BOOT_FILE), "boot.dat is missing"
with open(BOOT_FILE, "rb") as boot_file:
#read the header
hdr = boot_dat_hdr.from_buffer_copy(boot_file.read(sizeof(boot_dat_hdr)))
#read stage 2 right after the header
stage_two = boot_file.read(hdr.s2_size)
#seek back to the beginning and hash the header
boot_file.seek(0)
hdr_sha = SHA256.new(boot_file.read(sizeof(boot_dat_hdr) - 0x20)).digest()
#check the header's SHA256
assert bytes(hdr.hdr_sha) == hdr_sha, "Invalid header SHA256"
#seek back to the end of the header
boot_file.seek(sizeof(boot_dat_hdr))
#convert the boot.dat's identifier version to UTF-8
utf_ver = bytes(hdr.ident.version).decode("utf8")
if utf_ver == "V1.0":
#Stage 2
s2_key = unhexlify("47E6BFB05965ABCD00E2EE4DDF540261")
s2_ctr = unhexlify("8E4C7889CBAE4A3D64797DDA84BDB086")
#ARM64
arm64_key = unhexlify("35D8FFC4AA1BAB9514825EB0658FB493")
arm64_ctr = unhexlify("C38EA26FF3CCE98FD8D5ED431D9D5B94")
arm64_off = 0x53BAB0
arm64_size = 0x36370
arm64_base = 0x80FFFE00
#FB
fb_key = unhexlify("E2AC05206A701C9AA514D2B2B7C9F395")
fb_ctr = unhexlify("46FAB59AF0E469EF116614DEC366D15F")
fb_off = 0x17BAB0
fb_size = 0x3C0000
fb_base = 0xF0000000
#Data
data_key = unhexlify("030D865B7E458B10AD5706F6E227F4EB")
data_ctr = unhexlify("AFFC93692EBD2E3D252339F01E03416B")
data_off = 0x5F40
data_size = 0x175B70
data_base = 0x80000000
elif utf_ver == "V1.1":
#Stage 2
s2_key = unhexlify("47E6BFB05965ABCD00E2EE4DDF540261")
s2_ctr = unhexlify("8E4C7889CBAE4A3D64797DDA84BDB086")
#ARM64
arm64_key = unhexlify("51A39F0B46BAE4691AD39A698146E865")
arm64_ctr = unhexlify("7A307ED7F1ECC792F0E821ECD6999853")
arm64_off = 0x53BAE0
arm64_size = 0x36370
arm64_base = 0x80FFFE00
#FB
fb_key = unhexlify("27BABEE3DCFEF100C744A2388B57E957")
fb_ctr = unhexlify("0B88AC25AFAF9B92D81372331AD66E24")
fb_off = 0x17BAE0
fb_size = 0x3C0000
fb_base = 0xF0000000
#Data
data_key = unhexlify("8D6FEABE0F3936145A474D3F05D33679")
data_ctr = unhexlify("2846EFA9DACB065C51C71C154F0E9EA2")
data_off = 0x5F50
data_size = 0x175B90
data_base = 0x80000000
else:
print("Unsupported version!")
exit(0)
#Stage 2
s2_dec = aes_ctr_dec(stage_two, s2_key, s2_ctr)
s2_sha = SHA256.new(s2_dec).digest()
#make sure the stage 2 hash is valid
assert bytes(hdr.s2_sha) == s2_sha, "Invalid stage 2 hash"
s2_name = "stage2_{0:08X}.bin".format(hdr.s2_dst)
with open(join(OUTPUT_DIR, s2_name), "wb") as s2_file:
s2_file.write(s2_dec)
print("Wrote stage 2 to " + s2_name)
#ARM64
boot_file.seek(arm64_off)
arm64_dec = aes_ctr_dec(boot_file.read(arm64_size), arm64_key, arm64_ctr)
arm64_name = "arm64_{0:08X}.bin".format(arm64_base)
with open(join(OUTPUT_DIR, arm64_name), "wb") as arm64_file:
arm64_file.write(arm64_dec)
print("Wrote arm64 to " + arm64_name)
#FB
boot_file.seek(fb_off)
fb_dec = aes_ctr_dec(boot_file.read(fb_size), fb_key, fb_ctr)
fb_name = "fb_{0:08X}.bin".format(fb_base)
with open(join(OUTPUT_DIR, fb_name), "wb") as fb_file:
fb_file.write(fb_dec)
print("Wrote raw FB to " + fb_name)
#create PNG from the FB
assert int(len(fb_dec) / 4) == (1280 * 768), "Invalid FB size"
fb_img = Image.frombytes("RGBA", (768, 1280), fb_dec, "raw")
fb_img = fb_img.rotate(-90, expand=True)
fb_img.save(join(OUTPUT_DIR, FB_IMG_FILE))
fb_img.close()
print("Wrote FB PNG to " + FB_IMG_FILE)
#Data
boot_file.seek(data_off)
data_dec = aes_ctr_dec(boot_file.read(data_size), data_key, data_ctr)
data_name = "data_{0:08X}.bin".format(data_base)
with open(join(OUTPUT_DIR, data_name), "wb") as data_file:
data_file.write(data_dec)
print("Wrote data to " + data_name)
#RSA key within data
rsa_key_offs = data_dec.find(b"\x01\x00\x01\x00")
with BytesIO(data_dec) as bio:
bio.seek(rsa_key_offs)
rsa_key_data = bio.read(0x104)
rsa_key_struct = rsa_pub_key_2048.from_buffer_copy(rsa_key_data)
rsa_key = RSA.construct((bytes_to_long(bytes(rsa_key_struct.modulus)), rsa_key_struct.pub_exp))
with open(join(OUTPUT_DIR, RSA_KEY_FILE), "wb") as rsa_file:
rsa_file.write(rsa_key.export_key())
print("Wrote 2048-bit RSA public key to " + RSA_KEY_FILE)
print("Done!")
#extra shit that won't lead anywhere
"""
bio = BytesIO(data_dec)
bio.seek(RSA_KEY_OFFS)
rsa_key_struct = rsa_pub_key_2048.from_buffer_copy(bio.read(sizeof(rsa_pub_key_2048)))
#create RSA public key from the key in data
rsa_pub_key = RSA.construct((bytes_to_long(bytes(rsa_key_struct.modulus)), rsa_key_struct.pub_exp))
rsa_signer_v15 = PKCS1_v1_5_Signer.new(rsa_pub_key)
rsa_signer_pss = PKCS1_PSS.new(rsa_pub_key)
rsa_cipher = PKCS1_v1_5_Cipher.new(rsa_pub_key)
#generate license request
redeem_code = "(redacted)"
cf_hex = "(redacted)"
cf = unhexlify(cf_hex.replace("-", ""))
lic_req = SHA256.new(cf).digest()
lic_req_full = lic_req + (b"\x00" * 0x20)
"""
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment