Advertisement
Guest User

3ds_decrypt.py

a guest
Jul 24th, 2017
518
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 15.20 KB | None | 0 0
  1. from Crypto.Cipher import AES
  2. from Crypto.Util import Counter
  3. from sys import argv
  4. import struct
  5.  
  6. rol = lambda val, r_bits, max_bits: \
  7.     (val << r_bits%max_bits) & (2**max_bits-1) | \
  8.     ((val & (2**max_bits-1)) >> (max_bits-(r_bits%max_bits)))
  9.  
  10. def to_bytes(num):
  11.     numstr = ''
  12.     tmp = num
  13.     while len(numstr) < 16:
  14.         numstr += chr(tmp & 0xFF)
  15.         tmp >>= 8
  16.     return numstr[::-1]
  17.  
  18. # Setup Keys and IVs
  19. plain_counter = struct.unpack('>Q', '\x01\x00\x00\x00\x00\x00\x00\x00')
  20. exefs_counter = struct.unpack('>Q', '\x02\x00\x00\x00\x00\x00\x00\x00')
  21. romfs_counter = struct.unpack('>Q', '\x03\x00\x00\x00\x00\x00\x00\x00')
  22.  
  23. # Keys removed for reasons
  24. # Replace ?? with the appropriate values
  25. Constant = struct.unpack('>QQ', '\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??') # 3DS AES Hardware Constant
  26.  
  27. #Retail keys
  28. KeyX0x18 = struct.unpack('>QQ', '\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??') # KeyX 0x18 (New 3DS 9.3)
  29. KeyX0x1B = struct.unpack('>QQ', '\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??') # KeyX 0x1B (New 3DS 9.6)
  30. KeyX0x25 = struct.unpack('>QQ', '\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??') # KeyX 0x25 (> 7.x)
  31. KeyX0x2C = struct.unpack('>QQ', '\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??') # KeyX 0x2C (< 6.x)
  32.  
  33. # Dev Keys: (Uncomment these lines if your 3ds rom is encrypted with Dev Keys)
  34. #KeyX0x18 = struct.unpack('>QQ', '\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??') # Dev KeyX 0x18 (New 3DS 9.3)
  35. #KeyX0x1B = struct.unpack('>QQ', '\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??') # Dev KeyX 0x1B (New 3DS 9.6)
  36. #KeyX0x25 = struct.unpack('>QQ', '\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??') # Dev KeyX 0x25 (> 7.x)
  37. #KeyX0x2C = struct.unpack('>QQ', '\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??\x??') # Dev KeyX 0x2C (< 6.x)
  38.  
  39. with open(argv[1], 'rb') as f:
  40.     with open(argv[1], 'rb+') as g:
  41.         print argv[1] # Print the filename of the file being decrypted
  42.         f.seek(0x100) # Seek to start of NCSD header
  43.         magic = f.read(0x04)
  44.         if magic == "NCSD":
  45.  
  46.             f.seek(0x188)
  47.             ncsd_flags = struct.unpack('<BBBBBBBB', f.read(0x8))
  48.             sectorsize = 0x200 * (2**ncsd_flags[6])
  49.  
  50.             for p in xrange(8):
  51.                 f.seek((0x120) + (p*0x08)) # Seek to start of partition information, read offsets and lengths
  52.                 part_off = struct.unpack('<L', f.read(0x04))
  53.                 part_len = struct.unpack('<L', f.read(0x04))
  54.  
  55.                 f.seek(((part_off[0]) * sectorsize) + 0x188) # Get the partition flags to determine encryption type.
  56.                 partition_flags = struct.unpack('<BBBBBBBB', f.read(0x8))
  57.  
  58.                 if (partition_flags[7] & 0x04): # check if the 'NoCrypto' bit (bit 3) is set
  59.                     print ("Partition %1d: Already Decrypted?...") % (p)
  60.                 else:
  61.                     if (part_off[0] * sectorsize) > 0: # check if partition exists
  62.                        
  63.                         f.seek(((part_off[0]) * sectorsize) + 0x100) # Find partition start (+ 0x100 to skip NCCH header)
  64.                         magic = f.read(0x04)
  65.                        
  66.                         if magic == "NCCH": # check if partition is valid
  67.                             f.seek(((part_off[0]) * sectorsize) + 0x0)
  68.                             part_keyy = struct.unpack('>QQ', f.read(0x10)) # KeyY is the first 16 bytes of partition RSA-2048 SHA-256 signature
  69.  
  70.                             f.seek(((part_off[0]) * sectorsize) + 0x108)
  71.                             tid = struct.unpack('<Q', f.read(0x8)) # TitleID is used as IV joined with the content type.
  72.                             plain_iv = (tid[::] + plain_counter[::]) # Get the IV for plain sector (TitleID + Plain Counter)
  73.                             exefs_iv = (tid[::] + exefs_counter[::]) # Get the IV for ExeFS (TitleID + ExeFS Counter)
  74.                             romfs_iv = (tid[::] + romfs_counter[::]) # Get the IV for RomFS (TitleID + RomFS Counter)
  75.  
  76.                             f.seek((part_off[0] * sectorsize) + 0x160) # get exheader hash
  77.                             exhdr_sbhash = str("%016X%016X%016X%016X") % (struct.unpack('>QQQQ', f.read(0x20)))
  78.  
  79.                             f.seek((part_off[0] * sectorsize) + 0x180)
  80.                             exhdr_len = struct.unpack('<L', f.read(0x04)) # get extended header length
  81.  
  82.                             f.seek((part_off[0] * sectorsize) + 0x190)
  83.                             plain_off = struct.unpack('<L', f.read(0x04)) # get plain sector offset
  84.                             plain_len = struct.unpack('<L', f.read(0x04)) # get plain sector length
  85.  
  86.                             f.seek((part_off[0] * sectorsize) + 0x198)
  87.                             logo_off = struct.unpack('<L', f.read(0x04)) # get logo offset
  88.                             logo_len = struct.unpack('<L', f.read(0x04)) # get logo length
  89.  
  90.                             f.seek((part_off[0] * sectorsize) + 0x1A0)
  91.                             exefs_off = struct.unpack('<L', f.read(0x04)) # get exefs offset
  92.                             exefs_len = struct.unpack('<L', f.read(0x04)) # get exefs length
  93.  
  94.                             f.seek((part_off[0] * sectorsize) + 0x1B0)
  95.                             romfs_off = struct.unpack('<L', f.read(0x04)) # get romfs offset
  96.                             romfs_len = struct.unpack('<L', f.read(0x04)) # get romfs length
  97.  
  98.                             f.seek((part_off[0] * sectorsize) + 0x1C0) # get exefs hash
  99.                             exefs_sbhash = str("%016X%016X%016X%016X") % (struct.unpack('>QQQQ', f.read(0x20)))
  100.  
  101.                             f.seek((part_off[0] * sectorsize) + 0x1E0) # get romfs hash
  102.                             romfs_sbhash = str("%016X%016X%016X%016X") % (struct.unpack('>QQQQ', f.read(0x20)))
  103.  
  104.                             plainIV = long(str("%016X%016X") % (plain_iv[::]), 16)
  105.                             exefsIV = long(str("%016X%016X") % (exefs_iv[::]), 16)
  106.                             romfsIV = long(str("%016X%016X") % (romfs_iv[::]), 16)
  107.                             KeyY = long(str("%016X%016X") % (part_keyy[::]), 16)
  108.                             Const = long(str("%016X%016X") % (Constant[::]), 16)
  109.  
  110.                             KeyX2C = long(str("%016X%016X") % (KeyX0x2C[::]), 16)
  111.                             NormalKey2C = rol((rol(KeyX2C, 2, 128) ^ KeyY) + Const, 87, 128)
  112.  
  113.                             if (partition_flags[3] == 0x00): # Uses Original Key
  114.                                 KeyX = long(str("%016X%016X") % (KeyX0x2C[::]), 16)
  115.                             elif (partition_flags[3] == 0x01): # Uses 7.x Key
  116.                                 KeyX = long(str("%016X%016X") % (KeyX0x25[::]), 16)
  117.                             elif (partition_flags[3] == 0x0A): # Uses New3DS 9.3 Key
  118.                                 KeyX = long(str("%016X%016X") % (KeyX0x18[::]), 16)
  119.                             elif (partition_flags[3] == 0x0B): # Uses New3DS 9.6 Key
  120.                                 KeyX = long(str("%016X%016X") % (KeyX0x1B[::]), 16)
  121.                             NormalKey = rol((rol(KeyX, 2, 128) ^ KeyY) + Const, 87, 128)
  122.  
  123.                             if (partition_flags[7] & 0x01): # fixed crypto key (aka 0-key)
  124.                                 NormalKey = 0x00
  125.                                 NormalKey2C = 0x00
  126.  
  127.                             if (exhdr_len[0] > 0):
  128.                                 # decrypt exheader
  129.                                 f.seek((part_off[0] + 1) * sectorsize)
  130.                                 g.seek((part_off[0] + 1) * sectorsize)
  131.                                 exhdr_filelen = 0x800
  132.                                 exefsctr2C = Counter.new(128, initial_value=(plainIV))
  133.                                 exefsctrmode2C = AES.new(to_bytes(NormalKey2C), AES.MODE_CTR, counter = exefsctr2C)
  134.                                 print ("Partition %1d ExeFS: Decrypting: ExHeader") % (p)
  135.                                 g.write(exefsctrmode2C.decrypt(f.read(exhdr_filelen)))
  136.  
  137.                             if (exefs_len[0] > 0):
  138.                                 # decrypt exefs filename table
  139.                                 f.seek((part_off[0] + exefs_off[0]) * sectorsize)
  140.                                 g.seek((part_off[0] + exefs_off[0]) * sectorsize)
  141.                                 exefsctr2C = Counter.new(128, initial_value=(exefsIV))
  142.                                 exefsctrmode2C = AES.new(to_bytes(NormalKey2C), AES.MODE_CTR, counter = exefsctr2C)
  143.                                 g.write(exefsctrmode2C.decrypt(f.read(sectorsize)))
  144.                                 print ("Partition %1d ExeFS: Decrypting: ExeFS Filename Table") % (p)
  145.  
  146.                                 if ( partition_flags[3] == 0x01 or partition_flags[3] == 0x0A or partition_flags[3] == 0x0B ):
  147.                                     code_filelen = 0
  148.                                     for j in xrange(10): # 10 exefs filename slots
  149.                                         # get filename, offset and length
  150.                                         f.seek(((part_off[0] + exefs_off[0]) * sectorsize) + j*0x10)
  151.                                         g.seek(((part_off[0] + exefs_off[0]) * sectorsize) + j*0x10)
  152.                                         exefs_filename = struct.unpack('<8s', g.read(0x08))
  153.                                         if str(exefs_filename[0]) == str(".code\x00\x00\x00"):
  154.                                             code_fileoff = struct.unpack('<L', g.read(0x04))
  155.                                             code_filelen = struct.unpack('<L', g.read(0x04))
  156.                                             datalenM = ((code_filelen[0]) / (1024*1024))
  157.                                             datalenB = ((code_filelen[0]) % (1024*1024))
  158.                                             ctroffset = ((code_fileoff[0] + sectorsize) / 0x10)
  159.                                             exefsctr = Counter.new(128, initial_value=(exefsIV + ctroffset))
  160.                                             exefsctr2C = Counter.new(128, initial_value=(exefsIV + ctroffset))
  161.                                             exefsctrmode = AES.new(to_bytes(NormalKey), AES.MODE_CTR, counter = exefsctr)
  162.                                             exefsctrmode2C = AES.new(to_bytes(NormalKey2C), AES.MODE_CTR, counter = exefsctr2C)
  163.                                             f.seek((((part_off[0] + exefs_off[0]) + 1) * sectorsize) + code_fileoff[0])
  164.                                             g.seek((((part_off[0] + exefs_off[0]) + 1) * sectorsize) + code_fileoff[0])                                        
  165.                                             if (datalenM > 0):
  166.                                                 for i in xrange(datalenM):
  167.                                                     g.write(exefsctrmode2C.encrypt(exefsctrmode.decrypt(f.read(1024*1024))))
  168.                                                     print ("\rPartition %1d ExeFS: Decrypting: %8s... %4d / %4d mb...") % (p, str(exefs_filename[0]), i, datalenM + 1),
  169.                                             if (datalenB > 0):
  170.                                                 g.write(exefsctrmode2C.encrypt(exefsctrmode.decrypt(f.read(datalenB))))
  171.                                             print ("\rPartition %1d ExeFS: Decrypting: %8s... %4d / %4d mb... Done!") % (p, str(exefs_filename[0]), datalenM + 1, datalenM + 1)
  172.  
  173.                                 # decrypt exefs
  174.                                 exefsSizeM = ((exefs_len[0] - 1) * sectorsize) / (1024*1024)
  175.                                 exefsSizeB = ((exefs_len[0] - 1) * sectorsize) % (1024*1024)
  176.                                 ctroffset = (sectorsize / 0x10)
  177.                                 exefsctr2C = Counter.new(128, initial_value=(exefsIV + ctroffset))
  178.                                 exefsctrmode2C = AES.new(to_bytes(NormalKey2C), AES.MODE_CTR, counter = exefsctr2C)
  179.                                 f.seek((part_off[0] + exefs_off[0] + 1) * sectorsize)
  180.                                 g.seek((part_off[0] + exefs_off[0] + 1) * sectorsize)
  181.                                 if (exefsSizeM > 0):
  182.                                     for i in xrange(exefsSizeM):
  183.                                         g.write(exefsctrmode2C.decrypt(f.read(1024*1024)))
  184.                                         print ("\rPartition %1d ExeFS: Decrypting: %4d / %4d mb") % (p, i, exefsSizeM + 1),
  185.                                 if (exefsSizeB > 0):
  186.                                     g.write(exefsctrmode2C.decrypt(f.read(exefsSizeB)))
  187.                                 print ("\rPartition %1d ExeFS: Decrypting: %4d / %4d mb... Done") % (p, exefsSizeM + 1, exefsSizeM + 1)
  188.              
  189.                             else:
  190.                                 print ("Partition %1d ExeFS: No Data... Skipping...") % (p)
  191.  
  192.                             if (romfs_off[0] != 0):
  193.                                 romfsSizeM = (romfs_len[0] * sectorsize) / (1024*1024)
  194.                                 romfsSizeB = (romfs_len[0] * sectorsize) % (1024*1024)
  195.  
  196.                                 romfsctr = Counter.new(128, initial_value=romfsIV)
  197.                                 romfsctrmode = AES.new(to_bytes(NormalKey), AES.MODE_CTR, counter = romfsctr)
  198.  
  199.                                 f.seek((part_off[0] + romfs_off[0]) * sectorsize)
  200.                                 g.seek((part_off[0] + romfs_off[0]) * sectorsize)
  201.                                 if (romfsSizeM > 0):
  202.                                     for i in xrange(romfsSizeM):
  203.                                         g.write(romfsctrmode.decrypt(f.read(1024*1024)))
  204.                                         print ("\rPartition %1d RomFS: Decrypting: %4d / %4d mb") % (p, i, romfsSizeM + 1),
  205.                                 if (romfsSizeB > 0):
  206.                                     g.write(romfsctrmode.decrypt(f.read(romfsSizeB)))
  207.                                    
  208.                                 print ("\rPartition %1d RomFS: Decrypting: %4d / %4d mb... Done") % (p, romfsSizeM + 1, romfsSizeM + 1)
  209.                          
  210.                             else:
  211.                                 print ("Partition %1d RomFS: No Data... Skipping...") % (p)
  212.                            
  213.                             g.seek((part_off[0] * sectorsize) + 0x18B)
  214.                             g.write(struct.pack('<B', int(0x00))) # set crypto-method to 0x00
  215.                             g.seek((part_off[0] * sectorsize) + 0x18F)
  216.                             flag = int(partition_flags[7]) # read partition flag
  217.                             flag = (flag & ((0x01|0x20)^0xFF)) # turn off 0x01 = FixedCryptoKey and 0x20 = CryptoUsingNewKeyY
  218.                             flag = (flag | 0x04) # turn on 0x04 = NoCrypto
  219.                             g.write(struct.pack('<B', int(flag))) # write flag
  220.  
  221.                         else:
  222.                             print ("Partition %1d Unable to read NCCH header") % (p)
  223.                     else:
  224.                         print ("Partition %1d Not found... Skipping...") % (p)
  225.             print ("Done...")
  226.         else:
  227.             print ("Error: Not a 3DS Rom?")
  228.  
  229. #raw_input('Press Enter to Exit...')
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement