def rebuild(): print(utils.make_bright("<rebuild>")) with sqlite3.connect(utils.get_libcs_db_filepath()) as conn: conn.execute("DROP TABLE IF EXISTS libcs") conn.execute( "CREATE TABLE libcs" "(relpath text, architecture text, distro text, release text, version text, patch text, buildID text)" ) for filepath in glob.glob(f"{utils.get_libcs_dirpath()}/**", recursive=True): match = re.match( r"(?:.*?)libcs/(?:(?P<distro>.+?)/)?(?:(?P<release>.+?)/)?libc-(?P<architecture>i386|i686|amd64|x86_64|armel|armhf|arm64)-(?P<version>\d.\d+)(?:-(?P<patch>.+?))?\.so$", filepath, ) if match: relpath = os.path.relpath(filepath, utils.get_libcs_dirpath()) print(f"Importing: {utils.make_bright(relpath)}") conn.execute( "INSERT INTO libcs VALUES (?, ?, ?, ?, ?, ?, ?)", ( relpath, match.group("architecture"), match.group("distro"), match.group("release"), match.group("version"), match.group("patch"), utils.extract_buildID(filepath), ), ) print(utils.make_bright("</rebuild>"))
def find(symbols): print(utils.make_bright("<find>")) matches = [] with sqlite3.connect(utils.get_libcs_db_filepath()) as conn: conn.row_factory = sqlite3.Row for libc in conn.execute("SELECT * FROM libcs"): libc_filepath = os.path.join(utils.get_libcs_dirpath(), libc["relpath"]) with open(libc_filepath, "rb") as f: elf = elftools.elf.elffile.ELFFile(f) dynsym_section = elf.get_section_by_name(".dynsym") for symbol, address in symbols: offset = address & 0xFFF try: libc_symbol = dynsym_section.get_symbol_by_name(symbol)[0] libc_offset = libc_symbol.entry.st_value & 0xFFF if libc_offset != offset: break except (IndexError, TypeError): break else: utils.dump(dict(libc)) matches.append(dict(libc)) print(utils.make_bright("</find>")) return matches
def _add_arch_linux_libcs(): def _find_packages_urls(architecture): url = "https://archive.archlinux.org/packages/g/glibc/" try: packages_filenames = utils.findall( fr"['\"](?P<filename>glibc-(?:.*?)-{architecture}\.pkg\.tar\.[gx]z)['\"]", url, ) except AttributeError: print(utils.make_warning(f"Problems: {utils.make_bright(url)}")) return [] else: packages_urls = [ os.path.join(url, package_filename) for package_filename in packages_filenames ] return packages_urls distro_dirpath = os.path.join(utils.get_libcs_dirpath(), "arch") os.makedirs(distro_dirpath, exist_ok=True) for architecture in ("i686", "x86_64"): for package_url in _find_packages_urls(architecture): if _already_in_db(package_url): print(f"Skipping: {utils.make_bright(package_url)}") continue with tempfile.TemporaryDirectory() as tmp_dirpath: print(f"Downloading: {utils.make_bright(package_url)}") package_filepath = utils.retrieve(package_url, tmp_dirpath) add(package_filepath, dest_dirpath=distro_dirpath)
def _add_debian_libcs(): def _find_packages_urls(release, architecture, package): url = f"https://packages.debian.org/{release}/{architecture}/{package}/download" try: package_url = utils.search( r"['\"](?P<url>https?.*?libc6.*?.deb)['\"]", url).group("url") except AttributeError: print(utils.make_warning(f"Problems: {utils.make_bright(url)}")) return [] else: return [package_url] distro_dirpath = os.path.join(utils.get_libcs_dirpath(), "debian") os.makedirs(distro_dirpath, exist_ok=True) for release in ("squeeze", "wheezy", "jessie", "stretch", "buster"): release_dirpath = os.path.join(distro_dirpath, release) os.makedirs(release_dirpath, exist_ok=True) for architecture in ("i386", "amd64"): for package in ("libc6", "libc6-dbg"): for package_url in _find_packages_urls(release, architecture, package): if _already_in_db(package_url): print(f"Skipping: {utils.make_bright(package_url)}") continue with tempfile.TemporaryDirectory() as tmp_dirpath: print(f"Downloading: {utils.make_bright(package_url)}") package_filepath = utils.retrieve( package_url, tmp_dirpath) add(package_filepath, dest_dirpath=release_dirpath)
def _find_matching_file_and_add_to_db(search_paths, dest_dirpath, new_filename): filepath = _find_matching_file(search_paths) if not filepath: return None new_filepath = os.path.join(dest_dirpath, new_filename) shutil.copy2(filepath, new_filepath) relpath = os.path.relpath(new_filepath, utils.get_libcs_dirpath()) print(f"Added: {utils.make_bright(f'.../{relpath}')}") return new_filepath
def _add_ubuntu_libcs(): def _find_packages_urls(release, architecture, package): url = f"https://launchpad.net/ubuntu/{release}/{architecture}/{package}" packages_versions = set( utils.findall( fr'"/ubuntu/.+?/{package}/(?P<version>.+?)(?:\.\d+)?"', url)) if not packages_versions: print(utils.make_warning(f"Problems: {utils.make_bright(url)}")) return [] n = 3 most_recent_packages_versions = sorted(packages_versions, reverse=True)[:n] packages_urls = [ utils.search( r"['\"](?P<url>https?.*?libc6.*?.deb)['\"]", f"https://launchpad.net/ubuntu/{release}/{architecture}/{package}/{package_filename}", ).group("url") for package_filename in most_recent_packages_versions ] if not packages_urls: print(utils.make_warning(f"Problems: {utils.make_bright(url)}")) return [] return packages_urls distro_dirpath = os.path.join(utils.get_libcs_dirpath(), "ubuntu") os.makedirs(distro_dirpath, exist_ok=True) for release in ("trusty", "xenial", "artful", "bionic"): release_dirpath = os.path.join(distro_dirpath, release) os.makedirs(release_dirpath, exist_ok=True) for architecture in ("i386", "amd64"): for package in ("libc6", "libc6-dbg"): for package_url in _find_packages_urls(release, architecture, package): if _already_in_db(package_url): print(f"Skipping: {utils.make_bright(package_url)}") continue with tempfile.TemporaryDirectory() as tmp_dirpath: print(f"Downloading: {utils.make_bright(package_url)}") package_filepath = utils.retrieve( package_url, tmp_dirpath) add(package_filepath, dest_dirpath=release_dirpath)
def patch(binary_filepath, supplied_libc_filepath): print(utils.make_bright("<patch>")) binary_dirpath = os.path.dirname(binary_filepath) # identify the supplied libc matches = identify(supplied_libc_filepath) if not matches: utils.abort("The supplied libc is not in the local library.") # TODO pick the first for now libc = matches[0] libc_filepath = os.path.join(utils.get_libcs_dirpath(), libc["relpath"]) libc_architecture = libc["architecture"] libc_patch = libc["patch"] libc_version = libc["version"] ld_filepath = os.path.join( os.path.dirname(libc_filepath), os.path.basename(libc_filepath).replace("libc-", "ld-"), ) # if the dynamic loader does not exist, abort (don't care about race conditions) if not os.path.isfile(ld_filepath): utils.abort( "The dynamic loader corresponding to the libc to use cannot be found." f" It should reside at {utils.make_bright(ld_filepath)}" ) # copy the dynamic loader and the libc to the directory where the binary is located libs_dirpath = os.path.join( binary_dirpath, "libs", libc_architecture, libc_version, libc_patch ) ld_proper_filename = f"ld-{libc_version}.so" ld_proper_filepath = os.path.join(libs_dirpath, ld_proper_filename) libc_proper_filename = f"libc-{libc_version}.so" libc_proper_filepath = os.path.join(libs_dirpath, libc_proper_filename) if not utils.query_yes_no( "Copy:\n" f"- {utils.make_bright(ld_filepath)}\n" f"- {utils.make_bright(libc_filepath)}\n" "to:\n" f"- {utils.make_bright(ld_proper_filepath)}\n" f"- {utils.make_bright(libc_proper_filepath)}\n" "?" ): utils.abort("Aborted by user.") os.makedirs(libs_dirpath, exist_ok=True) shutil.copy2(ld_filepath, ld_proper_filepath) shutil.copy2(libc_filepath, libc_proper_filepath) print() # if debug symbols exist, copy them also libc_dbg_filepath = f"{libc_filepath}.debug" if os.path.isfile(libc_dbg_filepath): libs_debug_dirpath = os.path.join(libs_dirpath, ".debug") libc_dbg_proper_filename = utils.get_libc_dbg_proper_filename(libc_filepath) libc_dbg_proper_filepath = os.path.join( libs_debug_dirpath, libc_dbg_proper_filename ) if utils.query_yes_no( "Copy:\n" f"- {utils.make_bright(libc_dbg_filepath)}\n" "to:\n" f"- {utils.make_bright(libc_dbg_proper_filepath)}\n" "?" ): os.makedirs(libs_debug_dirpath, exist_ok=True) shutil.copy2(libc_dbg_filepath, libc_dbg_proper_filepath) print() # patch the binary to use the new dynamic loader and libc patched_binary_filepath = ( f"{binary_filepath}-{libc_architecture}-{libc_version}-{libc_patch}" ) if not utils.query_yes_no( "Copy:\n" f"- {utils.make_bright(binary_filepath)}\n" "to:\n" f"- {utils.make_bright(patched_binary_filepath)}\n" "and patch the latter?" ): utils.abort("Aborted by user.") shutil.copy2(binary_filepath, patched_binary_filepath) ld_basename = os.path.basename(ld_proper_filename) libc_basename = os.path.basename(libc_proper_filename) subprocess.run( ( f"patchelf --set-interpreter {shlex.quote(os.path.relpath(ld_proper_filepath, binary_dirpath))} {shlex.quote(patched_binary_filepath)}" f" && patchelf --add-needed {shlex.quote(os.path.relpath(libc_proper_filepath, binary_dirpath))} {shlex.quote(patched_binary_filepath)}" ), check=True, shell=True, ) print(utils.make_bright("</patch>"))
def add(package_filepath, dest_dirpath=utils.get_libcs_dirpath()): print(utils.make_bright("<add>")) match = utils.match(package_filepath) if not match: print( utils.make_warning( f"Skipping: the filename of the package did not match any supported patterns." )) return libc_architecture = match.group("architecture") libc_version = match.group("version") libc_patch = match.group("patch") try: tmp_dirpath = extract(package_filepath) except subprocess.CalledProcessError: print( utils.make_warning( f"Problems during the parsing of the package: {package_filepath}" )) print(utils.make_warning(f"Probably format not supported (yet)")) return # find and add ld ld_search_paths = [ os.path.join(tmp_dirpath, subpath) for subpath in ( "lib/aarch64-linux-gnu/ld-*.so", "lib/arm-linux-gnueabihf/ld-*.so", "lib/arm-linux-gnueabi/ld-*.so", "lib/i386-linux-gnu/ld-*.so", "lib/x86_64-linux-gnu/ld-*.so", "usr/lib/ld-*.so", ) ] new_ld_filename = f"ld-{libc_architecture}-{libc_version}-{libc_patch}.so" new_ld_filepath = _find_matching_file_and_add_to_db( ld_search_paths, dest_dirpath, new_ld_filename) # find and add libc libc_search_paths = [ os.path.join(tmp_dirpath, subpath) for subpath in ( "lib/aarch64-linux-gnu/libc-*.so", "lib/arm-linux-gnueabihf/libc-*.so", "lib/arm-linux-gnueabi/libc-*.so", "lib/i386-linux-gnu/libc-*.so", "lib/x86_64-linux-gnu/libc-*.so", "usr/lib/libc-*.so", ) ] new_libc_filename = f"libc-{libc_architecture}-{libc_version}-{libc_patch}.so" new_libc_filepath = _find_matching_file_and_add_to_db( libc_search_paths, dest_dirpath, new_libc_filename) # find and add libc symbols libc_symbols_search_paths = [ os.path.join(tmp_dirpath, subpath) for subpath in ( "usr/lib/debug/lib/i386-linux-gnu/libc-*.so", "usr/lib/debug/lib/x86_64-linux-gnu/libc-*.so", ) ] new_libc_symbols_filename = ( f"libc-{libc_architecture}-{libc_version}-{libc_patch}.so.debug") new_libc_symbols_filepath = _find_matching_file_and_add_to_db( libc_symbols_search_paths, dest_dirpath, new_libc_symbols_filename) if not any( (new_ld_filepath, new_libc_filepath, new_libc_symbols_filepath)): print( utils.make_warning( f"Skipping: the package seems to not contain a dynamic loader, libc or debug symbols." )) return # keep the package, it may be useful later shutil.copy2(package_filepath, dest_dirpath) print(utils.make_bright("</add>"))