class ELF(Binary): def __init__(self, db, filename): Binary.__init__(self) fd = open(filename, "rb") self.elf = ELFFile(fd) self.db = db self.__parsed_reloc_tables = set() self.dtags = {} self.jmprel = [] self.dynamic_seg = None self.set_arch_name() if self.arch == "MIPS32": self.dynamic_tag_translation = { 0x70000001: "DT_MIPS_RLD_VERSION", 0x70000005: "DT_MIPS_FLAGS", 0x70000006: "DT_MIPS_BASE_ADDRESS", 0x7000000a: "DT_MIPS_LOCAL_GOTNO", 0x70000011: "DT_MIPS_SYMTABNO", 0x70000012: "DT_MIPS_UNREFEXTNO", 0x70000013: "DT_MIPS_GOTSYM", 0x70000016: "DT_MIPS_RLD_MAP", 0x70000032: "DT_MIPS_PLTGOT" } elif self.arch == "MIPS64": self.dynamic_tag_translation = { 0x70000001: "DT_MIPS_RLD_VERSION", 0x70000005: "DT_MIPS_FLAGS", 0x70000006: "DT_MIPS_BASE_ADDRESS", 0x7000000a: "DT_MIPS_LOCAL_GOTNO", 0x70000011: "DT_MIPS_SYMTABNO", 0x70000012: "DT_MIPS_UNREFEXTNO", 0x70000013: "DT_MIPS_GOTSYM", 0x70000016: "DT_MIPS_RLD_MAP" } else: self.dynamic_tag_translation = {} reloc = 0 # Load sections for s in self.elf.iter_sections(): if not s.name: continue # Keep only sections R|W|X # TODO : is it sufficiant ? if s.header.sh_flags & 0xf == 0: continue name = s.name if isinstance(name, bytes): name = name.decode() start = s.header.sh_addr if start == 0: start = reloc reloc += s.header.sh_size data = s.data() self.add_section(start, name, s.header.sh_size, len(data), self.__section_is_exec(s), self.__section_is_data(s), name == ".bss", data) # Load segments rename_counter = 1 seen = set() for seg in self.elf.iter_segments(): if seg.header.p_type == "PT_DYNAMIC": self.dynamic_seg = seg if seg.header.p_type != "PT_LOAD": continue name = seg.header.p_type if name in seen: name += "_%d" % rename_counter rename_counter += 1 seen.add(name) start = seg.header.p_vaddr bisect.insort_left(self._sorted_segments, start) is_data = self.__segment_is_data(seg) is_exec = self.__segment_is_exec(seg) data = seg.data() self._abs_segments[start] = SegmentAbs(name, start, seg.header.p_memsz, len(data), is_exec, is_data, data, seg.header.p_offset, not self.elf.little_endian) # No section headers, we add segments in sections if len(self._abs_sections) == 0: self._abs_sections = self._abs_segments self._sorted_sections = self._sorted_segments def read_addr_at(self, ad): seg = self.get_segment(ad) if self.wordsize == 4: return seg.read_dword(ad) else: return seg.read_qword(ad) def __translate_dynamic_tag(self, tag): if isinstance(tag, int): return self.dynamic_tag_translation[tag] return tag def __get_offset(self, ad): seg = self.get_segment(ad) return seg.file_offset + ad - seg.start def load_dyn_sym(self): if self.dynamic_seg is None: return self.dtags = {} for tag in self.dynamic_seg.iter_tags(): # Create a dictionary, mapping DT_* strings to their values tagstr = self.__translate_dynamic_tag(tag.entry.d_tag) self.dtags[tagstr] = tag.entry.d_val # None of the following things make sense without a string table if "DT_STRTAB" not in self.dtags: return # To handle binaries without section headers, we need to hack around # pyreadelf's assumptions make our own string table fakestrtabheader = { "sh_offset": self.__get_offset(self.dtags["DT_STRTAB"]), } strtab = StringTableSection(fakestrtabheader, "strtab_plasma", self.elf.stream) # ... # Here in CLE was checked the DT_SONAME # ... # None of the following structures can be used without a symbol table if "DT_SYMTAB" not in self.dtags or "DT_SYMENT" not in self.dtags: return # Construct our own symbol table to hack around pyreadelf # assuming section headers are around fakesymtabheader = { "sh_offset": self.__get_offset(self.dtags["DT_SYMTAB"]), "sh_entsize": self.dtags["DT_SYMENT"], "sh_size": 0 } # bogus size: no iteration allowed self.dynsym = SymbolTableSection(fakesymtabheader, "symtab_plasma", self.elf.stream, self.elf, strtab) # mips' relocations are absolutely screwed up, handle some of them here. self.__relocate_mips() # perform a lot of checks to figure out what kind of relocation # tables are around rela_type = None if "DT_PLTREL" in self.dtags: if self.dtags["DT_PLTREL"] == 7: rela_type = "RELA" relentsz = self.elf.structs.Elf_Rela.sizeof() elif self.dtags["DT_PLTREL"] == 17: rela_type = "REL" relentsz = self.elf.structs.Elf_Rel.sizeof() else: raise ExcElf("DT_PLTREL is not REL or RELA?") else: if "DT_RELA" in self.dtags: rela_type = "RELA" relentsz = self.elf.structs.Elf_Rela.sizeof() elif "DT_REL" in self.dtags: rela_type = "REL" relentsz = self.elf.structs.Elf_Rel.sizeof() else: return # try to parse relocations out of a table of type DT_REL{,A} if "DT_" + rela_type in self.dtags: reloffset = self.dtags["DT_" + rela_type] relsz = self.dtags["DT_" + rela_type + "SZ"] fakerelheader = { "sh_offset": self.__get_offset(reloffset), "sh_type": "SHT_" + rela_type, "sh_entsize": relentsz, "sh_size": relsz } reloc_sec = RelocationSection(fakerelheader, "reloc_plasma", self.elf.stream, self.elf) self.__register_relocs(reloc_sec) # try to parse relocations out of a table of type DT_JMPREL if "DT_JMPREL" in self.dtags: jmpreloffset = self.dtags["DT_JMPREL"] jmprelsz = self.dtags["DT_PLTRELSZ"] fakejmprelheader = { "sh_offset": self.__get_offset(jmpreloffset), "sh_type": "SHT_" + rela_type, "sh_entsize": relentsz, "sh_size": jmprelsz } jmprel_sec = RelocationSection(fakejmprelheader, "jmprel_plasma", self.elf.stream, self.elf) self.jmprel = self.__register_relocs(jmprel_sec) self.__resolve_plt() def __relocate_mips(self): if 'DT_MIPS_BASE_ADDRESS' not in self.dtags: return # The MIPS GOT is an array of addresses, simple as that. # number of local GOT entries got_local_num = self.dtags['DT_MIPS_LOCAL_GOTNO'] # a.k.a the index of the first global GOT entry # index of first symbol w/ GOT entry symtab_got_idx = self.dtags['DT_MIPS_GOTSYM'] symbol_count = self.dtags['DT_MIPS_SYMTABNO'] gotaddr = self.dtags['DT_PLTGOT'] for i in range(2, got_local_num): symbol = self.dynsym.get_symbol(i) reloc = MipsLocalReloc(self, symbol, gotaddr + i * self.wordsize) self.__save_symbol(reloc, reloc.symbol.entry.st_value) for i in range(symbol_count - symtab_got_idx): symbol = self.dynsym.get_symbol(i + symtab_got_idx) reloc = MipsGlobalReloc( self, symbol, gotaddr + (i + got_local_num) * self.wordsize) self.__save_symbol(reloc, reloc.symbol.entry.st_value) self.jmprel.append(reloc) def __resolve_plt(self): # For PPC32 and PPC64 the address to save is 'got' if self.arch in ('x86', 'x64'): for rel in self.jmprel: got = rel.addr # 0x6 is the size of the plt's jmpq instruction in x86_64 ad = self.read_addr_at(got) - 6 self.__save_symbol(rel, ad) elif self.arch in ('ARM', 'AARCH64', 'MIPS32', 'MIPS64'): for rel in self.jmprel: got = rel.addr ad = self.read_addr_at(got) self.__save_symbol(rel, ad) def __save_symbol(self, rel, ad): if ad == 0: return name = rel.symbol.name if isinstance(name, bytes): name = name.decode() if name in self.symbols: name = self.rename_sym(name) if rel.is_import: self.imports[ad] = True if self.is_function(rel.symbol): self.db.functions[ad] = None self.reverse_symbols[ad] = name self.symbols[name] = ad def __register_relocs(self, section): if section.header["sh_offset"] in self.__parsed_reloc_tables: return self.__parsed_reloc_tables.add(section.header["sh_offset"]) relocs = [] for r in section.iter_relocations(): # MIPS64 is just plain old f****d up # https://www.sourceware.org/ml/libc-alpha/2003-03/msg00153.html if self.arch == "MIPS64": # Little endian addionally needs one of its fields reversed... WHY if self.elf.little_endian: r.entry.r_info_sym = r.entry.r_info & 0xFFFFFFFF r.entry.r_info = struct.unpack( ">Q", struct.pack("<Q", r.entry.r_info))[0] type_1 = r.entry.r_info & 0xFF type_2 = r.entry.r_info >> 8 & 0xFF type_3 = r.entry.r_info >> 16 & 0xFF extra_sym = r.entry.r_info >> 24 & 0xFF if extra_sym != 0: die("r_info_extra_sym is nonzero??? PLEASE SEND HELP") sym = self.dynsym.get_symbol(r.entry.r_info_sym) if type_1 != 0: r.entry.r_info_type = type_1 reloc = self._make_reloc(r, sym) if reloc is not None: relocs.append(reloc) self.__save_symbol(reloc, reloc.symbol.entry.st_value) if type_2 != 0: r.entry.r_info_type = type_2 reloc = self._make_reloc(r, sym) if reloc is not None: relocs.append(reloc) self.__save_symbol(reloc, reloc.symbol.entry.st_value) if type_3 != 0: r.entry.r_info_type = type_3 reloc = self._make_reloc(r, sym) if reloc is not None: relocs.append(reloc) self.__save_symbol(reloc, reloc.symbol.entry.st_value) else: if "sh_link" in section.header: symtab = self.reader.get_section(section.header["sh_link"]) sym = symtab.get_symbol(r.entry.r_info_sym) else: sym = self.dynsym.get_symbol(r.entry.r_info_sym) reloc = self._make_reloc(r, sym) if reloc is not None: relocs.append(reloc) self.__save_symbol(reloc, reloc.symbol.entry.st_value) return relocs def _make_reloc(self, reloc_sec, symbol): addend = reloc_sec.entry.r_addend if reloc_sec.is_RELA() else None RelocClass = get_relocation(self.arch, reloc_sec.entry.r_info_type) if RelocClass is None: return None return RelocClass(self, symbol, reloc_sec.entry.r_offset, addend) def load_static_sym(self): symtab = self.elf.get_section_by_name(b".symtab") if symtab is None: return dont_save = [b"$a", b"$t", b"$d"] is_arm = self.arch == "ARM" for sy in symtab.iter_symbols(): if is_arm and sy.name in dont_save: continue ad = sy.entry.st_value if ad != 0 and sy.name != b"": name = sy.name if isinstance(name, bytes): name = name.decode() if self.is_address(ad): if name in self.symbols: name = self.rename_sym(name) self.reverse_symbols[ad] = name self.symbols[name] = ad if self.is_function(sy): self.db.functions[ad] = None def __section_is_data(self, s): mask = SH_FLAGS.SHF_WRITE | SH_FLAGS.SHF_ALLOC return s.header.sh_flags & mask and not self.__section_is_exec(s) def __section_is_exec(self, s): return s.header.sh_flags & SH_FLAGS.SHF_EXECINSTR def __segment_is_data(self, s): mask = P_FLAGS.PF_W | P_FLAGS.PF_R return s.header.p_flags & mask and not self.__segment_is_exec(s) def __segment_is_exec(self, s): return s.header.p_flags & P_FLAGS.PF_X def is_function(self, sy): return sy.entry.st_info.type == "STT_FUNC" def set_arch_name(self): arch = self.elf.get_machine_arch() if arch == "MIPS": if self.elf.elfclass == 32: arch += "32" elif self.elf.elfclass == 64: arch += "64" self.arch = arch def is_big_endian(self): return not self.elf.little_endian def get_entry_point(self): return self.elf.header['e_entry']
def load_dyn_sym(self): if self.dynamic_seg is None: return self.dtags = {} for tag in self.dynamic_seg.iter_tags(): # Create a dictionary, mapping DT_* strings to their values tagstr = self.__translate_dynamic_tag(tag.entry.d_tag) self.dtags[tagstr] = tag.entry.d_val # None of the following things make sense without a string table if "DT_STRTAB" not in self.dtags: return # To handle binaries without section headers, we need to hack around # pyreadelf's assumptions make our own string table fakestrtabheader = { "sh_offset": self.__get_offset(self.dtags["DT_STRTAB"]), } strtab = StringTableSection(fakestrtabheader, "strtab_plasma", self.elf.stream) # ... # Here in CLE was checked the DT_SONAME # ... # None of the following structures can be used without a symbol table if "DT_SYMTAB" not in self.dtags or "DT_SYMENT" not in self.dtags: return # Construct our own symbol table to hack around pyreadelf # assuming section headers are around fakesymtabheader = { "sh_offset": self.__get_offset(self.dtags["DT_SYMTAB"]), "sh_entsize": self.dtags["DT_SYMENT"], "sh_size": 0 } # bogus size: no iteration allowed self.dynsym = SymbolTableSection(fakesymtabheader, "symtab_plasma", self.elf.stream, self.elf, strtab) # mips' relocations are absolutely screwed up, handle some of them here. self.__relocate_mips() # perform a lot of checks to figure out what kind of relocation # tables are around rela_type = None if "DT_PLTREL" in self.dtags: if self.dtags["DT_PLTREL"] == 7: rela_type = "RELA" relentsz = self.elf.structs.Elf_Rela.sizeof() elif self.dtags["DT_PLTREL"] == 17: rela_type = "REL" relentsz = self.elf.structs.Elf_Rel.sizeof() else: raise ExcElf("DT_PLTREL is not REL or RELA?") else: if "DT_RELA" in self.dtags: rela_type = "RELA" relentsz = self.elf.structs.Elf_Rela.sizeof() elif "DT_REL" in self.dtags: rela_type = "REL" relentsz = self.elf.structs.Elf_Rel.sizeof() else: return # try to parse relocations out of a table of type DT_REL{,A} if "DT_" + rela_type in self.dtags: reloffset = self.dtags["DT_" + rela_type] relsz = self.dtags["DT_" + rela_type + "SZ"] fakerelheader = { "sh_offset": self.__get_offset(reloffset), "sh_type": "SHT_" + rela_type, "sh_entsize": relentsz, "sh_size": relsz } reloc_sec = RelocationSection(fakerelheader, "reloc_plasma", self.elf.stream, self.elf) self.__register_relocs(reloc_sec) # try to parse relocations out of a table of type DT_JMPREL if "DT_JMPREL" in self.dtags: jmpreloffset = self.dtags["DT_JMPREL"] jmprelsz = self.dtags["DT_PLTRELSZ"] fakejmprelheader = { "sh_offset": self.__get_offset(jmpreloffset), "sh_type": "SHT_" + rela_type, "sh_entsize": relentsz, "sh_size": jmprelsz } jmprel_sec = RelocationSection(fakejmprelheader, "jmprel_plasma", self.elf.stream, self.elf) self.jmprel = self.__register_relocs(jmprel_sec) self.__resolve_plt()
def load_dyn_sym(self): if self.dynamic_seg is None: return self.dtags = {} for tag in self.dynamic_seg.iter_tags(): # Create a dictionary, mapping DT_* strings to their values tagstr = self.__translate_dynamic_tag(tag.entry.d_tag) self.dtags[tagstr] = tag.entry.d_val # None of the following things make sense without a string table if "DT_STRTAB" not in self.dtags: return # To handle binaries without section headers, we need to hack around # pyreadelf's assumptions make our own string table fakestrtabheader = { "sh_offset": self.__get_offset(self.dtags["DT_STRTAB"]), } strtab = StringTableSection( fakestrtabheader, "strtab_plasma", self.elf.stream) # ... # Here in CLE was checked the DT_SONAME # ... # None of the following structures can be used without a symbol table if "DT_SYMTAB" not in self.dtags or "DT_SYMENT" not in self.dtags: return # Construct our own symbol table to hack around pyreadelf # assuming section headers are around fakesymtabheader = { "sh_offset": self.__get_offset(self.dtags["DT_SYMTAB"]), "sh_entsize": self.dtags["DT_SYMENT"], "sh_size": 0 } # bogus size: no iteration allowed self.dynsym = SymbolTableSection( fakesymtabheader, "symtab_plasma", self.elf.stream, self.elf, strtab) # mips' relocations are absolutely screwed up, handle some of them here. self.__relocate_mips() # perform a lot of checks to figure out what kind of relocation # tables are around rela_type = None if "DT_PLTREL" in self.dtags: if self.dtags["DT_PLTREL"] == 7: rela_type = "RELA" relentsz = self.elf.structs.Elf_Rela.sizeof() elif self.dtags["DT_PLTREL"] == 17: rela_type = "REL" relentsz = self.elf.structs.Elf_Rel.sizeof() else: raise ExcElf("DT_PLTREL is not REL or RELA?") else: if "DT_RELA" in self.dtags: rela_type = "RELA" relentsz = self.elf.structs.Elf_Rela.sizeof() elif "DT_REL" in self.dtags: rela_type = "REL" relentsz = self.elf.structs.Elf_Rel.sizeof() else: return # try to parse relocations out of a table of type DT_REL{,A} if "DT_" + rela_type in self.dtags: reloffset = self.dtags["DT_" + rela_type] relsz = self.dtags["DT_" + rela_type + "SZ"] fakerelheader = { "sh_offset": self.__get_offset(reloffset), "sh_type": "SHT_" + rela_type, "sh_entsize": relentsz, "sh_size": relsz } reloc_sec = RelocationSection( fakerelheader, "reloc_plasma", self.elf.stream, self.elf) self.__register_relocs(reloc_sec) # try to parse relocations out of a table of type DT_JMPREL if "DT_JMPREL" in self.dtags: jmpreloffset = self.dtags["DT_JMPREL"] jmprelsz = self.dtags["DT_PLTRELSZ"] fakejmprelheader = { "sh_offset": self.__get_offset(jmpreloffset), "sh_type": "SHT_" + rela_type, "sh_entsize": relentsz, "sh_size": jmprelsz } jmprel_sec = RelocationSection( fakejmprelheader, "jmprel_plasma", self.elf.stream, self.elf) self.jmprel = self.__register_relocs(jmprel_sec) self.__resolve_plt()
class ELF(Binary): def __init__(self, db, filename): Binary.__init__(self) fd = open(filename, "rb") self.elf = ELFFile(fd) self.db = db self.__parsed_reloc_tables = set() self.dtags = {} self.jmprel = [] self.dynamic_seg = None self.set_arch_name() if self.arch == "MIPS32": self.dynamic_tag_translation = { 0x70000001: "DT_MIPS_RLD_VERSION", 0x70000005: "DT_MIPS_FLAGS", 0x70000006: "DT_MIPS_BASE_ADDRESS", 0x7000000a: "DT_MIPS_LOCAL_GOTNO", 0x70000011: "DT_MIPS_SYMTABNO", 0x70000012: "DT_MIPS_UNREFEXTNO", 0x70000013: "DT_MIPS_GOTSYM", 0x70000016: "DT_MIPS_RLD_MAP", 0x70000032: "DT_MIPS_PLTGOT" } elif self.arch == "MIPS64": self.dynamic_tag_translation = { 0x70000001: "DT_MIPS_RLD_VERSION", 0x70000005: "DT_MIPS_FLAGS", 0x70000006: "DT_MIPS_BASE_ADDRESS", 0x7000000a: "DT_MIPS_LOCAL_GOTNO", 0x70000011: "DT_MIPS_SYMTABNO", 0x70000012: "DT_MIPS_UNREFEXTNO", 0x70000013: "DT_MIPS_GOTSYM", 0x70000016: "DT_MIPS_RLD_MAP" } else: self.dynamic_tag_translation = {} reloc = 0 # Load sections for s in self.elf.iter_sections(): if not s.name: continue # Keep only sections R|W|X # TODO : is it sufficiant ? if s.header.sh_flags & 0xf == 0: continue name = s.name.decode() start = s.header.sh_addr if start == 0: start = reloc reloc += s.header.sh_size data = s.data() self.add_section( start, s.name.decode(), s.header.sh_size, len(data), self.__section_is_exec(s), self.__section_is_data(s), data) # Load segments rename_counter = 1 seen = set() for seg in self.elf.iter_segments(): if seg.header.p_type == "PT_DYNAMIC": self.dynamic_seg = seg if seg.header.p_type != "PT_LOAD": continue name = seg.header.p_type if name in seen: name += "_%d" % rename_counter rename_counter += 1 seen.add(name) start = seg.header.p_vaddr bisect.insort_left(self._sorted_segments, start) is_data = self.__segment_is_data(seg) is_exec = self.__segment_is_exec(seg) data = seg.data() self._abs_segments[start] = SegmentAbs( name, start, seg.header.p_memsz, len(data), is_exec, is_data, data, seg.header.p_offset, not self.elf.little_endian) # No section headers, we add segments in sections if len(self._abs_sections) == 0: self._abs_sections = self._abs_segments self._sorted_sections = self._sorted_segments def read_addr_at(self, ad): seg = self.get_segment(ad) if self.wordsize == 4: return seg.read_dword(ad) else: return seg.read_qword(ad) def __translate_dynamic_tag(self, tag): if isinstance(tag, int): return self.dynamic_tag_translation[tag] return tag def __get_offset(self, ad): seg = self.get_segment(ad) return seg.file_offset + ad - seg.start def load_dyn_sym(self): if self.dynamic_seg is None: return self.dtags = {} for tag in self.dynamic_seg.iter_tags(): # Create a dictionary, mapping DT_* strings to their values tagstr = self.__translate_dynamic_tag(tag.entry.d_tag) self.dtags[tagstr] = tag.entry.d_val # None of the following things make sense without a string table if "DT_STRTAB" not in self.dtags: return # To handle binaries without section headers, we need to hack around # pyreadelf's assumptions make our own string table fakestrtabheader = { "sh_offset": self.__get_offset(self.dtags["DT_STRTAB"]), } strtab = StringTableSection( fakestrtabheader, "strtab_plasma", self.elf.stream) # ... # Here in CLE was checked the DT_SONAME # ... # None of the following structures can be used without a symbol table if "DT_SYMTAB" not in self.dtags or "DT_SYMENT" not in self.dtags: return # Construct our own symbol table to hack around pyreadelf # assuming section headers are around fakesymtabheader = { "sh_offset": self.__get_offset(self.dtags["DT_SYMTAB"]), "sh_entsize": self.dtags["DT_SYMENT"], "sh_size": 0 } # bogus size: no iteration allowed self.dynsym = SymbolTableSection( fakesymtabheader, "symtab_plasma", self.elf.stream, self.elf, strtab) # mips' relocations are absolutely screwed up, handle some of them here. self.__relocate_mips() # perform a lot of checks to figure out what kind of relocation # tables are around rela_type = None if "DT_PLTREL" in self.dtags: if self.dtags["DT_PLTREL"] == 7: rela_type = "RELA" relentsz = self.elf.structs.Elf_Rela.sizeof() elif self.dtags["DT_PLTREL"] == 17: rela_type = "REL" relentsz = self.elf.structs.Elf_Rel.sizeof() else: raise ExcElf("DT_PLTREL is not REL or RELA?") else: if "DT_RELA" in self.dtags: rela_type = "RELA" relentsz = self.elf.structs.Elf_Rela.sizeof() elif "DT_REL" in self.dtags: rela_type = "REL" relentsz = self.elf.structs.Elf_Rel.sizeof() else: return # try to parse relocations out of a table of type DT_REL{,A} if "DT_" + rela_type in self.dtags: reloffset = self.dtags["DT_" + rela_type] relsz = self.dtags["DT_" + rela_type + "SZ"] fakerelheader = { "sh_offset": self.__get_offset(reloffset), "sh_type": "SHT_" + rela_type, "sh_entsize": relentsz, "sh_size": relsz } reloc_sec = RelocationSection( fakerelheader, "reloc_plasma", self.elf.stream, self.elf) self.__register_relocs(reloc_sec) # try to parse relocations out of a table of type DT_JMPREL if "DT_JMPREL" in self.dtags: jmpreloffset = self.dtags["DT_JMPREL"] jmprelsz = self.dtags["DT_PLTRELSZ"] fakejmprelheader = { "sh_offset": self.__get_offset(jmpreloffset), "sh_type": "SHT_" + rela_type, "sh_entsize": relentsz, "sh_size": jmprelsz } jmprel_sec = RelocationSection( fakejmprelheader, "jmprel_plasma", self.elf.stream, self.elf) self.jmprel = self.__register_relocs(jmprel_sec) self.__resolve_plt() def __relocate_mips(self): if 'DT_MIPS_BASE_ADDRESS' not in self.dtags: return # The MIPS GOT is an array of addresses, simple as that. # number of local GOT entries got_local_num = self.dtags['DT_MIPS_LOCAL_GOTNO'] # a.k.a the index of the first global GOT entry # index of first symbol w/ GOT entry symtab_got_idx = self.dtags['DT_MIPS_GOTSYM'] symbol_count = self.dtags['DT_MIPS_SYMTABNO'] gotaddr = self.dtags['DT_PLTGOT'] for i in range(2, got_local_num): symbol = self.dynsym.get_symbol(i) reloc = MipsLocalReloc(self, symbol, gotaddr + i * self.wordsize) self.__save_symbol(reloc, reloc.symbol.entry.st_value) for i in range(symbol_count - symtab_got_idx): symbol = self.dynsym.get_symbol(i + symtab_got_idx) reloc = MipsGlobalReloc(self, symbol, gotaddr + (i + got_local_num) * self.wordsize) self.__save_symbol(reloc, reloc.symbol.entry.st_value) self.jmprel.append(reloc) def __resolve_plt(self): # For PPC32 and PPC64 the address to save is 'got' if self.arch in ('x86', 'x64'): for rel in self.jmprel: got = rel.addr # 0x6 is the size of the plt's jmpq instruction in x86_64 ad = self.read_addr_at(got) - 6 self.__save_symbol(rel, ad) elif self.arch in ('ARM', 'AARCH64', 'MIPS32', 'MIPS64'): for rel in self.jmprel: got = rel.addr ad = self.read_addr_at(got) self.__save_symbol(rel, ad) def __save_symbol(self, rel, ad): if ad == 0: return name = rel.symbol.name.decode() if name in self.symbols: name = self.rename_sym(name) if rel.is_import: self.imports[ad] = True if self.is_function(rel.symbol): self.db.functions[ad] = None self.reverse_symbols[ad] = name self.symbols[name] = ad def __register_relocs(self, section): if section.header["sh_offset"] in self.__parsed_reloc_tables: return self.__parsed_reloc_tables.add(section.header["sh_offset"]) relocs = [] for r in section.iter_relocations(): # MIPS64 is just plain old f****d up # https://www.sourceware.org/ml/libc-alpha/2003-03/msg00153.html if self.arch == "MIPS64": # Little endian addionally needs one of its fields reversed... WHY if self.elf.little_endian: r.entry.r_info_sym = r.entry.r_info & 0xFFFFFFFF r.entry.r_info = struct.unpack(">Q", struct.pack("<Q", r.entry.r_info))[0] type_1 = r.entry.r_info & 0xFF type_2 = r.entry.r_info >> 8 & 0xFF type_3 = r.entry.r_info >> 16 & 0xFF extra_sym = r.entry.r_info >> 24 & 0xFF if extra_sym != 0: die("r_info_extra_sym is nonzero??? PLEASE SEND HELP") sym = self.dynsym.get_symbol(r.entry.r_info_sym) if type_1 != 0: r.entry.r_info_type = type_1 reloc = self._make_reloc(r, sym) if reloc is not None: relocs.append(reloc) self.__save_symbol(reloc, reloc.symbol.entry.st_value) if type_2 != 0: r.entry.r_info_type = type_2 reloc = self._make_reloc(r, sym) if reloc is not None: relocs.append(reloc) self.__save_symbol(reloc, reloc.symbol.entry.st_value) if type_3 != 0: r.entry.r_info_type = type_3 reloc = self._make_reloc(r, sym) if reloc is not None: relocs.append(reloc) self.__save_symbol(reloc, reloc.symbol.entry.st_value) else: if "sh_link" in section.header: symtab = self.reader.get_section(section.header["sh_link"]) sym = symtab.get_symbol(r.entry.r_info_sym) else: sym = self.dynsym.get_symbol(r.entry.r_info_sym) reloc = self._make_reloc(r, sym) if reloc is not None: relocs.append(reloc) self.__save_symbol(reloc, reloc.symbol.entry.st_value) return relocs def _make_reloc(self, reloc_sec, symbol): addend = reloc_sec.entry.r_addend if reloc_sec.is_RELA() else None RelocClass = get_relocation(self.arch, reloc_sec.entry.r_info_type) if RelocClass is None: return None return RelocClass(self, symbol, reloc_sec.entry.r_offset, addend) def load_static_sym(self): symtab = self.elf.get_section_by_name(b".symtab") if symtab is None: return dont_save = [b"$a", b"$t", b"$d"] is_arm = self.arch == "ARM" for sy in symtab.iter_symbols(): if is_arm and sy.name in dont_save: continue ad = sy.entry.st_value if ad != 0 and sy.name != b"": name = sy.name.decode() if self.is_address(ad): if name in self.symbols: name = self.rename_sym(name) self.reverse_symbols[ad] = name self.symbols[name] = ad if self.is_function(sy): self.db.functions[ad] = None def __section_is_data(self, s): mask = SH_FLAGS.SHF_WRITE | SH_FLAGS.SHF_ALLOC return s.header.sh_flags & mask and not self.__section_is_exec(s) def __section_is_exec(self, s): return s.header.sh_flags & SH_FLAGS.SHF_EXECINSTR def __segment_is_data(self, s): mask = P_FLAGS.PF_W | P_FLAGS.PF_R return s.header.p_flags & mask and not self.__segment_is_exec(s) def __segment_is_exec(self, s): return s.header.p_flags & P_FLAGS.PF_X def is_function(self, sy): return sy.entry.st_info.type == "STT_FUNC" def set_arch_name(self): arch = self.elf.get_machine_arch() if arch == "MIPS": if self.elf.elfclass == 32: arch += "32" elif self.elf.elfclass == 64: arch += "64" self.arch = arch def is_big_endian(self): return not self.elf.little_endian def get_entry_point(self): return self.elf.header['e_entry']
def _parse_symbol_table(self, elfpath: str, elf: ELFFile, symTab: SymbolTableSection, strTab: StringTableSection, _locals: dict) -> list: if symTab.header["sh_entsize"] != 16: raise InvalidDataException( "Invalid symbol table format (sh_entsize != 16)") if not isinstance(strTab, StringTableSection): raise InvalidDataException( "String table does not have type SHT_STRTAB") _symbolNames = [] for symbol in symTab.iter_symbols(): name = symbol.name st_value = symbol["st_value"] st_size = symbol["st_size"] st_info = symbol["st_info"] st_shndx = symbol["st_shndx"] _symbolNames.append(name) if len(name) == 0 or st_shndx == "SHN_UNDEF": continue # What location is this referencing? if isinstance(st_shndx, int): # Reference refSection = elf.get_section(st_shndx) _refkey = self.__get_section_key(refSection) if _refkey not in self._sectionBases: continue # Skip past unwanted symbols addr = KWord(self._sectionBases[_refkey] + st_value, self._sectionBases[_refkey].type) elif st_shndx == "SHN_ABS": # Absolute symbol refSection = None addr = KWord(st_value, KWord.Types.ABSOLUTE) else: raise InvalidDataException( "Unknown section index found in symbol table") if st_info["bind"] == "STB_LOCAL": if name in _locals: raise InvalidDataException( f"Redefinition of local symbol {name}") _locals[name] = Linker.Symbol(addr, st_size) self._symbolSizes[addr] = st_size elif st_info["bind"] == "STB_GLOBAL": if name in self._globalSymbols and not self._globalSymbols[ name].isWeak: raise InvalidDataException( f"Redefinition of global symbol {name}") self._globalSymbols[name] = Linker.Symbol(addr, st_size) self._symbolSizes[addr] = st_size elif st_info["bind"] == "STB_WEAK": if name not in self._globalSymbols: self._globalSymbols[name] = Linker.Symbol(addr, st_size, isWeak=True) self._symbolSizes[addr] = st_size self._localSymbols[elfpath] = _locals return _symbolNames