PS4 PSP Emulation on PS4: Early Stages of Injecting Games / Homebrew on discovered PSP Emulator

First it was PS2 Emulation that came to exploited PlayStation 4's but now the developer's have appeared to made progress towards unlocking PSP Emulation as well on the PS4. As sometimes the case with re-release of games from older generations they contain not only the game but an emulator to play the original files. KiiWii (from GBAtemp) had noticed that Parappa the Rappa (PS4) files matched the PS2 Classic structure, from there a bit more digging was done and it was then concluded that Parappa the Rappa for the PS4 was running on a PSP Emulator. This is great news for the PS4 Homebrew Community and means some new adventures for PSP Emulation on the PS4 are on the horizon. Don't forget on PS4 Linux Side of things as well, PSP emulation is also possible, a recent Linux Distro by the PSXITA Team (psxitarch linux) bundles various emulators like RetroArch/PPSSPP so be sure to give that new distro release a look as well.


PS4_PSP.jpg


While the discovery is still in its early stages, the community is sure to progress this find into what hopes to be a fully working PSP emulator like what is now possible with the PS2 Classic emulator. So far there is positives and also some obstacles to overcome, the positives we have seen demonstrated the Cube Test Homebrew running on the Emu (via screenshot by ZiL0G80 )

DcvLzH7X4AA3e3S.jpg
Cube Test (PSP) working on PS4's PSPEMU

Overall there has not been much reported of being tested with PSP Homebrew and likely will take some figuring out how the emulator works for most to work.. Something that could take some time as we look at the PS3 community still learning and making discoveries to Sony's internal Ps2 Emulator and PsP Emulator. This could be quick work by developer's (would not surprise me) or also it could take some time for devs to document how this emulator works as there is no instructions manuals included for this emulator..

Darkelement has gave a bit of a sneak peak to PSP PKG creation with this tweet and pastie file. What we know from various test in KiiWii original thread we do have a few working games and also alot of games not working. This can be expected early in the process, the question however remains is this a simple fix to get these games (many) not working or its a more serious setback. The coming days and weeks should shed plenty of light on the situation and hopefully plenty of PSP goodies on our PS4..

 
Last edited:
PS2 Classics, now PSP Emu...this keeps getting better and better :D

I dont hope there its a PS1 Emu code hidden, but all its possible, but very unlikely ;)

I always knew that Sony didnt used the Original PS1 Parrapa The Rapper, it just seems too polished from the PS1 counterpart, not to mention, port it to PS4 would be a major headache, since the PS1 doesnt support floating-point sub-pixel precision, only introduced with the PS2 MIPS, a counterpart on the PSP CPU :D
 
Hi,

So, the PSP Emulator on PS4 requires the BOOT.BIN to have certain section names that it is scanning.
Most of the binaries actually do contain them AFTER decryption, but sometimes they are stripped.
Luckily the string table is still there and sony left the offsets of the section header names intact.
That means we can identify+restore them :D
Here is a script that can add those required (NPEZ00311, ULUS10567,..). If an empty entry is found, it will create a ".new" file. This will not magically make everything working, for that you still need to identify why the emulator is crashing, but you can read /dev/klog :)

I took partial elf reading code from flatz, so kudos to him

> python fix_elf.py <input_elf>

Code:
import struct
import sys

class ElfPHdr(object):
    FMT = '<8I'

    def __init__(self, idx):
        self.idx = idx
        self.type = None
        self.offset = None
        self.vaddr = None
        self.paddr = None
        self.filesz = None
        self.memsz = None
        self.flags = None
        self.align = None

    def load(self, f):
        self.type, self.offset, self.vaddr, self.paddr, self.filesz, self.memsz, self.flags, self.align = struct.unpack(ElfPHdr.FMT, f.read(struct.calcsize(ElfPHdr.FMT)))

class ElfSHdr(object):
    FMT = '<10I'

    def __init__(self, idx):
        self.idx = idx
        self.name = None
        self.name_end = None
        self.name_len = 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.entsize = None

    def load(self, f):
        self.name, self.type, self.flags, self.addr, self.offset, self.size, self.link, self.info, self.align, self.entsize = struct.unpack(ElfSHdr.FMT, f.read(struct.calcsize(ElfSHdr.FMT)))

