Created
September 23, 2019 22:35
-
-
Save pzb/3cd2c1550f5c625fac73fb411a5df45a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
def hex_to_bin(s) | |
[s].pack('H*') | |
end | |
def incline_to_bin(s) | |
# semicolon indicates a commment | |
# comment can be alone on a line or at the end of the line | |
l = s.strip.sub(/(^|\s);.*$/,'') | |
return '' if l.empty? | |
# Test generates captures as a side effect | |
if l !~ /\Add\s0([0-9a-f]{8})h\z/ | |
raise "Bad line: '#{s}'" | |
end | |
hex_to_bin($1) | |
end | |
def inc_to_bin(s) | |
o = String.new.force_encoding('BINARY') | |
s.each_line do |l| | |
o += incline_to_bin(l) | |
end | |
o | |
end | |
# Process .inc file and get the binary | |
puts ARGV[0] | |
inc = File.read(ARGV[0]) | |
bin = inc_to_bin(inc) | |
if bin.length < 48 | |
raise "Too short" | |
end | |
header = bin[0..47] | |
rest = bin[48..-1] | |
header_version = bin[0..3] | |
if header_version != "\x00\x00\x00\x01".b | |
raise "Unknown header version: #{header_version}" | |
end | |
update_revision = bin[4..7].unpack('L>').first | |
date = bin[8..11].unpack('C*') | |
processor_signature = bin[12..15].unpack('L>').first | |
checksum = bin[16..19].unpack('L>').first | |
loader_revision = bin[20..23].unpack('L>').first | |
if loader_revision != 1 | |
raise "Unknown loader revision: #{loader_revision}" | |
end | |
processor_flags = bin[24..27].unpack('L>').first | |
if processor_flags & 0xffffff00 != 0 | |
raise "Unknown processor flag bits set: #{processor_flags}" | |
end | |
data_size = bin[28..31].unpack('L>').first | |
if data_size == 0 | |
data_size = 2000 | |
end | |
total_size = bin[32..35].unpack('L>').first | |
if total_size == 0 | |
total_size = 2048 | |
end | |
if bin.length != total_size | |
raise "Size error: #{total_size} != #{bin.length}" | |
end | |
if (total_size/1024)*1024 != total_size | |
raise "Total size is not a multiple of 1024: #{total_size}" | |
end | |
extended_sigs = false | |
ext_sig_data = nil | |
if total_size > (data_size + 48) | |
extended_sigs = true | |
puts "EXT SIG" | |
ext_sig_data = bin[data_size+48..-1] | |
end | |
reserved = bin[36..47] | |
if reserved != "\x00".b*12 | |
raise "Reserved used" | |
end | |
# Validate checksum | |
c = 0 | |
bin.unpack('L>*').each do |n| | |
c += n | |
if c >= 0x0100000000 | |
c -= 0x0100000000 | |
end | |
end | |
if c != 0 | |
raise "Bad checksum" | |
end | |
# http://inertiawar.com/microcode/ was a starting point | |
# https://github.com/platomav/MCExtractor/wiki/Intel-Microcode-Extra-Undocumented-Header has more info | |
# Opaque seems to have a 128 byte header | |
# followed by a 2048-bit key and a 32 bit modulus | |
# followed by a 2048-bit signature | |
# which is 644 bytes total | |
# Module Type/SubType (both 0) | |
module_type = bin[0x30..0x31].unpack('S>').first | |
if module_type != 0 | |
raise "Module Type is #{module_type.to_s(16)}" | |
end | |
module_subtype = bin[0x32..0x33].unpack('S>').first | |
if module_subtype != 0 | |
raise "Module Subtype is #{module_subtype.to_s(16)}" | |
end | |
module_size_dwords = bin[0x34..0x37].unpack('L>').first | |
if (module_size_dwords != 0xa1) && (module_size_dwords != 0xe0) | |
raise "Module Size is #{module_size_dwords}" | |
end | |
opaque_header_end = 0x30 + (module_size_dwords * 4) - 1 | |
opaque_header = bin[0x30..opaque_header_end] | |
flags = opaque_header[8..9].unpack('S>').first | |
if flags != 2 | |
raise "Flags is #{flags.to_s(2)}" | |
end | |
keysize = opaque_header[10..11].unpack('S>').first | |
if module_size_dwords == 0xa1 | |
if keysize != 2 && keysize != 1 | |
raise "Keysize is #{keysize} with module size of #{module_size_dwords} dwords" | |
end | |
else # module_size_dwords == 0xe0 | |
if keysize != 3 | |
raise "Keysize is #{keysize} with module size of #{module_size_dwords} dwords" | |
end | |
end | |
h4 = opaque_header[12..15].unpack('L>').first | |
if h4 != update_revision | |
raise "Opaque H4 is not update revision: #{h4} != #{update_revision}" | |
end | |
vcn = opaque_header[16..19].unpack('L>').first | |
h6 = opaque_header[20..23].unpack('L>').first | |
inside_date = opaque_header[24..27].unpack('C*') | |
# The real length (in DWORDS, or 4 byte chunks) | |
h8 = opaque_header[28..31].unpack('L>').first * 4 | |
if h8 < 1 | |
raise "H8 is zero: #{h8}" | |
end | |
if h8 > (bin.length) - 0x30 | |
raise "H8 is longer than ucode" | |
end | |
#fill = opaque[h8..-1] | |
#opaque = opaque[opaque_header_len..h8-1] | |
# H9 is the number of processor signatures | |
h9 = opaque_header[32..35].unpack('L>').first | |
if h9 > 1 | |
if !extended_sigs | |
raise "Multiple processors but no extended signature" | |
elsif h9 > 2 | |
raise "More than two processors supported in #{ARGV[0]}" | |
end | |
end | |
h10 = opaque_header[36..39].unpack('L>').first | |
if h10 != processor_signature | |
raise "Processor signature mismatch" | |
end | |
# Reserved space for processor sig | |
if h9 == 1 | |
h11 = opaque_header[40..43].unpack('L>').first | |
if h11 != 0 | |
raise "Reserved processer sig is not 0" | |
end | |
end | |
h12 = opaque_header[44..67].unpack('L>*') | |
if !h12.all?{|n| n == 0} | |
raise "Reserved processor sigs are not 0" | |
end | |
# Unknown | |
h13 = opaque_header[68..71].unpack('L>').first | |
svn = opaque_header[72..75].unpack('L>').first | |
h15 = opaque_header[76..95].unpack('L>*') | |
if !h15.all?{|n| n == 0} | |
raise "Reserved unknown are not 0" | |
end | |
# The last 256 bits (32 bytes) of the 128 byte header | |
# are some sort of checksum | |
modulus = opaque_header[384..387].unpack('L>').first | |
if modulus != 17 | |
raise "Modulus is not 17! #{modulus}" | |
end | |
puts "#{ARGV[0]} has #{bin.length - 0x30} bytes of microcode for #{processor_signature.to_s(16)} rev #{update_revision.to_s(16)}" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment