Editing Talk:Package Files

Jump to navigation Jump to search
Warning: You are not logged in. Your IP address will be publicly visible if you make any edits. If you log in or create an account, your edits will be attributed to your username, along with other benefits.

The edit can be undone. Please check the comparison below to verify that this is what you want to do, and then publish the changes below to finish undoing the edit.

Latest revision Your text
Line 4: Line 4:


<source lang="python">
<source lang="python">
# UnPKG rev 0x00000008 (public edition), (c) flatz
# UnPKG rev 0x00000007 (public edition), (c) flatz


import sys, os, hashlib, hmac, struct, math, traceback
import sys, os, hashlib, hmac, struct, traceback
from cStringIO import StringIO
from cStringIO import StringIO


Line 89: Line 89:
CONTENT_ID_SIZE = 0x24
CONTENT_ID_SIZE = 0x24
SHA256_HASH_SIZE = 0x20
SHA256_HASH_SIZE = 0x20
META_ENTRY_SIZE = 0x20


FILE_TYPE_FLAGS_RETAIL = 1 << 31
FILE_TYPE_FLAGS_RETAIL = 1 << 31


ENTRY_TYPE_DIGEST_TABLE = 0x0001
ENTRY_TYPE_DIGEST_TABLE = 0x0001
ENTRY_TYPE_0x800        = 0x0010
ENTRY_TYPE_0x200        = 0x0020
ENTRY_TYPE_0x180        = 0x0080
ENTRY_TYPE_META_TABLE  = 0x0100
ENTRY_TYPE_META_TABLE  = 0x0100
ENTRY_TYPE_NAME_TABLE  = 0x0200
ENTRY_TYPE_NAME_TABLE  = 0x0200
Line 106: Line 102:
ENTRY_TABLE_MAP = {
ENTRY_TABLE_MAP = {
ENTRY_TYPE_DIGEST_TABLE: '.digests',
ENTRY_TYPE_DIGEST_TABLE: '.digests',
ENTRY_TYPE_0x800: '.entry_0x800',
ENTRY_TYPE_0x200: '.entry_0x200',
ENTRY_TYPE_0x180: '.entry_0x180',
ENTRY_TYPE_META_TABLE: '.meta',
ENTRY_TYPE_META_TABLE: '.meta',
ENTRY_TYPE_NAME_TABLE: '.names',
ENTRY_TYPE_NAME_TABLE: '.names',
Line 124: Line 117:
0x1200: 'icon0.png',
0x1200: 'icon0.png',
0x1220: 'pic0.png',
0x1220: 'pic0.png',
0x1240: 'snd0.at9',
0x1260: 'changeinfo/changeinfo.xml',
0x1260: 'changeinfo/changeinfo.xml',
}
}
Line 157: Line 149:
pkg_file.seek(0x10) # FIXME: or maybe uint16 at 0x16???
pkg_file.seek(0x10) # FIXME: or maybe uint16 at 0x16???
num_table_entries = read_uint32_be(pkg_file)
num_table_entries = read_uint32_be(pkg_file)
pkg_file.seek(0x14)
num_system_entries = read_uint16_be(pkg_file)


pkg_file.seek(0x18)
pkg_file.seek(0x18)
file_table_offset = read_uint32_be(pkg_file)
file_table_offset = read_uint32_be(pkg_file)
pkg_file.seek(0x1C)
main_entries_data_size = read_uint32_be(pkg_file)
pkg_file.seek(0x24)
body_offset = read_uint32_be(pkg_file)
pkg_file.seek(0x2C)
body_size = read_uint32_be(pkg_file)
pkg_file.seek(0x414)
content_offset = read_uint32_be(pkg_file)
pkg_file.seek(0x41C)
content_size = read_uint32_be(pkg_file)


pkg_file.seek(0x40)
pkg_file.seek(0x40)
Line 182: Line 158:
raise MyError('invalid content id')
raise MyError('invalid content id')


pkg_file.seek(0x100)
pkg_file.seek(0x140)
main_entries1_digest = pkg_file.read(SHA256_HASH_SIZE)
main_entries2_digest = pkg_file.read(SHA256_HASH_SIZE)
digest_table_digest = pkg_file.read(SHA256_HASH_SIZE)
digest_table_digest = pkg_file.read(SHA256_HASH_SIZE)
body_digest = pkg_file.read(SHA256_HASH_SIZE)
pkg_file.seek(0x440)
content_digest = pkg_file.read(SHA256_HASH_SIZE)
content_one_block_digest = pkg_file.read(SHA256_HASH_SIZE)


