efivalidate 0.1.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/FORMAT.md +27 -0
- data/README.md +19 -4
- data/efivalidate.gemspec +6 -3
- data/exe/efivalidate +26 -0
- data/lib/efivalidate/ealf_header.rb +32 -0
- data/lib/efivalidate/ealf_parser.rb +23 -0
- data/lib/efivalidate/ealf_row.rb +30 -0
- data/lib/efivalidate/efi_validation_error.rb +15 -0
- data/lib/efivalidate/efi_validator.rb +35 -0
- data/lib/efivalidate/intel_firmware_descriptor.rb +9 -0
- data/lib/efivalidate/version.rb +2 -2
- data/lib/efivalidate.rb +7 -1
- metadata +40 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 037ca593ce0ba5a08920d051c191e5f9c4d3a71a
|
4
|
+
data.tar.gz: cd59412dc988879399f553b91a965cdca1a59a77
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 156c354cdf536fbc2a09b9a820cf29c023ebdcbd3042241f4286e0689d0b37441ef5b187fea248a6bd218b64d874e726923b2747412356263ce7543457976281
|
7
|
+
data.tar.gz: 8496a21db212539db45a5e6547e361c16a6b25a581855e899ec7c45ad7a73c3a13f543c93d3bba114745709d784cd8e5a218598f294cb288262712ca5513a9de
|
data/.gitignore
CHANGED
data/FORMAT.md
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
|
2
|
+
NOTE: In progress
|
3
|
+
|
4
|
+
# Header
|
5
|
+
|
6
|
+
* 0-4: "magic" = "EALF"
|
7
|
+
* 5-8: number of hash rows
|
8
|
+
* 9-12: file size in bytes
|
9
|
+
* 13: ?? Hash function, 00 = sha1, 01 = sha256
|
10
|
+
* Zeros?
|
11
|
+
* 30: UInt16 Offset to hash table
|
12
|
+
* 32: UInt16 Number of Unicode-16 characters following
|
13
|
+
* 34: - NULL = UTF-16 null terminated EFI version string
|
14
|
+
|
15
|
+
# 60 byte "rows"
|
16
|
+
* 1 byte FD region (see `fdutil`)
|
17
|
+
* 0 = FD header / region
|
18
|
+
* 1 = BIOS / EFI
|
19
|
+
* 2 = Intel ME (Code + Data)
|
20
|
+
* 3 = Gigabit Ethernet (Unused)
|
21
|
+
* 4 = Platform Data & Bootloader
|
22
|
+
* 1 byte sub-region
|
23
|
+
* 2 byte index
|
24
|
+
* 4 byte offset
|
25
|
+
* 4 byte size
|
26
|
+
* 16 bytes GUID for section or "FF"
|
27
|
+
* 32 bytes SHA256 for section
|
data/README.md
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
-
#
|
1
|
+
# `efivalidate`
|
2
2
|
|
3
|
-
|
3
|
+
Important: See `FORMAT.md`
|
4
4
|
|
5
|
-
|
5
|
+
`efivalidate` is a ruby utility to take a given input EFI payload from macOS and to compare it against
|
6
|
+
Apple's validation schema. Being written in ruby this can occur off-box to ensure that the utility itself
|
7
|
+
hasn't been compromised
|
6
8
|
|
7
9
|
## Installation
|
8
10
|
|
@@ -22,7 +24,20 @@ Or install it yourself as:
|
|
22
24
|
|
23
25
|
## Usage
|
24
26
|
|
25
|
-
|
27
|
+
$ efivalidate
|
28
|
+
|
29
|
+
Usage: efivalidate {input.bin} [PARAMS]
|
30
|
+
|
31
|
+
{input.bin} is a EFI payload saved using either `eficheck` or `BootRomFlash.efi`
|
32
|
+
|
33
|
+
--download-signatures
|
34
|
+
|
35
|
+
Attempts to download Apple's EFI signatures from their update service
|
36
|
+
|
37
|
+
--signatures-path
|
38
|
+
|
39
|
+
Points to a path on disk where known EFI signatures exist
|
40
|
+
|
26
41
|
|
27
42
|
## Development
|
28
43
|
|
data/efivalidate.gemspec
CHANGED
@@ -5,12 +5,12 @@ require "efivalidate/version"
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "efivalidate"
|
8
|
-
spec.version =
|
8
|
+
spec.version = EFIValidate::VERSION
|
9
9
|
spec.authors = ["Rick Mark"]
|
10
|
-
spec.email = ["rickmark@
|
10
|
+
spec.email = ["rickmark@dropbox.com"]
|
11
11
|
|
12
12
|
spec.summary = %q{Validate Apple EFI images offline}
|
13
|
-
spec.description = %q{
|
13
|
+
spec.description = %q{Implements an algorithm to compare Apple EFI against their EALF baselines.}
|
14
14
|
spec.homepage = "https://github.com/rickmark/efivalidate"
|
15
15
|
spec.license = "MIT"
|
16
16
|
|
@@ -30,6 +30,9 @@ Gem::Specification.new do |spec|
|
|
30
30
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
31
31
|
spec.require_paths = ["lib"]
|
32
32
|
|
33
|
+
spec.add_runtime_dependency 'iostruct', '~> 0.0.4'
|
34
|
+
spec.add_runtime_dependency 'uuidtools', '~> 2.1'
|
35
|
+
|
33
36
|
spec.add_development_dependency "bundler", "~> 1.15"
|
34
37
|
spec.add_development_dependency "rake", "~> 10.0"
|
35
38
|
spec.add_development_dependency "rspec", "~> 3.0"
|
data/exe/efivalidate
CHANGED
@@ -1,3 +1,29 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
require 'bundler/setup'
|
2
3
|
|
3
4
|
require "efivalidate"
|
5
|
+
require 'optionparser'
|
6
|
+
|
7
|
+
if ARGV.count != 2
|
8
|
+
puts "efivalidate EFI_FILE EALF_FILE"
|
9
|
+
exit -1
|
10
|
+
end
|
11
|
+
|
12
|
+
efi = ARGV[0]
|
13
|
+
ealf = ARGV[1]
|
14
|
+
|
15
|
+
parser = EFIValidate::EALFParser.read ealf
|
16
|
+
validator = EFIValidate::EFIValidator.new parser, File.open(efi)
|
17
|
+
|
18
|
+
validator.validate
|
19
|
+
|
20
|
+
if validator.is_valid?
|
21
|
+
puts "EFI file matches EALF baseline. (#{parser.rows.count} hashes match)"
|
22
|
+
else
|
23
|
+
puts "EFI file matched #{parser.rows.count - validator.errors.count} hashes from EALF.\n"
|
24
|
+
puts "EFI file has #{validator.errors.count} hash errors:\n\n"
|
25
|
+
validator.errors.each do |error|
|
26
|
+
puts "Actual Hash: #{error.hash}"
|
27
|
+
puts "Expected Hash from EALF:\n#{error.row.to_s}\n\n"
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module EFIValidate
|
2
|
+
class EALFHeader < IOStruct.new 'a4LLC',
|
3
|
+
:ealf_magic,
|
4
|
+
:ealf_rows,
|
5
|
+
:ealf_size,
|
6
|
+
:ealf_hash_function,
|
7
|
+
:ealf_zeros,
|
8
|
+
:ealf_row_offset,
|
9
|
+
:ealf_signature_size
|
10
|
+
|
11
|
+
EALF_MAGIC = 'EALF'.freeze
|
12
|
+
|
13
|
+
EALF_ROW_SIZE = 28
|
14
|
+
|
15
|
+
EALF_HASH_FUNCTIONS = { 0 => { size: 20, function: -> { Digest::SHA1.new } }, 1 => { size: 32, funciton: -> { Digest::SHA2.new(256)} } }
|
16
|
+
|
17
|
+
EALF_HASH_SHA1 = 0
|
18
|
+
EALF_HASH_SHA256 = 1
|
19
|
+
|
20
|
+
def row_size
|
21
|
+
EALF_ROW_SIZE + self.hash_size
|
22
|
+
end
|
23
|
+
|
24
|
+
def hash_size
|
25
|
+
EFIValidate::EALFHeader::EALF_HASH_FUNCTIONS[self.ealf_hash_function][:size]
|
26
|
+
end
|
27
|
+
|
28
|
+
def create_hash
|
29
|
+
EFIValidate::EALFHeader::EALF_HASH_FUNCTIONS[self.ealf_hash_function][:funciton].call
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module EFIValidate
|
2
|
+
class EALFParser
|
3
|
+
class EALFParseError < Exception; end
|
4
|
+
|
5
|
+
attr_reader :header, :rows
|
6
|
+
|
7
|
+
def initialize(data)
|
8
|
+
@header = EFIValidate::EALFHeader.read(data, 97)
|
9
|
+
|
10
|
+
raise EALFParseError, 'File header magic mismatch.' unless @header.ealf_magic == EFIValidate::EALFHeader::EALF_MAGIC
|
11
|
+
raise EALFParseError, 'Only SHA256 EALF is supported.' unless @header.ealf_hash_function == EFIValidate::EALFHeader::EALF_HASH_SHA256
|
12
|
+
|
13
|
+
@rows = []
|
14
|
+
@header.ealf_rows.times do
|
15
|
+
@rows << EFIValidate::EALFRow.read(data, @header.row_size).tap { |row| row.header = @header }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.read(file)
|
20
|
+
EFIValidate::EALFParser.new(File.open(file))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'uuidtools'
|
2
|
+
|
3
|
+
module EFIValidate
|
4
|
+
# A class that represents a region of an EFI firmware with a valid hash for the region
|
5
|
+
#
|
6
|
+
# A EALF file is a collection of regions an
|
7
|
+
class EALFRow < IOStruct.new 'CCvLLa16a*',
|
8
|
+
:ealf_component,
|
9
|
+
:ealf_region,
|
10
|
+
:ealf_master,
|
11
|
+
:ealf_offset,
|
12
|
+
:ealf_length,
|
13
|
+
:ealf_uuid,
|
14
|
+
:ealf_hash
|
15
|
+
|
16
|
+
attr_accessor :header
|
17
|
+
|
18
|
+
def hash
|
19
|
+
self.ealf_hash.each_byte.map { |b| '%02x' % b }.join
|
20
|
+
end
|
21
|
+
|
22
|
+
def uuid
|
23
|
+
UUIDTools::UUID.parse_raw(self.ealf_uuid)
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_s
|
27
|
+
"<#{'%02x' % ealf_component}:#{ '%02x' % self.ealf_region }:#{ "%04x" % self.ealf_master }:#{ "%08x" % self.ealf_offset }:#{ "%08x" % self.ealf_length}:#{self.uuid}:#{self.hash}>"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module EFIValidate
|
2
|
+
class EFIValidator
|
3
|
+
attr_reader :parser, :data, :errors
|
4
|
+
|
5
|
+
def initialize(parser, data)
|
6
|
+
@parser = parser
|
7
|
+
@data = data
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
def validate!
|
12
|
+
@errors = []
|
13
|
+
|
14
|
+
@parser.rows.each do |row|
|
15
|
+
@data.seek row.ealf_offset
|
16
|
+
|
17
|
+
section_data = @data.read row.ealf_length
|
18
|
+
|
19
|
+
calculated_hash = @parser.header.create_hash.hexdigest section_data
|
20
|
+
|
21
|
+
@errors << EFIValidationError.new(row, section_data, calculated_hash) unless calculated_hash == row.hash
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def validate
|
26
|
+
self.validate! unless @errors
|
27
|
+
end
|
28
|
+
|
29
|
+
def is_valid?
|
30
|
+
self.validate
|
31
|
+
|
32
|
+
self.errors.count == 0
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/efivalidate/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
module
|
2
|
-
VERSION = "
|
1
|
+
module EFIValidate
|
2
|
+
VERSION = "1.0.0"
|
3
3
|
end
|
data/lib/efivalidate.rb
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
require "efivalidate/version"
|
2
|
+
require 'iostruct'
|
2
3
|
|
3
|
-
module
|
4
|
+
module EFIValidate
|
4
5
|
# Your code goes here...
|
6
|
+
autoload :EALFHeader, 'efivalidate/ealf_header'
|
7
|
+
autoload :EALFRow, 'efivalidate/ealf_row'
|
8
|
+
autoload :EALFParser, 'efivalidate/ealf_parser'
|
9
|
+
autoload :EFIValidator, 'efivalidate/efi_validator'
|
10
|
+
autoload :EFIValidationError, 'efivalidate/efi_validation_error'
|
5
11
|
end
|
metadata
CHANGED
@@ -1,15 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: efivalidate
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rick Mark
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-12-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: iostruct
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.0.4
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.0.4
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: uuidtools
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.1'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.1'
|
13
41
|
- !ruby/object:Gem::Dependency
|
14
42
|
name: bundler
|
15
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,9 +80,9 @@ dependencies:
|
|
52
80
|
- - "~>"
|
53
81
|
- !ruby/object:Gem::Version
|
54
82
|
version: '3.0'
|
55
|
-
description:
|
83
|
+
description: Implements an algorithm to compare Apple EFI against their EALF baselines.
|
56
84
|
email:
|
57
|
-
- rickmark@
|
85
|
+
- rickmark@dropbox.com
|
58
86
|
executables:
|
59
87
|
- efivalidate
|
60
88
|
extensions: []
|
@@ -64,6 +92,7 @@ files:
|
|
64
92
|
- ".rspec"
|
65
93
|
- ".travis.yml"
|
66
94
|
- CODE_OF_CONDUCT.md
|
95
|
+
- FORMAT.md
|
67
96
|
- Gemfile
|
68
97
|
- LICENSE.txt
|
69
98
|
- README.md
|
@@ -73,6 +102,12 @@ files:
|
|
73
102
|
- efivalidate.gemspec
|
74
103
|
- exe/efivalidate
|
75
104
|
- lib/efivalidate.rb
|
105
|
+
- lib/efivalidate/ealf_header.rb
|
106
|
+
- lib/efivalidate/ealf_parser.rb
|
107
|
+
- lib/efivalidate/ealf_row.rb
|
108
|
+
- lib/efivalidate/efi_validation_error.rb
|
109
|
+
- lib/efivalidate/efi_validator.rb
|
110
|
+
- lib/efivalidate/intel_firmware_descriptor.rb
|
76
111
|
- lib/efivalidate/version.rb
|
77
112
|
homepage: https://github.com/rickmark/efivalidate
|
78
113
|
licenses:
|
@@ -95,7 +130,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
95
130
|
version: '0'
|
96
131
|
requirements: []
|
97
132
|
rubyforge_project:
|
98
|
-
rubygems_version: 2.6.
|
133
|
+
rubygems_version: 2.6.14
|
99
134
|
signing_key:
|
100
135
|
specification_version: 4
|
101
136
|
summary: Validate Apple EFI images offline
|