示例#1
0
def patch_sections(path_in, path_out):
    elf = lief.parse(path_in)
    assert len(elf.sections) == 0, "Expected an executable without sections"
    sections = []
    sections.append(create_section_null())
    sections.append(create_section_interp(elf))
    sections.append(create_section_hash(elf))
    sections.append(create_section_dynsym(elf))
    sections.append(create_section_dynstr(elf))
    sections.append(create_section_text(elf))
    sections.append(create_section_dynamic(elf))
    sections.append(create_section_shstrtab(sections))

    e_shentsize = 0x40
    e_shoff = os.path.getsize(path_in)
    e_shnum = len(sections)
    e_shstrndx = INDEX_SHSTRTAB

    with open(path_out, 'wb') as f:
        with open(path_in, 'rb') as binary:
            f.write(binary.read())
        patch_i64(f, 0x28, e_shoff)
        patch_i08(f, 0x3A, e_shentsize)
        patch_i08(f, 0x3C, e_shnum)
        patch_i08(f, 0x3E, e_shstrndx)
        f.seek(0, io.SEEK_END)
        offset = e_shoff + (e_shnum * e_shentsize)
        for section in sections:
            if section.content:
                section.offset = offset
                section.size = len(section.content)
                offset += len(section.content)
            f.write(section.serialize())
        for section in sections:
            f.write(section.content)
示例#2
0
 def load_binary(self, filename):
     """Load in memory every opcode from an elf program."""
     import lief
     binary = lief.parse(filename)
     phdrs  = binary.segments
     for phdr in phdrs:
         size   = phdr.physical_size
         vaddr  = phdr.virtual_address
         self.Triton.setConcreteMemoryAreaValue(vaddr, phdr.content)
示例#3
0
def loadBinary(path):
    import lief
    binary = lief.parse(path)
    phdrs  = binary.segments
    for phdr in phdrs:
        size   = phdr.physical_size
        vaddr  = phdr.virtual_address
        print '[+] Loading 0x%06x - 0x%06x' %(vaddr, vaddr+size)
        Triton.setConcreteMemoryAreaValue(vaddr, phdr.content)
    return binary
示例#4
0
def make_binary_objects(filepath=None, pseudofile=None, filename=None, standalone=True, default_attributes_parameters={}):
    misp_file = FileObject(filepath=filepath, pseudofile=pseudofile, filename=filename,
                           standalone=standalone, default_attributes_parameters=default_attributes_parameters)
    if HAS_LIEF and filepath or (pseudofile and filename):
        try:
            if filepath:
                lief_parsed = lief.parse(filepath=filepath)
            else:
                if sys.version_info < (3, 0):
                    logger.critical('Pseudofile is not supported in python2. Just update.')
                    lief_parsed = None
                else:
                    lief_parsed = lief.parse(raw=pseudofile.getvalue(), name=filename)
            if isinstance(lief_parsed, lief.PE.Binary):
                return make_pe_objects(lief_parsed, misp_file, standalone, default_attributes_parameters)
            elif isinstance(lief_parsed, lief.ELF.Binary):
                return make_elf_objects(lief_parsed, misp_file, standalone, default_attributes_parameters)
            elif isinstance(lief_parsed, lief.MachO.Binary):
                return make_macho_objects(lief_parsed, misp_file, standalone, default_attributes_parameters)
        except lief.bad_format as e:
            logger.warning('Bad format: {}'.format(e))
        except lief.bad_file as e:
            logger.warning('Bad file: {}'.format(e))
        except lief.conversion_error as e:
            logger.warning('Conversion file: {}'.format(e))
        except lief.builder_error as e:
            logger.warning('Builder file: {}'.format(e))
        except lief.parser_error as e:
            logger.warning('Parser error: {}'.format(e))
        except lief.integrity_error as e:
            logger.warning('Integrity error: {}'.format(e))
        except lief.pe_error as e:
            logger.warning('PE error: {}'.format(e))
        except lief.type_error as e:
            logger.warning('Type error: {}'.format(e))
        except lief.exception as e:
            logger.warning('Lief exception: {}'.format(e))
        except FileTypeNotImplemented as e:
            logger.warning(e)
    if not HAS_LIEF:
        logger.warning('Please install lief, documentation here: https://github.com/lief-project/LIEF')
    return misp_file, None, None
示例#5
0
def ensure_binary(file):
    if not is_string(file):
        return file
    else:
        try:
            if not os.path.exists(file):
                return []
            return lief.parse(file)
        except:
            print('WARNING: liefldd: failed to ensure_binary({})'.format(file))
    return None
示例#6
0
def get_relocations(filename, arch='native'):
    if not os.path.exists(filename):
        return []
    try:
        binary = lief.parse(filename)
        res = []
        if len(binary.relocations):
            for r in binary.relocations:
                if r.has_symbol:
                    if r.symbol and r.symbol.name:
                        res.append(r.symbol.name)
            return res
    except:
        print('WARNING: liefldd: failed get_relocations({})'.format(filename))

    return []
示例#7
0
def nm(filename):
    """ Return symbols from *filename* binary """
    done = False
    try:
        binary = lief.parse(filename)  # Build an abstract binary
        symbols = binary.symbols

        if len(symbols) > 0:
            for symbol in symbols:
                print(dir(symbol))
                print(symbol)
                done = True
    except:
        pass
    if not done:
        print("No symbols found")
