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

Discussion in 'PS4 News' started by STLcardsWS, May 9, 2018.

By STLcardsWS on May 9, 2018 at 10:46 PM
  1. 7,710
    6,660
    747
    STLcardsWS

    STLcardsWS Administrator

    Joined:
    Sep 18, 2014
    Messages:
    7,710
    Likes Received:
    6,660
    Trophy Points:
    747
    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: May 10, 2018
    pinky, jolek, barelynotlegal and 4 others like this.

Comments

Discussion in 'PS4 News' started by STLcardsWS, May 9, 2018.

    1. Naked_Snake1995
      Naked_Snake1995
      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
      pinky likes this.
    2. mysis
      mysis
      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."
      littlebalup, pinky, CYB3R18 and 12 others like this.
    3. kozarovv
      kozarovv
      Awesome work @mysis ! Thanks for sharing!
      pinky, sabin1981, STLcardsWS and 3 others like this.
    4. mysis
      mysis
      Np, i was sitting on it for some weeks already.... :)
    5. CYB3R18
      CYB3R18
      Nice! we could see some games in pkg soon..
      pinky likes this.
    6. pinky
      pinky
      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. :)
      CYB3R18 likes this.

Share This Page