Implement kernel interception code

This commit is contained in:
vit9696 2019-03-14 00:30:00 +03:00
parent 1a505b7f23
commit eb6b186fee
14 changed files with 608 additions and 4 deletions

View File

@ -0,0 +1,41 @@
/** @file
Copyright (C) 2019, vit9696. All rights reserved.
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.
**/
#ifndef OC_APPLE_KERNEL_LIB_H
#define OC_APPLE_KERNEL_LIB_H
#include <Library/OcMachoLib.h>
#include <Protocol/SimpleFileSystem.h>
/**
Read Apple kernel for target architecture (possibly decompressing)
into pool allocated buffer.
@param[in] File File handle instance.
@param[in, out] Kernel Resulting non-fat kernel buffer from pool.
@param[out] KernelSize Actual kernel size.
@param[out] AllocatedSize Allocated kernel size (AllocatedSize >= KernelSize).
@return EFI_SUCCESS on success.
**/
EFI_STATUS
ReadAppleKernel (
IN EFI_FILE_PROTOCOL *File,
IN OUT UINT8 **Kernel,
OUT UINT32 *KernelSize,
OUT UINT32 *AllocatedSize
);
#endif // OC_APPLE_KERNEL_LIB_H

View File

@ -50,7 +50,8 @@ GetVolumeLabel (
IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FileSystem
);
/** Read file from device path with implicit double (2 byte) null termination.
/**
Read file from device path with implicit double (2 byte) null termination.
Null termination does not affect the returned file size.
Depending on the implementation 0 byte files may return null.
@ -67,4 +68,36 @@ ReadFile (
OUT UINTN *FileSize OPTIONAL
);
/**
Read exact amount of bytes from EFI_FILE_PROTOCOL at specified position.
@param[in] File A pointer to the file protocol.
@param[in] Position Position to read data from.
@param[in] Size The size of the data read.
@param[out] Buffer A pointer to previously allocated buffer to read data to.
@retval EFI_SUCCESS on success.
**/
EFI_STATUS
ReadFileData (
IN EFI_FILE_PROTOCOL *File,
IN UINT32 Position,
IN UINT32 Size,
OUT UINT8 *Buffer
);
/**
Determine file size if it is less than 4 GB.
@param[in] File A pointer to the file protocol.
@param[out] Size 32-bit file size.
@retval EFI_SUCCESS on success.
**/
EFI_STATUS
ReadFileSize (
IN EFI_FILE_PROTOCOL *File,
OUT UINT32 *Size
);
#endif // OC_FILE_LIB_H_

View File

@ -0,0 +1,45 @@
## @file
# OcAppleKernelLib
#
# Copyright (c) 2019, vit9696
#
# 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.
#
##
[Defines]
INF_VERSION = 0x00010005
BASE_NAME = OcAppleKernelLib
FILE_GUID = 9CFA01EA-9A9B-450C-B672-E108CA30DC3F
MODULE_TYPE = DXE_DRIVER
VERSION_STRING = 1.0
LIBRARY_CLASS = OcAppleKernelLib|DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SAL_DRIVER DXE_SMM_DRIVER SMM_CORE UEFI_APPLICATION UEFI_DRIVER
#
# VALID_ARCHITECTURES = X64
#
[Sources]
ReadAppleKernel.c
[Packages]
EfiPkg/EfiPkg.dec
MdePkg/MdePkg.dec
OcSupportPkg/OcSupportPkg.dec
[LibraryClasses]
BaseLib
BaseMemoryLib
MemoryAllocationLib
OcCompressionLib
OcFileLib
OcMachoLib

View File

