diff --git a/.gitignore b/.gitignore index 1515ee14..c3aef8d4 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ Utilities/TestKextInject/Result.xml Utilities/TestMacho/Macho Utilities/TestRsaPreprocess/RsaPreprocess Utilities/TestSmbios/Smbios +Utilities/TestPeCoff/PeCoff *.o *.exe DICT diff --git a/Include/Acidanthera/IndustryStandard/OcPeImage.h b/Include/Acidanthera/IndustryStandard/OcPeImage.h new file mode 100644 index 00000000..dd132bd8 --- /dev/null +++ b/Include/Acidanthera/IndustryStandard/OcPeImage.h @@ -0,0 +1,722 @@ +/** @file + EFI image format for PE32, PE32+ and TE. Please note some data structures are + different for PE32 and PE32+. EFI_IMAGE_NT_HEADERS32 is for PE32 and + EFI_IMAGE_NT_HEADERS64 is for PE32+. + + This file is coded to the Visual Studio, Microsoft Portable Executable and + Common Object File Format Specification, Revision 8.3 - February 6, 2013. + This file also includes some definitions in PI Specification, Revision 1.0. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+Portions Copyright (c) 2016 - 2020, Hewlett Packard Enterprise Development LP. All rights reserved.
+ +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef OC_PE_IMAGE_H +#define OC_PE_IMAGE_H + +// +// PE32+ Subsystem type for EFI images +// +#define EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION 10 +#define EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11 +#define EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12 +#define EFI_IMAGE_SUBSYSTEM_SAL_RUNTIME_DRIVER 13 ///< defined PI Specification, 1.0 + + +// +// PE32+ Machine type for EFI images +// +#define IMAGE_FILE_MACHINE_I386 0x014c +#define IMAGE_FILE_MACHINE_IA64 0x0200 +#define IMAGE_FILE_MACHINE_EBC 0x0EBC +#define IMAGE_FILE_MACHINE_X64 0x8664 +#define IMAGE_FILE_MACHINE_ARMTHUMB_MIXED 0x01c2 +#define IMAGE_FILE_MACHINE_ARM64 0xAA64 + +// +// EXE file formats +// +#define EFI_IMAGE_DOS_SIGNATURE SIGNATURE_16('M', 'Z') +#define EFI_IMAGE_OS2_SIGNATURE SIGNATURE_16('N', 'E') +#define EFI_IMAGE_OS2_SIGNATURE_LE SIGNATURE_16('L', 'E') +#define EFI_IMAGE_NT_SIGNATURE SIGNATURE_32('P', 'E', '\0', '\0') + +/// +/// PE images can start with an optional DOS header, so if an image is run +/// under DOS it can print an error message. +/// +typedef struct { + UINT16 e_magic; ///< Magic number. + UINT16 e_cblp; ///< Bytes on last page of file. + UINT16 e_cp; ///< Pages in file. + UINT16 e_crlc; ///< Relocations. + UINT16 e_cparhdr; ///< Size of header in paragraphs. + UINT16 e_minalloc; ///< Minimum extra paragraphs needed. + UINT16 e_maxalloc; ///< Maximum extra paragraphs needed. + UINT16 e_ss; ///< Initial (relative) SS value. + UINT16 e_sp; ///< Initial SP value. + UINT16 e_csum; ///< Checksum. + UINT16 e_ip; ///< Initial IP value. + UINT16 e_cs; ///< Initial (relative) CS value. + UINT16 e_lfarlc; ///< File address of relocation table. + UINT16 e_ovno; ///< Overlay number. + UINT16 e_res[4]; ///< Reserved words. + UINT16 e_oemid; ///< OEM identifier (for e_oeminfo). + UINT16 e_oeminfo; ///< OEM information; e_oemid specific. + UINT16 e_res2[10]; ///< Reserved words. + UINT32 e_lfanew; ///< File address of new exe header. +} EFI_IMAGE_DOS_HEADER; + +/// +/// COFF File Header (Object and Image). +/// +typedef struct { + UINT16 Machine; + UINT16 NumberOfSections; + UINT32 TimeDateStamp; + UINT32 PointerToSymbolTable; + UINT32 NumberOfSymbols; + UINT16 SizeOfOptionalHeader; + UINT16 Characteristics; +} EFI_IMAGE_FILE_HEADER; + +/// +/// Size of EFI_IMAGE_FILE_HEADER. +/// +#define EFI_IMAGE_SIZEOF_FILE_HEADER 20 + +// +// Characteristics +// +#define EFI_IMAGE_FILE_RELOCS_STRIPPED 1 ///< 0x0001 Relocation info stripped from file. + +/// +/// Header Data Directories. +/// +typedef struct { + UINT32 VirtualAddress; + UINT32 Size; +} EFI_IMAGE_DATA_DIRECTORY; + +// +// Directory Entries +// +#define EFI_IMAGE_DIRECTORY_ENTRY_EXPORT 0 +#define EFI_IMAGE_DIRECTORY_ENTRY_IMPORT 1 +#define EFI_IMAGE_DIRECTORY_ENTRY_RESOURCE 2 +#define EFI_IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 +#define EFI_IMAGE_DIRECTORY_ENTRY_SECURITY 4 +#define EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC 5 +#define EFI_IMAGE_DIRECTORY_ENTRY_DEBUG 6 +#define EFI_IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 +#define EFI_IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 +#define EFI_IMAGE_DIRECTORY_ENTRY_TLS 9 +#define EFI_IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 + +#define EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES 16 + +/// +/// @attention +/// EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC means PE32 and +/// EFI_IMAGE_OPTIONAL_HEADER32 must be used. The data structures only vary +/// after NT additional fields. +/// +#define EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b + +/// +/// @attention +/// EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC means PE32+ and +/// EFI_IMAGE_OPTIONAL_HEADER64 must be used. The data structures only vary +/// after NT additional fields. +/// +#define EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b + + +/// +/// @attention +/// EFI_IMAGE_NT_HEADERS32 is for use ONLY by tools. +/// +typedef struct { + UINT32 Signature; + EFI_IMAGE_FILE_HEADER FileHeader; +} EFI_IMAGE_NT_HEADERS_COMMON_HDR; + +STATIC_ASSERT ( + sizeof (EFI_IMAGE_NT_HEADERS_COMMON_HDR) == sizeof (UINT32) + sizeof (EFI_IMAGE_FILE_HEADER), + "Unsupported padding." + ); + +/// +/// @attention +/// EFI_IMAGE_NT_HEADERS32 is for use ONLY by tools. +/// +typedef struct { + EFI_IMAGE_NT_HEADERS_COMMON_HDR CommonHeader; + /// + /// Standard fields. + /// + UINT16 Magic; + UINT8 MajorLinkerVersion; + UINT8 MinorLinkerVersion; + UINT32 SizeOfCode; + UINT32 SizeOfInitializedData; + UINT32 SizeOfUninitializedData; + UINT32 AddressOfEntryPoint; + UINT32 BaseOfCode; + UINT32 BaseOfData; ///< PE32 contains this additional field, which is absent in PE32+. + /// + /// Optional Header Windows-Specific Fields. + /// + UINT32 ImageBase; + UINT32 SectionAlignment; + UINT32 FileAlignment; + UINT16 MajorOperatingSystemVersion; + UINT16 MinorOperatingSystemVersion; + UINT16 MajorImageVersion; + UINT16 MinorImageVersion; + UINT16 MajorSubsystemVersion; + UINT16 MinorSubsystemVersion; + UINT32 Win32VersionValue; + UINT32 SizeOfImage; + UINT32 SizeOfHeaders; + UINT32 CheckSum; + UINT16 Subsystem; + UINT16 DllCharacteristics; + UINT32 SizeOfStackReserve; + UINT32 SizeOfStackCommit; + UINT32 SizeOfHeapReserve; + UINT32 SizeOfHeapCommit; + UINT32 LoaderFlags; + UINT32 NumberOfRvaAndSizes; + EFI_IMAGE_DATA_DIRECTORY DataDirectory[]; +} EFI_IMAGE_NT_HEADERS32; + +/// +/// @attention +/// EFI_IMAGE_HEADERS64 is for use ONLY by tools. +/// +typedef struct { + EFI_IMAGE_NT_HEADERS_COMMON_HDR CommonHeader; + /// + /// Standard fields. + /// + UINT16 Magic; + UINT8 MajorLinkerVersion; + UINT8 MinorLinkerVersion; + UINT32 SizeOfCode; + UINT32 SizeOfInitializedData; + UINT32 SizeOfUninitializedData; + UINT32 AddressOfEntryPoint; + UINT32 BaseOfCode; + /// + /// Optional Header Windows-Specific Fields. + /// + UINT64 ImageBase; + UINT32 SectionAlignment; + UINT32 FileAlignment; + UINT16 MajorOperatingSystemVersion; + UINT16 MinorOperatingSystemVersion; + UINT16 MajorImageVersion; + UINT16 MinorImageVersion; + UINT16 MajorSubsystemVersion; + UINT16 MinorSubsystemVersion; + UINT32 Win32VersionValue; + UINT32 SizeOfImage; + UINT32 SizeOfHeaders; + UINT32 CheckSum; + UINT16 Subsystem; + UINT16 DllCharacteristics; + UINT64 SizeOfStackReserve; + UINT64 SizeOfStackCommit; + UINT64 SizeOfHeapReserve; + UINT64 SizeOfHeapCommit; + UINT32 LoaderFlags; + UINT32 NumberOfRvaAndSizes; + EFI_IMAGE_DATA_DIRECTORY DataDirectory[]; +} EFI_IMAGE_NT_HEADERS64; + +// +// Other Windows Subsystem Values +// +#define EFI_IMAGE_SUBSYSTEM_UNKNOWN 0 +#define EFI_IMAGE_SUBSYSTEM_NATIVE 1 +#define EFI_IMAGE_SUBSYSTEM_WINDOWS_GUI 2 +#define EFI_IMAGE_SUBSYSTEM_WINDOWS_CUI 3 +#define EFI_IMAGE_SUBSYSTEM_OS2_CUI 5 +#define EFI_IMAGE_SUBSYSTEM_POSIX_CUI 7 + +/// +/// Length of ShortName. +/// +#define EFI_IMAGE_SIZEOF_SHORT_NAME 8 + +/// +/// Section Table. This table immediately follows the optional header. +/// +typedef struct { + UINT8 Name[EFI_IMAGE_SIZEOF_SHORT_NAME]; + UINT32 VirtualSize; + UINT32 VirtualAddress; + UINT32 SizeOfRawData; + UINT32 PointerToRawData; + UINT32 PointerToRelocations; + UINT32 PointerToLinenumbers; + UINT16 NumberOfRelocations; + UINT16 NumberOfLinenumbers; + UINT32 Characteristics; +} EFI_IMAGE_SECTION_HEADER; + +// +// Section Flags Values +// +#define EFI_IMAGE_SCN_TYPE_NO_PAD BIT3 ///< 0x00000008 ///< Reserved. +#define EFI_IMAGE_SCN_CNT_CODE BIT5 ///< 0x00000020 +#define EFI_IMAGE_SCN_CNT_INITIALIZED_DATA BIT6 ///< 0x00000040 +#define EFI_IMAGE_SCN_CNT_UNINITIALIZED_DATA BIT7 ///< 0x00000080 + +#define EFI_IMAGE_SCN_LNK_OTHER BIT8 ///< 0x00000100 ///< Reserved. +#define EFI_IMAGE_SCN_LNK_INFO BIT9 ///< 0x00000200 ///< Section contains comments or some other type of information. +#define EFI_IMAGE_SCN_LNK_REMOVE BIT11 ///< 0x00000800 ///< Section contents will not become part of image. +#define EFI_IMAGE_SCN_LNK_COMDAT BIT12 ///< 0x00001000 + +#define EFI_IMAGE_SCN_ALIGN_1BYTES BIT20 ///< 0x00100000 +#define EFI_IMAGE_SCN_ALIGN_2BYTES BIT21 ///< 0x00200000 +#define EFI_IMAGE_SCN_ALIGN_4BYTES (BIT20|BIT21) ///< 0x00300000 +#define EFI_IMAGE_SCN_ALIGN_8BYTES BIT22 ///< 0x00400000 +#define EFI_IMAGE_SCN_ALIGN_16BYTES (BIT20|BIT22) ///< 0x00500000 +#define EFI_IMAGE_SCN_ALIGN_32BYTES (BIT21|BIT22) ///< 0x00600000 +#define EFI_IMAGE_SCN_ALIGN_64BYTES (BIT20|BIT21|BIT22) ///< 0x00700000 + +#define EFI_IMAGE_SCN_MEM_DISCARDABLE BIT25 ///< 0x02000000 +#define EFI_IMAGE_SCN_MEM_NOT_CACHED BIT26 ///< 0x04000000 +#define EFI_IMAGE_SCN_MEM_NOT_PAGED BIT27 ///< 0x08000000 +#define EFI_IMAGE_SCN_MEM_SHARED BIT28 ///< 0x10000000 +#define EFI_IMAGE_SCN_MEM_EXECUTE BIT29 ///< 0x20000000 +#define EFI_IMAGE_SCN_MEM_READ BIT30 ///< 0x40000000 +#define EFI_IMAGE_SCN_MEM_WRITE BIT31 ///< 0x80000000 + +/// +/// Size of a Symbol Table Record. +/// +#define EFI_IMAGE_SIZEOF_SYMBOL 18 + +// +// Symbols have a section number of the section in which they are +// defined. Otherwise, section numbers have the following meanings: +// +#define EFI_IMAGE_SYM_UNDEFINED (UINT16) 0 ///< Symbol is undefined or is common. +#define EFI_IMAGE_SYM_ABSOLUTE (UINT16) -1 ///< Symbol is an absolute value. +#define EFI_IMAGE_SYM_DEBUG (UINT16) -2 ///< Symbol is a special debug item. + +// +// Symbol Type (fundamental) values. +// +#define EFI_IMAGE_SYM_TYPE_NULL 0 ///< no type. +#define EFI_IMAGE_SYM_TYPE_VOID 1 ///< no valid type. +#define EFI_IMAGE_SYM_TYPE_CHAR 2 ///< type character. +#define EFI_IMAGE_SYM_TYPE_SHORT 3 ///< type short integer. +#define EFI_IMAGE_SYM_TYPE_INT 4 +#define EFI_IMAGE_SYM_TYPE_LONG 5 +#define EFI_IMAGE_SYM_TYPE_FLOAT 6 +#define EFI_IMAGE_SYM_TYPE_DOUBLE 7 +#define EFI_IMAGE_SYM_TYPE_STRUCT 8 +#define EFI_IMAGE_SYM_TYPE_UNION 9 +#define EFI_IMAGE_SYM_TYPE_ENUM 10 ///< enumeration. +#define EFI_IMAGE_SYM_TYPE_MOE 11 ///< member of enumeration. +#define EFI_IMAGE_SYM_TYPE_BYTE 12 +#define EFI_IMAGE_SYM_TYPE_WORD 13 +#define EFI_IMAGE_SYM_TYPE_UINT 14 +#define EFI_IMAGE_SYM_TYPE_DWORD 15 + +// +// Symbol Type (derived) values. +// +#define EFI_IMAGE_SYM_DTYPE_NULL 0 ///< no derived type. +#define EFI_IMAGE_SYM_DTYPE_POINTER 1 +#define EFI_IMAGE_SYM_DTYPE_FUNCTION 2 +#define EFI_IMAGE_SYM_DTYPE_ARRAY 3 + +// +// Storage classes. +// +#define EFI_IMAGE_SYM_CLASS_END_OF_FUNCTION ((UINT8) -1) +#define EFI_IMAGE_SYM_CLASS_NULL 0 +#define EFI_IMAGE_SYM_CLASS_AUTOMATIC 1 +#define EFI_IMAGE_SYM_CLASS_EXTERNAL 2 +#define EFI_IMAGE_SYM_CLASS_STATIC 3 +#define EFI_IMAGE_SYM_CLASS_REGISTER 4 +#define EFI_IMAGE_SYM_CLASS_EXTERNAL_DEF 5 +#define EFI_IMAGE_SYM_CLASS_LABEL 6 +#define EFI_IMAGE_SYM_CLASS_UNDEFINED_LABEL 7 +#define EFI_IMAGE_SYM_CLASS_MEMBER_OF_STRUCT 8 +#define EFI_IMAGE_SYM_CLASS_ARGUMENT 9 +#define EFI_IMAGE_SYM_CLASS_STRUCT_TAG 10 +#define EFI_IMAGE_SYM_CLASS_MEMBER_OF_UNION 11 +#define EFI_IMAGE_SYM_CLASS_UNION_TAG 12 +#define EFI_IMAGE_SYM_CLASS_TYPE_DEFINITION 13 +#define EFI_IMAGE_SYM_CLASS_UNDEFINED_STATIC 14 +#define EFI_IMAGE_SYM_CLASS_ENUM_TAG 15 +#define EFI_IMAGE_SYM_CLASS_MEMBER_OF_ENUM 16 +#define EFI_IMAGE_SYM_CLASS_REGISTER_PARAM 17 +#define EFI_IMAGE_SYM_CLASS_BIT_FIELD 18 +#define EFI_IMAGE_SYM_CLASS_BLOCK 100 +#define EFI_IMAGE_SYM_CLASS_FUNCTION 101 +#define EFI_IMAGE_SYM_CLASS_END_OF_STRUCT 102 +#define EFI_IMAGE_SYM_CLASS_FILE 103 +#define EFI_IMAGE_SYM_CLASS_SECTION 104 +#define EFI_IMAGE_SYM_CLASS_WEAK_EXTERNAL 105 + +// +// type packing constants +// +#define EFI_IMAGE_N_BTMASK 017 +#define EFI_IMAGE_N_TMASK 060 +#define EFI_IMAGE_N_TMASK1 0300 +#define EFI_IMAGE_N_TMASK2 0360 +#define EFI_IMAGE_N_BTSHFT 4 +#define EFI_IMAGE_N_TSHIFT 2 + +// +// Communal selection types. +// +#define EFI_IMAGE_COMDAT_SELECT_NODUPLICATES 1 +#define EFI_IMAGE_COMDAT_SELECT_ANY 2 +#define EFI_IMAGE_COMDAT_SELECT_SAME_SIZE 3 +#define EFI_IMAGE_COMDAT_SELECT_EXACT_MATCH 4 +#define EFI_IMAGE_COMDAT_SELECT_ASSOCIATIVE 5 + +// +// the following values only be referred in PeCoff, not defined in PECOFF. +// +#define EFI_IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY 1 +#define EFI_IMAGE_WEAK_EXTERN_SEARCH_LIBRARY 2 +#define EFI_IMAGE_WEAK_EXTERN_SEARCH_ALIAS 3 + +/// +/// Relocation format. +/// +typedef struct { + UINT32 VirtualAddress; + UINT32 SymbolTableIndex; + UINT16 Type; +} EFI_IMAGE_RELOCATION; + +/// +/// Size of EFI_IMAGE_RELOCATION +/// +#define EFI_IMAGE_SIZEOF_RELOCATION 10 + +// +// I386 relocation types. +// +#define EFI_IMAGE_REL_I386_ABSOLUTE 0x0000 ///< Reference is absolute, no relocation is necessary. +#define EFI_IMAGE_REL_I386_DIR16 0x0001 ///< Direct 16-bit reference to the symbols virtual address. +#define EFI_IMAGE_REL_I386_REL16 0x0002 ///< PC-relative 16-bit reference to the symbols virtual address. +#define EFI_IMAGE_REL_I386_DIR32 0x0006 ///< Direct 32-bit reference to the symbols virtual address. +#define EFI_IMAGE_REL_I386_DIR32NB 0x0007 ///< Direct 32-bit reference to the symbols virtual address, base not included. +#define EFI_IMAGE_REL_I386_SEG12 0x0009 ///< Direct 16-bit reference to the segment-selector bits of a 32-bit virtual address. +#define EFI_IMAGE_REL_I386_SECTION 0x000A +#define EFI_IMAGE_REL_I386_SECREL 0x000B +#define EFI_IMAGE_REL_I386_REL32 0x0014 ///< PC-relative 32-bit reference to the symbols virtual address. + +// +// x64 processor relocation types. +// +#define IMAGE_REL_AMD64_ABSOLUTE 0x0000 +#define IMAGE_REL_AMD64_ADDR64 0x0001 +#define IMAGE_REL_AMD64_ADDR32 0x0002 +#define IMAGE_REL_AMD64_ADDR32NB 0x0003 +#define IMAGE_REL_AMD64_REL32 0x0004 +#define IMAGE_REL_AMD64_REL32_1 0x0005 +#define IMAGE_REL_AMD64_REL32_2 0x0006 +#define IMAGE_REL_AMD64_REL32_3 0x0007 +#define IMAGE_REL_AMD64_REL32_4 0x0008 +#define IMAGE_REL_AMD64_REL32_5 0x0009 +#define IMAGE_REL_AMD64_SECTION 0x000A +#define IMAGE_REL_AMD64_SECREL 0x000B +#define IMAGE_REL_AMD64_SECREL7 0x000C +#define IMAGE_REL_AMD64_TOKEN 0x000D +#define IMAGE_REL_AMD64_SREL32 0x000E +#define IMAGE_REL_AMD64_PAIR 0x000F +#define IMAGE_REL_AMD64_SSPAN32 0x0010 + +/// +/// Based relocation format. +/// +typedef struct { + UINT32 VirtualAddress; + UINT32 SizeOfBlock; + UINT16 Relocations[]; +} EFI_IMAGE_BASE_RELOCATION; + +/// +/// Size of EFI_IMAGE_BASE_RELOCATION. +/// +#define EFI_IMAGE_SIZEOF_BASE_RELOCATION 8 + +// +// Based relocation types. +// +#define EFI_IMAGE_REL_BASED_ABSOLUTE 0 +#define EFI_IMAGE_REL_BASED_HIGH 1 +#define EFI_IMAGE_REL_BASED_LOW 2 +#define EFI_IMAGE_REL_BASED_HIGHLOW 3 +#define EFI_IMAGE_REL_BASED_HIGHADJ 4 +#define EFI_IMAGE_REL_BASED_MIPS_JMPADDR 5 +#define EFI_IMAGE_REL_BASED_ARM_MOV32A 5 +#define EFI_IMAGE_REL_BASED_ARM_MOV32T 7 +#define EFI_IMAGE_REL_BASED_IA64_IMM64 9 +#define EFI_IMAGE_REL_BASED_MIPS_JMPADDR16 9 +#define EFI_IMAGE_REL_BASED_DIR64 10 + +/// +/// Line number format. +/// +typedef struct { + union { + UINT32 SymbolTableIndex; ///< Symbol table index of function name if Linenumber is 0. + UINT32 VirtualAddress; ///< Virtual address of line number. + } Type; + UINT16 Linenumber; ///< Line number. +} EFI_IMAGE_LINENUMBER; + +/// +/// Size of EFI_IMAGE_LINENUMBER. +/// +#define EFI_IMAGE_SIZEOF_LINENUMBER 6 + +// +// Archive format. +// +#define EFI_IMAGE_ARCHIVE_START_SIZE 8 +#define EFI_IMAGE_ARCHIVE_START "!\n" +#define EFI_IMAGE_ARCHIVE_END "`\n" +#define EFI_IMAGE_ARCHIVE_PAD "\n" +#define EFI_IMAGE_ARCHIVE_LINKER_MEMBER "/ " +#define EFI_IMAGE_ARCHIVE_LONGNAMES_MEMBER "// " + +/// +/// Archive Member Headers +/// +typedef struct { + UINT8 Name[16]; ///< File member name - `/' terminated. + UINT8 Date[12]; ///< File member date - decimal. + UINT8 UserID[6]; ///< File member user id - decimal. + UINT8 GroupID[6]; ///< File member group id - decimal. + UINT8 Mode[8]; ///< File member mode - octal. + UINT8 Size[10]; ///< File member size - decimal. + UINT8 EndHeader[2]; ///< String to end header. (0x60 0x0A). +} EFI_IMAGE_ARCHIVE_MEMBER_HEADER; + +/// +/// Size of EFI_IMAGE_ARCHIVE_MEMBER_HEADER. +/// +#define EFI_IMAGE_SIZEOF_ARCHIVE_MEMBER_HDR 60 + + +// +// DLL Support +// + +/// +/// Export Directory Table. +/// +typedef struct { + UINT32 Characteristics; + UINT32 TimeDateStamp; + UINT16 MajorVersion; + UINT16 MinorVersion; + UINT32 Name; + UINT32 Base; + UINT32 NumberOfFunctions; + UINT32 NumberOfNames; + UINT32 AddressOfFunctions; + UINT32 AddressOfNames; + UINT32 AddressOfNameOrdinals; +} EFI_IMAGE_EXPORT_DIRECTORY; + +/// +/// Hint/Name Table. +/// +typedef struct { + UINT16 Hint; + UINT8 Name[1]; +} EFI_IMAGE_IMPORT_BY_NAME; + +/// +/// Import Address Table RVA (Thunk Table). +/// +typedef struct { + union { + UINT32 Function; + UINT32 Ordinal; + EFI_IMAGE_IMPORT_BY_NAME *AddressOfData; + } u1; +} EFI_IMAGE_THUNK_DATA; + +#define EFI_IMAGE_ORDINAL_FLAG BIT31 ///< Flag for PE32. +#define EFI_IMAGE_SNAP_BY_ORDINAL(Ordinal) ((Ordinal & EFI_IMAGE_ORDINAL_FLAG) != 0) +#define EFI_IMAGE_ORDINAL(Ordinal) (Ordinal & 0xffff) + +/// +/// Import Directory Table +/// +typedef struct { + UINT32 Characteristics; + UINT32 TimeDateStamp; + UINT32 ForwarderChain; + UINT32 Name; + EFI_IMAGE_THUNK_DATA *FirstThunk; +} EFI_IMAGE_IMPORT_DESCRIPTOR; + + +/// +/// Debug Directory Format. +/// +typedef struct { + UINT32 Characteristics; + UINT32 TimeDateStamp; + UINT16 MajorVersion; + UINT16 MinorVersion; + UINT32 Type; + UINT32 SizeOfData; + UINT32 RVA; ///< The address of the debug data when loaded, relative to the image base. + UINT32 FileOffset; ///< The file pointer to the debug data. +} EFI_IMAGE_DEBUG_DIRECTORY_ENTRY; + +#define EFI_IMAGE_DEBUG_TYPE_CODEVIEW 2 ///< The Visual C++ debug information. + +/// +/// Debug Data Structure defined in Microsoft C++. +/// +#define CODEVIEW_SIGNATURE_NB10 SIGNATURE_32('N', 'B', '1', '0') +typedef struct { + UINT32 Signature; ///< "NB10" + UINT32 Unknown; + UINT32 Unknown2; + UINT32 Unknown3; + // + // Filename of .PDB goes here + // +} EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY; + +/// +/// Debug Data Structure defined in Microsoft C++. +/// +#define CODEVIEW_SIGNATURE_RSDS SIGNATURE_32('R', 'S', 'D', 'S') +typedef struct { + UINT32 Signature; ///< "RSDS". + UINT32 Unknown; + UINT32 Unknown2; + UINT32 Unknown3; + UINT32 Unknown4; + UINT32 Unknown5; + // + // Filename of .PDB goes here + // +} EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY; + + +/// +/// Debug Data Structure defined by Apple Mach-O to Coff utility. +/// +#define CODEVIEW_SIGNATURE_MTOC SIGNATURE_32('M', 'T', 'O', 'C') +typedef struct { + UINT32 Signature; ///< "MTOC". + UINT64 MachOUuid[2]; + // + // Filename of .DLL (Mach-O with debug info) goes here + // +} EFI_IMAGE_DEBUG_CODEVIEW_MTOC_ENTRY; + +/// +/// Resource directory entry format. +/// +typedef struct { + union { + struct { + UINT32 NameOffset:31; + UINT32 NameIsString:1; + } s; + UINT32 Id; + } u1; + union { + UINT32 OffsetToData; + struct { + UINT32 OffsetToDirectory:31; + UINT32 DataIsDirectory:1; + } s; + } u2; +} EFI_IMAGE_RESOURCE_DIRECTORY_ENTRY; + +/// +/// Resource format. +/// +typedef struct { + UINT32 Characteristics; + UINT32 TimeDateStamp; + UINT16 MajorVersion; + UINT16 MinorVersion; + UINT16 NumberOfNamedEntries; + UINT16 NumberOfIdEntries; + EFI_IMAGE_RESOURCE_DIRECTORY_ENTRY Entires[]; +} EFI_IMAGE_RESOURCE_DIRECTORY; + +/// +/// Resource directory entry for string. +/// +typedef struct { + UINT16 Length; + CHAR16 String[1]; +} EFI_IMAGE_RESOURCE_DIRECTORY_STRING; + +/// +/// Resource directory entry for data array. +/// +typedef struct { + UINT32 OffsetToData; + UINT32 Size; + UINT32 CodePage; + UINT32 Reserved; +} EFI_IMAGE_RESOURCE_DATA_ENTRY; + +/// +/// Header format for TE images, defined in the PI Specification, 1.0. +/// +typedef struct { + UINT16 Signature; ///< The signature for TE format = "VZ". + UINT16 Machine; ///< From the original file header. + UINT8 NumberOfSections; ///< From the original file header. + UINT8 Subsystem; ///< From original optional header. + UINT16 StrippedSize; ///< Number of bytes we removed from the header. + UINT32 AddressOfEntryPoint; ///< Offset to entry point -- from original optional header. + UINT32 BaseOfCode; ///< From original image -- required for ITP debug. + UINT64 ImageBase; ///< From original file header. + EFI_IMAGE_DATA_DIRECTORY DataDirectory[2]; ///< Only base relocation and debug directory. +} EFI_TE_IMAGE_HEADER; + + +#define EFI_TE_IMAGE_HEADER_SIGNATURE SIGNATURE_16('V', 'Z') + +// +// Data directory indexes in our TE image header +// +#define EFI_TE_IMAGE_DIRECTORY_ENTRY_BASERELOC 0 +#define EFI_TE_IMAGE_DIRECTORY_ENTRY_DEBUG 1 + + +/// +/// Union of PE32, PE32+, and TE headers. +/// +typedef union { + EFI_IMAGE_NT_HEADERS_COMMON_HDR PeCommon; + EFI_IMAGE_NT_HEADERS32 Pe32; + EFI_IMAGE_NT_HEADERS64 Pe32Plus; + EFI_TE_IMAGE_HEADER Te; +} EFI_IMAGE_OPTIONAL_HEADER_UNION; + +#endif // OC_PE_IMAGE_H diff --git a/Include/Acidanthera/Library/OcAppleSecureBootLib.h b/Include/Acidanthera/Library/OcAppleSecureBootLib.h index 7336ddcb..30d08e85 100644 --- a/Include/Acidanthera/Library/OcAppleSecureBootLib.h +++ b/Include/Acidanthera/Library/OcAppleSecureBootLib.h @@ -72,11 +72,13 @@ OcAppleSecureBootSetDmgLoading ( /** Get DMG loading status on Apple Secure Boot protocol. + @param[out] RealPolicy Actual secure boot policy, optional. + @retval TRUE when loading DMG. **/ BOOLEAN OcAppleSecureBootGetDmgLoading ( - VOID + OUT UINT8 *RealPolicy OPTIONAL ); /** diff --git a/Include/Acidanthera/Library/OcBootManagementLib.h b/Include/Acidanthera/Library/OcBootManagementLib.h index fa619af2..a339f3a8 100755 --- a/Include/Acidanthera/Library/OcBootManagementLib.h +++ b/Include/Acidanthera/Library/OcBootManagementLib.h @@ -1238,4 +1238,43 @@ OcRegisterBootOption ( IN CONST CHAR16 *FilePath ); +/** + Initialises custom Boot Services overrides to support direct images. +**/ +VOID +OcInitDirectImageLoader ( + VOID + ); + +/** + Make DirectImageLoader the default for Apple Secure Boot. +**/ +VOID +OcActivateDirectImageLoader ( + VOID + ); + +/** + Simplified load image routine, which bypasses UEFI and loads the image directly. + + @param[in] BootPolicy Ignored. + @param[in] ParentImageHandle The caller's image handle. + @param[in] DevicePath Ignored. + @param[in] SourceBuffer Pointer to the memory location containing image to be loaded. + @param[in] SourceSize The size in bytes of SourceBuffer. + @param[out] ImageHandle The pointer to the returned image handle created on success. + + @retval EFI_SUCCESS on success. +**/ +EFI_STATUS +EFIAPI +OcDirectLoadImage ( + IN BOOLEAN BootPolicy, + IN EFI_HANDLE ParentImageHandle, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + IN VOID *SourceBuffer OPTIONAL, + IN UINTN SourceSize, + OUT EFI_HANDLE *ImageHandle + ); + #endif // OC_BOOT_MANAGEMENT_LIB_H diff --git a/Include/Acidanthera/Library/OcGuardLib.h b/Include/Acidanthera/Library/OcGuardLib.h index 29b1d623..9f08de46 100644 --- a/Include/Acidanthera/Library/OcGuardLib.h +++ b/Include/Acidanthera/Library/OcGuardLib.h @@ -109,6 +109,13 @@ OcOverflowMulAddU32 ( UINT32 *Result ); +BOOLEAN +OcOverflowAlignUpU32 ( + UINT32 Value, + UINT32 Alignment, + UINT32 *Result + ); + BOOLEAN OcOverflowAddS32 ( INT32 A, diff --git a/Include/Acidanthera/Library/OcPeCoffLib.h b/Include/Acidanthera/Library/OcPeCoffLib.h new file mode 100644 index 00000000..f38b4cf1 --- /dev/null +++ b/Include/Acidanthera/Library/OcPeCoffLib.h @@ -0,0 +1,198 @@ +/** @file + Provides services to load and relocate a PE/COFF image. + + The PE/COFF Loader Library abstracts the implementation of a PE/COFF loader for + IA-32, x86, and EBC processor types. The library functions are memory-based + and can be ported easily to any environment. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef OC_PE_COFF_LIB_H +#define OC_PE_COFF_LIB_H + +#include + +// TODO: move? +/** + Performs digest on a data buffer of the specified length. This function can + be called multiple times to compute the digest of long or discontinuous data streams. + + If HashContext is NULL, then ASSERT(). + + @param[in,out] HashContext Pointer to the MD5 context. + @param[in] Data Pointer to the buffer containing the data to be hashed. + @param[in] DataLength Length of Data buffer in bytes. + + @retval TRUE HASH data digest succeeded. + @retval FALSE Invalid HASH context. After HashFinal function has been called, the + HASH context cannot be reused. + +**/ +typedef +BOOLEAN +(EFIAPI *HASH_UPDATE)( + IN OUT VOID *HashContext, + IN CONST VOID *Data, + IN UINTN DataLength + ); + +// +// Return status codes from the PE/COFF Loader services +// +#define IMAGE_ERROR_SUCCESS 0U +#define IMAGE_ERROR_IMAGE_READ 1U +#define IMAGE_ERROR_INVALID_PE_HEADER_SIGNATURE 2U +#define IMAGE_ERROR_INVALID_MACHINE_TYPE 3U +#define IMAGE_ERROR_INVALID_SUBSYSTEM 4U +#define IMAGE_ERROR_INVALID_IMAGE_ADDRESS 5U +#define IMAGE_ERROR_INVALID_IMAGE_SIZE 6U +#define IMAGE_ERROR_INVALID_SECTION_ALIGNMENT 7U +#define IMAGE_ERROR_SECTION_NOT_LOADED 8U +#define IMAGE_ERROR_FAILED_RELOCATION 9U +#define IMAGE_ERROR_FAILED_ICACHE_FLUSH 10U +#define IMAGE_ERROR_UNSUPPORTED 11U + +typedef UINTN IMAGE_STATUS; + +typedef enum { + ImageTypeTe, + ImageTypePe32, + ImageTypePe32Plus, + ImageTypeMax +} IMAGE_LOADER_IMAGE_TYPE; + +/// +/// The context structure used while PE/COFF image is being loaded and relocated. +/// +typedef struct { + /// + /// Set by OcPeCoffLoaderInitializeContext() to the ImageBase in the PE/COFF header. + /// + UINT64 ImageBase; + // + // Before LoadImage returns, a pointer to the raw file image. + // After LoadImage returns, a pointer to the loaded image. + // + VOID *FileBuffer; + /// + /// Set by OcPeCoffLoaderInitializeContext() to the SizeOfImage in the PE/COFF header. + /// Image size includes the size of Debug Entry if it is present. + /// + UINT32 SizeOfImage; + UINT32 SectionAlignment; + /// + /// Set by OcPeCoffLoaderInitializeContext() to offset to the PE/COFF header. + /// If the PE/COFF image does not start with a DOS header, this value is zero. + /// Otherwise, it's the offset to the PE/COFF header. + /// + UINT32 ExeHdrOffset; + /// + /// Is set by OcPeCoffLoaderInitializeContext() to the Section Alignment in the PE/COFF header. + /// + UINT32 SizeOfHeaders; + UINT32 AddressOfEntryPoint; + /// + /// Set by OcPeCoffLoaderInitializeContext() to TRUE if the PE/COFF image does not contain + /// relocation information. + /// + BOOLEAN RelocsStripped; + /// + /// Set by OcPeCoffLoaderInitializeContext() to TRUE if the image is a TE image. + /// For a definition of the TE Image format, see the Platform Initialization Pre-EFI + /// Initialization Core Interface Specification. + /// + UINT8 ImageType; + UINT16 Subsystem; + UINT16 Machine; + UINT32 TeStrippedOffset; + + UINT32 RelocDirRva; + UINT32 RelocDirSize; +} PE_COFF_LOADER_IMAGE_CONTEXT; + +/** + Retrieves information about a PE/COFF image. + + Computes the ExeHdrOffset, IsTeImage, ImageType, ImageAddress, ImageSize, + DestinationAddress, RelocsStripped, SectionAlignment, SizeOfHeaders, and + DebugDirectoryEntryRva fields of the ImageContext structure. + If ImageContext is NULL, then return RETURN_INVALID_PARAMETER. + If the PE/COFF image accessed through the ImageRead service in the ImageContext + structure is not a supported PE/COFF image type, then return RETURN_UNSUPPORTED. + If any errors occur while computing the fields of ImageContext, + then the error status is returned in the ImageError field of ImageContext. + If the image is a TE image, then SectionAlignment is set to 0. + The ImageRead and Handle fields of ImageContext structure must be valid prior + to invoking this service. + + @param ImageContext The pointer to the image context structure that + describes the PE/COFF image that needs to be + examined by this function. + + @retval RETURN_SUCCESS The information on the PE/COFF image was collected. + @retval RETURN_INVALID_PARAMETER ImageContext is NULL. + @retval RETURN_UNSUPPORTED The PE/COFF image is not supported. + +**/ +IMAGE_STATUS +OcPeCoffLoaderInitializeContext ( + OUT PE_COFF_LOADER_IMAGE_CONTEXT *Context, + IN CONST VOID *FileBuffer, + IN UINTN FileSize + ); + +/** + Loads a PE/COFF image into memory. + + Loads the PE/COFF image accessed through the ImageRead service of ImageContext into the buffer + specified by the ImageAddress and ImageSize fields of ImageContext. The caller must allocate + the load buffer and fill in the ImageAddress and ImageSize fields prior to calling this function. + The EntryPoint, FixupDataSize, CodeView, PdbPointer and HiiResourceData fields of ImageContext are computed. + The ImageRead, Handle, ExeHdrOffset, IsTeImage, Machine, ImageType, ImageAddress, ImageSize, + DestinationAddress, RelocsStripped, SectionAlignment, SizeOfHeaders, and DebugDirectoryEntryRva + fields of the ImageContext structure must be valid prior to invoking this service. + + If ImageContext is NULL, then ASSERT(). + + Note that if the platform does not maintain coherency between the instruction cache(s) and the data + cache(s) in hardware, then the caller is responsible for performing cache maintenance operations + prior to transferring control to a PE/COFF image that is loaded using this library. + + @param ImageContext The pointer to the image context structure that describes the PE/COFF + image that is being loaded. + + @retval RETURN_SUCCESS The PE/COFF image was loaded into the buffer specified by + the ImageAddress and ImageSize fields of ImageContext. + Extended status information is in the ImageError field of ImageContext. + @retval RETURN_BUFFER_TOO_SMALL The caller did not provide a large enough buffer. + Extended status information is in the ImageError field of ImageContext. + @retval RETURN_LOAD_ERROR The PE/COFF image is an EFI Runtime image with no relocations. + Extended status information is in the ImageError field of ImageContext. + @retval RETURN_INVALID_PARAMETER The image address is invalid. + Extended status information is in the ImageError field of ImageContext. + +**/ +IMAGE_STATUS +OcPeCoffLoaderLoadImage ( + IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *Context, + OUT VOID *Destination, + IN UINT32 DestinationSize + ); + +IMAGE_STATUS +OcPeCoffLoaderRelocateImage ( + IN CONST PE_COFF_LOADER_IMAGE_CONTEXT *Context, + IN UINTN BaseAddress + ); + +BOOLEAN +OcPeCoffLoaderHashImage ( + IN CONST PE_COFF_LOADER_IMAGE_CONTEXT *Context, + IN HASH_UPDATE HashUpdate, + IN OUT VOID *HashContext + ); + +#endif // OC_PE_COFF_LIB_H diff --git a/Library/OcApfsLib/OcApfsConnect.c b/Library/OcApfsLib/OcApfsConnect.c index ca7ef3f0..adb42824 100644 --- a/Library/OcApfsLib/OcApfsConnect.c +++ b/Library/OcApfsLib/OcApfsConnect.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -239,6 +240,9 @@ ApfsStartDriver ( EFI_DEVICE_PATH_PROTOCOL *DevicePath; EFI_HANDLE ImageHandle; EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; + EFI_IMAGE_LOAD LoadImage; + APPLE_SECURE_BOOT_PROTOCOL *SecureBoot; + UINT8 Policy; Status = VerifyApplePeImageSignature ( DriverBuffer, @@ -272,8 +276,26 @@ ApfsStartDriver ( DevicePath = NULL; } + SecureBoot = OcAppleSecureBootGetProtocol (); + ASSERT (SecureBoot != NULL); + Status = SecureBoot->GetPolicy ( + SecureBoot, + &Policy + ); + // + // Load directly when we have Apple Secure Boot. + // - Either normal. + // - Or during DMG loading. + // + if ((!EFI_ERROR (Status) && Policy != AppleImg4SbModeDisabled) + || (OcAppleSecureBootGetDmgLoading (&Policy) && Policy != AppleImg4SbModeDisabled)) { + LoadImage = OcDirectLoadImage; + } else { + LoadImage = gBS->LoadImage; + } + ImageHandle = NULL; - Status = gBS->LoadImage ( + Status = LoadImage ( FALSE, gImageHandle, DevicePath, diff --git a/Library/OcAppleSecureBootLib/OcAppleSecureBootLib.c b/Library/OcAppleSecureBootLib/OcAppleSecureBootLib.c index 9cb66b9e..efbe313a 100644 --- a/Library/OcAppleSecureBootLib/OcAppleSecureBootLib.c +++ b/Library/OcAppleSecureBootLib/OcAppleSecureBootLib.c @@ -37,6 +37,7 @@ STATIC APPLE_SECURE_BOOT_PROTOCOL *mSecureBoot; STATIC CHAR8 mSbHardwareModel[16]; STATIC UINT64 mSbEcid; STATIC BOOLEAN mDmgLoading = FALSE; +STATIC UINT8 mDmgLoadingPolicy = AppleImg4SbModeMedium; STATIC BOOLEAN mSbAvailable = TRUE; STATIC UINT8 mSbPolicy = AppleImg4SbModeMedium; STATIC UINT8 mSbWindowsPolicy = 1; @@ -958,24 +959,33 @@ OcAppleSecureBootSetDmgLoading ( IN BOOLEAN LoadingDmg ) { + EFI_STATUS Status; ASSERT (mSecureBoot != NULL); mDmgLoading = LoadingDmg; + Status = mSecureBoot->GetPolicy (mSecureBoot, &mDmgLoadingPolicy); + if (EFI_ERROR (Status)) { + mDmgLoadingPolicy = AppleImg4SbModeMedium; + } + if (LoadingDmg) { - DEBUG ((DEBUG_INFO, "OCB: Disabling secure boot for Apple images\n")); + DEBUG ((DEBUG_INFO, "OCSB: Disabling secure boot for Apple images\n")); mSecureBoot->SetAvailability (mSecureBoot, FALSE); } else { - DEBUG ((DEBUG_INFO, "OCB: Reenabling secure boot after Apple images\n")); + DEBUG ((DEBUG_INFO, "OCSB: Reenabling secure boot after Apple images\n")); mSecureBoot->SetAvailability (mSecureBoot, FALSE); } } BOOLEAN OcAppleSecureBootGetDmgLoading ( - VOID + OUT UINT8 *RealPolicy OPTIONAL ) { + if (RealPolicy != NULL) { + *RealPolicy = mDmgLoadingPolicy; + } return mDmgLoading; } @@ -1015,7 +1025,8 @@ OcAppleSecureBootVerify ( // they do not even have global manifests in DMG images. // Can consider checking boot.efi codesign integrity if we want. // - if (Policy == AppleImg4SbModeDisabled && OcAppleSecureBootGetDmgLoading ()) { + if (Policy == AppleImg4SbModeDisabled && OcAppleSecureBootGetDmgLoading (NULL)) { + DEBUG ((DEBUG_INFO, "OCSB: Direct booting for DMG image\n")); return EFI_SUCCESS; } @@ -1023,6 +1034,7 @@ OcAppleSecureBootVerify ( // For everything else it is unsupported, meaning let the system decide. // if (Policy == AppleImg4SbModeDisabled) { + DEBUG ((DEBUG_INFO, "OCSB: Secure boot is disabled, skipping\n")); return EFI_UNSUPPORTED; } @@ -1034,7 +1046,7 @@ OcAppleSecureBootVerify ( &ManifestSize ); if (EFI_ERROR (Status)) { - DEBUG ((DEBUG_INFO, "OCB: No IMG4 found - %r\n", Status)); + DEBUG ((DEBUG_INFO, "OCSB: No IMG4 found - %r\n", Status)); return EFI_UNSUPPORTED; } @@ -1055,18 +1067,12 @@ OcAppleSecureBootVerify ( FALSE ); - // - // This is our signature, but the file is corrupted. - // - if (Status == EFI_SECURITY_VIOLATION) { - DEBUG ((DEBUG_WARN, "OCB: IMG4 %08X verification gave secure violation\n")); - return EFI_SECURITY_VIOLATION; - } - // // We are successful. // if (!EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "OCSB: Verified IMG4 without issues\n")); + FreePool (ManifestBuffer); return Status; } } @@ -1074,5 +1080,7 @@ OcAppleSecureBootVerify ( // // No suitable signature. // + DEBUG ((DEBUG_INFO, "OCSB: No suitable signature - %r\n", Status)); + FreePool (ManifestBuffer); return EFI_UNSUPPORTED; } diff --git a/Library/OcBootManagementLib/BootEntryManagement.c b/Library/OcBootManagementLib/BootEntryManagement.c index a5e82a3d..0869f85c 100644 --- a/Library/OcBootManagementLib/BootEntryManagement.c +++ b/Library/OcBootManagementLib/BootEntryManagement.c @@ -1841,7 +1841,7 @@ OcLoadBootEntry ( if (!EFI_ERROR (Status)) { Status = Context->StartImage (BootEntry, EntryHandle, NULL, NULL); if (EFI_ERROR (Status)) { - DEBUG ((DEBUG_ERROR, "OCB: StartImage failed - %r\n", Status)); + DEBUG ((DEBUG_WARN, "OCB: StartImage failed - %r\n", Status)); // // Unload dmg if any. // diff --git a/Library/OcBootManagementLib/BootManagementInternal.h b/Library/OcBootManagementLib/BootManagementInternal.h index 1a8164d5..0811f1cf 100644 --- a/Library/OcBootManagementLib/BootManagementInternal.h +++ b/Library/OcBootManagementLib/BootManagementInternal.h @@ -176,14 +176,4 @@ InternalSystemActionResetNvram ( VOID ); -/** - Initialises custom gBS->LoadImage override. - - @retval EFI_SUCCESS on success. -**/ -EFI_STATUS -InternalInitImageLoader ( - VOID - ); - #endif // BOOT_MANAGEMENET_INTERNAL_H diff --git a/Library/OcBootManagementLib/ImageLoader.c b/Library/OcBootManagementLib/ImageLoader.c index d78ffe9c..9c5a0335 100644 --- a/Library/OcBootManagementLib/ImageLoader.c +++ b/Library/OcBootManagementLib/ImageLoader.c @@ -14,7 +14,10 @@ #include "BootManagementInternal.h" +#include + #include +#include #include #include @@ -33,12 +36,44 @@ #include #include #include +#include #include #include #include #include -STATIC EFI_IMAGE_LOAD mOriginalEfiLoadImage; +#if defined(MDE_CPU_IA32) + #define OC_IMAGE_FILE_MACHINE IMAGE_FILE_MACHINE_I386 +#elif defined(MDE_CPU_X64) + #define OC_IMAGE_FILE_MACHINE IMAGE_FILE_MACHINE_X64 +#else + #error Unsupported architecture. +#endif + +STATIC EFI_GUID mOcLoadedImageProtocolGuid = { + 0x1f3c963d, 0xf9dc, 0x4537, { 0xbb, 0x06, 0xd8, 0x08, 0x46, 0x4a, 0x85, 0x2e } +}; + +typedef struct { + EFI_IMAGE_ENTRY_POINT EntryPoint; + EFI_PHYSICAL_ADDRESS ImageArea; + UINTN PageCount; + EFI_STATUS Status; + VOID *JumpBuffer; + BASE_LIBRARY_JUMP_BUFFER *JumpContext; + CHAR16 *ExitData; + UINTN ExitDataSize; + UINT16 Subsystem; + BOOLEAN Started; + EFI_LOADED_IMAGE_PROTOCOL LoadedImage; +} OC_LOADED_IMAGE_PROTOCOL; + +STATIC EFI_IMAGE_LOAD mOriginalEfiLoadImage; +STATIC EFI_IMAGE_START mOriginalEfiStartImage; +STATIC EFI_IMAGE_UNLOAD mOriginalEfiUnloadImage; +STATIC EFI_EXIT mOriginalEfiExit; +STATIC EFI_HANDLE mCurrentImageHandle; +STATIC BOOLEAN mDirectImageLoaderEnabled; STATIC EFI_STATUS @@ -148,6 +183,409 @@ InternalUpdateLoadedImage ( return EFI_SUCCESS; } +EFI_STATUS +EFIAPI +OcDirectLoadImage ( + IN BOOLEAN BootPolicy, + IN EFI_HANDLE ParentImageHandle, + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + IN VOID *SourceBuffer OPTIONAL, + IN UINTN SourceSize, + OUT EFI_HANDLE *ImageHandle + ) +{ + EFI_STATUS Status; + IMAGE_STATUS ImageStatus; + PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; + EFI_PHYSICAL_ADDRESS DestinationArea; + VOID *DestinationBuffer; + OC_LOADED_IMAGE_PROTOCOL *OcLoadedImage; + EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; + + ASSERT (SourceBuffer != NULL); + + // + // Initialize the image context. + // + ImageStatus = OcPeCoffLoaderInitializeContext ( + &ImageContext, + SourceBuffer, + SourceSize + ); + if (ImageStatus != IMAGE_ERROR_SUCCESS) { + DEBUG ((DEBUG_INFO, "OCB: PeCoff init failure - %d\n", ImageStatus)); + return EFI_UNSUPPORTED; + } + // + // Reject images that are not meant for the platform's architecture. + // + if (ImageContext.Machine != OC_IMAGE_FILE_MACHINE) { + DEBUG ((DEBUG_INFO, "OCB: PeCoff wrong machine - %x\n", ImageContext.Machine)); + return EFI_UNSUPPORTED; + } + // + // Reject RT drivers for the moment. + // + if (ImageContext.Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER) { + DEBUG ((DEBUG_INFO, "OCB: PeCoff no support for RT drivers\n")); + return EFI_UNSUPPORTED; + } + // + // Allocate the image destination memory. + // FIXME: RT drivers require EfiRuntimeServicesCode. + // + Status = gBS->AllocatePages ( + AllocateAnyPages, + ImageContext.Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION + ? EfiLoaderCode : EfiBootServicesCode, + EFI_SIZE_TO_PAGES (ImageContext.SizeOfImage), + &DestinationArea + ); + if (EFI_ERROR (Status)) { + return Status; + } + + DestinationBuffer = (VOID *)(UINTN) DestinationArea; + + // + // Load SourceBuffer into DestinationBuffer. + // + ImageStatus = OcPeCoffLoaderLoadImage ( + &ImageContext, + DestinationBuffer, + ImageContext.SizeOfImage + ); + if (ImageStatus != IMAGE_ERROR_SUCCESS) { + DEBUG ((DEBUG_INFO, "OCB: PeCoff load image error - %d\n", ImageStatus)); + FreePages (DestinationBuffer, EFI_SIZE_TO_PAGES (ImageContext.SizeOfImage)); + return EFI_UNSUPPORTED; + } + // + // Relocate the loaded image to the destination address. + // + ImageStatus = OcPeCoffLoaderRelocateImage ( + &ImageContext, + (UINTN) DestinationBuffer + ); + if (ImageStatus != IMAGE_ERROR_SUCCESS) { + DEBUG ((DEBUG_INFO, "OCB: PeCoff relocate image error - %d\n", ImageStatus)); + FreePages (DestinationBuffer, EFI_SIZE_TO_PAGES (ImageContext.SizeOfImage)); + return EFI_UNSUPPORTED; + } + // + // Construct a LoadedImage protocol for the image. + // + OcLoadedImage = AllocateZeroPool (sizeof (*OcLoadedImage)); + if (OcLoadedImage == NULL) { + FreePages (DestinationBuffer, EFI_SIZE_TO_PAGES (ImageContext.SizeOfImage)); + return EFI_OUT_OF_RESOURCES; + } + + OcLoadedImage->EntryPoint = (EFI_IMAGE_ENTRY_POINT) ((UINTN) DestinationBuffer + ImageContext.AddressOfEntryPoint); + OcLoadedImage->ImageArea = DestinationArea; + OcLoadedImage->PageCount = EFI_SIZE_TO_PAGES (ImageContext.SizeOfImage); + OcLoadedImage->Subsystem = ImageContext.Subsystem; + + LoadedImage = &OcLoadedImage->LoadedImage; + + LoadedImage->Revision = EFI_LOADED_IMAGE_INFORMATION_REVISION; + LoadedImage->ParentHandle = ParentImageHandle; + LoadedImage->SystemTable = gST; + LoadedImage->ImageBase = DestinationBuffer; + LoadedImage->ImageSize = ImageContext.SizeOfImage; + // + // FIXME: Support RT drivers. + // + if (ImageContext.Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION) { + LoadedImage->ImageCodeType = EfiLoaderCode; + LoadedImage->ImageDataType = EfiLoaderData; + } else { + LoadedImage->ImageCodeType = EfiBootServicesCode; + LoadedImage->ImageDataType = EfiBootServicesData; + } + // + // Install LoadedImage and the image's entry point. + // + *ImageHandle = NULL; + Status = gBS->InstallMultipleProtocolInterfaces ( + ImageHandle, + &gEfiLoadedImageProtocolGuid, + LoadedImage, + &mOcLoadedImageProtocolGuid, + OcLoadedImage, + NULL + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "OCB: PeCoff proto install error - %r\n", Status)); + FreePool (OcLoadedImage); + FreePages (DestinationBuffer, EFI_SIZE_TO_PAGES (ImageContext.SizeOfImage)); + return Status; + } + + DEBUG ((DEBUG_VERBOSE, "OCB: Loaded image at %p\n", *ImageHandle)); + + return EFI_SUCCESS; +} + +/** + Unload image routine for OcDirectLoadImage. + + @param[in] OcLoadedImage Our loaded image instance. + @param[in] ImageHandle Handle that identifies the image to be unloaded. + + @retval EFI_SUCCESS The image has been unloaded. +**/ +STATIC +EFI_STATUS +InternalDirectUnloadImage ( + IN OC_LOADED_IMAGE_PROTOCOL *OcLoadedImage, + IN EFI_HANDLE ImageHandle + ) +{ + EFI_STATUS Status; + EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; + + LoadedImage = &OcLoadedImage->LoadedImage; + if (LoadedImage->Unload != NULL) { + Status = LoadedImage->Unload (ImageHandle); + if (EFI_ERROR (Status)) { + return Status; + } + // + // Do not allow to execute Unload multiple times. + // + LoadedImage->Unload = NULL; + } else if (OcLoadedImage->Started) { + return EFI_UNSUPPORTED; + } + + Status = gBS->UninstallMultipleProtocolInterfaces ( + ImageHandle, + &gEfiLoadedImageProtocolGuid, + LoadedImage, + &mOcLoadedImageProtocolGuid, + OcLoadedImage, + NULL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + gBS->FreePages (OcLoadedImage->ImageArea, OcLoadedImage->PageCount); + FreePool (OcLoadedImage); + // + // NOTE: Avoid EFI 1.10 extension of closing opened protocols. + // + return EFI_SUCCESS; +} + +/** + Unload image routine for OcDirectLoadImage. + + @param[in] OcLoadedImage Our loaded image instance. + @param[in] ImageHandle Handle that identifies the image to be unloaded. + @param[in] ExitStatus The image's exit code. + @param[in] ExitDataSize The size, in bytes, of ExitData. Ignored if ExitStatus is EFI_SUCCESS. + @param[in] ExitData The pointer to a data buffer that includes a Null-terminated string, + optionally followed by additional binary data. The string is a + description that the caller may use to further indicate the reason + for the image's exit. ExitData is only valid if ExitStatus + is something other than EFI_SUCCESS. The ExitData buffer + must be allocated by calling AllocatePool(). + + @retval EFI_SUCCESS The image has been unloaded. +**/ +STATIC +EFI_STATUS +InternalDirectExit ( + IN OC_LOADED_IMAGE_PROTOCOL *OcLoadedImage, + IN EFI_HANDLE ImageHandle, + IN EFI_STATUS ExitStatus, + IN UINTN ExitDataSize, + IN CHAR16 *ExitData OPTIONAL + ) +{ + EFI_TPL OldTpl; + + DEBUG (( + DEBUG_VERBOSE, "OCB: Exit %p %p (%d) - %r\n", + ImageHandle, + mCurrentImageHandle, + OcLoadedImage->Started, + ExitStatus + )); + + // + // Prevent possible reentrance to this function for the same ImageHandle. + // + OldTpl = gBS->RaiseTPL (TPL_NOTIFY); + + // + // If the image has not been started just free its resources. + // Should not happen normally. + // + if (!OcLoadedImage->Started) { + InternalDirectUnloadImage (OcLoadedImage, ImageHandle); + gBS->RestoreTPL (OldTpl); + return EFI_SUCCESS; + } + + // + // If the image has been started, verify this image can exit. + // + if (ImageHandle != mCurrentImageHandle) { + DEBUG ((DEBUG_LOAD|DEBUG_ERROR, "Exit: Image is not exitable image\n")); + gBS->RestoreTPL (OldTpl); + return EFI_INVALID_PARAMETER; + } + + // + // Set the return status. + // + OcLoadedImage->Status = ExitStatus; + + // + // If there's ExitData info provide it. + // + if (ExitData != NULL) { + OcLoadedImage->ExitDataSize = ExitDataSize; + OcLoadedImage->ExitData = AllocatePool (OcLoadedImage->ExitDataSize); + if (OcLoadedImage->ExitData != NULL) { + CopyMem (OcLoadedImage->ExitData, ExitData, OcLoadedImage->ExitDataSize); + } else { + OcLoadedImage->ExitDataSize = 0; + } + } + + // + // return to StartImage + // + gBS->RestoreTPL (OldTpl); + LongJump (OcLoadedImage->JumpContext, (UINTN)-1); + + // + // If we return from LongJump, then it is an error + // + ASSERT (FALSE); + CpuDeadLoop (); + return EFI_ACCESS_DENIED; +} + + +/** + Simplified start image routine for OcDirectLoadImage. + + @param[in] OcLoadedImage Our loaded image instance. + @param[in] ImageHandle Handle of image to be started. + @param[out] ExitDataSize The pointer to the size, in bytes, of ExitData. + @param[out] ExitData The pointer to a pointer to a data buffer that includes a Null-terminated + string, optionally followed by additional binary data. + + @retval EFI_SUCCESS on success. +**/ +STATIC +EFI_STATUS +InternalDirectStartImage ( + IN OC_LOADED_IMAGE_PROTOCOL *OcLoadedImage, + IN EFI_HANDLE ImageHandle, + OUT UINTN *ExitDataSize, + OUT CHAR16 **ExitData OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_HANDLE LastImage; + UINTN SetJumpFlag; + + // + // Push the current image. + // + LastImage = mCurrentImageHandle; + mCurrentImageHandle = ImageHandle; + + // + // Set long jump for Exit() support + // JumpContext must be aligned on a CPU specific boundary. + // Overallocate the buffer and force the required alignment + // + OcLoadedImage->JumpBuffer = AllocatePool ( + sizeof (BASE_LIBRARY_JUMP_BUFFER) + BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT + ); + if (OcLoadedImage->JumpBuffer == NULL) { + // + // Pop the current start image context + // + mCurrentImageHandle = LastImage; + return EFI_OUT_OF_RESOURCES; + } + + OcLoadedImage->JumpContext = ALIGN_POINTER ( + OcLoadedImage->JumpBuffer, BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT + ); + + SetJumpFlag = SetJump (OcLoadedImage->JumpContext); + // + // The initial call to SetJump() must always return 0. + // Subsequent calls to LongJump() cause a non-zero value to be returned by SetJump(). + // + if (SetJumpFlag == 0) { + // + // Invoke the manually loaded image entry point. + // + DEBUG ((DEBUG_VERBOSE, "OCB: Starting image %p\n", ImageHandle)); + OcLoadedImage->Started = TRUE; + OcLoadedImage->Status = OcLoadedImage->EntryPoint ( + ImageHandle, + OcLoadedImage->LoadedImage.SystemTable + ); + // + // If the image returns, exit it through Exit() + // + InternalDirectExit (OcLoadedImage, ImageHandle, OcLoadedImage->Status, 0, NULL); + } + + FreePool (OcLoadedImage->JumpBuffer); + + // + // Pop the current image. + // + mCurrentImageHandle = LastImage; + + // + // NOTE: EFI 1.10 is not supported, refer to + // https://github.com/tianocore/edk2/blob/d8dd54f071cfd60a2dcf5426764a89cd91213420/MdeModulePkg/Core/Dxe/Image/Image.c#L1686-L1697 + // + + // + // Return the exit data to the caller + // + if (ExitData != NULL && ExitDataSize != NULL) { + *ExitDataSize = OcLoadedImage->ExitDataSize; + *ExitData = OcLoadedImage->ExitData; + } else if (OcLoadedImage->ExitData != NULL) { + // + // Caller doesn't want the exit data, free it + // + FreePool (OcLoadedImage->ExitData); + OcLoadedImage->ExitData = NULL; + } + + // + // Save the Status because Image will get destroyed if it is unloaded. + // + Status = OcLoadedImage->Status; + + // + // If the image returned an error, or if the image is an application + // unload it + // + if (EFI_ERROR (OcLoadedImage->Status) + || OcLoadedImage->Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION) { + InternalDirectUnloadImage (OcLoadedImage, ImageHandle); + } + + return Status; +} + STATIC EFI_STATUS EFIAPI @@ -198,7 +636,7 @@ InternalEfiLoadImage ( } } - if (DevicePath != NULL && SourceBuffer != NULL) { + if (DevicePath != NULL && SourceBuffer != NULL && mDirectImageLoaderEnabled) { SecureBootStatus = OcAppleSecureBootVerify ( DevicePath, SourceBuffer, @@ -226,6 +664,17 @@ InternalEfiLoadImage ( #else Status = FatFilterArchitecture64 ((UINT8 **) &SourceBuffer, &RealSize); #endif + + DEBUG (( + DEBUG_INFO, + "OCB: Arch filtering %p(%u)->%p(%u) - %r\n", + AllocatedBuffer, + (UINT32) SourceSize, + SourceBuffer, + RealSize, + Status + )); + if (!EFI_ERROR (Status)) { SourceSize = RealSize; } else if (AllocatedBuffer != NULL) { @@ -234,21 +683,37 @@ InternalEfiLoadImage ( } } + // + // Load the image ourselves in secure boot mode. + // if (SecureBootStatus == EFI_SUCCESS) { - // - // TODO: Here we should use a custom COFF loader! - // + if (SourceBuffer != NULL) { + Status = OcDirectLoadImage ( + FALSE, + ParentImageHandle, + DevicePath, + SourceBuffer, + SourceSize, + ImageHandle + ); + } else { + // + // We verified the image, but contained garbage. + // This should not happen, just abort. + // + Status = EFI_UNSUPPORTED; + } + } else { + Status = mOriginalEfiLoadImage ( + BootPolicy, + ParentImageHandle, + DevicePath, + SourceBuffer, + SourceSize, + ImageHandle + ); } - Status = mOriginalEfiLoadImage ( - BootPolicy, - ParentImageHandle, - DevicePath, - SourceBuffer, - SourceSize, - ImageHandle - ); - if (AllocatedBuffer != NULL) { FreePool (AllocatedBuffer); } @@ -264,15 +729,126 @@ InternalEfiLoadImage ( return Status; } +STATIC EFI_STATUS -InternalInitImageLoader ( +EFIAPI +InternalEfiStartImage ( + IN EFI_HANDLE ImageHandle, + OUT UINTN *ExitDataSize, + OUT CHAR16 **ExitData OPTIONAL + ) +{ + EFI_STATUS Status; + OC_LOADED_IMAGE_PROTOCOL *OcLoadedImage; + + // + // If we loaded the image, invoke the entry point manually. + // + Status = gBS->HandleProtocol ( + ImageHandle, + &mOcLoadedImageProtocolGuid, + (VOID **) &OcLoadedImage + ); + if (!EFI_ERROR (Status)) { + return InternalDirectStartImage ( + OcLoadedImage, + ImageHandle, + ExitDataSize, + ExitData + ); + } + + return mOriginalEfiStartImage (ImageHandle, ExitDataSize, ExitData); +} + +STATIC +EFI_STATUS +EFIAPI +InternalEfiUnloadImage ( + IN EFI_HANDLE ImageHandle + ) +{ + EFI_STATUS Status; + OC_LOADED_IMAGE_PROTOCOL *OcLoadedImage; + + // + // If we loaded the image, do the unloading manually. + // + Status = gBS->HandleProtocol ( + ImageHandle, + &mOcLoadedImageProtocolGuid, + (VOID **) &OcLoadedImage + ); + if (!EFI_ERROR (Status)) { + return InternalDirectUnloadImage ( + OcLoadedImage, + ImageHandle + ); + } + + return mOriginalEfiUnloadImage (ImageHandle); +} + +STATIC +EFI_STATUS +EFIAPI +InternalEfiExit ( + IN EFI_HANDLE ImageHandle, + IN EFI_STATUS ExitStatus, + IN UINTN ExitDataSize, + IN CHAR16 *ExitData OPTIONAL + ) +{ + EFI_STATUS Status; + OC_LOADED_IMAGE_PROTOCOL *OcLoadedImage; + + // + // If we loaded the image, do the exit manually. + // + Status = gBS->HandleProtocol ( + ImageHandle, + &mOcLoadedImageProtocolGuid, + (VOID **) &OcLoadedImage + ); + + DEBUG ((DEBUG_VERBOSE, "OCB: InternalEfiExit %p - %r / %r\n", ImageHandle, ExitStatus, Status)); + + if (!EFI_ERROR (Status)) { + return InternalDirectExit ( + OcLoadedImage, + ImageHandle, + ExitStatus, + ExitDataSize, + ExitData + ); + } + + return mOriginalEfiExit (ImageHandle, ExitStatus, ExitDataSize, ExitData); +} + +VOID +OcInitDirectImageLoader ( VOID ) { - mOriginalEfiLoadImage = gBS->LoadImage; - gBS->LoadImage = InternalEfiLoadImage; + mOriginalEfiLoadImage = gBS->LoadImage; + mOriginalEfiStartImage = gBS->StartImage; + mOriginalEfiUnloadImage = gBS->UnloadImage; + mOriginalEfiExit = gBS->Exit; + + gBS->LoadImage = InternalEfiLoadImage; + gBS->StartImage = InternalEfiStartImage; + gBS->UnloadImage = InternalEfiUnloadImage; + gBS->Exit = InternalEfiExit; gBS->Hdr.CRC32 = 0; gBS->CalculateCrc32 (gBS, gBS->Hdr.HeaderSize, &gBS->Hdr.CRC32); - return EFI_SUCCESS; +} + +VOID +OcActivateDirectImageLoader ( + VOID + ) +{ + mDirectImageLoaderEnabled = TRUE; } diff --git a/Library/OcBootManagementLib/OcBootManagementLib.c b/Library/OcBootManagementLib/OcBootManagementLib.c index 366116b4..f59e2cee 100644 --- a/Library/OcBootManagementLib/OcBootManagementLib.c +++ b/Library/OcBootManagementLib/OcBootManagementLib.c @@ -505,10 +505,7 @@ OcRunBootPicker ( SaidWelcome = FALSE; - Status = InternalInitImageLoader (); - if (EFI_ERROR (Status)) { - return Status; - } + OcActivateDirectImageLoader (); // // Reset NVRAM right away if requested by a key combination. diff --git a/Library/OcBootManagementLib/OcBootManagementLib.inf b/Library/OcBootManagementLib/OcBootManagementLib.inf index 696756fd..9a4498ca 100644 --- a/Library/OcBootManagementLib/OcBootManagementLib.inf +++ b/Library/OcBootManagementLib/OcBootManagementLib.inf @@ -110,6 +110,7 @@ OcGuardLib OcFileLib OcMachoLib + OcPeCoffLib OcRtcLib OcXmlLib TimerLib diff --git a/Library/OcGuardLib/Alignment.c b/Library/OcGuardLib/Alignment.c new file mode 100644 index 00000000..66ef79c5 --- /dev/null +++ b/Library/OcGuardLib/Alignment.c @@ -0,0 +1,36 @@ +/** @file + +OcGuardLib + +Copyright (c) 2020, Download-Fritz + +All rights reserved. + +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ +#include + +#include +#include + +BOOLEAN +OcOverflowAlignUpU32 ( + UINT32 Value, + UINT32 Alignment, + UINT32 *Result + ) +{ + BOOLEAN Status; + + Status = OcOverflowAddU32 (Value, Alignment - 1U, Result); + *Result &= ~(Alignment - 1U); + + return Status; +} diff --git a/Library/OcGuardLib/OcGuardLib.inf b/Library/OcGuardLib/OcGuardLib.inf index 86a60f14..b360b152 100644 --- a/Library/OcGuardLib/OcGuardLib.inf +++ b/Library/OcGuardLib/OcGuardLib.inf @@ -29,6 +29,7 @@ # [Sources] + Alignment.c BitOverflow.c Canary.c NativeOverflow.c diff --git a/Library/OcPeCoffLib/OcPeCoffLib.c b/Library/OcPeCoffLib/OcPeCoffLib.c new file mode 100644 index 00000000..c012952b --- /dev/null +++ b/Library/OcPeCoffLib/OcPeCoffLib.c @@ -0,0 +1,1178 @@ +/** @file + Base PE/COFF loader supports loading any PE32/PE32+ or TE image. + + Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
+ Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#define IS_POW2(v) ((v) != 0U && ((v) & ((v) - 1U)) == 0U) +#define IS_ALIGNED(v, a) (((v) & ((a) - 1U)) == 0U) + +#define IMAGE_RELOC_TYPE(Relocation) ((Relocation) >> 12U) +#define IMAGE_RELOC_OFFSET(Relocation) ((Relocation) & 0x0FFFU) + +#define IMAGE_IS_EFI_SUBYSYSTEM(Subsystem) \ + ((Subsystem) >= EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION && \ + (Subsystem) <= EFI_IMAGE_SUBSYSTEM_SAL_RUNTIME_DRIVER) + +#define IMAGE_RELOC_TYPE_SUPPORTED(Type) \ + ((Type) == EFI_IMAGE_REL_BASED_ABSOLUTE) || \ + ((Type) == EFI_IMAGE_REL_BASED_HIGHLOW) || \ + ((Type) == EFI_IMAGE_REL_BASED_DIR64)) + +#define IMAGE_RELOC_SUPPORTED(Reloc) \ + IMAGE_RELOC_TYPE_SUPPORTED (IMAGE_RELOC_TYPE (Reloc)) + +STATIC +IMAGE_STATUS +InternalVerifySections ( + IN CONST PE_COFF_LOADER_IMAGE_CONTEXT *Context, + IN UINTN FileSize, + IN UINT32 SectionsOffset, + IN UINT16 NumberOfSections, + OUT UINT32 *BottomAddress, + OUT UINT32 *TopAddress + ) +{ + BOOLEAN Result; + UINT32 NextSectRva; + UINT32 SectRvaPrevEnd; + UINT32 SectRvaEnd; + UINT32 SectRawEnd; + UINT16 SectIndex; + CONST EFI_IMAGE_SECTION_HEADER *Sections; + + ASSERT (Context != NULL); + ASSERT (Context->SizeOfHeaders >= Context->TeStrippedOffset); + ASSERT (IS_POW2 (Context->SectionAlignment)); + ASSERT (NumberOfSections > 0); + ASSERT (BottomAddress != NULL); + ASSERT (TopAddress != NULL); + + Sections = (CONST EFI_IMAGE_SECTION_HEADER *) (CONST VOID *) ( + (CONST CHAR8 *) Context->FileBuffer + SectionsOffset + ); + + if (Sections[0].VirtualAddress == 0) { + if (Context->ImageType == ImageTypeTe) { + return IMAGE_ERROR_UNSUPPORTED; + } + + NextSectRva = 0; + } else { + // TODO: Disallow PCD + + Result = OcOverflowAlignUpU32 ( + Context->SizeOfHeaders, + Context->SectionAlignment, + &NextSectRva + ); + if (Result) { + return IMAGE_ERROR_UNSUPPORTED; + } + } + + SectRvaPrevEnd = NextSectRva; + + *BottomAddress = NextSectRva; + + for (SectIndex = 0; SectIndex < NumberOfSections; ++SectIndex) { + if (Sections[SectIndex].SizeOfRawData > 0) { + if (Context->TeStrippedOffset > Sections[SectIndex].PointerToRawData) { + return IMAGE_ERROR_UNSUPPORTED; + } + + Result = OcOverflowAddU32 ( + Sections[SectIndex].PointerToRawData, + Sections[SectIndex].SizeOfRawData, + &SectRawEnd + ); + if (Result) { + return IMAGE_ERROR_UNSUPPORTED; + } + + if ((SectRawEnd - Context->TeStrippedOffset) > FileSize) { + return IMAGE_ERROR_UNSUPPORTED; + } + } + + Result = OcOverflowAddU32 ( + Sections[SectIndex].VirtualAddress, + Sections[SectIndex].VirtualSize, + &SectRvaEnd + ); + if (Result) { + return IMAGE_ERROR_UNSUPPORTED; + } + + // + // FIXME: Misaligned images should be handled with a PCD. + // + if (Sections[SectIndex].VirtualAddress < SectRvaPrevEnd + || Sections[SectIndex].VirtualAddress > NextSectRva) { + return IMAGE_ERROR_UNSUPPORTED; + } + + SectRvaPrevEnd = SectRvaEnd; + + // + // Sections must have virtual addresses adjacent in ascending order. + // SectionSize does not need to be aligned, so align the result. + // + Result = OcOverflowAlignUpU32 ( + SectRvaEnd, + Context->SectionAlignment, + &NextSectRva + ); + + if (Result) { + return IMAGE_ERROR_UNSUPPORTED; + } + } + + *TopAddress = NextSectRva; + + return IMAGE_ERROR_SUCCESS; +} + +STATIC +IMAGE_STATUS +InternalValidateRelocInfo ( + IN CONST PE_COFF_LOADER_IMAGE_CONTEXT *Context, + IN UINT32 BottomAddress + ) +{ + BOOLEAN Result; + UINT32 SectRvaEnd; + + ASSERT (Context != NULL); + ASSERT (BottomAddress == 0 || BottomAddress == ALIGN_VALUE (Context->SizeOfHeaders, Context->SectionAlignment)); + // + // If the relocations have not been stripped, sanitize their directory. + // + if (!Context->RelocsStripped) { + if (sizeof (EFI_IMAGE_BASE_RELOCATION) > Context->RelocDirSize) { + return IMAGE_ERROR_UNSUPPORTED; + } + + Result = OcOverflowAddU32 ( + Context->RelocDirRva, + Context->RelocDirSize, + &SectRvaEnd + ); + // + // Ensure no overflow has occured. + // + if (Result) { + return IMAGE_ERROR_UNSUPPORTED; + } + // + // Ensure the directory does not overlap with the image header. + // + if (BottomAddress > Context->RelocDirRva) { + return IMAGE_ERROR_UNSUPPORTED; + } + // + // Ensure the directory is within the bounds of the image's virtual space. + // + if (SectRvaEnd > Context->SizeOfImage) { + return IMAGE_ERROR_UNSUPPORTED; + } + // + // Ensure the directory's start is propery aligned. + // + if (!OC_TYPE_ALIGNED (EFI_IMAGE_BASE_RELOCATION, Context->RelocDirRva)) { + return IMAGE_ERROR_UNSUPPORTED; + } + } else { + if (!IS_ALIGNED (Context->ImageBase, Context->SectionAlignment)) { + return IMAGE_ERROR_UNSUPPORTED; + } + // + // Ensure DXE Runtime Driver can be relocated. + // + if (Context->Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER) { + return IMAGE_ERROR_UNSUPPORTED; + } + } + + return IMAGE_ERROR_SUCCESS; +} + +STATIC +IMAGE_STATUS +InternalInitializeTe ( + IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *Context, + IN UINTN FileSize + ) +{ + IMAGE_STATUS Status; + BOOLEAN Result; + CONST EFI_TE_IMAGE_HEADER *TeHdr; + UINT32 BottomAddress; + UINT32 SizeOfImage; + UINT32 SectionsOffset; + + ASSERT (Context != NULL); + ASSERT (Context->ExeHdrOffset <= FileSize); + ASSERT (FileSize - Context->ExeHdrOffset >= sizeof (EFI_TE_IMAGE_HEADER)); + ASSERT (OC_TYPE_ALIGNED (EFI_TE_IMAGE_HEADER, Context->ExeHdrOffset)); + + Context->ImageType = ImageTypeTe; + + + TeHdr = (CONST EFI_TE_IMAGE_HEADER *) (CONST VOID *) ( + (CONST CHAR8 *) Context->FileBuffer + Context->ExeHdrOffset + ); + + STATIC_ASSERT ( + OC_TYPE_ALIGNED (EFI_IMAGE_SECTION_HEADER, sizeof (*TeHdr)), + "The section alignment requirements are violated." + ); + + Result = OcOverflowSubU32 ( + TeHdr->StrippedSize, + sizeof (*TeHdr), + &Context->TeStrippedOffset + ); + if (Result) { + return IMAGE_ERROR_UNSUPPORTED; + } + + if (TeHdr->NumberOfSections == 0) { + return IMAGE_ERROR_UNSUPPORTED; + } + + STATIC_ASSERT ( + sizeof (EFI_IMAGE_SECTION_HEADER) <= (MAX_UINT32 - sizeof (*TeHdr)) / MAX_UINT8, + "These arithmetics may overflow." + ); + // + // Assign SizeOfHeaders in a way that is equivalent to what the size would + // be if this was the original (unstripped) PE32 binary. As the TE image + // creation fixes no fields up, tests work the same way as for PE32. + // when referencing raw data however, the offset must be subracted. + // + Result = OcOverflowAddU32 ( + TeHdr->StrippedSize, + (UINT32) TeHdr->NumberOfSections * sizeof (EFI_IMAGE_SECTION_HEADER), + &Context->SizeOfHeaders + ); + if (Result) { + return IMAGE_ERROR_UNSUPPORTED; + } + // + // Ensure that all headers are in bounds of the file buffer. + // + if ((Context->SizeOfHeaders - Context->TeStrippedOffset) > FileSize) { + return IMAGE_ERROR_UNSUPPORTED; + } + + Context->SectionAlignment = BASE_4KB; + SectionsOffset = Context->ExeHdrOffset + sizeof (EFI_TE_IMAGE_HEADER); + // + // Validate the sections. + // TE images do not have a field to explicitly describe the image size. + // Set it to the top of the image's virtual space. + // + Status = InternalVerifySections ( + Context, + FileSize, + SectionsOffset, + TeHdr->NumberOfSections, + &BottomAddress, + &SizeOfImage + ); + + if (Status != IMAGE_ERROR_SUCCESS) { + return Status; + } + + Context->SizeOfImage = SizeOfImage; + Context->Machine = TeHdr->Machine; + Context->Subsystem = TeHdr->Subsystem; + Context->ImageBase = TeHdr->ImageBase; + Context->RelocsStripped = TeHdr->DataDirectory[0].Size > 0; + Context->AddressOfEntryPoint = TeHdr->AddressOfEntryPoint; + Context->RelocDirRva = TeHdr->DataDirectory[0].VirtualAddress; + Context->RelocDirSize = TeHdr->DataDirectory[0].Size; + + return InternalValidateRelocInfo (Context, BottomAddress); +} + +STATIC +IMAGE_STATUS +InternalInitializePe ( + IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *Context, + IN UINTN FileSize + ) +{ + BOOLEAN Result; + CONST EFI_IMAGE_NT_HEADERS_COMMON_HDR *PeCommon; + CONST EFI_IMAGE_NT_HEADERS32 *Pe32; + CONST EFI_IMAGE_NT_HEADERS64 *Pe32Plus; + CONST VOID *OptHdrPtr; + UINT32 HdrSizeWithoutDataDir; + UINT32 SizeOfOptionalHdr; + UINT32 SizeOfHeaders; + CONST EFI_IMAGE_DATA_DIRECTORY *RelocDir; + UINT32 NumberOfRvaAndSizes; + UINT32 SectHdrOffset; + IMAGE_STATUS Status; + UINT32 BottomAddress; + UINT32 SizeOfImage; + + ASSERT (Context != NULL); + + OptHdrPtr = (CONST CHAR8 *) Context->FileBuffer + Context->ExeHdrOffset + sizeof (EFI_IMAGE_NT_HEADERS_COMMON_HDR); + ASSERT (OC_TYPE_ALIGNED (EFI_IMAGE_NT_HEADERS_COMMON_HDR, Context->ExeHdrOffset)); + + STATIC_ASSERT ( + OC_TYPE_ALIGNED (UINT16, OC_ALIGNOF (EFI_IMAGE_NT_HEADERS_COMMON_HDR)) + && OC_TYPE_ALIGNED (UINT16, sizeof (EFI_IMAGE_NT_HEADERS_COMMON_HDR)), + "The following operation might be an unaligned access." + ); + + // + // Determine the type of and retrieve data from the PE Optional Header. + // + // + // FIXME: OptHdrPtr could point to unaligned memory. + // + switch (*(CONST UINT16 *) OptHdrPtr) { + case EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC: + if (sizeof (*Pe32) > FileSize - Context->ExeHdrOffset) { + DEBUG ((DEBUG_INFO, "OCPE: Invalid 32-bit OPT header\n")); + return IMAGE_ERROR_UNSUPPORTED; + } + + Context->ImageType = ImageTypePe32; + + Pe32 = (CONST EFI_IMAGE_NT_HEADERS32 *) (CONST VOID *) ( + (CONST CHAR8 *) Context->FileBuffer + Context->ExeHdrOffset + ); + + HdrSizeWithoutDataDir = OFFSET_OF (EFI_IMAGE_NT_HEADERS32, DataDirectory) - sizeof (EFI_IMAGE_NT_HEADERS_COMMON_HDR); + Context->Subsystem = Pe32->Subsystem; + NumberOfRvaAndSizes = Pe32->NumberOfRvaAndSizes; + + RelocDir = Pe32->DataDirectory + EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC; + + Context->SizeOfImage = Pe32->SizeOfImage; + Context->SizeOfHeaders = Pe32->SizeOfHeaders; + Context->ImageBase = Pe32->ImageBase; + Context->AddressOfEntryPoint = Pe32->AddressOfEntryPoint; + Context->SectionAlignment = Pe32->SectionAlignment; + + PeCommon = &Pe32->CommonHeader; + break; + + case EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC: + if (sizeof (*Pe32Plus) > FileSize - Context->ExeHdrOffset) { + DEBUG ((DEBUG_INFO, "OCPE: Invalid 64-bit OPT header\n")); + return IMAGE_ERROR_UNSUPPORTED; + } + + Context->ImageType = ImageTypePe32Plus; + + + Pe32Plus = (CONST EFI_IMAGE_NT_HEADERS64 *) (CONST VOID *) ( + (CONST CHAR8 *) Context->FileBuffer + Context->ExeHdrOffset + ); + + HdrSizeWithoutDataDir = OFFSET_OF (EFI_IMAGE_NT_HEADERS64, DataDirectory) - sizeof (EFI_IMAGE_NT_HEADERS_COMMON_HDR); + Context->Subsystem = Pe32Plus->Subsystem; + NumberOfRvaAndSizes = Pe32Plus->NumberOfRvaAndSizes; + + RelocDir = Pe32Plus->DataDirectory + EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC; + + Context->SizeOfImage = Pe32Plus->SizeOfImage; + Context->SizeOfHeaders = Pe32Plus->SizeOfHeaders; + Context->ImageBase = Pe32Plus->ImageBase; + Context->AddressOfEntryPoint = Pe32Plus->AddressOfEntryPoint; + Context->SectionAlignment = Pe32Plus->SectionAlignment; + + PeCommon = &Pe32Plus->CommonHeader; + break; + + default: + return IMAGE_ERROR_UNSUPPORTED; + } + + if (PeCommon->FileHeader.NumberOfSections == 0) { + DEBUG ((DEBUG_INFO, "OCPE: No sections in the image\n")); + return IMAGE_ERROR_UNSUPPORTED; + } + // + // Do not load images with unknown directories. + // + if (NumberOfRvaAndSizes > EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES) { + DEBUG ((DEBUG_INFO, "OCPE: NumberOfRvaAndSizes is too high %u\n", NumberOfRvaAndSizes)); + return IMAGE_ERROR_UNSUPPORTED; + } + + if (!IS_POW2 (Context->SectionAlignment)) { + DEBUG ((DEBUG_INFO, "OCPE: Invalid section alignment %u\n", Context->SectionAlignment)); + return IMAGE_ERROR_UNSUPPORTED; + } + // + // SizeOfOptionalHdr cannot overflow because NumberOfRvaAndSizes has + // been sanitized and the other two components are validated constants. + // + STATIC_ASSERT ( + sizeof (EFI_IMAGE_DATA_DIRECTORY) <= MAX_UINT32 / EFI_IMAGE_NUMBER_OF_DIRECTORY_ENTRIES, + "These arithmetics may overflow." + ); + + SizeOfOptionalHdr = HdrSizeWithoutDataDir + + NumberOfRvaAndSizes * sizeof (EFI_IMAGE_DATA_DIRECTORY); + + ASSERT (SizeOfOptionalHdr >= HdrSizeWithoutDataDir); + // + // Context->ExeHdrOffset + sizeof (*PeCommon) cannot overflow because + // * ExeFileSize > sizeof (*PeCommon) and + // * Context->ExeHdrOffset + ExeFileSize = FileSize + // + Result = OcOverflowAddU32 ( + Context->ExeHdrOffset + sizeof (*PeCommon), + SizeOfOptionalHdr, + &SectHdrOffset + ); + + if (Result) { + DEBUG (( + DEBUG_INFO, + "OCPE: Sections offset overflow %u + %u + %u\n", + Context->ExeHdrOffset, + (UINT32) sizeof (*PeCommon), + SizeOfOptionalHdr + )); + return IMAGE_ERROR_UNSUPPORTED; + } + // + // Ensure the section headers offset is properly aligned. + // + if (!OC_TYPE_ALIGNED (EFI_IMAGE_SECTION_HEADER, SectHdrOffset)) { + DEBUG ((DEBUG_INFO, "OCPE: Sections are unaligned %u\n", SectHdrOffset)); + return IMAGE_ERROR_UNSUPPORTED; + } + + STATIC_ASSERT ( + sizeof (EFI_IMAGE_SECTION_HEADER) <= (MAX_UINT32 + 1ULL) / (MAX_UINT16 + 1ULL), + "These arithmetics may overflow." + ); + + Result = OcOverflowAddU32 ( + SectHdrOffset, + (UINT32) PeCommon->FileHeader.NumberOfSections * sizeof (EFI_IMAGE_SECTION_HEADER), + &SizeOfHeaders + ); + + if (Result) { + DEBUG ((DEBUG_INFO, "OCPE: Sections overflow %u %u\n", SectHdrOffset, PeCommon->FileHeader.NumberOfSections)); + return IMAGE_ERROR_UNSUPPORTED; + } + // + // Ensure the header sizes are sane. SizeOfHeaders contains all header + // components (DOS, PE Common and Optional Header). + // + if (PeCommon->FileHeader.SizeOfOptionalHeader < SizeOfOptionalHdr + || Context->SizeOfHeaders < SizeOfHeaders) { + DEBUG ((DEBUG_INFO, "OCPE: SizeOfOptionalHeader %u %u\n", PeCommon->FileHeader.SizeOfOptionalHeader, SizeOfOptionalHdr)); + DEBUG ((DEBUG_INFO, "OCPE: ImageSizeOfHeaders %u %u\n", Context->SizeOfHeaders, SizeOfHeaders)); + return IMAGE_ERROR_UNSUPPORTED; + } + // + // Ensure that all headers are in bounds of the file buffer. + // + if (Context->SizeOfHeaders > FileSize) { + DEBUG ((DEBUG_INFO, "OCPE: Context->SizeOfHeaders > FileSize %u %u\n", Context->SizeOfHeaders, FileSize)); + return IMAGE_ERROR_UNSUPPORTED; + } + + Status = InternalVerifySections ( + Context, + FileSize, + SectHdrOffset, + PeCommon->FileHeader.NumberOfSections, + &BottomAddress, + &SizeOfImage + ); + if (Status != IMAGE_ERROR_SUCCESS) { + DEBUG ((DEBUG_INFO, "OCPE: InternalVerifySections %d\n", Status)); + return Status; + } + // + // Ensure SizeOfImage is equal to the top of the image's virtual space. + // FIXME: Misaligned images should load with a PCD + // + if (Context->SizeOfImage < SizeOfImage) { + DEBUG ((DEBUG_INFO, "OCPE: Context->SizeOfImage < SizeOfImage %u %u\n", Context->SizeOfImage, SizeOfImage)); + return IMAGE_ERROR_UNSUPPORTED; + } + // + // If there's no relocations, then make sure it's not a runtime driver. + // + Context->RelocsStripped = + ( + PeCommon->FileHeader.Characteristics & EFI_IMAGE_FILE_RELOCS_STRIPPED + ) != 0; + + Context->Machine = PeCommon->FileHeader.Machine; + + if (EFI_IMAGE_DIRECTORY_ENTRY_BASERELOC < NumberOfRvaAndSizes) { + Context->RelocDirRva = RelocDir->VirtualAddress; + Context->RelocDirSize = RelocDir->Size; + } else { + Context->RelocDirRva = 0; + Context->RelocDirSize = 0; + } + + return InternalValidateRelocInfo (Context, BottomAddress); +} + +IMAGE_STATUS +OcPeCoffLoaderInitializeContext ( + OUT PE_COFF_LOADER_IMAGE_CONTEXT *Context, + IN CONST VOID *FileBuffer, + IN UINTN FileSize + ) +{ + CONST VOID *ImageSig; + CONST EFI_IMAGE_DOS_HEADER *DosHdr; + + ASSERT (Context != NULL); + ASSERT (FileBuffer != NULL); + ASSERT (FileSize > 0); + + ZeroMem (Context, sizeof (*Context)); + + Context->FileBuffer = (VOID *) FileBuffer; + + ASSERT (Context != NULL); + // + // Check whether the DOS image header is present. + // + if (FileSize > sizeof (*DosHdr) + && *(CONST UINT16 *) Context->FileBuffer == EFI_IMAGE_DOS_SIGNATURE) { + DosHdr = (CONST EFI_IMAGE_DOS_HEADER *) Context->FileBuffer; + // + // When the DOS image header is present, sanitize the offset and + // retrieve the size of the executable image. + // + if (sizeof (EFI_IMAGE_DOS_HEADER) > DosHdr->e_lfanew + || DosHdr->e_lfanew >= FileSize) { + return IMAGE_ERROR_UNSUPPORTED; + } + + Context->ExeHdrOffset = DosHdr->e_lfanew; + } else { + // + // When the DOS image header is not present, assume the image starts with + // the executable header. + // + Context->ExeHdrOffset = 0; + } + // + // Use Signature to determine and handle the image format (PE32(+) / TE). + // + ImageSig = (CONST CHAR8 *) Context->FileBuffer + Context->ExeHdrOffset; + + if (FileSize - Context->ExeHdrOffset >= sizeof (EFI_TE_IMAGE_HEADER) + && OC_TYPE_ALIGNED (EFI_TE_IMAGE_HEADER, Context->ExeHdrOffset) + && *(CONST UINT16 *) ImageSig == EFI_TE_IMAGE_HEADER_SIGNATURE) { + return InternalInitializeTe (Context, FileSize); + } + + if (FileSize - Context->ExeHdrOffset >= sizeof (EFI_IMAGE_NT_HEADERS_COMMON_HDR) + sizeof (UINT16) + && OC_TYPE_ALIGNED (EFI_IMAGE_NT_HEADERS_COMMON_HDR, Context->ExeHdrOffset) + && *(CONST UINT32 *) ImageSig == EFI_IMAGE_NT_SIGNATURE) { + return InternalInitializePe (Context, FileSize); + } + + return IMAGE_ERROR_INVALID_MACHINE_TYPE; +} + +STATIC +BOOLEAN +InternalHashSections ( + IN CONST PE_COFF_LOADER_IMAGE_CONTEXT *Context, + IN UINT32 SectionsOffset, + IN UINT16 NumberOfSections, + IN HASH_UPDATE HashUpdate, + IN OUT VOID *HashContext + ) +{ + BOOLEAN Result; + + CONST EFI_IMAGE_SECTION_HEADER *Sections; + CONST EFI_IMAGE_SECTION_HEADER **SortedSections; + UINT16 SectIndex; + UINT16 SectionPos; + UINT32 SectionTop; + // + // Build a temporary table of pointers to all section headers of the image + // to sort them appropiately. + // + SortedSections = AllocatePool ( + (UINT32) NumberOfSections * sizeof (*SortedSections) + ); + + if (SortedSections == NULL) { + return FALSE; + } + + Sections = (CONST EFI_IMAGE_SECTION_HEADER *) (CONST VOID *) ( + (CONST CHAR8 *) Context->FileBuffer + SectionsOffset + ); + // + // Sort SortedSections by PointerToRawData in ascending order. + // + SortedSections[0] = &Sections[0]; + // + // Insertion Sort. + // + for (SectIndex = 1; SectIndex < NumberOfSections; ++SectIndex) { + for (SectionPos = SectIndex; + 0 < SectionPos + && SortedSections[SectionPos - 1]->PointerToRawData > Sections[SectIndex].PointerToRawData; + --SectionPos) { + SortedSections[SectionPos] = SortedSections[SectionPos - 1]; + } + + SortedSections[SectionPos] = &Sections[SectIndex]; + } + + Result = TRUE; + + SectionTop = 0; + // + // Hash the image's sections' data in the ascending order of their offset. + // + for (SectIndex = 0; SectIndex < NumberOfSections; ++SectIndex) { + if (PcdGetBool (PcdImageLoaderHashProhibitOverlap)) { + if (SectionTop > SortedSections[SectIndex]->PointerToRawData) { + Result = FALSE; + break; + } + + SectionTop = SortedSections[SectIndex]->PointerToRawData + SortedSections[SectIndex]->SizeOfRawData; + } + + if (SortedSections[SectIndex]->SizeOfRawData > 0) { + Result = HashUpdate ( + HashContext, + (CONST CHAR8 *) Context->FileBuffer + SortedSections[SectIndex]->PointerToRawData, + SortedSections[SectIndex]->SizeOfRawData + ); + if (!Result) { + break; + } + } + } + + FreePool ((VOID *)SortedSections); + return Result; +} + +BOOLEAN +OcPeCoffLoaderHashImage ( + IN CONST PE_COFF_LOADER_IMAGE_CONTEXT *Context, + IN HASH_UPDATE HashUpdate, + IN OUT VOID *HashContext + ) +{ + BOOLEAN Result; + UINT32 NumberOfRvaAndSizes; + UINT32 ChecksumOffset; + UINT32 SecurityDirOffset; + UINT32 CurrentOffset; + UINT32 HashSize; + CONST EFI_IMAGE_NT_HEADERS32 *Pe32; + CONST EFI_IMAGE_NT_HEADERS64 *Pe32Plus; + UINT32 SectionsOffset; + UINT16 NumberOfSections; + // + // Hash the entire image excluding: + // * its checksum + // * its security directory + // * its signature + // + switch (Context->ImageType) { + case ImageTypeTe: + // + // TE images are not to be signed, as they are supposed to only be part of + // Firmware Volumes, which may be signed as a whole. + // + return FALSE; + + case ImageTypePe32: + Pe32 = (CONST EFI_IMAGE_NT_HEADERS32 *) (CONST VOID *) ( + (CONST CHAR8 *) Context->FileBuffer + Context->ExeHdrOffset + ); + + ChecksumOffset = Context->ExeHdrOffset + OFFSET_OF (EFI_IMAGE_NT_HEADERS32, CheckSum); + SecurityDirOffset = Context->ExeHdrOffset + (UINT32) OFFSET_OF (EFI_IMAGE_NT_HEADERS32, DataDirectory) +(UINT32) (EFI_IMAGE_DIRECTORY_ENTRY_SECURITY * sizeof (EFI_IMAGE_DATA_DIRECTORY)); + NumberOfRvaAndSizes = Pe32->NumberOfRvaAndSizes; + SectionsOffset = Context->ExeHdrOffset + sizeof (Pe32->CommonHeader) + Pe32->CommonHeader.FileHeader.SizeOfOptionalHeader; + NumberOfSections = Pe32->CommonHeader.FileHeader.NumberOfSections; + + break; + + case ImageTypePe32Plus: + Pe32Plus = (CONST EFI_IMAGE_NT_HEADERS64 *) (CONST VOID *) ( + (CONST CHAR8 *) Context->FileBuffer + Context->ExeHdrOffset + ); + + ChecksumOffset = Context->ExeHdrOffset + OFFSET_OF (EFI_IMAGE_NT_HEADERS64, CheckSum); + SecurityDirOffset = Context->ExeHdrOffset + (UINT32) OFFSET_OF (EFI_IMAGE_NT_HEADERS64, DataDirectory) +(UINT32) (EFI_IMAGE_DIRECTORY_ENTRY_SECURITY * sizeof (EFI_IMAGE_DATA_DIRECTORY)); + NumberOfRvaAndSizes = Pe32Plus->NumberOfRvaAndSizes; + SectionsOffset = Context->ExeHdrOffset + sizeof (Pe32Plus->CommonHeader) + Pe32Plus->CommonHeader.FileHeader.SizeOfOptionalHeader; + NumberOfSections = Pe32Plus->CommonHeader.FileHeader.NumberOfSections; + + break; + + default: + ASSERT (FALSE); + return FALSE; + } + // + // Hash the image header till the image's checksum. + // + Result = HashUpdate (HashContext, Context->FileBuffer, ChecksumOffset); + if (!Result) { + return FALSE; + } + // + // Skip over the image's checksum. + // + CurrentOffset = ChecksumOffset + sizeof (UINT32); + + if (EFI_IMAGE_DIRECTORY_ENTRY_SECURITY < NumberOfRvaAndSizes) { + // + // Hash everything after the checksum till the security directory. + // + HashSize = SecurityDirOffset - CurrentOffset; + Result = HashUpdate ( + HashContext, + (CONST CHAR8 *) Context->FileBuffer + CurrentOffset, + HashSize + ); + if (!Result) { + return FALSE; + } + // + // Skip over the security directory. If no further directory exists, this + // will point to the top of the directory. + // + CurrentOffset = SecurityDirOffset + sizeof (EFI_IMAGE_DATA_DIRECTORY); + } + // + // Hash the remainders of the image header. + // + HashSize = Context->SizeOfHeaders - CurrentOffset; + Result = HashUpdate ( + HashContext, + (CONST CHAR8 *) Context->FileBuffer + CurrentOffset, + HashSize + ); + + if (!Result) { + return FALSE; + } + + return InternalHashSections ( + Context, + SectionsOffset, + NumberOfSections, + HashUpdate, + HashContext + ); +} + +STATIC +IMAGE_STATUS +InternalApplyRelocation ( + IN CONST PE_COFF_LOADER_IMAGE_CONTEXT *Context, + IN UINT32 RelocOffset, + IN UINT32 RelocIndex, + IN UINT64 Adjust + ) +{ + CONST EFI_IMAGE_BASE_RELOCATION *RelocWalker; + UINT16 RelocType; + UINT16 RelocOff; + BOOLEAN Result; + UINT32 RelocTarget; + UINT32 RemRelocTargetSize; + UINT32 Fixup32; + UINT64 Fixup64; + CHAR8 *Fixup; + + RelocWalker = (CONST EFI_IMAGE_BASE_RELOCATION *) ( + (CONST VOID *) ((CONST CHAR8 *) Context->FileBuffer + RelocOffset) + ); + + RelocType = IMAGE_RELOC_TYPE (RelocWalker->Relocations[RelocIndex]); + RelocOff = IMAGE_RELOC_OFFSET (RelocWalker->Relocations[RelocIndex]); + + if (RelocType == EFI_IMAGE_REL_BASED_ABSOLUTE) { + return IMAGE_ERROR_SUCCESS; + } + // + // Determine the relocation's target address. + // + Result = OcOverflowAddU32 ( + RelocWalker->VirtualAddress, + RelocOff, + &RelocTarget + ); + + if (Result) { + return IMAGE_ERROR_FAILED_RELOCATION; + } + + Result = OcOverflowSubU32 ( + Context->SizeOfImage, + RelocTarget, + &RemRelocTargetSize + ); + + if (Result) { + return IMAGE_ERROR_FAILED_RELOCATION; + } + + Fixup = (CHAR8 *) Context->FileBuffer + RelocTarget; + // + // Apply the relocation fixup per type. + // If RelocationData is not NULL, store the current value of the fixup + // target to determine whether it has been changed during runtime + // execution. + // + // It is not clear how EFI_IMAGE_REL_BASED_HIGH and + // EFI_IMAGE_REL_BASED_LOW are supposed to be handled. While PE reference + // suggests to just add the high or low part of the displacement, there + // are concerns about how it's supposed to deal with wraparounds. + // As neither LLD, + // + + switch (RelocType) { + case EFI_IMAGE_REL_BASED_HIGHLOW: + if (sizeof (UINT32) > RemRelocTargetSize) { + return IMAGE_ERROR_FAILED_RELOCATION; + } + + if (RelocTarget + sizeof (UINT32) > Context->RelocDirRva + && Context->RelocDirRva + Context->RelocDirSize > RelocTarget) { + return IMAGE_ERROR_FAILED_RELOCATION; + } + + Fixup32 = ReadUnaligned32 ((UINT32 *) (VOID *) Fixup) +(UINT32)Adjust; + WriteUnaligned32 ((UINT32 *) (VOID *) Fixup, Fixup32); + + break; + + case EFI_IMAGE_REL_BASED_DIR64: + if (sizeof (UINT64) > RemRelocTargetSize) { + return IMAGE_ERROR_FAILED_RELOCATION; + } + + if (RelocTarget + sizeof (UINT64) > Context->RelocDirRva + && Context->RelocDirRva + Context->RelocDirSize > RelocTarget) { + return IMAGE_ERROR_FAILED_RELOCATION; + } + + Fixup64 = ReadUnaligned64 ((UINT64 *) (VOID *) Fixup) + Adjust; + WriteUnaligned64 ((UINT64 *) (VOID *) Fixup, Fixup64); + + break; + + default: + return IMAGE_ERROR_FAILED_RELOCATION; + } + + return IMAGE_ERROR_SUCCESS; +} + +IMAGE_STATUS +OcPeCoffLoaderRelocateImage ( + IN CONST PE_COFF_LOADER_IMAGE_CONTEXT *Context, + IN UINTN BaseAddress + ) +{ + BOOLEAN Result; + IMAGE_STATUS Status; + + UINT64 Adjust; + CONST EFI_IMAGE_BASE_RELOCATION *RelocWalker; + + UINT32 SizeOfRelocs; + UINT32 NumRelocs; + + UINT32 RelocDataIndex; + + UINT32 RelocOffset; + UINT32 RelocMax; + + UINT32 RelocIndex; + + ASSERT (Context != NULL); + ASSERT (Context->RelocDirRva + Context->RelocDirSize >= Context->RelocDirRva); + ASSERT (Context->RelocDirRva + Context->RelocDirSize <= Context->SizeOfImage); + // + // Calculate the image's displacement from its prefered location. + // + Adjust = (UINT64) BaseAddress -Context->ImageBase; + // + // Runtime drivers should unconditionally go through the full relocation + // procedure early to eliminate the possibility of errors later at runtime. + // Runtime drivers don't have their relocations stripped, this is verified + // during context creation. + // Skip explicit relocation when the image is already loaded at its + // prefered location. + // + if (Context->Subsystem != EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER + && Adjust == 0) { + return IMAGE_ERROR_SUCCESS; + } + // + // Ensure relocations have not been stripped. + // + ASSERT (!Context->RelocsStripped); + // + // Apply relocation fixups to the image. + // + RelocOffset = Context->RelocDirRva; + + RelocMax = Context->RelocDirRva + Context->RelocDirSize - sizeof (EFI_IMAGE_BASE_RELOCATION); + + RelocDataIndex = 0; + + while (RelocOffset < RelocMax) { + RelocWalker = (CONST EFI_IMAGE_BASE_RELOCATION *) ( + (CONST VOID *) ((CONST CHAR8 *) Context->FileBuffer + RelocOffset) + ); + + STATIC_ASSERT ( + (sizeof (UINT32) % OC_ALIGNOF (EFI_IMAGE_BASE_RELOCATION)) == 0, + "The following accesses must be performed unaligned." + ); + + Result = OcOverflowSubU32 ( + RelocWalker->SizeOfBlock, + sizeof (EFI_IMAGE_BASE_RELOCATION), + &SizeOfRelocs + ); + // + // Ensure no overflow has occured and there is at least one entry. + // + if (Result || SizeOfRelocs == 0 || SizeOfRelocs > RelocMax - RelocOffset) { + return IMAGE_ERROR_FAILED_RELOCATION; + } + // + // Ensure the block's size is padded to ensure proper alignment. + // + if ((RelocWalker->SizeOfBlock % sizeof (UINT32)) != 0) { + return IMAGE_ERROR_FAILED_RELOCATION; + } + // + // This division is safe due to the guarantee made above. + // + NumRelocs = SizeOfRelocs / sizeof (*RelocWalker->Relocations); + // + // Apply all relocation fixups of the current block. + // + for (RelocIndex = 0; RelocIndex < NumRelocs; ++RelocIndex) { + // + // Apply the relocation fixup per type. + // If RelocationData is not NULL, store the current value of the fixup + // target to determine whether it has been changed during runtime + // execution. + // + // It is not clear how EFI_IMAGE_REL_BASED_HIGH and + // EFI_IMAGE_REL_BASED_LOW are supposed to be handled. While PE reference + // suggests to just add the high or low part of the displacement, there + // are concerns about how it's supposed to deal with wraparounds. + // As neither LLD, + // + Status = InternalApplyRelocation ( + Context, + RelocOffset, + RelocIndex, + Adjust + ); + if (Status != IMAGE_ERROR_SUCCESS) { + return Status; + } + } + + RelocDataIndex += NumRelocs; + RelocOffset += RelocWalker->SizeOfBlock; + } + // + // Ensure the relocation directory size matches the contained relocations. + // + if (RelocOffset != RelocMax + sizeof (EFI_IMAGE_BASE_RELOCATION)) { + return IMAGE_ERROR_FAILED_RELOCATION; + } + + return IMAGE_ERROR_SUCCESS; +} + +STATIC +VOID +InternalLoadSections ( + IN CONST PE_COFF_LOADER_IMAGE_CONTEXT *Context, + IN UINT32 LoadedSizeOfHeaders, + IN UINT32 SectionsOffset, + IN UINT16 NumberOfSections, + OUT VOID *Destination, + IN UINT32 DestinationSize + ) +{ + CONST EFI_IMAGE_SECTION_HEADER *Sections; + UINT16 Index; + UINT32 DataSize; + UINT32 PreviousTopRva; + + Sections = (CONST EFI_IMAGE_SECTION_HEADER *) (CONST VOID *) ( + (CONST CHAR8 *) Context->FileBuffer + SectionsOffset + ); + + PreviousTopRva = LoadedSizeOfHeaders; + + for (Index = 0; Index < NumberOfSections; ++Index) { + if (Sections[Index].VirtualSize < Sections[Index].SizeOfRawData) { + DataSize = Sections[Index].VirtualSize; + } else { + DataSize = Sections[Index].SizeOfRawData; + } + + ZeroMem ((CHAR8 *) Destination + PreviousTopRva, Sections[Index].VirtualAddress - PreviousTopRva); + CopyMem ( + (CHAR8 *) Destination + Sections[Index].VirtualAddress, + (CONST CHAR8 *) Context->FileBuffer + (Sections[Index].PointerToRawData -Context->TeStrippedOffset), + DataSize + ); + + PreviousTopRva = Sections[Index].VirtualAddress + DataSize; + } + + ZeroMem ( + (CHAR8 *) Destination + PreviousTopRva, + DestinationSize - PreviousTopRva + ); +} + +IMAGE_STATUS +OcPeCoffLoaderLoadImage ( + IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *Context, + OUT VOID *Destination, + IN UINT32 DestinationSize + ) +{ + CHAR8 *AlignedDest; + UINT32 AlignOffset; + UINT32 AlignedSize; + UINT32 LoadedSizeOfHeaders; + CONST EFI_IMAGE_NT_HEADERS32 *SrcPe32; + CONST EFI_IMAGE_NT_HEADERS64 *SrcPe32Plus; + CONST EFI_TE_IMAGE_HEADER *SrcTe; + CONST EFI_IMAGE_SECTION_HEADER *Sections; + UINT32 SectionsOffset; + UINT16 NumberOfSections; + UINTN Address; + UINTN AlignedAddress; + + ASSERT (Context != NULL); + ASSERT (Destination != NULL); + ASSERT (DestinationSize > 0); + ASSERT (DestinationSize >= Context->SectionAlignment); + + Address = (UINTN)Destination; + + ASSERT (!Context->RelocsStripped || Context->ImageBase == Address); + + AlignedAddress = ALIGN_VALUE (Address, (UINTN) Context->SectionAlignment); + AlignOffset = (UINT32) (AlignedAddress - Address); + AlignedSize = DestinationSize - AlignOffset; + + ASSERT (Context->SizeOfImage <= AlignedSize); + + AlignedDest = (CHAR8 *) Destination + AlignOffset; + + ZeroMem (Destination, AlignOffset); + + switch (Context->ImageType) { + case ImageTypeTe: + SrcTe = (CONST EFI_TE_IMAGE_HEADER *) (CONST VOID *) ( + (CONST CHAR8 *) Context->FileBuffer + Context->ExeHdrOffset + ); + + NumberOfSections = SrcTe->NumberOfSections; + SectionsOffset = Context->ExeHdrOffset + sizeof (*SrcTe); + break; + + case ImageTypePe32: + SrcPe32 = (CONST EFI_IMAGE_NT_HEADERS32 *) (CONST VOID *) ( + (CONST CHAR8 *) Context->FileBuffer + Context->ExeHdrOffset + ); + + NumberOfSections = SrcPe32->CommonHeader.FileHeader.NumberOfSections; + SectionsOffset = Context->ExeHdrOffset + sizeof (SrcPe32->CommonHeader) + SrcPe32->CommonHeader.FileHeader.SizeOfOptionalHeader; + break; + + case ImageTypePe32Plus: + SrcPe32Plus = (CONST EFI_IMAGE_NT_HEADERS64 *) (CONST VOID *) ( + (CONST CHAR8 *) Context->FileBuffer + Context->ExeHdrOffset + ); + + NumberOfSections = SrcPe32Plus->CommonHeader.FileHeader.NumberOfSections; + SectionsOffset = Context->ExeHdrOffset + sizeof (SrcPe32Plus->CommonHeader) + SrcPe32Plus->CommonHeader.FileHeader.SizeOfOptionalHeader; + break; + + default: + ASSERT (FALSE); + return IMAGE_ERROR_UNSUPPORTED; + } + + Sections = (CONST EFI_IMAGE_SECTION_HEADER *) (CONST VOID *) ( + (CONST CHAR8 *) Context->FileBuffer + SectionsOffset + ); + + if (Sections[0].VirtualAddress != 0 && PcdGetBool (PcdImageLoaderLoadHeader)) { + LoadedSizeOfHeaders = (Context->SizeOfHeaders - Context->TeStrippedOffset); + + CopyMem (AlignedDest, Context->FileBuffer, LoadedSizeOfHeaders); + } else { + LoadedSizeOfHeaders = 0; + } + + InternalLoadSections ( + Context, + LoadedSizeOfHeaders, + SectionsOffset, + NumberOfSections, + AlignedDest, + AlignedSize + ); + // + // Update the location-dependent fields to the loaded destination. + // + Context->FileBuffer = AlignedDest; + + return IMAGE_ERROR_SUCCESS; +} diff --git a/Library/OcPeCoffLib/OcPeCoffLib.inf b/Library/OcPeCoffLib/OcPeCoffLib.inf new file mode 100644 index 00000000..604b6bd2 --- /dev/null +++ b/Library/OcPeCoffLib/OcPeCoffLib.inf @@ -0,0 +1,36 @@ +## @file +# PE/COFF Loader Library implementation. +# +# Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+# Portions copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = OcPeCoffLib + FILE_GUID = 556f5d10-7309-4af4-b80a-8196bd60946e + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = OcPeCoffLib + +[Sources] + OcPeCoffLib.c + +[Packages] + MdePkg/MdePkg.dec + OpenCorePkg/OpenCorePkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + MemoryAllocationLib + OcGuardLib + +[FixedPcd] + gOpenCorePkgTokenSpaceGuid.PcdImageLoaderLoadHeader + gOpenCorePkgTokenSpaceGuid.PcdImageLoaderHashProhibitOverlap diff --git a/OpenCorePkg.dec b/OpenCorePkg.dec index 0eef4f14..8bd662ac 100755 --- a/OpenCorePkg.dec +++ b/OpenCorePkg.dec @@ -702,6 +702,9 @@ ## @Prompt Allow these signature hashing algorithms for cryptographic usage. gOpenCorePkgTokenSpaceGuid.PcdOcCryptoAllowedSigHashTypes|0x07|UINT16|0x00000501 + gOpenCorePkgTokenSpaceGuid.PcdImageLoaderLoadHeader|TRUE|BOOLEAN|0x00000600 + gOpenCorePkgTokenSpaceGuid.PcdImageLoaderHashProhibitOverlap|TRUE|BOOLEAN|0x00000601 + [LibraryClasses] ## @libraryclass OcAcpiLib|Include/Acidanthera/Library/OcAcpiLib.h diff --git a/OpenCorePkg.dsc b/OpenCorePkg.dsc index 20d8cbb3..6a901d98 100755 --- a/OpenCorePkg.dsc +++ b/OpenCorePkg.dsc @@ -108,6 +108,7 @@ OcUnicodeCollationEngLocalLib|OpenCorePkg/Library/OcUnicodeCollationEngLib/OcUnicodeCollationEngLocalLib.inf OcVirtualFsLib|OpenCorePkg/Library/OcVirtualFsLib/OcVirtualFsLib.inf OcXmlLib|OpenCorePkg/Library/OcXmlLib/OcXmlLib.inf + OcPeCoffLib|OpenCorePkg/Library/OcPeCoffLib/OcPeCoffLib.inf PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf PciCf8Lib|MdePkg/Library/BasePciCf8Lib/BasePciCf8Lib.inf PciLib|MdePkg/Library/BasePciLibCf8/BasePciLibCf8.inf @@ -225,6 +226,7 @@ OpenCorePkg/Library/OcUnicodeCollationEngLib/OcUnicodeCollationEngLocalLib.inf OpenCorePkg/Library/OcVirtualFsLib/OcVirtualFsLib.inf OpenCorePkg/Library/OcXmlLib/OcXmlLib.inf + OpenCorePkg/Library/OcPeCoffLib/OcPeCoffLib.inf OpenCorePkg/Platform/CrScreenshotDxe/CrScreenshotDxe.inf OpenCorePkg/Platform/OpenCanopy/OpenCanopy.inf OpenCorePkg/Platform/OpenCore/OpenCore.inf diff --git a/Platform/OpenCore/OpenCoreUefi.c b/Platform/OpenCore/OpenCoreUefi.c index e64e4d49..c501eaf7 100644 --- a/Platform/OpenCore/OpenCoreUefi.c +++ b/Platform/OpenCore/OpenCoreUefi.c @@ -516,6 +516,8 @@ OcLoadUefiSupport ( OcReinstallProtocols (Config); + OcInitDirectImageLoader (); + OcLoadAppleSecureBoot (Config); OcLoadUefiInputSupport (Config); diff --git a/User/Include/Pcd.h b/User/Include/Pcd.h index 361a1f70..20fc9d64 100644 --- a/User/Include/Pcd.h +++ b/User/Include/Pcd.h @@ -24,6 +24,8 @@ extern UINT32 _gPcd_FixedAtBuild_PcdMaximumLinkedListLength; extern BOOLEAN _gPcd_FixedAtBuild_PcdVerifyNodeInList; extern UINT32 _gPcd_FixedAtBuild_PcdCpuNumberOfReservedVariableMtrrs; extern UINT32 _gPcd_FixedAtBuild_PcdMaximumDevicePathNodeCount; +extern BOOLEAN _gPcd_FixedAtBuild_PcdImageLoaderHashProhibitOverlap; +extern BOOLEAN _gPcd_FixedAtBuild_PcdImageLoaderLoadHeader; #define _PCD_GET_MODE_32_PcdUefiLibMaxPrintBufferSize _gPcd_FixedAtBuild_PcdUefiLibMaxPrintBufferSize #define _PCD_GET_MODE_BOOL_PcdUgaConsumeSupport _gPcd_FixedAtBuild_PcdUgaConsumeSupport @@ -42,5 +44,7 @@ extern UINT32 _gPcd_FixedAtBuild_PcdMaximumDevicePathNodeCount; // this will not be of any effect at userspace #define _PCD_GET_MODE_64_PcdPciExpressBaseAddress 0 #define _PCD_GET_MODE_32_PcdMaximumDevicePathNodeCount _gPcd_FixedAtBuild_PcdMaximumDevicePathNodeCount +#define _PCD_GET_MODE_BOOL_PcdImageLoaderHashProhibitOverlap _gPcd_FixedAtBuild_PcdImageLoaderHashProhibitOverlap +#define _PCD_GET_MODE_BOOL_PcdImageLoaderLoadHeader _gPcd_FixedAtBuild_PcdImageLoaderLoadHeader #endif // OC_USER_PCD_H diff --git a/User/Library/Pcd.c b/User/Library/Pcd.c index 0e623870..8817e601 100644 --- a/User/Library/Pcd.c +++ b/User/Library/Pcd.c @@ -17,6 +17,8 @@ #define _PCD_VALUE_PcdVerifyNodeInList ((BOOLEAN)0U) #define _PCD_VALUE_PcdCpuNumberOfReservedVariableMtrrs 0x2U #define _PCD_VALUE_PcdMaximumDevicePathNodeCount 0U +#define _PCD_VALUE_PcdImageLoaderHashProhibitOverlap 1U +#define _PCD_VALUE_PcdImageLoaderLoadHeader 0U UINT32 _gPcd_FixedAtBuild_PcdUefiLibMaxPrintBufferSize = _PCD_VALUE_PcdUefiLibMaxPrintBufferSize; BOOLEAN _gPcd_FixedAtBuild_PcdUgaConsumeSupport = _PCD_VALUE_PcdUgaConsumeSupport; @@ -30,3 +32,5 @@ UINT32 _gPcd_FixedAtBuild_PcdMaximumLinkedListLength = _PCD_VALUE_PcdMaximumLink BOOLEAN _gPcd_FixedAtBuild_PcdVerifyNodeInList = _PCD_VALUE_PcdVerifyNodeInList; UINT32 _gPcd_FixedAtBuild_PcdCpuNumberOfReservedVariableMtrrs = _PCD_VALUE_PcdCpuNumberOfReservedVariableMtrrs; UINT32 _gPcd_FixedAtBuild_PcdMaximumDevicePathNodeCount = _PCD_VALUE_PcdMaximumDevicePathNodeCount; +BOOLEAN _gPcd_FixedAtBuild_PcdImageLoaderHashProhibitOverlap = _PCD_VALUE_PcdImageLoaderHashProhibitOverlap; +BOOLEAN _gPcd_FixedAtBuild_PcdImageLoaderLoadHeader = _PCD_VALUE_PcdImageLoaderLoadHeader; diff --git a/User/Makefile b/User/Makefile index 1e78fffd..b4271f42 100644 --- a/User/Makefile +++ b/User/Makefile @@ -112,7 +112,7 @@ ifneq ($(STANDALONE),1) # # OcGuardLib targets. # - OBJS += BitOverflow.o NativeOverflow.o TripleOverflow.o + OBJS += BitOverflow.o NativeOverflow.o TripleOverflow.o Alignment.o # # OcSerializeLib targets. # diff --git a/Utilities/TestPeCoff/Makefile b/Utilities/TestPeCoff/Makefile new file mode 100644 index 00000000..1bf5a058 --- /dev/null +++ b/Utilities/TestPeCoff/Makefile @@ -0,0 +1,11 @@ +## @file +# Copyright (c) 2020, vit9696. All rights reserved. +# SPDX-License-Identifier: BSD-3-Clause +## + +PROJECT = PeCoff +PRODUCT = $(PROJECT)$(SUFFIX) +OBJS = $(PROJECT).o \ + OcPeCoffLib.o +VPATH = ../../Library/OcPeCoffLib +include ../../User/Makefile diff --git a/Utilities/TestPeCoff/PeCoff.c b/Utilities/TestPeCoff/PeCoff.c new file mode 100644 index 00000000..1b615aaf --- /dev/null +++ b/Utilities/TestPeCoff/PeCoff.c @@ -0,0 +1,149 @@ +/** @file + Copyright (c) 2018, vit9696. All rights reserved. + SPDX-License-Identifier: BSD-3-Clause +**/ + +#include "../Include/Uefi.h" + +#include +#include +#include +#include + +#include +#include + +#include + +/** + +clang -g -fsanitize=undefined,address -Wno-incompatible-pointer-types-discards-qualifiers -fshort-wchar -I../Include -I../../Include -I../../../MdePkg/Include/ -I../../../EfiPkg/Include/ -include ../Include/Base.h DiskImage.c ../../Library/OcXmlLib/OcXmlLib.c ../../Library/OcTemplateLib/OcTemplateLib.c ../../Library/OcSerializeLib/OcSerializeLib.c ../../Library/OcMiscLib/Base64Decode.c ../../Library/OcStringLib/OcAsciiLib.c ../../Library/OcAppleDiskImageLib/OcAppleDiskImageLib.c ../../Library/OcAppleDiskImageLib/OcAppleDiskImageLibInternal.c ../../Library/OcMiscLib/DataPatcher.c ../../Library/OcCompressionLib/zlib/zlib_uefi.c ../../Library/OcCompressionLib/zlib/adler32.c ../../Library/OcCompressionLib/zlib/deflate.c ../../Library/OcCompressionLib/zlib/crc32.c ../../Library/OcCompressionLib/zlib/compress.c ../../Library/OcCompressionLib/zlib/infback.c ../../Library/OcCompressionLib/zlib/inffast.c ../../Library/OcCompressionLib/zlib/inflate.c ../../Library/OcCompressionLib/zlib/inftrees.c ../../Library/OcCompressionLib/zlib/trees.c ../../Library/OcCompressionLib/zlib/uncompr.c ../../Library/OcCryptoLib/Sha256.c ../../Library/OcCryptoLib/Rsa2048Sha256.c ../../Library/OcAppleKeysLib/OcAppleKeysLib.c ../../Library/OcAppleChunklistLib/OcAppleChunklistLib.c ../../Library/OcAppleRamDiskLib/OcAppleRamDiskLib.c ../../Library/OcFileLib/ReadFile.c ../../Library/OcFileLib/FileProtocol.c -o DiskImage + +clang-mp-7.0 -DFUZZING_TEST=1 -g -fsanitize=undefined,address,fuzzer -Wno-incompatible-pointer-types-discards-qualifiers -fshort-wchar -I../Include -I../../Include -I../../../MdePkg/Include/ -I../../../EfiPkg/Include/ -include ../Include/Base.h DiskImage.c ../../Library/OcXmlLib/OcXmlLib.c ../../Library/OcTemplateLib/OcTemplateLib.c ../../Library/OcSerializeLib/OcSerializeLib.c ../../Library/OcMiscLib/Base64Decode.c ../../Library/OcStringLib/OcAsciiLib.c ../../Library/OcAppleDiskImageLib/OcAppleDiskImageLib.c ../../Library/OcAppleDiskImageLib/OcAppleDiskImageLibInternal.c ../../Library/OcMiscLib/DataPatcher.c ../../Library/OcCompressionLib/zlib/zlib_uefi.c ../../Library/OcCompressionLib/zlib/adler32.c ../../Library/OcCompressionLib/zlib/deflate.c ../../Library/OcCompressionLib/zlib/crc32.c ../../Library/OcCompressionLib/zlib/compress.c ../../Library/OcCompressionLib/zlib/infback.c ../../Library/OcCompressionLib/zlib/inffast.c ../../Library/OcCompressionLib/zlib/inflate.c ../../Library/OcCompressionLib/zlib/inftrees.c ../../Library/OcCompressionLib/zlib/trees.c ../../Library/OcCompressionLib/zlib/uncompr.c ../../Library/OcCryptoLib/Sha256.c ../../Library/OcCryptoLib/Rsa2048Sha256.c ../../Library/OcAppleKeysLib/OcAppleKeysLib.c ../../Library/OcAppleChunklistLib/OcAppleChunklistLib.c ../../Library/OcAppleRamDiskLib/OcAppleRamDiskLib.c../../Library/OcFileLib/ReadFile.c ../../Library/OcFileLib/FileProtocol.c -o DiskImage +rm -rf DICT fuzz*.log ; mkdir DICT ; UBSAN_OPTIONS='halt_on_error=1' ./DiskImage -jobs=4 DICT -rss_limit_mb=4096 + +**/ + +#ifdef FUZZING_TEST +#define main no_main +#include +#else +#define ASAN_POISON_MEMORY_REGION(addr, size) +#define ASAN_UNPOISON_MEMORY_REGION(addr, size) +#endif + +#if 0 +#include +#endif + +EFI_STATUS +TestImageLoad ( + IN VOID *SourceBuffer, + IN UINTN SourceSize + ) +{ + EFI_STATUS Status; + IMAGE_STATUS ImageStatus; + PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; + EFI_PHYSICAL_ADDRESS DestinationArea; + VOID *DestinationBuffer; + + + // + // Initialize the image context. + // + ImageStatus = OcPeCoffLoaderInitializeContext ( + &ImageContext, + SourceBuffer, + SourceSize + ); + if (ImageStatus != IMAGE_ERROR_SUCCESS) { + DEBUG ((DEBUG_INFO, "OCB: PeCoff init failure - %d\n", ImageStatus)); + return EFI_UNSUPPORTED; + } + // + // Reject images that are not meant for the platform's architecture. + // + if (ImageContext.Machine != IMAGE_FILE_MACHINE_X64) { + DEBUG ((DEBUG_INFO, "OCB: PeCoff wrong machine - %x\n", ImageContext.Machine)); + return EFI_UNSUPPORTED; + } + // + // Reject RT drivers for the moment. + // + if (ImageContext.Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER) { + DEBUG ((DEBUG_INFO, "OCB: PeCoff no support for RT drivers\n")); + return EFI_UNSUPPORTED; + } + // + // Allocate the image destination memory. + // FIXME: RT drivers require EfiRuntimeServicesCode. + // + Status = gBS->AllocatePages ( + AllocateAnyPages, + EfiBootServicesCode, + EFI_SIZE_TO_PAGES (ImageContext.SizeOfImage), + &DestinationArea + ); + if (EFI_ERROR (Status)) { + return Status; + } + + DestinationBuffer = (VOID *)(UINTN) DestinationArea; + + // + // Load SourceBuffer into DestinationBuffer. + // + ImageStatus = OcPeCoffLoaderLoadImage ( + &ImageContext, + DestinationBuffer, + ImageContext.SizeOfImage + ); + if (ImageStatus != IMAGE_ERROR_SUCCESS) { + DEBUG ((DEBUG_INFO, "OCB: PeCoff load image error - %d\n", ImageStatus)); + FreePages (DestinationBuffer, EFI_SIZE_TO_PAGES (ImageContext.SizeOfImage)); + return EFI_UNSUPPORTED; + } + // + // Relocate the loaded image to the destination address. + // + ImageStatus = OcPeCoffLoaderRelocateImage ( + &ImageContext, + (UINTN) DestinationBuffer + ); + + FreePages (DestinationBuffer, EFI_SIZE_TO_PAGES (ImageContext.SizeOfImage)); + + if (ImageStatus != IMAGE_ERROR_SUCCESS) { + DEBUG ((DEBUG_INFO, "OCB: PeCoff relocate image error - %d\n", ImageStatus)); + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} + +int main (int argc, char *argv[]) { + if (argc < 2) { + printf ("Please provide a valid PE image path\n"); + return -1; + } + + PcdGet32 (PcdFixedDebugPrintErrorLevel) |= DEBUG_INFO; + PcdGet32 (PcdDebugPrintErrorLevel) |= DEBUG_INFO; + + uint8_t *Image; + uint32_t ImageSize; + + if ((Image = readFile (argv[1], &ImageSize)) == NULL) { + printf ("Read fail\n"); + return 1; + } + + EFI_STATUS Status = TestImageLoad (Image, ImageSize); + free(Image); + if (EFI_ERROR (Status)) { + return 1; + } + + return 0; +} diff --git a/build_oc.tool b/build_oc.tool index 46e79813..be1fa972 100755 --- a/build_oc.tool +++ b/build_oc.tool @@ -14,6 +14,7 @@ buildutil() { "TestImg4" "TestKextInject" "TestMacho" + "TestPeCoff" "TestRsaPreprocess" "TestSmbios" )