#!/usr/bin/env python
import unittest
import logging
import os
import sys
import stat
import re
import subprocess
import tempfile
import shutil
from subprocess import Popen

import lief
from lief.ELF import Section

from unittest import TestCase
from utils import get_sample, has_recent_glibc

CURRENT_DIRECTORY = os.path.dirname(os.path.abspath(__file__))
STUB = lief.parse(os.path.join(CURRENT_DIRECTORY, "hello_lief.bin"))

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(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)
        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(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')
        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)

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)

