Translate

Archives

Use 010 Editor to Obtain Header Fields From Intel Microcode Binary Files

In my last blog post, I used the well-known 010 text and hex editor (010 Editor) to examine both the documented and the undocumented header of individual Intel microcode binary blobs which I extracted from a Lenovo T450 firmware update file.

I decided to see how easy or difficult it was to use the 010 Editor to output a single of header information for a group of Intel microcode blobs, i.e. all five binary blobs extracted from the Lenovo firmware update using UEFItool. This post describes my experience.

Originally I hoped to be able to just write a simple 010 Editor script to perform this task. Unfortunately this approach did not work because 010 Editor scripts do not support structure definitions. If you define and declare a structure in a script you get the following error message in the output screen:

Executing script 'C:\Users\fpm\Desktop\Script1.1sc'...
*ERROR Line 33: Structs only allowed in templates. See 'RunTemplate' function to execute a template from a script. 


Interestingly, this major limitation is not documented anywhere in the 010 Editor online manual or FAQ. The result of this limitation is that I had to come up with a combination of an 010 Editor script and template to get the functionality I desired.

Here is the 010 Editor template I used:

//
//   Authors: Finnbarr P. Murphy
//   Version: 1.0   2016-11-22
// File Mask: *.ucd
//   Purpose: Parse Intel Sandy Bridge and later microcode headers in Lenovo firmware.
//

typedef struct {
    uint32  HeaderVersion;
    int32   UpdateRevision;
    uint32  Date;
    uint32  Signature;
    uint32  CheckSum;
    uint32  LoaderVersion;
    uint32  ProcessorFlags;
    uint32  DataSize;
    uint32  TotalSize;
    uint32  reserved[3];
} INTEL_MICROCODE_HDR;


// entry point
LittleEndian();
struct INTEL_MICROCODE_HDR header <bgcolor=cLtRed>;
#define INTEL_MICROCODE_HDR_SIZE (sizeof header)

string DateStr(uint date)
{
    local string s;

    SPrintf(s, "%0.4X-%0.2X-%0.2X", date & 0xffff, date >> 24, (date >> 16) & 0xff);

    return s;
}

uint CpuFamily(uint sig)
{
    local uint fam;

    fam = (sig >> 8) & 0xf; 
    if (fam == 0xf)
        fam += (sig >> 20) & 0xff;

    return fam;
}

uint CpuMode(uint sig)
{
    local uint fam, model;
    
    fam = CpuFamily(sig);
    model = (sig >> 4) & 0xf;
    if (fam >= 0x6)
       model += ((sig >> 16) & 0xf) << 4;

    return model;
}
 
uint CpuStepping(uint sig)
{
    return sig & 0xf;
}

string BinStr(uint pf)
{
    local string s;

    s = IntToBinaryStr(pf);

    return s;
}


Basically, it is a cut-down version of the template I used on my previous post. \being somewhat lazy, I left some helper functions here rather than moving them to my 010 Editor script.

Here is the 010 Editor script I developed for this exercise:

//
//   Authors: Finnbarr P. Murphy
//   Version: 1.0   2016-11-22
//   Purpose: Output certain header information for a list of Intel microcode files.
//


// variables
int   len, listfile;
int64 listfile_offset = 0;
char  filename[512], listfilename[512];

char outfilename[512];
int outfile = FileNew(); 
int64 outfile_offset = 0;

char cwd[512], filename1[512];
char outline[512];


// Input text file containing a list of files to process
listfilename = InputOpenFileName( "Choose file contacting list of Intel Microcode files", 
    "(*.lst)" );
if( Strlen( listfilename ) == 0 )
    return -1;

// Open text file containing a list of files
if( FileOpen( listfilename ) < 0 )
    return -1;
listfile = GetFileNum();

// get current working directory
cwd = FileNameGetPath( GetFileName(), true );

