diff --git a/Include/Acidanthera/Library/OcAppleKernelLib.h b/Include/Acidanthera/Library/OcAppleKernelLib.h
index 81b0b679..f4352a6b 100644
--- a/Include/Acidanthera/Library/OcAppleKernelLib.h
+++ b/Include/Acidanthera/Library/OcAppleKernelLib.h
@@ -46,6 +46,10 @@
#define PRELINK_INFO_INTEGER_ATTRIBUTES "size=\"64\""
+#define KC_REGION_SEGMENT_PREFIX "__REGION"
+#define KC_TEXT_SEGMENT "__TEXT"
+#define KC_MOSCOW_SEGMENT "__MOSCOW101"
+
//
// Failsafe default for plist reserve allocation.
//
@@ -125,9 +129,15 @@ typedef struct {
VOID *LinkBuffer;
UINT32 LinkBufferSize;
//
- // Used for caching prelinked kexts.
+ // Used for caching all prelinked kexts.
+ // I.e. this contains kernel, injected kexts, and kexts used as dependencies.
//
LIST_ENTRY PrelinkedKexts;
+ //
+ // Used for caching prelinked kexts, which we inject.
+ // This is a sublist of PrelinkedKexts.
+ //
+ LIST_ENTRY InjectedKexts;
} PRELINKED_CONTEXT;
//
diff --git a/Include/Acidanthera/Library/OcMachoLib.h b/Include/Acidanthera/Library/OcMachoLib.h
index 2f236c1f..0b6c0a82 100644
--- a/Include/Acidanthera/Library/OcMachoLib.h
+++ b/Include/Acidanthera/Library/OcMachoLib.h
@@ -231,6 +231,21 @@ MachoGetSectionByAddress64 (
IN UINT64 Address
);
+/**
+ Merge Mach-O segments into one with lowest protection.
+
+ @param[in,out] Context Context of the Mach-O.
+ @param[in] Prefix Segment prefix to merge.
+
+ @retval TRUE on success
+
+**/
+BOOLEAN
+MachoMergeSegments64 (
+ IN OUT OC_MACHO_CONTEXT *Context,
+ IN CONST CHAR8 *Prefix
+ );
+
/**
Returns whether Symbol describes a section.
diff --git a/Include/Apple/IndustryStandard/AppleMachoImage.h b/Include/Apple/IndustryStandard/AppleMachoImage.h
index ec645a2e..263c0408 100644
--- a/Include/Apple/IndustryStandard/AppleMachoImage.h
+++ b/Include/Apple/IndustryStandard/AppleMachoImage.h
@@ -1779,6 +1779,21 @@ typedef struct {
UINT32 HeaderAddress; ///< files virtual address
} MACH_FIXED_VM_FILE_COMMAND;
+///
+/// LC_FILESET_ENTRY commands describe constituent Mach-O files that are part
+/// of a fileset. In one implementation, entries are dylibs with individual
+/// mach headers and repositionable text and data segments. Each entry is
+/// further described by its own mach header.
+///
+typedef struct {
+ MACH_LOAD_COMMAND_HDR_
+ UINT64 VirtualAddress; ///< memory address of the entry
+ UINT64 FileOffset; ///< file offset of the entry
+ MACH_LOAD_COMMAND_STRING EntryId; ///< contained entry id
+ UINT32 Reserved; ///< reserved
+ CHAR8 Payload[]; ///< file information starting at entry id
+} MACH_FILESET_ENTRY_COMMAND;
+
///
/// The entry_point_command is a replacement for thread_command.
/// It is used for main executables to specify the location (file offset)
@@ -1813,37 +1828,40 @@ typedef struct {
} MACH_SOURCE_VERSION_COMMAND;
typedef union {
- CONST MACH_LOAD_COMMAND *Hdr;
- CONST MACH_SEGMENT_COMMAND *Segment;
- CONST MACH_SEGMENT_COMMAND_64 *Segment64;
- CONST MACH_FIXED_VM_LIB_COMMAND *VmLib;
- CONST MACH_DYLIB_COMMAND *Dylib;
- CONST MACH_SUB_FRAMEWORK_COMMAND *SubFramework;
- CONST MACH_SUB_CLIENT_COMMAND *SubClient;
- CONST MACH_SUB_UMBRELLA_COMMAND *SubUmbrella;
- CONST MACH_SUB_LIBRARY_COMMAND *SubLibrary;
- CONST MACH_PREBOUND_DYLIB_COMMAND *PreboundDyLib;
- CONST MACH_DYLINKER_COMMAND *Dylinker;
- CONST MACH_THREAD_COMMAND *Thread;
- CONST MACH_ROUTINES_COMMAND *Routines;
- CONST MACH_ROUTINES_COMMAND_64 *Routines64;
- CONST MACH_SYMTAB_COMMAND *Symtab;
- CONST MACH_DYSYMTAB_COMMAND *Dysymtab;
- CONST MACH_TWO_LEVEL_HINTS_COMMAND *TwoLevelHints;
- CONST MACH_PREBIND_CHECKSUM_COMMAND *PrebindChecksum;
- CONST MACH_UUID_COMMAND *Uuid;
- CONST MACH_RUN_PATH_COMMAND *RunPath;
- CONST MACH_LINKEDIT_DATA_COMMAND *LinkeditData;
- CONST MACH_ENCRYPTION_INFO_COMMAND *EncryptionInfo;
- CONST MACH_ENCRYPTION_INFO_COMMAND_64 *EncryptionInfo64;
- CONST MACH_VERSION_MIN_COMMAND *VersionMin;
- CONST MACH_BUILD_VERSION_COMMAND *BuildVersion;
- CONST MACH_DYLD_INFO_COMMAND *DyldInfo;
- CONST MACH_LINKER_OPTION_COMMAND *LinkerOption;
- CONST MACH_SYMBOL_SEGMENT_COMMAND *SymbolSegment;
- CONST MACH_IDENTIFICATION_COMMAND *Identification;
- CONST MACH_FIXED_VM_FILE_COMMAND *FixedVmFile;
- CONST VOID *Pointer;
+ MACH_LOAD_COMMAND *Hdr;
+ MACH_SEGMENT_COMMAND *Segment;
+ MACH_SEGMENT_COMMAND_64 *Segment64;
+ MACH_FIXED_VM_LIB_COMMAND *VmLib;
+ MACH_DYLIB_COMMAND *Dylib;
+ MACH_SUB_FRAMEWORK_COMMAND *SubFramework;
+ MACH_SUB_CLIENT_COMMAND *SubClient;
+ MACH_SUB_UMBRELLA_COMMAND *SubUmbrella;
+ MACH_SUB_LIBRARY_COMMAND *SubLibrary;
+ MACH_PREBOUND_DYLIB_COMMAND *PreboundDyLib;
+ MACH_DYLINKER_COMMAND *Dylinker;
+ MACH_THREAD_COMMAND *Thread;
+ MACH_ROUTINES_COMMAND *Routines;
+ MACH_ROUTINES_COMMAND_64 *Routines64;
+ MACH_SYMTAB_COMMAND *Symtab;
+ MACH_DYSYMTAB_COMMAND *Dysymtab;
+ MACH_TWO_LEVEL_HINTS_COMMAND *TwoLevelHints;
+ MACH_PREBIND_CHECKSUM_COMMAND *PrebindChecksum;
+ MACH_UUID_COMMAND *Uuid;
+ MACH_RUN_PATH_COMMAND *RunPath;
+ MACH_LINKEDIT_DATA_COMMAND *LinkeditData;
+ MACH_ENCRYPTION_INFO_COMMAND *EncryptionInfo;
+ MACH_ENCRYPTION_INFO_COMMAND_64 *EncryptionInfo64;
+ MACH_VERSION_MIN_COMMAND *VersionMin;
+ MACH_BUILD_VERSION_COMMAND *BuildVersion;
+ MACH_DYLD_INFO_COMMAND *DyldInfo;
+ MACH_LINKER_OPTION_COMMAND *LinkerOption;
+ MACH_SYMBOL_SEGMENT_COMMAND *SymbolSegment;
+ MACH_IDENTIFICATION_COMMAND *Identification;
+ MACH_FIXED_VM_FILE_COMMAND *FixedVmFile;
+ MACH_LINKEDIT_DATA_COMMAND *DyldExportsTrie;
+ MACH_LINKEDIT_DATA_COMMAND *DyldChainedFixups;
+ MACH_FILESET_ENTRY_COMMAND *FilesetEntry;
+ VOID *Pointer;
UINTN Address;
} MACH_LOAD_COMMAND_PTR;
diff --git a/Library/OcAppleKernelLib/KernelCollection.c b/Library/OcAppleKernelLib/KernelCollection.c
new file mode 100644
index 00000000..31e6c04b
--- /dev/null
+++ b/Library/OcAppleKernelLib/KernelCollection.c
@@ -0,0 +1,231 @@
+/** @file
+ Kernel collection support.
+
+ Copyright (c) 2020, 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.
+
+**/
+
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "PrelinkedInternal.h"
+
+STATIC
+UINTN
+InternalKcGetKextFilesetSize (
+ IN OUT PRELINKED_CONTEXT *Context
+ )
+{
+ PRELINKED_KEXT *PrelinkedKext;
+ LIST_ENTRY *Kext;
+ UINTN Size;
+ UINTN CommandSize;
+
+ Size = 0;
+
+ Kext = GetFirstNode (&Context->InjectedKexts);
+ while (!IsNull (&Context->InjectedKexts, Kext)) {
+ PrelinkedKext = GET_INJECTED_KEXT_FROM_LINK (Kext);
+
+ //
+ // Each command must be NUL-terminated and 8-byte aligned.
+ //
+ CommandSize = sizeof (MACH_FILESET_ENTRY_COMMAND) + AsciiStrSize (PrelinkedKext->Identifier);
+ Size += ALIGN_VALUE (CommandSize, 8);
+
+ Kext = GetNextNode (&Context->InjectedKexts, Kext);
+ }
+
+ return Size;
+}
+
+STATIC
+VOID
+InternalKcWriteCommandHeaders (
+ IN OUT PRELINKED_CONTEXT *Context,
+ IN OUT MACH_HEADER_64 *MachHeader
+ )
+{
+ PRELINKED_KEXT *PrelinkedKext;
+ PRELINKED_KEXT *LowestKext;
+ LIST_ENTRY *Kext;
+ MACH_LOAD_COMMAND_PTR Command;
+ MACH_SEGMENT_COMMAND_64 *Segment;
+ UINTN StringSize;
+
+ Command.Address = (UINTN) MachHeader->Commands + MachHeader->CommandsSize;
+
+ Kext = GetFirstNode (&Context->InjectedKexts);
+ LowestKext = GET_INJECTED_KEXT_FROM_LINK (Kext);
+
+ while (!IsNull (&Context->InjectedKexts, Kext)) {
+ PrelinkedKext = GET_INJECTED_KEXT_FROM_LINK (Kext);
+
+ StringSize = AsciiStrSize (PrelinkedKext->Identifier);
+
+ //
+ // Write 8-byte aligned fileset command.
+ //
+ Command.FilesetEntry->CommandType = MACH_LOAD_COMMAND_FILESET_ENTRY;
+ Command.FilesetEntry->CommandSize = ALIGN_VALUE (sizeof (MACH_FILESET_ENTRY_COMMAND) + StringSize, 8);
+ Command.FilesetEntry->VirtualAddress = PrelinkedKext->Context.VirtualBase;
+ Segment = MachoGetNextSegment64 (&PrelinkedKext->Context.MachContext, NULL);
+ ASSERT (Segment != NULL);
+ Command.FilesetEntry->FileOffset = Segment->FileOffset;
+ Command.FilesetEntry->EntryId.Offset = OFFSET_OF (MACH_FILESET_ENTRY_COMMAND, Payload);
+ Command.FilesetEntry->Reserved = 0;
+ CopyMem (Command.FilesetEntry->Payload, PrelinkedKext->Identifier, StringSize);
+ ZeroMem (
+ &Command.FilesetEntry->Payload[StringSize],
+ Command.FilesetEntry->CommandSize - Command.FilesetEntry->EntryId.Offset - StringSize
+ );
+ Command.Address += Command.FilesetEntry->CommandSize;
+
+ //
+ // Refresh Mach-O header constants to include the new command.
+ //
+ MachHeader->NumCommands++;
+ MachHeader->CommandsSize += Command.FilesetEntry->CommandSize;
+
+ Kext = GetNextNode (&Context->InjectedKexts, Kext);
+ }
+
+ //
+ // Get last kext.
+ //
+ Kext = GetPreviousNode (&Context->InjectedKexts, &Context->InjectedKexts);
+ PrelinkedKext = GET_INJECTED_KEXT_FROM_LINK (Kext);
+
+ //
+ // Write a segment covering all the kexts.
+ //
+ Command.Segment64->CommandType = MACH_LOAD_COMMAND_SEGMENT_64;
+ Command.Segment64->CommandSize = sizeof (MACH_SEGMENT_COMMAND_64);
+ CopyMem (Command.Segment64->SegmentName, KC_MOSCOW_SEGMENT, sizeof (KC_MOSCOW_SEGMENT));
+ ZeroMem (
+ &Command.Segment64->SegmentName[sizeof (KC_MOSCOW_SEGMENT)],
+ sizeof (Command.Segment64->SegmentName) - sizeof (KC_MOSCOW_SEGMENT)
+ );
+ Segment = MachoGetNextSegment64 (&LowestKext->Context.MachContext, NULL);
+ Command.Segment64->VirtualAddress = Segment->VirtualAddress;
+ Command.Segment64->FileOffset = Segment->FileOffset;
+ Segment = MachoGetNextSegment64 (&PrelinkedKext->Context.MachContext, NULL);
+ Command.Segment64->Size = Segment->VirtualAddress - Command.Segment64->VirtualAddress
+ + MachoGetVmSize64 (&PrelinkedKext->Context.MachContext);
+ Command.Segment64->FileSize = Segment->FileOffset - Command.Segment64->FileOffset
+ + MachoGetFileSize (&PrelinkedKext->Context.MachContext);
+ Command.Segment64->MaximumProtection = MACH_SEGMENT_VM_PROT_READ
+ | MACH_SEGMENT_VM_PROT_WRITE | MACH_SEGMENT_VM_PROT_EXECUTE;
+ Command.Segment64->InitialProtection = MACH_SEGMENT_VM_PROT_READ
+ | MACH_SEGMENT_VM_PROT_WRITE | MACH_SEGMENT_VM_PROT_EXECUTE;
+ Command.Segment64->NumSections = 0;
+ Command.Segment64->Flags = 0;
+
+ //
+ // Refresh Mach-O header constants to include the new segment command.
+ //
+ MachHeader->NumCommands++;
+ MachHeader->CommandsSize += sizeof (MACH_SEGMENT_COMMAND_64);
+}
+
+EFI_STATUS
+InternalKcRebuildMachHeader (
+ IN OUT PRELINKED_CONTEXT *Context
+ )
+{
+ MACH_HEADER_64 *MachHeader;
+ MACH_SEGMENT_COMMAND_64 *TextSegment;
+ UINTN CurrentSize;
+ UINTN FilesetSize;
+ UINTN RequiredSize;
+
+ MachHeader = MachoGetMachHeader64 (
+ &Context->PrelinkedMachContext
+ );
+
+ CurrentSize = MachHeader->CommandsSize + sizeof (*MachHeader);
+ FilesetSize = InternalKcGetKextFilesetSize (Context);
+ RequiredSize = FilesetSize + sizeof (MACH_LOAD_COMMAND_SEGMENT_64);
+
+ TextSegment = MachoGetSegmentByName64 (
+ &Context->PrelinkedMachContext,
+ KC_TEXT_SEGMENT
+ );
+
+ DEBUG ((
+ DEBUG_INFO,
+ "OCAK: KC TEXT is %u bytes with %u Mach-O headers need %u\n",
+ (UINT32) (TextSegment != NULL ? TextSegment->FileSize : 0),
+ (UINT32) CurrentSize,
+ (UINT32) RequiredSize
+ ));
+
+ if (TextSegment == NULL
+ || TextSegment->FileOffset != 0
+ || TextSegment->FileSize != TextSegment->Size
+ || TextSegment->FileSize < CurrentSize) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (FilesetSize == 0) {
+ return EFI_SUCCESS; ///< Just in case.
+ }
+
+ if (CurrentSize + RequiredSize > TextSegment->FileSize) {
+ //
+ // We do not have enough memory in the header, free some memory by merging
+ // kext segments (__REGION###) into a single RWX segment.
+ // - This is not bad for security as it is actually how it is done before
+ // 11.0, where OSKext::setVMAttributes sets the proper memory permissions
+ // on every kext soon after the kernel loads.
+ // - This is not bad for compatibility as the only thing referencing these
+ // segments is dyld fixups command, and it does not matter whether it
+ // references the base segment or points to the middle of it.
+ // The actual reason this was added might actually be because of the ARM
+ // transition, where you can restrict the memory permissions till the next
+ // hardware reset (like on iOS) and they now really try to require W^X:
+ // https://developer.apple.com/videos/play/wwdc2020/10686/
+ //
+ if (!MachoMergeSegments64 (&Context->PrelinkedMachContext, KC_REGION_SEGMENT_PREFIX)) {
+ DEBUG ((DEBUG_INFO, "OCAK: Segment expansion failure\n"));
+ return EFI_UNSUPPORTED;
+ }
+
+ CurrentSize = MachHeader->CommandsSize + sizeof (*MachHeader);
+ }
+
+ //
+ // If we do not fit even after the expansion, we are really doomed.
+ // This should never happen.
+ //
+ if (CurrentSize + RequiredSize > TextSegment->FileSize) {
+ DEBUG ((DEBUG_INFO, "OCAK: Used header %u is still too large\n", (UINT32) CurrentSize));
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // At this step we have memory for all the new commands.
+ // Just write the here.
+ //
+ InternalKcWriteCommandHeaders (Context, MachHeader);
+
+ return EFI_SUCCESS;
+}
diff --git a/Library/OcAppleKernelLib/OcAppleKernelLib.inf b/Library/OcAppleKernelLib/OcAppleKernelLib.inf
index 8f3e7db5..25972065 100644
--- a/Library/OcAppleKernelLib/OcAppleKernelLib.inf
+++ b/Library/OcAppleKernelLib/OcAppleKernelLib.inf
@@ -33,6 +33,7 @@
KextPatcher.c
Link.c
CommonPatches.c
+ KernelCollection.c
PrelinkedContext.c
PrelinkedInternal.h
PrelinkedKext.c
diff --git a/Library/OcAppleKernelLib/PrelinkedContext.c b/Library/OcAppleKernelLib/PrelinkedContext.c
index d0838097..dcb09bdc 100644
--- a/Library/OcAppleKernelLib/PrelinkedContext.c
+++ b/Library/OcAppleKernelLib/PrelinkedContext.c
@@ -173,6 +173,7 @@ PrelinkedContextInit (
// Initialize kext list with kernel pseudo kext.
//
InitializeListHead (&Context->PrelinkedKexts);
+ InitializeListHead (&Context->InjectedKexts);
if (InternalCachedPrelinkedKernel (Context) == NULL) {
return EFI_INVALID_PARAMETER;
}
@@ -320,6 +321,11 @@ PrelinkedContextFree (
}
ZeroMem (&Context->PrelinkedKexts, sizeof (Context->PrelinkedKexts));
+
+ //
+ // We do not need to iterate InjectedKexts here, as its memory was freed above.
+ //
+ ZeroMem (&Context->InjectedKexts, sizeof (Context->InjectedKexts));
}
EFI_STATUS
@@ -707,6 +713,11 @@ PrelinkedInjectKext (
//
if (PrelinkedKext != NULL) {
InsertTailList (&Context->PrelinkedKexts, &PrelinkedKext->Link);
+ //
+ // Additionally register this kext in the injected list, as this is required
+ // for KernelCollection support.
+ //
+ InsertTailList (&Context->InjectedKexts, &PrelinkedKext->InjectedLink);
}
return EFI_SUCCESS;
diff --git a/Library/OcAppleKernelLib/PrelinkedInternal.h b/Library/OcAppleKernelLib/PrelinkedInternal.h
index 2ba79b09..c91c588f 100644
--- a/Library/OcAppleKernelLib/PrelinkedInternal.h
+++ b/Library/OcAppleKernelLib/PrelinkedInternal.h
@@ -61,8 +61,15 @@ struct PRELINKED_KEXT_ {
// eventually be part of a list and to save separate allocations per KEXT.
//
UINT32 Signature;
+ //
+ // Link for global list (PRELINKED_CONTEXT -> PrelinkedKexts).
+ //
LIST_ENTRY Link;
//
+ // Link for local list (PRELINKED_CONTEXT -> InjectedKexts).
+ //
+ LIST_ENTRY InjectedLink;
+ //
// Kext CFBundleIdentifier.
//
CONST CHAR8 *Identifier;
@@ -129,7 +136,7 @@ struct PRELINKED_KEXT_ {
#define PRELINKED_KEXT_SIGNATURE SIGNATURE_32 ('P', 'K', 'X', 'T')
/**
- Gets the next element in a linked list of PRELINKED_KEXT.
+ Gets the next element in PrelinkedKexts list of PRELINKED_KEXT.
@param[in] This The current ListEntry.
**/
@@ -141,6 +148,20 @@ struct PRELINKED_KEXT_ {
PRELINKED_KEXT_SIGNATURE \
))
+/**
+ Gets the next element in InjectedKexts list of PRELINKED_KEXT.
+
+ @param[in] This The current ListEntry.
+**/
+#define GET_INJECTED_KEXT_FROM_LINK(This) \
+ (CR ( \
+ (This), \
+ PRELINKED_KEXT, \
+ InjectedLink, \
+ PRELINKED_KEXT_SIGNATURE \
+ ))
+
+
/**
Creates new PRELINKED_KEXT from OC_MACHO_CONTEXT.
**/
diff --git a/Library/OcMachoLib/Header.c b/Library/OcMachoLib/Header.c
index 70af2203..e2ba94e8 100644
--- a/Library/OcMachoLib/Header.c
+++ b/Library/OcMachoLib/Header.c
@@ -1485,3 +1485,123 @@ MachoRuntimeGetEntryAddress (
return Address;
}
+
+BOOLEAN
+MachoMergeSegments64 (
+ IN OUT OC_MACHO_CONTEXT *Context,
+ IN CONST CHAR8 *Prefix
+ )
+{
+ UINT32 LcIndex;
+ MACH_LOAD_COMMAND *LoadCommand;
+ MACH_SEGMENT_COMMAND_64 *Segment;
+ MACH_SEGMENT_COMMAND_64 *FirstSegment;
+ UINT64 MaxAddress;
+ UINT64 MaxOffset;
+ MACH_VM_PROTECTION MaxInitProt;
+ MACH_VM_PROTECTION MaxMaxProt;
+ MACH_HEADER_64 *Header;
+ UINTN PrefixLength;
+ UINTN SkipCount;
+ UINTN RemainingArea;
+
+ ASSERT (Context != NULL);
+ ASSERT (Context->FileSize != 0);
+ ASSERT (Prefix != NULL);
+
+ Header = MachoGetMachHeader64 (Context);
+ PrefixLength = AsciiStrLen (Prefix);
+ FirstSegment = NULL;
+
+ MaxAddress = 0;
+ MaxOffset = 0;
+ MaxInitProt = 0;
+ MaxMaxProt = 0;
+ SkipCount = 0;
+
+ LoadCommand = &Header->Commands[0];
+
+ for (LcIndex = 0; LcIndex < Header->NumCommands; ++LcIndex) {
+ //
+ // Either skip or stop at unrelated commands.
+ //
+ if (LoadCommand->CommandType != MACH_LOAD_COMMAND_SEGMENT_64
+ || AsciiStrnCmp (Segment->SegmentName, Prefix, PrefixLength) != 0) {
+ if (FirstSegment != NULL) {
+ break;
+ }
+
+ LoadCommand = NEXT_MACH_LOAD_COMMAND (LoadCommand);
+ continue;
+ }
+
+ //
+ // We have a segment starting with the prefix.
+ //
+ Segment = (MACH_SEGMENT_COMMAND_64 *) (VOID *) LoadCommand;
+
+ //
+ // Do not support this for now as it will require changes in the file.
+ //
+ if (Segment->Size != Segment->FileSize) {
+ return FALSE;
+ }
+
+ //
+ // Remember the first segment or assume it is a skip.
+ //
+ if (FirstSegment == NULL) {
+ FirstSegment = Segment;
+ } else {
+ ++SkipCount;
+
+ //
+ // Expand the first segment.
+ // TODO: Do we need to check these for overflow for our own purposes?
+ //
+ FirstSegment->Size = Segment->VirtualAddress - FirstSegment->VirtualAddress + Segment->Size;
+ FirstSegment->FileSize = Segment->FileOffset - FirstSegment->FileOffset + Segment->FileSize;
+
+ //
+ // Add new segment protection to the first segment.
+ //
+ FirstSegment->InitialProtection |= Segment->InitialProtection;
+ FirstSegment->MaximumProtection |= Segment->MaximumProtection;
+ }
+
+ LoadCommand = NEXT_MACH_LOAD_COMMAND (LoadCommand);
+ }
+
+ //
+ // The segment does not exist.
+ //
+ if (FirstSegment == NULL) {
+ return FALSE;
+ }
+
+ //
+ // The segment is only one.
+ //
+ if (SkipCount == 0) {
+ return FALSE;
+ }
+
+ //
+ // Move back remaining commands ontop of the skipped ones and zero this area.
+ //
+ RemainingArea = Header->CommandsSize - ((UINTN) LoadCommand - (UINTN) &Header->Commands[0]);
+ CopyMem (
+ (UINT8 *) FirstSegment + FirstSegment->CommandSize,
+ LoadCommand,
+ RemainingArea
+ );
+ ZeroMem (LoadCommand, RemainingArea);
+
+ //
+ // Account for dropped commands in the header.
+ //
+ Header->NumCommands -= SkipCount;
+ Header->CommandsSize -= sizeof (MACH_SEGMENT_COMMAND_64) * SkipCount;
+
+ return TRUE;
+}
diff --git a/Platform/OpenCore/OpenCoreKernel.c b/Platform/OpenCore/OpenCoreKernel.c
index d8b89c49..d5552548 100644
--- a/Platform/OpenCore/OpenCoreKernel.c
+++ b/Platform/OpenCore/OpenCoreKernel.c
@@ -722,7 +722,7 @@ OcKernelFileOpen (
// On 10.9 mach_kernel is loaded for manual linking aferwards, so we cannot skip it.
//
if (OpenMode == EFI_FILE_MODE_READ
- && StrStr (FileName, L"kernel") != NULL
+ && OcStriStr (FileName, L"kernel") != NULL
&& StrCmp (FileName, L"System\\Library\\Kernels\\kernel") != 0) {
DEBUG ((DEBUG_INFO, "OC: Trying XNU hook on %s\n", FileName));