def read_libraries(self): for path in os.scandir(self.basedir): if not path.is_file(): continue fullpath = os.path.join(self.basedir, path.name) try: lib = Library(fullpath) except (ELFError, OSError) as err: logging.error('\'%s\' => %s', path, err) continue lib.parse_functions(release=True) logging.debug('Adding %s', lib.fullname) self.libraries.append(lib)
def test_6_propagate_calls_loaderlike(self): store = LibraryStore() bin1 = Library(os.path.abspath(TEST_LL)) bin1a = Library(os.path.abspath(TEST_LL1A)) bin2 = Library(os.path.abspath(TEST_LL2)) # Save possibly set LD_LIBRARY_PATH backup = None if 'LD_LIBRARY_PATH' in os.environ: backup = os.environ['LD_LIBRARY_PATH'] # Set LD_LIBRARY_PATH and resolve libraries os.environ['LD_LIBRARY_PATH'] = '$ORIGIN/' for binary in (bin1, bin1a, bin2): store.resolve_libs_recursive(binary) resolve_calls(store) store.resolve_all_functions_from_binaries() # Possibly restore LD_LIBRARY_PATH if backup: os.environ['LD_LIBRARY_PATH'] = backup else: del os.environ['LD_LIBRARY_PATH'] lib1 = bin1.needed_libs['lib1.so'] lib2 = store[lib1].needed_libs['lib2.so'] # Check if the weak definition of 'wfunc' in 'lib2.so' is not used by # 'lib1.so' but overridden by the strong definition in 'bin' self.assertIn(lib1, store[bin1.fullname].get_users_by_name('wfunc')) self.assertNotIn(bin1.fullname, store[lib2].get_users_by_name('wfunc')) self.assertNotIn(lib1, store[lib2].get_users_by_name('wfunc')) # However, 'bin1a' and 'lib1a.so' should appear for 'wfunc' in 'lib2.so' # as bin1a does not override 'wfunc' itself lib1a = bin1a.needed_libs['lib1a.so'] self.assertIn(bin1a.fullname, store[lib2].get_users_by_name('wfunc')) self.assertIn(lib1a, store[lib2].get_users_by_name('wfunc')) # Check that the weak definition in 'bin2' is only marked as external # but the use is actually overriden by the one in 'lib2.so' self.assertSetEqual(store[bin2.fullname].get_users_by_name('one_more'), set(['TOPLEVEL'])) # Check if the deepest transitively called functions has all binaries # as their user lib3 = store[lib2].needed_libs['lib3.so'] for binary in (bin1, bin1a, bin2): self.assertIn(binary.fullname, store[lib3].get_users_by_name('deeper'))
def create_store_and_lib(libpath=TEST_LIBRARY, parse=False, resolve_libs_recursive=False, call_resolve=False): store = LibraryStore(ldconfig_file=LDCONFIG_FILE) lib = Library(os.path.abspath(libpath), parse=parse) if resolve_libs_recursive: store.resolve_libs_recursive(lib) if call_resolve: resolve_calls(store) return store, lib
def map_wrapper(input_tuple): path, start, size = input_tuple try: if isinstance(path, str): lib = Library(path, parse=True) else: lib = path lib.fd = open(lib.fullname, 'rb') except Exception as err: logging.error('%s: %s', lib.fullname, err) return (None, -1, None, None, None, None, None, None, None, None, None, 0) internal_calls, external_calls, local_calls, indirect_calls, \ imported_uses, exported_uses, local_uses, dlsym_refs, \ dlopen_refs, duration = resolve_calls_in_library(lib, start, size) lib.fd.close() del lib.fd return (lib.fullname, start, internal_calls, external_calls, local_calls, indirect_calls, imported_uses, exported_uses, local_uses, dlsym_refs, dlopen_refs, duration)
def test_7_store_load(self): store, binary = create_store_and_lib(TEST_BINARY, resolve_libs_recursive=True, call_resolve=True) lib = Library(os.path.abspath(TEST_LIBRARY)) resolved_functions = store.resolve_all_functions(all_entries=True) store.propagate_call_usage(all_entries=True) # Create a temporary file, close it (we only need the path) and dump fd, name = tempfile.mkstemp() os.close(fd) store.dump(name) # Reload into an empty store new_store = LibraryStore(ldconfig_file=LDCONFIG_FILE) new_store.load(name) # The file is no longer needed, delete it os.remove(name) # Assert restoration of store self.assertEqual(store.keys(), new_store.keys()) self.assertIn(lib.fullname, new_store.keys()) self.assertIn(binary.fullname, new_store.keys()) # Assert restoration of needed_libs self.assertIn(lib.fullname, new_store[binary.fullname].needed_libs.values()) lib = new_store[lib.fullname] # Assert restoration of calls self.assertIn(binary.fullname, lib.get_users_by_name('external')) self.assertIn(binary.fullname, lib.get_users_by_name('external_caller')) self.assertIn(binary.fullname, lib.get_users_by_name('second_level_caller'))
def _get_or_create_library(self, path): link_path = None try: if os.path.islink(path) or \ os.path.abspath(os.path.dirname(path)) != \ os.path.realpath(os.path.abspath(os.path.dirname(path))): link_path = path path = os.path.realpath(path) path = os.path.abspath(path) if path in self: return (self.get_from_path(path), link_path) return (Library(path), link_path) except (ELFError, OSError) as err: logging.error('\'%s\' => %s', path, err) return (None, None)
def test_6_propagate_calls_exec_only(self): store, binary = create_store_and_lib(TEST_BINARY, resolve_libs_recursive=True, call_resolve=True) libpath = os.path.abspath(TEST_LIBRARY) not_imported = Library(os.path.abspath(TEST_EXECONLY)) store.resolve_libs_recursive(not_imported) store.resolve_all_functions(all_entries=False) store.propagate_call_usage(all_entries=False) # Check if all transitively called functions have the binary as their user self.assertIn(binary.fullname, store[libpath].get_users_by_name('external')) self.assertIn(binary.fullname, store[libpath].get_users_by_name('external_caller')) self.assertIn(binary.fullname, store[libpath].get_users_by_name('second_level_caller')) # In this case, the library not imported from TEST_BINARY should not # show up as a user of TEST_LIBRARY self.assertNotIn(not_imported.fullname, store[libpath].get_users_by_name('external'))
def test_6_propagate_calls_all_entries(self): store, binary = create_store_and_lib(TEST_BINARY, resolve_libs_recursive=True, call_resolve=True) libpath = os.path.abspath(TEST_LIBRARY) not_imported = Library(os.path.abspath(TEST_EXECONLY)) store.resolve_libs_recursive(not_imported) store.resolve_all_functions(all_entries=True) store.propagate_call_usage(all_entries=True) # Check if all transitively called functions have the binary as their user self.assertIn(binary.fullname, store[libpath].get_users_by_name('external')) self.assertIn(binary.fullname, store[libpath].get_users_by_name('external_caller')) self.assertIn(binary.fullname, store[libpath].get_users_by_name('second_level_caller')) # If we use all libraries in the store as entry points for the # resolution, TEST_EXECONLY should show up as a user of 'external' in # TEST_LIBRARY self.assertIn(not_imported.fullname, store[libpath].get_users_by_name('external'))
def test_0_find_export(self): libc = Library(os.path.abspath(TEST_LIBC), parse=True) versioned_addr = libc.find_export('malloc@@GLIBC_2.2.5') unversioned_addr = libc.find_export('malloc') self.assertEqual(versioned_addr, unversioned_addr)
def load(self, input_file): self.reset() logging.debug('loading input from \'%s\'...', input_file) with open(input_file, 'r') as infd: in_dict = json.load(infd) for path, content in in_dict.items(): logging.debug("loading %s -> %s", path, content["type"]) if content["type"] == "link": self._add_library(path, content["target"]) else: library = Library(path, load_elffile=False) def load_dict_with_set_values(from_dict, library, name, convert_key=None): for key, value in from_dict.get(name, {}).items(): key = convert_key(key) if convert_key else key getattr(library, name)[key] = set(value) def load_ordered_dict_from_list(from_dict, library, name): # Recreate order from list for key, value in from_dict.get(name, []): getattr(library, name)[key] = value library.entrypoint = content["entrypoint"] library.e_machine = content.get("e_machine", None) library.ei_class = content.get("ei_class", None) library.imports = content.get("imports", {}) load_ordered_dict_from_list(content, library, "exported_names") library.exported_addrs = collections.defaultdict(list) for name, addr in library.exported_names.items(): library.exported_addrs[addr].append(name) library.export_bind = content["export_bind"] load_dict_with_set_values(content, library, "export_users", int) for key in library.exported_addrs.keys(): if key not in library.export_users: library.export_users[key] = set() load_ordered_dict_from_list(content, library, "imports_plt") load_ordered_dict_from_list(content, library, "exports_plt") load_ordered_dict_from_list(content, library, "needed_libs") load_ordered_dict_from_list(content, library, "all_imported_libs") library.rpaths = content.get("rpaths", []) library.runpaths = content.get("runpaths", []) load_dict_with_set_values(content, library, "internal_calls", int) load_dict_with_set_values(content, library, "external_calls", int) library.local_functions = collections.defaultdict(list) for addr, names in content.get("local_functions", {}).items(): library.local_functions[int(addr)] = names load_dict_with_set_values(content, library, "local_calls", int) load_dict_with_set_values(content, library, "local_users", int) load_ordered_dict_from_list(content, library, "exported_objs") load_ordered_dict_from_list(content, library, "exported_obj_names") load_ordered_dict_from_list(content, library, "imported_objs") load_ordered_dict_from_list(content, library, "imported_objs_locations") load_ordered_dict_from_list(content, library, "local_objs") load_dict_with_set_values(content, library, "object_to_functions", int) load_dict_with_set_values(content, library, "object_to_objects", int) load_dict_with_set_values(content, library, "export_object_refs", int) load_dict_with_set_values(content, library, "local_object_refs", int) load_dict_with_set_values(content, library, "import_object_refs", int) load_dict_with_set_values(content, library, "object_users", int) load_ordered_dict_from_list(content, library, "reloc_to_local") load_ordered_dict_from_list(content, library, "reloc_to_exported") load_dict_with_set_values(content, library, "dlsym_refs", int) load_dict_with_set_values(content, library, "dlopen_refs", int) library.init_functions = content.get("init_functions", []) library.fini_functions = content.get("fini_functions", []) library.ranges = {int(key):value for key, value in content.get("ranges", {}).items()} library.object_ranges = {int(key):value for key, value in content.get("object_ranges", {}).items()} #print('{}: {}'.format(path, sorted(["calls"].items()))) library.defines_versions = content.get("defines_versions", False) library.version_descriptions = content.get("version_descriptions", {}) library.parse_time = float(content.get("parse_time", 0)) library.total_disas_time = float(content.get("total_disas_time", 0)) library.external_debug_file = content.get("external_debug_file", None) library.interpreter = content.get("interpreter", None) self._add_library(path, library) logging.debug('... done with %s entries', len(self))