def _run_uefi_extract_on_md(self, test, md):

        # remove any old shards we added
        for shard in md.shards:
            if shard.plugin_id == self.id:
                db.session.delete(shard)
        db.session.commit()

        # is this a AMI BIOS with PFAT sections
        if md.blob[8:16] == b'_AMIPFAT':
            pfat = PfatFile(md.blob)
            shards = []
            for shard in pfat.shards:
                shards.append(shard)
                if shard.name == 'com.ami.BIOS_FV_BB.bin':
                    continue
                shards.extend(self._get_shards_for_blob(shard.blob))
            test.add_pass('Found PFAT blob')

        # try with the plain blob (possibly with a capsule header) and
        # then look for a Zlib section (with an optional PFS-prefixed) blob
        else:
            shards = self._get_shards_for_blob(md.blob)
            if not shards:
                for blob in self._find_zlib_sections(md.blob):
                    try:
                        pfs = PfsFile(blob)
                        for shard in pfs.shards:
                            shards.append(shard)
                            shards.extend(self._get_shards_for_blob(
                                shard.blob))
                        test.add_pass('Found PFS in Zlib compressed blob')
                    except RuntimeError as _:
                        shard = ComponentShard(plugin_id=self.id)
                        shard.set_blob(blob)
                        shard.name = 'Zlib'
                        shard.guid = '68b8cc0e-4664-5c7a-9ce3-8ed9b4ffbffb'
                        shards.append(shard)
                        shards.extend(self._get_shards_for_blob(shard.blob))
                        test.add_pass('Found Zlib compressed blob')
            if not shards:
                test.add_pass('No firmware volumes found in {}'.format(
                    md.filename_contents))
                return

        # add shard to component
        for shard in shards:
            shard.plugin_id = self.id
            shard.component_id = md.component_id
            if self.get_setting_bool('uefi_extract_write_shards'):
                shard.save()
            md.shards.append(shard)
Пример #2
0
    def parse(self, blob):

        # sanity check
        nt = namedtuple('PfsHeaderTuple', ['tag', 'hdr_ver', 'payload_size'])
        try:
            pfs_hdr = nt._make(
                struct.unpack_from(PfsFile.PFS_HEADER, blob, 0x0))
        except struct.error as e:
            raise RuntimeError(str(e))
        if pfs_hdr.tag != b'PFS.HDR.':
            raise RuntimeError('Not a PFS header')
        if pfs_hdr.hdr_ver != 1:
            raise RuntimeError('PFS header version %i unsupported' %
                               pfs_hdr.hdr_ver)

        # parse sections
        offset = struct.calcsize(PfsFile.PFS_HEADER)
        while offset < len(blob) - struct.calcsize(PfsFile.PFS_FOOTER):

            # parse the section
            nt = namedtuple('PfsHeaderSection', [
                'guid', 'hdr_ver', 'ver_type', 'version', 'reserved',
                'data_sz', 'data_sig_sz', 'metadata_sz', 'metadata_sig_sz'
            ])
            try:
                pfs_sect = nt._make(
                    struct.unpack_from(PfsFile.PFS_SECTION, blob, offset))
            except struct.error as e:
                raise RuntimeError(str(e))
            if pfs_sect.hdr_ver != 1:
                raise RuntimeError('PFS section version %i unsupported' %
                                   pfs_hdr.hdr_ver)
            offset += struct.calcsize(PfsFile.PFS_SECTION)

            # parse the data and ignore the rest
            shard = ComponentShard()
            shard.set_blob(blob[offset:offset + pfs_sect.data_sz])
            shard.guid = str(uuid.UUID(bytes_le=pfs_sect.guid))
            if shard.guid == 'fd041960-0dc8-4b9f-8225-bba9e37c71e0':
                self._parse_info(shard.blob)
            elif shard.guid == '233ae3fb-da68-4fd4-92cb-a6229a611d6f':
                self._parse_model(shard.blob)
            else:
                self.shards.append(shard)

            # advance to the next section
            offset += pfs_sect.data_sz
            offset += pfs_sect.data_sig_sz
            offset += pfs_sect.metadata_sz
            offset += pfs_sect.metadata_sig_sz

        # the INFO structure is typically last, so fix up added shards
        for shard in self.shards:
            if shard.guid in self._names:
                shard.name = 'com.dell.' + self._names[shard.guid].replace(
                    ' ', '')
            else:
                shard.name = 'com.dell.' + shard.guid
