rajkosto

nxo64.py with hack to fix zero after BL in sysmodule

Jun 15th, 2018
893
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 24.67 KB | None | 0 0
  1. # Copyright 2017 Reswitched Team
  2. #
  3. # Permission to use, copy, modify, and/or distribute this software for any purpose with or
  4. # without fee is hereby granted, provided that the above copyright notice and this permission
  5. # notice appear in all copies.
  6. #
  7. # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
  8. # SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
  9. # THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
  10. # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
  11. # CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
  12. # OR PERFORMANCE OF THIS SOFTWARE.
  13.  
  14. # nxo64.py: IDA loader (and library for reading nso/nro files)
  15.  
  16. import gzip, math, os, re, struct, sys
  17. from struct import unpack as up, pack as pk
  18.  
  19. from io import BytesIO
  20. from cStringIO import StringIO
  21.  
  22. import lz4.block
  23.  
  24. uncompress = lz4.block.decompress
  25.  
  26. def kip1_blz_decompress(compressed, fixZeroAfterBL=False):
  27.     compressed_size, init_index, uncompressed_addl_size = struct.unpack('<III', compressed[-0xC:])
  28.     decompressed = compressed[:] + '\x00' * uncompressed_addl_size
  29.     decompressed_size = len(decompressed)
  30.     if len(compressed) != compressed_size:
  31.         assert len(compressed) > compressed_size
  32.         compressed = compressed[len(compressed) - compressed_size:]
  33.     if not (compressed_size + uncompressed_addl_size):
  34.         return ''
  35.     compressed = map(ord, compressed)
  36.     decompressed = map(ord, decompressed)
  37.     index = compressed_size - init_index
  38.     outindex = decompressed_size
  39.     while outindex > 0:
  40.         index -= 1
  41.         control = compressed[index]
  42.         for i in xrange(8):
  43.             if control & 0x80:
  44.                 if index < 2:
  45.                     raise ValueError('Compression out of bounds!')
  46.                 index -= 2
  47.                 segmentoffset = compressed[index] | (compressed[index+1] << 8)
  48.                 segmentsize = ((segmentoffset >> 12) & 0xF) + 3
  49.                 segmentoffset &= 0x0FFF
  50.                 segmentoffset += 2
  51.                 if outindex < segmentsize:
  52.                     raise ValueError('Compression out of bounds!')
  53.                 for j in xrange(segmentsize):
  54.                     if outindex + segmentoffset >= decompressed_size:
  55.                         raise ValueError('Compression out of bounds!')
  56.                     data = decompressed[outindex+segmentoffset]
  57.                     outindex -= 1
  58.                     decompressed[outindex] = data
  59.             else:
  60.                 if outindex < 1:
  61.                     raise ValueError('Compression out of bounds!')
  62.                 outindex -= 1
  63.                 index -= 1
  64.                 decompressed[outindex] = compressed[index]
  65.             control <<= 1
  66.             control &= 0xFF
  67.             if not outindex:
  68.                 break
  69.  
  70.     if fixZeroAfterBL is True:
  71.         for i in xrange(0, len(decompressed), 4):
  72.             if (i >= 4) and (decompressed[i+0] == 0) and (decompressed[i+1] == 0) and (decompressed[i+2] == 0) and (decompressed[i+3] == 0):
  73.                 prevInstr = decompressed[i-4] | (decompressed[i-3] << 8) | (decompressed[i-2] << 16) | (decompressed[i-1] << 24)
  74.                 blMask = 0xEC000000
  75.                 blBits = 0x84000000
  76.                 if (prevInstr & blMask) == blBits:
  77.                     decompressed[i+0] = 0xC0
  78.                     decompressed[i+1] = 0x03
  79.                     decompressed[i+2] = 0x5F
  80.                     decompressed[i+3] = 0xD6
  81.  
  82.     return ''.join(map(chr, decompressed))
  83.  
  84. class BinFile(object):
  85.     def __init__(self, li):
  86.         self._f = li
  87.  
  88.     def read(self, arg):
  89.         if isinstance(arg, str):
  90.             fmt = '<' + arg
  91.             size = struct.calcsize(fmt)
  92.             raw = self._f.read(size)
  93.             out = struct.unpack(fmt, raw)
  94.             if len(out) == 1:
  95.                 return out[0]
  96.             return out
  97.         elif arg is None:
  98.             return self._f.read()
  99.         else:
  100.             out = self._f.read(arg)
  101.             return out
  102.  
  103.     def read_from(self, arg, offset):
  104.         old = self.tell()
  105.         try:
  106.             self.seek(offset)
  107.             out = self.read(arg)
  108.         finally:
  109.             self.seek(old)
  110.         return out
  111.  
  112.     def seek(self, off):
  113.         self._f.seek(off)
  114.  
  115.     def close(self):
  116.         self._f.close()
  117.  
  118.     def tell(self):
  119.         return self._f.tell()
  120.  
  121.  
  122. (DT_NULL, DT_NEEDED, DT_PLTRELSZ, DT_PLTGOT, DT_HASH, DT_STRTAB, DT_SYMTAB, DT_RELA, DT_RELASZ,
  123.  DT_RELAENT, DT_STRSZ, DT_SYMENT, DT_INIT, DT_FINI, DT_SONAME, DT_RPATH, DT_SYMBOLIC, DT_REL,
  124.  DT_RELSZ, DT_RELENT, DT_PLTREL, DT_DEBUG, DT_TEXTREL, DT_JMPREL, DT_BIND_NOW, DT_INIT_ARRAY,
  125.  DT_FINI_ARRAY, DT_INIT_ARRAYSZ, DT_FINI_ARRAYSZ, DT_RUNPATH, DT_FLAGS) = xrange(31)
  126. DT_GNU_HASH = 0x6ffffef5
  127. DT_VERSYM = 0x6ffffff0
  128. DT_RELACOUNT = 0x6ffffff9
  129. DT_RELCOUNT = 0x6ffffffa
  130. DT_FLAGS_1 = 0x6ffffffb
  131. DT_VERDEF = 0x6ffffffc
  132. DT_VERDEFNUM = 0x6ffffffd
  133.  
  134. STT_NOTYPE = 0
  135. STT_OBJECT = 1
  136. STT_FUNC = 2
  137. STT_SECTION = 3
  138.  
  139. STB_LOCAL = 0
  140. STB_GLOBAL = 1
  141. STB_WEAK = 2
  142.  
  143. R_ARM_ABS32 = 2
  144. R_ARM_TLS_DESC = 13
  145. R_ARM_GLOB_DAT = 21
  146. R_ARM_JUMP_SLOT = 22
  147. R_ARM_RELATIVE = 23
  148.  
  149. R_AARCH64_ABS64 = 257
  150. R_AARCH64_GLOB_DAT = 1025
  151. R_AARCH64_JUMP_SLOT = 1026
  152. R_AARCH64_RELATIVE = 1027
  153. R_AARCH64_TLSDESC = 1031
  154.  
  155. MULTIPLE_DTS = set([DT_NEEDED])
  156.  
  157.  
  158. class Range(object):
  159.     def __init__(self, start, size):
  160.         self.start = start
  161.         self.size = size
  162.         self.end = start+size
  163.         self._inclend = start+size-1
  164.  
  165.     def overlaps(self, other):
  166.         return self.start <= other._inclend and other.start <= self._inclend
  167.  
  168.     def includes(self, other):
  169.         return other.start >= self.start and other._inclend <= self._inclend
  170.  
  171.     def __repr__(self):
  172.         return 'Range(0x%X -> 0x%X)' % (self.start, self.end)
  173.  
  174.  
  175. class Segment(object):
  176.     def __init__(self, r, name, kind):
  177.         self.range = r
  178.         self.name = name
  179.         self.kind = kind
  180.         self.sections = []
  181.  
  182.     def add_section(self, s):
  183.         for i in self.sections:
  184.             assert not i.range.overlaps(s.range), '%r overlaps %r' % (s, i)
  185.         self.sections.append(s)
  186.  
  187.  
  188. class Section(object):
  189.     def __init__(self, r, name):
  190.         self.range = r
  191.         self.name = name
  192.  
  193.     def __repr__(self):
  194.         return 'Section(%r, %r)' % (self.range, self.name)
  195.  
  196.  
  197. def suffixed_name(name, suffix):
  198.     if suffix == 0:
  199.         return name
  200.     return '%s.%d' % (name, suffix)
  201.  
  202.  
  203. class SegmentBuilder(object):
  204.     def __init__(self):
  205.         self.segments = []
  206.  
  207.     def add_segment(self, start, size, name, kind):
  208.         r = Range(start, size)
  209.         for i in self.segments:
  210.             assert not r.overlaps(i.range)
  211.         self.segments.append(Segment(r, name, kind))
  212.  
  213.     def add_section(self, name, start, end=None, size=None):
  214.         assert end is None or size is None
  215.         if size is None:
  216.             size = end-start
  217.         assert size > 0
  218.         r = Range(start, size)
  219.         for i in self.segments:
  220.             if i.range.includes(r):
  221.                 i.add_section(Section(r, name))
  222.                 return
  223.         assert False, "no containing segment for %r" % (name,)
  224.  
  225.     def flatten(self):
  226.         self.segments.sort(key=lambda s: s.range.start)
  227.         parts = []
  228.         for segment in self.segments:
  229.             suffix = 0
  230.             segment.sections.sort(key=lambda s: s.range.start)
  231.             pos = segment.range.start
  232.             for section in segment.sections:
  233.                 if pos < section.range.start:
  234.                     parts.append((pos, section.range.start, suffixed_name(segment.name, suffix), segment.kind))
  235.                     suffix += 1
  236.                     pos = section.range.start
  237.                 parts.append((section.range.start, section.range.end, section.name, segment.kind))
  238.                 pos = section.range.end
  239.             if pos < segment.range.end:
  240.                 parts.append((pos, segment.range.end, suffixed_name(segment.name, suffix), segment.kind))
  241.                 suffix += 1
  242.                 pos = segment.range.end
  243.         return parts
  244.  
  245.  
  246. class ElfSym(object):
  247.     def __init__(self, name, info, other, shndx, value, size):
  248.         self.name = name
  249.         self.shndx = shndx
  250.         self.value = value
  251.         self.size = size
  252.  
  253.         self.vis = other & 3
  254.         self.type = info & 0xF
  255.         self.bind = info >> 4
  256.  
  257.     def __repr__(self):
  258.         return 'Sym(name=%r, shndx=0x%X, value=0x%X, size=0x%X, vis=%r, type=%r, bind=%r)' % (
  259.             self.name, self.shndx, self.value, self.size, self.vis, self.type, self.bind)
  260.  
  261.  
  262. class NxoFileBase(object):
  263.     def __init__(self, f, tloc, tsize, rloc, rsize, dloc, dsize):
  264.         self.textoff    = tloc
  265.         self.textsize   = tsize
  266.         self.rodataoff  = rloc
  267.         self.rodatasize = rsize
  268.         self.dataoff    = dloc
  269.         flatsize = dloc + dsize
  270.  
  271.  
  272.         self.binfile = f
  273.  
  274.         # read MOD
  275.         self.modoff = f.read_from('I', 4)
  276.  
  277.         f.seek(self.modoff)
  278.         if f.read('4s') != 'MOD0':
  279.             raise NxoException('invalid MOD0 magic')
  280.  
  281.         self.dynamicoff = self.modoff + f.read('i')
  282.         self.bssoff     = self.modoff + f.read('i')
  283.         self.bssend     = self.modoff + f.read('i')
  284.         self.unwindoff  = self.modoff + f.read('i')
  285.         self.unwindend  = self.modoff + f.read('i')
  286.         self.moduleoff  = self.modoff + f.read('i')
  287.  
  288.  
  289.         self.datasize = self.bssoff - self.dataoff
  290.         self.bsssize = self.bssend - self.bssoff
  291.  
  292.  
  293.         self.segment_builder = builder = SegmentBuilder()
  294.         for off,sz,name,kind in [
  295.             (self.textoff, self.textsize, ".text", "CODE"),
  296.             (self.rodataoff, self.rodatasize, ".rodata", "CONST"),
  297.             (self.dataoff, self.datasize, ".data", "DATA"),
  298.             (self.bssoff, self.bsssize, ".bss", "BSS"),
  299.         ]:
  300.             builder.add_segment(off, sz, name, kind)
  301.  
  302.         # read dynamic
  303.         self.armv7 = (f.read_from('Q', self.dynamicoff) > 0xFFFFFFFF or f.read_from('Q', self.dynamicoff+0x10) > 0xFFFFFFFF)
  304.         self.offsize = 4 if self.armv7 else 8
  305.  
  306.         f.seek(self.dynamicoff)
  307.         self.dynamic = dynamic = {}
  308.         for i in MULTIPLE_DTS:
  309.             dynamic[i] = []
  310.         for i in xrange((flatsize - self.dynamicoff) / 0x10):
  311.             tag, val = f.read('II' if self.armv7 else 'QQ')
  312.             if tag == DT_NULL:
  313.                 break
  314.             if tag in MULTIPLE_DTS:
  315.                 dynamic[tag].append(val)
  316.             else:
  317.                 dynamic[tag] = val
  318.         builder.add_section('.dynamic', self.dynamicoff, end=f.tell())
  319.  
  320.         # read .dynstr
  321.         if DT_STRTAB in dynamic and DT_STRSZ in dynamic:
  322.             f.seek(dynamic[DT_STRTAB])
  323.             self.dynstr = f.read(dynamic[DT_STRSZ])
  324.         else:
  325.             self.dynstr = '\0'
  326.             print 'warning: no dynstr'
  327.  
  328.         for startkey, szkey, name in [
  329.             (DT_STRTAB, DT_STRSZ, '.dynstr'),
  330.             (DT_INIT_ARRAY, DT_INIT_ARRAYSZ, '.init_array'),
  331.             (DT_FINI_ARRAY, DT_FINI_ARRAYSZ, '.fini_array'),
  332.             (DT_RELA, DT_RELASZ, '.rela.dyn'),
  333.             (DT_REL, DT_RELSZ, '.rel.dyn'),
  334.             (DT_JMPREL, DT_PLTRELSZ, ('.rel.plt' if self.armv7 else '.rela.plt')),
  335.         ]:
  336.             if startkey in dynamic and szkey in dynamic:
  337.                 builder.add_section(name, dynamic[startkey], size=dynamic[szkey])
  338.  
  339.         self.needed = [self.get_dynstr(i) for i in self.dynamic[DT_NEEDED]]
  340.  
  341.         # load .dynsym
  342.         self.symbols = symbols = []
  343.         f.seek(dynamic[DT_SYMTAB])
  344.         while True:
  345.             if dynamic[DT_SYMTAB] < dynamic[DT_STRTAB] and f.tell() >= dynamic[DT_STRTAB]:
  346.                 break
  347.             if self.armv7:
  348.                 st_name, st_value, st_size, st_info, st_other, st_shndx = f.read('IIIBBH')
  349.             else:
  350.                 st_name, st_info, st_other, st_shndx, st_value, st_size = f.read('IBBHQQ')
  351.             if st_name > len(self.dynstr):
  352.                 break
  353.             symbols.append(ElfSym(self.get_dynstr(st_name), st_info, st_other, st_shndx, st_value, st_size))
  354.         builder.add_section('.dynsym', dynamic[DT_SYMTAB], end=f.tell())
  355.  
  356.         self.plt_entries = []
  357.         self.relocations = []
  358.         locations = set()
  359.         if DT_REL in dynamic:
  360.             locations |= self.process_relocations(f, symbols, dynamic[DT_REL], dynamic[DT_RELSZ])
  361.  
  362.         if DT_RELA in dynamic:
  363.             locations |= self.process_relocations(f, symbols, dynamic[DT_RELA], dynamic[DT_RELASZ])
  364.  
  365.         if DT_JMPREL in dynamic:
  366.             pltlocations = self.process_relocations(f, symbols, dynamic[DT_JMPREL], dynamic[DT_PLTRELSZ])
  367.             locations |= pltlocations
  368.  
  369.             plt_got_start = min(pltlocations)
  370.             plt_got_end = max(pltlocations) + self.offsize
  371.             if DT_PLTGOT in dynamic:
  372.                 builder.add_section('.got.plt', dynamic[DT_PLTGOT], end=plt_got_end)
  373.  
  374.             if not self.armv7:
  375.                 f.seek(0)
  376.                 text = f.read(self.textsize)
  377.                 last = 12
  378.                 while True:
  379.                     pos = text.find(struct.pack('<I', 0xD61F0220), last)
  380.                     if pos == -1: break
  381.                     last = pos+1
  382.                     if (pos % 4) != 0: continue
  383.                     off = pos - 12
  384.                     a, b, c, d = struct.unpack_from('<IIII', text, off)
  385.                     if d == 0xD61F0220 and (a & 0x9f00001f) == 0x90000010 and (b & 0xffe003ff) == 0xf9400211:
  386.                         base = off & ~0xFFF
  387.                         immhi = (a >> 5) & 0x7ffff
  388.                         immlo = (a >> 29) & 3
  389.                         paddr = base + ((immlo << 12) | (immhi << 14))
  390.                         poff = ((b >> 10) & 0xfff) << 3
  391.                         target = paddr + poff
  392.                         if plt_got_start <= target < plt_got_end:
  393.                             self.plt_entries.append((off, target))
  394.                 builder.add_section('.plt', min(self.plt_entries)[0], end=max(self.plt_entries)[0] + 0x10)
  395.  
  396.             # try to find the ".got" which should follow the ".got.plt"
  397.             good = False
  398.             got_end = plt_got_end + self.offsize
  399.             while got_end in locations and (DT_INIT_ARRAY not in dynamic or got_end < dynamic[DT_INIT_ARRAY]):
  400.                 good = True
  401.                 got_end += self.offsize
  402.  
  403.             if good:
  404.                 builder.add_section('.got', plt_got_end, end=got_end)
  405.  
  406.         self.sections = []
  407.         for start, end, name, kind in builder.flatten():
  408.             self.sections.append((start, end, name, kind))
  409.  
  410.  
  411.     def process_relocations(self, f, symbols, offset, size):
  412.         locations = set()
  413.         f.seek(offset)
  414.         relocsize = 8 if self.armv7 else 0x18
  415.         for i in xrange(size / relocsize):
  416.             # NOTE: currently assumes all armv7 relocs have no addends,
  417.             # and all 64-bit ones do.
  418.             if self.armv7:
  419.                 offset, info = f.read('II')
  420.                 addend = None
  421.                 r_type = info & 0xff
  422.                 r_sym = info >> 8
  423.             else:
  424.                 offset, info, addend = f.read('QQq')
  425.                 r_type = info & 0xffffffff
  426.                 r_sym = info >> 32
  427.  
  428.             sym = symbols[r_sym] if r_sym != 0 else None
  429.  
  430.             if r_type != R_AARCH64_TLSDESC and r_type != R_ARM_TLS_DESC:
  431.                 locations.add(offset)
  432.             self.relocations.append((offset, r_type, sym, addend))
  433.         return locations
  434.  
  435.     def get_dynstr(self, o):
  436.         return self.dynstr[o:self.dynstr.index('\0', o)]
  437.  
  438.  
  439. class NsoFile(NxoFileBase):
  440.     def __init__(self, fileobj):
  441.         f = BinFile(fileobj)
  442.  
  443.         if f.read_from('4s', 0) != 'NSO0':
  444.             raise NxoException('Invalid NSO magic')
  445.  
  446.         toff, tloc, tsize = f.read_from('III', 0x10)
  447.         roff, rloc, rsize = f.read_from('III', 0x20)
  448.         doff, dloc, dsize = f.read_from('III', 0x30)
  449.  
  450.         tfilesize, rfilesize, dfilesize = f.read_from('III', 0x60)
  451.         bsssize = f.read_from('I', 0x3C)
  452.  
  453.         print 'load text: '
  454.         text = uncompress(f.read_from(tfilesize, toff), uncompressed_size=tsize)
  455.         ro   = uncompress(f.read_from(rfilesize, roff), uncompressed_size=rsize)
  456.         data = uncompress(f.read_from(dfilesize, doff), uncompressed_size=dsize)
  457.  
  458.         full = text
  459.         if rloc >= len(full):
  460.             full += '\0' * (rloc - len(full))
  461.         else:
  462.             print 'truncating?'
  463.             full = full[:rloc]
  464.         full += ro
  465.         if dloc >= len(full):
  466.             full += '\0' * (dloc - len(full))
  467.         else:
  468.             print 'truncating?'
  469.             full = full[:dloc]
  470.         full += data
  471.  
  472.         super(NsoFile, self).__init__(BinFile(StringIO(full)), tloc, tsize, rloc, rsize, dloc, dsize)
  473.  
  474.  
  475. class NroFile(NxoFileBase):
  476.     def __init__(self, fileobj):
  477.         f = BinFile(fileobj)
  478.  
  479.         if f.read_from('4s', 0x10) != 'NRO0':
  480.             raise NxoException('Invalid NRO magic')
  481.  
  482.         f.seek(0x20)
  483.  
  484.         tloc, tsize = f.read('II')
  485.         rloc, rsize = f.read('II')
  486.         dloc, dsize = f.read('II')
  487.  
  488.         super(NroFile, self).__init__(f, tloc, tsize, rloc, rsize, dloc, dsize)
  489.  
  490. class KipFile(NxoFileBase):
  491.     def __init__(self, fileobj):
  492.         f = BinFile(fileobj)
  493.  
  494.         if f.read_from('4s', 0) != 'KIP1':
  495.             raise NxoException('Invalid KIP magic')
  496.  
  497.         tloc, tsize, tfilesize = f.read_from('III', 0x20)
  498.         rloc, rsize, rfilesize = f.read_from('III', 0x30)
  499.         dloc, dsize, dfilesize = f.read_from('III', 0x40)
  500.        
  501.         toff = 0x100
  502.         roff = toff + tfilesize
  503.         doff = roff + rfilesize
  504.  
  505.        
  506.         bsssize = f.read_from('I', 0x18)
  507.  
  508.         print 'load text: '
  509.         text = kip1_blz_decompress(str(f.read_from(tfilesize, toff)), fixZeroAfterBL=True)
  510.         ro   = kip1_blz_decompress(str(f.read_from(rfilesize, roff)))
  511.         data = kip1_blz_decompress(str(f.read_from(dfilesize, doff)))
  512.        
  513.        
  514.         full = text
  515.         if rloc >= len(full):
  516.             full += '\0' * (rloc - len(full))
  517.         else:
  518.             print 'truncating?'
  519.             full = full[:rloc]
  520.         full += ro
  521.         if dloc >= len(full):
  522.             full += '\0' * (dloc - len(full))
  523.         else:
  524.             print 'truncating?'
  525.             full = full[:dloc]
  526.         full += data
  527.  
  528.         super(KipFile, self).__init__(BinFile(StringIO(full)), tloc, tsize, rloc, rsize, dloc, dsize)
  529.  
  530.  
  531. class NxoException(Exception):
  532.     pass
  533.  
  534.  
  535. def load_nxo(fileobj):
  536.     fileobj.seek(0)
  537.     header = fileobj.read(0x14)
  538.  
  539.     if header[:4] == 'NSO0':
  540.         return NsoFile(fileobj)
  541.     elif header[0x10:0x14] == 'NRO0':
  542.         return NroFile(fileobj)
  543.     elif header[:4] == 'KIP1':
  544.         return KipFile(fileobj)
  545.     else:
  546.         raise NxoException("not an NRO or NSO or KIP file")
  547.  
  548.  
  549. try:
  550.     import idaapi
  551.     import idc
  552. except ImportError:
  553.     pass
  554. else:
  555.     # IDA specific code
  556.     def accept_file(li, n):
  557.         if not isinstance(n, (int,long)) or n == 0:
  558.             li.seek(0)
  559.             if li.read(4) == 'NSO0':
  560.                 return 'nxo.py: Switch binary (NSO)'
  561.             li.seek(0)
  562.             if li.read(4) == 'KIP1':
  563.                 return 'nxo.py: Switch binary (KIP)'
  564.             li.seek(0x10)
  565.             if li.read(4) == 'NRO0':
  566.                 return 'nxo.py: Switch binary (NRO)'
  567.         return 0
  568.  
  569.     def ida_make_offset(f, ea):
  570.         if f.armv7:
  571.             idc.MakeDword(ea)
  572.         else:
  573.             idc.MakeQword(ea)
  574.         idc.OpOff(ea, 0, 0)
  575.  
  576.     def find_bl_targets(text_start, text_end):
  577.         targets = set()
  578.         for pc in range(text_start, text_end, 4):
  579.             d = Dword(pc)
  580.             if (d & 0xfc000000) == 0x94000000:
  581.                 imm = d & 0x3ffffff
  582.                 if imm & 0x2000000:
  583.                     imm |= ~0x1ffffff
  584.                 if 0 <= imm <= 2:
  585.                     continue
  586.                 target = pc + imm * 4
  587.                 if target >= text_start and target < text_end:
  588.                     targets.add(target)
  589.         return targets
  590.  
  591.     def load_file(li, neflags, format):
  592.         idaapi.set_processor_type("arm", SETPROC_ALL|SETPROC_FATAL)
  593.         f = load_nxo(li)
  594.         if f.armv7:
  595.             idc.SetShortPrm(idc.INF_LFLAGS, idc.GetShortPrm(idc.INF_LFLAGS) | idc.LFLG_PC_FLAT)
  596.         else:
  597.             idc.SetShortPrm(idc.INF_LFLAGS, idc.GetShortPrm(idc.INF_LFLAGS) | idc.LFLG_64BIT)
  598.  
  599.         idc.SetCharPrm(idc.INF_DEMNAMES, idaapi.DEMNAM_GCC3)
  600.         idaapi.set_compiler_id(idaapi.COMP_GNU)
  601.         idaapi.add_til2('gnulnx_arm' if f.armv7 else 'gnulnx_arm64', 1)
  602.  
  603.         loadbase = 0x60000000 if f.armv7 else 0x7100000000
  604.  
  605.         f.binfile.seek(0)
  606.         as_string = f.binfile.read(f.bssoff)
  607.         idaapi.mem2base(as_string, loadbase)
  608.  
  609.         for start, end, name, kind in f.sections:
  610.             if name.startswith('.got'):
  611.                 kind = 'CONST'
  612.             idaapi.add_segm(0, loadbase+start, loadbase+end, name, kind)
  613.             segm = idaapi.get_segm_by_name(name)
  614.             if kind == 'CONST':
  615.                 segm.perm = idaapi.SEGPERM_READ
  616.             elif kind == 'CODE':
  617.                 segm.perm = idaapi.SEGPERM_READ | idaapi.SEGPERM_EXEC
  618.             elif kind == 'DATA':
  619.                 segm.perm = idaapi.SEGPERM_READ | idaapi.SEGPERM_WRITE
  620.             elif kind == 'BSS':
  621.                 segm.perm = idaapi.SEGPERM_READ | idaapi.SEGPERM_WRITE
  622.             idaapi.update_segm(segm)
  623.             idaapi.set_segm_addressing(segm, 1 if f.armv7 else 2)
  624.  
  625.         # do imports
  626.         # TODO: can we make imports show up in "Imports" window?
  627.         undef_count = 0
  628.         for s in f.symbols:
  629.             if not s.shndx and s.name:
  630.                 undef_count += 1
  631.         last_ea = max(loadbase + end for start, end, name, kind in f.sections)
  632.         undef_entry_size = 8
  633.         undef_ea = ((last_ea + 0xFFF) & ~0xFFF) + undef_entry_size # plus 8 so we don't end up on the "end" symbol
  634.         idaapi.add_segm(0, undef_ea, undef_ea+undef_count*undef_entry_size, "UNDEF", "XTRN")
  635.         segm = idaapi.get_segm_by_name("UNDEF")
  636.         segm.type = idaapi.SEG_XTRN
  637.         idaapi.update_segm(segm)
  638.         for i,s in enumerate(f.symbols):
  639.             if not s.shndx and s.name:
  640.                 idc.MakeQword(undef_ea)
  641.                 idaapi.do_name_anyway(undef_ea, s.name)
  642.                 s.resolved = undef_ea
  643.                 undef_ea += undef_entry_size
  644.             elif i != 0:
  645.                 assert s.shndx
  646.                 s.resolved = loadbase + s.value
  647.                 if s.name:
  648.                     if s.type == STT_FUNC:
  649.                         print hex(s.resolved), s.name
  650.                         idaapi.add_entry(s.resolved, s.resolved, s.name, 0)
  651.                     else:
  652.                         idaapi.do_name_anyway(s.resolved, s.name)
  653.  
  654.             else:
  655.                 # NULL symbol
  656.                 s.resolved = 0
  657.  
  658.         funcs = set()
  659.         for s in f.symbols:
  660.             if s.name and s.shndx and s.value:
  661.                 if s.type == STT_FUNC:
  662.                     funcs.add(loadbase+s.value)
  663.  
  664.         got_name_lookup = {}
  665.         for offset, r_type, sym, addend in f.relocations:
  666.             target = offset + loadbase
  667.             if r_type in (R_ARM_GLOB_DAT, R_ARM_JUMP_SLOT, R_ARM_ABS32):
  668.                 if not sym:
  669.                     print 'error: relocation at %X failed' % target
  670.                 else:
  671.                     idaapi.put_long(target, sym.resolved)
  672.             elif r_type == R_ARM_RELATIVE:
  673.                 idaapi.put_long(target, idaapi.get_long(target) + loadbase)
  674.             elif r_type in (R_AARCH64_GLOB_DAT, R_AARCH64_JUMP_SLOT, R_AARCH64_ABS64):
  675.                 idaapi.put_qword(target, sym.resolved + addend)
  676.                 if addend == 0:
  677.                     got_name_lookup[offset] = sym.name
  678.             elif r_type == R_AARCH64_RELATIVE:
  679.                 idaapi.put_qword(target, loadbase + addend)
  680.                 if addend < f.textsize:
  681.                     funcs.add(loadbase + addend)
  682.             else:
  683.                 print 'TODO r_type %d' % (r_type,)
  684.             ida_make_offset(f, target)
  685.  
  686.         for func, target in f.plt_entries:
  687.             if target in got_name_lookup:
  688.                 addr = loadbase + func
  689.                 funcs.add(addr)
  690.                 idaapi.do_name_anyway(addr, got_name_lookup[target])
  691.  
  692.         funcs |= find_bl_targets(loadbase, loadbase+f.textsize)
  693.  
  694.         for addr in sorted(funcs, reverse=True):
  695.             idc.AutoMark(addr, AU_CODE)
  696.             idc.AutoMark(addr, AU_PROC)
  697.  
  698.         return 1
Add Comment
Please, Sign In to add comment