Skip to content

Instantly share code, notes, and snippets.

@zhangyoufu
Created January 14, 2021 10:30
Show Gist options
  • Select an option

  • Save zhangyoufu/06919f592cb2fe9faeac22b469e016fa to your computer and use it in GitHub Desktop.

Select an option

Save zhangyoufu/06919f592cb2fe9faeac22b469e016fa to your computer and use it in GitHub Desktop.
Secure Boot related blob parser (for PK/KEK/db/dbx/dbxupdate), requires Python 3.7
#!/usr/bin/env python3
from dataclasses import dataclass, field
import enum
import uuid
class Bytes(bytes):
def __str__(self):
return self.hex()
def __repr__(self):
return self.hex()
class Enum(enum.Enum):
def __str__(self):
return self.name
def __repr__(self):
return self.name
def u16(data):
assert len(data) == 2
return int.from_bytes(data, byteorder='little')
def u32(data):
assert len(data) == 4
return int.from_bytes(data, byteorder='little')
class EFI_SIGNATURE_TYPE(Enum):
EFI_CERT_SHA256_GUID = uuid.UUID('c1c41626-504c-4092-aca9-41f936934328')
EFI_CERT_RSA2048_GUID = uuid.UUID('3c5766e8-269c-4e34-aa14-ed776e85b3b6')
EFI_CERT_RSA2048_SHA256_GUID = uuid.UUID('e2b36190-879b-4a3d-ad8d-f2e7bba32784')
EFI_CERT_SHA1_GUID = uuid.UUID('826ca512-cf10-4ac9-b187-be01496631bd')
EFI_CERT_RSA2048_SHA1_GUID = uuid.UUID('67f8444f-8743-48f1-a328-1eaab8736080')
EFI_CERT_X509_GUID = uuid.UUID('a5c059a1-94e4-4aa7-87b5-ab155c2bf072')
class EFI_SIGNATURE_OWNER(Enum):
EFI_SIGNATURE_OWNER_CANONICAL = uuid.UUID('685984e3-5d0f-4682-94c1-0f85ecb55d34')
EFI_SIGNATURE_OWNER_MICROSOFT = uuid.UUID('77fa9abd-0359-4d32-bd60-28f4e78f784b')
@dataclass
class EFI_SIGNATURE_DATA:
Owner: uuid.UUID
Data: Bytes
@classmethod
def from_bytes(cls, data):
return cls(
Owner=EFI_SIGNATURE_OWNER(uuid.UUID(bytes_le=data[:0x10])),
Data=Bytes(data[0x10:]),
)
@dataclass
class EFI_SIGNATURE_LIST(list):
Type: EFI_SIGNATURE_TYPE
Size: int
Header: Bytes
@classmethod
def from_bytes(cls, data):
Type = EFI_SIGNATURE_TYPE(uuid.UUID(bytes_le=data[:0x10]))
ListSize = u32(data[0x10:0x14])
assert len(data) >= ListSize
HeaderSize = u32(data[0x14:0x18])
SignatureSize = u32(data[0x18:0x1C])
assert SignatureSize > 0
offsetof_Signatures = 0x1C + HeaderSize
sizeof_Signatures = ListSize - offsetof_Signatures
assert sizeof_Signatures % SignatureSize == 0
Signatures = data[offsetof_Signatures:ListSize]
self = cls(
Type=Type,
Size=ListSize,
Header=Bytes(data[0x1C:0x1C+HeaderSize]),
)
self += [
EFI_SIGNATURE_DATA.from_bytes(Signatures[offset:offset+SignatureSize])
for offset in range(0, sizeof_Signatures, SignatureSize)
]
return self
def __repr__(self):
Signatures = '\n'.join(f' {item},' for item in self)
return f'EFI_SIGNATURE_LIST(Type={self.Type}, Header={self.Header}, Signatures=[\n{Signatures}\n])'
@dataclass
class EFI_TIME:
Year: int
Month: int
Day: int
Hour: int
Minute: int
Second: int
Nanosecond: int
TimeZone: int
Daylight: int
@classmethod
def from_bytes(cls, data):
assert len(data) == 16
Pad1 = data[7]
Pad2 = data[15]
assert Pad1 == Pad2 == 0
return cls(
Year=u16(data[0:2]),
Month=data[2],
Day=data[3],
Hour=data[4],
Minute=data[5],
Second=data[6],
Nanosecond=u32(data[8:12]),
TimeZone=u16(data[12:14]),
Daylight=data[14],
)
class WIN_CERT_TYPE(Enum):
WIN_CERT_TYPE_PKCS_SIGNED_DATA = 0x0002
WIN_CERT_TYPE_EFI_PKCS115 = 0x0EF0
WIN_CERT_TYPE_EFI_GUID = 0x0EF1
@dataclass(init=False)
class WIN_CERTIFICATE:
Length: int
Revision: int
CertificateType: WIN_CERT_TYPE
Certificate: Bytes
@classmethod
def from_bytes(cls, data):
self = cls()
self.Length = u32(data[0:4])
assert self.Length > 8
self.Revision = u16(data[4:6])
assert self.Revision == 0x0200, f'Revision=0x{self.Revision:X}'
self.CertificateType = WIN_CERT_TYPE(u16(data[6:8]))
self.Certificate = data[8:self.Length]
return self
def __len__(self):
return self.Length
class EFI_CERT_TYPE(Enum):
EFI_CERT_TYPE_PKCS7_GUID = uuid.UUID('4aafd29d-68df-49ee-8aa9-347d375665a7')
EFI_CERT_TYPE_RSA2048_SHA256_GUID = uuid.UUID('a7717414-c616-4977-9420-844712a735bf')
@dataclass(init=False)
class WIN_CERTIFICATE_UEFI_GUID(WIN_CERTIFICATE):
Certificate: Bytes = field(repr=False)
CertType: EFI_CERT_TYPE
CertData: Bytes
@classmethod
def from_bytes(cls, data):
self = super().from_bytes(data)
self.CertType = EFI_CERT_TYPE(uuid.UUID(bytes_le=self.Certificate[:16]))
self.CertData = Bytes(self.Certificate[16:])
return self
@dataclass
class EFI_VARIABLE_AUTHENTICATION_2:
TimeStamp: EFI_TIME
AuthInfo: WIN_CERTIFICATE_UEFI_GUID
@classmethod
def from_bytes(cls, data):
return cls(
TimeStamp=EFI_TIME.from_bytes(data[:16]),
AuthInfo=WIN_CERTIFICATE_UEFI_GUID.from_bytes(data[16:]),
)
def __len__(self):
return 0x10 + len(self.AuthInfo)
def __repr__(self):
return f'EFI_VARIABLE_AUTHENTICATION_2(\n TimeStamp={self.TimeStamp},\n AuthInfo={self.AuthInfo},\n)'
def parse_binary(data):
if data[:4] == b'\x27\x00\x00\x00': # EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE
# skip 4 bytes attributes exposed by efivarfs filesystem
data = data[4:]
if data[22:24] != b'\x00\x00':
var_auth_2 = EFI_VARIABLE_AUTHENTICATION_2.from_bytes(data)
print(var_auth_2)
data = data[len(var_auth_2):]
offset = 0
while offset < len(data):
sig_list = EFI_SIGNATURE_LIST.from_bytes(data[offset:])
print(sig_list)
offset += sig_list.Size
def parse_file(path):
import mmap
with open(path, 'rb') as f:
try:
data = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
except OSError:
data = f.read()
parse_binary(data)
def main():
import argparse
parser = argparse.ArgumentParser('Secure Boot related blob parser (for PK/KEK/db/dbx/dbxupdate)')
parser.add_argument('path', help='path to input blob')
args = parser.parse_args()
parse_file(args.path)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment