class TestRemoveSection(TestCase): def setUp(self): self.logger = logging.getLogger(__name__) self.tmp_dir = tempfile.mkdtemp(suffix='_lief_test_section') self.logger.debug("temp dir: {}".format(self.tmp_dir)) @unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux") @unittest.skipUnless(has_recent_glibc(), "Need a recent GLIBC version") def test_simple(self): sample_path = get_sample('ELF/ELF64_x86-64_binary_ls.bin') output = os.path.join(self.tmp_dir, "ls.section") ls = lief.parse(sample_path) ls.remove_section(".text", clear=False) ls.write(output) 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.assertIsNotNone(re.search(r'GNU coreutils', stdout.decode("utf8"))) def tearDown(self): # Delete it if os.path.isdir(self.tmp_dir): shutil.rmtree(self.tmp_dir)
class TestGOTPatch(TestCase): def setUp(self): self.logger = logging.getLogger(__name__) self.tmp_dir = tempfile.mkdtemp(suffix='_lief_test_466') self.logger.debug("temp dir: {}".format(self.tmp_dir)) @unittest.skipUnless(is_linux() and is_x86_64(), "requires Linux x86-64") @unittest.skipUnless(has_recent_glibc(), "Need a recent GLIBC version") def test_freebl(self): libfreebl3_path = get_sample('ELF/ELF64_x86-64_library_libfreebl3.so') output_ls = os.path.join(self.tmp_dir, "ls.new") output_libfreebl3 = os.path.join(self.tmp_dir, "libfreebl3.so") libfreebl3 = lief.parse(libfreebl3_path) ls = lief.parse("/usr/bin/ls") if lief.ELF.DYNAMIC_TAGS.FLAGS_1 in ls and ls[ lief.ELF.DYNAMIC_TAGS.FLAGS_1].has( lief.ELF.DYNAMIC_FLAGS_1.PIE): ls[lief.ELF.DYNAMIC_TAGS.FLAGS_1].remove( lief.ELF.DYNAMIC_FLAGS_1.PIE) ls.add_library("libfreebl3.so") ls += lief.ELF.DynamicEntryRunPath("$ORIGIN") libfreebl3 += lief.ELF.DynamicEntryRunPath("$ORIGIN") ls.write(output_ls) libfreebl3.write(output_libfreebl3) st = os.stat(output_ls) os.chmod(output_ls, st.st_mode | stat.S_IEXEC) st = os.stat(output_libfreebl3) os.chmod(output_libfreebl3, st.st_mode | stat.S_IEXEC) p = Popen([output_ls, "--version"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdout, _ = p.communicate() self.logger.debug(stdout.decode("utf8")) self.assertIsNotNone( re.search(r'ls \(GNU coreutils\) ', stdout.decode("utf8"))) self.assertEqual(p.returncode, 0) def tearDown(self): # Delete it if os.path.isdir(self.tmp_dir): shutil.rmtree(self.tmp_dir)
class TestEmptyGNUHash(TestCase): SYMBOLS = { "myinstance": 0x1159, "myinit": 0x1175, "mycalc": 0x1199, "mydelete": 0x1214, } def setUp(self): self.logger = logging.getLogger(__name__) self.tmp_dir = tempfile.mkdtemp(suffix='_lief_test_empty_gnu_hash') self.logger.debug("temp dir: {}".format(self.tmp_dir)) @unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux") @unittest.skipUnless(has_recent_glibc(), "Need a recent GLIBC version") def test_export(self): target_path = get_sample('ELF/ELF64_x86-64_binary_empty-gnu-hash.bin') output = os.path.join(self.tmp_dir, "libnoempty.so") binary = lief.parse(target_path) binary[lief.ELF.DYNAMIC_TAGS.FLAGS_1].remove( lief.ELF.DYNAMIC_FLAGS_1.PIE) for name, addr in TestEmptyGNUHash.SYMBOLS.items(): binary.add_exported_function(addr, name) binary.write(output) st = os.stat(output) os.chmod(output, st.st_mode | stat.S_IEXEC) lib = ctypes.cdll.LoadLibrary(output) # Raise 'AttributeError' if not exported print(lib.myinstance) self.assertIsNotNone(lib.myinstance) def tearDown(self): # Delete it if os.path.isdir(self.tmp_dir): shutil.rmtree(self.tmp_dir)
class TestELF(TestCase): def setUp(self): self.logger = logging.getLogger(__name__) def test_rpath(self): etterlog = lief.parse(get_sample('ELF/ELF64_x86-64_binary_etterlog.bin')) dynamic_entries = etterlog.dynamic_entries rpath = [e for e in dynamic_entries if e.tag == lief.ELF.DYNAMIC_TAGS.RPATH] self.assertEqual(len(rpath), 1) rpath = rpath.pop() self.assertEqual(rpath.name, "/usr/lib") def test_runpath(self): etterlog = lief.parse(get_sample('ELF/ELF64_x86-64_binary_systemd-resolve.bin')) dynamic_entries = etterlog.dynamic_entries runpath = [e for e in dynamic_entries if e.tag == lief.ELF.DYNAMIC_TAGS.RUNPATH] self.assertEqual(len(runpath), 1) runpath = runpath.pop() self.assertEqual(runpath.name, "/usr/lib/systemd") def test_gnuhash(self): ls = lief.parse(get_sample('ELF/ELF64_x86-64_binary_ls.bin')) gnu_hash = ls.gnu_hash self.assertEqual(gnu_hash.nb_buckets, 33) self.assertEqual(gnu_hash.symbol_index, 109) self.assertEqual(gnu_hash.shift2, 7) bloom_filters = gnu_hash.bloom_filters self.assertEqual(len(bloom_filters), 2) self.assertIn(0x3FAE01120C48A1A6, bloom_filters) self.assertIn(0x900004A81310D428, bloom_filters) buckets = gnu_hash.buckets self.assertEqual(len(buckets), 33) buckets_test = [109, 110, 0, 0, 0, 0, 0, 111, 113, 114, 0, 0, 0, 115, 0, 116, 0, 0, 117, 118, 119, 0, 120, 0, 0, 121, 123, 124, 126, 128, 129, 130, 0] self.assertEqual(buckets_test, buckets) hash_values = gnu_hash.hash_values hash_values_test = [0x60E0C78D, 0xF54162E5, 0x7FFD8E4E, 0x1C8BF239, 0xEEFD3EB, 0x1C8C1D29, 0x1C5871D9, 0x5B7F3E03, 0x759A6A7F, 0xEF18DB9, 0xBA53E4D, 0x9789A097, 0x9E7650BC, 0xD39AD3D, 0x12F7C433, 0xEB01FAB6, 0xECD54543, 0xAD3C9892, 0x72632CCF, 0x12F7A2B3, 0x7C92E3BB, 0x7C96F087] self.assertEqual(hash_values, hash_values_test) #for s in list(ls.dynamic_symbols)[gnu_hash.symbol_index:]: # print(gnu_hash.check(s.name), s.name) self.assertTrue(all(gnu_hash.check(x.name) for x in list(ls.dynamic_symbols)[gnu_hash.symbol_index:])) self.assertFalse(gnu_hash.check("foofdsfdsfds")) self.assertFalse(gnu_hash.check("fazertrvkdfsrezklqpfjeopqdi")) @unittest.skipUnless(is_linux() and is_x86_64(), "requires Linux x86-64") @unittest.skipUnless(has_recent_glibc(), "Need a recent GLIBC version") 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 test_notes(self): systemd_resolve = lief.parse(get_sample('ELF/ELF64_x86-64_binary_systemd-resolve.bin')) notes = systemd_resolve.notes self.assertEqual(len(notes), 3) n1 = notes[0] n2 = notes[1] n3 = notes[2] self.assertEqual(n1.name, "GNU") self.assertEqual(n2.name, "GNU") self.assertEqual(n3.name, "GNU") self.assertEqual(n1.type, lief.ELF.NOTE_TYPES.ABI_TAG) self.assertEqual(n2.type, lief.ELF.NOTE_TYPES.BUILD_ID) self.assertEqual(n3.type, lief.ELF.NOTE_TYPES.GOLD_VERSION) self.assertEqual(n1.details.abi, lief.ELF.NOTE_ABIS.LINUX) self.assertEqual(n1.details.version, [2, 6, 32]) self.assertEqual(list(n2.description), [ 0x7e, 0x68, 0x6c, 0x7d, 0x79, 0x9b, 0xa4, 0xcd, 0x32, 0xa2, 0x34, 0xe8, 0x4f, 0xd7, 0x45, 0x98, 0x21, 0x32, 0x9d, 0xc8 ]) self.assertEqual("".join(map(chr, n3.description)), "gold 1.12") def test_symbols_access(self): hello = lief.parse(get_sample('ELF/ELF64_x86-64_binary_hello-gdb.bin')) symbols = hello.symbols dynamic_symbols = hello.dynamic_symbols static_symbols = hello.static_symbols self.assertTrue(all(s in symbols for s in dynamic_symbols)) self.assertTrue(all(s in symbols for s in static_symbols)) def test_relocation_size(self): aarch64_toybox = lief.parse(get_sample('ELF/ELF64_AARCH64_piebinary_toybox.pie')) arm_ls = lief.parse(get_sample('ELF/ELF32_ARM_binary_ls.bin')) x86_ls = lief.parse(get_sample('ELF/ELF32_x86_binary_ls.bin')) x86_64_ls = lief.parse(get_sample('ELF/ELF64_x86-64_binary_ld.bin')) for r in itertools.chain(aarch64_toybox.dynamic_relocations, aarch64_toybox.pltgot_relocations): if lief.ELF.RELOCATION_AARCH64(r.type) == lief.ELF.RELOCATION_AARCH64.RELATIVE: self.assertEqual(r.size, 64) if lief.ELF.RELOCATION_AARCH64(r.type) == lief.ELF.RELOCATION_AARCH64.GLOB_DAT: self.assertEqual(r.size, 64) if lief.ELF.RELOCATION_AARCH64(r.type) == lief.ELF.RELOCATION_AARCH64.JUMP_SLOT: self.assertEqual(r.size, 64) for r in itertools.chain(arm_ls.dynamic_relocations, arm_ls.pltgot_relocations): if lief.ELF.RELOCATION_ARM(r.type) == lief.ELF.RELOCATION_ARM.RELATIVE: self.assertEqual(r.size, 32) if lief.ELF.RELOCATION_ARM(r.type) == lief.ELF.RELOCATION_ARM.GLOB_DAT: self.assertEqual(r.size, 32) if lief.ELF.RELOCATION_ARM(r.type) == lief.ELF.RELOCATION_ARM.ABS32: self.assertEqual(r.size, 32) if lief.ELF.RELOCATION_ARM(r.type) == lief.ELF.RELOCATION_ARM.JUMP_SLOT: self.assertEqual(r.size, 32) for r in itertools.chain(x86_ls.dynamic_relocations, x86_ls.pltgot_relocations): if lief.ELF.RELOCATION_i386(r.type) == lief.ELF.RELOCATION_i386.GLOB_DAT: self.assertEqual(r.size, 32) if lief.ELF.RELOCATION_i386(r.type) == lief.ELF.RELOCATION_i386.COPY: self.assertEqual(r.size, 32) if lief.ELF.RELOCATION_i386(r.type) == lief.ELF.RELOCATION_i386.JUMP_SLOT: self.assertEqual(r.size, 32) for r in itertools.chain(x86_64_ls.dynamic_relocations, x86_64_ls.pltgot_relocations): if lief.ELF.RELOCATION_X86_64(r.type) == lief.ELF.RELOCATION_X86_64.GLOB_DAT: self.assertEqual(r.size, 64) if lief.ELF.RELOCATION_X86_64(r.type) == lief.ELF.RELOCATION_X86_64.COPY: self.assertEqual(r.size, 32) if lief.ELF.RELOCATION_X86_64(r.type) == lief.ELF.RELOCATION_X86_64.JUMP_SLOT: self.assertEqual(r.size, 64) def test_sectionless(self): sample = "ELF/ELF64_x86-64_binary_rvs.bin" rvs = lief.parse(get_sample(sample)) dynsym = list(rvs.dynamic_symbols) self.assertEqual(len(dynsym), 10) def test_dynamic_flags(self): sample = "ELF/ELF32_ARM_binary_ls.bin" ls = lief.parse(get_sample(sample)) d_flags = ls.get(lief.ELF.DYNAMIC_TAGS.FLAGS) d_flags_1 = ls.get(lief.ELF.DYNAMIC_TAGS.FLAGS_1) self.assertIn(lief.ELF.DYNAMIC_FLAGS.BIND_NOW, d_flags) self.assertIn(lief.ELF.DYNAMIC_FLAGS_1.NOW, d_flags_1) def test_unwind_arm(self): sample = "ELF/ELF32_ARM_binary_ls.bin" ls = lief.parse(get_sample(sample)) functions = sorted(ls.functions, key=lambda f: f.address) self.assertEqual(len(functions), 265) self.assertEqual(functions[0].address, 19684) self.assertEqual(functions[0].size, 0) self.assertEqual(functions[0].name, "open") self.assertEqual(functions[-1].address, 102372) self.assertEqual(functions[-1].size, 0) self.assertEqual(functions[-1].name, "") def test_unwind_x86(self): sample = "ELF/ELF64_x86-64_binary_ld.bin" ld = lief.parse(get_sample(sample)) functions = sorted(ld.functions, key=lambda f: f.address) self.assertEqual(len(functions), 503) self.assertEqual(functions[0].address, 4209304) self.assertEqual(functions[0].size, 0) self.assertEqual(functions[0].name, "_init") self.assertEqual(functions[10].size, 174) self.assertEqual(functions[10].name, "") self.assertEqual(functions[-1].address, 4409396) self.assertEqual(functions[-1].size, 0) self.assertEqual(functions[-1].name, "_fini") def test_misc(self): sample = "ELF/ELF64_x86-64_binary_ld.bin" ld = lief.parse(get_sample(sample)) text = ld.get_section(".text") self.assertFalse(ld.has_section_with_offset(0)) self.assertFalse(ld.has_section_with_va(0xFFFFFFFF)) self.assertTrue(ld.has_section_with_offset(text.offset + 10)) self.assertTrue(ld.has_section_with_va(text.virtual_address + 10))
class TestAddSegment(TestCase): def setUp(self): self.logger = logging.getLogger(__name__) self.tmp_dir = tempfile.mkdtemp(suffix='_lief_test_add_segment') self.logger.debug("temp dir: {}".format(self.tmp_dir)) @unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux") @unittest.skipUnless(has_recent_glibc(), "Need a recent GLIBC version") def test_simple(self): sample_path = get_sample('ELF/ELF64_x86-64_binary_ls.bin') stub = lief.parse(os.path.join(CURRENT_DIRECTORY, "hello_lief.bin")) output = os.path.join(self.tmp_dir, "ls.segment") target = lief.parse(sample_path) for i in range(4): segment = stub.segments[0] original_va = segment.virtual_address segment.virtual_address = 0 segment = target.add(segment) new_ep = (stub.header.entrypoint - original_va) + segment.virtual_address target.header.entrypoint = new_ep 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'LIEF is Working', stdout.decode("utf8"))) @unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux") @unittest.skipUnless(has_recent_glibc(), "Need a recent GLIBC version") def test_gcc(self): sample_path = get_sample('ELF/ELF64_x86-64_binary_gcc.bin') stub = lief.parse(os.path.join(CURRENT_DIRECTORY, "hello_lief.bin")) output = os.path.join(self.tmp_dir, "gcc.segment") target = lief.parse(sample_path) segment = stub.segments[0] original_va = segment.virtual_address segment.virtual_address = 0 segment = target.add(segment) new_ep = (stub.header.entrypoint - original_va) + segment.virtual_address target.header.entrypoint = new_ep 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'LIEF is Working', stdout.decode("utf8"))) @unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux") def test_static(self): sample_path = get_sample('ELF/ELF64_x86-64_binary_static-binary.bin') stub = lief.parse(os.path.join(CURRENT_DIRECTORY, "hello_lief.bin")) output = os.path.join(self.tmp_dir, "static.segment") target = lief.parse(sample_path) segment = stub.segments[0] original_va = segment.virtual_address segment.virtual_address = 0 segment = target.add(segment) new_ep = (stub.header.entrypoint - original_va) + segment.virtual_address target.header.entrypoint = new_ep 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'LIEF is Working', stdout.decode("utf8"))) @unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux") def test_misc(self): list_binaries = [ '/usr/bin/ls', '/usr/bin/ssh', '/usr/bin/nm', '/usr/bin/openssl', '/usr/bin/bc', '/usr/bin/bzip2', '/usr/bin/cp', '/usr/bin/find', '/usr/bin/file', ] for binary in list_binaries: self.logger.debug("Test with '{}'".format(binary)) self.run_add_segment(binary) def run_add_segment(self, target): if not os.path.isfile(target): return name = os.path.basename(target) stub = lief.parse(os.path.join(CURRENT_DIRECTORY, "hello_lief.bin")) target = lief.parse(target) output = os.path.join(self.tmp_dir, "{}.segment".format(name)) for i in range(6): segment = stub.segments[0] original_va = segment.virtual_address segment.virtual_address = 0 segment = target.add(segment) new_ep = (stub.header.entrypoint - original_va) + segment.virtual_address target.header.entrypoint = new_ep 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'LIEF is Working', stdout.decode("utf8"))) @unittest.skipUnless(False, "requires Linux") def test_libc(self): stub = lief.parse(os.path.join(CURRENT_DIRECTORY, "hello_lief.bin")) tmp_dir = tempfile.mkdtemp(suffix='_lief_test_add_segment_libc') self.logger.debug("temp dir: {}".format(tmp_dir)) libc_name = "libc.so.6" for e in lief.parse("/bin/ls").libraries: if e.startswith("libc."): libc_name = e break self.logger.debug("libc used: {}".format(libc_name)) libc = lief.parse('/usr/lib/{}'.format(libc_name)) out = os.path.join(tmp_dir, libc_name) for i in range(10): segment = stub.segments[0] original_va = segment.virtual_address segment.virtual_address = 0 segment = libc.add(segment) new_ep = (stub.header.entrypoint - original_va) + segment.virtual_address if libc.has(lief.ELF.DYNAMIC_TAGS.INIT_ARRAY): init_array = libc.get(lief.ELF.DYNAMIC_TAGS.INIT_ARRAY) callbacks = init_array.array callbacks[0] = new_ep init_array.array = callbacks if libc.has(lief.ELF.DYNAMIC_TAGS.INIT): init = libc.get(lief.ELF.DYNAMIC_TAGS.INIT) init.value = new_ep libc.write(out) st = os.stat(out) os.chmod(out, st.st_mode | stat.S_IEXEC) p = Popen(["/usr/bin/ls"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env={"LD_LIBRARY_PATH": tmp_dir}) stdout, _ = p.communicate() self.logger.debug(stdout.decode("utf8")) self.assertIsNotNone( re.search(r'LIEF is Working', stdout.decode("utf8"))) if os.path.isdir(tmp_dir): shutil.rmtree(tmp_dir) def tearDown(self): # Delete it if os.path.isdir(self.tmp_dir): shutil.rmtree(self.tmp_dir)
class TestAddSegment(TestCase): def setUp(self): self.logger = logging.getLogger(__name__) self.tmp_dir = tempfile.mkdtemp(suffix='_lief_test_add_segment') self.logger.debug("temp dir: {}".format(self.tmp_dir)) @unittest.skipUnless(is_linux() and is_x86_64(), "requires Linux x86-64") @unittest.skipUnless(has_recent_glibc(), "Need a recent GLIBC version") def test_simple(self): sample_path = get_sample('ELF/ELF64_x86-64_binary_ls.bin') stub = lief.parse(os.path.join(CURRENT_DIRECTORY, "hello_lief.bin")) output = os.path.join(self.tmp_dir, "ls.segment") target = lief.parse(sample_path) for i in range(4): segment = stub.segments[0] original_va = segment.virtual_address segment.virtual_address = 0 segment = target.add(segment) new_ep = (stub.header.entrypoint - original_va) + segment.virtual_address target.header.entrypoint = new_ep 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'LIEF is Working', stdout.decode("utf8"))) @unittest.skipUnless(is_linux() and is_x86_64(), "requires Linux x86-64") @unittest.skipUnless(has_recent_glibc(), "Need a recent GLIBC version") def test_gcc(self): sample_path = get_sample('ELF/ELF64_x86-64_binary_gcc.bin') stub = lief.parse(os.path.join(CURRENT_DIRECTORY, "hello_lief.bin")) output = os.path.join(self.tmp_dir, "gcc.segment") target = lief.parse(sample_path) segment = stub.segments[0] original_va = segment.virtual_address segment.virtual_address = 0 segment = target.add(segment) new_ep = (stub.header.entrypoint - original_va) + segment.virtual_address target.header.entrypoint = new_ep 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'LIEF is Working', stdout.decode("utf8"))) @unittest.skipUnless(is_linux() and is_x86_64(), "requires Linux x86-64") def test_static(self): sample_path = get_sample('ELF/ELF64_x86-64_binary_static-binary.bin') stub = lief.parse(os.path.join(CURRENT_DIRECTORY, "hello_lief.bin")) output = os.path.join(self.tmp_dir, "static.segment") target = lief.parse(sample_path) segment = stub.segments[0] original_va = segment.virtual_address segment.virtual_address = 0 segment = target.add(segment) new_ep = (stub.header.entrypoint - original_va) + segment.virtual_address target.header.entrypoint = new_ep 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'LIEF is Working', stdout.decode("utf8"))) @unittest.skipUnless(is_linux(), "requires Linux") def test_misc(self): list_binaries = [ '/usr/bin/ls', '/usr/bin/ssh', '/usr/bin/nm', '/usr/bin/openssl', '/usr/bin/bc', '/usr/bin/bzip2', '/usr/bin/cp', '/usr/bin/find', '/usr/bin/file', ] for binary in list_binaries: self.logger.debug("Test with '{}'".format(binary)) self.run_add_segment(binary) def run_add_segment(self, target): if not os.path.isfile(target): self.logger.debug("%s does not exists. Skip!", target) return stub = None if is_x86_64(): stub = lief.parse(os.path.join(CURRENT_DIRECTORY, "hello_lief.bin")) elif is_aarch64(): stub = lief.parse( os.path.join(CURRENT_DIRECTORY, "hello_lief_aarch64.bin")) name = os.path.basename(target) target = lief.parse(target) output = os.path.join(self.tmp_dir, "{}.segment".format(name)) for i in range(6): stub_segment = stub.segments[0] segment = lief.ELF.Segment() segment.content = stub.segments[0].content segment.type = stub_segment.type segment.alignment = stub_segment.alignment segment.flags = stub_segment.flags new_segment = target.add(segment) new_ep = (stub.header.entrypoint - stub.imagebase - stub_segment.file_offset) + new_segment.virtual_address target.header.entrypoint = new_ep 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'LIEF is Working', stdout.decode("utf8"))) # TODO(romain): To fix #@unittest.skipUnless(is_linux(), "requires Linux x86-64") #def test_libc(self): # stub = None # if is_x86_64(): # stub = lief.parse(os.path.join(CURRENT_DIRECTORY, "hello_lief.bin")) # elif is_aarch64(): # stub = lief.parse(os.path.join(CURRENT_DIRECTORY, "hello_lief_aarch64.bin")) # tmp_dir = tempfile.mkdtemp(suffix='_lief_test_add_segment_libc') # self.logger.debug("temp dir: {}".format(tmp_dir)) # libc_name = "libc.so.6" # for e in lief.parse("/bin/ls").libraries: # if e.startswith("libc."): # libc_name = e # break # libc_path = '/usr/lib/{}'.format(libc_name) # if not os.path.isfile(libc_path): # libc_path = '/usr/lib/aarch64-linux-gnu/{}'.format(libc_name) # self.logger.debug("libc used: {}".format(libc_path)) # libc = lief.parse(libc_path) # out = os.path.join(tmp_dir, libc_name) # stub_segment = stub.segments[0] # for i in range(10): # segment = lief.ELF.Segment() # segment.content = stub.segments[0].content # segment.type = stub_segment.type # segment.alignment = stub_segment.alignment # segment.flags = stub_segment.flags # new_segment = libc.add(segment) # new_ep = (stub.header.entrypoint - stub.imagebase - stub_segment.file_offset) + new_segment.virtual_address # libc.header.entrypoint = new_ep # if libc.has(lief.ELF.DYNAMIC_TAGS.INIT_ARRAY): # init_array = libc.get(lief.ELF.DYNAMIC_TAGS.INIT_ARRAY) # callbacks = init_array.array # callbacks[0] = new_ep # init_array.array = callbacks # if libc.has(lief.ELF.DYNAMIC_TAGS.INIT): # init = libc.get(lief.ELF.DYNAMIC_TAGS.INIT) # init.value = new_ep # libc.write(out) # st = os.stat(out) # os.chmod(out, st.st_mode | stat.S_IEXEC) # p = Popen(["/usr/bin/ls"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env={"LD_LIBRARY_PATH": tmp_dir}) # stdout, _ = p.communicate() # self.logger.debug(stdout.decode("utf8")) # self.assertIsNotNone(re.search(r'LIEF is Working', stdout.decode("utf8"))) # if os.path.isdir(tmp_dir): # shutil.rmtree(tmp_dir) def tearDown(self): # Delete it if os.path.isdir(self.tmp_dir): shutil.rmtree(self.tmp_dir)
class TestAddSection(TestCase): def setUp(self): self.logger = logging.getLogger(__name__) self.tmp_dir = tempfile.mkdtemp(suffix='_lief_test_section') self.logger.debug("temp dir: {}".format(self.tmp_dir)) @unittest.skipUnless(is_linux() and is_x86_64(), "requires Linux x86-64") @unittest.skipUnless(has_recent_glibc(), "Need a recent GLIBC version") def test_simple(self): sample_path = get_sample('ELF/ELF64_x86-64_binary_ls.bin') output = os.path.join(self.tmp_dir, "ls.section") ls = lief.parse(sample_path) for i in range(10): section = Section(".test.{:d}".format(i), lief.ELF.SECTION_TYPES.PROGBITS) section += lief.ELF.SECTION_FLAGS.EXECINSTR section += lief.ELF.SECTION_FLAGS.WRITE section.content = STUB.segments[0].content # First LOAD segment which holds payload if i % 2 == 0: section = ls.add(section, loaded=True) ls.header.entrypoint = section.virtual_address + STUB.header.entrypoint else: section = ls.add(section, loaded=False) 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"))) @unittest.skipUnless(is_linux() and is_x86_64(), "requires Linux x86-64") @unittest.skipUnless(has_recent_glibc(), "Need a recent GLIBC version") def test_gcc(self): sample_path = get_sample('ELF/ELF64_x86-64_binary_gcc.bin') output = os.path.join(self.tmp_dir, "gcc.section") gcc = lief.parse(sample_path) for i in range(10): section = Section(".test.{:d}".format(i), lief.ELF.SECTION_TYPES.PROGBITS) section.type = lief.ELF.SECTION_TYPES.PROGBITS section += lief.ELF.SECTION_FLAGS.EXECINSTR section += lief.ELF.SECTION_FLAGS.WRITE section.content = STUB.segments[0].content # First LOAD segment which holds payload if i % 2 == 0: section = gcc.add(section, loaded=True) gcc.header.entrypoint = section.virtual_address + STUB.header.entrypoint else: section = gcc.add(section, loaded=False) gcc.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 tearDown(self): # Delete it if os.path.isdir(self.tmp_dir): shutil.rmtree(self.tmp_dir)
#!/usr/bin/env python import stat import re import subprocess from subprocess import Popen import pytest import lief import pathlib from utils import get_sample, has_recent_glibc, is_linux, is_x86_64 @pytest.mark.skipif(not (is_linux() and is_x86_64() and has_recent_glibc()), reason="incompatible env") def test_freebl(tmp_path): tmp = pathlib.Path(tmp_path) libfreebl3_path = get_sample('ELF/ELF64_x86-64_library_libfreebl3.so') output_ls = tmp / "ls.new" output_libfreebl3 = tmp / "libfreebl3.so" libfreebl3 = lief.parse(libfreebl3_path) ls = lief.parse("/usr/bin/ls") if ls is None: ls = lief.parse("/bin/ls") if lief.ELF.DYNAMIC_TAGS.FLAGS_1 in ls and ls[ lief.ELF.DYNAMIC_TAGS.FLAGS_1].has(lief.ELF.DYNAMIC_FLAGS_1.PIE): ls[lief.ELF.DYNAMIC_TAGS.FLAGS_1].remove(lief.ELF.DYNAMIC_FLAGS_1.PIE)
class TestAddSegment(TestCase): def setUp(self): self.logger = logging.getLogger(__name__) self.tmp_dir = tempfile.mkdtemp(suffix='_lief_test_add_segment') self.logger.debug("temp dir: {}".format(self.tmp_dir)) @unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux") @unittest.skipUnless(has_recent_glibc(), "Need a recent GLIBC version") def test_simple(self): sample_path = get_sample('ELF/ELF64_x86-64_binary_ls.bin') stub = lief.parse(os.path.join(CURRENT_DIRECTORY, "hello_lief.bin")) output = os.path.join(self.tmp_dir, "ls.replace_segment") target = lief.parse(sample_path) if not lief.ELF.SEGMENT_TYPES.NOTE in target: self.logger.error("Note not found!") return segment = stub.segments[0] original_va = segment.virtual_address segment.virtual_address = 0 segment = target.replace(segment, target[lief.ELF.SEGMENT_TYPES.NOTE]) new_ep = (stub.header.entrypoint - original_va) + segment.virtual_address target.header.entrypoint = new_ep 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'LIEF is Working', stdout.decode("utf8"))) @unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux") @unittest.skipUnless(has_recent_glibc(), "Need a recent GLIBC version") def test_gcc(self): sample_path = get_sample('ELF/ELF64_x86-64_binary_gcc.bin') stub = lief.parse(os.path.join(CURRENT_DIRECTORY, "hello_lief.bin")) output = os.path.join(self.tmp_dir, "gcc.replace_segment") target = lief.parse(sample_path) if not lief.ELF.SEGMENT_TYPES.NOTE in target: self.logger.error("Note not found!") return segment = stub.segments[0] original_va = segment.virtual_address segment.virtual_address = 0 segment = target.replace(segment, target[lief.ELF.SEGMENT_TYPES.NOTE]) new_ep = (stub.header.entrypoint - original_va) + segment.virtual_address target.header.entrypoint = new_ep 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'LIEF is Working', stdout.decode("utf8"))) @unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux") @unittest.skipUnless(has_recent_glibc(), "Need a recent GLIBC version") def test_ssh(self): stub = lief.parse(os.path.join(CURRENT_DIRECTORY, "hello_lief.bin")) output = os.path.join(self.tmp_dir, "ssh.replace_segment") target = lief.parse("/usr/bin/ssh") if not lief.ELF.SEGMENT_TYPES.NOTE in target: self.logger.error("Note not found!") return segment = stub.segments[0] original_va = segment.virtual_address segment.virtual_address = 0 segment = target.replace(segment, target[lief.ELF.SEGMENT_TYPES.NOTE]) new_ep = (stub.header.entrypoint - original_va) + segment.virtual_address target.header.entrypoint = new_ep 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'LIEF is Working', stdout.decode("utf8"))) def tearDown(self): # Delete it if os.path.isdir(self.tmp_dir): shutil.rmtree(self.tmp_dir)
#!/usr/bin/env python import stat import re import subprocess from subprocess import Popen import pytest import lief import pathlib from utils import get_sample, has_recent_glibc, is_linux, is_x86_64 @pytest.mark.skipif(not (is_linux() and is_x86_64() and has_recent_glibc()), reason="incompatible env") def test_freebl(tmp_path): tmp = pathlib.Path(tmp_path) libfreebl3_path = get_sample('ELF/ELF64_x86-64_library_libfreebl3.so') output_ls = tmp / "ls.new" output_libfreebl3 = tmp / "libfreebl3.so" libfreebl3 = lief.parse(libfreebl3_path) ls = lief.parse("/usr/bin/ls") if ls is None: ls = lief.parse("/bin/ls") if lief.ELF.DYNAMIC_TAGS.FLAGS_1 in ls and ls[lief.ELF.DYNAMIC_TAGS.FLAGS_1].has(lief.ELF.DYNAMIC_FLAGS_1.PIE): ls[lief.ELF.DYNAMIC_TAGS.FLAGS_1].remove(lief.ELF.DYNAMIC_FLAGS_1.PIE) ls.add_library("libfreebl3.so")
class TestRelocations(TestCase): def setUp(self): self.logger = logging.getLogger(__name__) self.tmp_dir = tempfile.mkdtemp(suffix='_lief_test_relocations') self.logger.debug("temp dir: {}".format(self.tmp_dir)) @unittest.skipUnless(is_linux() and is_x86_64(), "requires Linux x86-64") @unittest.skipUnless(has_recent_glibc(), "Need a recent GLIBC version") def test_simple(self): sample_path = get_sample('ELF/ELF64_x86-64_binary_ls.bin') output = os.path.join(self.tmp_dir, "ls.relocation") ls = lief.parse(sample_path) relocation = lief.ELF.Relocation( 0x61D370, type=lief.ELF.RELOCATION_X86_64.JUMP_SLOT, is_rela=True) symbol = lief.ELF.Symbol() symbol.name = "printf123" relocation.symbol = symbol ls.add_pltgot_relocation(relocation) ls.write(output) st = os.stat(output) os.chmod(output, st.st_mode | stat.S_IEXEC) p = Popen([output, "--version"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdout, _ = p.communicate() self.logger.debug(stdout.decode("utf8")) self.assertIsNotNone( re.search(r'ls \(GNU coreutils\) ', stdout.decode("utf8"))) @unittest.skipUnless(is_linux() and is_x86_64(), "requires Linux x86-64") 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"))) @unittest.skipUnless(is_linux() and is_x86_64(), "requires Linux x86-64") def test_all32(self): sample_path = get_sample('ELF/ELF32_x86_binary_all.bin') output = os.path.join(self.tmp_dir, "all32.relocation") target = lief.parse(sample_path) relocation = lief.ELF.Relocation( 0x2018, type=lief.ELF.RELOCATION_i386.JUMP_SLOT, is_rela=False) symbol = lief.ELF.Symbol() symbol.name = "printf123" relocation.symbol = symbol target.add_pltgot_relocation(relocation) target.write(output) new = lief.parse(output) self.assertTrue(new.has_symbol("printf123")) #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 tearDown(self): # Delete it if os.path.isdir(self.tmp_dir): shutil.rmtree(self.tmp_dir)
import lief import pytest from utils import get_sample, has_recent_glibc, is_linux, is_x86_64 SYMBOLS = { "myinstance": 0x1159, "myinit": 0x1175, "mycalc": 0x1199, "mydelete": 0x1214, } @pytest.mark.skipif(not is_linux() or not is_x86_64(), reason="requires Linux x86-64") @pytest.mark.skipif(not has_recent_glibc(), reason="needs a recent GLIBC version") def test_gnu_hash(tmpdir): target_path = get_sample('ELF/ELF64_x86-64_binary_empty-gnu-hash.bin') output = os.path.join(tmpdir, "libnoempty.so") binary = lief.parse(target_path) binary[lief.ELF.DYNAMIC_TAGS.FLAGS_1].remove(lief.ELF.DYNAMIC_FLAGS_1.PIE) for name, addr in SYMBOLS.items(): binary.add_exported_function(addr, name) binary.write(output) st = os.stat(output) os.chmod(output, st.st_mode | stat.S_IEXEC) print(output)