text: #!/usr/bin/python
  
  import argparse
  import io
  import os
  import struct
  import re
  
  import lief
  import idb
  
  # Indices
  INDEX_NULL = 0
  INDEX_INTERP = 1
  INDEX_DYNSYM = 2
  INDEX_DYNSTR = 3
  INDEX_TEXT = 4
  INDEX_DYNAMIC = 5
  INDEX_SHSTRTAB = 6
  
  # Dynamic tags
  DT_SCE_UNK_SYSCALLTAB   = 0x6100002F
  DT_SCE_UNK_SYSCALLTABSZ = 0x61000031
  
  # Utilities
  class Section(object):
      def __init__(self):
          self.name_idx = 0
          self.type = lief.ELF.SECTION_TYPES.NULL
          self.flags = lief.ELF.SECTION_FLAGS.NONE
          self.virtual_address = 0
          self.offset = 0
          self.size = 0
          self.link = 0
          self.information = 0
          self.alignment = 0
          self.entry_size = 0
          # Helpers
          self.name = ""
          self.content = b""
  
      def serialize(self):
          section_fmt = 'IIQQQQIIQQ'
          assert struct.calcsize(section_fmt) == 0x40
          return struct.pack(section_fmt,
              self.name_idx,
              int(self.type),
              int(self.flags),
              self.virtual_address,
              self.offset,
              self.size,
              self.link,
              self.information,
              self.alignment,
              self.entry_size)
  
  class Symbol(object):
      fmt = 'IBBHQQ'
      def __init__(self, data=None):
          if not data:
              return
          assert struct.calcsize(self.fmt) == 0x18
          fields = struct.unpack(self.fmt, data)
          self.name = fields[0]
          self.info = fields[1]
          self.other = fields[2]
          self.shndx = fields[3]
          self.value = fields[4]
          self.size = fields[5]
          # Helpers
          self.content = b""
  
      def serialize(self):
          assert struct.calcsize(self.fmt) == 0x18
          return struct.pack(self.fmt,
              self.name,
              self.info,
              self.other,
              self.shndx,
              self.value,
              self.size)
  
  def patch_i08(stream, offset, value):
      data = struct.pack('B', value)
      stream.seek(offset)
      stream.write(data)
      
  def patch_i16(stream, offset, value):
      data = struct.pack('H', value)
      stream.seek(offset)
      stream.write(data)
      
  def patch_i32(stream, offset, value):
      data = struct.pack('I', value)
      stream.seek(offset)
      stream.write(data)
      
  def patch_i64(stream, offset, value):
      data = struct.pack('Q', value)
      stream.seek(offset)
      stream.write(data)
  
  def read_i32(elf, addr):
      data = bytes(elf.get_content_from_virtual_address(addr, 4))
      value = struct.unpack('I', data)[0]
      return value
  
  def get_load_segment(elf, index=0):
      for segment in elf.segments:
          if segment.type != lief.ELF.SEGMENT_TYPES.LOAD:
              continue
          if index == 0:
              return segment
          index -= 1
      raise Exception("Segment not found")
  
  def get_dynamic_segment(elf, index=0):
      for segment in elf.segments:
          if segment.type == lief.ELF.SEGMENT_TYPES.DYNAMIC:
              return segment
      raise Exception("Segment not found")
  
  def get_dynamic_entry(elf, tag):
      for de in elf.dynamic_entries:
          if de.tag == tag:
              return de
      raise Exception("Segment not found")
  
  def parse_map_symbols(path_map):
      with open(path_map, 'r') as f:
          for i in range(4):
              f.readline()
          for line in f.readlines():
              match = re.search("[0-9A-F]{8}:([0-9A-F]{16})\s+(.*)", line)
              if not match: break
              g = match.groups()
              yield g[1], int(g[0],16), 0
  
  # Sections
  def create_section_null():
      return Section()
  
  
  def create_section_interp(elf):
      section = Section()
      section.name = ".interp"
      section.type = lief.ELF.SECTION_TYPES.PROGBITS
      section.flags = lief.ELF.SECTION_FLAGS.ALLOC
      section.link = 0
      section.information = 0
      section.alignment = 1
      section.entry_size = 0
      section.size = len(elf.dynamic_entries) * section.entry_size
      
      # Assume it appears after Ehdr+Phdr's of the first LOAD segment
      hdr = elf.header
      assert hdr.header_size == hdr.program_header_offset
      segment = get_load_segment(elf, index=0)
      section.virtual_address = segment.virtual_address
      section.virtual_address += hdr.header_size
      section.virtual_address += hdr.numberof_segments * hdr.program_header_size
      section.offset = elf.virtual_address_to_offset(segment.virtual_address)
      # Assume it appears before the DT_INIT function
      init_vaddr = get_dynamic_entry(elf, lief.ELF.DYNAMIC_TAGS.INIT).value
      assert init_vaddr > section.virtual_address
      section.size = init_vaddr - section.virtual_address
      return section
  
  
  def create_section_dynsym(elf, path_idb=None, path_map=None):
      section = Section()
      section.name = ".dynsym"
      section.type = lief.ELF.SECTION_TYPES.DYNSYM
      section.flags = lief.ELF.SECTION_FLAGS.ALLOC
      section.link = INDEX_DYNSTR
      section.information = 0
      section.alignment = 8
      section.offset = 0
      section.entry_size = 0
      section.size = 0
      section.virtual_address = 0
      section.entry_size = 0x18
  
      index = 1
      sym = Symbol()
      if path_idb:
          with idb.from_file(path_idb) as db:
              api = idb.IDAPython(db)
              for ea in api.idautils.Functions():
                  name = api.idc.GetFunctionName(ea)
                  sym.name = index
                  sym.info = 0x12
                  sym.other = 0
                  sym.shndx = 0x0 # TODO
                  sym.value = ea
                  sym.size = api.idc.GetFunctionAttr(ea, api.idc.FUNCATTR_END) - ea
                  section.content += sym.serialize()
                  section.size += section.entry_size
                  index += len(name) + 1
      elif path_map:
          sym = Symbol()
          for name, addr, size in parse_map_symbols(path_map):
              sym.name = index
              sym.info = 0x12
              sym.other = 0
              sym.shndx = 0x0 # TODO
              sym.value = addr
              sym.size = 0x10000 # TODO
              section.content += sym.serialize()
              section.size += section.entry_size
              index += len(name) + 1
      return section
  
  
  def create_section_dynstr(elf, path_idb=None, path_map=None):
      section = Section()
      section.name = ".dynstr"
      section.type = lief.ELF.SECTION_TYPES.STRTAB
      section.flags = lief.ELF.SECTION_FLAGS.ALLOC
      section.link = 0
      section.information = 0
      section.alignment = 1
      section.offset = 0
      section.entry_size = 0
      section.size = 0
  
      section.virtual_address = 0
      if path_idb:
          section.content = b"\x00"
          with idb.from_file(path_idb) as db:
              api = idb.IDAPython(db)
              for ea in api.idautils.Functions():
                  section.content += api.idc.GetFunctionName(ea).encode('ascii')
                  section.content += b"\x00"
      elif path_map:
          section.content = b"\x00"
          for name, _, _ in parse_map_symbols(path_map):
              section.content += name.encode('ascii')
              section.content += b"\x00"
      return section
  
  
  def create_section_text(elf):
      section = Section()
      section.name = ".text"
      section.type = lief.ELF.SECTION_TYPES.PROGBITS
      section.flags = lief.ELF.SECTION_FLAGS.ALLOC | lief.ELF.SECTION_FLAGS.EXECINSTR
      section.link = 0
      section.information = 0
      section.alignment = 16
      section.entry_size = 0
      section.size = len(elf.dynamic_entries) * section.entry_size
  
      # TODO
      segment = get_load_segment(elf, index=0)
      section.virtual_address = segment.virtual_address
      section.offset = elf.virtual_address_to_offset(section.virtual_address)
      section.size = segment.virtual_size
      return section
  
  
  def create_section_dynamic(elf):
      section = Section()
      section.name = ".dynamic"
      section.type = lief.ELF.SECTION_TYPES.DYNAMIC
      section.flags = lief.ELF.SECTION_FLAGS.WRITE | lief.ELF.SECTION_FLAGS.ALLOC
      section.link = INDEX_DYNSTR
      section.information = 0
      section.entry_size = 0x10
  
      segment = get_dynamic_segment(elf)
      section.virtual_address = segment.virtual_address
      section.offset = elf.virtual_address_to_offset(segment.virtual_address)
      section.size = segment.physical_size
      section.alignment = segment.alignment
      return section
  
  
  def create_section_shstrtab(sections):
      section = Section()
      section.name = ".shstrtab"
      section.type = lief.ELF.SECTION_TYPES.STRTAB
      section.flags = lief.ELF.SECTION_FLAGS.NONE
      section.link = 0
      section.information = 0
      section.alignment = 1
      section.offset = 0
      section.entry_size = 0
      section.size = 0
  
      section.content = b''
      for other_section in sections:
          other_section.name_idx = len(section.content)
          section.content += other_section.name.encode('ascii') + b'\x00'
      section.name_idx = len(section.content)
      section.content += section.name.encode('ascii') + b'\x00'
      return section
      
  
  def patch_sections(path_in, path_out, path_idb=None, path_map=None):
      elf = lief.parse(path_in)
      assert len(elf.sections) == 0, "Expected an executable without sections"
      sections = []
      sections.append(create_section_null())
      sections.append(create_section_interp(elf))
      sections.append(create_section_dynsym(elf, path_idb, path_map))
      sections.append(create_section_dynstr(elf, path_idb, path_map))
      sections.append(create_section_text(elf))
      sections.append(create_section_dynamic(elf))
      sections.append(create_section_shstrtab(sections))
  
      e_shentsize = 0x40
      e_shoff = os.path.getsize(path_in)
      e_shnum = len(sections)
      e_shstrndx = INDEX_SHSTRTAB
  
      with open(path_out, 'wb') as f:
          with open(path_in, 'rb') as binary:
              f.write(binary.read())
          patch_i64(f, 0x28, e_shoff)
          patch_i08(f, 0x3A, e_shentsize)
          patch_i08(f, 0x3C, e_shnum)
          patch_i08(f, 0x3E, e_shstrndx)
          f.seek(0, io.SEEK_END)
          offset = e_shoff + (e_shnum * e_shentsize)
          for section in sections:
              if section.content:
                  section.offset = offset
                  section.size = len(section.content)
                  offset += len(section.content)
              f.write(section.serialize())
          for section in sections:
              f.write(section.content)
  
  
  def main():
      parser = argparse.ArgumentParser(
          description='Generate ELF sections from dynamic entries in PS4 4.xx/5.xx kernels.')
      parser.add_argument('--idb',
          metavar='input.idb', help='Path to IDA database',
      )
      parser.add_argument('--map',
          metavar='input.map', help='Path to MAP file (fallback)',
      )
      parser.add_argument('input',
          metavar='input.elf', help='Path to input file',
      )
      parser.add_argument('output',
          metavar='output.elf', help='Path to output file',
      )
      args = parser.parse_args()
      patch_sections(args.input, args.output, args.idb, args.map)
  
  if __name__ == '__main__':
      main()
  