mirror of
https://github.com/xprism1/ntool.git
synced 2025-06-19 06:55:33 -04:00
138 lines
4.6 KiB
Python
138 lines
4.6 KiB
Python
from .common import *
|
|
from .keys import *
|
|
|
|
class crrHdr(Structure):
|
|
_pack_ = 1
|
|
|
|
_fields_ = [
|
|
('magic', c_char * 4),
|
|
('reserved1', c_uint32),
|
|
('next_crr', c_uint32),
|
|
('prev_crr', c_uint32),
|
|
('debug_info_offset', c_uint32),
|
|
('debug_info_size', c_uint32),
|
|
('reserved2', c_uint64),
|
|
('unique_id_mask', c_uint32),
|
|
('unique_id_pattern', c_uint32),
|
|
('reserved3', c_uint8 * 0x18),
|
|
('crr_body_mod', c_uint8 * 0x100),
|
|
('sig', c_uint8 * 0x100),
|
|
]
|
|
|
|
def __new__(cls, buf):
|
|
return cls.from_buffer_copy(buf)
|
|
|
|
def __init__(self, data):
|
|
pass
|
|
|
|
class crrBodyHdr(Structure):
|
|
_pack_ = 1
|
|
|
|
_fields_ = [
|
|
('sig', c_uint8 * 0x100),
|
|
('unique_id', c_uint32),
|
|
('size', c_uint32),
|
|
('reserved1', c_uint64),
|
|
('hash_offset', c_uint32),
|
|
('hash_count', c_uint32),
|
|
('plain_offset', c_uint32),
|
|
('plain_size', c_uint32),
|
|
]
|
|
|
|
def __new__(cls, buf):
|
|
return cls.from_buffer_copy(buf)
|
|
|
|
def __init__(self, data):
|
|
pass
|
|
|
|
class crrReader:
|
|
def __init__(self, file, dev=0):
|
|
self.file = file
|
|
self.dev = dev
|
|
|
|
with open(file, 'rb') as f:
|
|
self.hdr = crrHdr(f.read(0x240))
|
|
self.body_hdr = crrBodyHdr(f.read(0x120))
|
|
|
|
f.seek(self.body_hdr.hash_offset)
|
|
cro_hash_list = []
|
|
for i in range(self.body_hdr.hash_count):
|
|
cro_hash_list.append(f.read(0x20))
|
|
self.cro_hash_list = cro_hash_list
|
|
|
|
# If re-generating hash / want to verify CRO hashlist, place all CROs in the same directory as static.crr
|
|
self.current_dir = os.path.dirname(os.path.abspath(self.file))
|
|
self.cros = [i for i in os.listdir(self.current_dir) if i.endswith('.cro')]
|
|
|
|
if len(self.cros) != 0 and len(self.cros) != self.body_hdr.hash_count:
|
|
raise Exception(f'Expected {self.body_hdr.hash_count} CROs but found {len(self.cros)}')
|
|
|
|
def regen_hash(self): # Overwrites existing file
|
|
if len(self.cros) == 0:
|
|
raise Exception('Please place all CROs in the same directory as static.crr')
|
|
|
|
hashes = []
|
|
for i in self.cros:
|
|
with open(os.path.join(self.current_dir, i), 'rb') as g:
|
|
h = hashlib.sha256()
|
|
h.update(g.read(0x80))
|
|
hashes.append(h.digest())
|
|
hashes = [hex(readbe(i))[2:].zfill(64) for i in hashes]
|
|
hashes.sort()
|
|
hashes = [hextobytes(i) for i in hashes]
|
|
|
|
with open(self.file, 'r+b') as f:
|
|
f.seek(0x360)
|
|
f.write(b''.join(hashes))
|
|
print(f'{self.file} rehashed')
|
|
|
|
def regen_sig(self, dev=0): # Overwrites existing file
|
|
with open(self.file, 'r+b') as f:
|
|
# Body sig
|
|
f.seek(0x340)
|
|
crr_body = f.read(self.body_hdr.plain_offset - 0x340)
|
|
body_sig = Crypto.sign_rsa_sha256(CTR.crr_body_mod, CTR.crr_body_priv, crr_body)
|
|
|
|
f.seek(0x40)
|
|
f.write(CTR.crr_body_mod)
|
|
f.seek(0x240)
|
|
f.write(body_sig)
|
|
|
|
if dev == 1: # Header sig
|
|
f.seek(0x20)
|
|
data = f.read(0x120)
|
|
hdr_sig = Crypto.sign_rsa_sha256(CTR.crr_mod[1], CTR.crr_priv[1], data)
|
|
f.seek(0x140)
|
|
f.write(hdr_sig)
|
|
|
|
print(f'{self.file} resigned')
|
|
|
|
def verify(self):
|
|
f = open(self.file, 'rb')
|
|
|
|
hash_check = []
|
|
if len(self.cros) != 0:
|
|
hashes = []
|
|
for i in self.cros: # Check if sha256 of first 0x80 bytes of cro exists in cro hashlist
|
|
with open(os.path.join(self.current_dir, i), 'rb') as g:
|
|
h = hashlib.sha256()
|
|
h.update(g.read(0x80))
|
|
hashes.append(h.digest() in self.cro_hash_list)
|
|
hash_check.append(('CRO Hashlist', all(hashes)))
|
|
|
|
sig_check = []
|
|
sig_check.append(('CRR Header', Crypto.verify_rsa_sha256(CTR.crr_mod[self.dev], bytes(self.hdr)[0x20:0x140], bytes(self.hdr.sig))))
|
|
|
|
f.seek(0x340)
|
|
crr_body = f.read(self.body_hdr.plain_offset - 0x340)
|
|
sig_check.append(('CRR Body', Crypto.verify_rsa_sha256(bytes(self.hdr.crr_body_mod), crr_body, bytes(self.body_hdr.sig))))
|
|
|
|
f.close()
|
|
if hash_check != []:
|
|
print("Hashes:")
|
|
for i in hash_check:
|
|
print(' > {0:15} {1:4}'.format(i[0] + ':', 'GOOD' if i[1] else 'FAIL'))
|
|
print("Signatures:")
|
|
for i in sig_check:
|
|
print(' > {0:15} {1:4}'.format(i[0] + ':', 'GOOD' if i[1] else 'FAIL'))
|