Proceeding the initial PS4 BCP Method Backporting Guide and Stable Version of the PS4 6.72 Jailbreak by sleirsgoevy, today @zecoxao shared PS4 Backporting Instructions to Play 6.72 Games on 5.05 Firmware via Twitter for those continuing to wait cautiously in updating their PlayStation 4 Firmware while the legendary @flatz shared on Twitter a pair of Python scripts to downgrade PS4 ELFs / SFOs to lower Firmware to aid in the process!
Download: downgrade_elf.py / downgrade_sfo.py / PS4 RiPKiT / Modding / Cheat / Backport Tool / SELFUtil by Znullptr / Noob404_EZ_PS4_Downporter_1.0.7z
downgrade_elf.py
downgrade_sfo.py
And from Pastebin.com on the PS4 Backporting Instructions to Play 6.72 Games on 5.05 Firmware:
Instructions on how to backport
Step 1: The eboot
Unfself it and use readelf -a on it~
you should see this section
Example for final fantasy vii remake demo
Align it upwards from 0x200000 bytes on 2nd Memsize, like so:
Then search for the *** Version in ORBI Place, for example in ff7r eboot the offset is at 0x6c00010
Then at the bottom of the eboot you can find the elf build name
After the PATH and name patch every single instance
Step 2: The modules
These are easy, at the bottom there's an *** version
Near end of module
Specifically for some modules, an extra step needs to be done:
After there is *** version, patch it
Some modules are also linked with others modules
Again at bottom
After
Every single instance
Step 3: The Param.sfo
This one is the most simple
After this put
ASCII values
Then there is a hidden one
Before the game's name put
This completes backporting
Spoiler: Related Tweets
How to Backport 6.72 Games to Run on 5.05
Finally, from zecoxao comes a Tutorial to Backport EA Titles with Flatz's Trick as follows:
Tools Required:
Sometimes they have underscore behind them so:
Eboot of the game you want to fix (for example fifa 20) in ELF format, NOT FSELF!
The process:
Load your eboot with IDA and balika's loader, the process will be similar to this:
When it finishes loading go to the pink area at the bottom (the nids) under functions window:
Patch all these from:
So:
To:
Apply and your EA Eboot should work properly together with 5.05 backport tools.
This concludes the tutorial.
Credits:
for the mem hole, I did 600000, and here are the EA patches.
did you patch 25 modules + eboot? this is not an EA game and this shouldn't need those patches.
right.sprx is not needed since FPKG tool generates one every time you make a pkg.
small note: in the backporting tutorial, there were two things i left unoticed: the offset 0x28 of eboot ELF has to have 4 zeroes (start of section headers is 0) and the offset 0x3C has to have a zero (number of section headers is 0)
Download: downgrade_elf.py / downgrade_sfo.py / PS4 RiPKiT / Modding / Cheat / Backport Tool / SELFUtil by Znullptr / Noob404_EZ_PS4_Downporter_1.0.7z
downgrade_elf.py
Code:
#!/usr/bin/env python2.7
# (c) flatz
import sys, os, struct
import argparse
import shutil
from hexdump import hexdump
from pprint import pprint
def align_up(x, alignment):
return (x + (alignment - 1)) & ~(alignment - 1)
def align_down(x, alignment):
return x & ~(alignment - 1)
def is_intervals_overlap(p1, p2):
return p1[0] <= p2[1] and p1[1] <= p2[0]
def check_file_magic(f, expected_magic):
old_offset = f.tell()
try:
magic = f.read(len(expected_magic))
except:
return False
finally:
f.seek(old_offset)
return magic == expected_magic
def check_sdk_version(sdk_version):
if len(sdk_version) != 10:
return False
parts = sdk_version.split('.', 2)
if len(parts) != 3:
return False
try:
lengths = [2, 3, 3]
for i, n in enumerate(parts):
if len(n) != lengths[i]:
return False
n = int(n, 10)
except:
return False
return True
# *** version have 001 in "patch" field
def parse_sdk_version(sdk_version):
major, minor, patch = sdk_version >> 24, (sdk_version >> 12) & 0xFFF, sdk_version & 0xFFF
return major, minor, patch
def stringify_sdk_version(major, minor, patch):
return '{0:02x}.{1:03x}.{2:03x}'.format(major, minor, patch)
def unstringify_sdk_version(sdk_version):
major, minor, patch = map(lambda x: int(x, 16), sdk_version.split('.', 2))
return major, minor, patch
def build_sdk_version(major, minor, patch):
sdk_version = ((major & 0xFF) << 24) | ((minor & 0xFFF) << 12) | (patch & 0xFFF)
return sdk_version
class ElfProgramHeader(object):
FMT = '<2I6Q'
PT_NULL = 0x0
PT_LOAD = 0x1
PT_DYNAMIC = 0x2
PT_INTERP = 0x3
PT_TLS = 0x7
PT_SCE_DYNLIBDATA = 0x61000000
PT_SCE_PROCPARAM = 0x61000001
PT_SCE_MODULE_PARAM = 0x61000002
PT_SCE_RELRO = 0x61000010
PT_SCE_COMMENT = 0x6FFFFF00
PT_SCE_VERSION = 0x6FFFFF01
PT_GNU_EH_FRAME = 0x6474E550
PF_X = 0x1
PF_W = 0x2
PF_R = 0x4
PF_RX = PF_R | PF_X
PF_RW = PF_R | PF_W
def __init__(self):
self.type = None
self.offset = None
self.vaddr = None
self.paddr = None
self.file_size = None
self.mem_size = None
self.flags = None
self.align = None
def load(self, f):
data = f.read(struct.calcsize(ElfProgramHeader.FMT))
if len(data) != struct.calcsize(ElfProgramHeader.FMT):
return False
self.type, self.flags, self.offset, self.vaddr, self.paddr, self.file_size, self.mem_size, self.align = struct.unpack(ElfProgramHeader.FMT, data)
return True
def save(self, f):
data = struct.pack(ElfProgramHeader.FMT, self.type, self.flags, self.offset, self.vaddr, self.paddr, self.file_size, self.mem_size, self.align)
if len(data) != struct.calcsize(ElfProgramHeader.FMT):
return False
f.write(data)
return True
class ElfSectionHeader(object):
FMT = '<2I4Q2I2Q'
def __init__(self, fmt):
self.name = None
self.type = None
self.flags = None
self.addr = None
self.offset = None
self.size = None
self.link = None
self.info = None
self.align = None
self.entry_size = None
def load(self, f):
data = f.read(struct.calcsize(ElfProgramHeader.FMT))
if len(data) != struct.calcsize(ElfProgramHeader.FMT):
return False
self.name, self.type, self.flags, self.addr, self.offset, self.size, self.link, self.info, self.align, self.entry_size = struct.unpack(ElfProgramHeader.FMT, data)
return True
def save(self, f):
data = struct.pack(ElfProgramHeader.FMT, self.name, self.type, self.flags, self.addr, self.offset, self.size, self.link, self.info, self.align, self.entry_size)
if len(data) != struct.calcsize(ElfProgramHeader.FMT):
return False
f.write(data)
return True
class ElfFile(object):
MAGIC = '\x7FELF'
FMT = '<4s5B6xB2HI3QI6H'
CLASS_NONE = 0
CLASS_64 = 2
DATA_NONE = 0
DATA_LSB = 1
VERSION_CURRENT = 1
MACHINE_X86_64 = 0x3E
TYPE_EXEC = 0x2
TYPE_SCE_EXEC = 0xFE00
TYPE_SCE_EXEC_ASLR = 0xFE10
TYPE_SCE_DYNAMIC = 0xFE18
def __init__(self):
self.magic = None
self.cls = None
self.encoding = None
self.version = None
self.os_abi = None
self.abi_version = None
self.nident_size = None
self.type = None
self.machine = None
self.version = None
self.entry = None
self.phdr_offset = None
self.shdr_offset = None
self.flags = None
self.ehdr_size = None
self.phdr_size = None
self.phdr_count = None
self.shdr_size = None
self.shdr_count = None
self.shdr_strtable_idx = None
self.phdrs = None
self.shdrs = None
def check(self, f):
old_offset = f.tell()
try:
result = check_file_magic(f, ElfFile.MAGIC)
except:
return False
finally:
f.seek(old_offset)
return result
def load(self, f):
data = f.read(struct.calcsize(ElfFile.FMT))
if len(data) != struct.calcsize(ElfFile.FMT):
print('error: unable to read header')
return False
self.magic, self.cls, self.encoding, self.legacy_version, self.os_abi, self.abi_version, self.nident_size, self.type, self.machine, self.version, self.entry, self.phdr_offset, self.shdr_offset, self.flags, self.ehdr_size, self.phdr_size, self.phdr_count, self.shdr_size, self.shdr_count, self.shdr_strtable_idx = struct.unpack(ElfFile.FMT, data)
if self.magic != ElfFile.MAGIC:
print('error: invalid magic: 0x{0:08X}'.format(self.magic))
return False
if self.encoding != ElfFile.DATA_LSB:
print('error: unsupported encoding: 0x{0:02X}'.format(self.encoding))
return False
if self.legacy_version != ElfFile.VERSION_CURRENT:
raise Exception('Unsupported version: 0x{0:x}'.format(self.version))
if self.cls != ElfFile.CLASS_64:
print('error: unsupported class: 0x{0:02X}'.format(self.cls))
return False
if self.type not in [ElfFile.TYPE_SCE_EXEC, ElfFile.TYPE_SCE_EXEC_ASLR, ElfFile.TYPE_SCE_DYNAMIC]:
print('error: unsupported type: 0x{0:04X}'.format(self.type))
return False
if self.machine != ElfFile.MACHINE_X86_64:
print('error: unexpected machine: 0x{0:X}'.format(self.machine))
return False
if self.ehdr_size != struct.calcsize(ElfFile.FMT):
print('error: invalid elf header size: 0x{0:X}'.format(self.ehdr_size))
return False
if self.phdr_size > 0 and self.phdr_size != struct.calcsize(ElfProgramHeader.FMT):
print('error: invalid program header size: 0x{0:X}'.format(self.phdr_size))
return False
if self.shdr_size > 0 and self.shdr_size != struct.calcsize(ElfSectionHeader.FMT):
print('error: invalid section header size: 0x{0:X}'.format(self.shdr_size))
return False
self.phdrs = []
for i in xrange(self.phdr_count):
phdr = ElfProgramHeader()
f.seek(self.phdr_offset + i * self.phdr_size)
if not phdr.load(f):
print('error: unable to load program header #{0}'.format(i))
return False
self.phdrs.append(phdr)
self.shdrs = []
#if self.shdr_size > 0:
# for i in xrange(self.shdr_count):
# shdr = ElfSectionHeader()
# f.seek(self.shdr_offset + i * self.shdr_size)
# if not shdr.load(f):
# print('error: unable to load section header #{0}'.format(i))
# return False
# self.shdrs.append(shdr)
return True
def save_hdr(self, f):
data = struct.pack(ElfFile.FMT, self.magic, self.cls, self.encoding, self.legacy_version, self.os_abi, self.abi_version, self.nident_size, self.type, self.machine, self.version, self.entry, self.phdr_offset, self.shdr_offset, self.flags, self.ehdr_size, self.phdr_size, self.phdr_count, self.shdr_size, self.shdr_count, self.shdr_strtable_idx)
if len(data) != struct.calcsize(ElfFile.FMT):
print('error: unable to save header')
return False
f.write(data)
for i, phdr in enumerate(self.phdrs):
f.seek(self.phdr_offset + i * self.phdr_size)
if not phdr.save(f):
print('error: unable to save program header #{0}'.format(i))
return False
for i, shdr in enumerate(self.shdrs):
f.seek(self.shdr_offset + i * self.shdr_size)
if not shdr.save(f):
print('error: unable to save section header #{0}'.format(i))
return False
return True
def get_phdr_by_type(self, type):
for i, phdr in enumerate(elf.phdrs):
if phdr.type == type:
return phdr
return None
class MyParser(argparse.ArgumentParser):
def error(self, message):
self.print_help()
sys.stderr.write('\nerror: {0}\n'.format(message))
sys.exit(2)
parser = MyParser(description='elf downgrader tool')
parser.add_argument('input', type=str, help='old file')
parser.add_argument('output', type=str, help='new file')
parser.add_argument('--verbose', action='store_true', default=False, help='show details')
parser.add_argument('--***-version', type=str, required=True, default='06.200.001', help='needed *** version')
if len(sys.argv) == 1:
parser.print_usage()
sys.exit(1)
args = parser.parse_args()
input_file_path = args.input
if not os.path.isfile(input_file_path):
parser.error('invalid input file: {0}'.format(input_file_path))
output_file_path = args.output
if os.path.exists(output_file_path) and not os.path.isfile(output_file_path):
parser.error('invalid output file: {0}'.format(output_file_path))
if args.sdk_version and not check_sdk_version(args.sdk_version):
parser.error('bad *** version')
shutil.copyfile(input_file_path, output_file_path)
if args.verbose:
print('processing elf file: {0}'.format(output_file_path))
with open(output_file_path, 'r+b') as f:
elf = ElfFile()
if not elf.check(f):
print('error: invalid elf file format')
sys.exit(1)
if not elf.load(f):
print('error: unable to load elf file')
sys.exit(1)
#
# Fixing proc/module param structure.
#
if elf.type in [ElfFile.TYPE_SCE_EXEC, ElfFile.TYPE_SCE_EXEC_ASLR]:
needed_type = ElfProgramHeader.PT_SCE_PROCPARAM
param_magic = 'ORBI'
if args.verbose:
print('executable file detected')
elif elf.type == ElfFile.TYPE_SCE_DYNAMIC:
needed_type = ElfProgramHeader.PT_SCE_MODULE_PARAM
param_magic = '\xBF\xF4\x13\x3C'
if args.verbose:
print('module file detected')
else:
print('error: unsupported elf type')
sys.exit(1)
major, minor, patch = unstringify_sdk_version(args.sdk_version)
new_sdk_version = build_sdk_version(major, minor, patch)
new_sdk_version_str = stringify_sdk_version(major, minor, patch)
if args.verbose:
print('wanted *** version: {0}'.format(new_sdk_version_str))
if args.verbose:
print('searching for {0} param segment...'.format('proc' if needed_type == ElfProgramHeader.PT_SCE_PROCPARAM else 'module'))
phdr = elf.get_phdr_by_type(needed_type)
if phdr is not None:
if args.verbose:
print('parsing param structure...')
f.seek(phdr.offset)
data = f.read(phdr.file_size)
if len(data) != phdr.file_size:
print('error: insufficient data read')
sys.exit(1)
param_size, = struct.unpack('<I', data[0x0:0x4])
if param_size < 0x14:
print('error: param structure is too small')
sys.exit(1)
data = data[:param_size]
if data[0x8:0xC] != param_magic:
print('error: unexpected param structure format')
sys.exit(1)
old_sdk_version, = struct.unpack('<I', data[0x10:0x14])
major, minor, patch = parse_sdk_version(old_sdk_version)
old_sdk_version_str = stringify_sdk_version(major, minor, patch)
if args.verbose:
print('*** version: {0}'.format(old_sdk_version_str))
if old_sdk_version > new_sdk_version:
if args.verbose:
print('fixing param structure...')
f.seek(phdr.offset + 0x10)
f.write(struct.pack('<I', new_sdk_version))
else:
print('warning: param segment not found (elf from old ***?)')
#
# Removing memory holes in PHDRs.
# Prevents error on old kernel versions: uncountigous RELRO and DATA segments
#
if new_sdk_version < 0x06000000: # less than 6.00 fw
segs = []
for i, phdr in enumerate(elf.phdrs):
if phdr.type not in [ElfProgramHeader.PT_LOAD, ElfProgramHeader.PT_SCE_RELRO]:
continue
if phdr.type == ElfProgramHeader.PT_LOAD and phdr.flags == ElfProgramHeader.PF_RX:
#print('skipping text segment...')
continue
#print('type:0x{0:X} vaddr:0x{1:X} paddr:0x{2:X} file_size:0x{3:X} mem_size:0x{4:X} align:0x{5:X}'.format(phdr.type, phdr.vaddr, phdr.paddr, phdr.file_size, phdr.mem_size, phdr.align))
segs.append(phdr)
#for i, phdr in enumerate(segs):
# print('vaddr:0x{0:X} mem_size:0x{1:X}'.format(phdr.vaddr, phdr.mem_size))
segs.sort(key=lambda x: (x.vaddr, -(x.vaddr + x.mem_size)))
i, count = 0, len(segs)
while i < count:
if i > 0 and (segs[i].vaddr >= segs[i - 1].vaddr and (segs[i].vaddr + segs[i].mem_size <= segs[i - 1].vaddr + segs[i - 1].mem_size)):
#print('removing seg vaddr:0x{0:X} mem_size:0x{1:X}'.format(segs[i].vaddr, segs[i].mem_size))
#print(' previous seg vaddr:0x{0:X} mem_size:0x{1:X}'.format(segs[i - 1].vaddr, segs[i - 1].mem_size))
segs = segs[:i] + segs[i + 1:]
count -= 1
continue
i += 1
count = len(segs)
has_changes = False
for i in xrange(count):
mem_size_aligned = align_up(segs[i].mem_size, 0x4000)
if (i + 1) < count and (segs[i].vaddr + mem_size_aligned) < segs[i + 1].vaddr:
segs[i].mem_size = segs[i + 1].vaddr - segs[i].vaddr
has_changes = True
#print('')
#for i, phdr in enumerate(segs):
# #print('type:0x{0:X} vaddr:0x{1:X} paddr:0x{2:X} file_size:0x{3:X} mem_size:0x{4:X} align:0x{5:X}'.format(phdr.type, phdr.vaddr, phdr.paddr, phdr.file_size, phdr.mem_size, phdr.align))
# print('vaddr:0x{0:X} mem_size:0x{1:X} end_vaddr:0x{2:X}'.format(phdr.vaddr, phdr.mem_size, phdr.vaddr + phdr.mem_size))
if has_changes:
if args.verbose:
print('removing memory holes...')
#
# Fixing version information in version segment.
#
if args.verbose:
print('searching for version segment...')
phdr = elf.get_phdr_by_type(ElfProgramHeader.PT_SCE_VERSION)
if phdr is not None:
if args.verbose:
print('parsing library list...')
f.seek(phdr.offset)
data = f.read(phdr.file_size)
if len(data) != phdr.file_size:
print('error: insufficient data read')
sys.exit(1)
has_changes = False
if phdr.file_size > 0:
offset = 0
while offset < phdr.file_size:
length = ord(data[offset])
offset += 1
name = data[offset:offset + length]
name, old_sdk_version = name.split(':', 1)
if len(old_sdk_version) != struct.calcsize('I'):
print('error: unexpected library list entry format')
sys.exit(1)
old_sdk_version, = struct.unpack('>I', old_sdk_version)
major, minor, patch = parse_sdk_version(old_sdk_version)
old_sdk_version_str = stringify_sdk_version(major, minor, patch)
if args.verbose:
print('{0} (*** version: {1})'.format(name, old_sdk_version_str))
if old_sdk_version > new_sdk_version:
data = data[:offset] + name + ':' + struct.pack('>I', new_sdk_version) + data[offset + length:]
has_changes = True
offset += length
if has_changes:
if args.verbose:
print('fixing *** versions in library list...')
f.seek(phdr.offset)
f.write(data)
else:
if args.verbose:
print('version segment not found')
#
# Fixing section headers.
#
if args.verbose:
print('fixing elf header...')
# Prevents error in orbis-bin:
# Section header offset (XXX) exceeds file size (YYY).
elf.shdr_offset = 0
elf.shdr_count = 0
f.seek(0)
if not elf.save_hdr(f):
print('error: unable to save elf file')
sys.exit(1)
if args.verbose:
print('done')
Code:
#!/usr/bin/env python2.7
# (c) flatz
import sys, os, struct
import argparse
import re
def align_up(x, alignment):
return (x + (alignment - 1)) & ~(alignment - 1)
def align_down(x, alignment):
return x & ~(alignment - 1)
def check_file_magic(f, expected_magic):
old_offset = f.tell()
try:
magic = f.read(len(expected_magic))
except:
return False
finally:
f.seek(old_offset)
return magic == expected_magic
def read_cstring(f):
s = ''
while True:
c = f.read(1)
if not c:
return False
if c == '\0':
break
s += c
return s
def check_sdk_version(sdk_version):
if len(sdk_version) != 10:
return False
parts = sdk_version.split('.', 2)
if len(parts) != 3:
return False
try:
lengths = [2, 3, 3]
for i, n in enumerate(parts):
if len(n) != lengths[i]:
return False
n = int(n, 10)
except:
return False
return True
# *** version have 001 in "patch" field
def parse_sdk_version(sdk_version):
major, minor, patch = sdk_version >> 24, (sdk_version >> 12) & 0xFFF, sdk_version & 0xFFF
return major, minor, patch
def stringify_sdk_version(major, minor, patch):
return '{0:02x}.{1:03x}.{2:03x}'.format(major, minor, patch)
def unstringify_sdk_version(sdk_version):
major, minor, patch = map(lambda x: int(x, 16), sdk_version.split('.', 2))
return major, minor, patch
def build_sdk_version(major, minor, patch):
sdk_version = ((major & 0xFF) << 24) | ((minor & 0xFFF) << 12) | (patch & 0xFFF)
return sdk_version
class SfoFile(object):
FMT = '<4sIIII'
MAGIC = '\x00PSF'
FMT_STRING_SPECIAL = 0x004
FMT_STRING = 0x204
FMT_UINT32 = 0x404
class Entry(object):
FMT = '<HHIII'
def __init__(self):
self.key_offset = None
self.format = None
self.size = None
self.max_size = None
self.data_offset = None
self.key = None
self.value = None
def load(self, f):
data = f.read(struct.calcsize(SfoFile.Entry.FMT))
if len(data) != struct.calcsize(SfoFile.Entry.FMT):
print('error: unable to read entry')
return False
self.key_offset, self.format, self.size, self.max_size, self.data_offset = struct.unpack(SfoFile.Entry.FMT, data)
return True
def save(self, f):
data = struct.pack(SfoFile.Entry.FMT, self.key_offset, self.format, self.size, self.max_size, self.data_offset)
if len(data) != struct.calcsize(SfoFile.Entry.FMT):
print('error: unable to save entry')
return False
f.write(data)
return True
def __init__(self):
self.start_offset = None
self.magic = None
self.version = None
self.key_table_offset = None
self.data_table_offset = None
self.num_entries = None
self.entries = None
self.entry_map = None
def check(self, f):
old_offset = f.tell()
try:
result = check_file_magic(f, SfoFile.MAGIC)
except:
return False
finally:
f.seek(old_offset)
return result
def load(self, f):
self.start_offset = f.tell()
data = f.read(struct.calcsize(SfoFile.FMT))
if len(data) != struct.calcsize(SfoFile.FMT):
print('error: unable to read header')
return False
self.magic, self.version, self.key_table_offset, self.data_table_offset, self.num_entries = struct.unpack(SfoFile.FMT, data)
self.entries = []
for i in xrange(self.num_entries):
entry = SfoFile.Entry()
if not entry.load(f):
return False
assert entry.max_size >= entry.size
self.entries.append(entry)
self.entry_map = {}
for i, entry in enumerate(self.entries):
f.seek(self.start_offset + self.key_table_offset + entry.key_offset)
entry.key = read_cstring(f)
f.seek(self.start_offset + self.data_table_offset + entry.data_offset)
entry.value = f.read(entry.max_size)
entry.value = entry.value[:entry.size]
self.entry_map[entry.key] = entry
return True
def fixup(self):
self.num_entries = len(self.entries)
self.key_table_offset = struct.calcsize(SfoFile.FMT)
self.key_table_offset += self.num_entries * struct.calcsize(SfoFile.Entry.FMT)
offset = 0
for i, entry in enumerate(self.entries):
entry.key_offset = offset
offset += len(entry.key) + 1
self.data_table_offset = self.key_table_offset + align_up(offset, 0x4)
offset = 0
for i, entry in enumerate(self.entries):
entry.data_offset = offset
assert len(entry.value) <= entry.max_size
offset += entry.max_size
def save(self, f):
data = struct.pack(SfoFile.FMT, self.magic, self.version, self.key_table_offset, self.data_table_offset, self.num_entries)
if len(data) != struct.calcsize(SfoFile.FMT):
print('error: unable to save header')
return False
f.write(data)
for i, entry in enumerate(self.entries):
if not entry.save(f):
return False
for i, entry in enumerate(self.entries):
f.seek(self.start_offset + self.key_table_offset + entry.key_offset)
data = entry.key + '\0'
f.write(data)
f.seek(self.start_offset + self.data_table_offset + entry.data_offset)
data = entry.value.ljust(entry.max_size, '\0')
f.write(data)
return True
def get_entry(self, key):
return self.entry_map[key] if key in self.entry_map else None
def dump(self):
for i, entry in enumerate(self.entries):
assert entry.format in [SfoFile.FMT_STRING_SPECIAL, SfoFile.FMT_STRING, SfoFile.FMT_UINT32]
if entry.format == SfoFile.FMT_STRING_SPECIAL:
value = entry.value[:entry.size]
elif entry.format == SfoFile.FMT_STRING:
value = entry.value[:entry.size - 1]
elif entry.format == SfoFile.FMT_UINT32:
assert entry.size in [1, 2, 4, 8]
if entry.size == struct.calcsize('B'):
value = '0x{0:02X}'.format(struct.unpack('<B', entry.value)[0])
elif entry.size == struct.calcsize('H'):
value = '0x{0:04X}'.format(struct.unpack('<H', entry.value)[0])
elif entry.size == struct.calcsize('I'):
value = '0x{0:08X}'.format(struct.unpack('<I', entry.value)[0])
elif entry.size == struct.calcsize('Q'):
value = '0x{0:016X}'.format(struct.unpack('<Q', entry.value)[0])
print('{0} = {1}'.format(entry.key, value))
class MyParser(argparse.ArgumentParser):
def error(self, message):
self.print_help()
sys.stderr.write('\nerror: {0}\n'.format(message))
sys.exit(2)
parser = MyParser(description='sfo tool')
parser.add_argument('input', type=str, help='old file')
parser.add_argument('output', type=str, help='new file')
parser.add_argument('--verbose', action='store_true', default=False, help='show details')
parser.add_argument('--dump', action='store_true', default=False, help='dump entries')
parser.add_argument('--***-version', type=str, default=None, help='needed *** version')
parser.add_argument('--system-version', type=str, default=None, help='needed *** version')
if len(sys.argv) == 1:
parser.print_usage()
sys.exit(1)
args = parser.parse_args()
input_file_path = args.input
if not os.path.isfile(input_file_path):
parser.error('invalid input file: {0}'.format(input_file_path))
output_file_path = args.output
if os.path.exists(output_file_path) and not os.path.isfile(output_file_path):
parser.error('invalid output file: {0}'.format(output_file_path))
if args.sdk_version and not check_sdk_version(args.sdk_version):
parser.error('bad *** version')
if args.system_version and not check_sdk_version(args.system_version):
parser.error('bad system version')
if args.verbose:
print('reading sfo file: {0}'.format(input_file_path))
with open(input_file_path, 'rb') as f:
sfo = SfoFile()
if not sfo.check(f):
print('error: invalid sfo file format')
sys.exit(1)
if not sfo.load(f):
print('error: unable to load sfo file')
sys.exit(1)
if args.sdk_version is not None and 'PUBTOOLINFO' in sfo.entry_map:
entry = sfo.entry_map['PUBTOOLINFO']
assert entry.format == SfoFile.FMT_STRING
sdk_ver_regexp = re.compile(r'sdk_ver=(\d{8})')
matches = sdk_ver_regexp.search(entry.value)
if matches is not None:
start_pos, end_pos = matches.span(1)
old_sdk_version = int(entry.value[start_pos:end_pos], 16)
major, minor, patch = parse_sdk_version(old_sdk_version)
old_sdk_version_str = stringify_sdk_version(major, minor, patch)
if args.verbose:
print('*** version: {0}'.format(old_sdk_version_str))
major, minor, patch = unstringify_sdk_version(args.sdk_version)
new_sdk_version = build_sdk_version(major, minor, patch)
new_sdk_version_str = stringify_sdk_version(major, minor, patch)
if old_sdk_version > new_sdk_version:
print('fixing *** version...')
if args.verbose:
print('wanted *** version: {0}'.format(new_sdk_version_str))
new_sdk_version = '{0:08X}'.format(new_sdk_version)
assert len(new_sdk_version) == (end_pos - start_pos)
entry.value = entry.value[:start_pos] + new_sdk_version + entry.value[end_pos:]
if args.system_version is not None and 'SYSTEM_VER' in sfo.entry_map:
if not check_sdk_version(args.system_version):
parser.error('error: bad *** version')
entry = sfo.entry_map['SYSTEM_VER']
assert entry.format == SfoFile.FMT_UINT32 and entry.size == struct.calcsize('I')
old_system_version, = struct.unpack('<I', entry.value)
major, minor, patch = parse_sdk_version(old_system_version)
old_system_version_str = stringify_sdk_version(major, minor, patch)
if args.verbose:
print('system version: {0}'.format(old_system_version_str))
major, minor, patch = unstringify_sdk_version(args.system_version)
new_system_version = build_sdk_version(major, minor, patch)
new_system_version_str = stringify_sdk_version(major, minor, patch)
if old_system_version > new_system_version:
print('fixing system version...')
if args.verbose:
print('wanted system version: {0}'.format(new_system_version_str))
entry.value = struct.pack('<I', new_system_version)
if args.verbose:
print('recalculating offsets...')
sfo.fixup()
if args.dump:
print('dumping entries...')
sfo.dump()
if args.verbose:
print('writing sfo file: {0}'.format(output_file_path))
with open(output_file_path, 'wb') as f:
if not sfo.save(f):
print('error: unable to save sfo file')
sys.exit(1)
if args.verbose:
print('done')
Instructions on how to backport
Step 1: The eboot
Unfself it and use readelf -a on it~
you should see this section
Code:
LOOS+0x1000010 0x00000000063c4000 0x0000000006400000 0x0000000006400000
0x000000000083a100 0x000000000083a100 R 0x4000
Align it upwards from 0x200000 bytes on 2nd Memsize, like so:
Code:
LOOS+0x1000010 0x00000000063c4000 0x0000000006400000 0x0000000006400000
0x000000000083a100 0x0000000000a00000 R 0x4000
Code:
01 00 05 05 <- correct *** version
Code:
PATHH���D���c:/j/workspace/B/b_BuPS4M/cw/End/Binaries/PS4/End-PS4-Shipping.self
Code:
05 05 00 01
These are easy, at the bottom there's an *** version
Code:
05 05 00 01
Specifically for some modules, an extra step needs to be done:
Code:
Search for 18 00 00 00 00 00 00 00 BF F4 13 3C 01 00 00 00
Code:
01 00 05 05
Again at bottom
Code:
PATH0���*���C:/devel/projects/bink/build/Bink2PS4.prx
Code:
05 05 00 01
Step 3: The Param.sfo
This one is the most simple
Code:
sdk_ver=
Code:
05050001
Then there is a hidden one
Before the game's name put
Code:
00 00 05 05
Spoiler: Related Tweets
How to Backport 6.72 Games to Run on 5.05
Finally, from zecoxao comes a Tutorial to Backport EA Titles with Flatz's Trick as follows:
Tools Required:
- IDA Pro or Ghidra with proper PS4 loaders / tools (i'll be using IDA Pro 7.x with balika's loader for this)
- Hexeditor of choice (I Use HxD)
- Flatz's required functions
Code:
7CxI50-xlCk
+OnbUs1CV0M
xmhnAoxN3Wk
pMxXhNozUX
Code:
_7CxI50-xlCk
_+OnbUs1CV0M
_xmhnAoxN3Wk
_pMxXhNozUX
The process:
Load your eboot with IDA and balika's loader, the process will be similar to this:
When it finishes loading go to the pink area at the bottom (the nids) under functions window:
Patch all these from:
So:
Code:
ff 25 72
ff 25 6a
ff 25 62
ff 25 5a
Code:
31 C0 C3
Apply and your EA Eboot should work properly together with 5.05 backport tools.
This concludes the tutorial.
Credits:
- flatz for the original discovery
- Joonie for the implementation and POC in the scene
for the mem hole, I did 600000, and here are the EA patches.
Code:
[7CxI50-xlCk] FF 25 DA A5 7B 01 >> 31 C0 C3 A5 7B 01
[OnbUs1CV0M] FF 25 EA A5 7B 01 >> 31 C0 C3 A5 7B 01
[pMxXhNozUX8] FF 25 DA A6 7B 01 >> 31 C0 C3 A6 7B 01
[xmhnAoxN3Wk] FF 25 62 A6 7B 01 >> 31 C0 C3 A6 7B 01
right.sprx is not needed since FPKG tool generates one every time you make a pkg.
small note: in the backporting tutorial, there were two things i left unoticed: the offset 0x28 of eboot ELF has to have 4 zeroes (start of section headers is 0) and the offset 0x3C has to have a zero (number of section headers is 0)
Code:
@echo off
SetLocal EnableDelayedExpansion
set PYTHON_PATH=C:\python27-x64\python.exe
set PKG_TOOL_PATH=C:\Program Files\LibOrbisPkg\PkgTool.exe
set SDKVERSION=00.000.000
set SYSVERSION=00.000.000
REM TODO: Generate GP4 file
for /f "tokens=* delims=" %%f in ('dir "%1\*" /s /b') do (
for %%i IN ("%%f") do (
set filedrive=%%~di
set filepath=%%~pi
set filename=%%~ni
set fileextension=%%~xi
)
if "!fileextension!" == ".prx" (
"%PYTHON_PATH%" backport_elf.py --***-version %SDKVERSION% --verbose "%%f" "%%f.backport"
"%PYTHON_PATH%" make_fself.py --paid 0x3800000000000011 "%%f.backport" "!filedrive!!filepath!!filename!.fself"
REM TODO: Edit GP4 file
)
if "!fileextension!" == ".sprx" (
"%PYTHON_PATH%" backport_elf.py --***-version %SDKVERSION% --verbose "%%f" "%%f.backport"
"%PYTHON_PATH%" make_fself.py --paid 0x3800000000000011 "%%f.backport" "!filedrive!!filepath!!filename!.fself"
REM TODO: Edit GP4 file
)
if /i "!filename:~0,5!" == "eboot" (
if "!fileextension!" == ".bin" (
"%PYTHON_PATH%" backport_elf.py --***-version %SDKVERSION% --verbose "%%f" "%%f.backport"
"%PYTHON_PATH%" make_fself.py --paid 0x3800000000000011 "%%f.backport" "!filedrive!!filepath!!filename!.fself"
REM TODO: Edit GP4 file
)
if "!fileextension!" == ".elf" (
"%PYTHON_PATH%" backport_elf.py --***-version %SDKVERSION% --verbose "%%f" "%%f.backport"
"%PYTHON_PATH%" make_fself.py --paid 0x3800000000000011 "%%f.backport" "!filedrive!!filepath!!filename!.fself"
REM TODO: Edit GP4 file
)
)
if "!filename!" == "param" (
if "!fileextension!" == ".sfo" (
"%PYTHON_PATH%" backport_sfo.py --***-version %SDKVERSION% --system-version %SYSVERSION% --verbose "%%f" "%%f.backport"
REM TODO: Edit GP4 file
)
)
)
REM "%PKG_TOOL_PATH%" pkg_build pkg.gp4 .
pause