コード例 #1
0
    def signed_datas(self):
        """Returns an iterator over :class:`signify.authenticode.SignedData` objects relevant for this PE file.

        :raises SignedPEParseError: For parse errors in the PEFile
        :raises signify.authenticode.AuthenticodeParseError: For parse errors in the SignedData
        :return: iterator of signify.authenticode.SignedData
        """

        from signify.authenticode import SignedData

        found = False
        for certificate in self._parse_cert_table():
            if certificate['revision'] != 0x200:
                raise SignedPEParseError("Unknown certificate revision %x" %
                                         certificate['revision'])

            if certificate['type'] == 2:
                yield SignedData.from_certificate(certificate['certificate'],
                                                  pefile=self)
                found = True

        if not found:
            raise SignedPEParseError(
                "A SignedData structure was not found in the PE file's Certificate Table"
            )
コード例 #2
0
    def _parse_cert_table(self):
        """Parses the Certificate Table, iterates over all certificates"""

        locations = self.get_authenticode_omit_sections()
        if not locations or 'certtable' not in locations:
            raise SignedPEParseError(
                "The PE file does not contain a certificate table.")

        position = locations['certtable'].start
        while position < sum(locations['certtable']):
            # check if this position is viable, we need at least 8 bytes for our header
            if position + 8 > self._filelength:
                raise SignedPEParseError(
                    "Position of certificate table is beyond length of file")
            self.file.seek(position, os.SEEK_SET)
            length = struct.unpack('<I', self.file.read(4))[0]
            revision = struct.unpack('<H', self.file.read(2))[0]
            certificate_type = struct.unpack('<H', self.file.read(2))[0]

            # check if we are not going to perform a negative read (and 0 bytes is weird as well)
            if length <= 8:
                raise SignedPEParseError(
                    "Invalid length in certificate table header")
            certificate = self.file.read(length - 8)

            yield {
                'revision': revision,
                'type': certificate_type,
                'certificate': certificate
            }
            position += length + (8 - (length % 8))
コード例 #3
0
ファイル: signed_pe.py プロジェクト: ralphje/signify
    def iter_signed_datas(self, include_nested=True):
        """Returns an iterator over :class:`AuthenticodeSignedData` objects relevant for this PE file.

        :param include_nested: Boolean, if True, will also iterate over all nested SignedData structures
        :raises SignedPEParseError: For parse errors in the PEFile
        :raises signify.authenticode.AuthenticodeParseError: For parse errors in the SignedData
        :return: iterator of signify.authenticode.SignedData
        """

        from signify.authenticode.structures import AuthenticodeSignedData

        def recursive_nested(signed_data):
            yield signed_data
            if include_nested:
                for nested in signed_data.signer_info.nested_signed_datas:
                    yield from recursive_nested(nested)

        found = False
        for certificate in self._parse_cert_table():
            if certificate['revision'] != 0x200:
                raise SignedPEParseError("Unknown certificate revision %x" % certificate['revision'])

            if certificate['type'] == 2:
                yield from recursive_nested(AuthenticodeSignedData.from_envelope(certificate['certificate'],
                                                                                 pefile=self))
                found = True

        if not found:
            raise SignedPEParseError("A SignedData structure was not found in the PE file's Certificate Table")
コード例 #4
0
ファイル: signed_pe.py プロジェクト: kfirstri/signify
    def _parse_cert_table(self):
        """Parses the Certificate Table, iterates over all certificates"""

        locations = self.get_authenticode_omit_sections()
        if not locations or 'certtable' not in locations:
            raise SignedPEParseError(
                "The PE file does not contain a certificate table.")

        position = locations['certtable'].start
        while position < sum(locations['certtable']):
            self.file.seek(position, os.SEEK_SET)
            length = struct.unpack('<I', self.file.read(4))[0]
            revision = struct.unpack('<H', self.file.read(2))[0]
            certificate_type = struct.unpack('<H', self.file.read(2))[0]
            certificate = self.file.read(length - 8)

            yield {
                'revision': revision,
                'type': certificate_type,
                'certificate': certificate
            }
            position += length + (8 - (length % 8))
