mirror of
https://github.com/google/go-attestation.git
synced 2025-06-04 00:10:53 +00:00
windows events: Extract the ELAM driver names and their configuration (#173)
This commit is contained in:
parent
fe22f29ec8
commit
6d760d44a1
@ -21,6 +21,8 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"strings"
|
||||||
|
"unicode/utf16"
|
||||||
|
|
||||||
"github.com/google/go-attestation/attest/internal"
|
"github.com/google/go-attestation/attest/internal"
|
||||||
)
|
)
|
||||||
@ -142,6 +144,9 @@ type WinEvents struct {
|
|||||||
// LoadedModules contains authenticode hashes for binaries which
|
// LoadedModules contains authenticode hashes for binaries which
|
||||||
// were loaded during boot.
|
// were loaded during boot.
|
||||||
LoadedModules map[string]WinModuleLoad
|
LoadedModules map[string]WinModuleLoad
|
||||||
|
// ELAM describes the configuration of each Early Launch AntiMalware driver,
|
||||||
|
// for each AV Vendor key.
|
||||||
|
ELAM map[string]WinELAM
|
||||||
// BootDebuggingEnabled is true if boot debugging was ever reported
|
// BootDebuggingEnabled is true if boot debugging was ever reported
|
||||||
// as enabled.
|
// as enabled.
|
||||||
BootDebuggingEnabled bool
|
BootDebuggingEnabled bool
|
||||||
@ -175,13 +180,25 @@ type WinModuleLoad struct {
|
|||||||
ImageBase []uint64
|
ImageBase []uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WinELAM describes the configuration of an Early Launch AntiMalware driver.
|
||||||
|
// These values represent the 3 measured registry values stored in the ELAM
|
||||||
|
// hive for the driver.
|
||||||
|
type WinELAM struct {
|
||||||
|
Measured []byte
|
||||||
|
Config []byte
|
||||||
|
Policy []byte
|
||||||
|
}
|
||||||
|
|
||||||
// ParseWinEvents parses a series of events to extract information about
|
// ParseWinEvents parses a series of events to extract information about
|
||||||
// the bringup of Microsoft Windows. This information is not trustworthy
|
// the bringup of Microsoft Windows. This information is not trustworthy
|
||||||
// unless the integrity of platform & bootloader events has already been
|
// unless the integrity of platform & bootloader events has already been
|
||||||
// established.
|
// established.
|
||||||
func ParseWinEvents(events []Event) (*WinEvents, error) {
|
func ParseWinEvents(events []Event) (*WinEvents, error) {
|
||||||
var (
|
var (
|
||||||
out = WinEvents{LoadedModules: map[string]WinModuleLoad{}}
|
out = WinEvents{
|
||||||
|
LoadedModules: map[string]WinModuleLoad{},
|
||||||
|
ELAM: map[string]WinELAM{},
|
||||||
|
}
|
||||||
seenSeparator bool
|
seenSeparator bool
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -348,39 +365,38 @@ func (w *WinEvents) parseAuthenticodeHash(header microsoftEventHeader, r io.Read
|
|||||||
|
|
||||||
func (w *WinEvents) readLoadedModuleAggregation(rdr *bytes.Reader, header microsoftEventHeader) error {
|
func (w *WinEvents) readLoadedModuleAggregation(rdr *bytes.Reader, header microsoftEventHeader) error {
|
||||||
var (
|
var (
|
||||||
r = io.LimitReader(rdr, int64(header.Size))
|
r = &io.LimitedReader{R: rdr, N: int64(header.Size)}
|
||||||
codeHash []byte
|
codeHash []byte
|
||||||
imgBase uint64
|
imgBase uint64
|
||||||
)
|
)
|
||||||
|
|
||||||
eventLoop:
|
for r.N > 0 {
|
||||||
for {
|
var h microsoftEventHeader
|
||||||
var header microsoftEventHeader
|
if err := binary.Read(r, binary.LittleEndian, &h); err != nil {
|
||||||
if err := binary.Read(r, binary.LittleEndian, &header); err != nil {
|
|
||||||
if errors.Is(err, io.EOF) {
|
|
||||||
break eventLoop
|
|
||||||
}
|
|
||||||
return fmt.Errorf("parsing LMA sub-event: %v", err)
|
return fmt.Errorf("parsing LMA sub-event: %v", err)
|
||||||
}
|
}
|
||||||
|
if int64(h.Size) > r.N {
|
||||||
|
return fmt.Errorf("LMA sub-event is larger than available data: %d > %d", h.Size, r.N)
|
||||||
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
switch header.Type {
|
switch h.Type {
|
||||||
case imageBase:
|
case imageBase:
|
||||||
if imgBase != 0 {
|
if imgBase != 0 {
|
||||||
return errors.New("duplicate image base data in LMA event")
|
return errors.New("duplicate image base data in LMA event")
|
||||||
}
|
}
|
||||||
if imgBase, err = w.parseImageBase(header, r); err != nil {
|
if imgBase, err = w.parseImageBase(h, r); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case authenticodeHash:
|
case authenticodeHash:
|
||||||
if codeHash != nil {
|
if codeHash != nil {
|
||||||
return errors.New("duplicate authenticode hash structure in LMA event")
|
return errors.New("duplicate authenticode hash structure in LMA event")
|
||||||
}
|
}
|
||||||
if codeHash, err = w.parseAuthenticodeHash(header, r); err != nil {
|
if codeHash, err = w.parseAuthenticodeHash(h, r); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unknown event in LMA aggregation: %v", header.Type)
|
return fmt.Errorf("unknown event in LMA aggregation: %v", h.Type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -394,6 +410,83 @@ eventLoop:
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parseUTF16 decodes data representing a UTF16 string. It is assumed the
|
||||||
|
// caller has validated that the data size is within allowable bounds.
|
||||||
|
func (w *WinEvents) parseUTF16(header microsoftEventHeader, r io.Reader) (string, error) {
|
||||||
|
data := make([]uint16, header.Size/2)
|
||||||
|
if err := binary.Read(r, binary.LittleEndian, &data); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return strings.TrimSuffix(string(utf16.Decode(data)), string(0x00)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WinEvents) readELAMAggregation(rdr *bytes.Reader, header microsoftEventHeader) error {
|
||||||
|
var (
|
||||||
|
r = &io.LimitedReader{R: rdr, N: int64(header.Size)}
|
||||||
|
driverName string
|
||||||
|
measured []byte
|
||||||
|
policy []byte
|
||||||
|
config []byte
|
||||||
|
)
|
||||||
|
|
||||||
|
for r.N > 0 {
|
||||||
|
var h microsoftEventHeader
|
||||||
|
if err := binary.Read(r, binary.LittleEndian, &h); err != nil {
|
||||||
|
return fmt.Errorf("parsing ELAM aggregation sub-event: %v", err)
|
||||||
|
}
|
||||||
|
if int64(h.Size) > r.N {
|
||||||
|
return fmt.Errorf("ELAM aggregation sub-event is larger than available data: %d > %d", h.Size, r.N)
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
switch h.Type {
|
||||||
|
case elamKeyname:
|
||||||
|
if driverName != "" {
|
||||||
|
return errors.New("duplicate driver name in ELAM aggregation event")
|
||||||
|
}
|
||||||
|
if driverName, err = w.parseUTF16(h, r); err != nil {
|
||||||
|
return fmt.Errorf("parsing ELAM driver name: %v", err)
|
||||||
|
}
|
||||||
|
case elamMeasured:
|
||||||
|
if measured != nil {
|
||||||
|
return errors.New("duplicate measured data in ELAM aggregation event")
|
||||||
|
}
|
||||||
|
measured = make([]byte, h.Size)
|
||||||
|
if err := binary.Read(r, binary.LittleEndian, &measured); err != nil {
|
||||||
|
return fmt.Errorf("reading ELAM measured value: %v", err)
|
||||||
|
}
|
||||||
|
case elamPolicy:
|
||||||
|
if policy != nil {
|
||||||
|
return errors.New("duplicate policy data in ELAM aggregation event")
|
||||||
|
}
|
||||||
|
policy = make([]byte, h.Size)
|
||||||
|
if err := binary.Read(r, binary.LittleEndian, &policy); err != nil {
|
||||||
|
return fmt.Errorf("reading ELAM policy value: %v", err)
|
||||||
|
}
|
||||||
|
case elamConfiguration:
|
||||||
|
if config != nil {
|
||||||
|
return errors.New("duplicate config data in ELAM aggregation event")
|
||||||
|
}
|
||||||
|
config = make([]byte, h.Size)
|
||||||
|
if err := binary.Read(r, binary.LittleEndian, &config); err != nil {
|
||||||
|
return fmt.Errorf("reading ELAM config value: %v", err)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unknown event in LMA aggregation: %v", h.Type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if driverName == "" {
|
||||||
|
return errors.New("ELAM driver name not specified")
|
||||||
|
}
|
||||||
|
w.ELAM[driverName] = WinELAM{
|
||||||
|
Measured: measured,
|
||||||
|
Config: config,
|
||||||
|
Policy: policy,
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (w *WinEvents) readSIPAEvent(r *bytes.Reader) error {
|
func (w *WinEvents) readSIPAEvent(r *bytes.Reader) error {
|
||||||
var header microsoftEventHeader
|
var header microsoftEventHeader
|
||||||
if err := binary.Read(r, binary.LittleEndian, &header); err != nil {
|
if err := binary.Read(r, binary.LittleEndian, &header); err != nil {
|
||||||
@ -401,6 +494,8 @@ func (w *WinEvents) readSIPAEvent(r *bytes.Reader) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch header.Type {
|
switch header.Type {
|
||||||
|
case elamAggregation:
|
||||||
|
return w.readELAMAggregation(r, header)
|
||||||
case loadedModuleAggregation:
|
case loadedModuleAggregation:
|
||||||
return w.readLoadedModuleAggregation(r, header)
|
return w.readLoadedModuleAggregation(r, header)
|
||||||
case bootCounter:
|
case bootCounter:
|
||||||
|
@ -287,6 +287,9 @@ func TestParseWinEvents(t *testing.T) {
|
|||||||
AuthenticodeHash: []byte{206, 206, 205, 136, 150, 241, 250, 153, 119, 202, 90, 156, 153, 3, 200, 140, 32, 158, 252, 45, 213, 179, 244, 48, 212, 60, 18, 155, 182, 98, 198, 161},
|
AuthenticodeHash: []byte{206, 206, 205, 136, 150, 241, 250, 153, 119, 202, 90, 156, 153, 3, 200, 140, 32, 158, 252, 45, 213, 179, 244, 48, 212, 60, 18, 155, 182, 98, 198, 161},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
ELAM: map[string]WinELAM{
|
||||||
|
"Windows Defender": WinELAM{Measured: []byte{0x06, 0x7d, 0x5b, 0x9d, 0xc5, 0x62, 0x7f, 0x97, 0xdc, 0xf3, 0xfe, 0xff, 0x60, 0x2a, 0x34, 0x2e, 0xd6, 0x98, 0xd2, 0xcc}},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := ioutil.ReadFile("testdata/windows_gcp_shielded_vm.json")
|
data, err := ioutil.ReadFile("testdata/windows_gcp_shielded_vm.json")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user