class ElfEHdr(object):
    FMT = '<4s5B6xB'
    EX_FMT = '<2HI3II6H'

    def __init__(self):
        self.magic = None
        self.machine_class = None
        self.data_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.phoff = None
        self.shoff = None
        self.flags = None
        self.ehsize = None
        self.phentsize = None
        self.phnum = None
        self.shentsize = None
        self.shnum = None
        self.shstridx = None

    def load(self, f):
        self.magic, self.machine_class, self.data_encoding, self.version, self.os_abi, self.abi_version, self.nident_size = struct.unpack(ElfEHdr.FMT, f.read(struct.calcsize(ElfEHdr.FMT)))
        self.type, self.machine, self.version, self.entry, self.phoff, self.shoff, self.flags, self.ehsize, self.phentsize, self.phnum, self.shentsize, self.shnum, self.shstridx = struct.unpack(ElfEHdr.EX_FMT, f.read(struct.calcsize(ElfEHdr.EX_FMT)))

    def has_segments(self):
        return self.phentsize > 0 and self.phnum > 0

    def has_sections(self):
        return self.shentsize > 0 and self.shnum > 0

class ElfFile(object):
    def __init__(self):
        self.ehdr = None
        self.phdrs = None
        self.shdrs = None
        self.file_size = None
        self.segments = None
        self.sections = None
        self.shstrtab = None
        self.shstrtab_offset = None

    def load(self, f):
        start_offset = f.tell()
        data = f.read()
        self.file_size = len(data)
        f.seek(start_offset)

        self.ehdr = ElfEHdr()
        self.ehdr.load(f)

        self.phdrs = []
        self.segments = []
        if self.ehdr.has_segments():
            for i in xrange(self.ehdr.phnum):
                #print "[*] ElfPHdr Offset: %x" % (start_offset + self.ehdr.phoff + i * self.ehdr.phentsize)
                f.seek(start_offset + self.ehdr.phoff + i * self.ehdr.phentsize)
                phdr = ElfPHdr(i)
                phdr.load(f)
                self.phdrs.append(phdr)
                if phdr.filesz > 0:
                    f.seek(start_offset + phdr.offset)
                    data = f.read(phdr.filesz)
                else:
                    data = ''
                self.segments.append(data)

        self.shdrs = []
        self.sections = []
        if self.ehdr.has_sections():
            for i in xrange(self.ehdr.shnum):
                #print "[*] ElfSHdr Offset: %x" % (start_offset + self.ehdr.shoff + i * self.ehdr.shentsize)
                f.seek(start_offset + self.ehdr.shoff + i * self.ehdr.shentsize)
                shdr = ElfSHdr(i)
                shdr.load(f)
                self.shdrs.append(shdr)
            for i in xrange(self.ehdr.shnum):
                if self.shdrs[i].type == 3 and self.shdrs[i].size > 1:#self.shdrs[i].name_len == 9: # shstrtab
                    self.shstrtab_offset = self.shdrs[i].offset
                    print "[*] shstrtab found at: 0x%x" % self.shstrtab_offset
                    f.seek(self.shstrtab_offset)
                    self.shstrtab = f.read(self.shdrs[i].size)
                    #print self.shstrtab
            for i in xrange(self.ehdr.shnum):
                if i > 0:
                    self.shdrs[i-1].name_end = self.shdrs[i].name - 1
                    self.shdrs[i-1].name_len = self.shdrs[i-1].name_end - self.shdrs[i-1].name
                      #print "%d name_end - name - name_len: %x - %x = %x" % (i,self.shdrs[i-1].name_end, self.shdrs[i-1].name, self.shdrs[i-1].name_len)
                if i == self.ehdr.shnum-1:
                    self.shdrs[i].name_end = len(self.shstrtab) - 1
                    self.shdrs[i].name_len = self.shdrs[i].name_end - self.shdrs[i].name
                    #print "%d name_end - name - name_len: %x - %x = %x" % (i, self.shdrs[i].name_end, self.shdrs[i].name, self.shdrs[i].name_len)

if len(sys.argv) < 2:
    print "python fix_elf.py <in_elf>"
    sys.exit(0)

print "[.] Opening file %s..." % sys.argv[1]
f = open(sys.argv[1], "rb")
#f = open("ULUS10491.BIN", "rb")
#f = open("NPEZ00311.BIN", "rb")
#f = open("ULUS10567.BIN", "rb")
#f = open("NPUG80248.BIN", "rb")
#f = open("ULES00193.BIN", "rb")

