diff --git a/U8.py b/U8.py index f838ae8..6d1b6c9 100644 --- a/U8.py +++ b/U8.py @@ -38,7 +38,7 @@ class U8(): if(os.path.isdir(file)): node.type = 0x0100 - self.data_offset = recursion + node.data_offset = recursion - 1 recursion += 1 files = sorted(os.listdir(file)) #if(sorted(files) == ["banner.bin", "icon.bin", "sound.bin"]): @@ -60,10 +60,9 @@ class U8(): data = f.read() f.close() sz = len(data) - while len(data) % 32 != 0: - data += "\x00" + data += "\x00" * (align(sz, 64) - sz) + node.data_offset = len(self.data) self.data += data - node.data_offset = len(data) node.size = sz node.type = 0x0000 if(is_root != 1): @@ -74,11 +73,11 @@ class U8(): This creates valid U8 archives for all purposes.""" header = self.U8Header() - self.rootnode = self.U8Node() + rootnode = self.U8Node() header.tag = "U\xAA8-" header.rootnode_offset = 0x20 - header.zeroes = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + header.zeroes = "\x00" * 16 self.nodes = [] self.strings = "\x00" @@ -88,13 +87,16 @@ class U8(): self._pack(".", 0, 1) os.chdir(origdir) - header.header_size = (len(self.nodes) + 1) * len(self.rootnode) + len(self.strings) - header.data_offset = align(header.header_size + header.rootnode_offset, 0x40) - self.rootnode.size = len(self.nodes) + 1 - self.rootnode.type = 0x0100 + header.header_size = (len(self.nodes) + 1) * len(rootnode) + len(self.strings) + print "\nstff starts: " + str(align(header.header_size + header.rootnode_offset, 64)) + print header.header_size + header.rootnode_offset + header.data_offset = align(header.header_size + header.rootnode_offset, 64) + rootnode.size = len(self.nodes) + 1 + rootnode.type = 0x0100 - for node in self.nodes: - node.data_offset += header.data_offset + for i in range(len(self.nodes)): + if(self.nodes[i].type == 0x0000): + self.nodes[i].data_offset += header.data_offset if(fn == ""): if(self.f[len(self.f) - 4:] == "_out"): @@ -102,15 +104,16 @@ class U8(): else: fn = self.f - f = open(fn, "wb") - f.write(header.pack()) - f.write(self.rootnode.pack()) + fd = open(fn, "wb") + fd.write(header.pack()) + fd.write(rootnode.pack()) for node in self.nodes: - f.write(node.pack()) - f.write(self.strings) - f.write("\x00" * (header.data_offset - header.rootnode_offset - header.header_size)) - f.write(self.data) - f.close() + fd.write(node.pack()) + fd.write(self.strings) + fd.write("\x00" * (header.data_offset - header.rootnode_offset - header.header_size)) + print (header.data_offset - header.rootnode_offset - header.header_size) + fd.write(self.data) + fd.close() return fn @@ -136,7 +139,7 @@ class U8(): offset += len(rootnode) nodes = [] - for i in xrange(rootnode.size - 1): + for i in range(rootnode.size - 1): node = self.U8Node() node.unpack(data[offset:offset + len(node)]) offset += len(node) @@ -296,7 +299,7 @@ class IMET(): self.sizes = Struct.uint32[3] #icon, banner, sound self.unk2 = Struct.uint32 self.names = Struct.string(84, encoding = "utf-16-be", stripNulls = True)[7] - self.zeroes2 = Struct.uint8[904] + self.zeroes2 = Struct.uint8[840] self.hash = Struct.string(16) def __init__(self, f): self.f = f @@ -365,4 +368,16 @@ class IMET(): return "" imet.unpack(data[:len(imet)]) - return imet.names[1] + name = imet.names[1] + topop = [] + for i in range(len(name)): + if(name[i] == "\x00"): + topop.append(i) + name = list(name) + popped = 0 #don't ask me why I did this + for pop in topop: + name.pop(pop - popped) + popped += 1 + + name = ''.join(name) + return name diff --git a/nand.py b/nand.py index 3a3d1d2..156b5c8 100644 --- a/nand.py +++ b/nand.py @@ -6,7 +6,7 @@ from Crypto.Cipher import AES from Struct import Struct from common import * - +from title import * class NAND: @@ -95,9 +95,9 @@ class NAND: cmfp.write(value) cmfp.close() return cnt - def importTitle(self, prefix, tmd, tik): - """When passed a prefix (the directory to obtain the .app files from, sorted by content id), a TMD instance, and a Ticket instance, this will add that title to the NAND base folder specified in the constructor.""" - self.ES.AddTitleStart(tmd, None, None) + def importTitle(self, prefix, tmd, tik, is_decrypted = False, result_decrypted = False): + """When passed a prefix (the directory to obtain the .app files from, sorted by content id), a TMD instance, and a Ticket instance, this will add that title to the NAND base folder specified in the constructor. Unless is_decrypted is set, the contents are assumed to be encrypted. If result_decrypted is True, then the contents will not end up decrypted.""" + self.ES.AddTitleStart(tmd, None, None, is_decrypted, result_decrypted, use_version = True) self.ES.AddTitleTMD(tmd) self.ES.AddTicket(tik) contents = tmd.getContents() @@ -167,7 +167,7 @@ class ESClass: hashout += cmfp.read(20) cmfp.close() return hashout - def AddTitleStart(self, tmd, certs, crl): + def AddTitleStart(self, tmd, certs, crl, is_decrypted = False, result_decrypted = True, use_version = False): if(not os.path.isdir(self.f + "/title/%08x" % (tmd.tmd.titleid >> 32))): os.mkdir(self.f + "/title/%08x" % (tmd.tmd.titleid >> 32)) if(not os.path.isdir(self.f + "/title/%08x/%08x" % (tmd.tmd.titleid >> 32, tmd.tmd.titleid & 0xFFFFFFFF))): @@ -180,6 +180,9 @@ class ESClass: os.mkdir(self.f + "/ticket/%08x" % (tmd.tmd.titleid >> 32)) self.workingcids = array.array('L') self.wtitleid = tmd.tmd.titleid + self.is_decrypted = is_decrypted + self.result_decrypted = result_decrypted + self.use_version = use_version return def AddTicket(self, tik): """Adds ticket to the title being added.""" @@ -261,15 +264,26 @@ class ESClass: outfp = open(filestr, "wb") data = fp.read() titlekey = tik.getTitleKey() - tmpdata = Crypto().DecryptContent(titlekey, contents[idx].index, data) - if(Crypto().ValidateHash(tmpdata, contents[idx].hash) == 0): + if(self.is_decrypted): + tmpdata = data + else: + tmpdata = Crypto().DecryptContent(titlekey, contents[idx].index, data) + if(Crypto().ValidateSHAHash(tmpdata, contents[idx].hash) == 0): "Decryption failed! SHA1 mismatch." return -44 + if(self.result_decrypted != True): + if(self.is_decrypted): + tmpdata = Crypto().EncryptContent(titlekey, contents[idx].index, data) + else: + tmpdata = data + fp.close() outfp.write(tmpdata) outfp.close() - if(self.tmdadded): + if(self.tmdadded and self.use_version): tmd.rawdump(self.f + "/title/%08x/%08x/content/title.tmd.%d" % (self.wtitleid >> 32, self.wtitleid & 0xFFFFFFFF, tmd.tmd.title_version)) + elif(self.tmdadded): + tmd.rawdump(self.f + "/title/%08x/%08x/content/title.tmd" % (self.wtitleid >> 32, self.wtitleid & 0xFFFFFFFF)) if(self.ticketadded): tik.rawdump(self.f + "/ticket/%08x/%08x.tik" % (self.wtitleid >> 32, self.wtitleid & 0xFFFFFFFF)) self.AddTitleCancel() diff --git a/title.py b/title.py index 04d236d..24763c1 100644 --- a/title.py +++ b/title.py @@ -59,12 +59,12 @@ class Ticket: out += " Title ID: %08x-%08x\n" % (self.getTitleID() >> 32, self.getTitleID() & 0xFFFFFFFF) out += " Title key IV: " - out += hexdump(report, struct.pack(">Q", self.getTitleID()) + "\x00\x00\x00\x00\x00\x00\x00\x00") + out += hexdump(struct.pack(">Q", self.getTitleID()) + "\x00\x00\x00\x00\x00\x00\x00\x00") out += "\n" out += " Title key (encrypted): " out += hexdump(self.tik.enctitlekey) - report.write("\n") + out += "\n" out += " Title key (decrypted): " out += hexdump(self.getTitleKey()) @@ -153,11 +153,11 @@ class TMD: out = "" out += " TMD:\n" out += " Versions: (todo) %u, CA CRL (todo) %u, Signer CRL (todo) %u, System %u-%u\n" % (0, 0, 0, self.getIOSVersion() >> 32, self.getIOSVersion() & 0xFFFFFFFF) - out += " Title ID: %08x-%08x\n" % (item.titleid >> 32, item.titleid & 0xFFFFFFFF) - out += " Title Type: %u\n" % tmd.tmd.title_type - out += " Group ID: '%02u'\n" % tmd.tmd.group_id - out += " Access Rights: 0x%08x\n" % tmd.tmd.access_rights - out += " Title Version: 0x%04x\n" % item.version + out += " Title ID: %08x-%08x\n" % (self.getTitleID() >> 32, self.getTitleID() & 0xFFFFFFFF) + out += " Title Type: %u\n" % self.tmd.title_type + out += " Group ID: '%02u'\n" % self.tmd.group_id + out += " Access Rights: 0x%08x\n" % self.tmd.access_rights + out += " Title Version: 0x%04x\n" % self.tmd.title_version out += " Boot Index: %u\n" % self.getBootIndex() out += " Contents: \n"