Skip to content

Instantly share code, notes, and snippets.

@ihaveamac
Created December 5, 2016 21:15
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ihaveamac/65fe622e99f6f5e6c0b5eb7acdad2ad7 to your computer and use it in GitHub Desktop.
Save ihaveamac/65fe622e99f6f5e6c0b5eb7acdad2ad7 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
import binascii
import struct
import sys
from Crypto.Cipher import AES
if len(sys.argv) < 3:
print("usage: cia_extract_deckey.py <cia-file> <decrypted-titlekey>")
sys.exit(1)
dectkey = binascii.unhexlify(sys.argv[2])
readsize = 8 * 1024 * 1024
# from http://pastebin.com/K3pVsnkq
def to_bytes(num):
numstr = b''
tmp = num
while len(numstr) < 16:
numstr += bytes([tmp & 0xFF])
tmp >>= 8
return numstr[::-1]
# based on http://stackoverflow.com/questions/1766535/bit-hack-round-off-to-multiple-of-8/1766566#1766566
def roundup(x):
return (x + 63) >> 6 << 6
def showprogress(val, maxval):
# crappy workaround I bet, but print() didn't do what I wanted
minval = min(val, maxval)
sys.stdout.write("\r {:>5.1f}% {:>10} / {}".format((minval / maxval) * 100, minval, maxval))
sys.stdout.flush()
with open(sys.argv[1], 'rb') as cia:
# assuming the certchain and ticket never change size, which they shouldn't
cia.seek(0x10)
content_offset = roundup(0x2DC0 + struct.unpack("<I", cia.read(4))[0])
cia.seek(0x2F9E)
content_count = struct.unpack(">H", cia.read(2))[0]
print("Content count: {}".format(content_count))
cia.seek(0x38C4)
chunk_records = cia.read(0x30 * content_count)
for c in range(content_count):
content_id = binascii.hexlify(chunk_records[(c * 0x30):(c * 0x30) + 4]).decode('utf-8')
content_index = chunk_records[(c * 0x30) + 4:(c * 0x30) + 6]
content_type = struct.unpack(">H", chunk_records[(c * 0x30) + 6:(c * 0x30) + 8])[0]
content_size = struct.unpack(">Q", chunk_records[(c * 0x30) + 8:(c * 0x30) + 16])[0]
if content_type ^ 1:
print("Index {} with ID {} is already decrypted.".format(content_index, content_id))
content_offset += content_size
continue
print("Decrypting index {}, ID {}, size {:X}".format(binascii.hexlify(content_index).decode('utf-8'), content_id, content_size))
cia.seek(content_offset)
cipher = AES.new(dectkey, AES.MODE_CBC, content_index + (b'\0' * 14))
left = content_size
with open("contents.{}.{}".format(binascii.hexlify(content_index).decode('utf-8'), content_id), 'wb') as content:
while left > 0:
toread = min(readsize, left)
content.write(cipher.decrypt(cia.read(toread)))
content_offset += toread
left -= readsize
showprogress(content_size - left, content_size)
print("")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment