mirror of
https://github.com/acidanthera/OpenCorePkg.git
synced 2025-12-08 19:25:01 +00:00
407 lines
11 KiB
C
407 lines
11 KiB
C
/** @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/AppleKmodInfo.h>
|
|
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/OcAppleKernelLib.h>
|
|
#include <Library/OcMachoLib.h>
|
|
#include <Library/OcMiscLib.h>
|
|
#include <Library/OcXmlLib.h>
|
|
#include <Library/PrintLib.h>
|
|
|
|
#include "Link.h"
|
|
|
|
STATIC
|
|
EFI_STATUS
|
|
InternalInitPatcherContext (
|
|
IN OUT PATCHER_CONTEXT *Context,
|
|
IN OUT UINT8 *Buffer,
|
|
IN UINT32 BufferSize,
|
|
IN UINT64 VirtualBase OPTIONAL,
|
|
IN UINT64 VirtualKmod OPTIONAL
|
|
)
|
|
{
|
|
MACH_SEGMENT_COMMAND_64 *Segment;
|
|
|
|
if (!MachoInitializeContext (&Context->MachContext, Buffer, BufferSize)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (VirtualBase == 0) {
|
|
//
|
|
// Get the segment containing kernel segment.
|
|
//
|
|
Segment = MachoGetSegmentByName64 (
|
|
&Context->MachContext,
|
|
"__TEXT"
|
|
);
|
|
if (Segment == NULL || Segment->VirtualAddress < Segment->FileOffset) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
VirtualBase = Segment->VirtualAddress - Segment->FileOffset;
|
|
}
|
|
|
|
Context->VirtualBase = VirtualBase;
|
|
Context->VirtualKmod = VirtualKmod;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
PRELINKED_KEXT *
|
|
InternalGetPrelinkedKext (
|
|
IN OUT PRELINKED_CONTEXT *Prelinked,
|
|
IN CONST CHAR8 *Identifier
|
|
)
|
|
{
|
|
PRELINKED_KEXT *NewKext;
|
|
LIST_ENTRY *Kext;
|
|
EFI_STATUS Status;
|
|
UINT32 Index;
|
|
UINT32 KextCount;
|
|
UINT32 FieldIndex;
|
|
UINT32 FieldCount;
|
|
XML_NODE *KextPlist;
|
|
XML_NODE *KextPlistKey;
|
|
XML_NODE *KextPlistValue;
|
|
XML_NODE *BundleLibraries;
|
|
CONST CHAR8 *KextIdentifier;
|
|
CONST CHAR8 *CompatibleVersion;
|
|
UINT64 SourceBase;
|
|
UINT64 SourceSize;
|
|
UINT64 SourceEnd;
|
|
UINT64 VirtualBase;
|
|
UINT64 VirtualKmod;
|
|
BOOLEAN Found;
|
|
|
|
//
|
|
// Find cached entry if any.
|
|
//
|
|
Kext = GetFirstNode (&Prelinked->PrelinkedKexts);
|
|
while (!IsNull (&Prelinked->PrelinkedKexts, Kext)) {
|
|
if (AsciiStrCmp (Identifier, GET_PRELINKED_KEXT_FROM_LINK (Kext)->Identifier) == 0) {
|
|
return GET_PRELINKED_KEXT_FROM_LINK (Kext);
|
|
}
|
|
|
|
Kext = GetNextNode (&Prelinked->PrelinkedKexts, Kext);
|
|
}
|
|
|
|
//
|
|
// Try with real entry.
|
|
//
|
|
Found = FALSE;
|
|
KextCount = XmlNodeChildren (Prelinked->KextList);
|
|
|
|
for (Index = 0; Index < KextCount && !Found; ++Index) {
|
|
KextPlist = PlistNodeCast (XmlNodeChild (Prelinked->KextList, Index), PLIST_NODE_TYPE_DICT);
|
|
|
|
if (KextPlist == NULL) {
|
|
continue;
|
|
}
|
|
|
|
VirtualBase = 0;
|
|
VirtualKmod = 0;
|
|
SourceBase = 0;
|
|
SourceSize = 0;
|
|
BundleLibraries = NULL;
|
|
CompatibleVersion = NULL;
|
|
|
|
FieldCount = PlistDictChildren (KextPlist);
|
|
for (FieldIndex = 0; FieldIndex < FieldCount; ++FieldIndex) {
|
|
KextPlistKey = PlistDictChild (KextPlist, FieldIndex, &KextPlistValue);
|
|
if (KextPlistKey == NULL) {
|
|
continue;
|
|
}
|
|
|
|
if (!Found && AsciiStrCmp (PlistKeyValue (KextPlistKey), INFO_BUNDLE_IDENTIFIER_KEY) == 0) {
|
|
KextIdentifier = XmlNodeContent (KextPlistValue);
|
|
if (PlistNodeCast (KextPlistValue, PLIST_NODE_TYPE_STRING) == NULL || KextIdentifier == NULL) {
|
|
break;
|
|
}
|
|
if (AsciiStrCmp (KextIdentifier, Identifier) == 0) {
|
|
Found = TRUE;
|
|
continue;
|
|
}
|
|
} else if (VirtualBase == 0 && AsciiStrCmp (PlistKeyValue (KextPlistKey), PRELINK_INFO_EXECUTABLE_LOAD_ADDR_KEY) == 0) {
|
|
if (!PlistIntegerValue (KextPlistValue, &VirtualBase, sizeof (VirtualBase), TRUE)) {
|
|
break;
|
|
}
|
|
} else if (VirtualKmod == 0 && AsciiStrCmp (PlistKeyValue (KextPlistKey), PRELINK_INFO_KMOD_INFO_KEY) == 0) {
|
|
if (!PlistIntegerValue (KextPlistValue, &VirtualKmod, sizeof (VirtualKmod), TRUE)) {
|
|
break;
|
|
}
|
|
} else if (SourceBase == 0 && AsciiStrCmp (PlistKeyValue (KextPlistKey), PRELINK_INFO_EXECUTABLE_SOURCE_ADDR_KEY) == 0) {
|
|
if (!PlistIntegerValue (KextPlistValue, &SourceBase, sizeof (SourceBase), TRUE)) {
|
|
break;
|
|
}
|
|
} else if (SourceSize == 0 && AsciiStrCmp (PlistKeyValue (KextPlistKey), PRELINK_INFO_EXECUTABLE_SIZE_KEY) == 0) {
|
|
if (!PlistIntegerValue (KextPlistValue, &SourceSize, sizeof (SourceSize), TRUE)) {
|
|
break;
|
|
}
|
|
} else if (BundleLibraries == NULL && AsciiStrCmp (PlistKeyValue (KextPlistKey), INFO_BUNDLE_LIBRARIES_KEY) == 0) {
|
|
if (PlistNodeCast (KextPlistValue, PLIST_NODE_TYPE_DICT) == NULL) {
|
|
break;
|
|
}
|
|
BundleLibraries = KextPlistValue;
|
|
} else if (CompatibleVersion == NULL && AsciiStrCmp (PlistKeyValue (KextPlistKey), INFO_BUNDLE_COMPATIBLE_VERSION_KEY) == 0) {
|
|
if (PlistNodeCast (KextPlistValue, PLIST_NODE_TYPE_STRING) == NULL) {
|
|
break;
|
|
}
|
|
CompatibleVersion = XmlNodeContent (KextPlistValue);
|
|
if (CompatibleVersion == NULL) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Found
|
|
&& VirtualBase != 0
|
|
&& VirtualKmod != 0
|
|
&& SourceBase != 0
|
|
&& SourceSize != 0
|
|
&& BundleLibraries != NULL
|
|
&& CompatibleVersion != NULL) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// CompatibleVersion is optional and thus not checked.
|
|
//
|
|
if (!Found || VirtualBase == 0 || VirtualKmod == 0 || SourceBase == 0 || SourceSize == 0 || BundleLibraries == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (SourceBase < VirtualBase) {
|
|
return NULL;
|
|
}
|
|
|
|
SourceBase -= Prelinked->PrelinkedTextSegment->VirtualAddress;
|
|
if (OcOverflowAddU64 (SourceBase, Prelinked->PrelinkedTextSegment->FileOffset, &SourceBase) ||
|
|
OcOverflowAddU64 (SourceBase, SourceSize, &SourceEnd) ||
|
|
SourceEnd > Prelinked->PrelinkedSize) {
|
|
return NULL;
|
|
}
|
|
|
|
NewKext = AllocatePool (sizeof (*NewKext));
|
|
if (NewKext == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
Status = InternalInitPatcherContext (
|
|
&NewKext->Context,
|
|
&Prelinked->Prelinked[SourceBase],
|
|
(UINT32) SourceSize,
|
|
VirtualBase,
|
|
VirtualKmod
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
FreePool (NewKext);
|
|
return NULL;
|
|
}
|
|
|
|
NewKext->Signature = PRELINKED_KEXT_SIGNATURE;
|
|
NewKext->Identifier = KextIdentifier;
|
|
NewKext->BundleLibraries = BundleLibraries;
|
|
NewKext->CompatibleVersion = CompatibleVersion;
|
|
|
|
InsertTailList (&Prelinked->PrelinkedKexts, &NewKext->Link);
|
|
|
|
return NewKext;
|
|
}
|
|
|
|
VOID
|
|
InternalFreePrelinkedKexts (
|
|
LIST_ENTRY *Kexts
|
|
)
|
|
{
|
|
LIST_ENTRY *Link;
|
|
PRELINKED_KEXT *Kext;
|
|
|
|
while (!IsListEmpty (Kexts)) {
|
|
Link = GetFirstNode (Kexts);
|
|
Kext = GET_PRELINKED_KEXT_FROM_LINK (Link);
|
|
RemoveEntryList (Link);
|
|
FreePool (Kext);
|
|
}
|
|
|
|
ZeroMem (Kexts, sizeof (*Kexts));
|
|
}
|
|
|
|
EFI_STATUS
|
|
PatcherInitContextFromPrelinked (
|
|
IN OUT PATCHER_CONTEXT *Context,
|
|
IN OUT PRELINKED_CONTEXT *Prelinked,
|
|
IN CONST CHAR8 *Name
|
|
)
|
|
{
|
|
PRELINKED_KEXT *Kext;
|
|
|
|
Kext = InternalGetPrelinkedKext (Prelinked, Name);
|
|
if (Kext == NULL) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
CopyMem (Context, &Kext->Context, sizeof (*Context));
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
PatcherInitContextFromBuffer (
|
|
IN OUT PATCHER_CONTEXT *Context,
|
|
IN OUT UINT8 *Buffer,
|
|
IN UINT32 BufferSize
|
|
)
|
|
{
|
|
return InternalInitPatcherContext (Context, Buffer, BufferSize, 0, 0);
|
|
}
|
|
|
|
EFI_STATUS
|
|
PatcherGetSymbolAddress (
|
|
IN OUT PATCHER_CONTEXT *Context,
|
|
IN CONST CHAR8 *Name,
|
|
IN OUT UINT8 **Address
|
|
)
|
|
{
|
|
MACH_NLIST_64 *Symbol;
|
|
CONST CHAR8 *SymbolName;
|
|
UINT32 Offset;
|
|
UINT32 Index;
|
|
|
|
Index = 0;
|
|
while (TRUE) {
|
|
Symbol = MachoGetSymbolByIndex64 (&Context->MachContext, Index);
|
|
if (Symbol == NULL) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
SymbolName = MachoGetSymbolName64 (&Context->MachContext, Symbol);
|
|
|
|
if (SymbolName && AsciiStrCmp (Name, SymbolName) == 0) {
|
|
break;
|
|
}
|
|
|
|
Index++;
|
|
}
|
|
|
|
if (!MachoSymbolGetFileOffset64 (&Context->MachContext, Symbol, &Offset)) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
*Address = (UINT8 *) MachoGetMachHeader64 (&Context->MachContext) + Offset;
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
PatcherApplyGenericPatch (
|
|
IN OUT PATCHER_CONTEXT *Context,
|
|
IN PATCHER_GENERIC_PATCH *Patch
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT8 *Base;
|
|
UINT32 Size;
|
|
UINT32 ReplaceCount;
|
|
|
|
Base = (UINT8 *) MachoGetMachHeader64 (&Context->MachContext);
|
|
Size = MachoGetFileSize (&Context->MachContext);
|
|
if (Patch->Base != NULL) {
|
|
Status = PatcherGetSymbolAddress (Context, Patch->Base, &Base);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Size -= (UINT32) (Base - (UINT8 *) MachoGetMachHeader64 (&Context->MachContext));
|
|
}
|
|
|
|
if (Patch->Find == NULL) {
|
|
if (Size < Patch->Size) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
CopyMem (Base, Patch->Replace, Patch->Size);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
ReplaceCount = ApplyPatch (
|
|
Patch->Find,
|
|
Patch->Mask,
|
|
Patch->Size,
|
|
Patch->Replace,
|
|
Base,
|
|
Size,
|
|
Patch->Count,
|
|
Patch->Skip
|
|
);
|
|
|
|
if ((ReplaceCount > 0 && Patch->Count == 0)
|
|
|| (ReplaceCount == Patch->Count && Patch->Count > 0)) {
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
EFI_STATUS
|
|
PatcherBlockKext (
|
|
IN OUT PATCHER_CONTEXT *Context
|
|
)
|
|
{
|
|
UINT64 KmodOffset;
|
|
UINT64 TmpOffset;
|
|
KMOD_INFO_64_V1 *KmodInfo;
|
|
UINT8 *PatchAddr;
|
|
|
|
//
|
|
// Kernel has 0 kmod.
|
|
//
|
|
if (Context->VirtualKmod == 0 || Context->VirtualBase > Context->VirtualKmod) {
|
|
return EFI_UNSUPPORTED;
|
|
}
|
|
|
|
KmodOffset = Context->VirtualKmod - Context->VirtualBase;
|
|
KmodInfo = (KMOD_INFO_64_V1 *) ((UINT8 *) MachoGetMachHeader64 (&Context->MachContext) + KmodOffset);
|
|
if (OcOverflowAddU64 (KmodOffset, sizeof (KMOD_INFO_64_V1), &TmpOffset)
|
|
|| KmodOffset > MachoGetFileSize (&Context->MachContext)
|
|
|| !OC_ALIGNED (KmodInfo)
|
|
|| KmodInfo->StartAddr == 0
|
|
|| Context->VirtualBase > KmodInfo->StartAddr) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
TmpOffset = KmodInfo->StartAddr - Context->VirtualBase;
|
|
if (TmpOffset > MachoGetFileSize (&Context->MachContext) - 6) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
PatchAddr = (UINT8 *) MachoGetMachHeader64 (&Context->MachContext) + TmpOffset;
|
|
|
|
//
|
|
// mov eax, KMOD_RETURN_FAILURE
|
|
// ret
|
|
//
|
|
PatchAddr[0] = 0xB8;
|
|
PatchAddr[1] = KMOD_RETURN_FAILURE;
|
|
PatchAddr[2] = 0x00;
|
|
PatchAddr[3] = 0x00;
|
|
PatchAddr[4] = 0x00;
|
|
PatchAddr[5] = 0xC3;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|