Last active
June 23, 2018 00:04
-
-
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!
This file contains hidden or 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
#!/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