4
0
mirror of https://github.com/QuasarApp/LIEF.git synced 2025-04-29 05:44:32 +00:00
LIEF/tests/elf/add_segment.py

240 lines
8.7 KiB
Python
Raw Normal View History

2017-03-30 16:56:49 +02:00
#!/usr/bin/env python
import logging
import os
import re
2020-11-09 21:02:50 +01:00
import shutil
import stat
2017-03-30 16:56:49 +02:00
import subprocess
2020-11-09 21:02:50 +01:00
import sys
2017-03-30 16:56:49 +02:00
import tempfile
2020-11-09 21:02:50 +01:00
import unittest
2017-03-30 16:56:49 +02:00
from subprocess import Popen
2020-11-09 21:02:50 +01:00
from unittest import TestCase
2017-03-30 16:56:49 +02:00
import lief
from lief.ELF import Segment
2021-01-03 17:52:44 +01:00
from utils import get_sample, has_recent_glibc, is_linux, is_x86_64, is_aarch64
2017-03-30 16:56:49 +02:00
2020-11-09 21:02:50 +01:00
lief.logging.set_level(lief.logging.LOGGING_LEVEL.INFO)
2017-03-30 16:56:49 +02:00
CURRENT_DIRECTORY = os.path.dirname(os.path.abspath(__file__))
class TestAddSegment(TestCase):
def setUp(self):
self.logger = logging.getLogger(__name__)
self.tmp_dir = tempfile.mkdtemp(suffix='_lief_test_add_segment')
2017-03-30 16:56:49 +02:00
self.logger.debug("temp dir: {}".format(self.tmp_dir))
2021-01-03 17:52:44 +01:00
@unittest.skipUnless(is_linux() and is_x86_64(), "requires Linux x86-64")
Build manylinux1-compliant wheels in Travis * Temporarily disable Melkor in test suite The Makefile uses the -executable flag which doesn't work on RHEL5. Disabling until I find a workaround for this. * Patch Makefile in Melkor fuzzer This replaces the `-executable` flag which is not supported by `find` on CentOS 5. * Respect CC environment variable Several tests hard coded /usr/bin/cc which might not exist in some environments. We first check the CC environment variable and fall back to the hard coded path if CC is unset. * Skip tests on GLIBC < 2.17 Some test binaries were linked against GLIBC 2.17. Skip tests which use this binary if the platform does not have the required GLIBC version. * Enable ccache in Docker in Travis builds * Run `auditwheel repair` on the produced wheels This will vendor the needed external shared libraries into the wheel and tag it as manylinux1. * Install ccache in Docker image * Avoid using bind mount volume in Docker build The bind mount volume wrote files as 'root' which causes issues with the deploy script in Travis. Copying the source code into the image and retrieving the built wheels instead of mounting the source tree fixes this issue. * Fix missing build folder when building with Docker After finishing the build inside Docker we need the build directory from the container to be able to deploy the built artifacts with deploy.sh. * Use the right Python interpreter for Linux builds The Dockerized .travis.yml builds attempt to invoke the interpreter in the PYTHON_BINARY environment variable, which is only valid inside the Docker image. To fix this, override the variable on Linux for tasks which require the host's Python interpreter. * Fix missing pip installation in Travis The Ubuntu image in Travis does not come with `pip` preinstalled for Python 3. * Remove .git directory from .dockerignore As `setup.py` uses `git` to determine the version number we need to copy the contents of `.git` into the image.
2019-08-22 22:05:53 +01:00
@unittest.skipUnless(has_recent_glibc(), "Need a recent GLIBC version")
2017-03-30 16:56:49 +02:00
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"))
2017-03-30 16:56:49 +02:00
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
2017-03-30 16:56:49 +02:00
target.header.entrypoint = new_ep
target.write(output)
2017-03-30 16:56:49 +02:00
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")))
2021-01-03 17:52:44 +01:00
@unittest.skipUnless(is_linux() and is_x86_64(), "requires Linux x86-64")
Build manylinux1-compliant wheels in Travis * Temporarily disable Melkor in test suite The Makefile uses the -executable flag which doesn't work on RHEL5. Disabling until I find a workaround for this. * Patch Makefile in Melkor fuzzer This replaces the `-executable` flag which is not supported by `find` on CentOS 5. * Respect CC environment variable Several tests hard coded /usr/bin/cc which might not exist in some environments. We first check the CC environment variable and fall back to the hard coded path if CC is unset. * Skip tests on GLIBC < 2.17 Some test binaries were linked against GLIBC 2.17. Skip tests which use this binary if the platform does not have the required GLIBC version. * Enable ccache in Docker in Travis builds * Run `auditwheel repair` on the produced wheels This will vendor the needed external shared libraries into the wheel and tag it as manylinux1. * Install ccache in Docker image * Avoid using bind mount volume in Docker build The bind mount volume wrote files as 'root' which causes issues with the deploy script in Travis. Copying the source code into the image and retrieving the built wheels instead of mounting the source tree fixes this issue. * Fix missing build folder when building with Docker After finishing the build inside Docker we need the build directory from the container to be able to deploy the built artifacts with deploy.sh. * Use the right Python interpreter for Linux builds The Dockerized .travis.yml builds attempt to invoke the interpreter in the PYTHON_BINARY environment variable, which is only valid inside the Docker image. To fix this, override the variable on Linux for tasks which require the host's Python interpreter. * Fix missing pip installation in Travis The Ubuntu image in Travis does not come with `pip` preinstalled for Python 3. * Remove .git directory from .dockerignore As `setup.py` uses `git` to determine the version number we need to copy the contents of `.git` into the image.
2019-08-22 22:05:53 +01:00
@unittest.skipUnless(has_recent_glibc(), "Need a recent GLIBC version")
2017-03-30 16:56:49 +02:00
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"))
2017-03-30 16:56:49 +02:00
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
2017-03-30 16:56:49 +02:00
target.header.entrypoint = new_ep
target.write(output)
2017-03-30 16:56:49 +02:00
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")))
2021-01-03 17:52:44 +01:00
@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")))
2021-01-03 17:52:44 +01:00
@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):
2021-01-03 17:52:44 +01:00
self.logger.debug("%s does not exists. Skip!", target)
return
2021-01-03 17:52:44 +01:00
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):
2021-01-03 17:52:44 +01:00
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)
2017-03-30 16:56:49 +02:00
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")))
2021-01-03 17:52:44 +01:00
# 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))
2021-01-03 17:52:44 +01:00
# libc_name = "libc.so.6"
# for e in lief.parse("/bin/ls").libraries:
# if e.startswith("libc."):
# libc_name = e
# break
2021-01-03 17:52:44 +01:00
# libc_path = '/usr/lib/{}'.format(libc_name)
# if not os.path.isfile(libc_path):
# libc_path = '/usr/lib/aarch64-linux-gnu/{}'.format(libc_name)
2021-01-03 17:52:44 +01:00
# self.logger.debug("libc used: {}".format(libc_path))
2021-01-03 17:52:44 +01:00
# libc = lief.parse(libc_path)
# out = os.path.join(tmp_dir, libc_name)
2021-01-03 17:52:44 +01:00
# 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
2021-01-03 17:52:44 +01:00
# new_segment = libc.add(segment)
# new_ep = (stub.header.entrypoint - stub.imagebase - stub_segment.file_offset) + new_segment.virtual_address
2021-01-03 17:52:44 +01:00
# libc.header.entrypoint = new_ep
2021-01-03 17:52:44 +01:00
# 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
2021-01-03 17:52:44 +01:00
# if libc.has(lief.ELF.DYNAMIC_TAGS.INIT):
# init = libc.get(lief.ELF.DYNAMIC_TAGS.INIT)
# init.value = new_ep
2021-01-03 17:52:44 +01:00
# libc.write(out)
2021-01-03 17:52:44 +01:00
# st = os.stat(out)
# os.chmod(out, st.st_mode | stat.S_IEXEC)
2021-01-03 17:52:44 +01:00
# 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"))
2021-01-03 17:52:44 +01:00
# self.assertIsNotNone(re.search(r'LIEF is Working', stdout.decode("utf8")))
2021-01-03 17:52:44 +01:00
# if os.path.isdir(tmp_dir):
# shutil.rmtree(tmp_dir)
2017-03-30 16:56:49 +02:00
def tearDown(self):
# Delete it
if os.path.isdir(self.tmp_dir):
shutil.rmtree(self.tmp_dir)
if __name__ == '__main__':
root_logger = logging.getLogger()
root_logger.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
root_logger.addHandler(ch)
unittest.main(verbosity=2)