@ -0,0 +1,337 @@
/** @file
Copyright (C) 2019, vit9696. All rights reserved.
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 <IndustryStandard/AppleFatBinaryImage.h>
#include <IndustryStandard/AppleCompressedBinaryImage.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/OcAppleKernelLib.h>
#include <Library/OcCompressionLib.h>
#include <Library/OcFileLib.h>
#include <Library/OcMachoLib.h>
#include <Library/OcGuardLib.h>
//
// Pick a reasonable maximum to fit.
//
#define KERNEL_HEADER_SIZE (EFI_PAGE_SIZE*2)
STATIC
EFI_STATUS
ReplaceBuffer (
IN UINT32 TargetSize,
IN OUT UINT8 **Buffer,
OUT UINT32 *AllocatedSize
)
{
UINT8 *TmpBuffer;
if (*AllocatedSize >= TargetSize) {
return EFI_SUCCESS;
}
TmpBuffer = AllocatePool (TargetSize);
if (TmpBuffer == NULL) {
return EFI_OUT_OF_RESOURCES;
}
FreePool (*Buffer);
*Buffer = TmpBuffer;
*AllocatedSize = TargetSize;
return EFI_SUCCESS;
}
STATIC
UINT32
ParseFatArchitecture (
IN EFI_FILE_PROTOCOL *File,
IN OUT UINT8 **Buffer,
IN OUT UINT32 *Offset
)
{
BOOLEAN SwapBytes;
MACH_FAT_HEADER *FatHeader;
UINT32 NumberOfFatArch;
MACH_CPU_TYPE CpuType;
UINT32 TmpSize;
UINT32 Index;
UINT32 Size;
FatHeader = (MACH_FAT_HEADER *) *Buffer;
SwapBytes = FatHeader->Signature == MACH_FAT_BINARY_INVERT_SIGNATURE;
NumberOfFatArch = FatHeader->NumberOfFatArch;
if (SwapBytes) {
NumberOfFatArch = SwapBytes32 (NumberOfFatArch);
}
if (OcOverflowMulAddU32 (NumberOfFatArch, sizeof (MACH_FAT_ARCH), sizeof (MACH_FAT_HEADER), &TmpSize)
|| TmpSize > KERNEL_HEADER_SIZE) {
DEBUG ((DEBUG_INFO, "Fat kernel invalid arch count %u\n", NumberOfFatArch));
return 0;
}
//
// TODO: Currently there are no kernels with MachCpuSubtypeX8664H, but we should support them.
//
for (Index = 0; Index < NumberOfFatArch; Index++) {
CpuType = FatHeader->FatArch[Index].CpuType;
if (SwapBytes) {
CpuType = SwapBytes32 (CpuType);
}
if (CpuType == MachCpuTypeX8664) {
*Offset = FatHeader->FatArch[Index].Offset;
Size = FatHeader->FatArch[Index].Size;
if (SwapBytes) {
*Offset = SwapBytes32 (*Offset);
Size = SwapBytes32 (Size);
}
if (*Offset == 0) {
DEBUG ((DEBUG_INFO, "Fat kernel has 0 offset\n"));
return 0;
}
if (OcOverflowAddU32 (*Offset, Size, &TmpSize)) {
DEBUG ((DEBUG_INFO, "Fat kernel invalid size %u\n", Size));
return 0;
}
return Size;
}
}
DEBUG ((DEBUG_INFO, "Fat kernel has no x86_64 arch\n"));
return 0;
}
STATIC
UINT32
ParseCompressedHeader (
IN EFI_FILE_PROTOCOL *File,
IN OUT UINT8 **Buffer,
IN UINT32 *Offset,
OUT UINT32 *AllocatedSize
)
{
EFI_STATUS Status;
UINT32 KernelSize;
MACH_COMP_HEADER *CompHeader;
UINT8 *CompressedBuffer;
UINT32 CompressionType;
UINT32 CompressedSize;
UINT32 DecompressedSize;
UINT32 DecompressedHash;
CompHeader = (MACH_COMP_HEADER *) *Buffer;
CompressionType = CompHeader->Compression;
CompressedSize = SwapBytes32 (CompHeader->Compressed);
DecompressedSize = SwapBytes32 (CompHeader->Decompressed);
DecompressedHash = SwapBytes32 (CompHeader->Hash);
KernelSize = 0;
if (CompressedSize > OC_COMPRESSION_MAX_LENGTH
|| CompressedSize == 0
|| DecompressedSize > OC_COMPRESSION_MAX_LENGTH
|| DecompressedSize < KERNEL_HEADER_SIZE) {
DEBUG ((DEBUG_INFO, "Comp kernel invalid comp %u or decomp %u at %08X\n", CompressedSize, DecompressedSize, *Offset));
return KernelSize;
}
Status = ReplaceBuffer (DecompressedSize, Buffer, AllocatedSize);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO, "Decomp kernel (%u bytes) cannot be allocated at %08X\n", DecompressedSize, *Offset));
return KernelSize;
}
CompressedBuffer = AllocatePool (CompressedSize);
if (CompressedBuffer == NULL) {
DEBUG ((DEBUG_INFO, "Comp kernel (%u bytes) cannot be allocated at %08X\n", CompressedSize, *Offset));
return KernelSize;
}
Status = ReadFileData (File, *Offset + sizeof (MACH_COMP_HEADER), CompressedSize, CompressedBuffer);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO, "Comp kernel (%u bytes) cannot be read at %08X\n", CompressedSize, *Offset));
FreePool (CompressedBuffer);
return KernelSize;
}
if (CompressionType == MACH_COMPRESSED_BINARY_INVERT_LZVN) {
KernelSize = (UINT32) DecompressLZVN (*Buffer, DecompressedSize, CompressedBuffer, CompressedSize);
} else if (CompressionType == MACH_COMPRESSED_BINARY_INVERT_LZSS) {
KernelSize = (UINT32) DecompressLZSS (*Buffer, DecompressedSize, CompressedBuffer, CompressedSize);
}
if (KernelSize != DecompressedSize) {
KernelSize = 0;
}
//
// TODO: implement adler32 hash verification.
//
FreePool (CompressedBuffer);
return KernelSize;
}
STATIC
EFI_STATUS
ReadAppleKernelImage (
IN EFI_FILE_PROTOCOL *File,
IN OUT UINT8 **Buffer,
OUT UINT32 *KernelSize,
OUT UINT32 *AllocatedSize,
IN UINT32 Offset
)
{
EFI_STATUS Status;
UINT32 *MagicPtr;
BOOLEAN ForbidFat;
BOOLEAN Compressed;
Status = ReadFileData (File, Offset, KERNEL_HEADER_SIZE, *Buffer);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Do not allow FAT architectures with Offset > 0 (recursion).
//
ForbidFat = Offset > 0;
Compressed = FALSE;
while (TRUE) {
MagicPtr = (UINT32 *) *Buffer;
if (!OC_ALIGNED (MagicPtr)) {
DEBUG ((DEBUG_INFO, "Misaligned kernel header %p at %08X\n", MagicPtr, Offset));
return EFI_INVALID_PARAMETER;
}
switch (*MagicPtr) {
case MACH_HEADER_64_SIGNATURE:
DEBUG ((DEBUG_VERBOSE, "Found Mach-O compressed %d offset %u size %u\n", Compressed, Offset, *KernelSize));
//
// This is just a valid (formerly) compressed image.
//
if (Compressed) {
return EFI_SUCCESS;
}
//
// This is an uncompressed image, just fully read it.
//
if (Offset == 0) {
//
// Figure out size for a non fat image.
//
Status = ReadFileSize (File, KernelSize);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO, "Kernel size cannot be determined - %r\n", Status));
return EFI_OUT_OF_RESOURCES;
}
}
Status = ReplaceBuffer (*KernelSize, Buffer, AllocatedSize);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO, "Kernel (%u bytes) cannot be allocated at %08X\n", *KernelSize, Offset));
return Status;
}
Status = ReadFileData (File, Offset, *KernelSize, *Buffer);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_INFO, "Kernel (%u bytes) cannot be read at %08X\n", *KernelSize, Offset));
}
return Status;
case MACH_FAT_BINARY_SIGNATURE:
case MACH_FAT_BINARY_INVERT_SIGNATURE: {
if (ForbidFat) {
DEBUG ((DEBUG_INFO, "Fat kernel recursion %p at %08X\n", MagicPtr, Offset));
return EFI_INVALID_PARAMETER;
}
*KernelSize = ParseFatArchitecture (File, Buffer, &Offset);
if (*KernelSize != 0) {
return ReadAppleKernelImage (File, Buffer, KernelSize, AllocatedSize, Offset);
}
return EFI_INVALID_PARAMETER;
}
case MACH_COMPRESSED_BINARY_INVERT_SIGNATURE: {
if (Compressed) {
DEBUG ((DEBUG_INFO, "Compression recursion %p at %08X\n", MagicPtr, Offset));
return EFI_INVALID_PARAMETER;
}
//
// No FAT or Comp is allowed after compressed.
//
ForbidFat = Compressed = TRUE;
//
// Loop into updated image in Buffer.
//
*KernelSize = ParseCompressedHeader (File, Buffer, &Offset, AllocatedSize);
if (*KernelSize != 0) {
DEBUG ((DEBUG_VERBOSE, "Compressed result has %08X magic\n", *(UINT32 *) Buffer));
continue;
}
return EFI_INVALID_PARAMETER;
}
default:
DEBUG ((Offset > 0 ? DEBUG_INFO : DEBUG_VERBOSE, "Invalid kernel magic %08X at %08X\n", *MagicPtr, Offset));
return EFI_INVALID_PARAMETER;
}
}
}
EFI_STATUS
ReadAppleKernel (
IN EFI_FILE_PROTOCOL *File,
IN OUT UINT8 **Kernel,
OUT UINT32 *KernelSize,
OUT UINT32 *AllocatedSize
)
{
EFI_STATUS Status;
*KernelSize = 0;
*AllocatedSize = KERNEL_HEADER_SIZE;
*Kernel = AllocatePool (KERNEL_HEADER_SIZE);
if (*Kernel == NULL) {
return EFI_INVALID_PARAMETER;
}
Status = ReadAppleKernelImage (
File,
Kernel,
KernelSize,
AllocatedSize,
0
);
if (EFI_ERROR (Status)) {
FreePool (*Kernel);
}
return Status;
}

View File

@ -40,3 +40,5 @@
[LibraryClasses]
BaseLib
BaseMemoryLib
MemoryAllocationLib

View File

@ -0,0 +1,72 @@
/** @file
Copyright (C) 2019, vit9696. All rights reserved.
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 <Uefi.h>
#include <Guid/FileInfo.h>
#include <Protocol/SimpleFileSystem.h>
EFI_STATUS
ReadFileData (
IN EFI_FILE_PROTOCOL *File,
IN UINT32 Position,
IN UINT32 Size,
OUT UINT8 *Buffer
)
{
EFI_STATUS Status;
UINTN ReadSize;
Status = File->SetPosition (File, Position);
if (EFI_ERROR (Status)) {
return Status;
}
ReadSize = Size;
Status = File->Read (File, &ReadSize, Buffer);
if (EFI_ERROR (Status)) {
return Status;
}
if (ReadSize != Size) {
return EFI_BAD_BUFFER_SIZE;
}
return EFI_SUCCESS;
}
EFI_STATUS
ReadFileSize (
IN EFI_FILE_PROTOCOL *File,
OUT UINT32 *Size
)
{
EFI_STATUS Status;
UINT64 Position;
Status = File->SetPosition (File, 0xFFFFFFFFFFFFFFFFULL);
if (EFI_ERROR (Status)) {
return Status;
}
Status = File->GetPosition (File, &Position);
if (EFI_ERROR (Status)) {
return Status;
}
if ((UINT32) Position != Position) {
return EFI_OUT_OF_RESOURCES;
}
return EFI_SUCCESS;
}

View File

@ -27,6 +27,7 @@
# VALID_ARCHITECTURES = IA32 X64
[Sources]
FileProtocol.c
GetFileInfo.c
GetVolumeLabel.c
ReadFile.c

View File

@ -100,7 +100,7 @@ EFIAPI
VirtualFileRead (
IN EFI_FILE_PROTOCOL *This,
IN OUT UINTN *BufferSize,
OUT VOID *Buffer
OUT VOID *Buffer
)
{
VIRTUAL_FILE_DATA *Data;

View File

@ -59,6 +59,9 @@
## @libraryclass
OcAppleImageVerificationLib|Include/Library/OcAppleImageVerificationLib.h
## @libraryclass
OcAppleKernelLib|Include/Library/OcAppleKernelLib.h
## @libraryclass
OcCompressionLib|Include/Library/OcCompressionLib.h

View File

@ -51,6 +51,7 @@
OcAppleBootPolicyLib|OcSupportPkg/Library/OcAppleBootPolicyLib/OcAppleBootPolicyLib.inf
OcAppleChunklistLib|OcSupportPkg/Library/OcAppleChunklistLib/OcAppleChunklistLib.inf
OcAppleImageVerificationLib|OcSupportPkg/Library/OcAppleImageVerificationLib/OcAppleImageVerificationLib.inf
OcAppleKernelLib|OcSupportPkg/Library/OcAppleKernelLib/OcAppleKernelLib.inf
OcCpuLib|OcSupportPkg/Library/OcCpuLib/OcCpuLib.inf
OcCryptoLib|OcSupportPkg/Library/OcCryptoLib/OcCryptoLib.inf
OcCompressionLib|OcSupportPkg/Library/OcCompressionLib/OcCompressionLib.inf
@ -78,6 +79,7 @@
OcSupportPkg/Library/OcAppleBootPolicyLib/OcAppleBootPolicyLib.inf
OcSupportPkg/Library/OcAppleChunklistLib/OcAppleChunklistLib.inf
OcSupportPkg/Library/OcAppleImageVerificationLib/OcAppleImageVerificationLib.inf
OcSupportPkg/Library/OcAppleKernelLib/OcAppleKernelLib.inf
OcSupportPkg/Library/OcCpuLib/OcCpuLib.inf
OcSupportPkg/Library/OcCryptoLib/OcCryptoLib.inf
OcSupportPkg/Library/OcCompressionLib/OcCompressionLib.inf

View File

@ -17,6 +17,12 @@ listed here.
**Issues**:
1. No proper interface for OS detection.
1. No dmg boot detection.
* OcAppleKernelLib
**Status**: functional
**Issues**: none
* OcCompressionLib
**Status**: functional
**Issues**: none
* OcAppleChunklistLib
**Status**: in progress
**Issues**:

View File

@ -28,7 +28,9 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#include <Library/OcAppleBootPolicyLib.h>
#include <Library/OcSmbiosLib.h>
#include <Library/OcCpuLib.h>
#include <Library/OcStringLib.h>
#include <Library/OcVirtualFsLib.h>
#include <Library/OcAppleKernelLib.h>
#include <Protocol/AppleBootPolicy.h>
#include <Protocol/DevicePathPropertyDatabase.h>
@ -55,11 +57,68 @@ TestFileOpen (
IN UINT64 Attributes
)
{
EFI_STATUS Status;
EFI_STATUS Status;
UINT8 *Kernel;
UINT32 KernelSize;
UINT32 AllocatedSize;
CHAR16 *FileNameCopy;
EFI_FILE_PROTOCOL *VirtualFileHandle;
Status = This->Open (This, NewHandle, FileName, OpenMode, Attributes);
Print (L"[FS %p] %s in %X/%X - %r\n", This, FileName, OpenMode, Attributes, Status);
if (!EFI_ERROR (Status)
&& OpenMode == EFI_FILE_MODE_READ
&& StrStr (FileName, L"kernel") != NULL) {
Print (L"Trying XNU hook on %s\n", FileName);
Status = ReadAppleKernel (*NewHandle, &Kernel, &KernelSize, &AllocatedSize);
Print (L"Result of XNU hook on %s is %r\n", FileName, Status);
//
// This is not Apple kernel, just return the original file.
//
if (EFI_ERROR (Status)) {
return EFI_SUCCESS;
}
//
// TODO: patches, dropping, and injection here.
//
ApplyPatch (
(UINT8 *) "Darwin Kernel Version",
NULL,
L_STR_LEN ("Darwin Kernel Version"),
(UINT8 *) "OpenCore Boot Version",
Kernel,
KernelSize,
1,
0
);
//
// This was our file, yet firmware is dying.
//
FileNameCopy = AllocateCopyPool (StrSize (FileName), FileName);
if (FileNameCopy == NULL) {
DEBUG ((DEBUG_WARN, "Failed to allocate kernel name (%a) copy\n", FileName));
FreePool (Kernel);
return EFI_SUCCESS;
}
Status = CreateVirtualFile (FileNameCopy, Kernel, KernelSize, &VirtualFileHandle);
if (EFI_ERROR (Status)) {
DEBUG ((DEBUG_WARN, "Failed to virtualise kernel file (%a)\n", FileName));
FreePool (Kernel);
FreePool (FileNameCopy);
return EFI_SUCCESS;
}
//
// Return our handle.
//
(*NewHandle)->Close(*NewHandle);
*NewHandle = VirtualFileHandle;
}
return Status;
}

View File

@ -62,6 +62,7 @@
OcMiscLib
OcProtocolLib
OcAppleBootPolicyLib
OcAppleKernelLib
OcSmbiosLib
OcDataHubLib
OcVirtualFsLib

View File

@ -61,4 +61,6 @@
OcAppleBootPolicyLib
OcSmbiosLib
OcDataHubLib
OcAppleKernelLib
OcCompressionLib
OcVirtualFsLib