コード例 #5
0
    def _parse_pe_header_locations(self):
        """Parses a PE file to find the sections to exclude from the AuthentiCode hash.

        See http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx for information about the structure.
        """

        location = {}

        # Check if file starts with MZ
        self.file.seek(0, os.SEEK_SET)
        if self.file.read(2) != b'MZ':
            raise SignedPEParseError("MZ header not found")

        # Offset to e_lfanew (which is the PE header) is at 0x3C of the MZ header
        self.file.seek(0x3C, os.SEEK_SET)
        pe_offset = struct.unpack('<I', self.file.read(4))[0]
        if pe_offset >= self._filelength:
            raise SignedPEParseError(
                "PE header location is beyond file boundaries (%d >= %d)" %
                (pe_offset, self._filelength))

        # Check if the PE header is PE
        self.file.seek(pe_offset, os.SEEK_SET)
        if self.file.read(4) != b'PE\0\0':
            raise SignedPEParseError("PE header not found")

        # The COFF header contains the size of the optional header
        self.file.seek(pe_offset + 20, os.SEEK_SET)
        optional_header_size = struct.unpack('<H', self.file.read(2))[0]
        optional_header_offset = pe_offset + 24
        if optional_header_size + optional_header_offset > self._filelength:
            # This is not strictly a failure for windows, but such files better
            # be treated as generic files. They can not be carrying SignedData.
            raise SignedPEParseError(
                "The optional header exceeds the file length (%d + %d > %d)" %
                (optional_header_size, optional_header_offset,
                 self._filelength))

        if optional_header_size < 68:
            # We can't do authenticode-style hashing. If this is a valid binary,
            # which it can be, the header still does not even contain a checksum.
            raise SignedPEParseError(
                "The optional header size is %d < 68, which is insufficient for authenticode",
                optional_header_size)

        # The optional header contains the signature of the image
        self.file.seek(optional_header_offset, os.SEEK_SET)
        signature = struct.unpack('<H', self.file.read(2))[0]
        if signature == 0x10b:  # IMAGE_NT_OPTIONAL_HDR32_MAGIC
            rva_base = optional_header_offset + 92  # NumberOfRvaAndSizes
            cert_base = optional_header_offset + 128  # Certificate Table
        elif signature == 0x20b:  # IMAGE_NT_OPTIONAL_HDR64_MAGIC
            rva_base = optional_header_offset + 108  # NumberOfRvaAndSizes
            cert_base = optional_header_offset + 144  # Certificate Table
        else:
            # A ROM image or such, not in the PE/COFF specs. Not sure what to do.
            raise SignedPEParseError(
                "The PE Optional Header signature is %x, which is unknown",
                signature)

        # According to the specification, the checksum should not be hashed.
        location['checksum'] = RelRange(optional_header_offset + 64, 4)

        # Read the RVA
        if optional_header_offset + optional_header_size < rva_base + 4:
            logger.debug(
                "The PE Optional Header size can not accommodate for the NumberOfRvaAndSizes field"
            )
            return location
        self.file.seek(rva_base, os.SEEK_SET)
        number_of_rva = struct.unpack('<I', self.file.read(4))[0]
        if number_of_rva < 5:
            logger.debug(
                "The PE Optional Header does not have a Certificate Table entry in its Data Directory; "
                "NumberOfRvaAndSizes = %d", number_of_rva)
            return location
        if optional_header_offset + optional_header_size < cert_base + 8:
            logger.debug(
                "The PE Optional Header size can not accommodate for a Certificate Table entry in its Data "
                "Directory")
            return location

        # According to the spec, the certificate table entry of the data directory should be omitted
        location['datadir_certtable'] = RelRange(cert_base, 8)

        # Read the certificate table entry of the Data Directory
        self.file.seek(cert_base, os.SEEK_SET)
        address, size = struct.unpack('<II', self.file.read(8))

        if not size:
            logger.debug("The Certificate Table is empty")
            return location

        if address < optional_header_size + optional_header_offset or address + size > self._filelength:
            logger.debug(
                "The location of the Certificate Table in the binary makes no sense and is either beyond the "
                "boundaries of the file, or in the middle of the PE header; "
                "VirtualAddress: %x, Size: %x", address, size)
            return location

        location['certtable'] = RelRange(address, size)
        return location