示例#1
0
def getRuntimeTraceOfLoadedFiles(logger, path):
    """ Returns the files loaded when executing a binary. """

    # This will make a crazy amount of work,
    # pylint: disable=I0021,too-many-branches,too-many-locals,too-many-statements

    if not os.path.exists(path):
        # TODO: Have a logger package passed.
        logger.sysexit("Error, cannot find %r (%r)." % (path, os.path.abspath(path)))

    result = []

    if os.name == "posix":
        if sys.platform == "darwin" or sys.platform.startswith("freebsd"):
            if not isExecutableCommand("dtruss"):
                sys.exit(
                    """\
Error, needs 'dtruss' on your system to scan used libraries."""
                )

            if not isExecutableCommand("sudo"):
                sys.exit(
                    """\
Error, needs 'sudo' on your system to scan used libraries."""
                )

            args = ("sudo", "dtruss", "-t", "open", path)
        else:
            if not isExecutableCommand("strace"):
                sys.exit(
                    """\
Error, needs 'strace' on your system to scan used libraries."""
                )

            args = (
                "strace",
                "-e",
                "file",
                "-s4096",  # Some paths are truncated in output otherwise
                os.path.abspath(path),
            )

        # Ensure executable is not polluted with third party stuff,
        # tests may fail otherwise due to unexpected libs being loaded
        with withEnvironmentVarOverriden("LD_PRELOAD", None):
            tracing_command = args[0] if args[0] != "sudo" else args[1]

            process = subprocess.Popen(
                args=args, stdout=subprocess.PIPE, stderr=subprocess.PIPE
            )

            _stdout_strace, stderr_strace = process.communicate()
            exit_strace = process.returncode

            if exit_strace != 0:
                if str is not bytes:
                    stderr_strace = stderr_strace.decode("utf8")

                logger.warning(stderr_strace)
                logger.sysexit("Failed to run %r." % tracing_command)

            with open(path + ".strace", "wb") as f:
                f.write(stderr_strace)

            for line in stderr_strace.split(b"\n"):
                if process.returncode != 0:
                    logger.my_print(line)

                if not line:
                    continue

                # Don't consider files not found. The "site" module checks lots
                # of things.
                if b"ENOENT" in line:
                    continue

                if line.startswith(b"stat(") and b"S_IFDIR" in line:
                    continue

                # Allow stats on the python binary, and stuff pointing to the
                # standard library, just not uses of it. It will search there
                # for stuff.
                if (
                    line.startswith(b"lstat(")
                    or line.startswith(b"stat(")
                    or line.startswith(b"readlink(")
                ):
                    filename = line[line.find(b"(") + 2 : line.find(b", ") - 1]

                    # At least Python3.7 considers the default Python3 path.
                    if filename == b"/usr/bin/python3":
                        continue

                    if filename in (
                        b"/usr/bin/python3." + version
                        for version in (b"5", b"6", b"7", b"8", b"9")
                    ):
                        continue

                    binary_path = _python_executable
                    if str is not bytes:
                        binary_path = binary_path.encode("utf-8")

                    found = False
                    while binary_path:
                        if filename == binary_path:
                            found = True
                            break

                        if binary_path == os.path.dirname(binary_path):
                            break

                        binary_path = os.path.dirname(binary_path)

                        if filename == os.path.join(
                            binary_path,
                            b"python"
                            + (
                                "%d%d" % (_python_version[0], _python_version[1])
                            ).encode("utf8"),
                        ):
                            found = True
                            continue

                    if found:
                        continue

                result.extend(
                    os.path.abspath(match)
                    for match in re.findall(b'"(.*?)(?:\\\\0)?"', line)
                )

            if sys.version.startswith("3"):
                result = [s.decode("utf-8") for s in result]
    elif os.name == "nt":
        command = (
            getDependsExePath(),
            "-c",  # Console mode
            "-ot%s" % path + ".depends",
            "-f1",
            "-pb",
            "-pa1",  # Turn on all profiling options.
            "-ps1",  # Simulate ShellExecute with app dirs in PATH.
            "-pp1",  # Do not long DllMain calls.
            "-po1",  # Log DllMain call for all other messages.
            "-ph1",  # Hook the process.
            "-pl1",  # Log LoadLibrary calls.
            "-pt1",  # Thread information.
            "-pe1",  # First chance exceptions.
            "-pg1",  # Log GetProcAddress calls.
            "-pf1",  # Use full paths.
            "-pc1",  # Profile child processes.
            path,
        )

        subprocess.call(command)

        inside = False
        for line in getFileContentByLine(path + ".depends"):
            if "| Module Dependency Tree |" in line:
                inside = True
                continue

            if not inside:
                continue

            if "| Module List |" in line:
                break

            if "]" not in line:
                continue

            # Skip missing DLLs, apparently not needed anyway.
            if "?" in line[: line.find("]")]:
                continue

            dll_filename = line[line.find("]") + 2 :].rstrip()
            dll_filename = os.path.normcase(dll_filename)

            assert os.path.isfile(dll_filename), repr(dll_filename)

            # The executable itself is of course exempted.
            if dll_filename == os.path.normcase(os.path.abspath(path)):
                continue

            result.append(dll_filename)

        os.unlink(path + ".depends")

    result = list(sorted(set(result)))

    return result
