forked from linuxboot/fiano
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add intel me structures from CSS (linuxboot#334)
* pkg/me: Add parser for the Intel ME Flash Partition Table The flash partition table $FPT describes the partitions found in the ME region. The new API allows basic detection, enumeration and modification support for those partitions. To be used to patch the UEP partition with the KM hash. Based on Igor Skochinsky talk "Rootkit in your laptop" and ME Analyzer written by Plato Mavropoulos. Signed-off-by: Patrick Rudolph <[email protected]> Move intel me file from pkg/me to pkg/intel/me Signed-off-by: Christopher Meis <[email protected]> Get rid of 9elements/converged-security-suite dependency Signed-off-by: Christopher Meis <[email protected]> correct formatting verbs Signed-off-by: Christopher Meis <[email protected]> Remove spaces and correct return statements Signed-off-by: Christopher Meis <[email protected]> * Refactor new me parsing Add tests for me parsing Signed-off-by: Christopher Meis <[email protected]> Co-authored-by: Patrick Rudolph <[email protected]>
- Loading branch information
1 parent
75b23bb
commit 43cb739
Showing
3 changed files
with
367 additions
and
0 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
// Copyright 2021 the LinuxBoot Authors. All rights reserved | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package me | ||
|
||
import ( | ||
"bytes" | ||
"encoding/binary" | ||
"io" | ||
) | ||
|
||
var ( | ||
Signature = [4]byte{0x24, 0x46, 0x50, 0x54} | ||
) | ||
|
||
func parseLegacyFlashPartitionTableHeader(r io.Reader) (*LegacyFlashPartitionTableHeader, error) { | ||
var header LegacyFlashPartitionTableHeader | ||
var scrap [12]byte | ||
if err := binary.Read(r, binary.LittleEndian, &scrap); err != nil { | ||
return nil, err | ||
} | ||
if err := binary.Read(r, binary.LittleEndian, &header.Marker); err != nil { | ||
return nil, err | ||
} | ||
if err := binary.Read(r, binary.LittleEndian, &header.NumFptEntries); err != nil { | ||
return nil, err | ||
} | ||
if err := binary.Read(r, binary.LittleEndian, &header.HeaderVersion); err != nil { | ||
return nil, err | ||
} | ||
if err := binary.Read(r, binary.LittleEndian, &header.EntryVersion); err != nil { | ||
return nil, err | ||
} | ||
if err := binary.Read(r, binary.LittleEndian, &header.HeaderLength); err != nil { | ||
return nil, err | ||
} | ||
if err := binary.Read(r, binary.LittleEndian, &header.HeaderChecksum); err != nil { | ||
return nil, err | ||
} | ||
if err := binary.Read(r, binary.LittleEndian, &header.TicksToAdd); err != nil { | ||
return nil, err | ||
} | ||
if err := binary.Read(r, binary.LittleEndian, &header.TokensToAdd); err != nil { | ||
return nil, err | ||
} | ||
if err := binary.Read(r, binary.LittleEndian, &header.UMASize); err != nil { | ||
return nil, err | ||
} | ||
if err := binary.Read(r, binary.LittleEndian, &header.Flags); err != nil { | ||
return nil, err | ||
} | ||
return &header, nil | ||
|
||
} | ||
|
||
func parseFlashPartitionTableHeader(r io.Reader) (*FlashPartitionTableHeader, error) { | ||
var header FlashPartitionTableHeader | ||
// Set Signature | ||
header.Marker = Signature | ||
|
||
if err := binary.Read(r, binary.LittleEndian, &header.NumFptEntries); err != nil { | ||
return nil, err | ||
} | ||
if err := binary.Read(r, binary.LittleEndian, &header.HeaderVersion); err != nil { | ||
return nil, err | ||
} | ||
if err := binary.Read(r, binary.LittleEndian, &header.EntryVersion); err != nil { | ||
return nil, err | ||
} | ||
if err := binary.Read(r, binary.LittleEndian, &header.HeaderLength); err != nil { | ||
return nil, err | ||
} | ||
if err := binary.Read(r, binary.LittleEndian, &header.HeaderChecksum); err != nil { | ||
return nil, err | ||
} | ||
if err := binary.Read(r, binary.LittleEndian, &header.TicksToAdd); err != nil { | ||
return nil, err | ||
} | ||
if err := binary.Read(r, binary.LittleEndian, &header.TokensToAdd); err != nil { | ||
return nil, err | ||
} | ||
if err := binary.Read(r, binary.LittleEndian, &header.UMASizeOrReserved); err != nil { | ||
return nil, err | ||
} | ||
if err := binary.Read(r, binary.LittleEndian, &header.FlashLayoutOrFlags); err != nil { | ||
return nil, err | ||
} | ||
if err := binary.Read(r, binary.LittleEndian, &header.FitcMajor); err != nil { | ||
return nil, err | ||
} | ||
if err := binary.Read(r, binary.LittleEndian, &header.FitcMinor); err != nil { | ||
return nil, err | ||
} | ||
if err := binary.Read(r, binary.LittleEndian, &header.FitcHotfix); err != nil { | ||
return nil, err | ||
} | ||
if err := binary.Read(r, binary.LittleEndian, &header.FitcBuild); err != nil { | ||
return nil, err | ||
} | ||
|
||
return &header, nil | ||
} | ||
|
||
func parseEntry(r io.Reader) (*FlashPartitionTableEntry, error) { | ||
var entry FlashPartitionTableEntry | ||
if err := binary.Read(r, binary.LittleEndian, &entry); err != nil { | ||
return nil, err | ||
} | ||
return &entry, nil | ||
} | ||
|
||
// ParseIntelFirmware parses the Intel firmware image by uefi.Firmware interface` | ||
func ParseIntelME(r io.Reader) (*IntelME, error) { | ||
var me IntelME | ||
me.legacy = false | ||
var numEntries uint32 | ||
|
||
// Read first 4 byte, we catch the marker as prefix or suffix | ||
var markerarea [4]byte | ||
if err := binary.Read(r, binary.LittleEndian, &markerarea); err != nil { | ||
return nil, err | ||
} | ||
|
||
// Check on new header | ||
if bytes.HasPrefix(markerarea[:], Signature[:]) { | ||
hdr, err := parseFlashPartitionTableHeader(r) | ||
if err != nil { | ||
return nil, err | ||
} | ||
me.hdr = hdr | ||
numEntries = hdr.NumFptEntries | ||
} else { | ||
me.legacy = true | ||
hdr, err := parseLegacyFlashPartitionTableHeader(r) | ||
if err != nil { | ||
return nil, err | ||
} | ||
me.legacyhdr = hdr | ||
numEntries = hdr.NumFptEntries | ||
} | ||
|
||
partitions := make([]FlashPartitionTableEntry, 0) | ||
for i := uint32(0); i < numEntries; i++ { | ||
entry, err := parseEntry(r) | ||
if err != nil { | ||
return nil, err | ||
} | ||
partitions = append(partitions, *entry) | ||
} | ||
me.partitions = partitions | ||
return &me, nil | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
// Copyright 2021 the LinuxBoot Authors. All rights reserved | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package me | ||
|
||
import ( | ||
"bytes" | ||
"errors" | ||
"fmt" | ||
"testing" | ||
) | ||
|
||
var ( | ||
validLegacyHeaderPadding = []byte{ | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
} | ||
validME = []byte{ | ||
0x24, 0x46, 0x50, 0x54, 0x02, 0x00, 0x00, 0x00, | ||
0x20, 0x01, 0x20, 0x30, 0x00, 0x01, 0x01, 0x01, | ||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
// first entry | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
// second entry | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
} | ||
invalidMEEntryNumber = []byte{ | ||
0x24, 0x46, 0x50, 0x54, 0x07, 0x00, 0x00, 0x00, | ||
0x20, 0x01, 0x20, 0x30, 0x00, 0x01, 0x01, 0x01, | ||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
// first entry | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
// second entry | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||
} | ||
) | ||
|
||
func TestParseIntelME(t *testing.T) { | ||
for _, tt := range []struct { | ||
name string | ||
data []byte | ||
wantErr error | ||
}{ | ||
{ | ||
name: "Test Lagacy ME", | ||
data: append(validLegacyHeaderPadding, validME...), | ||
}, | ||
{ | ||
name: "Test ME", | ||
data: validME, | ||
}, | ||
{ | ||
name: "Invalid Entry number", | ||
data: invalidMEEntryNumber, | ||
wantErr: errors.New("EOF"), | ||
}, | ||
} { | ||
t.Run(tt.name, func(t *testing.T) { | ||
_, gotErr := ParseIntelME(bytes.NewReader(tt.data)) | ||
if gotErrorString, wantErrorString := fmt.Sprint(gotErr), fmt.Sprint(tt.wantErr); gotErrorString != wantErrorString { | ||
t.Errorf("ParseIntelME() got err %q; want err %q", | ||
gotErrorString, | ||
wantErrorString) | ||
} | ||
}) | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
// Copyright 2021 the LinuxBoot Authors. All rights reserved | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package me | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
) | ||
|
||
// LegacyFlashPartitionTableHeader describes the old flash partition table header | ||
// in Intel ME binaries. | ||
type LegacyFlashPartitionTableHeader struct { | ||
Padding [16]byte // 16 zeros | ||
Marker [4]byte // Always $FPT | ||
NumFptEntries uint32 | ||
HeaderVersion uint8 | ||
EntryVersion uint8 | ||
HeaderLength uint8 // Usually 0x30 | ||
HeaderChecksum uint8 | ||
TicksToAdd uint16 | ||
TokensToAdd uint16 | ||
UMASize uint32 | ||
Flags uint32 | ||
} | ||
|
||
func (h LegacyFlashPartitionTableHeader) String() string { | ||
var b strings.Builder | ||
b.WriteString("Flash partition table:\n") | ||
fmt.Fprintf(&b, " Entries : %d\n", h.NumFptEntries) | ||
fmt.Fprintf(&b, " HeaderVersion : 0x%x\n", h.HeaderVersion) | ||
fmt.Fprintf(&b, " EntryVersion : 0x%x\n", h.EntryVersion) | ||
fmt.Fprintf(&b, " HeaderLength : 0x%x\n", h.HeaderLength) | ||
fmt.Fprintf(&b, " HeaderChecksum: 0x%x\n", h.HeaderChecksum) | ||
fmt.Fprintf(&b, " TicksToAdd : 0x%x\n", h.TicksToAdd) | ||
fmt.Fprintf(&b, " TokensToAdd : 0x%x\n", h.TokensToAdd) | ||
fmt.Fprintf(&b, " UMASize : 0x%x\n", h.UMASize) | ||
fmt.Fprintf(&b, " Flags : 0x%x\n", h.Flags) | ||
|
||
return b.String() | ||
} | ||
|
||
// FlashPartitionTableHeader describes the new flash partition table header | ||
// in Intel ME binaries. | ||
type FlashPartitionTableHeader struct { | ||
Marker [4]byte // Always $FPT | ||
NumFptEntries uint32 | ||
HeaderVersion uint8 // Only support 2.0 | ||
EntryVersion uint8 | ||
HeaderLength uint8 // Usually 0x20 | ||
HeaderChecksum uint8 | ||
TicksToAdd uint16 | ||
TokensToAdd uint16 | ||
UMASizeOrReserved uint32 | ||
FlashLayoutOrFlags uint32 | ||
// Not Present in ME version 7 | ||
FitcMajor uint16 | ||
FitcMinor uint16 | ||
FitcHotfix uint16 | ||
FitcBuild uint16 | ||
} | ||
|
||
func (h FlashPartitionTableHeader) String() string { | ||
var b strings.Builder | ||
|
||
b.WriteString("Flash partition table:\n") | ||
fmt.Fprintf(&b, " Entries : %d\n", h.NumFptEntries) | ||
fmt.Fprintf(&b, " HeaderVersion : 0x%x\n", h.HeaderVersion) | ||
fmt.Fprintf(&b, " EntryVersion : 0x%x\n", h.EntryVersion) | ||
fmt.Fprintf(&b, " HeaderLength : 0x%x\n", h.HeaderLength) | ||
fmt.Fprintf(&b, " HeaderChecksum : 0x%x\n", h.HeaderChecksum) | ||
fmt.Fprintf(&b, " TicksToAdd : 0x%x\n", h.TicksToAdd) | ||
fmt.Fprintf(&b, " TokensToAdd : 0x%x\n", h.TokensToAdd) | ||
fmt.Fprintf(&b, " UMASizeOrReserved : 0x%x\n", h.UMASizeOrReserved) | ||
fmt.Fprintf(&b, " FlashLayoutOrFlags : 0x%x\n", h.FlashLayoutOrFlags) | ||
fmt.Fprintf(&b, " Fitc Version : %d.%d.%d.%d\n", h.FitcMajor, h.FitcMinor, h.FitcHotfix, h.FitcBuild) | ||
|
||
return b.String() | ||
} | ||
|
||
type name [4]byte | ||
|
||
func (n *name) String() string { | ||
return string(n[:]) | ||
} | ||
|
||
// FlashPartitionTableEntry describes information of a flash partition table entry. | ||
type FlashPartitionTableEntry struct { | ||
Name name | ||
Owner name | ||
Offset uint32 | ||
Length uint32 | ||
StartTokens uint32 | ||
MaxTokens uint32 | ||
ScratchSectors uint32 | ||
Flags uint32 | ||
} | ||
|
||
func (e FlashPartitionTableEntry) String() string { | ||
var b strings.Builder | ||
b.WriteString("Flash partition entry:\n") | ||
fmt.Fprintf(&b, " Name : %s\n", e.Name.String()) | ||
fmt.Fprintf(&b, " Owner : %s\n", e.Owner.String()) | ||
fmt.Fprintf(&b, " Offset : 0x%x\n", e.Offset) | ||
fmt.Fprintf(&b, " Length : 0x%x\n", e.Length) | ||
fmt.Fprintf(&b, " StartTokens : 0x%x\n", e.StartTokens) | ||
fmt.Fprintf(&b, " MaxTokens : 0x%x\n", e.MaxTokens) | ||
fmt.Fprintf(&b, " ScratchSectors: 0x%x\n", e.ScratchSectors) | ||
fmt.Fprintf(&b, " Flags : 0x%x\n", e.Flags) | ||
|
||
if e.Flags>>24 == 0xff { | ||
b.WriteString(" Valid : No\n") | ||
} else { | ||
b.WriteString(" Valid : yes\n") | ||
} | ||
if e.Flags&1 > 0 { | ||
b.WriteString(" Partition : Data\n") | ||
} else { | ||
b.WriteString(" Partition : Code\n") | ||
} | ||
|
||
return b.String() | ||
} | ||
|
||
// IntelME abstracts the ME/CSME/SPS firmware found on intel platforms | ||
type IntelME struct { | ||
hdr *FlashPartitionTableHeader | ||
legacyhdr *LegacyFlashPartitionTableHeader | ||
legacy bool | ||
partitions []FlashPartitionTableEntry | ||
} |