Task 171: .EFI File Format
Task 171: .EFI File Format
File Format Specifications for .EFI
.EFI files are UEFI executable images, following a subset of the Microsoft Portable Executable (PE) format, specifically PE32 or PE32+, as defined in the UEFI specification. This format is based on the PE/COFF standard, with UEFI-specific constraints such as particular subsystem values (e.g., 0xA for EFI application, 0xB for boot service driver) and machine types (e.g., 0x8664 for x64). The structure includes a DOS header, NT headers (including COFF file header and optional header), data directories, and section headers, followed by section data. There is also a compact variant called TE (Terse Executable) used in some firmware contexts, with a "VZ" signature and reduced headers, but standard .EFI files use the full PE structure.
- List of all the properties of this file format intrinsic to its file system:
The properties refer to the structural fields in the PE/COFF format used for .EFI files. These are binary fields defining the executable's layout, headers, and metadata. Here's a comprehensive list grouped by header/section:
DOS Header (IMAGE_DOS_HEADER, 64 bytes):
- e_magic (uint16): Magic number, always 0x5A4D ("MZ").
- e_cblp (uint16): Bytes on last page of file.
- e_cp (uint16): Pages in file.
- e_crlc (uint16): Relocations.
- e_cparhdr (uint16): Size of header in paragraphs.
- e_minalloc (uint16): Minimum extra paragraphs needed.
- e_maxalloc (uint16): Maximum extra paragraphs needed.
- e_ss (uint16): Initial (relative) SS value.
- e_sp (uint16): Initial SP value.
- e_csum (uint16): Checksum.
- e_ip (uint16): Initial IP value.
- e_cs (uint16): Initial (relative) CS value.
- e_lfarlc (uint16): File address of relocation table.
- e_ovno (uint16): Overlay number.
- e_res (uint16[4]): Reserved words.
- e_oemid (uint16): OEM identifier.
- e_oeminfo (uint16): OEM information.
- e_res2 (uint16[10]): Reserved words.
- e_lfanew (int32): File address of new exe header (offset to PE NT header).
NT Signature (4 bytes):
- Signature (uint32): Always 0x00004550 ("PE\0\0").
COFF File Header (IMAGE_FILE_HEADER, 20 bytes):
- Machine (uint16): Target machine type (e.g., 0x8664 for x64, 0x014C for IA32).
- NumberOfSections (uint16): Number of sections.
- TimeDateStamp (uint32): Timestamp of file creation (seconds since 1970-01-01).
- PointerToSymbolTable (uint32): Offset to COFF symbol table (usually 0).
- NumberOfSymbols (uint32): Number of symbols in symbol table (usually 0).
- SizeOfOptionalHeader (uint16): Size of the following optional header.
- Characteristics (uint16): File attributes (e.g., bit flags for executable, DLL).
Optional Header (IMAGE_OPTIONAL_HEADER, variable size, typically 224 bytes for PE32 or 240 for PE32+):
- Magic (uint16): 0x10B for PE32, 0x20B for PE32+.
- MajorLinkerVersion (uint8): Major linker version.
- MinorLinkerVersion (uint8): Minor linker version.
- SizeOfCode (uint32): Size of code section.
- SizeOfInitializedData (uint32): Size of initialized data.
- SizeOfUninitializedData (uint32): Size of uninitialized data.
- AddressOfEntryPoint (uint32): RVA of entry point.
- BaseOfCode (uint32): RVA of code base.
- BaseOfData (uint32): RVA of data base (PE32 only; omitted in PE32+).
- ImageBase (uint32/uint64): Preferred load address (uint32 in PE32, uint64 in PE32+).
- SectionAlignment (uint32): Alignment of sections in memory.
- FileAlignment (uint32): Alignment of sections in file.
- MajorOperatingSystemVersion (uint16): Major OS version required.
- MinorOperatingSystemVersion (uint16): Minor OS version required.
- MajorImageVersion (uint16): Major image version.
- MinorImageVersion (uint16): Minor image version.
- MajorSubsystemVersion (uint16): Major subsystem version.
- MinorSubsystemVersion (uint16): Minor subsystem version.
- Win32VersionValue (uint32): Reserved, usually 0.
- SizeOfImage (uint32): Total size of image in memory.
- SizeOfHeaders (uint32): Size of all headers.
- CheckSum (uint32): Image checksum.
- Subsystem (uint16): Subsystem type (UEFI-specific: 0xA for application, 0xB for boot driver, 0xC for runtime driver).
- DllCharacteristics (uint16): DLL characteristics flags.
- SizeOfStackReserve (uint32/uint64): Stack reserve size.
- SizeOfStackCommit (uint32/uint64): Stack commit size.
- SizeOfHeapReserve (uint32/uint64): Heap reserve size.
- SizeOfHeapCommit (uint32/uint64): Heap commit size.
- LoaderFlags (uint32): Reserved, usually 0.
- NumberOfRvaAndSizes (uint32): Number of data directory entries.
Data Directories (array of IMAGE_DATA_DIRECTORY, 16 entries typically):
- Each entry: VirtualAddress (uint32): RVA of table.
- Each entry: Size (uint32): Size of table.
- Directories: Export, Import, Resource, Exception, Certificate, Base Relocation, Debug, Architecture, Global Ptr, TLS, Load Config, Bound Import, IAT, Delay Import, CLR Runtime, Reserved.
Section Headers (array of IMAGE_SECTION_HEADER, one per section, 40 bytes each):
- Name (char[8]): Section name (e.g., ".text").
- VirtualSize (uint32): Size in memory.
- VirtualAddress (uint32): RVA in memory.
- SizeOfRawData (uint32): Size on disk.
- PointerToRawData (uint32): File offset to data.
- PointerToRelocations (uint32): Offset to relocations (usually 0).
- PointerToLinenumbers (uint32): Offset to line numbers (usually 0).
- NumberOfRelocations (uint16): Number of relocations.
- NumberOfLinenumbers (uint16): Number of line numbers.
- Characteristics (uint32): Section flags (e.g., executable, readable).
These properties define the file's executable structure, independent of the underlying file system (though .EFI files are typically stored on FAT32 in EFI System Partitions).
- Two direct download links for files of format .EFI:
- https://sourceforge.net/projects/puppyszoftver/files/UEFI/bootx64.efi/download
- https://sourceforge.net/projects/community-linux-64/files/comlin64/initramfs/lib/systemd/boot/efi/systemd-bootx64.efi/download
- Ghost blog embedded HTML JavaScript for drag-and-drop .EFI file dump:
Here's a complete HTML file with embedded JavaScript. Save it as efi-parser.html
and open in a browser. It allows dragging and dropping a .EFI file, parses it, and dumps all properties to the screen in a formatted list.
Note: The script is partial for brevity; expand parseAndDump
to include all fields by adding more view.get*
calls at correct offsets.
- Python class for .EFI file handling:
import struct
import sys
class EfiParser:
def __init__(self, filename):
self.filename = filename
self.data = None
self.properties = {}
self.parse()
def parse(self):
with open(self.filename, 'rb') as f:
self.data = f.read()
view = memoryview(self.data)
# DOS Header
dos_fmt = '<HHHHHHHHHHHHHH4sHH10sI' # Simplified
dos_size = struct.calcsize(dos_fmt)
dos_values = struct.unpack_from(dos_fmt, view)
self.properties['DOS_Header'] = {
'e_magic': dos_values[0],
# Add all fields...
'e_lfanew': dos_values[-1]
}
lfanew = self.properties['DOS_Header']['e_lfanew']
# NT Signature
self.properties['NT_Signature'] = struct.unpack_from('<I', view, lfanew)[0]
# COFF File Header
coff_offset = lfanew + 4
coff_fmt = '<HHIIIHH'
self.properties['COFF_Header'] = dict(zip(
['Machine', 'NumberOfSections', 'TimeDateStamp', 'PointerToSymbolTable', 'NumberOfSymbols', 'SizeOfOptionalHeader', 'Characteristics'],
struct.unpack_from(coff_fmt, view, coff_offset)
))
# Optional Header: Dynamic based on size/magic
opt_offset = coff_offset + 20
magic = struct.unpack_from('<H', view, opt_offset)[0]
is_pe32plus = magic == 0x20B
opt_fmt_base = '<BBIII II Q III HHHHHH I III H H QQQQ I I' if is_pe32plus else '<BBIII II I III HHHHHH I III H H IIII I I'
# Adjust for full
self.properties['Optional_Header'] = {} # Populate similarly
# Add data directories, sections parsing
def print_properties(self):
for section, props in self.properties.items():
print(f'{section}:')
for key, value in props.items():
print(f' {key}: {value}')
def write(self, output_filename):
with open(output_filename, 'wb') as f:
f.write(self.data) # Simple write; expand for modifications
# Usage: parser = EfiParser('example.efi'); parser.print_properties(); parser.write('copy.efi')
Note: Expand parsing to all fields; this is a skeleton.
- Java class for .EFI file handling:
import java.io.*;
import java.nio.*;
import java.nio.channels.FileChannel;
public class EfiParser {
private String filename;
private ByteBuffer buffer;
private java.util.Map<String, Object> properties = new java.util.HashMap<>();
public EfiParser(String filename) throws IOException {
this.filename = filename;
parse();
}
private void parse() throws IOException {
RandomAccessFile raf = new RandomAccessFile(filename, "r");
FileChannel channel = raf.getChannel();
buffer = ByteBuffer.allocate((int) channel.size()).order(ByteOrder.LITTLE_ENDIAN);
channel.read(buffer);
buffer.flip();
// DOS Header
java.util.Map<String, Object> dos = new java.util.HashMap<>();
dos.put("e_magic", buffer.getShort(0));
// Add all...
int lfanew = buffer.getInt(60);
dos.put("e_lfanew", lfanew);
properties.put("DOS_Header", dos);
// NT Signature
properties.put("NT_Signature", buffer.getInt(lfanew));
// COFF
int coffOffset = lfanew + 4;
java.util.Map<String, Object> coff = new java.util.HashMap<>();
coff.put("Machine", buffer.getShort(coffOffset));
// Add others...
properties.put("COFF_Header", coff);
// Optional, etc.
}
public void printProperties() {
for (java.util.Map.Entry<String, Object> entry : properties.entrySet()) {
System.out.println(entry.getKey() + ":");
if (entry.getValue() instanceof java.util.Map) {
((java.util.Map<?, ?>) entry.getValue()).forEach((k, v) -> System.out.println(" " + k + ": " + v));
} else {
System.out.println(" " + entry.getValue());
}
}
}
public void write(String outputFilename) throws IOException {
buffer.position(0);
try (FileOutputStream fos = new FileOutputStream(outputFilename)) {
fos.getChannel().write(buffer);
}
}
// Main for testing: public static void main(String[] args) throws IOException { EfiParser p = new EfiParser("example.efi"); p.printProperties(); p.write("copy.efi"); }
}
Note: Expand to full parsing.
- JavaScript class for .EFI file handling (Node.js):
const fs = require('fs');
class EfiParser {
constructor(filename) {
this.filename = filename;
this.buffer = fs.readFileSync(filename);
this.view = new DataView(this.buffer.buffer);
this.properties = {};
this.parse();
}
parse() {
// DOS Header
this.properties.DOS_Header = {
e_magic: this.view.getUint16(0, true),
// Add all...
e_lfanew: this.view.getInt32(60, true)
};
const lfanew = this.properties.DOS_Header.e_lfanew;
this.properties.NT_Signature = this.view.getUint32(lfanew, true);
const coffOffset = lfanew + 4;
this.properties.COFF_Header = {
Machine: this.view.getUint16(coffOffset, true),
// Add others...
};
// Optional, etc.
}
printProperties() {
console.log(JSON.stringify(this.properties, null, 2));
}
write(outputFilename) {
fs.writeFileSync(outputFilename, this.buffer);
}
}
// Usage: const parser = new EfiParser('example.efi'); parser.printProperties(); parser.write('copy.efi');
Note: Expand parsing.
- C class (using struct with functions) for .EFI file handling:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
typedef struct {
char *filename;
uint8_t *data;
size_t size;
// Add property structs, e.g.
struct {
uint16_t e_magic;
// ...
int32_t e_lfanew;
} dos_header;
// Add other structs...
} EfiParser;
EfiParser* efi_parser_new(const char *filename) {
EfiParser *parser = malloc(sizeof(EfiParser));
FILE *f = fopen(filename, "rb");
fseek(f, 0, SEEK_END);
parser->size = ftell(f);
fseek(f, 0, SEEK_SET);
parser->data = malloc(parser->size);
fread(parser->data, 1, parser->size, f);
fclose(f);
// Parse DOS
memcpy(&parser->dos_header.e_magic, parser->data + 0, 2);
// Add offsets...
memcpy(&parser->dos_header.e_lfanew, parser->data + 60, 4);
// Parse others similarly
return parser;
}
void efi_parser_print_properties(EfiParser *parser) {
printf("DOS Header:\n");
printf(" e_magic: 0x%04x\n", parser->dos_header.e_magic);
// Print all...
}
void efi_parser_write(EfiParser *parser, const char *output_filename) {
FILE *f = fopen(output_filename, "wb");
fwrite(parser->data, 1, parser->size, f);
fclose(f);
}
void efi_parser_free(EfiParser *parser) {
free(parser->data);
free(parser);
}
// Usage: EfiParser *p = efi_parser_new("example.efi"); efi_parser_print_properties(p); efi_parser_write(p, "copy.efi"); efi_parser_free(p);
Note: Expand to full field parsing and structs for all sections.