示例#2
0
def getRuntimeTraceOfLoadedFiles(path, trace_error=True):
    """ Returns the files loaded when executing a binary. """

    # This will make a crazy amount of work,
    # pylint: disable=I0021,too-many-branches,too-many-locals,too-many-statements

    result = []

    if os.name == "posix":
        if sys.platform == "darwin" or sys.platform.startswith("freebsd"):
            if not isExecutableCommand("dtruss"):
                sys.exit("""\
Error, needs 'dtruss' on your system to scan used libraries.""")

            if not isExecutableCommand("sudo"):
                sys.exit("""\
Error, needs 'sudo' on your system to scan used libraries.""")

            args = ("sudo", "dtruss", "-t", "open", path)
        else:
            if not isExecutableCommand("strace"):
                sys.exit("""\
Error, needs 'strace' on your system to scan used libraries.""")

            args = (
                "strace",
                "-e",
                "file",
                "-s4096",  # Some paths are truncated otherwise.
                path,
            )

        # Ensure executable is not polluted with third party stuff,
        # tests may fail otherwise due to unexpected libs being loaded
        with withEnvironmentVarOverriden("LD_PRELOAD", None):
            process = subprocess.Popen(args=args,
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.PIPE)

            _stdout_strace, stderr_strace = process.communicate()
            exit_strace = process.returncode

            if exit_strace != 0:
                if str is not bytes:
                    stderr_strace = stderr_strace.decode("utf8")

                my_print(stderr_strace, file=sys.stderr)
                sys.exit("Failed to run strace.")

            with open(path + ".strace", "wb") as f:
                f.write(stderr_strace)

            for line in stderr_strace.split(b"\n"):
                if process.returncode != 0 and trace_error:
                    my_print(line)

                if not line:
                    continue

                # Don't consider files not found. The "site" module checks lots
                # of things.
                if b"ENOENT" in line:
                    continue

                if line.startswith(b"stat(") and b"S_IFDIR" in line:
                    continue

                # Allow stats on the python binary, and stuff pointing to the
                # standard library, just not uses of it. It will search there
                # for stuff.
                if (line.startswith(b"lstat(") or line.startswith(b"stat(")
                        or line.startswith(b"readlink(")):
                    filename = line[line.find(b"(") + 2:line.find(b", ") - 1]

                    # At least Python3.7 considers the default Python3 path.
                    if filename == b"/usr/bin/python3":
                        continue

                    if filename in (b"/usr/bin/python3." + version
                                    for version in (b"5", b"6", b"7")):
                        continue

                    binary_path = _python_executable
                    if str is not bytes:
                        binary_path = binary_path.encode("utf-8")

                    found = False
                    while binary_path:
                        if filename == binary_path:
                            found = True
                            break

                        if binary_path == os.path.dirname(binary_path):
                            break

                        binary_path = os.path.dirname(binary_path)

                        if filename == os.path.join(
                                binary_path, b"python" +
                                _python_version[:3].encode("utf8")):
                            found = True
                            continue

                    if found:
                        continue

                result.extend(
                    os.path.abspath(match)
                    for match in re.findall(b'"(.*?)(?:\\\\0)?"', line))

            if sys.version.startswith("3"):
                result = [s.decode("utf-8") for s in result]
    elif os.name == "nt":
        subprocess.call((
            getDependsExePath(),
            "-c",
            "-ot%s" % path + ".depends",
            "-f1",
            "-pa1",
            "-ps1",
            "-pp0",
            "-pl1",
            path,
        ))

        inside = False
        for line in getFileContentByLine(path + ".depends"):
            if "| Module Dependency Tree |" in line:
                inside = True
                continue

            if not inside:
                continue

            if "| Module List |" in line:
                break

            if "]" not in line:
                continue

            # Skip missing DLLs, apparently not needed anyway.
            if "?" in line[:line.find("]")]:
                continue

            dll_filename = line[line.find("]") + 2:-1]
            assert os.path.isfile(dll_filename), dll_filename

            # The executable itself is of course exempted.
            if os.path.normcase(dll_filename) == os.path.normcase(
                    os.path.abspath(path)):
                continue

            dll_filename = os.path.normcase(dll_filename)

            result.append(dll_filename)

        os.unlink(path + ".depends")

    result = list(sorted(set(result)))

    return result