mirror of
https://github.com/acidanthera/OpenCorePkg.git
synced 2025-12-08 19:25:01 +00:00
262 lines
6.3 KiB
C
262 lines
6.3 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 <Base.h>
|
|
|
|
#include <IndustryStandard/AppleKmodInfo.h>
|
|
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/OcAppleKernelLib.h>
|
|
#include <Library/OcMachoLib.h>
|
|
#include <Library/OcMiscLib.h>
|
|
#include <Library/OcXmlLib.h>
|
|
|
|
#include "PrelinkedInternal.h"
|
|
|
|
EFI_STATUS
|
|
PatcherInitContextFromPrelinked (
|
|
IN OUT PATCHER_CONTEXT *Context,
|
|
IN OUT PRELINKED_CONTEXT *Prelinked,
|
|
IN CONST CHAR8 *Name
|
|
)
|
|
{
|
|
PRELINKED_KEXT *Kext;
|
|
|
|
Kext = InternalCachedPrelinkedKext (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
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
OC_MACHO_CONTEXT InnerContext;
|
|
MACH_SEGMENT_COMMAND_64 *Segment;
|
|
|
|
ASSERT (Context != NULL);
|
|
ASSERT (Buffer != NULL);
|
|
ASSERT (BufferSize > 0);
|
|
|
|
//
|
|
// This interface is still used for the kernel due to the need to patch
|
|
// standalone kernel outside of prelinkedkernel in e.g. 10.9.
|
|
// Once 10.9 support is dropped one could call PatcherInitContextFromPrelinked
|
|
// and request PRELINK_KERNEL_IDENTIFIER.
|
|
//
|
|
|
|
if (!MachoInitializeContext (&Context->MachContext, Buffer, BufferSize, 0)) {
|
|
DEBUG ((DEBUG_INFO, "OCAK: Patcher init from buffer %p %u has unsupported mach-o\n", Buffer, BufferSize));
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
Segment = MachoGetSegmentByName64 (
|
|
&Context->MachContext,
|
|
"__TEXT"
|
|
);
|
|
if (Segment == NULL || Segment->VirtualAddress < Segment->FileOffset) {
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
Context->VirtualBase = Segment->VirtualAddress - Segment->FileOffset;
|
|
Context->VirtualKmod = 0;
|
|
|
|
Status = InternalConnectExternalSymtab (
|
|
&Context->MachContext,
|
|
&InnerContext,
|
|
Buffer,
|
|
BufferSize,
|
|
NULL
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
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, NULL)) {
|
|
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)) {
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"OCAK: %a base lookup failure %r\n",
|
|
Patch->Comment != NULL ? Patch->Comment : "Patch",
|
|
Status
|
|
));
|
|
return Status;
|
|
}
|
|
|
|
Size -= (UINT32)(Base - (UINT8 *)MachoGetMachHeader64 (&Context->MachContext));
|
|
}
|
|
|
|
if (Patch->Find == NULL) {
|
|
if (Size < Patch->Size) {
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"OCAK: %a is borked, not found\n",
|
|
Patch->Comment != NULL ? Patch->Comment : "Patch"
|
|
));
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
CopyMem (Base, Patch->Replace, Patch->Size);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
if (Patch->Limit > 0 && Patch->Limit < Size) {
|
|
Size = Patch->Limit;
|
|
}
|
|
|
|
ReplaceCount = ApplyPatch (
|
|
Patch->Find,
|
|
Patch->Mask,
|
|
Patch->Size,
|
|
Patch->Replace,
|
|
Patch->ReplaceMask,
|
|
Base,
|
|
Size,
|
|
Patch->Count,
|
|
Patch->Skip
|
|
);
|
|
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"OCAK: %a replace count - %u\n",
|
|
Patch->Comment != NULL ? Patch->Comment : "Patch",
|
|
ReplaceCount
|
|
));
|
|
|
|
if (ReplaceCount > 0 && Patch->Count > 0 && ReplaceCount != Patch->Count) {
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"OCAK: %a performed only %u replacements out of %u\n",
|
|
Patch->Comment != NULL ? Patch->Comment : "Patch",
|
|
ReplaceCount,
|
|
Patch->Count
|
|
));
|
|
}
|
|
|
|
if (ReplaceCount > 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)
|
|
|| KmodInfo->StartAddr == 0
|
|
|| Context->VirtualBase > KmodInfo->StartAddr) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
TmpOffset = KmodInfo->StartAddr - Context->VirtualBase;
|
|
if (TmpOffset > MachoGetFileSize (&Context->MachContext) - 6) {
|
|
return EFI_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
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;
|
|
}
|