-
Notifications
You must be signed in to change notification settings - Fork 117
Intel Microcode Extra Undocumented Header
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 990 microcodes which incorporate the Extra Header from 2006-03 to 2017-11.
The Extra Header starts at offset 0x30, exactly after the official documented Main Header.
-
The 1st entry (0x30-0x34, HeaderType) determines the type of the Header which for Extra is always zero. This is evident from various other Intel structures which include similar RSA blocks such as Engine firmware (ME/TXE/SPS), BootGuard etc.
-
The 2nd entry (0x34-0x38, HeaderSize) determines the Header size counted in dwords, ending at the last byte of the RSA Signature. For Extra, the Header size is always 0xA1 * 4 = 0x284. This is also evident from various other Intel structures which include similar RSA blocks such as Engine firmware (ME/TXE/SPS), BootGuard etc.
-
The 3rd entry (0x38-0x3A, Flags) seems to be a Flags field. At Ben's 2013 paper, Observation #5, he notices that changing the first/only-used bit from 1 to 0 resulted in significantly lower update cycle count before failure. Because the decrease seemed proportional to the number of CPU cores, 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. This conclusion is derived from one unique Pre-Production microcode which has zero/null Unknown Hash, RSA Public Key & RSA Signature fields and also a value of 0, compared to the expected 1, at the first Flags bit.
-
The 4th entry (0x3A-0x3C, Unknown1) is always equal to 0002 and its purpose is unknown. Since that fields value is always 2, it seems likely that it could represent some sort of version tag as in "2nd microcode structure revision" or similar. It could also be an indicator of what type of protection is used at the microcode file. A hypothetical example would be 1 = RSA Signature covers Header + Encrypted Patch, 2 = RSA Signature covers Header + Patch and so on.
-
The 5th 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 6th entry (0x40-0x44, VCN) is the 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 7th entry (0x44-0x48, Unknown2) 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 8th 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 9th 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 10th 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 19th entry (0x74-0x78, Unknown3) 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 20th entry (0x40-0x44, SVN) is the 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, Unknown4-8) are always zero so probably Reserved for future use.
-
The next 8 entries (0x90-0xB0, Unknown9) seem to hold some sort of 32-byte value, maybe a 256-bit SHA-2 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 (9th entry) but have the same Unknown9 value. However, by comparing multiple microcodes, field Unknown9 is definitely related to the RSA block (Public Key, Exponent, Signature) as it is also zero/null at that unique Pre-Production microcode mentioned at the 3rd (Flags) entry above. Various checks were performed but Unknown9 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 one protected by the RSA Signature.
-
The next 64 entries (0xB0-0x1B0, RSAPublicKey) hold the RSA Public Key. It is noteworthy that the RSA Public Key is zero/null at that unique Pre-Production microcode mentioned at the 3rd (Flags) field above.
-
The next entry (0x1B0-0x1B4, RSAExponent) is the RSA Exponent which is always 0x11 = 17. It is noteworthy that the RSA Exponent is the only non-zero/null RSA block field of that unique Pre-Production microcode mentioned at the 3rd (Flags) field above.
-
The next 64 entries (0x1B4-0x2B4, RSASignature) hold the RSA Signature. It is noteworthy that the RSA Signature is zero/null at that unique Pre-Production microcode mentioned at the 3rd (Flags) field above.
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 Unknown2 & Unknown3 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.
In the end we have this revised Intel Extra Microcode Header structure:
For example, here is the Extra Header of cpu906EB_plat02_ver00000072_2017-09-20_PRD_A08C2841:
We have now determined the purpose of a lot more Extra Header fields:
- HeaderType (0)
- HeaderSize (0x284)
- Flags (0 RSA Signed, 1-31 Reserved)
- Unknown1 (protection type tag maybe)
- UpdateRevision (Signed for PRD/PRE)
- VCN (Version Control Number)
- Unknown2 (Multi Purpose 1, depends)
- ProcessorSignatureCount (max 8)
- ProcessorSignature0-7 (0x20)
- Unknown3 (Multi Purpose 2, 4 types)
- SVN (Security Version Number)
- Unknown4-8 (Reserved)
- Unknown9 (256-bit Hash maybe)


