Skip to content

Intel Microcode Extra Undocumented Header

Plato Mavropoulos edited this page Apr 3, 2019 · 7 revisions

Introduction

Starting from 2006, Intel added an additional Microcode Header which, to this day, remains undocumented. This page is meant for those who are looking to understand how that Header is structured. At MC Extractor (MCE) I call this Header "Extra" and should not be confused with the Intel documented "Extended" optional Header. Note that the "Main" and "Extended" Headers are documented by Intel and thus not relevant to this research.

The foundations for the latter were built by Ben Hawkes (Ben) in his 2013 Notes on Intel Microcode Updates paper. Since various comparisons and references are made to Ben's findings, it is highly recommended to first read his paper.

Based on MCE's database, we are now able to determine the purpose of almost all Extra Header fields and also improve the ones suggested by Ben back in 2013. The following observations derive from a collection of 1267 microcodes which incorporate the Extra Header from 2006-03 to 2019-03.

Extra Header Analysis

The Extra Header starts at offset 0x30, exactly after the official documented Main Header.

  • The 1st entry (0x30-0x32, ModuleType) determines the type of the Module which for Extra is always 0. This is evident from various other Intel structures which include similar RSA blocks such as Engine firmware (ME/TXE/SPS), TXT ACM, BootGuard etc.

  • The 2nd entry (0x32-0x34, ModuleSubType) determines the sub-type of the Module which for Extra is always 0. This is evident from various other Intel structures which include similar RSA blocks such as Engine firmware (ME/TXE/SPS), TXT ACM, BootGuard etc.

  • The 3rd entry (0x34-0x38, ModuleSize) determines the Module size counted in dwords, ending at the last byte of the RSA Signature. For Extra, the Module size is either 0xA1 * 4 = 0x284 or 0xE0 * 4 = 0x380. This is also evident from various other Intel structures which include similar RSA blocks such as Engine firmware (ME/TXE/SPS), TXT ACM, BootGuard etc.

  • The 4th entry (0x38-0x3A, Flags) seems to be a Flags field. At Ben's 2013 paper, Observation #5, he suggested that this flag bit may relate to the application of the update to one or multiple cores. However, I believe that it determines whether the microcode is protected by an RSA Signature, with the other 15 bits being reserved or at least unknown.

  • The 5th entry (0x3A-0x3C, RSAKeySize) seems to be the size of the RSA Public Key and Signature in multiples of 1024. So when the field equals to 2, the RSA Key Size is 2 * 1024 = 2048 bits whereas when it equals to 3, the RSA Key Size is 3 * 1024 = 3072 bits.

  • The 6th entry (0x3C-0x40, UpdateRevision) is the Update Revision. It is the only field which holds a Signed value and the MSB is used to determine whether the microcode is Production (PRD, 0) or Pre-Production (PRE, 1) tagged. For the purposes of version control & display, the entire 32-bit field is used (0-31) and not just the unsigned value of bits 1-31. It should be noted that PRD & PRE microcodes are interchangeable as they use the same RSA Public Key & Exponent for the same CPUID, something which has been verified on the field. Thus, the PRD/PRE distinction is not cross-flash restrictive like Engine firmware (ME/TXE/SPS).

  • The 7th entry (0x40-0x44, VCN) could be a Version Control Number. By comparing multiple Update Revisions of the same CPUID, it becomes clear that this field gets incremented at newer Update Revisions, thus only when important CPU issues are fixed. For example, if we take 31 microcodes with CPUID 506E3 and Platform 0x36, we can see that Update Revisions 10-1E are 0, 20-24 are 1, 2E-3A are 2, 4A-76 are 3, 7C is 4, 82-88 are 5, 8A-B2 are 6 etc. Engine firmware has a similar field called Version Control Number (VCN) which gets incremented if there is a security fix, a significant firmware change or a new feature addition. VCN is also used to control firmware upgrade/downgrade to versions which are known to be vulnerable or problematic. In the context of CPU microcodes, the 6th entry (or VCN as I decided to also call it) may be used in a similar fashion to block certain downgrades at the OS level.

  • The 8th entry (0x44-0x48, MultiPurpose1) can be used for various purposes based on microcode generation. Sometimes it is zero or equal the Update Size field (explained below) or some other type of dword counter (field * 4) starting from the Extra Header and onward (0x30+) etc. At some older Extra Headers that field may have been two words instead of one dword. Due to that field's volatility, it is hard to pinpoint its exact purpose.

  • The 9th entry (0x48-0x4C, Day-Month-Year) consists of a date in the form of Day (byte), Month (byte) and Year (word). That date is usually different (some days earlier) than the one found at the Main Header. Maybe it is the date the microcode was built and the one at the Main Header corresponds to the official release to Intel's partners.

  • The 10th entry (0x4C-0x50, UpdateSize) was described by Ben as the Update Size, meaning the amount of dwords the actual Update consists of, starting from the Extra Header (0x30+) and ending before the (also encrypted) alignment/garbage padding. As it was proven at Ben's Observation #3, there is no denying that this field is indeed the actual microcode Update Size.

  • The 11th entry (0x50-0x54, ProcessorSignatureCount) is a counter for the supported microcode Processor Signatures (CPUID). It seems to hold a maximum value of 8 CPUIDs, judging by the reserved CPUID fields which follow it, as explained below.

  • The next 8 entries (0x54-0x74, ProcessorSignature0-7) hold the CPUID values which are supported by a given microcode update. Each CPUID is a dword field which remains zero when not needed. The number 8 (0-7) for the maximum number of CPUIDs and thus ProcessorSignatureCount, is derived from multiple factors. All microcodes with ProcessorSignatureCount = 1 have the first dword populated with CPUID 0 and the following 7 are zero with the next unrelated populated dword found 0x20 bytes (8 * 4 = 0x20) later. All microcodes with ProcessorSignatureCount = 2 populate the first two dwords with CPUID 0-1 and the following 6 are zero with the next unrelated populated dword found again after 0x20 bytes. Moreover, the Platform field found at the Main, Extra and/or Extended Headers can also hold a maximum of 8 CPUs at the only-used most significant byte, as explained by Intel at their official documentation. It is worth mentioning that the presence of multiple CPUIDs at the Extra Header (ProcessorSignatureCount > 1) does not mean that the microcode features Intel's Extended Header at the end of the data. However the opposite seems to always apply, meaning that a microcode with an Extended Header will also mention the multiple CPUIDs at the Extra Header's ProcessorSignatureCount and ProcessorSignature0-7 fields. For example, microcode cpu206F0_plat05_ver00000003_2010-06-15_PRD_4722BE58 has ProcessorSignatureCount = 2 (CPUID 206F0 & 206F1) but no Extended Header at the end of the data. On the other hand, microcode cpu406A8_plat01_ver0000081F_2014-08-12_PRD_1EC9E61C has an Extended Header with two entries and both supported CPUIDs (406A8 & 406A9) can also be found at its Extra Header's ProcessorSignatureCount and ProcessorSignature0-7 fields.

  • The 20th entry (0x74-0x78, MultiPurpose2) can also be used for various purposes based on microcode generation. For this one I have narrowed it down to 4 categories. It can be a) zero or b) equal to Update Size or c) equal to Platform or d) represent the amount of dwords the entire microcode consists of, starting from the Extra Header (0x30+) but additionally including the (also encrypted) alignment/garbage padding (contrary to Update Size). It is worth noting that a select few microcodes do not fall under any of the aforementioned 4 categories but I consider these anomalies based on some other weird characteristics they have related to Update Revision, Platform, Size etc.

  • The 21st entry (0x40-0x44, SVN) could be a Security Version Number. By comparing multiple Update Revisions of the same CPUID, it becomes clear that this field gets incremented at newer Update Revisions, but at a slower pace compared to Version Control Number (VCN), thus only when very important CPU issues are fixed. For example, if we take 31 microcodes with CPUID 506E3 and Platform 0x36, we can see that Update Revisions 10-1E are 0, 20-24 are 1, 2E-7C are 2, 82-88 are 3, 8A-B2 are 4 etc. Engine firmware has a similar field called Security Version Number (SVN) which gets incremented if there is a high or critical security fix that requires a Trusted Computing Base (TCB) recovery operation, a significant event in the life cycle of the firmware which requires renewal of the security signing keys in use. SVN is also used to control firmware upgrade/downgrade to versions which are known to be vulnerable or problematic. In the context of CPU microcodes, the 20th entry (or SVN as I decided to also call it) may be used in a similar fashion to block certain downgrades at the OS level.

  • The next 5 entries (0x7C-0x90, Reserved) are always 0 so probably Reserved for future use.

  • The next 8 entries (0x90-0xB0, Unknown) seem to hold some sort of 32-byte value, maybe a 256-bit hash, but I have not been able to determine for what data. It seems it is neither affected by the Main Header nor by the (also encrypted) alignment/garbage padding. The latter is proven by various identical microcodes, when it comes to Patch data, which target different Platforms and end with different padding after the Update Size (10th entry) but have the same Unknown value. However, by comparing multiple microcodes, field Unknown is definitely related to the RSA block (Public Key, Exponent, Signature). Various checks were performed but Unknown does not seem to be directly related to the RSA Public Key & Exponent, even if it could be a good candidate for their combined hash, maybe used as part of the data of the 160/256-bit hash protected by the RSA Signature.

  • The next 64 or 96 entries (0xB0-0x1B0 or 0xB0-0x230, RSAPublicKey) hold the RSA Public Key. Its size depends on RSAKeySize field and it is either 2048-bits or 3072-bits.

  • The next entry (0x1B0-0x1B4, RSAExponent) is the RSA Exponent which is always 0x11 = 17 for 2048-bit RSA Keys. This field is missing when the RSA Key Size equals to 3072-bits. In such cases, its value is always 0x10001 = 65537.

  • The next 64 or 96 entries (0x1B4-0x2B4 or 0x230-0x3B0, RSASignature) hold the RSA Signature. Its size depends on RSAKeySize field and it is either 2048-bits or 3072-bits. When the RSA Key Size equals to 2048-bits, it includes a PKCS#1 v1.5 padded 160-bit or 256-bit Hash. When the RSA Key Size equals to 3072-bits, it includes a PKCS#1 v1.5 padded 256-bit Hash which is proceeded by some unknown (seemingly static) 0x13 sized data. Various checks were performed but I could not figure out the data which the hash represents. It is possible that Intel compares the RSA decrypted hash with the one calculated from the non-encrypted Path data. In such case, we cannot verify the integrity of a microcode update via tools such as MC Extractor, only the CPU Microcode Loader can.

