Last active
July 14, 2022 16:59
-
-
Save liske/6743d6dc692dead93228848dc480281c to your computer and use it in GitHub Desktop.
PoC - Check for matching AMD microcode updates.
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 perl | |
# Copyright Holder: | |
# 2019 (C) Thomas Liske [http://fiasko-nw.net/~thomas/] | |
# | |
# License: | |
# This program is free software; you can redistribute it and/or modify | |
# it under the terms of the GNU General Public License as published by | |
# the Free Software Foundation; either version 2 of the License, or | |
# (at your option) any later version. | |
# | |
# | |
# This script is based on the result of the following paper: | |
# | |
# Security Analysis of x86 Processor Microcode | |
# Daming D. Chen <[email protected]> | |
# Gail-Joon Ahn <[email protected]> | |
# | |
# https://www.dcddcc.com/docs/2014_paper_microcode.pdf | |
# | |
# | |
# The CPUID building from /proc/cpuinfo is based on the script `cpuinfo2cpuid` | |
# from the cpuid package written by | |
# | |
# Todd Allen <[email protected]> | |
# http://www.etallen.com/cpuid.html | |
# | |
use File::Basename; | |
use warnings; | |
use strict; | |
print("-=======[ SYSTEM ]=======-\n"); | |
# get CPUID of cpu0 using kernel module | |
my $cpuid; | |
if ( open( my $fh, '<:raw', '/dev/cpu/0/cpuid' ) ) { | |
seek( $fh, 1, 0 ); | |
read( $fh, my $eax, 16 ); | |
close($fh); | |
$cpuid = unpack( 'V', $eax ); | |
printf( "CPUID=%08x (/dev/cpu/0/cpuid)\n", $cpuid ); | |
} | |
else { | |
warn "Failed to read CPUID (Missed \`modprobe cpuid\`?): $!\n"; | |
} | |
# get CPUID from /proc/cpuinfo | |
{ | |
open( my $fh, '<', '/proc/cpuinfo' ) | |
|| die "Failed to read /proc/cpuinfo: $!\n"; | |
my $vendor = ''; | |
my $family = ''; | |
my $model = ''; | |
my $stepping = ''; | |
# Read cpuinfo from STDIN | |
while (<$fh>) { | |
if (/^vendor_id\s*: (.*)$/) { | |
$vendor = $1; | |
} | |
if (/^cpu family\s*: (.*)$/) { | |
$family = $1; | |
} | |
if (/^model\s*: (.*)$/) { | |
$model = $1; | |
} | |
if (/^stepping\s*: (.*)$/) { | |
$stepping = $1; | |
} | |
# stop after cpu0 | |
last if(/^$/); | |
} | |
my $xfamily = 0; | |
if($family > 0xf) { | |
$xfamily = $family - 0xf; | |
$family = 0xf; | |
} | |
my $xmodel = $model >> 4; | |
$model = $model & 0xf; | |
my $eax = | |
( ( ( $xfamily & 0xff ) << 20 ) + | |
( ( $xmodel & 0xf ) << 16 ) + | |
( ( $family & 0xf ) << 8 ) + | |
( ( $model & 0xf ) << 4 ) + | |
( ( $stepping & 0xf ) << 0 ) ); | |
printf( "CPUID=%08x (/proc/cpuinfo)\n", $eax); | |
if($cpuid) { | |
if ($cpuid != $eax) { | |
warn "CPUID mismatch!\n"; | |
} | |
} | |
else { | |
$cpuid = $eax; | |
} | |
} | |
# get microcode version of cpu0 | |
open( my $fh, '<', '/proc/cpuinfo' ); | |
my ($ucode) = grep { s/^microcode\s*:\s+(\S+)\s*/$1/s; } <$fh>; | |
close($fh); | |
$ucode = hex($ucode); | |
printf( "UCODE=%08x\n\n", $ucode ); | |
# scan AMD ucode files | |
foreach my $fn (</lib/firmware/amd-ucode/microcode_*.bin>) { | |
my $bn = basename( $fn, '.bin' ); | |
print("\n=======[ $bn ]=======-\n"); | |
open( $fh, '<:raw', $fn ) | |
|| die "Failed to open ucode source file '$fn': $!\n"; | |
my @stat = stat($fh); | |
my $fpos = read( $fh, my $buf, 12 ); | |
my ( $hdr_magic, $hdr_type, $hdr_size ) = unpack( 'a4VV', $buf ); | |
die "Invalid magic header ($hdr_magic)!\n" if ( $hdr_magic ne "DMA\0" ); | |
die "Unsupported table type $hdr_type!\n" if ( $hdr_type != 0 ); | |
print("File size = $stat[7]\n"); | |
print("Table size = $hdr_size\n\n"); | |
my $prid; | |
for ( ; $fpos < $hdr_size ; ) { | |
$fpos += read( $fh, $buf, 16 ); | |
my ( $pkg_cpuid, $pkg_errmask, $pkg_errcomp, $pkg_prid, $pkg_unk ) = | |
unpack( 'VVVvv', $buf ); | |
if ( $pkg_cpuid > 0 ) { | |
my $mark = ''; | |
# check ucode version if CPUID does match | |
if ( $cpuid == $pkg_cpuid ) { | |
$prid = $pkg_prid; | |
$mark = ' <<'; | |
} | |
printf( "CPUID=%08x PRID=%04x%s\n", $pkg_cpuid, $pkg_prid, $mark ); | |
} | |
} | |
print("\n"); | |
for ( ; $fpos < $stat[7] ; ) { | |
$fpos += read( $fh, $buf, 8 ); | |
my ( $upd_type, $upd_size ) = unpack( 'VV', $buf ); | |
$fpos += read( $fh, $buf, $upd_size ); | |
my ( | |
$pat_date, $pat_pid, $pat_did, $pat_dlen, $pat_iflg, | |
$pat_dchk, $pat_ndid, $pat_sdid, $pat_prid | |
) = unpack( 'VVvCCVVVv', $buf ); | |
my $mark = ''; | |
if ( $prid && $pat_prid == $prid ) { | |
if ( $pat_pid == $ucode ) { | |
$mark = " UP-TO-DATE"; | |
} | |
elsif ( $pat_pid > $ucode ) { | |
$mark = " UPDATE-AVAIL"; | |
} | |
elsif ( $pat_pid < $ucode ) { | |
$mark = " OUTDATED"; | |
} | |
} | |
printf( "PRID=%04x PID=%08x%s\n", $pat_prid, $pat_pid, $mark ); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment