Beispiel #1
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
Beispiel #2
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>"))
Beispiel #3
0
def extract(package_filepath):
    print(utils.make_bright("<extract>"))

    package_filename = os.path.basename(package_filepath)

    tmp_dirpath = tempfile.mkdtemp()
    shutil.copy2(package_filepath, tmp_dirpath)
    # extract the package
    subprocess.run(
        f"tar xf {shlex.quote(package_filename)}"
        if package_filepath.endswith("tar.xz") else
        f"ar x {shlex.quote(package_filename)}",
        cwd=tmp_dirpath,
        check=True,
        shell=True,
        stderr=subprocess.PIPE,
    )
    # extract data.tar.?z if it exists
    subprocess.run(
        f"if [ -f data.tar.?z ]; then tar xf data.tar.?z; fi",
        cwd=tmp_dirpath,
        check=True,
        shell=True,
    )
    print(f"Extracted: {utils.make_bright(tmp_dirpath)}")

    print(utils.make_bright("</extract>"))
    return tmp_dirpath
Beispiel #4
0
def bootstrap(ubuntu_only):
    print(utils.make_bright("<bootstrap>"))

    if not utils.query_yes_no(
            "This operation will download a bunch of libcs into"
            f" {utils.make_bright(utils.get_libcs_dirpath())}. Proceed?"):
        utils.abort("Aborted by user.")

    _add_ubuntu_libcs()
    if not ubuntu_only:
        _add_debian_libcs()
        _add_arch_linux_libcs()

    print(utils.make_bright("</bootstrap>"))
Beispiel #5
0
def dump(libc_filepath, symbols):
    print(utils.make_bright("<dump>"))

    with open(libc_filepath, "rb") as f:
        elf = elftools.elf.elffile.ELFFile(f)
        dynsym_section = elf.get_section_by_name(".dynsym")
        for symbol in symbols:
            try:
                libc_symbol = dynsym_section.get_symbol_by_name(symbol)[0]
                libc_offset = libc_symbol.entry.st_value & 0xFFF
            except TypeError:
                pass
            else:
                print(f"{symbol}={hex(libc_offset)}")

    print(utils.make_bright("</dump>"))
Beispiel #6
0
def identify(libc_filepath):
    print(utils.make_bright("<identify>"))

    matches = []
    with sqlite3.connect(utils.get_libcs_db_filepath()) as conn:
        conn.row_factory = sqlite3.Row
        matches = [
            dict(libc)
            for libc in conn.execute(
                "SELECT * FROM libcs where buildID=?",
                (utils.extract_buildID(libc_filepath),),
            )
        ]

    for libc in matches:
        utils.dump(libc)

    print(utils.make_bright("</identify>"))
    return matches
Beispiel #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>"))
Beispiel #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>"))