示例#8
0
    def test_simple(self):
        sample_path = get_sample('ELF/ELF64_x86-64_binary_ls.bin')
        output      = os.path.join(self.tmp_dir, "ls.segment")

        ls = lief.parse(sample_path)
        segment = Segment()
        segment.type      = lief.ELF.SEGMENT_TYPES.LOAD
        segment.flag      = lief.ELF.SEGMENT_FLAGS.PF_R | lief.ELF.SEGMENT_FLAGS.PF_W | lief.ELF.SEGMENT_FLAGS.PF_X
        segment.content   = STUB.segments[0].content # First LOAD segment which holds payload
        segment.alignment = 8
        segment           = ls.add_segment(segment, base=0xA00000, force_note=True)

        ls.header.entrypoint = segment.virtual_address + STUB.header.entrypoint
        ls.write(output)

        st = os.stat(output)
        os.chmod(output, st.st_mode | stat.S_IEXEC)

        p = Popen(output, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
        stdout, _ = p.communicate()
        self.logger.debug(stdout.decode("utf8"))
        self.assertIsNotNone(re.search(r'LIEF is Working', stdout.decode("utf8")))
示例#9
0
    def test_cmd(self):
        """
        Test on cmd.exe
        """
        binary: lief.PE.Binary = lief.parse(
            get_sample("PE/PE64_x86-64_binary_cmd.exe"))

        self.assertEqual(binary.has_delay_imports, True)
        self.assertEqual(len(binary.delay_imports), 4)

        self.assertEqual(len(binary.imported_functions), 247)
        self.assertEqual(len(binary.libraries), 8)

        shell32 = binary.get_delay_import("SHELL32.dll")
        self.assertEqual(shell32.name, "SHELL32.dll")
        self.assertEqual(shell32.attribute, 1)
        self.assertEqual(shell32.handle, 0x2e2e8)
        self.assertEqual(shell32.iat, 0x2e078)
        self.assertEqual(shell32.names_table, 0x2a5a0)
        self.assertEqual(shell32.biat, 0)
        self.assertEqual(shell32.uiat, 0)
        self.assertEqual(shell32.timestamp, 0)
        self.assertEqual(len(shell32.entries), 2)

        SHChangeNotify = shell32.entries[0]

        self.assertEqual(SHChangeNotify.name, "SHChangeNotify")
        self.assertEqual(SHChangeNotify.value, 0x0002e078)
        self.assertEqual(SHChangeNotify.iat_value, 0x0300905a4d)
        self.assertEqual(SHChangeNotify.data, 0x2a6ee)
        self.assertEqual(SHChangeNotify.hint, 0)

        ShellExecuteExW = shell32.entries[1]

        self.assertEqual(ShellExecuteExW.name, "ShellExecuteExW")
        self.assertEqual(ShellExecuteExW.value, 0x0002e080)
        self.assertEqual(ShellExecuteExW.iat_value, 0xffff00000004)
        self.assertEqual(ShellExecuteExW.data, 0x2a700)
        self.assertEqual(ShellExecuteExW.hint, 0)
示例#10
0
def get(malware, mydoc):
    h_main = mydoc.add_heading("DEBUG TIMESTAMPS", 2)
    h_main.alignment = 0

    paragraph_string = ""

    binary = lief.parse(malware)
    try:
        if binary.has_debug:
            dbg_time = datetime.datetime.fromtimestamp(binary.debug.timestamp)
            if dbg_time > datetime.datetime.now():
                paragraph_string = '[' + '\u2713' + "]" + " The age (%s) of the debug file is suspicious" % (
                    str(dbg_time))

            else:
                paragraph_string = "[X]: Not Suspicious"
        else:
            paragraph_string = "[X] PE has not debug object"
    except Exception as e:
        paragraph_string = "[X] Can't Determine"

    mydoc.add_paragraph(paragraph_string + "\n\n")
示例#11
0
def main():
    global INPUT
    global SERIAL
    global FINISH

    # Get a Triton context
    ctx = TritonContext()

    # Set the architecture
    ctx.setArchitecture(ARCH.ARM32)

    # Set optimization
    ctx.setMode(MODE.ALIGNED_MEMORY, True)
    ctx.setMode(MODE.ONLY_ON_SYMBOLIZED, True)

    # Parse the binary
    binary = lief.parse(TARGET)

    # Load the binary
    loadBinary(ctx, binary)

    # Perform our own relocations
    makeRelocation(ctx, binary)

    # First emulation
    run(ctx, binary)

    FINISH = False

    # Replace the input with the good serial to validate the chall
    INPUT = SERIAL

    # Second emulation
    print(
        '[+] Start a second emualtion with the good serial to validate the chall'
    )
    run(ctx, binary)

    return not VALID == True
示例#12
0
    async def scan(self, payload: Payload, request: Request) -> WorkerResponse:
        """
        Scan a payload using LIEF

        """
        filename = payload.results.payload_meta.extra_data.get(
            'filename', payload.results.payload_id)

        try:
            binary = lief.parse(raw=payload.content, name=filename)
        except lief.exception as err:
            raise StoqPluginException(f'Unable to parse payload: {err}')

        if binary is None:
            raise StoqPluginException('The file type isn\'t supported by LIEF')

        if self.abstract == True:
            results = lief.to_json_from_abstract(binary.abstract)
        else:
            results = lief.to_json(binary)

        return WorkerResponse(json.loads(results))
示例#13
0
def test_function_starts():
    dd = lief.parse(get_sample('MachO/MachO64_x86-64_binary_dd.bin'))

    functions = [
        0x100001581, 0x1000016cc, 0x1000017cc,
        0x1000019e3, 0x100001a03, 0x100001a1d,
        0x1000020ad, 0x1000022f6, 0x1000023ef,
        0x10000246b, 0x10000248c, 0x1000026da,
        0x100002754, 0x10000286b, 0x100002914,
        0x100002bd8, 0x100002be8, 0x100002c2b,
        0x100002c62, 0x100002d24, 0x100002d5a,
        0x100002d91, 0x100002dd5, 0x100002de6,
        0x100002dfc, 0x100002e40, 0x100002e51,
        0x100002e67, 0x100002f9e
    ]

    assert dd.function_starts.data_offset == 21168
    assert dd.function_starts.data_size ==   48
    text_segment = list(filter(lambda e : e.name == "__TEXT", dd.segments))[0]
    functions_dd = map(text_segment.virtual_address .__add__, dd.function_starts.functions)

    assert functions == list(functions_dd)
示例#14
0
    def section_append(self, seed=None):
        # append to a section (changes size and entropy)
        random.seed(seed)
        binary = lief.parse(self.bytez)
        for targeted_section in binary.sections:
            L = self.__random_length()
            available_size = targeted_section.size - len(
                targeted_section.content)
            # print("available_size:{}".format(available_size))
            if available_size == 0:
                continue

            if L > available_size:
                L = available_size

            upper = random.randrange(256)
            targeted_section.content = targeted_section.content + \
                                   [random.randint(0, upper) for _ in range(L)]
            break

        self.bytez = self.__binary_to_bytez(binary)
        return self.bytez
示例#15
0
    def test_all(self):
        original = lief.parse(
            get_sample('MachO/MachO64_x86-64_binary_all.bin'))
        _, output = tempfile.mkstemp(prefix="lief_all_")

        section = lief.MachO.Section("__shell", self.shellcode)
        section.alignment = 2
        section += lief.MachO.SECTION_FLAGS.SOME_INSTRUCTIONS
        section += lief.MachO.SECTION_FLAGS.PURE_INSTRUCTIONS

        section = original.add_section(section)

        __TEXT = original.get_segment("__TEXT")

        original.main_command.entrypoint = section.virtual_address - __TEXT.virtual_address

        original.write(output)

        if sys.platform.startswith("darwin"):
            stdout = run_program(output)
            self.logger.debug(stdout)
            self.assertIsNotNone(re.search(r'Hello World!', stdout))
示例#16
0
    def test_permutation(self):
        samples = [
            "ELF/ELF64_x86-64_binary_ls.bin",
            #"ELF/ELF64_x86-64_binary_gcc.bin",
            #"ELF/ELF64_x86-64_binary_openssl.bin",
        ]
        tmp_dir = tempfile.mkdtemp(suffix='_lief_test_permutation')
        for sample in samples:
            binary = lief.parse(get_sample(sample))
            dynamic_symbols = binary.dynamic_symbols

            gnu_hash_table = binary.gnu_hash

            idx = gnu_hash_table.symbol_index

            permutation = [i for i in range(1, len(dynamic_symbols))]
            random.shuffle(permutation)
            permutation = [0] + permutation
            binary.permute_dynamic_symbols(permutation)

            builder = lief.ELF.Builder(binary)
            builder.empties_gnuhash(True)
            builder.build()
            output = os.path.join(tmp_dir, "{}.permutated".format(binary.name))
            self.logger.debug("Output: {}".format(output))
            builder.write(output)

            if not sys.platform.startswith("linux"):
                return

            st = os.stat(output)
            os.chmod(output, st.st_mode | stat.S_IEXEC)

            p = Popen([output, "--help"],
                      stdout=subprocess.PIPE,
                      stderr=subprocess.STDOUT)
            stdout, _ = p.communicate()
            self.logger.debug(stdout.decode("utf8"))
            self.assertEqual(p.returncode, 0)
示例#17
0
文件: solve.py 项目: zumb08/Triton
def main():
    # Get a Triton context
    ctx = TritonContext()

    # Set the architecture
    ctx.setArchitecture(ARCH.X86_64)

    # Set optimization
    ctx.setMode(MODE.ALIGNED_MEMORY, True)
    ctx.setMode(MODE.ONLY_ON_SYMBOLIZED, True)

    # Parse the binary
    binary = lief.parse(TARGET)

    # Load the binary
    loadBinary(ctx, binary)

    # Perform our own relocations
    makeRelocation(ctx, binary)

    # Init and emulate
    return run(ctx, binary)
    def get_peinfo(
        filepath: str,
        save_to_json_path: Optional[str] = None
    ) -> Tuple[List[int], List[int]]:  # pragma: no cover
        """
        Given a PE file we extract out the section information to determine the slack regions in the file.
        We return two lists 1) with the start location of the slack regions and 2) with the size of the slack region.
        We are using the lief library (https://github.com/lief-project/LIEF) to manipulate the PE file.

        :param filepath: Path to file we want to analyse with pedump and get the section information.
        :param save_to_json_path: (Optional) if we want to save the results of pedump to a json file, provide the path.
        :return start_of_slack: A list with the slack starts
        :return size_of_slack: A list with the slack start positions
        """
        import lief  # pylint: disable=C0415

        start_of_slack = []
        size_of_slack = []

        cleaned_dump = {}

        binary = lief.parse(filepath)  # pylint: disable=I1101
        for section in binary.sections:
            section_info = {}
            slack = section.sizeof_raw_data - section.virtual_size
            section_info["PointerToRawData"] = section.pointerto_raw_data
            section_info["VirtualAddress"] = section.virtual_size
            section_info["SizeOfRawData"] = section.sizeof_raw_data
            cleaned_dump[section.name] = section_info
            if slack > 0:
                size_of_slack.append(slack)
                start_of_slack.append(section.pointerto_raw_data +
                                      section.virtual_size)

        if save_to_json_path is not None:
            with open(save_to_json_path, "w") as outfile:
                json.dump(cleaned_dump, outfile, indent=4, sort_keys=True)

        return start_of_slack, size_of_slack
示例#19
0
def unpack_ea06(filename: str) -> Optional[str]:
    pe = lief.parse(filename)
    if not pe:
        log.error("Failed to parse the input file")
        return None

    if not pe.has_resources:
        log.error("The input file has no resources")
        return None

    script_resource = get_script_resource(pe)
    if script_resource is None or not script_resource.childs:
        log.error("Couldn't find the script resource")
        return None

    script_data = list(script_resource.childs)[0].content
    parsed_data = parse_all(bytes(script_data)[0x18:], AutoItVersion.EA06)
    if not parsed_data:
        log.error("Couldn't decode the autoit script")
        return None

    return parsed_data
示例#20
0
    def _analyze_elf(self, file_object):
        elf_dict = {}
        try:
            parsed_binary = lief.parse(file_object.file_path)
            binary_json_dict = json.loads(
                lief.to_json_from_abstract(parsed_binary))
            if parsed_binary.exported_functions:
                binary_json_dict['exported_functions'] = normalize_lief_items(
                    parsed_binary.exported_functions)
            if parsed_binary.imported_functions:
                binary_json_dict['imported_functions'] = normalize_lief_items(
                    parsed_binary.imported_functions)
            if parsed_binary.libraries:
                binary_json_dict['libraries'] = normalize_lief_items(
                    parsed_binary.libraries)
        except (TypeError, lief.bad_file) as error:
            logging.error('Bad file for lief/elf analysis {}. {}'.format(
                file_object.uid, error))
            return elf_dict

        self.get_final_analysis_dict(binary_json_dict, elf_dict)
        return elf_dict, parsed_binary
示例#21
0
def loadBinary(ctx, path):
    global ENTRY_FUNC_ADDR

    logging.info(f"Loading the binary at path {path}..")
    import lief
    binary = lief.parse(path)
    phdrs = binary.segments
    for phdr in phdrs:
        size = phdr.physical_size
        vaddr = phdr.virtual_address
        logging.info('[+] Loading 0x%06x - 0x%06x' % (vaddr, vaddr + size))
        ctx.setConcreteMemoryAreaValue(vaddr, phdr.content)

    logging.info(f"Findind the exported function of interest {path}..")
    res = binary.exported_functions
    for function in res:
        if ENTRY_FUNC_NAME in function.name:
            ENTRY = function.address
            logging.info(f"Function of interest found at address {ENTRY}")
            break

    assert ENTRY != None, "Exported function wasn't found"
示例#22
0
def get(malware, csv):
    print((colors.WHITE +
           "\n------------------------------- {0:^13}{1:3}".format(
               "TLS", " -------------------------------") + colors.DEFAULT))
    binary = lief.parse(malware)
    if not binary.has_tls:
        print((colors.GREEN + "[X]" + colors.DEFAULT + " None"))
        csv.write("0,")

    else:
        csv.write("1,")
        table_entry_address = binary.tls.addressof_callbacks
        callback = binary.get_content_from_virtual_address(
            table_entry_address, 4)
        callback = '0x' + "".join(
            ["{0:02x}".format(x) for x in callback[::-1]])
        while int(callback, 16) != 0:
            print(('\t' + callback))
            table_entry_address += 4
            callback = binary.get_content_from_virtual_address(
                table_entry_address, 4)
            callback = '0x' + "".join(["{0:02x}".format(x) for x in callback])
示例#23
0
def patch_pie(binary, x86):
	sc=generate_seccomp(x86)
	b = lief.parse(binary)
	entrypoint = b.header.entrypoint
	print("entrypoint: 0x%x"%entrypoint)
	code = lief.ELF.Section()
	# code += lief.ELF.SECTION_FLAGS.EXECINSTR
	# code += lief.ELF.SECTION_FLAGS.WRITE
	code.content=[0x90]*0x10
	new_code = b.add(code)
	new_entrypoint = b.header.entrypoint
	print("entrypoint: 0x%x"%new_entrypoint)
	addr_new_code = new_code.virtual_address
	if x86:
		new_code.content = convert(sc, 0) + convert(shell_seccomp_x86_pie%( len(sc)/8, addr_new_code , new_entrypoint))
	else:
		new_code.content = convert(sc, 0) + convert(shell_seccomp_pie%( len(sc)/8, addr_new_code , new_entrypoint))
	b.header.entrypoint = addr_new_code + len(sc) 	
	print("new entrypoint: 0x%x" % (addr_new_code + len(sc) ))
	os.system("rm -f %s_patched"%binary)
	b.write("%s_patched"%binary)
	os.system("chmod +x %s_patched"%binary)
示例#24
0
def get_modifiable_range_in_section_table(fn,
                                          new_sections_cnt=0x10,
                                          write_path="tmp/tmp_exe.exe"):
    exe_info = lief.parse(fn)
    bytez = open(fn, 'rb').read()
    byte_ary = list(struct.unpack('B' * len(bytez), bytez))
    mrl = []  # 可改字节的位置范围存于此数组

    e_lfanew = byte_ary[0x3c:0x40]
    e_lfanew.reverse()

    optional_header_offset = reduce(lambda x, y: x * 256 + y, e_lfanew)
    section_table_offset = optional_header_offset + 4 + 20 + exe_info.header.sizeof_optional_header
    offset = section_table_offset

    # 创建新节
    for _ in range(new_sections_cnt):
        extra_s = lief.PE.Section()
        extra_s.name = ''.join(
            random.sample('zyxwvutsrqponmlkjihgfedcba9876543210_', 7))
        extra_s.virtual_size = 0x10
        extra_s.size = extra_s.virtual_size
        extra_s.characteristics = 0x40000040
        # extra_s.virtual_address = 0x1000
        extra_s.content = [0xff] * extra_s.size
        exe_info.add_section(extra_s)

        mrl.extend([
            (offset, offset + 7),  # 节名
            (offset + 8 + 8, offset + 8 + 8 + 3),  # SizeOfRawData 前3字节
            (offset + 36, offset + 36 + 4),  # Characteristics
        ])
        offset += 40

    bld = lief.PE.Builder(exe_info)
    bld.build()
    bld.write(write_path)

    return mrl
示例#25
0
def fixInFolder(folder, exitProcess, code):

    for file in os.listdir(folder):
        filename = os.fsdecode(file)

        if (os.path.isdir(folder + "\\" + filename)):
            fixInFolder(folder + "\\" + filename, exitProcess, code)

        if any(fixExtension in filename for fixExtension in fixExtensions):

            binary = lief.parse(folder + "\\" + filename)
            try:
                section = binary.get_section("ExitMe")
                print("[*] Fixing: " + filename)
                section.content = code

                builder = lief.PE.Builder(binary)
                builder.build()
                builder.write(folder + "\\" + filename)

            except:
                continue
示例#26
0
def gen_byteentropy(mypath):
    lief_binary = lief.parse(mypath)    
    bytez = bytearray(open(mypath, 'rb').read())
    name = 'byteentropy'
    dim = 256
    window = 2048
    step = 1024
    def _entropy_bin_counts(block):
        # coarse histogram, 16 bytes per bin
        c = np.bincount(block >> 4, minlength=16)  # 16-bin histogram
        p = c.astype(np.float32) / window
        wh = np.where(c)[0]
        H = np.sum(-p[wh] * np.log2(
            p[wh])) * 2  # * x2 b.c. we reduced information by half: 256 bins (8 bits) to 16 bins (4 bits)

        Hbin = int(H * 2)  # up to 16 bins (max entropy is 8 bits)
        if Hbin == 16:  # handle entropy = 8.0 bits
            Hbin = 15

        return Hbin, c

    output = np.zeros((16, 16), dtype=np.int)
    a = np.frombuffer(bytez, dtype=np.uint8)
    if a.shape[0] < window:
        Hbin, c = _entropy_bin_counts(a)
        output[Hbin, :] += c
    else:
        # strided trick from here: http://www.rigtorp.se/2011/01/01/rolling-statistics-numpy.html
        shape = a.shape[:-1] + (a.shape[-1] - window + 1, window)
        strides = a.strides + (a.strides[-1],)
        blocks = np.lib.stride_tricks.as_strided(a, shape=shape, strides=strides)[::step, :]

        # from the blocks, compute histogram
        for block in blocks:
            Hbin, c = _entropy_bin_counts(block)
            output[Hbin, :] += c

    return output.flatten().tolist()
def modify_bin(in_bin):
    bin = lief.parse(in_bin)
    in_fd = open(in_bin, 'rb')
    out_data = in_fd.read()

    # get 'note.ABI-tag' section
    if bin.has_section(".note.ABI-tag"):
        nat_sec = bin.get_section(".note.ABI-tag")
        nat_size = nat_sec.size

        new_seg_idx = -1
        cb_phoff = bin.header.program_header_offset
        cb_phentsize = bin.header.program_header_size
        for seg in bin.segments:
            new_seg_idx += 1
            if seg.type == lief.ELF.SEGMENT_TYPES.NOTE:
                print "Found the 'NOTE' segment"
                seg_offset = cb_phoff + cb_phentsize * new_seg_idx
                # changing file size
                w_off = seg_offset + p_filesz_off
                w_value = pack('l', nat_size)
                out_data = memWrite(out_data, w_off, 8, w_value)
                print hex(w_off), hex(nat_size)

                # changing mem size
                w_off = seg_offset + p_memsz_off
                w_value = pack('l', nat_size)
                out_data = memWrite(out_data, w_off, 8, w_value)
                print hex(w_off), hex(nat_size)



    else:
        print "There is no section with name '.note.ABI-tag'!"
        exit(1)


    return out_data
示例#28
0
    def change_interpreter(self, target):
        if not os.path.isfile(target):
            return

        name = os.path.basename(target)
        target = lief.parse(target)
        new_interpreter = os.path.join(self.tmp_dir,
                                       os.path.basename(target.interpreter))
        if not os.path.islink(new_interpreter):
            os.symlink(target.interpreter, new_interpreter)
        target.interpreter = new_interpreter
        output = os.path.join(self.tmp_dir, "{}.interpreter".format(name))
        target.write(output)

        st = os.stat(output)
        os.chmod(output, st.st_mode | stat.S_IEXEC)

        p = Popen(output, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
        stdout, _ = p.communicate()

        self.logger.debug(stdout.decode("utf8"))
        self.assertNotEqual(p.returncode, -signal.SIGSEGV,
                            "{} segfault!!!!".format(name))
示例#29
0
    def test_all(self):
        sample_path = get_sample('ELF/ELF64_x86-64_binary_all.bin')
        output      = os.path.join(self.tmp_dir, "all.relocation")

        target = lief.parse(sample_path)

        relocation = lief.ELF.Relocation(0x201028, type=lief.ELF.RELOCATION_X86_64.JUMP_SLOT, is_rela=True)

        symbol = lief.ELF.Symbol()
        symbol.name = "printf123"

        relocation.symbol = symbol
        target.add_pltgot_relocation(relocation)

        target.write(output)

        st = os.stat(output)
        os.chmod(output, st.st_mode | stat.S_IEXEC)

        p = Popen([output], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
        stdout, _ = p.communicate()
        self.logger.debug(stdout.decode("utf8"))
        self.assertIsNotNone(re.search(r'Hello World: 1', stdout.decode("utf8")))
示例#30
0
def test_change_interpreter(tmp_path):
    TARGET = SAMPLE_DIR / "ELF" / "batch-x86-64" / "test.clang.gold.wronglinker.bin"
    tmp = pathlib.Path(tmp_path)
    out_path = tmp / TARGET.name

    elf: lief.ELF.Binary = lief.parse(TARGET.as_posix())
    fsize = TARGET.stat().st_size

    elf.interpreter = "/lib64/ld-linux-x86-64.so.2"

    elf.write(out_path.as_posix())

    print(f"File written in {out_path}")
    out_path.chmod(out_path.stat().st_mode | stat.S_IEXEC)
    delta_size = out_path.stat().st_size - fsize
    print(f"delta size: {convert_size(delta_size)}")

    env = os.environ
    with Popen(out_path.as_posix(), universal_newlines=True, env=env,
               stdout=subprocess.PIPE, stderr=subprocess.STDOUT) as proc:
        stdout = proc.stdout.read()
        proc.poll()
        assert normalize(OUTPUT) == normalize(stdout)
示例#31
0
def test_issue_exports():
    pe: lief.PE.Binary = lief.parse(get_sample("PE/24e3ea78835748c9995e0d0c64f4f6bd3a0ca1b495b61a601703eb19b8c27f95.pe"))
    exports = pe.get_export()

    assert exports.name == "Uniscribe.dll"
    assert exports.export_flags == 0
    assert exports.timestamp == 1446632214
    assert exports.major_version == 0
    assert exports.minor_version == 0
    assert exports.ordinal_base == 1
    assert len(exports.entries) == 7

    assert exports.entries[0].name == "GetModuleFileNameDll"
    assert exports.entries[0].ordinal == 1
    assert exports.entries[0].address == 0x15bd0
    assert not exports.entries[0].is_extern
    assert exports.entries[0].function_rva == 0x15bd0

    assert exports.entries[6].name == "ncProxyXll"
    assert exports.entries[6].ordinal == 7
    assert exports.entries[6].address == 0x203a0
    assert not exports.entries[6].is_extern
    assert exports.entries[6].function_rva == 0x203a0
示例#32
0
def _inspect_linkages_this(filename, sysroot='', arch='native'):
    '''

    :param filename:
    :param sysroot:
    :param arch:
    :return:
    '''

    if not os.path.exists(filename):
        return None, [], []
    sysroot = _trim_sysroot(sysroot)
    try:
        binary = lief.parse(filename)
        # Future lief has this:
        # json_data = json.loads(lief.to_json_from_abstract(binary))
        json_data = json.loads(lief.to_json(binary))
        if json_data:
            return filename, json_data['imported_libraries'], json_data['imported_libraries']
    except:
        print('WARNING: liefldd: failed _inspect_linkages_this({})'.format(filename))

    return None, [], []
示例#33
0
    def test_remove_section(self):
        path = get_sample('PE/PE64_x86-64_remove_section.exe')
        sample = lief.parse(path)

        output = os.path.join(self.tmp_dir, "section_removed.exe")

        sample.remove_section("lief")
        sample.write(output)

        st = os.stat(output)
        os.chmod(output, st.st_mode | stat.S_IEXEC)

        if sys.platform.startswith("win"):
            subprocess_flags = 0x8000000  # win32con.CREATE_NO_WINDOW?
            p = Popen([output],
                      shell=True,
                      stdout=subprocess.PIPE,
                      stderr=subprocess.STDOUT,
                      creationflags=subprocess_flags)

            stdout, _ = p.communicate()
            self.logger.debug(stdout.decode("utf8"))
            self.assertIn("Hello World", stdout)
示例#34
0
def _inspect_linkages_this(filename, sysroot='', arch='native'):
    '''

    :param filename:
    :param sysroot:
    :param arch:
    :return:
    '''

    if not os.path.exists(filename):
        return None, [], []
    sysroot = _trim_sysroot(sysroot)
    try:
        binary = lief.parse(filename)
        # Future lief has this:
        # json_data = json.loads(lief.to_json_from_abstract(binary))
        json_data = json.loads(lief.to_json(binary))
        if json_data:
            return filename, json_data['imported_libraries'], json_data['imported_libraries']
    except:
        print('WARNING: liefldd: failed _inspect_linkages_this({})'.format(filename))

    return None, [], []
示例#35
0
def size_disk_memory(fileName):
    file = lief.parse(fileName)
    size_disk = 0
    size_mem = 0
    sections = [s.name for s in file.sections]
    for seg in file.segments:
        if (".text" in sections and ".code" in sections):
            if (file.get_section(".text") in seg
                    or file.get_section(".code") in seg):
                size_disk += seg.physical_size
                size_mem += seg.virtual_size
        elif (".text" in sections and not (".code" in sections)):
            if (file.get_section(".text") in seg):
                size_disk += seg.physical_size
                size_mem += seg.virtual_size
        elif (".code" in sections and not (".text" in sections)):
            if (file.get_section(".code") in seg):
                size_disk += seg.physical_size
                size_mem += seg.virtual_size
    if (size_mem > size_disk):
        return False
    else:
        return True
示例#36
0
    def VectorizeFromRawFile(self, FilePath):
        bytes = []
        with open(FilePath, "rb") as f:
            bytes = f.read()
        PE = lief.parse(FilePath)
        if PE == None:
            return None
        Entropy = EntropyFeatures()
        Headers = HeadersFeatures()
        Imports = ImportsFeatures()
        Sections = SectionsFeatures()
        DataDirectory = DataDirectoryFeatures()
        Extra = ExtraFeatures()

        X_file = np.concatenate([
            Entropy.VectorizeFromRaw(bytes),
            Headers.VectorizeFromRaw(bytes, PE),
            Imports.VectorizeFromRaw(PE),
            Sections.VectorizeFromRaw(PE),
            DataDirectory.VectorizeFromRaw(PE),
            Extra.VectorizeFromRaw(PE)
        ])
        return X_file
示例#37
0
def test_force_relocate(tmp_path):
    SKIP_LIST = {
        "test.clang.gold.wronglinker.bin", "test.android.bin", "test.android.aarch64.bin",
        "test.rust.bin", "test.go.pie.bin", "test.clang.lld.nolinker.bin", "test.dart.bin",
        "test.clang.lld.tbss.tdata.nopie.bin", "test.go.static.bin"
    }
    BINS = SAMPLE_DIR / "ELF" / "batch-x86-64"
    tmp = pathlib.Path(tmp_path)
    for file in BINS.rglob("*.bin"):
        if file.name in SKIP_LIST:
            continue
        print(f"Dealing with {file}")
        if not file.exists():
            print(f"{file} does not exist. Skipping ...", file=sys.stderr)
            continue
        elf: lief.ELF.Binary = lief.parse(file.as_posix())
        fsize = file.stat().st_size

        builder = lief.ELF.Builder(elf)
        builder.config.force_relocate = True
        builder.build()

        out_path = tmp / file.name

        print(f"File written in {out_path}")
        builder.write(out_path.as_posix())

        out_path.chmod(out_path.stat().st_mode | stat.S_IEXEC)
        delta_size = out_path.stat().st_size - fsize
        print(f"delta size: {convert_size(delta_size)}")

        env = os.environ
        with Popen(out_path.as_posix(), universal_newlines=True, env=env,
                   stdout=subprocess.PIPE, stderr=subprocess.STDOUT) as proc:
            stdout = proc.stdout.read()
            proc.poll()
            assert normalize(OUTPUT) == normalize(stdout)
示例#38
0
    def test_oat_dex_files(self):
        WallpaperCropper2 = lief.parse(
            get_sample("OAT/OAT_064_AArch64_WallpaperCropper2.oat"))
        self.assertEqual(len(WallpaperCropper2.oat_dex_files), 3)

        # OAT Dex File 0
        oat_dex_file = WallpaperCropper2.oat_dex_files[0]

        self.assertEqual(
            oat_dex_file.location,
            "/system/priv-app/WallpaperCropper2/WallpaperCropper2.apk")
        self.assertEqual(oat_dex_file.checksum, 0xbb07e4e)
        self.assertEqual(oat_dex_file.dex_offset, 0x23a4)
        self.assertTrue(oat_dex_file.has_dex_file)

        # OAT Dex File 1
        oat_dex_file = WallpaperCropper2.oat_dex_files[1]

        self.assertEqual(
            oat_dex_file.location,
            "/system/priv-app/WallpaperCropper2/WallpaperCropper2.apk:classes2.dex"
        )
        self.assertEqual(oat_dex_file.checksum, 1150225935)
        self.assertEqual(oat_dex_file.dex_offset, 340324)
        self.assertTrue(oat_dex_file.has_dex_file)

        # OAT Dex File 2
        oat_dex_file = WallpaperCropper2.oat_dex_files[2]

        self.assertEqual(
            oat_dex_file.location,
            "/system/priv-app/WallpaperCropper2/WallpaperCropper2.apk:classes3.dex"
        )
        self.assertEqual(oat_dex_file.checksum, 459332982)
        self.assertEqual(oat_dex_file.dex_offset, 1617040)
        self.assertTrue(oat_dex_file.has_dex_file)
示例#39
0
def get_static_lib_exports(file):
    # file = '/Users/rdonnelly/conda/main-augmented-tmp/osx-64_14354bd0cd1882bc620336d9a69ae5b9/lib/python2.7/config/libpython2.7.a'
    # References:
    # https://github.com/bminor/binutils-gdb/tree/master/bfd/archive.c
    # https://en.wikipedia.org/wiki/Ar_(Unix)
    # https://web.archive.org/web/20100314154747/http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx
    def _parse_ar_hdr(content, index):
        '''
        0   16  File identifier                 ASCII
        16  12 	File modification timestamp     Decimal
        28  6   Owner ID                        Decimal
        34  6   Group ID                        Decimal
        40  8   File mode                       Octal
        48  10  File size in bytes              Decimal
        58  2   Ending characters               0x60 0x0A
        '''
        header_fmt = '<16s 12s 6s 6s 8s 10s 2s'
        header_sz = struct.calcsize(header_fmt)

        name, modified, owner, group, mode, size, ending = \
            struct.unpack(header_fmt, content[index:index + header_sz])
        try:
            size = int(size)
        except:
            print('ERROR: {} has non-integral size of {}'.format(name, size))
            return index, '', 0, 0, 'INVALID'
        name_len = 0  # File data in BSD format archives begin with a name of this length.
        if name.startswith(b'#1/'):
            typ = 'BSD'
            name_len = int(name[3:])
            name, = struct.unpack('<' + str(name_len) + 's', content[index + header_sz:index + header_sz + name_len])
            if b'\x00' in name:
                name = name[:name.find(b'\x00')]
        elif name.startswith(b'//'):
            typ = 'GNU_TABLE'
        elif name.strip() == b'/':
            typ = 'GNU_SYMBOLS'
        elif name.startswith(b'/'):
            typ = 'GNU'
        else:
            typ = 'NORMAL'
        if b'/' in name:
            name = name[:name.find(b'/')]
        # if debug_static_archives: print("index={}, name={}, ending={}, size={}, type={}".format(index, name, ending, size, typ))
        index += header_sz + name_len
        return index, name, name_len, size, typ

    results = []
    signature, len_signature = _get_archive_signature(file)
    if signature != b'!<arch>\n':
        print("ERROR: {} is not an archive".format(file))
        return results
    with open(file, 'rb') as f:
        if debug_static_archives:
            print("Archive file {}".format(file))
        index = 0
        content = f.read()
        index += len_signature
        obj_starts = set()
        obj_ends = set()
        functions = []
        if index & 1:
            index += 1
        if debug_static_archives:
            print("ar_hdr index = {}".format(hex(index)))
        index, name, name_len, size, typ = _parse_ar_hdr(content, index)
        if typ == 'GNU_SYMBOLS':
            # Reference:
            # https://web.archive.org/web/20070924090618/http://www.microsoft.com/msj/0498/hood0498.aspx
            nsymbols, = struct.unpack('>I', content[index:index + 4])
            # Reference:
            # https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_image_file_header
            offsets = []
            for i in range(nsymbols):
                offset, = struct.unpack('>I', content[index + 4 + i * 4:index + 4 + (i + 1) * 4])
                offsets.append(offset)
            syms = [symname.decode('utf-8')
                    for symname in content[index + 4 + (nsymbols * 4):index + size].split(b'\x00')[:nsymbols]]
            for i in range(nsymbols):
                index2, name, name_len, size, typ = _parse_ar_hdr(content, offsets[i])
                obj_starts.add(index2)
                obj_ends.add(offsets[i])
                if debug_static_archives:
                    print("symname {}, offset {}, name {}, elf? {}".format(syms[i], offsets[i], name, content[index2:index2 + 4]))
        elif name.startswith(b'__.SYMDEF'):
            # Reference:
            # http://www.manpagez.com/man/5/ranlib/
            # https://opensource.apple.com/source/cctools/cctools-921/misc/libtool.c.auto.html
            # https://opensource.apple.com/source/cctools/cctools-921/misc/nm.c.auto.html
            # https://opensource.apple.com/source/cctools/cctools-921/libstuff/writeout.c
            # https://developer.apple.com/documentation/kernel/nlist_64/1583944-n_type?language=objc
            if b'64' in name:
                # 2 uint64_t, a string table index and an offset
                ranlib_struct_field_fmt = 'Q'
                toc_integers_fmt = 'Q'
            else:
                # 2 uint32_t, a string table index and an offset
                ranlib_struct_field_fmt = 'I'
                toc_integers_fmt = 'I'
            ranlib_struct_sz = struct.calcsize(ranlib_struct_field_fmt) * 2
            toc_integers_sz = struct.calcsize(toc_integers_fmt)
            size_ranlib_structs, = struct.unpack('<' + toc_integers_fmt, content[index:index + toc_integers_sz])
            # Each of the ranlib structures consists of a zero based offset into the next
            # section (a string table of symbols) and an offset from the beginning of
            # the archive to the start of the archive file which defines the symbol
            nsymbols = size_ranlib_structs // 8
            size_string_table, = struct.unpack('<' + toc_integers_fmt,
                                               content[index + toc_integers_sz + (nsymbols * ranlib_struct_sz):index + 4 + 4 + (nsymbols * ranlib_struct_sz)])
            ranlib_structs = []
            ranlib_index = index + (toc_integers_sz * 2)
            for i in range(nsymbols):
                ran_off, ran_strx = struct.unpack('<' + ranlib_struct_field_fmt + ranlib_struct_field_fmt,
                                                    content[ranlib_index + (i * ranlib_struct_sz):ranlib_index + ((i + 1) * ranlib_struct_sz)])
                ranlib_structs.append((ran_strx, ran_off))
            if debug_static_archives > 1:
                print("string_table: start: {} end: {}".format(hex(ranlib_index + (nsymbols * ranlib_struct_sz)),
                                                               hex(ranlib_index + (nsymbols * ranlib_struct_sz) + size_string_table)))
            string_table = content[ranlib_index + (nsymbols * ranlib_struct_sz):ranlib_index + (nsymbols * ranlib_struct_sz) + size_string_table]
            string_table = string_table.decode('utf-8', errors='ignore')
            syms = []
            for i in range(nsymbols):
                ranlib_struct = ranlib_structs[i]
                strx, off = ranlib_struct
                sym = string_table[strx:strx + string_table[strx:].find('\x00')]
                syms.append(sym)
                if debug_static_archives > 1:
                    print("{} :: strx={}, off={}".format(syms[i], hex(strx), hex(off)))
                # This is probably a different structure altogether! Something symobol-y not file-y.
                off2, name, name_len, size, typ = _parse_ar_hdr(content, off)
                obj_starts.add(off2)
                obj_ends.add(off)
        obj_ends.add(len(content))
        obj_starts = sorted(list(obj_starts))
        obj_ends = sorted(list(obj_ends))[1:]
        if debug_static_archives > 1:
            print('obj_starts: {}'.format(" ".join('0x{:05x}'.format(o) for o in obj_starts)))
        if debug_static_archives > 1:
            print('  obj_ends: {}'.format(" ".join('0x{:05x}'.format(o) for o in obj_ends)))
        for obj_start, obj_end in zip(obj_starts, obj_ends):
            IMAGE_FILE_MACHINE_I386 = 0x014c
            IMAGE_FILE_MACHINE_AMD64 = 0x8664
            MACHINE_TYPE, = struct.unpack('<H', content[obj_start:obj_start + 2])
            if debug_static_archives > 0:
                print(hex(obj_start), hex(obj_end), obj_end - obj_start)
            if MACHINE_TYPE in (IMAGE_FILE_MACHINE_I386, IMAGE_FILE_MACHINE_AMD64):
                # 'This file is not a PE binary' (yeah, fair enough, it's a COFF file).
                # Reported at https://github.com/lief-project/LIEF/issues/233#issuecomment-452580391
                # obj = lief.PE.parse(raw=content[obj_start:obj_end-1])
                obj = None
            elif MACHINE_TYPE == 0xfacf:
                obj = lief.parse(raw=content[obj_start:obj_end])
                # filename = '/Users/rdonnelly/conda/conda-build/macOS-libpython2.7.a/getbuildinfo.o'
                # obj = lief.parse(filename)
                # syms_a = get_symbols(obj, defined=True, undefined=False)
                # obj = lief.parse(filename)
                # syms_b = get_symbols(obj, defined=True, undefined=False)
                # print(syms_b)
            else:
                obj = lief.ELF.parse(raw=content[obj_start:obj_end])
            if not obj:
                # Cannot do much here except return the index.
                return syms, [[0, 0] for sym in syms], syms, [[0, 0] for sym in syms]
            # You can unpack an archive via:
            # /mingw64/bin/ar.exe xv /mingw64/lib/libz.dll.a
            # obj = lief.PE.parse('C:\\Users\\rdonnelly\\conda\\conda-build\\mingw-w64-libz.dll.a\\d000103.o')
            # for sym in obj.symbols:
            #     # Irrespective of whether you pass -g or not to nm, it still
            #     # lists symbols that are either exported or is_static.
            #     if sym.is_function and (sym.exported or sym.is_static):
            #         functions.append(sym.name)
            functions.extend(get_symbols(obj, defined=True, undefined=False))
        return functions, [[0, 0] for sym in functions], functions, [[0, 0] for sym in functions]
#!/usr/bin/env python3

import lief

libnative = lief.parse("./libnative-lib.so")

# Change Symbol version (All to global)
for s in libnative.dynamic_symbols:
    if s.has_version:
        if s.symbol_version.value > 1:
            s.symbol_version.value = 1

# 1. Remove liblog (Android specific)
liblog     = libnative.get_library("liblog.so")
liblog.tag = lief.ELF.DYNAMIC_TAGS.NULL

# 2. Change library names ACCORDING TO YOUR LINUX VERSION USED (here ArchLinux & Debian)
libnative.get_library("libc.so").name = "libc.so.6"
libnative.get_library("libm.so").name = "libm.so.6"

libnative.write("./libnative-lib.so")
示例#41
0
    0xfd527,
    0xfd527,
    0xfd527,
    0xfd527,
    0xfd527,
    0xfd527,
    0xfd527,
    0x1164b0,
    0x800b0,
    0x2e786,
    0x809c0,
]
# import offset within libc
exit_offset = 0x43120

binary = lief.parse('./dummy')

# Remove executable bits
for seg in binary.segments:
    if seg.flags & lief.ELF.SEGMENT_FLAGS.X:
        seg.flags = lief.ELF.SEGMENT_FLAGS.R

# Modify preinit_array relocations
idx = 0
for reloc in binary.relocations:
    if reloc.symbol.name != 'exit':
        continue
    reloc.addend = chain[idx] - exit_offset
    idx += 1
    if idx == len(chain):
        break
示例#42
0
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# __author__ = "nonick"

from pwn import *
import sys
import lief

argc = len(sys.argv)
if argc < 2:
    log.error('No input!')
try:
    e = lief.parse(sys.argv[1])
except:
    log.error('Fail to load elf!')
is64 = 0
if e.header.machine_type == lief.ELF.ARCH.x86_64:
    is64 = 1
if is64:
    context.arch = 'amd64'
    asmcode = '''
        push rbx
        push rcx
        push rdx
        push rsi
        push rdi
        push rbp
        push r8
        push r9
        push r10
        push r11
示例#43
0
 def analyze(filepath):
     res = lief.parse(filepath)
     json_res = lief.to_json(res)
     return json.loads(json_res)
示例#44
0
#! /usr/bin/env python3

import argparse
import zlib
import lief
import struct


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("elf")
    parser.add_argument("-s", "--symbol-name", required=True)
    parser.add_argument("-b", "--bin-file", required=True)
    args = parser.parse_args()

    elf = lief.parse(args.elf)

    def symbol_by_name(name):
        return next(filter(lambda x: x.name == name,
                           elf.static_symbols))

    crc_symbol = symbol_by_name(args.symbol_name)

    with open(args.bin_file, "rb") as f:
        data = f.read()
        crc_value = zlib.crc32(data)

    assert len(data) % 4 == 0, "Code length not aligned to 4 byte boundary"

    elf.patch_address(crc_symbol.value,
                      struct.pack("<II", crc_value, len(data)))
示例#45
0
def inspect_linkages_lief(filename, resolve_filenames=True, recurse=True,
                          sysroot='', envroot='', arch='native'):
    # Already seen is partly about implementing single SONAME
    # rules and its appropriateness on macOS is TBD!
    already_seen = set()
    exedir = os.path.dirname(filename)
    binary = lief.parse(filename)
    todo = [[filename, binary]]

    default_paths = []
    if binary.format == lief.EXE_FORMATS.ELF:
        if binary.type == lief.ELF.ELF_CLASS.CLASS64:
            default_paths = ['$SYSROOT/lib64', '$SYSROOT/usr/lib64', '$SYSROOT/lib', '$SYSROOT/usr/lib']
        else:
            default_paths = ['$SYSROOT/lib', '$SYSROOT/usr/lib']
    elif binary.format == lief.EXE_FORMATS.MACHO:
        default_paths = ['$SYSROOT/usr/lib']
    elif binary.format == lief.EXE_FORMATS.PE:
        # We do not include C:\Windows nor C:\Windows\System32 in this list. They are added in
        # get_rpaths() instead since we need to carefully control the order.
        default_paths = ['$SYSROOT/System32/Wbem', '$SYSROOT/System32/WindowsPowerShell/v1.0']
    results = set()
    rpaths_by_binary = dict()
    parents_by_filename = dict({filename: None})
    while todo:
        for element in todo:
            todo.pop(0)
            filename2 = element[0]
            binary = element[1]
            uniqueness_key = get_uniqueness_key(binary)
            if uniqueness_key not in already_seen:
                parent_exe_dirname = None
                if binary.format == lief.EXE_FORMATS.PE:
                    tmp_filename = filename2
                    while tmp_filename:
                        if not parent_exe_dirname and codefile_type(tmp_filename) == 'EXEfile':
                            parent_exe_dirname = os.path.dirname(tmp_filename)
                        tmp_filename = parents_by_filename[tmp_filename]
                else:
                    parent_exe_dirname = exedir
                rpaths_by_binary[filename2] = get_rpaths(binary,
                                                         parent_exe_dirname,
                                                         envroot.replace(os.sep, '/'),
                                                         sysroot)
                tmp_filename = filename2
                rpaths_transitive = []
                if binary.format == lief.EXE_FORMATS.PE:
                    rpaths_transitive = rpaths_by_binary[tmp_filename]
                else:
                    while tmp_filename:
                        rpaths_transitive[:0] = rpaths_by_binary[tmp_filename]
                        tmp_filename = parents_by_filename[tmp_filename]
                libraries = get_libraries(binary)
                if filename2 in libraries:  # Happens on macOS, leading to cycles.
                    libraries.remove(filename2)
                # RPATH is implicit everywhere except macOS, make it explicit to simplify things.
                these_orig = [('$RPATH/' + lib if not lib.startswith('/') and not lib.startswith('$') and  # noqa
                               binary.format != lief.EXE_FORMATS.MACHO else lib)
                              for lib in libraries]
                for orig in these_orig:
                    resolved = _get_resolved_location(binary,
                                                      orig,
                                                      exedir,
                                                      exedir,
                                                      rpaths_transitive=rpaths_transitive,
                                                      default_paths=default_paths,
                                                      sysroot=sysroot)
                    if resolve_filenames:
                        results.add(resolved[0])
                        parents_by_filename[resolved[0]] = filename2
                    else:
                        results.add(orig)
                    if recurse:
                        if os.path.exists(resolved[0]):
                            todo.append([resolved[0], lief.parse(resolved[0])])
                already_seen.add(get_uniqueness_key(binary))
    return results