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)
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)
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
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
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
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 []
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")
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")))
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)
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")
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
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))
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)
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
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))
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)
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
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
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
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"
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])
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)
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
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
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
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))
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")))
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)
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
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, [], []
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)
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
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
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)
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)
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")
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
#!/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
def analyze(filepath): res = lief.parse(filepath) json_res = lief.to_json(res) return json.loads(json_res)
#! /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)))
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