[docs]classUefiDriverLoadError(Exception):""" This error is raised (and caught internally) if the data contained in the UEFI entity tree doesn't make sense. """
[docs]classUefiFirmware(Backend):""" A UEFI firmware blob loader. Support is provided by the ``uefi_firmware`` package. """is_default=True@classmethoddef_to_bytes(cls,fileobj:io.IOBase):try:fileno=fileobj.fileno()exceptio.UnsupportedOperation:passelse:returnmmap.mmap(fileno,0,access=mmap.ACCESS_READ)ifisinstance(fileobj,io.BytesIO):returnfileobj.getbuffer()# fuck it, we'll do it livefileobj.seek(0)returnfileobj.read()
[docs]def__init__(self,*args,**kwargs)->None:ifuefi_firmwareisNone:raiseCLEError("run `pip install uefi_firmware==1.10` to load UEFI firmware")super().__init__(*args,**kwargs)# hack: we are using a loader internal method in a non-kosher way which will cause our children to be# marked as the main binary if we are also the main binary# work around this by setting ourself here:ifself.loader._main_objectisNone:self.loader._main_object=selfself._drivers:dict[UUID,UefiModuleMixin]={}self._drivers_pending:dict[UUID,UefiModulePending]={}self._current_file:UUID|None=Noneself.set_arch(archinfo.arch_from_id("x86_64"))# TODO: ???buffer=self._to_bytes(self._binary_stream)parser=uefi_firmware.AutoParser(buffer)firmware=parser.parse()self._load(firmware)whileself._drivers_pending:uuid,pending=self._drivers_pending.popitem()try:child=pending.build(self,uuid)exceptUefiDriverLoadErrorase:log.warning("Failed to load %s: %s",uuid,e.args[0])else:self._drivers[uuid]=childchild.parent_object=selfself.child_objects.append(child)ifself.child_objects:self._arch=self.child_objects[0].archelse:log.warning("Loaded empty UEFI firmware?")self.has_memory=Falseself.pic=True# hack pt. 2ifself.loader._main_objectisself:self.loader._main_object=None
@singledispatchmethoddef_load(self,uefi_obj):# pylint: disable=no-self-useraiseCLEUnknownFormatError(f"Can't load firmware object: {uefi_obj}")ifuefi_firmwareisnotNone:@_load.registerdef_load_generic(self,uefi_obj:uefi_firmware.FirmwareObject):forobjinuefi_obj.objects:self._load(obj)@_load.registerdef_load_none(self,uefi_obj:None):pass@_load.registerdef_load_firmwarefile(self,uefi_obj:uefi_firmware.uefi.FirmwareFile):old_uuid=self._current_fileifuefi_obj.type==7:# driveruuid=UUID(bytes=uefi_obj.guid)self._drivers_pending[uuid]=UefiModulePending()self._current_file=uuidself._load_generic(uefi_obj)self._current_file=old_uuid@_load.registerdef_load_firmwarefilesection(self,uefi_obj:uefi_firmware.uefi.FirmwareFileSystemSection):pending=self._drivers_pending.get(self._current_file,None)ifpendingisnotNone:ifuefi_obj.type==16:# pe32 imagepending.pe_image=uefi_obj.contentelifuefi_obj.type==18:# te imagepending.te_image=uefi_obj.contentelifuefi_obj.type==21:# user interface namepending.name=uefi_obj.content.decode("utf-16").strip("\0")self._load_generic(uefi_obj)
[docs]@dataclassclassUefiModulePending:""" A worklist entry for the UEFI firmware loader. """name:str|None=Nonepe_image:bytes|None=Nonete_image:bytes|None=None# version# dependencies
[docs]defbuild(self,parent:UefiFirmware,guid:UUID)->UefiModuleMixin:count=(self.pe_imageisnotNone)+(self.te_imageisnotNone)ifcount>1:raiseUefiDriverLoadError("Multiple image sections")cls:type[UefiModuleMixin]ifself.pe_imageisnotNone:cls=UefiPEdata=self.pe_imageelifself.te_imageisnotNone:cls=UefiTEdata=self.te_imageelse:raiseUefiDriverLoadError("Missing PE or TE image section")returncls(None,io.BytesIO(data),is_main_bin=False,loader=parent.loader,name=self.name,guid=guid)
[docs]classUefiModuleMixin(Backend):""" A mixin to make other kinds of backends load as UEFI modules. """