def insert_section(
        self,
        datapoint: list[int] | str,
        sample_size: int | None = None,
        padding_char: int = 256,
        maxlen: int = 2**20,
        bytes_to_assign: int | None = None,
        verbose: bool = False,
    ) -> (
        tuple[np.ndarray, int, int, int, list[int], list[int]] | tuple[None, None, None, None, None, None]
    ):  # pragma: no cover
        """
        Create a new section in a PE file that the attacker can perturb to create an adversarial example.
        we are using the lief library (https://github.com/lief-project/LIEF) to manipulate the PE file.

        :param datapoint: either 1) path to file we want to analyse with lief and get the section information.
                          or 2) list of ints that can be processed by lief.

                          If we have already pre-processed the file into a numpy array, we convert it to a form
                          that can be read by lief.
                          eg, if we have it as a numpy array this could be done by:

                          datapoint = datapoint[0:size]  # size is the original size of the malware file
                          datapoint = datapoint.astype('uint8')
                          datapoint = datapoint.tolist()
        :param sample_size: Size of the original datapoint. Only if it is an array and the l0 budget is fractional
        :param padding_char: The char to use to pad the file to be of length maxlen
        :param maxlen: Maximum length of the data that the MalConv model can process
        :param bytes_to_assign: (Optional) how many bytes we wish to specify when inserting a new section.
                                If unspecified the whole l0 budget will be used on a single section.
        :param verbose: lief outputs a lot to the console, particularly if we are processing many files.
                        By default, suppress printing of messages. Can be toggled on/off by True/False
        :return manipulated_data: Executable with section inserted and turned into a numpy array of
                                  the appropriate size.
        :return len(manipulated_file): Size of original file
        :return information_on_section.pointerto_raw_data: The start of the inserted section
        :return information_on_section.virtual_size: Size of the inserted section
        :return size_of_slack: Size of slack regions in this executable (including from the section we just inserted)
        :return start_of_slack: Start of slack regions in this executable (including from the section we just inserted)
        """

        import lief  # pylint: disable=import-outside-toplevel

        if not verbose:
            lief.logging.disable()

        binary_parse = lief.PE.parse(datapoint)
        if binary_parse is not None:
            binary = binary_parse
        else:
            raise ValueError("Failed to load binary.")

        name_in_use = True
        while name_in_use:
            new_section_name = "." + "".join(chr(random.randrange(ord("a"), ord("z"))) for _ in range(5))
            name_in_use = False

            # check if the random name is already by chance in use
            for section in binary.sections:
                if new_section_name == section.name:
                    name_in_use = True

        new_section = lief.PE.Section(new_section_name)

        if bytes_to_assign is None:
            if self.l_0 < 1:  # l0 is a fraction of the file size
                # if it's a filepath we need to get the file size
                if isinstance(datapoint, str):
                    with open(datapoint, "rb") as file:
                        open_file = file.read()
                    sample_size = len(open_file)
                else:
                    if sample_size is None:
                        raise ValueError(
                            "if the data is an array and the l0 budget is fractional "
                            "the sample size must be provided"
                        )

                perturbation_size = int(sample_size * self.l_0)
            else:  # or l0 is interpreted as total perturbation size
                perturbation_size = int(self.l_0)
            new_section.content = [random.randint(0, 255) for _ in range(perturbation_size)]  # type: ignore
        else:
            new_section.content = [random.randint(0, 255) for _ in range(bytes_to_assign)]  # type: ignore

        # we add the new section at the end of the existing sections
        section_end_points = []
        for section in binary.sections:
            section_end_points.append(section.virtual_address + section.size)

        new_section.virtual_address = max(section_end_points)

        binary.add_section(
            new_section,
            random.choice(
                [
                    lief.PE.SECTION_TYPES.BSS,
                    lief.PE.SECTION_TYPES.DATA,
                    lief.PE.SECTION_TYPES.EXPORT,
                    lief.PE.SECTION_TYPES.IDATA,
                    lief.PE.SECTION_TYPES.RELOCATION,
                    lief.PE.SECTION_TYPES.RESOURCE,
                    lief.PE.SECTION_TYPES.TEXT,
                    lief.PE.SECTION_TYPES.UNKNOWN,
                ]
            ),
        )

        information_on_section = binary.get_section(new_section_name)

        size_of_slack = []
        start_of_slack = []
        for section in binary.sections:
            slack = section.sizeof_raw_data - section.virtual_size
            if slack > 0:
                size_of_slack.append(slack)
                start_of_slack.append(section.pointerto_raw_data + section.virtual_size)

        builder = lief.PE.Builder(binary)
        builder.build()

        manipulated_file = np.array(builder.get_build(), dtype=np.uint8)

        manipulated_data = np.ones((maxlen,), dtype=np.uint16) * padding_char

        # Only process files which are less than the max file size supported
        if len(manipulated_file) < maxlen:
            manipulated_data[: len(manipulated_file)] = manipulated_file[:maxlen]

            return (
                manipulated_data,
                len(manipulated_file),
                information_on_section.pointerto_raw_data,
                information_on_section.virtual_size,
                size_of_slack,
                start_of_slack,
            )
        return None, None, None, None, None, None

        Create a new section in a PE file that the attacker can perturb to create an adversarial example.
        we are using the lief library (https://github.com/lief-project/LIEF) to manipulate the PE file.

        :param datapoint: either 1) path to file we want to analyse with lief and get the section information.
                          or 2) list of ints that can be processed by lief.

                          If we have already pre-processed the file into a numpy array, we convert it to a form
                          that can be read by lief.
                          eg, if we have it as a numpy array this could be done by:

                          datapoint = datapoint[0:size]  # size is the original size of the malware file
                          datapoint = datapoint.astype('uint8')
                          datapoint = datapoint.tolist()
        :param sample_size: Size of the original datapoint. Only if it is an array and the l0 budget is fractional
        :param padding_char: The char to use to pad the file to be of length maxlen
        :param maxlen: Maximum length of the data that the MalConv model can process
        :param bytes_to_assign: (Optional) how many bytes we wish to specify when inserting a new section.
                                If unspecified the whole l0 budget will be used on a single section.
        :param verbose: lief outputs a lot to the console, particularly if we are processing many files.
                        By default, suppress printing of messages. Can be toggled on/off by True/False
        :return manipulated_data: Executable with section inserted and turned into a numpy array of
                                  the appropriate size.
        :return len(manipulated_file): Size of original file
        :return information_on_section.pointerto_raw_data: The start of the inserted section
        :return information_on_section.virtual_size: Size of the inserted section
        :return size_of_slack: Size of slack regions in this executable (including from the section we just inserted)
        :return start_of_slack: Start of slack regions in this executable (including from the section we just inserted)
        
art/attacks/evasion/pe_malware_attack.py