table_entries = []
table_entries = []
table_entries_map = {}
pkg_file.seek(file_table_offset)
pkg_file.seek(file_table_offset)
for i in xrange(num_table_entries):
for i in xrange(num_table_entries):
entry = FileTableEntry()
entry = FileTableEntry()
entry.read(pkg_file)
entry.read(pkg_file)
table_entries_map[entry.type] = len(table_entries)
table_entries.append(entry)
table_entries.append(entry)


Line 237: Line 204:
entry_digests = pkg_file.read(entry.size)
entry_digests = pkg_file.read(entry.size)


data = ''
computed_digest_table_digest = None
for entry_type in [ENTRY_TYPE_0x800, ENTRY_TYPE_0x200, ENTRY_TYPE_0x180, ENTRY_TYPE_META_TABLE, ENTRY_TYPE_DIGEST_TABLE]:
computed_entry_digests = '\x00' * 32
entry = table_entries[table_entries_map[entry_type]]
pkg_file.seek(entry.offset)
data += pkg_file.read(entry.size)
computed_main_entries1_digest = sha256(data)
 
data = ''
for entry_type in [ENTRY_TYPE_0x800, ENTRY_TYPE_0x200, ENTRY_TYPE_0x180, ENTRY_TYPE_META_TABLE]:
entry = table_entries[table_entries_map[entry_type]]
pkg_file.seek(entry.offset)
size = entry.size if entry_type != ENTRY_TYPE_META_TABLE else num_system_entries * META_ENTRY_SIZE
data += pkg_file.read(size)
computed_main_entries2_digest = sha256(data)
 
entry = table_entries[table_entries_map[ENTRY_TYPE_DIGEST_TABLE]]
pkg_file.seek(entry.offset)
data = pkg_file.read(entry.size)
computed_digest_table_digest = sha256(data)
 
pkg_file.seek(body_offset)
body = pkg_file.read(body_size)
computed_body_digest = sha256(body)
 
computed_entry_digests = '\x00' * SHA256_HASH_SIZE
for i in xrange(num_table_entries):
entry = table_entries[i]
if entry.type == ENTRY_TYPE_DIGEST_TABLE:
continue
pkg_file.seek(entry.offset)
data = pkg_file.read(entry.size)
computed_entry_digests += sha256(data)
 
for i in xrange(num_table_entries):
for i in xrange(num_table_entries):
entry = table_entries[i]
entry = table_entries[i]
Line 281: Line 217:
data = pkg_file.read(entry.size)
data = pkg_file.read(entry.size)
entry_file.write(data)
entry_file.write(data)
 
if entry.type != ENTRY_TYPE_DIGEST_TABLE:
block_size = 0x10000
computed_entry_digests += sha256(data)
num_blocks = 1 + int((content_size - 1) / block_size) if content_size > 0 else 0
else:
 
computed_digest_table_digest = sha256(data)
pkg_file.seek(content_offset)
is_digests_valid = computed_entry_digests == entry_digests and computed_digest_table_digest == digest_table_digest
data = pkg_file.read(block_size)
computed_content_one_block_digest = sha256(data)
 
hash_context = hashlib.sha256()
pkg_file.seek(content_offset)
bytes_left = content_size
for i in xrange(num_blocks):
current_size = block_size if bytes_left > block_size else bytes_left
data = pkg_file.read(current_size)
hash_context.update(data)
bytes_left -= block_size
computed_content_digest = hash_context.digest()
 
is_digests_valid = computed_main_entries1_digest == main_entries1_digest
is_digests_valid = is_digests_valid and computed_main_entries2_digest == main_entries2_digest
is_digests_valid = is_digests_valid and computed_digest_table_digest == digest_table_digest
is_digests_valid = is_digests_valid and computed_body_digest == body_digest
is_digests_valid = is_digests_valid and computed_entry_digests == entry_digests
is_digests_valid = is_digests_valid and computed_content_digest == content_digest
is_digests_valid = is_digests_valid and computed_content_one_block_digest == content_one_block_digest


         print 'File information:'
         print 'File information:'
Please note that all contributions to PS4 Developer wiki are considered to be released under the GNU Free Documentation License 1.2 (see PS4 Developer wiki:Copyrights for details). If you do not want your writing to be edited mercilessly and redistributed at will, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource. Do not submit copyrighted work without permission!

To protect the wiki against automated edit spam, we kindly ask you to solve the following hCaptcha:

Cancel Editing help (opens in new window)