Last but not least, it is worth mentioning that some Extra Header fields may exist for informational metadata purposes only which are relevant to Intel internal tools or processes and thus neither checked nor used by the CPU and its microcode Loader. That may explain why fields MultiPurpose1 & MultiPurpose2 can be used for multiple purposes based on microcode generation, why the Extra Header Date is usually different etc. The same could potentially apply to the possible SVN and VCN fields.

Extra Header Structure

In the end we have these revised Intel Extra Microcode Header structures:

For example, here is the Extra Header of cpu406A9_plat01_ver0000081F_2014-08-12_PRD_1EC9E61B:

Conclusion

We have now determined the purpose of almost all Extra Header fields:

  • ModuleType (0)
  • ModuleSubType (0)
  • ModuleSize (0x284 or 0x380)
  • Flags (0 RSA Signed, 1-31 Reserved)
  • RSAKeySize (2048-bit or 3072-bit)
  • UpdateRevision (Signed for PRD/PRE)
  • VCN (Version Control Number)
  • MultiPurpose1 (depends on generation)
  • ProcessorSignatureCount (max 8)
  • ProcessorSignature0-7 (0x20)
  • MultiPurpose2 (4 main types)
  • SVN (Security Version Number)
  • Reserved (0)
  • Unknown (256-bit Hash maybe)
Clone this wiki locally