未验证 提交 7e965893 编写于 作者: V vit9696 提交者: GitHub

OcAppleKernelLib: Implement 64-bit cacheless kext injection (#96)

references acidanthera/bugtracker#358
上级 6f4f7623
......@@ -42,6 +42,10 @@
#define INFO_BUNDLE_LIBRARIES_64_KEY "OSBundleLibraries_x86_64"
#define INFO_BUNDLE_VERSION_KEY "CFBundleVersion"
#define INFO_BUNDLE_COMPATIBLE_VERSION_KEY "OSBundleCompatibleVersion"
#define INFO_BUNDLE_OS_BUNDLE_REQUIRED_KEY "OSBundleRequired"
#define OS_BUNDLE_REQUIRED_ROOT "Root"
#define OS_BUNDLE_REQUIRED_SAFE_BOOT "Safe Boot"
#define PRELINK_INFO_INTEGER_ATTRIBUTES "size=\"64\""
......@@ -251,6 +255,36 @@ typedef struct {
UINT32 Limit;
} PATCHER_GENERIC_PATCH;
//
// Context for cacheless boot (S/L/E).
//
typedef struct {
//
// Extensions directory EFI_FILE_PROTOCOL instance.
//
EFI_FILE_PROTOCOL *ExtensionsDir;
//
// Extensions directory filename. This is freed by the caller.
//
CONST CHAR16 *ExtensionsDirFileName;
//
// Injected kext list.
//
LIST_ENTRY InjectedKexts;
//
// Dependency bundle list for injected kexts.
//
LIST_ENTRY InjectedDependencies;
//
// List of built-in shipping kexts.
//
LIST_ENTRY BuiltInKexts;
//
// Flag to indicate if above list is valid. List is built during the first read from SLE.
//
BOOLEAN BuiltInKextsValid;
} CACHELESS_CONTEXT;
/**
Read Apple kernel for target architecture (possibly decompressing)
into pool allocated buffer.
......@@ -741,4 +775,101 @@ PatchAppleRtcChecksum (
IN OUT PRELINKED_CONTEXT *Context
);
/**
Initializes cacheless context for later modification.
Must be freed with CachelessContextFree on success.
@param[in,out] Context Cacheless context.
@param[in] FileName Extensions directory filename.
@param[in] ExtensionsDir Extensions directory EFI_FILE_PROTOCOL.
@return EFI_SUCCESS on success.
**/
EFI_STATUS
CachelessContextInit (
IN OUT CACHELESS_CONTEXT *Context,
IN CONST CHAR16 *FileName,
IN EFI_FILE_PROTOCOL *ExtensionsDir
);
/**
Frees cacheless context.
@param[in,out] Context Cacheless context.
@return EFI_SUCCESS on success.
**/
VOID
CachelessContextFree (
IN OUT CACHELESS_CONTEXT *Context
);
/**
Add kext to cacheless context to be injected later on.
@param[in,out] Context Cacheless context.
@param[in] InfoPlist Kext Info.plist.
@param[in] InfoPlistSize Kext Info.plist size.
@param[in] Executable Kext executable, optional.
@param[in] ExecutableSize Kext executable size, optional.
@return EFI_SUCCESS on success.
**/
EFI_STATUS
CachelessContextAddKext (
IN OUT CACHELESS_CONTEXT *Context,
IN CONST CHAR8 *InfoPlist,
IN UINT32 InfoPlistSize,
IN CONST UINT8 *Executable OPTIONAL,
IN UINT32 ExecutableSize OPTIONAL
);
/**
Creates virtual directory overlay EFI_FILE_PROTOCOL from cacheless context.
@param[in,out] Context Cacheless context.
@param[out] File The virtual directory instance.
@return EFI_SUCCESS on success.
**/
EFI_STATUS
CachelessContextOverlayExtensionsDir (
IN OUT CACHELESS_CONTEXT *Context,
OUT EFI_FILE_PROTOCOL **File
);
/**
Perform kext injection.
@param[in,out] Context Prelinked context.
@param[in] FileName Filename of kext file to be injected.
@param[out] VirtualFile Newly created virtualised EFI_FILE_PROTOCOL instance.
@return EFI_SUCCESS on success.
**/
EFI_STATUS
CachelessContextPerformInject (
IN OUT CACHELESS_CONTEXT *Context,
IN CONST CHAR16 *FileName,
OUT EFI_FILE_PROTOCOL **VirtualFile
);
/**
Apply patches to built-in kexts.
@param[in,out] Context Prelinked context.
@param[in] FileName Filename of kext file to be injected.
@param[in] File EFI_FILE_PROTOCOL instance of kext file.
@param[out] VirtualFile Newly created virtualised EFI_FILE_PROTOCOL instance.
@return EFI_SUCCESS on success. If no patches are applicable, VirtualFile will be NULL.
**/
EFI_STATUS
CachelessContextHookBuiltin (
IN OUT CACHELESS_CONTEXT *Context,
IN CONST CHAR16 *FileName,
IN EFI_FILE_PROTOCOL *File,
OUT EFI_FILE_PROTOCOL **VirtualFile
);
#endif // OC_APPLE_KERNEL_LIB_H
......@@ -114,6 +114,21 @@ OcAsciiSafeSPrint (
...
);
/** Check if ASCII string ends with another ASCII string.
@param[in] String A pointer to a Null-terminated ASCII string.
@param[in] SearchString A pointer to a Null-terminated ASCII string
to compare against String.
@retval TRUE if String ends with SearchString.
**/
BOOLEAN
EFIAPI
OcAsciiEndsWith (
IN CONST CHAR8 *String,
IN CONST CHAR8 *SearchString
);
/**
Performs a case insensitive comparison of two Null-terminated Unicode strings,
and returns the difference between the first mismatched Unicode characters.
......@@ -269,6 +284,21 @@ OcUnicodeSafeSPrint (
...
);
/** Check if Unicode string ends with another Unicode string.
@param[in] String A pointer to a Null-terminated Unicode string.
@param[in] SearchString A pointer to a Null-terminated Unicode string
to compare against String.
@retval TRUE if String ends with SearchString.
**/
BOOLEAN
EFIAPI
OcUnicodeEndsWith (
IN CONST CHAR16 *String,
IN CONST CHAR16 *SearchString
);
/**
Convert path with mixed slashes to UEFI slashes (\\).
......
此差异已折叠。
/** @file
Cacheless boot (S/L/E) support.
Copyright (c) 2020, Goldfish64. 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 CACHELESS_INTERNAL_H
#define CACHELESS_INTERNAL_H
#include <Library/OcAppleKernelLib.h>
//
// Names are of format OcXXXXXXXX.kext, where XXXXXXXX is a 32-bit hexadecimal number.
//
#define KEXT_BUNDLE_NAME L"OcXXXXXXXX.kext"
#define KEXT_BUNDLE_NAME_SIZE (L_STR_SIZE (KEXT_BUNDLE_NAME))
#define KEXT_BUNDLE_NAME_LEN (L_STR_LEN (KEXT_BUNDLE_NAME))
#define KEXT_BUNDLE_INFO_SIZE (SIZE_OF_EFI_FILE_INFO + KEXT_BUNDLE_NAME_SIZE)
enum {
KEXT_OSBUNDLE_REQUIRED_NONE = 0,
KEXT_OSBUNDLE_REQUIRED_INVALID,
KEXT_OSBUNDLE_REQUIRED_VALID
};
//
// Kext dependency.
//
typedef struct {
//
// Signature.
//
UINT32 Signature;
//
// Link for global list.
//
LIST_ENTRY Link;
//
// Bundle ID.
//
CHAR8 *BundleId;
} DEPEND_KEXT;
//
// Cacheless kext.
//
typedef struct {
//
// Signature.
//
UINT32 Signature;
//
// Link for global list (CACHELESS_CONTEXT -> InjectedKexts).
//
LIST_ENTRY Link;
//
// Bundle filename used during S/L/E overlay creation.
//
CHAR16 BundleFileName[KEXT_BUNDLE_NAME_LEN + 1];
//
// Plist data.
//
CHAR8 *PlistData;
//
// Plist data size.
//
UINT32 PlistDataSize;
//
// Binary data.
//
UINT8 *BinaryData;
//
// Binary data size.
//
UINT32 BinaryDataSize;
//
// Binary file name.
//
CHAR16 *BinaryFileName;
} CACHELESS_KEXT;
//
// Built-in kexts in SLE.
//
typedef struct {
//
// Signature.
//
UINT32 Signature;
//
// Link for global list (CACHELESS_CONTEXT -> BuiltInKexts).
//
LIST_ENTRY Link;
//
// Plist path.
//
CHAR16 *PlistPath;
//
// Bundle ID.
//
CHAR8 *BundleId;
//
// Binary file name.
//
CHAR16 *BinaryFileName;
//
// Dependencies.
//
LIST_ENTRY Dependencies;
//
// OSBundleRequired is valid?
//
UINT8 OSBundleRequiredValue;
//
// Needs OSBundleRequired override for dependency injection?
//
BOOLEAN PatchValidOSBundleRequired;
//
// Needs patches or blocks?
//
BOOLEAN PatchKext;
} BUILTIN_KEXT;
//
// DEPEND_KEXT signature for list identification.
//
#define DEPEND_KEXT_SIGNATURE SIGNATURE_32 ('S', 'l', 'e', 'D')
/**
Gets the next element in list of DEPEND_KEXT.
@param[in] This The current ListEntry.
**/
#define GET_DEPEND_KEXT_FROM_LINK(This) \
(CR ( \
(This), \
DEPEND_KEXT, \
Link, \
DEPEND_KEXT_SIGNATURE \
))
//
// CACHELESS_KEXT signature for list identification.
//
#define CACHELESS_KEXT_SIGNATURE SIGNATURE_32 ('S', 'l', 'e', 'X')
/**
Gets the next element in InjectedKexts list of CACHELESS_KEXT.
@param[in] This The current ListEntry.
**/
#define GET_CACHELESS_KEXT_FROM_LINK(This) \
(CR ( \
(This), \
CACHELESS_KEXT, \
Link, \
CACHELESS_KEXT_SIGNATURE \
))
//
// BUILTIN_KEXT signature for list identification.
//
#define BUILTIN_KEXT_SIGNATURE SIGNATURE_32 ('S', 'l', 'e', 'B')
/**
Gets the next element in BuiltInKexts list of CACHELESS_KEXT.
@param[in] This The current ListEntry.
**/
#define GET_BUILTIN_KEXT_FROM_LINK(This) \
(CR ( \
(This), \
BUILTIN_KEXT, \
Link, \
BUILTIN_KEXT_SIGNATURE \
))
#endif
......@@ -38,6 +38,7 @@
PrelinkedInternal.h
PrelinkedKext.c
Vtables.c
CachelessContext.c
[Packages]
MdePkg/MdePkg.dec
......
......@@ -166,3 +166,23 @@ OcAsciiSafeSPrint (
return Status;
}
BOOLEAN
EFIAPI
OcAsciiEndsWith (
IN CONST CHAR8 *String,
IN CONST CHAR8 *SearchString
)
{
UINTN StringLength;
UINTN SearchStringLength;
ASSERT (String != NULL);
ASSERT (SearchString != NULL);
StringLength = AsciiStrLen (String);
SearchStringLength = AsciiStrLen (SearchString);
return StringLength >= SearchStringLength
&& AsciiStrnCmp (&String[StringLength - SearchStringLength], SearchString, SearchStringLength) == 0;
}
......@@ -271,3 +271,23 @@ OcUnicodeSafeSPrint (
return Status;
}
BOOLEAN
EFIAPI
OcUnicodeEndsWith (
IN CONST CHAR16 *String,
IN CONST CHAR16 *SearchString
)
{
UINTN StringLength;
UINTN SearchStringLength;
ASSERT (String != NULL);
ASSERT (SearchString != NULL);
StringLength = StrLen (String);
SearchStringLength = StrLen (SearchString);
return StringLength >= SearchStringLength
&& StrnCmp (&String[StringLength - SearchStringLength], SearchString, SearchStringLength) == 0;
}
......@@ -29,6 +29,11 @@ STATIC OC_STORAGE_CONTEXT *mOcStorage;
STATIC OC_GLOBAL_CONFIG *mOcConfiguration;
STATIC OC_CPU_INFO *mOcCpuInfo;
STATIC UINT32 mOcDarwinVersion;
STATIC CACHELESS_CONTEXT mOcCachelessContext;
STATIC BOOLEAN mOcCachelessInProgress;
STATIC
UINT32
OcParseDarwinVersion (
......@@ -720,6 +725,76 @@ OcKernelProcessPrelinked (
return Status;
}
STATIC
EFI_STATUS
OcKernelInitCacheless (
IN OC_GLOBAL_CONFIG *Config,
IN CACHELESS_CONTEXT *Context,
IN UINT32 DarwinVersion,
IN CHAR16 *FileName,
IN EFI_FILE_PROTOCOL *ExtensionsDir,
OUT EFI_FILE_PROTOCOL **File
)
{
EFI_STATUS Status;
UINT32 Index;
OC_KERNEL_ADD_ENTRY *Kext;
CHAR8 *BundlePath;
CHAR8 *Comment;
UINT32 MaxKernel;
UINT32 MinKernel;
Status = CachelessContextInit (Context, FileName, ExtensionsDir);
if (EFI_ERROR (Status)) {
return Status;
}
//
// Add kexts into cacheless context.
//
for (Index = 0; Index < Config->Kernel.Add.Count; Index++) {
Kext = Config->Kernel.Add.Values[Index];
if (!Kext->Enabled || Kext->PlistDataSize == 0) {
continue;
}
BundlePath = OC_BLOB_GET (&Kext->BundlePath);
Comment = OC_BLOB_GET (&Kext->Comment);
MaxKernel = OcParseDarwinVersion (OC_BLOB_GET (&Kext->MaxKernel));
MinKernel = OcParseDarwinVersion (OC_BLOB_GET (&Kext->MinKernel));
if (!OcMatchDarwinVersion (DarwinVersion, MinKernel, MaxKernel)) {
DEBUG ((
DEBUG_INFO,
"OC: Cacheless injection skips %a (%a) kext at %u due to version %u <= %u <= %u\n",
BundlePath,
Comment,
Index,
MinKernel,
DarwinVersion,
MaxKernel
));
continue;
}
Status = CachelessContextAddKext (
Context,
Kext->PlistData,
Kext->PlistDataSize,
Kext->ImageData,
Kext->ImageDataSize
);
if (EFI_ERROR (Status)) {
CachelessContextFree (Context);
return Status;
}
}
return CachelessContextOverlayExtensionsDir (Context, File);
}
STATIC
EFI_STATUS
EFIAPI
......@@ -740,12 +815,29 @@ OcKernelFileOpen (
EFI_FILE_PROTOCOL *VirtualFileHandle;
EFI_STATUS PrelinkedStatus;
EFI_TIME ModificationTime;
UINT32 DarwinVersion;
UINT32 ReservedInfoSize;
UINT32 ReservedExeSize;
UINT32 LinkedExpansion;
UINT32 ReservedFullSize;
//
// Hook injected OcXXXXXXXX.kext reads from /S/L/E.
//
if (mOcCachelessInProgress
&& OpenMode == EFI_FILE_MODE_READ
&& StrnCmp (FileName, L"System\\Library\\Extensions\\Oc", L_STR_LEN (L"System\\Library\\Extensions\\Oc")) == 0) {
Status = CachelessContextPerformInject (&mOcCachelessContext, FileName, NewHandle);
DEBUG ((
DEBUG_INFO,
"OC: Hooking SLE injected file %s with %u mode gave - %r\n",
FileName,
(UINT32) OpenMode,
Status
));
return Status;
}
Status = SafeFileOpen (This, NewHandle, FileName, OpenMode, Attributes);
DEBUG ((
......@@ -802,12 +894,12 @@ OcKernelFileOpen (
DEBUG ((DEBUG_INFO, "OC: Result of XNU hook on %s is %r\n", FileName, Status));
if (!EFI_ERROR (Status)) {
DarwinVersion = OcKernelReadDarwinVersion (Kernel, KernelSize);
OcKernelApplyPatches (mOcConfiguration, DarwinVersion, NULL, Kernel, KernelSize);
mOcDarwinVersion = OcKernelReadDarwinVersion (Kernel, KernelSize);
OcKernelApplyPatches (mOcConfiguration, mOcDarwinVersion, NULL, Kernel, KernelSize);
PrelinkedStatus = OcKernelProcessPrelinked (
mOcConfiguration,
DarwinVersion,
mOcDarwinVersion,
Kernel,
&KernelSize,
AllocatedSize,
......@@ -850,6 +942,61 @@ OcKernelFileOpen (
}
}
//
// Hook /S/L/E for cacheless boots.
//
if (OpenMode == EFI_FILE_MODE_READ
&& StrCmp (FileName, L"System\\Library\\Extensions") == 0) {
mOcCachelessInProgress = FALSE;
OcKernelLoadKextsAndReserve (
mOcStorage,
mOcConfiguration,
&ReservedExeSize,
&ReservedInfoSize
);
//
// Initialize Extensions directory overlay for cacheless injection.
//
Status = OcKernelInitCacheless (
mOcConfiguration,
&mOcCachelessContext,
mOcDarwinVersion,
FileName,
*NewHandle,
&VirtualFileHandle
);
DEBUG ((DEBUG_INFO, "OC: Result of SLE hook on %s is %r\n", FileName, Status));
if (!EFI_ERROR (Status)) {
mOcCachelessInProgress = TRUE;
*NewHandle = VirtualFileHandle;
return EFI_SUCCESS;
}
}
//
// Hook /S/L/E contents for processing during cacheless boots.
//
if (mOcCachelessInProgress
&& OpenMode == EFI_FILE_MODE_READ
&& StrnCmp (FileName, L"System\\Library\\Extensions\\", L_STR_LEN (L"System\\Library\\Extensions\\")) == 0) {
Status = CachelessContextHookBuiltin (
&mOcCachelessContext,
FileName,
*NewHandle,
&VirtualFileHandle
);
if (!EFI_ERROR (Status) && VirtualFileHandle != NULL) {
*NewHandle = VirtualFileHandle;
return EFI_SUCCESS;
}
}
//
// This is not Apple kernel, just return the original file.
// We recurse the filtering to additionally catch com.apple.boot.[RPS] directories.
......@@ -869,9 +1016,11 @@ OcLoadKernelSupport (
Status = EnableVirtualFs (gBS, OcKernelFileOpen);
if (!EFI_ERROR (Status)) {
mOcStorage = Storage;
mOcConfiguration = Config;
mOcCpuInfo = CpuInfo;
mOcStorage = Storage;
mOcConfiguration = Config;
mOcCpuInfo = CpuInfo;
mOcDarwinVersion = 0;
mOcCachelessInProgress = FALSE;
} else {
DEBUG ((DEBUG_ERROR, "OC: Failed to enable vfs - %r\n", Status));
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册