Пример #3
0
    def _convert_files_to_shards(self, files):

        # parse each EFI binary as a shard
        shards = []
        for fn in files:
            sections = fn.rsplit('/')
            name = sections[-1].split('.')[0]
            kind = None
            guid = None
            for section in reversed(sections[:-1]):
                if section.find('GUID_DEFINED') != -1:
                    continue
                if section.find('COMPRESSION') != -1:
                    continue
                dirname_sections = section.split('.')
                guid = dirname_sections[0].split('_')[1].lower()
                kind = dirname_sections[1]
                break
            if not guid:
                continue
            appstream_kinds = {
                'FV_APPLICATION': 'Application',
                'FV_DRIVER': 'Driver',
                'FV_DXE_CORE': 'Dxe',
                'FV_PEI_CORE': 'Pei',
                'FV_PEIM': 'Peim',
                'FV_RAW': 'Raw',
                'FV_SECURITY_CORE': 'Security',
                'FV_COMBINED_PEIM_DRIVER': 'PeimDriver',
            }
            if kind in appstream_kinds:
                appstream_id = 'com.intel.Uefi.{}.{}'.format(
                    appstream_kinds[kind], name)
            else:
                appstream_id = 'com.intel.Uefi.{}'.format(name)
            shard = ComponentShard(plugin_id=self.id,
                                   name=appstream_id,
                                   guid=guid)
            with open(fn, 'rb') as f:
                shard.set_blob(f.read())
            shards.append(shard)
        return shards
Пример #4
0
def _add_shards(self, fpt, md):

    # remove any old shards we added
    for shard in md.shards:
        if shard.plugin_id == self.id:
            db.session.delete(shard)
    db.session.commit()

    # add shards
    for entry in fpt.entries:
        if not entry.guid:
            continue
        if not entry.blob:
            continue
        shard = ComponentShard(component_id=md.component_id,
                               plugin_id=self.id,
                               guid=entry.guid,
                               name=entry.appstream_id)
        shard.set_blob(entry.blob, checksums='SHA256')
        md.shards.append(shard)
Пример #5
0
    def _run_chipsec_on_md(self, test, md):

        # remove any old shards we added
        for shard in md.shards:
            if shard.plugin_id == self.id:
                for result in shard.yara_query_results:
                    db.session.delete(result)
                db.session.delete(shard)
        db.session.commit()

        # try first with the plain blob (possibly with a capsule header) and
        # then look for a Zlib section (with an optional PFS-prefixed) blob
        shards = self._get_shards_for_blob(md.blob)
        if not shards:
            for blob in self._find_zlib_sections(md.blob):
                try:
                    pfs = PfsFile(blob)
                    for shard in pfs.shards:
                        shards.append(shard)
                        shards.extend(self._get_shards_for_blob(shard.blob))
                    test.add_pass('Found PFS in Zlib compressed blob')
                except RuntimeError as _:
                    shard = ComponentShard(plugin_id=self.id)
                    shard.set_blob(blob)
                    shard.name = 'Zlib'
                    shard.guid = '68b8cc0e-4664-5c7a-9ce3-8ed9b4ffbffb'
                    shards.append(shard)
                    shards.extend(self._get_shards_for_blob(shard.blob))
                    test.add_pass('Found Zlib compressed blob')
        if not shards:
            test.add_pass('No firmware volumes found in {}'.format(
                md.filename_contents))
            return

        # add shard to component
        for shard in shards:
            shard.plugin_id = self.id
            shard.component_id = md.component_id
            if self.get_setting_bool('chipsec_write_shards'):
                shard.save()
            md.shards.append(shard)
