Skip to content

Instantly share code, notes, and snippets.

@XVilka
Created December 28, 2013 19:38
Show Gist options
  • Save XVilka/8163272 to your computer and use it in GitHub Desktop.
Save XVilka/8163272 to your computer and use it in GitHub Desktop.
Unpacks *.BIO and *.CAP EFI capsule files
#!/bin/env python
import ctypes
import struct
import sys
import os
import array
EFI_CAPSULE_GUID = "BD86663B760D3040B70EB5519E2FC5A0".decode('hex') # 3B6686BD-0D76-4030-B70E-B5519E2FC5A0
EFI2_CAPSULE_GUID = "8BA63C4A2377FB48803D578CC1FEC44D".decode('hex') # 4A3CA68B-7723-48FB-803D-578CC1FEC44D
UEFI_CAPSULE_GUID = "B9829153B5AB9143B69AE3A943F72FCC".decode('hex') # 539182B9-ABB5-4391-B69A-E3A943F72FCC
uint8_t = ctypes.c_ubyte
char = ctypes.c_char
uint32_t = ctypes.c_uint
uint64_t = ctypes.c_uint64
uint16_t = ctypes.c_ushort
def read_struct(li, struct):
s = struct()
slen = ctypes.sizeof(s)
bytes = li.read(slen)
fit = min(len(bytes), slen)
ctypes.memmove(ctypes.addressof(s), bytes, fit)
return s
def get_struct(str_, off, struct):
s = struct()
slen = ctypes.sizeof(s)
bytes = str_[off:off+slen]
fit = min(len(bytes), slen)
ctypes.memmove(ctypes.addressof(s), bytes, fit)
return s
def format_guid(guid_s):
parts = struct.unpack("<LHH8B", guid_s)
return "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X" % parts
class EfiCapsuleHeader(ctypes.LittleEndianStructure):
_fields_ = [
("CapsuleGuid", uint8_t*16), #
("HeaderSize", uint32_t), #
("Flags", uint32_t), #
("CapsuleImageSize", uint32_t), #
("SequenceNumber", uint32_t), #
("InstanceId", uint8_t*16), #
("OffsetToSplitInformation", uint32_t), #
("OffsetToCapsuleBody", uint32_t), #
("OffsetToOemDefinedHeader", uint32_t), #
("OffsetToAuthorInformation", uint32_t), #
("OffsetToRevisionInformation", uint32_t), #
("OffsetToShortDescription", uint32_t), #
("OffsetToLongDescription", uint32_t), #
("OffsetToApplicableDevices", uint32_t), #
]
def __init__(self):
self.CapsuleImageSize = 0
def pprint(self):
print("CapsuleGuid: {0}".format(format_guid(self.CapsuleGuid)))
print("HeaderSize: {0}".format(self.HeaderSize))
print("Flags: {0}".format(self.Flags))
print("CapsuleImageSize: {0}".format(self.CapsuleImageSize))
print("SequenceNumber: {0}".format(self.SequenceNumber))
print("InstanceId: {0}".format(format_guid(self.InstanceId)))
print("OffsetToSplitInformation: {0}".format(self.OffsetToSplitInformation))
print("OffsetToCapsuleBody: {0}".format(self.OffsetToCapsuleBody))
print("OffsetToOemDefinedHeader {0}".format(self.OffsetToOemDefinedHeader))
print("OffsetToAuthorInformation {0}".format(self.OffsetToAuthorInformation))
print("OffsetToRevisionInformation {0}".format(self.OffsetToRevisionInformation))
print("OffsetToShortDescription: {0}".format(self.OffsetToShortDescription))
print("OffsetToLongDescription {0}".format(self.OffsetToLongDescription))
print("OffsetToApplicableDevices {0}".format(self.OffsetToApplicableDevices))
class Efi2CapsuleHeader(ctypes.LittleEndianStructure):
_fields_ = [
("CapsuleGuid", uint8_t*16), #
("HeaderSize", uint32_t), #
("Flags", uint32_t), #
("CapsuleImageSize", uint32_t), #
("FwImageOffset", uint16_t), #
("OemHdrOffset", uint16_t), #
]
def __init__(self):
self.CapsuleImageSize = 0
def pprint(self):
print("CapsuleGuid: {0}".format(format_guid(self.CapsuleGuid)))
print("HeaderSize: {0}".format(self.HeaderSize))
print("Flags: {0}".format(self.Flags))
print("CapsuleImageSize: {0}".format(self.CapsuleImageSize))
print("FwImageOffset: {0}".format(self.FwImageOffset))
print("OemHdrOffset: {0}".format(self.OemHdrOffset))
class UefiCapsuleHeader(ctypes.LittleEndianStructure):
_fields_ = [
("CapsuleGuid", uint8_t*16), #
("HeaderSize", uint32_t), #
("Flags", uint32_t), #
("CapsuleImageSize", uint32_t), #
]
def __init__(self):
self.CapsuleImageSize = 0
def pprint(self):
print("CapsuleGuid: {0}".format(format_guid(self.CapsuleGuid)))
print("HeaderSize: {0}".format(self.HeaderSize))
print("Flags: {0}".format(self.Flags))
print("CapsuleImageSize: {0}".format(self.CapsuleImageSize))
class OemCapsuleHeader(ctypes.LittleEndianStructure):
_fields_ = [
("OemGuid", uint8_t*16), #
("HeaderSize", uint32_t), #
]
def __init__(self):
self.HeaderSize = 0
def pprint(self):
print("OemGuid: {0}".format(format_guid(self.OemGuid)))
print("HeaderSize: {0}".format(self.HeaderSize))
def check_fv(fvdata):
(fvzero, fvfstype, fvlen, fvsig, fvattr, fvhdrlen, fvchecksum, fvrev) = struct.unpack("< 16s 16s Q 4s L H H 3x B", fvdata[0:0x38])
if fvsig != "_FVH":
if fvdata[0] == '\xFF':
print("Skipping FFs")
offset = 0
while fvdata[offset] == '\xFF':
offset += 1
return offset
else:
print("Not a EFI firmware volume (sig and GUID missing)")
return 0
else:
print("EFI firmware volume")
return 1
def analyze_diskfile(filename, offset = 0):
f = file(filename, "rb")
fvdata = f.read()
f.close()
all_len = len(fvdata)
print("Analyzing {0}, {1} bytes".format(filename, all_len))
if len(fvdata) + offset < offset + ctypes.sizeof(Efi2CapsuleHeader) :
return
if fvdata[offset:offset+16] == EFI_CAPSULE_GUID:
print("Old version of EFI capsule")
# old EFI capsule
caphdr_size = ctypes.sizeof(EfiCapsuleHeader)
caphdr = get_struct(fvdata, offset, EfiCapsuleHeader)
caphdr.pprint()
elif fvdata[offset:offset+16] == EFI2_CAPSULE_GUID :
print("New version of EFI capsule")
caphdr_size = ctypes.sizeof(Efi2CapsuleHeader)
caphdr = get_struct(fvdata, offset, Efi2CapsuleHeader)
caphdr.pprint()
oemhdr = get_struct(fvdata, offset+caphdr.OemHdrOffset, OemCapsuleHeader)
oemhdr.pprint()
fwimage = fvdata[offset+caphdr.FwImageOffset:all_len-offset-caphdr.FwImageOffset]
if check_fv(fwimage) != 0:
fout = file(filename+".fw", "wb")
fout.write(fwimage)
fout.close()
else:
print("Can't determine image type")
elif fvdata[offset:offset+16] == UEFI_CAPSULE_GUID :
print("New version of EFI capsule")
caphdr_size = ctypes.sizeof(UefiCapsuleHeader)
caphdr = get_struct(fvdata, offset, UefiCapsuleHeader)
caphdr.pprint()
fwstart = offset+caphdr.HeaderSize
fwimage = fvdata[fwstart:all_len-fwstart]
if check_fv(fwimage) != 0:
fout = file(filename+".fw", "wb")
fout.write(fwimage)
fout.close()
else:
print("Can't determine image type")
else :
print("Unrecognizeable format of EFI capsule")
exit(1)
### main code
if __name__ == '__main__':
if len(sys.argv) > 1:
filename = sys.argv[1]
if len(sys.argv) > 2 and sys.argv[2].isalnum():
offset = int(sys.argv[2], 16)
else:
offset = 0
analyze_diskfile(filename, offset)
else:
print("Usage: bio_unpack.py bios.rom [start offset]")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment