diff --git a/.gitignore b/.gitignore index 5b55e56..68d61be 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # Custom *.iso *.ird +*.txt ird region_* diff --git a/LibRay-PS3/core.py b/LibRay-PS3/core.py index 1e6f933..18a0fc1 100644 --- a/LibRay-PS3/core.py +++ b/LibRay-PS3/core.py @@ -21,11 +21,32 @@ import sys import gzip ORDER = 'big' +IRD_ORDER = 'little' SECTOR = 2048 def bytes_to_int(byte): return int.from_bytes(byte, ORDER) +def ird_bytes_to_int(byte): + return int.from_bytes(byte, IRD_ORDER) + +def read_seven_bit_encoded_int(fileobj): + # Read out an Int32 7 bits at a time. The high bit + # of the byte when on means to continue reading more bytes + count = 0 + shift = 0 + byte = -1 + while (byte & 0x80) != 0 or byte == -1: + # Check for a corrupted stream. Read a max of 5 bytes. + if shift == (5 * 7): + raise ValueError + byte = ird_bytes_to_int(fileobj.read(1)) + count |= (byte & 0x7F) << shift + shift += 7 + print(byte, count, shift) + return count + + class IRD: def is_compressed(self, fileobj): @@ -38,28 +59,54 @@ class IRD: outfile.write(gzfile.read()) def __init__(self, filename): + with open(filename, 'rb') as fileobj: if self.is_compressed(fileobj): self.uncompress(filename) - + self.size = os.stat('ird').st_size with open('ird', 'rb') as ird: self.magic_string = ird.read(4) - self.version = bytes_to_int(ird.read(1)) + self.version = ird_bytes_to_int(ird.read(1)) self.game_id = ird.read(9) - self.game_name = ird.read(12) + length = read_seven_bit_encoded_int(ird) + self.game_name = ird.read(length) self.update_version = ird.read(4) self.game_version = ird.read(5) + self.app_version = ird.read(5) if self.version == 7: self.identifier = ird.read(4) - self.header = ird.read(SECTOR*3) - self.footer = ird.read(SECTOR) - self.region_count = ird.read(1) + if self.version < 9: + back = ird.tell() + length = read_seven_bit_encoded_int(ird) + if length: + self.header = ird.read(length) + else: + ird.seek(back) + back = ird.tell() + length = read_seven_bit_encoded_int(ird) + if length: + self.footer = ird.read(length) + else: + ird.seek(back) + self.region_count = ird_bytes_to_int(ird.read(1)) + self.region_hash = [] + for i in range(0, self.region_count): + self.region_hash.append(ird.read(16)) + self.file_count = ird_bytes_to_int(ird.read(4)) + print(vars(self) ) + + + return + sys.exit() back = ird.tell() prefix = bytes_to_int(ird.read(1)) length = prefix >> 1 if prefix & 0b00000001: ird.seek(back) + + + self.header = ird.read(length) back = ird.tell() @@ -68,8 +115,6 @@ class IRD: ird.seek(back) self.footer = ird.read(length) self.region_count - print() - sys.exit() print(self.game_name, self.update_version, self.game_version, self.region_count) diff --git a/LibRay-PS3/libray-ps3.py b/LibRay-PS3/libray-ps3.py index 9b5fbf7..14b5c97 100644 --- a/LibRay-PS3/libray-ps3.py +++ b/LibRay-PS3/libray-ps3.py @@ -21,8 +21,10 @@ import os import sys import core +import base64 import struct import shutil +import binascii from Crypto.Cipher import AES def bytes_to_int(byte): @@ -34,16 +36,35 @@ def int_to_hexstr(integer): def int_to_bytes(integer): return bytes(bytearray.fromhex(int_to_hexstr(integer))) +def hexstr_to_bytes(hexstr): + return bytes(bytearray.fromhex(hexstr)) + def bprint(byte): byteint = bytes_to_int(byte) print(byte, '\t->', byteint, '\t->', byteint*8, '\t->', byteint*2048 ) +def decode(self, text): + ''' + Remove the PKCS#7 padding from a text string + ''' + nl = len(text) + val = int(binascii.hexlify(text[-1]), 16) + if val > self.k: + raise ValueError('Input is not padded or padding is corrupt') + + l = nl - val + return text[:l] + + +BS = 32 +pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS) +unpad = lambda s : s[:-ord(s[len(s)-1:])] if __name__ == '__main__': - - core.IRD('ird') - - sys.exit() + + #core.IRD('BCAS20001-CA107E13820801F29488EBEB7D82A2C4.ird') + #core.IRD('BLES00048-1AA29AD85F7770BDCED0B7030067D59A.ird') + #sys.exit() bprint(b'\x00\x00\x00\x00') bprint(b'\x00\x00\x0c\xbf') @@ -51,21 +72,29 @@ if __name__ == '__main__': bprint(b'\x00\x00s\xc2\x7f') bprint(b'\x00\x00s\xc2\x80') - data = bytes(bytearray.fromhex("02EE0CE9E4C7CC1AD739ACC0DB6A3AA1")) - key = bytes(bytearray.fromhex("380bcf0b53455b3c7817ab4fa3ba90ed")) - iv = bytes(bytearray.fromhex("69474772af6fdab342743aefaa186287")) + data = hexstr_to_bytes("11089487d46ec9c1ec71205c2a6e8adc") + key = hexstr_to_bytes("380bcf0b53455b3c7817ab4fa3ba90ed") + iv = hexstr_to_bytes("69474772af6fdab342743aefaa186287") cipher = AES.new(key, AES.MODE_CBC, iv) disc_key = cipher.encrypt(data) + print(disc_key) + print(disc_key.hex()) + disc_key = hexstr_to_bytes("01AD4F9DFED22E37998BDDC57E135935") + print(disc_key.hex()) + print(unpad(disc_key.hex())) + disc_key = hexstr_to_bytes("DCD55A55B033905C58E7FE2A7F969F27") regions = [ {'start': 0, 'end': 6682624, 'enc': False}, {'start': 6682624, 'end': 59641856, 'enc': True}, {'start': 59641856, 'end': 15537010688, 'enc': False}, + {'start': 15537010688, 'end': 15537012736, 'enc': True } # There's also a last sector between 15537010688 and 15537012736, but seems like it's not used ] files = [] + test = hexstr_to_bytes("70c2a1") with open(sys.argv[1], 'rb') as iso: for i, region in enumerate(regions): files.append('region_' + str(i)) @@ -81,16 +110,20 @@ if __name__ == '__main__': while iso.tell() < region['end']: data = iso.read(core.SECTOR) num = iso.tell() - iv = ['' for i in range(0,16)] + iv = bytearray([0 for i in range(0,16)]) for j in range(0,16): - iv[16 - j - 1] = hex(ord(struct.pack("B", num & 0xFF))).replace('0x','') + iv[16 - j - 1] = (num & 0xFF) num >>= 8 - - iv = "".join(iv)[-16:] - #print(iv) - cipher = AES.new(disc_key, AES.MODE_CBC, iv) - output.write(cipher.decrypt(data)) + cipher = AES.new(disc_key, AES.MODE_CBC, bytes(iv)) + decrypted = cipher.decrypt(data) + + if test in decrypted: + print(iv.hex()) + print(data.hex()) + print(decrypted.hex()) + + output.write(decrypted) print(iso.tell()) @@ -164,6 +197,3 @@ if __name__ == '__main__': - - -