Пример #6
0
def _run_psptool_on_blob(self, test, md):

    # remove any old shards we added
    for shard in md.shards:
        if shard.plugin_id == self.id:
            db.session.delete(shard)
    db.session.commit()

    # parse firmware
    try:
        psp = PSPTool(md.blob, verbose=True)
        for directory in psp.blob.directories:
            for entry in directory.entries:
                if isinstance(entry, HeaderEntry):
                    blob = entry.get_decompressed()
                    appstream_id = 'com.amd.PSP.HeaderEntry.{}'.\
                                        format(_get_readable_type(entry))
                elif isinstance(entry, PubkeyEntry):
                    blob = entry.get_pem_encoded()
                    appstream_id = 'com.amd.PSP.Entry.{}'.\
                                        format(_get_readable_type(entry))
                else:
                    blob = entry.get_bytes()
                    appstream_id = 'com.amd.PSP.{}'.\
                                        format(_get_readable_type(entry))

                # add shard to component
                shard = ComponentShard(component_id=md.component_id,
                                       plugin_id=self.id,
                                       guid=_mkguid(hex(entry.type)),
                                       name=appstream_id)
                shard.set_blob(blob, checksums='SHA256')
                md.shards.append(shard)
        test.add_pass('Found {} directories'.format(len(psp.blob.directories)))
    except (Blob.NoFirmwareEntryTableError, AssertionError) as _:
        pass
Пример #7
0
import datetime

# allows us to run this from the project root
sys.path.append(os.path.realpath('.'))

from lvfs.models import Test, Firmware, Component, Protocol, ComponentShard
from plugins.pecheck import Plugin

if __name__ == '__main__':
    for _argv in sys.argv[1:]:
        print('Processing', _argv)
        plugin = Plugin('pecheck')
        _test = Test(plugin.id)
        _fw = Firmware()
        _fw.timestamp = datetime.datetime.utcnow()
        _md = Component()
        _md.protocol = Protocol('org.uefi.capsule')
        _shard = ComponentShard(name=os.path.basename(_argv))
        try:
            with open(_argv, 'rb') as f:
                _shard.set_blob(f.read())
        except IsADirectoryError as _:
            continue
        _md.shards.append(_shard)
        _fw.mds.append(_md)
        plugin.run_test_on_fw(_test, _fw)
        for attribute in _test.attributes:
            print(attribute)
        for cert in _shard.certificates:
            print(cert)
Пример #8
0
            db.session.commit()
            for shard in md.shards:
                if shard.blob:
                    self._run_test_on_shard(test, shard)