// Loop through the list of microcode files
while(!FEof()) {

    // Get filename from list - remove end of line characters
    filename = ReadLine( FTell() );
    len = Strlen( filename );
    FSkip( len );
    listfile_offset = FTell();
    if( len > 0 && filename[len-1] == 0xA )
         filename[len-1] = 0;
    if( len > 1 && filename[len-2] == 0xD )
         filename[len-2] = 0;
    SPrintf(filename1, "%s%s", cwd, filename);
    
    // Something is wrong with the specified microcode file
    if( FileOpen( filename1, FALSE ) < 0 ) {
         FileSelect( listfile );
         FileClose();
         Exit(-1);
    }

    // Use a template to get the relevant information
    RunTemplate("Intel_Microcode_Min.bt");
    SPrintf( outline, "%s %s %d/%d/%d %s\r\n", filename, 
             DateStr(header.Date), CpuFamily(header.Signature), 
             CpuMode(header.Signature), CpuStepping(header.Signature),
             BinStr(header.ProcessorFlags));
    FileClose();

    // Switch to output file and append line
    FileSelect(outfile);
    FSeek(outfile_offset);
    FPrintf(outfile, outline);
    outfile_offset = FTell();

    // switch back to list file
    FileSelect( listfile );
    FSeek(listfile_offset);
}

// Close the list file
FileSelect(listfile);
FileClose();

// Assign a filename to output file and close it
outfilename=InputSaveFileName("Output filename","(*.out);"); 
FileSelect(outfile);
FileSave(outfilename);
FileClose();


As you can, 010 Editor scripts, like templates, are written in a very C language-like syntax. The second thing you will probably notice if you are a reasonably proficient C programmer is the switching of file handles and the saving and restored of file offsets as the script works on different files. According to the 010 Editor online manual

Only one file can be active at a time and all of the Read/Write operations occur on 
that file. Use FileSelect to select a different file to be the current file. The 
files are numbered from 0 up to FileCount()-1. See GetFileNum to get the index of 
the current file.


Further, it is up to the script developer to keep track of each file’s offset and save/restore it as necessary. This is a very serious limitation to using sophisticated 010 Editor scripts involving multiple files.

Here is the contents of the input file, i.e. the file containing a list of the Intel microcode binary blobs:

Intel_microcode1.ucd
Intel_microcode2.ucd
Intel_microcode3.ucd
Intel_microcode4.ucd
Intel_microcode5.ucd


I named this file ucd.lst.

Here is what you see when you load the script into the 010 editor and invoke it using F7:

Here is what you should see when the script loops through all the Intel microcode binary blobs listed in ucd.lst:

Note the output from the script does not appear in the output pane but in an “untitled” pane. For the purpose of this exercise. the output pane contains no useful output and it would be useful if there was an API to silence such spurious output such as “Executing script”, “Executing template”, etc. I could have sent the script output to the output pane using Printf statements and then saved the output pane contents to a file using OutputPaneSave. The problem with this approach is that the output would have been intermingled with all the spurious output.

Why the need to figure out the current working directory and append it to the microcode binary blog filename? Well, it turns out that the 010 Editor defaults to resolving file pathnames in a slightly unusual manner. See the 010 Editor Directory Options dialog for more information.

Here is what is saved in the specified output file:

Intel_microcode1.ucd 2013-12-19 6/61/2 11110010
Intel_microcode2.ucd 2014-07-02 6/61/3 11000000
Intel_microcode3.ucd 2016-04-29 6/61/4 11000000
Intel_microcode4.ucd 2012-12-06 6/69/0 01110010
Intel_microcode5.ucd 2016-04-01 6/69/1 01110010


The outputted columns are (1) Filename, (2) Release date, (3) CPU family/model/stepping and (4) CPU processor flags in binary format.

My experience in using the 010 Editor for this task exposed a number of serious shortcomings in the design of the product as far as I am concerned. These shortcomings did not prevent me from achieving the result I wanted but they certainly made it far more difficult to do so than I anticipated.

I hope this blog post helps you to understand the limitations of the current version of the 010 Editor.

P.S. I am no 010 Editor expert. If you know of a better way of doing things, please let me know.

Comments are closed.