Skip to content

Instantly share code, notes, and snippets.

@pzb
Created September 23, 2019 22:35
Show Gist options
  • Save pzb/3cd2c1550f5c625fac73fb411a5df45a to your computer and use it in GitHub Desktop.
Save pzb/3cd2c1550f5c625fac73fb411a5df45a to your computer and use it in GitHub Desktop.
#!/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