# run with PYTHONPATH=. ./.env3/bin/python3 plugins/pecheck/__init__.py ./test.efi
if __name__ == '__main__':
    import sys
    from lvfs.models import Firmware, Component, ComponentShard, ComponentShardInfo, Protocol

    for argv in sys.argv[1:]:
        print('Processing', argv)
        plugin = Plugin('pecheck')
        _test = Test(plugin.id)
        _fw = Firmware()
        _md = Component()
        _md.protocol = Protocol('org.uefi.capsule')
        _shard = ComponentShard()
        _shard.info = ComponentShardInfo(name=os.path.basename(argv))
        try:
            with open(argv, 'rb') as f:
                _shard.set_blob(f.read())
        except IsADirectoryError as _:
            continue
        _md.shards.append(_shard)
        _fw.mds.append(_md)
        plugin.run_test_on_fw(_test, _fw)
        for attribute in _test.attributes:
            print(attribute)
    def _convert_files_to_shards(self, files):

        # parse each EFI binary as a shard
        shards = []
        shard_by_checksum = {}
        for fn in files:
            dirname = os.path.dirname(fn)
            try:
                with open(os.path.join(dirname, 'body.bin'), 'rb') as f:
                    payload = f.read()
            except FileNotFoundError as _:
                continue
            if len(payload) < 0x100:
                #print('ignoring payload of {} bytes'.format(len(payload)))
                continue

            # read in child data
            with open(fn, 'rb') as f:
                data = InfoTxtFile(f.read())
            if data.get('Subtype') in ['PE32 image', 'TE image']:
                with open(os.path.join(dirname, '..', 'info.txt'), 'rb') as f:
                    data = InfoTxtFile(f.read())
            name = data.get('Text')
            if not name:
                if data.get('CPU signature') and data.get('CPU flags'):
                    name = '{:08X}.{:08X}'.format(
                        data.get_int('CPU signature'),
                        data.get_int('CPU flags'))
            if name:
                for src in [' ', '(', ')']:
                    name = name.replace(src, '_')
            kind = data.get('Type')
            subkind = data.get('Subtype')
            guid = data.get('File GUID')
            if guid:
                guid = guid.lower()
            if subkind:
                kind += '::' + subkind

            # generate something plausible
            if kind == 'Microcode::Intel':
                guid = '3f0229ad-0a00-5269-90cf-0a45d8781b72'
            if not guid:
                #print('No GUID for', kind, fn)
                continue

            # ignore some kinds
            appstream_kinds = {
                '00h::Unknown 0': None,
                '01h::Compressed': None,
                '01h::Raw': None,
                '02h::Freeform': None,
                '02h::GUID defined': 'com.intel.Uefi.Raw',
                '03h::SEC core': 'com.intel.Uefi.Security',
                '04h::PEI core': 'com.intel.Uefi.Pei',
                '05h::DXE core': 'com.intel.Uefi.Dxe',
                '06h::PEI module': 'com.intel.Uefi.Peim',
                '07h::DXE driver': 'com.intel.Uefi.Driver',
                '09h::Application': 'com.intel.Uefi.Application',
                '0Ah::SMM module': 'com.intel.Uefi',
                '0Bh::Volume image': None,
                '0Ch::Combined SMM/DXE': 'com.intel.Uefi.SmmDxe',
                '0Dh::SMM core': 'com.intel.Uefi',
                '12h::TE image': None,
                '13h::DXE dependency': None,
                '14h::Version': None,
                '15h::UI': None,
                '17h::Volume image': None,
                '18h::Freeform subtype GUID': None,
                '19h::Raw': None,
                '1Bh::PEI dependency': None,
                '1Ch::MM dependency': None,
                'BPDT store': None,
                'CPD entry': None,
                'CPD partition::Code': None,
                'CPD partition::Key': None,
                'CPD partition::Manifest': None,
                'CPD partition::Metadata': None,
                'CPD store': None,  # ??
                'ECh': None,
                'EDh::GUID': None,
                'EEh::Name': None,
                'EFh::Data': None,
                'F0h::Pad': None,
                'Free space': None,
                'FTW store': None,
                'Image::Intel': None,
                'Image::UEFI': None,
                'Microcode::Intel': 'com.intel.Microcode',
                'NVAR entry::Full': None,
                'Padding::Empty (0xFF)': None,
                'Padding::Non-empty': None,
                'Region::BIOS': None,
                'Region::Descriptor': None,
                'Region::DevExp1': None,
                'Volume::FFSv2': None,
                'Volume::NVRAM': 'com.intel.Uefi.NVRAM',
                'VSS2 store': None,
            }
            if kind not in appstream_kinds:
                if len(kind) > 3:
                    print('No appstream_kinds for', kind, fn)
                continue
            if not appstream_kinds[kind]:
                #print('Ignoring appstream kind', kind)
                continue

            # something plausible
            appstream_id = appstream_kinds[kind]
            if name:
                appstream_id += '.{}'.format(name)
            shard = ComponentShard(plugin_id=self.id,
                                   name=appstream_id,
                                   guid=guid)
            shard.set_blob(payload)

            # do not add duplicates!
            if shard.checksum in shard_by_checksum:
                #print('skipping duplicate {}'.format(guid))
                continue

            # add attributes
            if kind == 'Microcode::Intel':

                # e.g. 000806E9
                value = data.get_int('CPU signature')
                if value:
                    shard.attributes.append(
                        ComponentShardAttribute(key='cpuid',
                                                value='{:08X}'.format(value)))

                # e.g. C0
                value = data.get_int('CPU flags')
                if value:
                    shard.attributes.append(
                        ComponentShardAttribute(key='platform',
                                                value='{:08X}'.format(value)))

                # e.g. C6
                value = data.get_int('Revision')
                if value:
                    shard.attributes.append(
                        ComponentShardAttribute(key='version',
                                                value='{:08X}'.format(value)))

                # convert dd.mm.yyyy to yyyymmdd
                value = data.get('Date')
                if value:
                    split = value.split('.')
                    date_iso = '{}{}{}'.format(split[2], split[1], split[0])
                    shard.attributes.append(
                        ComponentShardAttribute(key='yyyymmdd',
                                                value=date_iso))

                # combined size of header and body
                sz_bdy = data.get_int('Full size')
                sz_hdr = data.get_int('Header size')
                if sz_bdy and sz_hdr:
                    value = sz_bdy + sz_hdr
                    shard.attributes.append(
                        ComponentShardAttribute(key='size',
                                                value='{:08X}'.format(value)))

                # e.g. 98458A98
                value = data.get_int('Checksum')
                if value:
                    shard.attributes.append(
                        ComponentShardAttribute(key='checksum',
                                                value='{:08X}'.format(value)))

            shard_by_checksum[shard.checksum] = shard
            shards.append(shard)
        return shards
    def parse(self, blob):

        # sanity check
        PfatHeader = namedtuple('PfatHeader', ['size', 'csum', 'tag', 'ctrl'])
        try:
            pfat_hdr = PfatHeader._make(
                struct.unpack_from(PfatFile.PFAT_HEADER, blob, 0x0))
        except struct.error as e:
            raise RuntimeError from e
        if pfat_hdr.tag != b'_AMIPFAT':
            raise RuntimeError('Not a PFAT header')

        # parse the header data which seems to be of the form:
        # "1 /B 4 ;BIOS_FV_BB.bin" where the blockcount is 4 in this example
        section_data = (blob[struct.calcsize(PfatFile.PFAT_HEADER):pfat_hdr.
                             size].decode('utf-8').splitlines())
        PfatSection = namedtuple('PfatSection',
                                 ['flash', 'param', 'blockcnt', 'filename'])
        sections = []
        for entry in section_data[1:]:
            entry_data = entry.split(' ')
            sections.append(
                PfatSection(
                    flash=int(entry_data[0]),
                    param=entry_data[1],
                    blockcnt=int(entry_data[2]),
                    filename=entry_data[3][1:],
                ))

        # parse sections
        offset = pfat_hdr.size
        for section in sections:
            data = b''
            for _ in range(section.blockcnt):
                PfatBlockHeader = namedtuple(
                    'PfatBlockHeader',
                    [
                        'revision',
                        'platform',
                        'unknown0',
                        'unknown1',
                        'flagsz',
                        'datasz',
                        'unknown2',
                        'unknown3',
                        'unknown4',
                    ],
                )
                block_hdr = PfatBlockHeader._make(
                    struct.unpack_from(PfatFile.PFAT_BLOCK_HEADER, blob,
                                       offset))
                block_data_start = (
                    offset + struct.calcsize(PfatFile.PFAT_BLOCK_HEADER) +
                    block_hdr.flagsz)
                data += blob[block_data_start:block_data_start +
                             block_hdr.datasz]
                offset += (struct.calcsize(PfatFile.PFAT_BLOCK_HEADER) +
                           block_hdr.flagsz + block_hdr.datasz +
                           struct.calcsize(PfatFile.PFAT_BLOCK_SIGN))

            # add shard blob
            shard = ComponentShard()
            shard.set_blob(data)
            shard.name = 'com.ami.' + section.filename
            shard.guid = uuid.uuid3(uuid.NAMESPACE_DNS, section.filename)
            self.shards.append(shard)
Пример #11
0
        # run analysis on the component and any shards
        for shard in md.shards:
            if shard.guid in self.infos_by_guid:
                info = self.infos_by_guid[shard.guid]
                md.add_claim(info.claim)
            if shard.checksum in self.claims_by_csum:
                shard_claim = self.claims_by_csum[shard.checksum]
                md.add_claim(shard_claim.claim)


# run with PYTHONPATH=. ./env/bin/python3 plugins/shard-claim/__init__.py
if __name__ == '__main__':
    import sys
    from lvfs.models import Firmware, Component

    plugin = Plugin('shard-claim')
    _test = Test(plugin_id=plugin.id)
    _fw = Firmware()
    _md = Component()
    _shard = ComponentShard(guid='f114faa8-4fd5-4b95-8bc3-bc5cb5454966')
    _shard.checksums.append(ComponentShardChecksum(kind='SHA256',
                                                   value='fd14d82dd6f4f6fdc3263c25c681b11ef8'\
                                                         'daccd169efcab451cbb32c5f45ef8a'))
    _md.shards.append(_shard)
    _fw.mds.append(_md)
    plugin.run_test_on_fw(_test, _fw)
    plugin.run_test_on_md(_test, _md)
    for _claim in _md.claims:
        print(_claim)