예제 #1
0
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>"))
예제 #2
0
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
예제 #3
0
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)
예제 #4
0
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)
예제 #5
0
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
예제 #6
0
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)
예제 #7
0
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>"))
예제 #8
0
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>"))