Advertisement
Guest User

Untitled

a guest
Jul 18th, 2020
27,667
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 15.22 KB | None | 0 0
  1. #!/usr/bin/env python2.7
  2. # (c) flatz
  3.  
  4. import sys, os, struct
  5. import argparse
  6. import shutil
  7.  
  8. from hexdump import hexdump
  9. from pprint import pprint
  10.  
  11. def align_up(x, alignment):
  12.     return (x + (alignment - 1)) & ~(alignment - 1)
  13.  
  14. def align_down(x, alignment):
  15.     return x & ~(alignment - 1)
  16.  
  17. def is_intervals_overlap(p1, p2):
  18.     return p1[0] <= p2[1] and p1[1] <= p2[0]
  19.  
  20. def check_file_magic(f, expected_magic):
  21.     old_offset = f.tell()
  22.     try:
  23.         magic = f.read(len(expected_magic))
  24.     except:
  25.         return False
  26.     finally:
  27.         f.seek(old_offset)
  28.     return magic == expected_magic
  29.  
  30. def check_sdk_version(sdk_version):
  31.     if len(sdk_version) != 10:
  32.         return False
  33.     parts = sdk_version.split('.', 2)
  34.     if len(parts) != 3:
  35.         return False
  36.     try:
  37.         lengths = [2, 3, 3]
  38.         for i, n in enumerate(parts):
  39.             if len(n) != lengths[i]:
  40.                 return False
  41.             n = int(n, 10)
  42.     except:
  43.         return False
  44.     return True
  45.  
  46. # SDK version have 001 in "patch" field
  47. def parse_sdk_version(sdk_version):
  48.     major, minor, patch = sdk_version >> 24, (sdk_version >> 12) & 0xFFF, sdk_version & 0xFFF
  49.     return major, minor, patch
  50.  
  51. def stringify_sdk_version(major, minor, patch):
  52.     return '{0:02x}.{1:03x}.{2:03x}'.format(major, minor, patch)
  53.  
  54. def unstringify_sdk_version(sdk_version):
  55.     major, minor, patch = map(lambda x: int(x, 16), sdk_version.split('.', 2))
  56.     return major, minor, patch
  57.  
  58. def build_sdk_version(major, minor, patch):
  59.     sdk_version = ((major & 0xFF) << 24) | ((minor & 0xFFF) << 12) | (patch & 0xFFF)
  60.     return sdk_version
  61.  
  62. class ElfProgramHeader(object):
  63.     FMT = '<2I6Q'
  64.  
  65.     PT_NULL = 0x0
  66.     PT_LOAD = 0x1
  67.     PT_DYNAMIC = 0x2
  68.     PT_INTERP = 0x3
  69.     PT_TLS = 0x7
  70.     PT_SCE_DYNLIBDATA = 0x61000000
  71.     PT_SCE_PROCPARAM = 0x61000001
  72.     PT_SCE_MODULE_PARAM = 0x61000002
  73.     PT_SCE_RELRO = 0x61000010
  74.     PT_SCE_COMMENT = 0x6FFFFF00
  75.     PT_SCE_VERSION = 0x6FFFFF01
  76.     PT_GNU_EH_FRAME = 0x6474E550
  77.  
  78.     PF_X = 0x1
  79.     PF_W = 0x2
  80.     PF_R = 0x4
  81.     PF_RX = PF_R | PF_X
  82.     PF_RW = PF_R | PF_W
  83.  
  84.     def __init__(self):
  85.         self.type = None
  86.         self.offset = None
  87.         self.vaddr = None
  88.         self.paddr = None
  89.         self.file_size = None
  90.         self.mem_size = None
  91.         self.flags = None
  92.         self.align = None
  93.  
  94.     def load(self, f):
  95.         data = f.read(struct.calcsize(ElfProgramHeader.FMT))
  96.         if len(data) != struct.calcsize(ElfProgramHeader.FMT):
  97.             return False
  98.         self.type, self.flags, self.offset, self.vaddr, self.paddr, self.file_size, self.mem_size, self.align = struct.unpack(ElfProgramHeader.FMT, data)
  99.         return True
  100.  
  101.     def save(self, f):
  102.         data = struct.pack(ElfProgramHeader.FMT, self.type, self.flags, self.offset, self.vaddr, self.paddr, self.file_size, self.mem_size, self.align)
  103.         if len(data) != struct.calcsize(ElfProgramHeader.FMT):
  104.             return False
  105.         f.write(data)
  106.         return True
  107.  
  108. class ElfSectionHeader(object):
  109.     FMT = '<2I4Q2I2Q'
  110.  
  111.     def __init__(self, fmt):
  112.         self.name = None
  113.         self.type = None
  114.         self.flags = None
  115.         self.addr = None
  116.         self.offset = None
  117.         self.size = None
  118.         self.link = None
  119.         self.info = None
  120.         self.align = None
  121.         self.entry_size = None
  122.  
  123.     def load(self, f):
  124.         data = f.read(struct.calcsize(ElfProgramHeader.FMT))
  125.         if len(data) != struct.calcsize(ElfProgramHeader.FMT):
  126.             return False
  127.         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)
  128.         return True
  129.  
  130.     def save(self, f):
  131.         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)
  132.         if len(data) != struct.calcsize(ElfProgramHeader.FMT):
  133.             return False
  134.         f.write(data)
  135.         return True
  136.  
  137. class ElfFile(object):
  138.     MAGIC = '\x7FELF'
  139.  
  140.     FMT = '<4s5B6xB2HI3QI6H'
  141.  
  142.     CLASS_NONE = 0
  143.     CLASS_64 = 2
  144.  
  145.     DATA_NONE = 0
  146.     DATA_LSB = 1
  147.  
  148.     VERSION_CURRENT = 1
  149.  
  150.     MACHINE_X86_64 = 0x3E
  151.  
  152.     TYPE_EXEC = 0x2
  153.     TYPE_SCE_EXEC = 0xFE00
  154.     TYPE_SCE_EXEC_ASLR = 0xFE10
  155.     TYPE_SCE_DYNAMIC = 0xFE18
  156.  
  157.     def __init__(self):
  158.         self.magic = None
  159.         self.cls = None
  160.         self.encoding = None
  161.         self.version = None
  162.         self.os_abi = None
  163.         self.abi_version = None
  164.         self.nident_size = None
  165.         self.type = None
  166.         self.machine = None
  167.         self.version = None
  168.         self.entry = None
  169.         self.phdr_offset = None
  170.         self.shdr_offset = None
  171.         self.flags = None
  172.         self.ehdr_size = None
  173.         self.phdr_size = None
  174.         self.phdr_count = None
  175.         self.shdr_size = None
  176.         self.shdr_count = None
  177.         self.shdr_strtable_idx = None
  178.  
  179.         self.phdrs = None
  180.         self.shdrs = None
  181.  
  182.     def check(self, f):
  183.         old_offset = f.tell()
  184.         try:
  185.             result = check_file_magic(f, ElfFile.MAGIC)
  186.         except:
  187.             return False
  188.         finally:
  189.             f.seek(old_offset)
  190.         return result
  191.  
  192.     def load(self, f):
  193.         data = f.read(struct.calcsize(ElfFile.FMT))
  194.         if len(data) != struct.calcsize(ElfFile.FMT):
  195.             print('error: unable to read header')
  196.             return False
  197.  
  198.         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)
  199.         if self.magic != ElfFile.MAGIC:
  200.             print('error: invalid magic: 0x{0:08X}'.format(self.magic))
  201.             return False
  202.         if self.encoding != ElfFile.DATA_LSB:
  203.             print('error: unsupported encoding: 0x{0:02X}'.format(self.encoding))
  204.             return False
  205.         if self.legacy_version != ElfFile.VERSION_CURRENT:
  206.             raise Exception('Unsupported version: 0x{0:x}'.format(self.version))
  207.         if self.cls != ElfFile.CLASS_64:
  208.             print('error: unsupported class: 0x{0:02X}'.format(self.cls))
  209.             return False
  210.         if self.type not in [ElfFile.TYPE_SCE_EXEC, ElfFile.TYPE_SCE_EXEC_ASLR, ElfFile.TYPE_SCE_DYNAMIC]:
  211.             print('error: unsupported type: 0x{0:04X}'.format(self.type))
  212.             return False
  213.         if self.machine != ElfFile.MACHINE_X86_64:
  214.             print('error: unexpected machine: 0x{0:X}'.format(self.machine))
  215.             return False
  216.         if self.ehdr_size != struct.calcsize(ElfFile.FMT):
  217.             print('error: invalid elf header size: 0x{0:X}'.format(self.ehdr_size))
  218.             return False
  219.         if self.phdr_size > 0 and self.phdr_size != struct.calcsize(ElfProgramHeader.FMT):
  220.             print('error: invalid program header size: 0x{0:X}'.format(self.phdr_size))
  221.             return False
  222.         if self.shdr_size > 0 and self.shdr_size != struct.calcsize(ElfSectionHeader.FMT):
  223.             print('error: invalid section header size: 0x{0:X}'.format(self.shdr_size))
  224.             return False
  225.  
  226.         self.phdrs = []
  227.         for i in xrange(self.phdr_count):
  228.             phdr = ElfProgramHeader()
  229.             f.seek(self.phdr_offset + i * self.phdr_size)
  230.             if not phdr.load(f):
  231.                 print('error: unable to load program header #{0}'.format(i))
  232.                 return False
  233.             self.phdrs.append(phdr)
  234.  
  235.         self.shdrs = []
  236.         #if self.shdr_size > 0:
  237.         #   for i in xrange(self.shdr_count):
  238.         #       shdr = ElfSectionHeader()
  239.         #       f.seek(self.shdr_offset + i * self.shdr_size)
  240.         #       if not shdr.load(f):
  241.         #           print('error: unable to load section header #{0}'.format(i))
  242.         #           return False
  243.         #       self.shdrs.append(shdr)
  244.  
  245.         return True
  246.  
  247.     def save_hdr(self, f):
  248.         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)
  249.         if len(data) != struct.calcsize(ElfFile.FMT):
  250.             print('error: unable to save header')
  251.             return False
  252.         f.write(data)
  253.  
  254.         for i, phdr in enumerate(self.phdrs):
  255.             f.seek(self.phdr_offset + i * self.phdr_size)
  256.             if not phdr.save(f):
  257.                 print('error: unable to save program header #{0}'.format(i))
  258.                 return False
  259.  
  260.         for i, shdr in enumerate(self.shdrs):
  261.             f.seek(self.shdr_offset + i * self.shdr_size)
  262.             if not shdr.save(f):
  263.                 print('error: unable to save section header #{0}'.format(i))
  264.                 return False
  265.  
  266.         return True
  267.  
  268.     def get_phdr_by_type(self, type):
  269.         for i, phdr in enumerate(elf.phdrs):
  270.             if phdr.type == type:
  271.                 return phdr
  272.         return None
  273.  
  274. class MyParser(argparse.ArgumentParser):
  275.     def error(self, message):
  276.         self.print_help()
  277.         sys.stderr.write('\nerror: {0}\n'.format(message))
  278.         sys.exit(2)
  279.  
  280. parser = MyParser(description='elf downgrader tool')
  281. parser.add_argument('input', type=str, help='old file')
  282. parser.add_argument('output', type=str, help='new file')
  283. parser.add_argument('--verbose', action='store_true', default=False, help='show details')
  284. parser.add_argument('--sdk-version', type=str, required=True, default='06.200.001', help='needed sdk version')
  285.  
  286. if len(sys.argv) == 1:
  287.     parser.print_usage()
  288.     sys.exit(1)
  289.  
  290. args = parser.parse_args()
  291.  
  292. input_file_path = args.input
  293. if not os.path.isfile(input_file_path):
  294.     parser.error('invalid input file: {0}'.format(input_file_path))
  295.  
  296. output_file_path = args.output
  297. if os.path.exists(output_file_path) and not os.path.isfile(output_file_path):
  298.     parser.error('invalid output file: {0}'.format(output_file_path))
  299.  
  300. if args.sdk_version and not check_sdk_version(args.sdk_version):
  301.     parser.error('bad sdk version')
  302.  
  303. shutil.copyfile(input_file_path, output_file_path)
  304.  
  305. if args.verbose:
  306.     print('processing elf file: {0}'.format(output_file_path))
  307. with open(output_file_path, 'r+b') as f:
  308.     elf = ElfFile()
  309.     if not elf.check(f):
  310.         print('error: invalid elf file format')
  311.         sys.exit(1)
  312.     if not elf.load(f):
  313.         print('error: unable to load elf file')
  314.         sys.exit(1)
  315.  
  316.     #
  317.     # Fixing proc/module param structure.
  318.     #
  319.  
  320.     if elf.type in [ElfFile.TYPE_SCE_EXEC, ElfFile.TYPE_SCE_EXEC_ASLR]:
  321.         needed_type = ElfProgramHeader.PT_SCE_PROCPARAM
  322.         param_magic = 'ORBI'
  323.         if args.verbose:
  324.             print('executable file detected')
  325.     elif elf.type == ElfFile.TYPE_SCE_DYNAMIC:
  326.         needed_type = ElfProgramHeader.PT_SCE_MODULE_PARAM
  327.         param_magic = '\xBF\xF4\x13\x3C'
  328.         if args.verbose:
  329.             print('module file detected')
  330.     else:
  331.         print('error: unsupported elf type')
  332.         sys.exit(1)
  333.  
  334.     major, minor, patch = unstringify_sdk_version(args.sdk_version)
  335.     new_sdk_version = build_sdk_version(major, minor, patch)
  336.     new_sdk_version_str = stringify_sdk_version(major, minor, patch)
  337.     if args.verbose:
  338.         print('wanted sdk version: {0}'.format(new_sdk_version_str))
  339.  
  340.     if args.verbose:
  341.         print('searching for {0} param segment...'.format('proc' if needed_type == ElfProgramHeader.PT_SCE_PROCPARAM else 'module'))
  342.     phdr = elf.get_phdr_by_type(needed_type)
  343.     if phdr is not None:
  344.         if args.verbose:
  345.             print('parsing param structure...')
  346.         f.seek(phdr.offset)
  347.         data = f.read(phdr.file_size)
  348.         if len(data) != phdr.file_size:
  349.             print('error: insufficient data read')
  350.             sys.exit(1)
  351.         param_size, = struct.unpack('<I', data[0x0:0x4])
  352.         if param_size < 0x14:
  353.             print('error: param structure is too small')
  354.             sys.exit(1)
  355.         data = data[:param_size]
  356.         if data[0x8:0xC] != param_magic:
  357.             print('error: unexpected param structure format')
  358.             sys.exit(1)
  359.  
  360.         old_sdk_version, = struct.unpack('<I', data[0x10:0x14])
  361.         major, minor, patch = parse_sdk_version(old_sdk_version)
  362.         old_sdk_version_str = stringify_sdk_version(major, minor, patch)
  363.         if args.verbose:
  364.             print('sdk version: {0}'.format(old_sdk_version_str))
  365.  
  366.         if old_sdk_version > new_sdk_version:
  367.             if args.verbose:
  368.                 print('fixing param structure...')
  369.             f.seek(phdr.offset + 0x10)
  370.             f.write(struct.pack('<I', new_sdk_version))
  371.     else:
  372.         print('warning: param segment not found (elf from old sdk?)')
  373.  
  374.     #
  375.     # Removing memory holes in PHDRs.
  376.     # Prevents error on old kernel versions: uncountigous RELRO and DATA segments
  377.     #
  378.  
  379.     if new_sdk_version < 0x06000000: # less than 6.00 fw
  380.         segs = []
  381.         for i, phdr in enumerate(elf.phdrs):
  382.             if phdr.type not in [ElfProgramHeader.PT_LOAD, ElfProgramHeader.PT_SCE_RELRO]:
  383.                 continue
  384.             if phdr.type == ElfProgramHeader.PT_LOAD and phdr.flags == ElfProgramHeader.PF_RX:
  385.                 #print('skipping text segment...')
  386.                 continue
  387.             #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))
  388.             segs.append(phdr)
  389.  
  390.         #for i, phdr in enumerate(segs):
  391.         #   print('vaddr:0x{0:X} mem_size:0x{1:X}'.format(phdr.vaddr, phdr.mem_size))
  392.  
  393.         segs.sort(key=lambda x: (x.vaddr, -(x.vaddr + x.mem_size)))
  394.  
  395.         i, count = 0, len(segs)
  396.         while i < count:
  397.             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)):
  398.                 #print('removing seg vaddr:0x{0:X} mem_size:0x{1:X}'.format(segs[i].vaddr, segs[i].mem_size))
  399.                 #print('  previous seg vaddr:0x{0:X} mem_size:0x{1:X}'.format(segs[i - 1].vaddr, segs[i - 1].mem_size))
  400.                 segs = segs[:i] + segs[i + 1:]
  401.                 count -= 1
  402.                 continue
  403.             i += 1
  404.  
  405.         count = len(segs)
  406.         has_changes = False
  407.         for i in xrange(count):
  408.             mem_size_aligned = align_up(segs[i].mem_size, 0x4000)
  409.             if (i + 1) < count and (segs[i].vaddr + mem_size_aligned) < segs[i + 1].vaddr:
  410.                 segs[i].mem_size = segs[i + 1].vaddr - segs[i].vaddr
  411.                 has_changes = True
  412.  
  413.         #print('')
  414.  
  415.         #for i, phdr in enumerate(segs):
  416.         #   #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))
  417.         #   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))
  418.  
  419.         if has_changes:
  420.             if args.verbose:
  421.                 print('removing memory holes...')
  422.  
  423.     #
  424.     # Fixing version information in version segment.
  425.     #
  426.  
  427.     if args.verbose:
  428.         print('searching for version segment...')
  429.     phdr = elf.get_phdr_by_type(ElfProgramHeader.PT_SCE_VERSION)
  430.     if phdr is not None:
  431.         if args.verbose:
  432.             print('parsing library list...')
  433.         f.seek(phdr.offset)
  434.         data = f.read(phdr.file_size)
  435.         if len(data) != phdr.file_size:
  436.             print('error: insufficient data read')
  437.             sys.exit(1)
  438.  
  439.         has_changes = False
  440.         if phdr.file_size > 0:
  441.             offset = 0
  442.             while offset < phdr.file_size:
  443.                 length = ord(data[offset])
  444.                 offset += 1
  445.                 name = data[offset:offset + length]
  446.                 name, old_sdk_version = name.split(':', 1)
  447.                 if len(old_sdk_version) != struct.calcsize('I'):
  448.                     print('error: unexpected library list entry format')
  449.                     sys.exit(1)
  450.  
  451.                 old_sdk_version, = struct.unpack('>I', old_sdk_version)
  452.                 major, minor, patch = parse_sdk_version(old_sdk_version)
  453.                 old_sdk_version_str = stringify_sdk_version(major, minor, patch)
  454.                 if args.verbose:
  455.                     print('{0} (sdk version: {1})'.format(name, old_sdk_version_str))
  456.  
  457.                 if old_sdk_version > new_sdk_version:
  458.                     data = data[:offset] + name + ':' + struct.pack('>I', new_sdk_version) + data[offset + length:]
  459.                     has_changes = True
  460.  
  461.                 offset += length
  462.  
  463.             if has_changes:
  464.                 if args.verbose:
  465.                     print('fixing sdk versions in library list...')
  466.                 f.seek(phdr.offset)
  467.                 f.write(data)
  468.     else:
  469.         if args.verbose:
  470.             print('version segment not found')
  471.  
  472.     #
  473.     # Fixing section headers.
  474.     #
  475.  
  476.     if args.verbose:
  477.         print('fixing elf header...')
  478.  
  479.     # Prevents error in orbis-bin:
  480.     #   Section header offset (XXX) exceeds file size (YYY).
  481.     elf.shdr_offset = 0
  482.     elf.shdr_count = 0
  483.  
  484.     f.seek(0)
  485.     if not elf.save_hdr(f):
  486.         print('error: unable to save elf file')
  487.         sys.exit(1)
  488.  
  489. if args.verbose:
  490.     print('done')
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement