def hook_symbol_batch(self, hooks): """ Hook many symbols at once. :param dict hooks: A mapping from symbol name to hook """ provisions = {} for name, obj in hooks.iteritems(): sym = self.loader.find_symbol(name) if sym is None: hook_addr, link_addr = self._simos.prepare_function_symbol( name) l.info("Providing extern symbol for unresolved %s at #%x", name, hook_addr) self.hook(hook_addr, obj) provisions[name] = (AT.from_mva(link_addr, self._extern_obj).to_lva(), 0, None) else: hook_addr, _ = self._simos.prepare_function_symbol( name, basic_addr=sym.rebased_addr) if self.is_hooked(hook_addr): l.warning("Re-hooking symbol %s", name) self.unhook(hook_addr) self.hook(hook_addr, obj) if provisions: self.loader.provide_symbol_batch(self._extern_obj, provisions)
def hook_symbol(self, symbol_name, obj, kwargs=None): """ Resolve a dependency in a binary. Uses the "externs object" (project.loader.extern_object) to allocate an address for a new symbol in the binary, and then tells the loader to re-perform the relocation process, taking into account the new symbol. :param symbol_name: The name of the dependency to resolve. :param obj: The thing with which to satisfy the dependency. :param kwargs: If you provide a SimProcedure for the hook, these are the keyword arguments that will be passed to the procedure's `run` method eventually. :returns: The address of the new symbol. :rtype: int """ if type(obj) in (int, long): # this is pretty intensely sketchy l.info("Instructing the loader to re-point symbol %s at address %#x", symbol_name, obj) self.loader.provide_symbol(self.loader.extern_object, symbol_name, AT.from_mva(obj, self.loader.extern_object).to_rva()) return obj sym = self.loader.find_symbol(symbol_name) if sym is None: l.error("Could not find symbol %s", symbol_name) return None hook_addr, _ = self._simos.prepare_function_symbol(symbol_name, basic_addr=sym.rebased_addr) if self.is_hooked(hook_addr): l.warning("Re-hooking symbol %s", symbol_name) self.unhook(hook_addr) self.hook(hook_addr, obj, kwargs=kwargs) return hook_addr
def hook_symbol(self, symbol_name, obj, kwargs=None, replace=None): """ Resolve a dependency in a binary. Looks up the address of the given symbol, and then hooks that address. If the symbol was not available in the loaded libraries, this address may be provided by the CLE externs object. :param symbol_name: The name of the dependency to resolve. :param obj: The thing with which to satisfy the dependency. :param kwargs: If you provide a SimProcedure for the hook, these are the keyword arguments that will be passed to the procedure's `run` method eventually. :param replace: Control the behavior on finding that the address is already hooked. If true, silently replace the hook. If false, warn and do not replace the hook. If none (default), warn and replace the hook. :returns: The address of the new symbol. :rtype: int """ if type(obj) in (int, long): # this is pretty intensely sketchy l.info("Instructing the loader to re-point symbol %s at address %#x", symbol_name, obj) self.loader.provide_symbol(self.loader.extern_object, symbol_name, AT.from_mva(obj, self.loader.extern_object).to_rva()) return obj sym = self.loader.find_symbol(symbol_name) if sym is None: l.error("Could not find symbol %s", symbol_name) return None hook_addr, _ = self._simos.prepare_function_symbol(symbol_name, basic_addr=sym.rebased_addr) self.hook(hook_addr, obj, kwargs=kwargs, replace=replace) return hook_addr
def find_symbol(self, name): """ Search for the symbol with the given name or address. :param name: Either the name or address of a symbol to look up :returns: A :class:`cle.backends.Symbol` object if found, None otherwise. """ if type(name) in (int, long): so = self.find_object_containing(name) if so is not None: addr = AT.from_mva(name, so).to_rva() if addr in so._symbols_by_addr: return so._symbols_by_addr[addr] else: for so in self.all_objects: if so is self._extern_object: continue sym = so.get_symbol(name) if sym is None: continue if sym.is_import: if sym.resolvedby is not None: return sym.resolvedby else: return sym if self._extern_object is not None: sym = self.extern_object.get_symbol(name) if sym is not None: return sym return None
def prepare_function_symbol(self, symbol_name, basic_addr=None): """ Prepare the address space with the data necessary to perform relocations pointing to the given symbol. Returns a 2-tuple. The first item is the address of the function code, the second is the address of the relocation target. """ if self.project.loader.main_object.is_ppc64_abiv1: if basic_addr is not None: pointer = self.project.loader.memory.unpack_word(basic_addr) return pointer, basic_addr pseudo_hookaddr = self.project.loader.extern_object.get_pseudo_addr( symbol_name) pseudo_toc = self.project.loader.extern_object.allocate(size=0x18) self.project.loader.extern_object.memory.pack_word( AT.from_mva(pseudo_toc, self.project.loader.extern_object).to_rva(), pseudo_hookaddr) return pseudo_hookaddr, pseudo_toc else: if basic_addr is None: basic_addr = self.project.loader.extern_object.get_pseudo_addr( symbol_name) return basic_addr, basic_addr
def prepare_function_symbol(self, symbol_name, basic_addr=None): """ Prepare the address space with the data necessary to perform relocations pointing to the given symbol. Returns a 2-tuple. The first item is the address of the function code, the second is the address of the relocation target. """ if self.arch.name == 'PPC64': if basic_addr is not None: pointer = self.project.loader.memory.read_addr_at(basic_addr) return pointer, basic_addr pseudo_hookaddr = self.project._extern_obj.get_pseudo_addr( symbol_name) pseudo_toc = self.project._extern_obj.get_pseudo_addr(symbol_name + '#func', size=0x18) self.project._extern_obj.memory.write_addr_at( AT.from_mva(pseudo_toc, self.project._extern_obj).to_rva(), pseudo_hookaddr) return pseudo_hookaddr, pseudo_toc else: if basic_addr is None: basic_addr = self.project._extern_obj.get_pseudo_addr( symbol_name) return basic_addr, basic_addr
def run_sections(arch, filename, sections): binary_path = os.path.join(TESTS_BASE, arch, filename) ld = cle.Loader(binary_path, auto_load_libs=False) nose.tools.assert_equal(len(ld.main_object.sections), len(sections)) for i, section in enumerate(ld.main_object.sections): nose.tools.assert_equal(section.name, sections[i].name) nose.tools.assert_equal(section.offset, sections[i].offset) nose.tools.assert_equal( AT.from_mva(section.vaddr, ld.main_object).to_lva(), sections[i].vaddr) nose.tools.assert_equal(section.memsize, sections[i].memsize) # address lookups nose.tools.assert_is_none( ld.main_object.sections.find_region_containing(-1)) # skip all sections that are not mapped into memory mapped_sections = [section for section in sections if section.vaddr != 0] for section in mapped_sections: nose.tools.assert_equal( ld.main_object.find_section_containing(section.vaddr).name, section.name) nose.tools.assert_equal( ld.main_object.sections.find_region_containing(section.vaddr).name, section.name) if section.memsize > 0: nose.tools.assert_equal( ld.main_object.find_section_containing(section.vaddr + 1).name, section.name) nose.tools.assert_equal( ld.main_object.sections.find_region_containing(section.vaddr + 1).name, section.name) nose.tools.assert_equal( ld.main_object.find_section_containing(section.vaddr + section.memsize - 1).name, section.name) nose.tools.assert_equal( ld.main_object.sections.find_region_containing( section.vaddr + section.memsize - 1).name, section.name) for i in range(len(mapped_sections) - 1): sec_a, sec_b = mapped_sections[i], mapped_sections[i + 1] if sec_a.vaddr + sec_a.memsize < sec_b.vaddr: # there is a gap between sec_a and sec_b for j in range(min(sec_b.vaddr - (sec_a.vaddr + sec_a.memsize), 20)): a = sec_a.vaddr + sec_a.memsize + j nose.tools.assert_is_none( ld.main_object.find_section_containing(a)) nose.tools.assert_is_none( ld.main_object.sections.find_region_containing(a)) nose.tools.assert_is_none( ld.main_object.find_section_containing(0xffffffff), None)
def make_extern(self, name, size=0, alignment=None, thumb=False, sym_type=Symbol.TYPE_FUNCTION, libname=None): try: return self._symbol_cache[name] except KeyError: pass l.info("Creating extern symbol for %s", name) if alignment is None: alignment = self.arch.bytes SymbolCls = Symbol simdata = lookup(name, libname) tls = sym_type == Symbol.TYPE_TLS_OBJECT if simdata is not None: SymbolCls = simdata size = simdata.static_size(self) if sym_type != simdata.type: l.warning("Symbol type mismatch between export request and response for %s. What's going on?", name) addr = self.allocate(max(size, 1), alignment=alignment, thumb=thumb, tls=tls) if hasattr(self.loader.main_object, 'is_ppc64_abiv1') and self.loader.main_object.is_ppc64_abiv1 and sym_type == Symbol.TYPE_FUNCTION: func_symbol = SymbolCls(self, name + '#func', AT.from_mva(addr, self).to_rva(), size, sym_type) func_symbol.is_export = True func_symbol.is_extern = True self._symbol_cache[name + '#func'] = func_symbol self.symbols.add(func_symbol) self._init_symbol(func_symbol) toc = self.allocate(0x18, alignment=8) size = 0x18 self.memory.pack_word(AT.from_mva(toc, self).to_rva(), addr) addr = toc sym_type = Symbol.TYPE_OBJECT SymbolCls = Symbol new_symbol = SymbolCls(self, name, addr if tls else AT.from_mva(addr, self).to_rva(), size, sym_type) new_symbol.is_export = True new_symbol.is_extern = True self._symbol_cache[name] = new_symbol self.symbols.add(new_symbol) self._init_symbol(new_symbol) return new_symbol
def hook_symbol(self, symbol_name, obj, kwargs=None, replace=None): """ Resolve a dependency in a binary. Looks up the address of the given symbol, and then hooks that address. If the symbol was not available in the loaded libraries, this address may be provided by the CLE externs object. Additionally, if instead of a symbol name you provide an address, some secret functionality will kick in and you will probably just hook that address, UNLESS you're on powerpc64 ABIv1 or some yet-unknown scary ABI that has its function pointers point to something other than the actual functions, in which case it'll do the right thing. :param symbol_name: The name of the dependency to resolve. :param obj: The thing with which to satisfy the dependency. :param kwargs: If you provide a SimProcedure for the hook, these are the keyword arguments that will be passed to the procedure's `run` method eventually. :param replace: Control the behavior on finding that the address is already hooked. If true, silently replace the hook. If false, warn and do not replace the hook. If none (default), warn and replace the hook. :returns: The address of the new symbol. :rtype: int """ if type(obj) in (int, long): # this is pretty intensely sketchy l.info("Instructing the loader to re-point symbol %s at address %#x", symbol_name, obj) self.loader.provide_symbol(self.loader.extern_object, symbol_name, AT.from_mva(obj, self.loader.extern_object).to_rva()) return obj if type(symbol_name) not in (int, long): sym = self.loader.find_symbol(symbol_name) if sym is None: # it could be a previously unresolved weak symbol..? new_sym = None for reloc in self.loader.find_relevant_relocations(symbol_name): if not reloc.symbol.is_weak: raise Exception("Symbol is strong but we couldn't find its resolution? Report to @rhelmot.") if new_sym is None: new_sym = self.loader.extern_object.make_extern(symbol_name) reloc.resolve(new_sym) reloc.relocate([]) if new_sym is None: l.error("Could not find symbol %s", symbol_name) return None sym = new_sym basic_addr = sym.rebased_addr else: basic_addr = symbol_name symbol_name = None hook_addr, _ = self.simos.prepare_function_symbol(symbol_name, basic_addr=basic_addr) self.hook(hook_addr, obj, kwargs=kwargs, replace=replace) return hook_addr
def hook_symbol(self, symbol_name, obj, kwargs=None, replace=None): """ Resolve a dependency in a binary. Looks up the address of the given symbol, and then hooks that address. If the symbol was not available in the loaded libraries, this address may be provided by the CLE externs object. Additionally, if instead of a symbol name you provide an address, some secret functionality will kick in and you will probably just hook that address, UNLESS you're on powerpc64 ABIv1 or some yet-unknown scary ABI that has its function pointers point to something other than the actual functions, in which case it'll do the right thing. :param symbol_name: The name of the dependency to resolve. :param obj: The thing with which to satisfy the dependency. :param kwargs: If you provide a SimProcedure for the hook, these are the keyword arguments that will be passed to the procedure's `run` method eventually. :param replace: Control the behavior on finding that the address is already hooked. If true, silently replace the hook. If false, warn and do not replace the hook. If none (default), warn and replace the hook. :returns: The address of the new symbol. :rtype: int """ if type(obj) in (int, long): # this is pretty intensely sketchy l.info( "Instructing the loader to re-point symbol %s at address %#x", symbol_name, obj) self.loader.provide_symbol( self.loader.extern_object, symbol_name, AT.from_mva(obj, self.loader.extern_object).to_rva()) return obj if type(symbol_name) not in (int, long): sym = self.loader.find_symbol(symbol_name) if sym is None: l.error("Could not find symbol %s", symbol_name) return None basic_addr = sym.rebased_addr else: basic_addr = symbol_name symbol_name = None hook_addr, _ = self.simos.prepare_function_symbol( symbol_name, basic_addr=basic_addr) self.hook(hook_addr, obj, kwargs=kwargs, replace=replace) return hook_addr
def find_object_containing(self, addr): """ Return the object that contains the given address, or None if the address is unmapped. """ for obj in self.all_objects: if not obj.min_addr <= addr < obj.max_addr: continue if isinstance(obj.memory, str): return obj elif isinstance(obj.memory, Clemory): if AT.from_va(addr, obj).to_rva() in obj.memory: return obj else: raise CLEError('Unsupported memory type %s' % type(obj.memory)) return None
def print_payload_code(self, constraints=None, print_instructions=True): """ :param print_instructions: prints the instructions that the rop gadgets use :return: prints the code for the rop payload """ if self._p.arch.bits == 32: pack = "p32(%#x)" pack_rebase = "p32(%#x + base_addr)" else: pack = "p64(%#x)" pack_rebase = "p64(%#x + base_addr)" if self._pie: payload = "base_addr = 0x0\n" else: payload = "" payload += 'chain = ""\n' gadget_dict = {g.addr: g for g in self._gadgets} concrete_vals = self._concretize_chain_values(constraints) for value, needs_rebase in concrete_vals: instruction_code = "" if print_instructions: if needs_rebase: #dealing with pie code value_in_gadget = AT.from_lva( value, self._p.loader.main_object).to_mva() else: value_in_gadget = value if value_in_gadget in gadget_dict: asmstring = rop_utils.gadget_to_asmstring( self._p, gadget_dict[value_in_gadget]) if asmstring != "": instruction_code = "\t# " + asmstring if needs_rebase: payload += "chain += " + pack_rebase % value + instruction_code else: payload += "chain += " + pack % value + instruction_code payload += "\n" print(payload)
def prepare_function_symbol(self, symbol_name, basic_addr=None): """ Prepare the address space with the data necessary to perform relocations pointing to the given symbol. Returns a 2-tuple. The first item is the address of the function code, the second is the address of the relocation target. """ if self.project.loader.main_object.is_ppc64_abiv1: if basic_addr is not None: pointer = self.project.loader.memory.read_addr_at(basic_addr) return pointer, basic_addr pseudo_hookaddr = self.project.loader.extern_object.get_pseudo_addr(symbol_name) pseudo_toc = self.project.loader.extern_object.allocate(size=0x18) self.project.loader.extern_object.memory.write_addr_at(AT.from_mva(pseudo_toc, self.project.loader.extern_object).to_rva(), pseudo_hookaddr) return pseudo_hookaddr, pseudo_toc else: if basic_addr is None: basic_addr = self.project.loader.extern_object.get_pseudo_addr(symbol_name) return basic_addr, basic_addr
def print_payload_code(self, constraints=None, print_instructions=True): """ :param print_instructions: prints the instructions that the rop gadgets use :return: prints the code for the rop payload """ if self._p.arch.bits == 32: pack = "p32(%#x)" pack_rebase = "p32(%#x + base_addr)" else: pack = "p64(%#x)" pack_rebase = "p64(%#x + base_addr)" if self._pie: payload = "base_addr = 0x0\n" else: payload = "" payload += 'chain = ""\n' gadget_dict = {g.addr:g for g in self._gadgets} concrete_vals = self._concretize_chain_values(constraints) for value, needs_rebase in concrete_vals: instruction_code = "" if print_instructions: if needs_rebase: #dealing with pie code value_in_gadget = AT.from_lva(value, self._p.loader.main_object).to_mva() else: value_in_gadget = value if value_in_gadget in gadget_dict: asmstring = rop_utils.gadget_to_asmstring(self._p,gadget_dict[value_in_gadget]) if asmstring != "": instruction_code = "\t# " + asmstring if needs_rebase: payload += "chain += " + pack_rebase % value + instruction_code else: payload += "chain += " + pack % value + instruction_code payload += "\n" print(payload)
def run_sections(arch, filename, sections): binary_path = os.path.join(TESTS_BASE, arch, filename) ld = cle.Loader(binary_path, auto_load_libs=False) nose.tools.assert_equal(len(ld.main_object.sections), len(sections)) for i, section in enumerate(ld.main_object.sections): nose.tools.assert_equal(section.name, sections[i].name) nose.tools.assert_equal(section.offset, sections[i].offset) nose.tools.assert_equal(AT.from_mva(section.vaddr, ld.main_object).to_lva(), sections[i].vaddr) nose.tools.assert_equal(section.memsize, sections[i].memsize) # address lookups nose.tools.assert_is_none(ld.main_object.sections.find_region_containing(-1)) # skip all sections that are not mapped into memory mapped_sections = [ section for section in sections if section.vaddr != 0 ] for section in mapped_sections: nose.tools.assert_equal(ld.main_object.find_section_containing(section.vaddr).name, section.name) nose.tools.assert_equal(ld.main_object.sections.find_region_containing(section.vaddr).name, section.name) if section.memsize > 0: nose.tools.assert_equal(ld.main_object.find_section_containing(section.vaddr + 1).name, section.name) nose.tools.assert_equal(ld.main_object.sections.find_region_containing(section.vaddr + 1).name, section.name) nose.tools.assert_equal(ld.main_object.find_section_containing(section.vaddr + section.memsize - 1).name, section.name) nose.tools.assert_equal( ld.main_object.sections.find_region_containing(section.vaddr + section.memsize - 1).name, section.name) for i in range(len(mapped_sections) - 1): sec_a, sec_b = mapped_sections[i], mapped_sections[i + 1] if sec_a.vaddr + sec_a.memsize < sec_b.vaddr: # there is a gap between sec_a and sec_b for j in range(min(sec_b.vaddr - (sec_a.vaddr + sec_a.memsize), 20)): a = sec_a.vaddr + sec_a.memsize + j nose.tools.assert_is_none(ld.main_object.find_section_containing(a)) nose.tools.assert_is_none(ld.main_object.sections.find_region_containing(a)) nose.tools.assert_is_none(ld.main_object.find_section_containing(0xffffffff), None)
def describe_addr(self, addr): """ Returns a textual description of what's in memory at the provided address """ o = self.find_object_containing(addr) if o is None: return None off = AT.from_va(addr, o).to_rva() nameof = 'main binary' if o is self.main_object else o.provides if isinstance(o, ELF): if addr in o._plt.values(): for k,v in o._plt.iteritems(): if v == addr: return "PLT stub of %s in %s (offset %#x)" % (k, nameof, off) if off in o._symbols_by_addr: name = o._symbols_by_addr[off].name return "%s (offset %#x) in %s" % (name, off, nameof) return "Offset %#x in %s" % (off, nameof)
def add_name(self, name, addr): self._symbol_cache[name] = Symbol(self, name, AT.from_mva(addr, self).to_rva(), 1, SymbolType.TYPE_FUNCTION)
def test_invalid_va_raw(): nose.tools.assert_equal(AT.from_va(0xa1b6ed4, owner).to_raw(), None)
def test_invalid_intersegment_raw_va(): AT.from_raw(0x1b3000, owner).to_va()
def test_valid_va_raw_translations(): nose.tools.assert_equal(AT.from_raw(0x1b3260, owner).to_va(), 0xa1b4260) nose.tools.assert_equal(AT.from_va(0xa1b6ed3, owner).to_raw(), 0x1b5ed3)
def test_va_rva_translation(): nose.tools.assert_equal(AT.from_rva(0, owner).to_va(), 0xa000000) nose.tools.assert_equal(AT.from_va(0xa1b9a1b, owner).to_rva(), 0x1b9a1b)
def test_lva_mva_translation(): nose.tools.assert_equal(AT.from_lva(0x8048000, owner).to_mva(), 0xa000000) nose.tools.assert_equal(AT.from_mva(0xa1b9a1b, owner).to_lva(), 0x8201a1b)
def rebase(self): super(AngrExternObject, self).rebase() self._next_addr = AT.from_lva(self._next_addr, self).to_mva()
def make_extern(self, name, size=0, alignment=None, thumb=False, sym_type=SymbolType.TYPE_FUNCTION, libname=None): try: return self._symbol_cache[name] except KeyError: pass l.info("Creating extern symbol for %s", name) if alignment is None: alignment = self.arch.bytes SymbolCls = Symbol simdata = lookup(name, libname) tls = sym_type == SymbolType.TYPE_TLS_OBJECT if simdata is not None: SymbolCls = simdata size = simdata.static_size(self) if sym_type != simdata.type: l.warning( "Symbol type mismatch between export request and response for %s. What's going on?", name) addr = self.allocate(max(size, 1), alignment=alignment, thumb=thumb, tls=tls) if hasattr( self.loader.main_object, 'is_ppc64_abiv1' ) and self.loader.main_object.is_ppc64_abiv1 and sym_type == SymbolType.TYPE_FUNCTION: func_symbol = SymbolCls(self, name + '#func', AT.from_mva(addr, self).to_rva(), size, sym_type) func_symbol.is_export = True func_symbol.is_extern = True self._symbol_cache[name + '#func'] = func_symbol self.symbols.add(func_symbol) self._init_symbol(func_symbol) toc = self.allocate(0x18, alignment=8) size = 0x18 self.memory.pack_word(AT.from_mva(toc, self).to_rva(), addr) addr = toc sym_type = SymbolType.TYPE_OBJECT SymbolCls = Symbol new_symbol = SymbolCls( self, name, addr if tls else AT.from_mva(addr, self).to_rva(), size, sym_type) new_symbol.is_export = True new_symbol.is_extern = True self._symbol_cache[name] = new_symbol self.symbols.add(new_symbol) self._init_symbol(new_symbol) return new_symbol
def add_name(self, name, addr): self._symbol_cache[name] = Symbol(self, name, AT.from_mva(addr, self).to_rva(), 1, Symbol.TYPE_FUNCTION)
def max_addr(self): return AT.from_rva(self.map_size - 1, self).to_mva()
def max_addr(self): return AT.from_rva(self.map_size, self).to_mva()