def _handle_relocation(self, container, section, rel): reloc_type = rel['type'] if reloc_type == ENUM_RELOC_TYPE_x64["R_X86_64_PC32"]: swbase = None for base in sorted(self.bases): if base > rel['offset']: break swbase = base value = rel['st_value'] + rel['addend'] - (rel['offset'] - swbase) swlbl = ".LC%x-.LC%x" % (value, swbase) section.replace(rel['offset'], 4, swlbl) elif reloc_type == ENUM_RELOC_TYPE_x64["R_X86_64_64"]: # C++ ABI functions, to be ignored # if not (rel["name"].startswith("_ZTV") or rel["name"] == "__gxx_personality_v0"): if rel['st_value']: value = rel['st_value'] + rel['addend'] label = ".LC%x" % value else: label = "%s+%d" % (rel['name'], rel['addend']) section.replace(rel['offset'], 8, label) elif reloc_type == ENUM_RELOC_TYPE_x64["R_X86_64_RELATIVE"]: value = rel['addend'] label = ".LC%x" % value section.replace(rel['offset'], 8, label) elif reloc_type == ENUM_RELOC_TYPE_x64["R_X86_64_COPY"]: # NOP pass else: print("[*] Unhandled relocation {}".format( describe_reloc_type(reloc_type, container.loader.elffile)))
def _handle_relocation(self, container, section, rel): reloc_type = rel['type'] if reloc_type == ENUM_RELOC_TYPE_x64["R_X86_64_PC32"]: swbase = None for base in sorted(self.bases): if base > rel['offset']: break swbase = base value = rel['st_value'] + rel['addend'] - (rel['offset'] - swbase) swlbl = ".LC%x-.LC%x" % (value, swbase) section.replace(rel['offset'], 4, swlbl) elif reloc_type == ENUM_RELOC_TYPE_x64["R_X86_64_64"]: value = rel['st_value'] + rel['addend'] label = ".LC%x" % value section.replace(rel['offset'], 8, label) elif reloc_type == ENUM_RELOC_TYPE_x64["R_X86_64_RELATIVE"]: value = rel['addend'] label = ".LC%x" % value section.replace(rel['offset'], 8, label) elif reloc_type == ENUM_RELOC_TYPE_x64["R_X86_64_COPY"]: # NOP pass else: print("[*] Unhandled relocation {}".format( describe_reloc_type(reloc_type, container.loader.elffile)))
def get_relocations(self): relocations = [] with open(self.filename, "rb") as elf_file: elf = ELFFile(elf_file) for section in elf.iter_sections(): if not isinstance(section, RelocationSection): continue symbols = elf.get_section(section["sh_link"]) for relocation in section.iter_relocations(): if relocation["r_info_sym"] == 0: continue relocation_data = {} relocation_data["offset"] = relocation["r_offset"] relocation_data["info"] = relocation["r_info"] relocation_data["info_type"] = describe_reloc_type(relocation["r_info_type"], elf) relocation_data["symbol"] = relocation["r_info_sym"] symbol = symbols.get_symbol(relocation_data["symbol"]) if symbol["st_name"] == 0: symbol_section = elf.get_section(symbol["st_shndx"]) relocation_data["symbol_name"] = symbol_section.name else: relocation_data["symbol_name"] = symbol.name relocation_data["symbol_value"] = symbol["st_value"] relocation_data["section_index"] = symbol["st_shndx"] relocations.append(relocation_data) return relocations
def display_relocations(self): """ Display the relocations contained in the file """ has_relocation_sections = False for section in self.elffile.iter_sections(): if not isinstance(section, RelocationSection): continue has_relocation_sections = True self._emitline( "\nRelocation section '%s' at offset %s contains %s entries:" % (bytes2str(section.name), self._format_hex( section['sh_offset']), section.num_relocations())) if section.is_RELA(): self._emitline( " Offset Info Type Sym. Value Sym. Name + Addend" ) else: self._emitline( " Offset Info Type Sym.Value Sym. Name") # The symbol table section pointed to in sh_link symtable = self.elffile.get_section(section['sh_link']) for rel in section.iter_relocations(): hexwidth = 8 if self.elffile.elfclass == 32 else 12 self._emit( '%s %s %-17.17s' % (self._format_hex( rel['r_offset'], fieldsize=hexwidth, lead0x=False), self._format_hex( rel['r_info'], fieldsize=hexwidth, lead0x=False), describe_reloc_type(rel['r_info_type'], self.elffile))) if rel['r_info_sym'] == 0: self._emitline() continue symbol = symtable.get_symbol(rel['r_info_sym']) # Some symbols have zero 'st_name', so instead what's used is # the name of the section they point at if symbol['st_name'] == 0: symsec = self.elffile.get_section(symbol['st_shndx']) symbol_name = symsec.name else: symbol_name = symbol.name self._emit(' %s %s%22.22s' % (self._format_hex( symbol['st_value'], fullhex=True, lead0x=False), ' ' if self.elffile.elfclass == 32 else '', bytes2str(symbol_name))) if section.is_RELA(): self._emit(' %s %x' % ('+' if rel['r_addend'] >= 0 else '-', abs(rel['r_addend']))) self._emitline() if not has_relocation_sections: self._emitline('\nThere are no relocations in this file.')
def display_relocations(self): """ Display the relocations contained in the file """ has_relocation_sections = False for section in self.elffile.iter_sections(): if not isinstance(section, RelocationSection): continue has_relocation_sections = True self._emitline("\nRelocation section '%s' at offset %s contains %s entries:" % ( bytes2str(section.name), self._format_hex(section['sh_offset']), section.num_relocations())) if section.is_RELA(): self._emitline(" Offset Info Type Sym. Value Sym. Name + Addend") else: self._emitline(" Offset Info Type Sym.Value Sym. Name") # The symbol table section pointed to in sh_link symtable = self.elffile.get_section(section['sh_link']) for rel in section.iter_relocations(): hexwidth = 8 if self.elffile.elfclass == 32 else 12 self._emit('%s %s %-17.17s' % ( self._format_hex(rel['r_offset'], fieldsize=hexwidth, lead0x=False), self._format_hex(rel['r_info'], fieldsize=hexwidth, lead0x=False), describe_reloc_type( rel['r_info_type'], self.elffile))) if rel['r_info_sym'] == 0: self._emitline() continue symbol = symtable.get_symbol(rel['r_info_sym']) # Some symbols have zero 'st_name', so instead what's used is # the name of the section they point at if symbol['st_name'] == 0: symsec = self.elffile.get_section(symbol['st_shndx']) symbol_name = symsec.name else: symbol_name = symbol.name self._emit(' %s %s%22.22s' % ( self._format_hex( symbol['st_value'], fullhex=True, lead0x=False), ' ' if self.elffile.elfclass == 32 else '', bytes2str(symbol_name))) if section.is_RELA(): self._emit(' %s %x' % ( '+' if rel['r_addend'] >= 0 else '-', abs(rel['r_addend']))) self._emitline() if not has_relocation_sections: self._emitline('\nThere are no relocations in this file.')
def _handle_relocation(self, container, section, all_symbols, rel): reloc_type = rel['type'] if reloc_type == ENUM_RELOC_TYPE_x64["R_X86_64_PC32"]: swbase = None for base in sorted(self.bases): if base > rel['offset']: break swbase = base value = rel['st_value'] + rel['addend'] - (rel['offset'] - swbase) swlbl = ".LC%x-.LC%x" % (value, swbase) section.replace(rel['offset'], 4, swlbl) elif reloc_type == ENUM_RELOC_TYPE_x64["R_X86_64_64"]: value = rel['st_value'] + rel['addend'] label = ".LC%x" % value #relocation already has a name if rel['st_value'] == 0 and rel['name'] != None: label = rel['name'] if rel['addend'] != 0: label += " + 0x%x" % rel['addend'] #internal symbol #we use name from symbols elif rel['st_value'] and rel['st_value'] in all_symbols: #if not, then use "sym + offset" label = container.loader.adjust_sym_name(all_symbols[rel['st_value']]) if rel['addend'] != 0: label += " + 0x%x" % rel['addend'] section.replace(rel['offset'], 8, label) #end by JX elif reloc_type == ENUM_RELOC_TYPE_x64["R_X86_64_RELATIVE"]: value = rel['addend'] label = ".LC%x" % value if rel['addend'] in all_symbols: label = container.loader.adjust_sym_name(all_symbols[rel['addend']]) section.replace(rel['offset'], 8, label) #end by JX elif reloc_type == ENUM_RELOC_TYPE_x64["R_X86_64_COPY"]: # NOP pass else: print("[*] Unhandled relocation {}".format( describe_reloc_type(reloc_type, container.loader.elffile)))
def __init__(self, elf: Any, section: Any, symtable: Any, symbol_objects: Any, r): self.offset = int(r["r_offset"]) stype = describe_reloc_type(r["r_info_type"], elf) if stype == "R_ARM_GOT_BREL": self.type = lief.ELF.RELOCATION_ARM.GOT_BREL elif stype == "R_ARM_ABS32": self.type = lief.ELF.RELOCATION_ARM.ABS32 elif stype == "R_ARM_THM_CALL": self.type = lief.ELF.RELOCATION_ARM.THM_CALL elif stype == "R_ARM_THM_JUMP24": self.type = lief.ELF.RELOCATION_ARM.THM_JUMP24 elif stype == "R_ARM_TARGET1": self.type = lief.ELF.RELOCATION_ARM.TARGET1 elif stype == "R_ARM_PREL31": self.type = lief.ELF.RELOCATION_ARM.PREL31 elif stype == "R_ARM_REL32": self.type = lief.ELF.RELOCATION_ARM.REL32 elif stype == "R_ARM_JUMP_SLOT": self.type = lief.ELF.RELOCATION_ARM.JUMP_SLOT elif stype == "R_ARM_GLOB_DAT": self.type = lief.ELF.RELOCATION_ARM.GLOB_DAT else: raise Exception("Unknown rel type: '%s'" % (stype)) symbol_index = r["r_info_sym"] s = symtable.get_symbol(symbol_index) if s["st_name"] != 0: self.name = str(s.name) self.symbol = symbol_objects[symbol_index] self.has_symbol = True else: self.symbol = None self.has_symbol = False try: symsec = elf.get_section(s["st_shndx"]) self.section = Section(symsec) self.name = self.section.name except: self.section = Section(section) self.name = self.section.name self.debug = "debug_" in self.section.name self.value = s["st_value"] self.address = s["st_value"]
def apply_data_relocation(self, container, section, relocation): reloc_type = relocation['type'] if reloc_type == ENUM_RELOC_TYPE_x64["R_X86_64_COPY"]: # NOP return relocation_size = Symbolizer.RELOCATION_SIZES[relocation['type']] relocation_target = None if relocation['symbol_address'] is None: # This relocation refers to an imported symbol relocation_target = '{} + {}'.format(relocation['name'], relocation['addend']) # PC32 and PLT32 are more or less the same in the kernel, but not in user space if reloc_type == ENUM_RELOC_TYPE_x64["R_X86_64_PC32"] or reloc_type == ENUM_RELOC_TYPE_x64["R_X86_64_PLT32"]: if not relocation_target: value = relocation['symbol_address'].offset + relocation['addend'] relocation_target = self.label_for_address(container, Address(relocation['symbol_address'].section, value)) relocation_target += ' - .' elif reloc_type == ENUM_RELOC_TYPE_x64["R_X86_64_PC64"]: if not relocation_target: value = relocation['symbol_address'].offset + relocation['addend'] relocation_target = self.label_for_address(container, Address(relocation['symbol_address'].section, value)) relocation_target += ' - .' elif reloc_type == ENUM_RELOC_TYPE_x64["R_X86_64_32S"]: if not relocation_target: value = relocation['symbol_address'].offset + relocation['addend'] relocation_target = self.label_for_address(container, Address(relocation['symbol_address'].section, value)) elif reloc_type == ENUM_RELOC_TYPE_x64["R_X86_64_64"]: if not relocation_target: value = relocation['symbol_address'].offset + relocation['addend'] relocation_target = self.label_for_address(container, Address(relocation['symbol_address'].section, value)) elif reloc_type == ENUM_RELOC_TYPE_x64["R_X86_64_RELATIVE"]: if not relocation_target: value = relocation['addend'] relocation_target = self.label_for_address(container, Address(relocation['symbol_address'].section, value)) elif reloc_type == ENUM_RELOC_TYPE_x64["R_X86_64_JUMP_SLOT"]: if not relocation_target: value = relocation['symbol_address'].offset relocation_target = self.label_for_address(container, Address(relocation['symbol_address'].section, value)) else: print("[*] Unhandled relocation {}".format( describe_reloc_type(reloc_type, container.loader.elffile))) if relocation_size: section.replace(relocation['address'].offset, relocation_size, relocation_target)
def main(): if len(sys.argv) < 2: print("usage: python3 {} elf [data symbol]".format(sys.argv[0])) sys.exit(1) elf = sys.argv[1] item = sys.argv[2] if len(sys.argv) > 2 else "" counters = {} reloc_types = {} elf = ELFFile(open(elf, "rb")) for sec in elf.iter_sections(): if not isinstance(sec, RelocationSection): continue symtab = elf.get_section(sec["sh_link"]) for reloc in sec.iter_relocations(): ref_sym = symtab.get_symbol(reloc["r_info_sym"]) name = ref_sym.name if item in name: if name in counters: counters[name] += 1 else: counters[name] = 1 reloc_types[name] = {} reloc_type = describe_reloc_type(reloc["r_info_type"], elf) if reloc_type in reloc_types[name]: reloc_types[name][reloc_type] += 1 else: reloc_types[name][reloc_type] = 1 counters_list = [(v, k) for (k, v) in counters.items()] for count, name in sorted(counters_list, reverse=True): print("{}: {}".format(name, count)) reloc_list = [(v, k) for (k, v) in reloc_types[name].items()] for count, reloc_type in sorted(reloc_list, reverse=True): print("\t{}: {}".format(reloc_type, count))
def _get_relocations(self) -> List[Dict[str, str]]: relocations = [] for section in self.elf.iter_sections(): if not isinstance(section, RelocationSection): continue section_relocations = set() for rel in section.iter_relocations(): relocation = { "offset": self._print_addr(rel["r_offset"]), "info": self._print_addr(rel["r_info"]), "type": describe_reloc_type(rel["r_info_type"], self.elf), "value": "", "name": "", } if rel["r_info_sym"] != 0: symtable = self.elf.get_section(section["sh_link"]) symbol = symtable.get_symbol(rel["r_info_sym"]) # Some symbols have zero "st_name", so instead use # the name of the section they point at if symbol["st_name"] == 0: symsec = self.elf.get_section(symbol["st_shndx"]) symbol_name = symsec.name else: symbol_name = symbol.name relocation["value"] = self._print_addr(symbol["st_value"]) relocation["name"] = symbol_name section_relocations.add(relocation) relocations.append( { "name": section.name, "entries": section_relocations, } ) return relocations
def _get_relocations(self): relocations = [] for section in self.elf.iter_sections(): if not isinstance(section, RelocationSection): continue section_relocations = [] for rel in section.iter_relocations(): relocation = { "offset": self._print_addr(rel["r_offset"]), "info": self._print_addr(rel["r_info"]), "type": describe_reloc_type(rel["r_info_type"], self.elf), "value": "", "name": "" } if rel["r_info_sym"] != 0: symtable = self.elf.get_section(section["sh_link"]) symbol = symtable.get_symbol(rel["r_info_sym"]) # Some symbols have zero "st_name", so instead use # the name of the section they point at if symbol["st_name"] == 0: symsec = self.elf.get_section(symbol["st_shndx"]) symbol_name = symsec.name else: symbol_name = symbol.name relocation["value"] = self._print_addr(symbol["st_value"]) relocation["name"] = symbol_name if relocation not in section_relocations: section_relocations.append(relocation) relocations.append({ "name": section.name, "entries": section_relocations, }) return relocations
def get_relocations_in_elf(obj): rels = [] with open(obj, "rb") as f: elf = ELFFile(f) for section in elf.iter_sections(): if not isinstance(section, RelocationSection): continue symtable = elf.get_section(section['sh_link']) for rel in section.iter_relocations(): if rel['r_info_sym'] == 0: continue rdata = {} rdata["offset"] = int(rel['r_offset']) rdata["info"] = rel['r_info'] rdata["type"] = describe_reloc_type(rel['r_info_type'], elf) symbol = symtable.get_symbol(rel['r_info_sym']) if symbol['st_name'] == 0: symsec = elf.get_section(symbol['st_shndx']) rdata["name"] = str(symsec.name) else: rdata["name"] = str(symbol.name) rdata["value"] = symbol["st_value"] rels.append(rdata) return rels
def lkm_dynlinker(self, ql, mem_start): def get_symbol(elffile, name): section = elffile.get_section_by_name('.symtab') for symbol in section.iter_symbols(): if symbol.name == name: return symbol return None elffile = ELFFile(open(ql.path, 'rb')) all_symbols = [] self.ql.os.hook_addr = API_HOOK_MEM # map address to symbol name ql.import_symbols = {} # reverse dictionary to map symbol name -> address rev_reloc_symbols = {} # dump_mem("XX Original code at 15a1 = ", ql.mem.read(0x15a1, 8)) for section in elffile.iter_sections(): # only care about reloc section if not isinstance(section, RelocationSection): continue # ignore reloc for module section if section.name == ".rela.gnu.linkonce.this_module": continue # The symbol table section pointed to in sh_link symtable = elffile.get_section(section['sh_link']) for rel in section.iter_relocations(): if rel['r_info_sym'] == 0: continue symbol = symtable.get_symbol(rel['r_info_sym']) # Some symbols have zero 'st_name', so instead what's used is # the name of the section they point at. if symbol['st_name'] == 0: symsec = elffile.get_section( symbol['st_shndx']) # save sh_addr of this section symbol_name = symsec.name sym_offset = symsec['sh_offset'] # we need to do reverse lookup from symbol to address rev_reloc_symbols[symbol_name] = sym_offset + mem_start else: symbol_name = symbol.name # get info about related section to be patched info_section = elffile.get_section(section['sh_info']) sym_offset = info_section['sh_offset'] if not symbol_name in all_symbols: _symbol = get_symbol(elffile, symbol_name) if _symbol['st_shndx'] == 'SHN_UNDEF': # external symbol # only save symbols of APIs all_symbols.append(symbol_name) # we need to lookup from address to symbol, so we can find the right callback # for sys_xxx handler for syscall, the address must be aligned to 8 if symbol_name.startswith('sys_'): if self.ql.os.hook_addr % self.ql.pointersize != 0: self.ql.os.hook_addr = ( int(self.ql.os.hook_addr / self.ql.pointersize) + 1) * self.ql.pointersize # print("hook_addr = %x" %self.ql.os.hook_addr) ql.import_symbols[ self.ql.os.hook_addr] = symbol_name # ql.nprint(":: Demigod is hooking %s(), at slot %x" %(symbol_name, self.ql.os.hook_addr)) if symbol_name == "page_offset_base": # FIXME: this is for rootkit to scan for syscall table from page_offset_base # write address of syscall table to this slot, # so syscall scanner can quickly find it ql.mem.write(self.ql.os.hook_addr, self.ql.pack(SYSCALL_MEM)) # we also need to do reverse lookup from symbol to address rev_reloc_symbols[ symbol_name] = self.ql.os.hook_addr sym_offset = self.ql.os.hook_addr - mem_start self.ql.os.hook_addr += self.ql.pointersize else: # local symbol all_symbols.append(symbol_name) _section = elffile.get_section(_symbol['st_shndx']) rev_reloc_symbols[symbol_name] = _section[ 'sh_offset'] + _symbol['st_value'] + mem_start # ql.nprint(":: Add reverse lookup for %s to %x (%x, %x)" %(symbol_name, rev_reloc_symbols[symbol_name], _section['sh_offset'], _symbol['st_value'])) # ql.nprint(":: Add reverse lookup for %s to %x" %(symbol_name, rev_reloc_symbols[symbol_name])) else: sym_offset = rev_reloc_symbols[symbol_name] - mem_start # ql.nprint("Relocating symbol %s -> 0x%x" %(symbol_name, rev_reloc_symbols[symbol_name])) loc = elffile.get_section( section['sh_info'])['sh_offset'] + rel['r_offset'] loc += mem_start if describe_reloc_type(rel['r_info_type'], elffile) == 'R_X86_64_32S': # patch this reloc if rel['r_addend']: val = sym_offset + rel['r_addend'] val += mem_start # ql.nprint('R_X86_64_32S %s: [0x%x] = 0x%x' %(symbol_name, loc, val & 0xFFFFFFFF)) ql.mem.write(loc, ql.pack32(val & 0xFFFFFFFF)) else: # print("sym_offset = %x, rel = %x" %(sym_offset, rel['r_addend'])) # ql.nprint('R_X86_64_32S %s: [0x%x] = 0x%x' %(symbol_name, loc, rev_reloc_symbols[symbol_name] & 0xFFFFFFFF)) ql.mem.write( loc, ql.pack32(rev_reloc_symbols[symbol_name] & 0xFFFFFFFF)) elif describe_reloc_type(rel['r_info_type'], elffile) == 'R_X86_64_64': # patch this function? val = sym_offset + rel['r_addend'] val += 0x2000000 # init_module position: FIXME # finally patch this reloc # ql.nprint('R_X86_64_64 %s: [0x%x] = 0x%x' %(symbol_name, loc, val)) ql.mem.write(loc, ql.pack64(val)) elif describe_reloc_type(rel['r_info_type'], elffile) == 'R_X86_64_PC32': # patch branch address: X86 case val = rel['r_addend'] - loc val += rev_reloc_symbols[symbol_name] # finally patch this reloc # ql.nprint('R_X86_64_PC32 %s: [0x%x] = 0x%x' %(symbol_name, loc, val & 0xFFFFFFFF)) ql.mem.write(loc, ql.pack32(val & 0xFFFFFFFF)) elif describe_reloc_type(rel['r_info_type'], elffile) == 'R_386_PC32': val = ql.unpack(ql.mem.read(loc, 4)) val = rev_reloc_symbols[symbol_name] + val - loc ql.mem.write(loc, ql.pack32(val & 0xFFFFFFFF)) elif describe_reloc_type(rel['r_info_type'], elffile) == 'R_386_32': val = ql.unpack(ql.mem.read(loc, 4)) val = rev_reloc_symbols[symbol_name] + val ql.mem.write(loc, ql.pack32(val & 0xFFFFFFFF)) return rev_reloc_symbols
def get_sections(self): sections = [] for nsec, section in enumerate(self._elf.iter_sections()): result = {} result['nsec'] = nsec result['name'] = section.name result['sh_type'] = describe_sh_type(section['sh_type']) if self._elf.elfclass == 32: result['sh_addr'] = section['sh_addr'] result['shoffset'] = section['sh_offset'] result['sh_size'] = section['sh_size'] result['sh_entsize'] = section['sh_entsize'] result['sh_flags'] = describe_sh_flags(section['sh_flags']) result['sh_link'] = section['sh_link'] result['sh_info'] = section['sh_info'] result['sh_addralign'] = section['sh_addralign'] else: # 64 result['sh_addr'] = section['sh_addr'] result['sh_offset'] = section['sh_offset'] result['sh_size'] = section['sh_size'] result['sh_entsize'] = section['sh_entsize'] result['sh_flags'] = describe_sh_flags(section['sh_flags']) result['sh_link'] = section['sh_link'], section['sh_info'] result['sh_addralign'] = section['sh_addralign'] # Dynamic Section if isinstance(section, DynamicSection): result['special_type'] = 'dynamic' result['dynamic'] = [] has_dynamic_sections = True for tag in section.iter_tags(): dynamic = {} if tag.entry.d_tag == 'DT_NEEDED': parsed = 'Shared library: [%s]' % tag.needed elif tag.entry.d_tag == 'DT_RPATH': parsed = 'Library rpath: [%s]' % tag.rpath elif tag.entry.d_tag == 'DT_RUNPATH': parsed = 'Library runpath: [%s]' % tag.runpath elif tag.entry.d_tag == 'DT_SONAME': parsed = 'Library soname: [%s]' % tag.soname elif tag.entry.d_tag.endswith(('SZ', 'ENT')): parsed = '%i (bytes)' % tag['d_val'] elif tag.entry.d_tag.endswith(('NUM', 'COUNT')): parsed = '%i' % tag['d_val'] elif tag.entry.d_tag == 'DT_PLTREL': s = describe_dyn_tag(tag.entry.d_val) if s.startswith('DT_'): s = s[3:] parsed = '%s' % s else: parsed = '%#x' % tag['d_val'] dynamic['tag'] = ENUM_D_TAG.get( tag.entry.d_tag, tag.entry.d_tag) dynamic['tag_type'] = tag.entry.d_tag[3:] dynamic['tag_value'] = parsed result['dynamic'].append(dynamic) #Relocation Section if isinstance(section, RelocationSection): result['special_type'] = 'relocation' result['relocation'] = [] has_relocation_sections = True # The symbol table section pointed to in sh_link symtable = self._elf.get_section(section['sh_link']) for rel in section.iter_relocations(): relocation = {} relocation['r_offset'] = rel['r_offset'] relocation['r_info'] = rel['r_info'] relocation['r_info_type'] = describe_reloc_type( rel['r_info_type'], self._elf) if rel['r_info_sym'] == 0: continue symbol = symtable.get_symbol(rel['r_info_sym']) # Some symbols have zero 'st_name', so instead what's used is # the name of the section they point at if symbol['st_name'] == 0: symsec = self._elf.get_section(symbol['st_shndx']) relocation['symbol_name'] = symbol_name = symsec.name else: symbol_name = symbol.name relocation['st_value'] = symbol['st_value'] relocation['symbol_name'] = symbol_name if section.is_RELA(): relocation['r_addend'] = rel['r_addend'] result['relocation'].append(relocation) #Symbol Section if isinstance(section, SymbolTableSection): self._init_versioninfo() if section['sh_entsize'] == 0: continue result['special_type'] = 'symbol' result['symbol'] = [] for nsym, symbol in enumerate(section.iter_symbols()): sym_dic = {} version_info = '' # readelf doesn't display version info for Solaris versioning if (section['sh_type'] == 'SHT_DYNSYM' and self._versioninfo['type'] == 'GNU'): version = self._symbol_version(nsym) if (version['name'] != symbol.name and version['index'] not in ('VER_NDX_LOCAL', 'VER_NDX_GLOBAL')): if version['filename']: # external symbol version_info = '@%(name)s (%(index)i)' % version else: # internal symbol if version['hidden']: version_info = '@%(name)s' % version else: version_info = '@@%(name)s' % version # symbol names are truncated to 25 chars, similarly to readelf sym_dic['nsym'] = nsym sym_dic['st_value'] = symbol['st_value'] sym_dic['st_size'] = symbol['st_size'] sym_dic['st_type'] = describe_symbol_type( symbol['st_info']['type']) sym_dic['bind'] = describe_symbol_bind( symbol['st_info']['bind']) sym_dic['vis'] = describe_symbol_visibility( symbol['st_other']['visibility']) sym_dic['ndx'] = describe_symbol_shndx( symbol['st_shndx']) sym_dic['name'] = symbol.name sym_dic['version'] = version_info result['symbol'].append(sym_dic) sections.append(result) return sections
def parse_elf_file(f, file_type, pkg): is_shlib = 'shared object' in file_type \ and '.so' in file_type # Detect PIEs with open(f, 'rb') as stream: elf_file = ELFFile(stream) f = os.path.basename(f) # First collect dependency info dynsect = elf_file.get_section_by_name('.dynamic') if not dynsect: error("%s: no .dynamic section" % f) elif not isinstance(dynsect, DynamicSection): # TODO: investigate error("%s: unexpected type of .dynamic" % f) soname = None deps = [] is_symbolic = False for tag in dynsect.iter_tags(): if tag.entry.d_tag == 'DT_NEEDED': deps.append(tag.needed) elif tag.entry.d_tag == 'DT_SONAME': if soname is not None: error("%s: multiple DT_SONAME in .dynamic section" % f) soname = tag.soname elif tag.entry.d_tag == 'DT_SYMBOLIC' \ or (tag.entry.d_tag == 'DT_FLAGS' and (tag.entry.d_val & 0x2)): is_symbolic = True if not deps and not linker.is_dynamic_linker(f): warn("%s: no DT_NEEDED in .dynamic section" % f) # Get copy relocs (they are not real exports) copy_relocated_addresses = set() reladyn_name = '.rela.dyn' reladyn = elf_file.get_section_by_name(reladyn_name) if not isinstance(reladyn, RelocationSection): warn("%s: unexpected type of .rela.dyn" % f) else: # The symbol table section pointed to in sh_link for rel in reladyn.iter_relocations(): rel_type = describe_reloc_type(rel['r_info_type'], elf_file) if rel_type == 'R_X86_64_COPY': copy_relocated_addresses.add(rel['r_offset']) # Get version names verdef = elf_file.get_section_by_name('.gnu.version_d') ver_names = set() if verdef: if not isinstance(verdef, GNUVerDefSection): error("%s: unexpected type of .gnu.version_d" % f) else: for verdef, verdaux_iter in verdef.iter_versions(): verdaux = next(verdaux_iter) ver_names.add(verdaux.name) # Now analyze interface # TODO: versions symtab = elf_file.get_section_by_name('.dynsym') if not symtab: error("%s: no symbol table in %s") return False if not isinstance(symtab, SymbolTableSection): error("%s: unexpected type of .dynsym" % f) return False obj = Object(f, soname, pkg, deps, [], [], is_shlib, is_symbolic) for ndx, elf_symbol in enumerate(symtab.iter_symbols()): bind = elf_symbol['st_info']['bind'] vis = elf_symbol['st_other']['visibility'] # STB_LOOS means STB_GNU_UNIQUE if bind in ('STB_GLOBAL', 'STB_WEAK', 'STB_LOOS') \ and vis in ('STV_DEFAULT', 'STV_PROTECTED'): if elf_symbol.name in ver_names: continue symbol = Symbol(elf_symbol.name, obj, bind == 'STB_WEAK', vis == 'STV_PROTECTED') if elf_symbol['st_shndx'] == 'SHN_UNDEF' \ or elf_symbol['st_value'] in copy_relocated_addresses: obj.imports.append(symbol) else: obj.exports.append(symbol) return obj
def lkm_dynlinker(self, elffile: ELFFile, mem_start: int) -> Mapping[str, int]: def __get_symbol(name: str) -> Optional[Symbol]: _symtab = elffile.get_section_by_name('.symtab') _sym = _symtab.get_symbol_by_name(name) return _sym[0] if _sym else None ql = self.ql all_symbols = [] self.ql.os.hook_addr = API_HOOK_MEM # map address to symbol name self.import_symbols = {} # reverse dictionary to map symbol name -> address rev_reloc_symbols = {} rh = RelocationHandler(elffile) sections = [ sec for sec in elffile.iter_sections() if sec['sh_flags'] & SH_FLAGS.SHF_ALLOC ] for sec in sections: reloc_sec = rh.find_relocations_for_section(sec) if reloc_sec and reloc_sec.name != '.rela.gnu.linkonce.this_module': # get the symbol table section pointed in sh_link symtab = elffile.get_section(reloc_sec['sh_link']) assert isinstance(symtab, SymbolTableSection) for rel in reloc_sec.iter_relocations(): # if reloc['r_info_sym'] == 0: # continue symbol = symtab.get_symbol(rel['r_info_sym']) assert symbol # Some symbols have zero 'st_name', so instead what's used is # the name of the section they point at. if symbol['st_name'] == 0: symsec = elffile.get_section(symbol['st_shndx']) symbol_name = symsec.name sym_offset = symsec['sh_offset'] rev_reloc_symbols[symbol_name] = sym_offset + mem_start else: symbol_name = symbol.name # get info about related section to be patched info_section = elffile.get_section( reloc_sec['sh_info']) sym_offset = info_section['sh_offset'] if symbol_name in all_symbols: sym_offset = rev_reloc_symbols[ symbol_name] - mem_start else: all_symbols.append(symbol_name) _symbol = __get_symbol(symbol_name) if _symbol['st_shndx'] == 'SHN_UNDEF': # external symbol # only save symbols of APIs # we need to lookup from address to symbol, so we can find the right callback # for sys_xxx handler for syscall, the address must be aligned to pointer size if symbol_name.startswith('sys_'): self.ql.os.hook_addr = self.ql.mem.align_up( self.ql.os.hook_addr, self.ql.arch.pointersize) self.import_symbols[ self.ql.os.hook_addr] = symbol_name # FIXME: this is for rootkit to scan for syscall table from page_offset_base # write address of syscall table to this slot, so syscall scanner can quickly find it if symbol_name == "page_offset_base": ql.mem.write_ptr(self.ql.os.hook_addr, SYSCALL_MEM) # we also need to do reverse lookup from symbol to address rev_reloc_symbols[ symbol_name] = self.ql.os.hook_addr sym_offset = self.ql.os.hook_addr - mem_start self.ql.os.hook_addr += self.ql.arch.pointersize else: # local symbol _section = elffile.get_section( _symbol['st_shndx']) rev_reloc_symbols[symbol_name] = _section[ 'sh_offset'] + _symbol[ 'st_value'] + mem_start # ql.log.info(f'relocating: {symbol_name} -> {rev_reloc_symbols[symbol_name]:#010x}') # FIXME: using the rh.apply_section_relocations method for the following relocation work # seems to be cleaner. loc = elffile.get_section( reloc_sec['sh_info'])['sh_offset'] + rel['r_offset'] loc += mem_start desc = describe_reloc_type(rel['r_info_type'], elffile) if desc in ('R_X86_64_32S', 'R_X86_64_32'): # patch this reloc if rel['r_addend']: val = sym_offset + rel['r_addend'] val += mem_start else: val = rev_reloc_symbols[symbol_name] ql.mem.write_ptr(loc, (val & 0xFFFFFFFF), 4) elif desc == 'R_X86_64_64': val = sym_offset + rel['r_addend'] val += 0x2000000 # init_module position: FIXME ql.mem.write_ptr(loc, val, 8) elif desc == 'R_X86_64_PC64': val = rel['r_addend'] - loc val += rev_reloc_symbols[symbol_name] ql.mem.write_ptr(loc, val, 8) elif desc in ('R_X86_64_PC32', 'R_X86_64_PLT32'): val = rel['r_addend'] - loc val += rev_reloc_symbols[symbol_name] ql.mem.write_ptr(loc, (val & 0xFFFFFFFF), 4) elif desc in ('R_386_PC32', 'R_386_PLT32'): val = ql.mem.read_ptr(loc, 4) val = rev_reloc_symbols[symbol_name] + val - loc ql.mem.write_ptr(loc, (val & 0xFFFFFFFF), 4) elif desc in ('R_386_32', 'R_MIPS_32'): val = ql.mem.read_ptr(loc, 4) val = rev_reloc_symbols[symbol_name] + val ql.mem.write_ptr(loc, (val & 0xFFFFFFFF), 4) elif desc == 'R_MIPS_HI16': # actual relocation is done in R_MIPS_LO16 prev_mips_hi16_loc = loc elif desc == 'R_MIPS_LO16': val = ql.mem.read_ptr(prev_mips_hi16_loc + 2, 2) << 16 | ql.mem.read_ptr( loc + 2, 2) val = rev_reloc_symbols[symbol_name] + val # *(word)(mips_lo16_loc + 2) is treated as signed if (val & 0xFFFF) >= 0x8000: val += (1 << 16) ql.mem.write_ptr(prev_mips_hi16_loc + 2, (val >> 16), 2) ql.mem.write_ptr(loc + 2, (val & 0xFFFF), 2) else: raise NotImplementedError( f'Relocation type {desc} not implemented') return rev_reloc_symbols