elf_file = ElfFile()
elf_file.load(f)
print "[*] magic: %s" % elf_file.ehdr.magic
print "[*] machine_class: %x" % elf_file.ehdr.machine_class
print "[*] data_encoding: %x" % elf_file.ehdr.data_encoding
print "[*] version: %x" % elf_file.ehdr.version
print "[*] os_abi: %x" % elf_file.ehdr.os_abi
print "[*] abi_version: %x" % elf_file.ehdr.abi_version
print "[*] nident_size: %x" % elf_file.ehdr.nident_size
print "[*] type: 0x%x" % elf_file.ehdr.type
print "[*] machine: %x" % elf_file.ehdr.machine
print "[*] version: %x" % elf_file.ehdr.version
print "[*] entry: 0x%x" % elf_file.ehdr.entry
print "[*] phoff: 0x%x" % elf_file.ehdr.phoff
print "[*] shoff: 0x%x" % elf_file.ehdr.shoff
print "[*] flags: 0x%x" % elf_file.ehdr.flags
print "[*] ehsize: 0x%x" % elf_file.ehdr.ehsize
print "[*] phentsize: 0x%x" % elf_file.ehdr.phentsize
print "[*] phnum: 0x%x" % elf_file.ehdr.phnum
print "[*] shentsize: 0x%x" % elf_file.ehdr.shentsize
print "[*] shnum: 0x%x" % elf_file.ehdr.shnum
print "[*] shstridx: %x" % elf_file.ehdr.shstridx
# Check first name if empty or not to determine fixing
if elf_file.shstrtab[elf_file.shdrs[1].name:elf_file.shdrs[1].name_end].replace('\x00',"") == "":
    print "[!] No Section header names found!"
    print "[.] attempting to identify required sections..."
    PF_WRITE = 0x1
    PF_READ = 0x2
    PF_EXEC = 0x4
    PF_READ_EXEC = PF_READ | PF_EXEC
    PF_READ_WRITE = PF_READ | PF_WRITE
    to_fix = 5
    for i in xrange(elf_file.ehdr.shnum):
        if (elf_file.shdrs[i].flags & PF_READ_EXEC) == PF_READ_EXEC and elf_file.shdrs[i].name_len == len(".text"):
            print "[!] found .text at section %d" % i
            elf_file.shstrtab = elf_file.shstrtab[:elf_file.shdrs[i].name] + ".text" + elf_file.shstrtab[elf_file.shdrs[i].name_end:]
            to_fix -= 1
        if elf_file.shdrs[i].type == 8 and elf_file.shdrs[i].name_len == len(".bss"):
            print "[!] found .bss at section %d" % i
            elf_file.shstrtab = elf_file.shstrtab[:elf_file.shdrs[i].name] + ".bss" + elf_file.shstrtab[elf_file.shdrs[i].name_end:]
            to_fix -= 1
        if elf_file.shdrs[i].type == 1 and elf_file.shdrs[i].flags == 3 and elf_file.shdrs[i].name_len == len(".data"):
            print "[!] found .data at section %d" % i
            elf_file.shstrtab = elf_file.shstrtab[:elf_file.shdrs[i].name] + ".data" + elf_file.shstrtab[elf_file.shdrs[i].name_end:]
            to_fix -= 1
        if elf_file.shdrs[i].type == 1 and elf_file.shdrs[i].size == 0x34 and elf_file.shdrs[i].name_len == len(".rodata.sceModuleInfo"):
            print "[!] found .rodata.sceModuleInfo at section %d" % i
            elf_file.shstrtab = elf_file.shstrtab[:elf_file.shdrs[i].name] + ".rodata.sceModuleInfo" + elf_file.shstrtab[elf_file.shdrs[i].name_end:]
            to_fix -= 1
        if elf_file.shdrs[i].type == 3 and elf_file.shdrs[i].size > 1 and elf_file.shdrs[i].name_len == len(".shstrtab"):
            print "[!] found .shstrtab at section %d" % i
            elf_file.shstrtab = elf_file.shstrtab[:elf_file.shdrs[i].name] + ".shstrtab" + elf_file.shstrtab[elf_file.shdrs[i].name_end:]
            to_fix -= 1
    if to_fix == 0:
        print "[.] Writing new file..."
        f.seek(0)
        data = f.read()
        data = data[:elf_file.shstrtab_offset] + elf_file.shstrtab + data[elf_file.shstrtab_offset+len(elf_file.shstrtab):]
        open(sys.argv[1] + ".new", "wb").write(data)
    else:
        print "[!] Error. Could not identify required sections."      
print "Section Headers:"
print "  [Nr] Name                Type            Addr       Off    Size  Flg Lk Inf Al"
for i in xrange(elf_file.ehdr.shnum):
    print "   %02d %-15.15s (%02x) %08x       %08x   %06x %06x %x" % (i, elf_file.shstrtab[elf_file.shdrs[i].name:elf_file.shdrs[i].name_end].replace('\x00',""),elf_file.shdrs[i].name_len, elf_file.shdrs[i].type,elf_file.shdrs[i].addr,elf_file.shdrs[i].offset,elf_file.shdrs[i].size,elf_file.shdrs[i].flags)

f.close()
print "[.] done."
 
just when I discovered that I had saved all of my psp and psx games on an external hdd transferred to my 10TB hdd... :D I still have them on the psp and ps vita/pstv too, but I don't mind making custom injects for the ps4. I have a 2TB hdd in mine, and only 200GBs (13 games) of used space. :)
 

Featured content

Trending content

Back
Top