Implement test kext reading from command line

Also remove legacy OcMachoPrelinkLib.
This commit is contained in:
vit9696 2019-03-23 16:15:30 +03:00
parent fc6fbed67c
commit f9d5c1c4fd
9 changed files with 14 additions and 4522 deletions

1
.gitignore vendored
View File

@ -11,5 +11,6 @@ DICT
fuzz-*.log
crash-*
oom-*
slow-unit-*
out.bin
prelinkedkernel.unpack

File diff suppressed because it is too large Load Diff

View File

@ -1,431 +0,0 @@
#ifndef OC_MACHO_PRELINK_INTERNAL_H_
#define OC_MACHO_PRELINK_INTERNAL_H_
#include <IndustryStandard/AppleMachoImage.h>
#include <Library/OcMachoLib.h>
#define KXLD_WEAK_TEST_SYMBOL "_gOSKextUnresolved"
#define OS_METACLASS_VTABLE_NAME "__ZTV11OSMetaClass"
#define X86_64_RIP_RELATIVE_LIMIT 0x80000000ULL
#define SYM_MAX_NAME_LEN 256U
#define VTABLE_ENTRY_SIZE_64 8U
#define VTABLE_HEADER_LEN_64 2U
#define VTABLE_HEADER_SIZE_64 (VTABLE_HEADER_LEN_64 * VTABLE_ENTRY_SIZE_64)
#define OS_BUNDLE_LIBRARIES_STR "OSBundleLibraries"
#define OS_BUNDLE_IDENTIFIER_STR "CFBundleIdentifier"
#define OS_BUNDLE_VERSION_STR "CFBundleVersion"
#define OS_BUNDLE_COMPATIBLE_VERSION_STR "OSBundleCompatibleVersion"
typedef struct {
UINT32 StringIndex; ///< index into the string table
UINT64 Value; ///< value of this symbol (or stab offset)
} OC_SYMBOL_64;
#define OC_SYMBOL_TABLE_64_SIGNATURE SIGNATURE_32 ('O', 'S', '6', '4')
/**
Gets the next element in a linked list of OC_SYMBOL_TABLE_64.
@param[in] This The current ListEntry.
**/
#define GET_OC_SYMBOL_TABLE_64_FROM_LINK(This) \
CR ( \
(This), \
OC_SYMBOL_TABLE_64, \
Link, \
OC_SYMBOL_TABLE_64_SIGNATURE \
)
#define STR_LEN(String) (ARRAY_SIZE (String) - 1)
#define STR_N_HELPER(String) (String), STR_LEN (String)
typedef struct {
///
/// These data are used to construct linked lists of dependency information
/// for each KEXT. It is declared hear for every dependency will
/// eventually be part of a list and to save separate allocations per KEXT.
///
UINT32 Signature;
LIST_ENTRY Link;
BOOLEAN IsIndirect;
///
/// The String Table associated with this symbol table.
///
CONST CHAR8 *StringTable;
///
/// The number of symbols in the entire symbols buffer.
///
UINT32 NumSymbols;
///
/// The number of C++ symbols at the end of the symbols buffer.
///
UINT32 NumCxxSymbols;
OC_SYMBOL_64 Symbols[]; ///< The symbol buffer.
} OC_SYMBOL_TABLE_64;
typedef struct {
CONST CHAR8 *Name; ///< The symbol's name.
UINT64 Address; ///< The symbol's address.
} OC_VTABLE_ENTRY;
#define GET_NEXT_OC_VTABLE(This) \
((OC_VTABLE *)(&(This)->Entries[(This)->NumEntries]))
typedef struct {
CONST CHAR8 *Name; ///< The VTable's name.
UINT32 NumEntries; ///< The number of VTable entries.
OC_VTABLE_ENTRY Entries[]; ///< The VTable entries.
} OC_VTABLE;
#define OC_VTABLE_ARRAY_SIGNATURE SIGNATURE_32 ('O', 'V', 'T', 'A')
/**
Gets the next element in a linked list of OC_VTABLE_ARRAY.
@param[in] This The current ListEntry.
**/
#define GET_OC_VTABLE_ARRAY_FROM_LINK(This) \
CR ( \
(This), \
OC_VTABLE_ARRAY, \
Link, \
OC_VTABLE_ARRAY_SIGNATURE \
)
#define GET_FIRST_OC_VTABLE(This) \
((OC_VTABLE *)((This) + 1))
typedef struct {
///
/// These data are used to construct linked lists of dependency information
/// for each KEXT. It is declared hear for every dependency will
/// eventually be part of a list and to save separate allocations per KEXT.
///
UINT32 Signature;
LIST_ENTRY Link;
///
/// The number of VTables in the array.
///
UINT32 NumVtables;
//
// NOTE: This is an array that cannot be declared as such as OC_VTABLE
// contains a flexible array itself. As the size is dynamic, do not
// try to use pointer arithmetics.
//
///
/// VTable array.
///
OC_VTABLE Vtables;
} OC_VTABLE_ARRAY;
typedef union {
struct {
UINT32 Major : 14;
UINT32 Minor : 7;
UINT32 Revision : 7;
UINT32 Stage : 4;
UINT32 StageLevel : 8;
} Bits;
UINT64 Value;
} OC_KEXT_VERSION;
typedef enum {
OcKextVersionStageDevelopment = 1,
OcKextVersionStageAlpha = 3,
OcKextVersionStageBeta = 5,
OcKextVersionStageCandidate = 7,
OcKextVersionStageRelease = 9
} OC_KEXT_VERSION_STAGE;
typedef struct {
OC_SYMBOL_TABLE_64 *SymbolTable;
OC_VTABLE_ARRAY *Vtables;
} OC_DEPENDENCY_DATA;
#define OC_DEPENDENCY_INFO_ENTRY_SIGNATURE \
SIGNATURE_32 ('O', 'D', 'I', 'E')
#define OC_DEP_INFO_FROM_LINK(This) \
(CR ( \
(This), \
OC_DEPENDENCY_INFO_ENTRY, \
Link, \
OC_DEPENDENCY_INFO_ENTRY_SIGNATURE \
))
typedef struct OC_DEPENDENCY_INFO_ENTRY_ OC_DEPENDENCY_INFO_ENTRY;
typedef struct {
MACH_HEADER_64 *MachHeader;
UINT32 MachoSize;
CONST CHAR8 *Name;
OC_KEXT_VERSION Version;
OC_KEXT_VERSION CompatibleVersion;
UINTN NumDependencies;
OC_DEPENDENCY_INFO_ENTRY *Dependencies[];
} OC_DEPENDENCY_INFO;
struct OC_DEPENDENCY_INFO_ENTRY_ {
UINT32 Signature;
LIST_ENTRY Link;
BOOLEAN Prelinked;
OC_DEPENDENCY_DATA Data;
OC_DEPENDENCY_INFO Info;
};
typedef struct {
struct {
MACH_HEADER_64 *MachHeader;
UINT32 MachoSize;
CONST CHAR8 *Plist;
} Input;
struct {
BOOLEAN Linked;
} Output;
//
// Private data.
//
struct {
OC_DEPENDENCY_INFO_ENTRY *Info;
OC_MACHO_CONTEXT MachoContext;
MACH_SEGMENT_COMMAND_64 *LinkEdit;
BOOLEAN IsDependedOn;
} Private;
} OC_KEXT_REQUEST;
typedef struct {
CONST MACH_NLIST_64 *Smcp;
CONST MACH_NLIST_64 *Vtable;
CONST MACH_NLIST_64 *MetaVtable;
} OC_VTABLE_PATCH_ENTRY;
typedef struct {
UINTN NumEntries;
OC_VTABLE_PATCH_ENTRY Entries[];
} OC_VTABLE_PATCH_ARRAY;
typedef struct {
UINT32 NumSymbols;
CONST MACH_NLIST_64 *Symbols[];
} OC_VTABLE_EXPORT_ARRAY;
//
// Dependencies
//
OC_DEPENDENCY_INFO_ENTRY *
InternalKextCollectInformation (
IN CONST CHAR8 *Plist,
IN OUT OC_MACHO_CONTEXT *MachoContext, OPTIONAL
IN UINT64 KextsVirtual, OPTIONAL
IN UINTN KextsPhysical, OPTIONAL
IN UINT64 RequestedVersion OPTIONAL
);
VOID
InternalFreeDependencyEntry (
IN OC_DEPENDENCY_INFO_ENTRY *Entry
);
BOOLEAN
InternalResolveDependencies (
IN LIST_ENTRY *Dependencies,
IN UINTN NumRequests, OPTIONAL
IN OUT OC_KEXT_REQUEST *Requests, OPTIONAL
IN CONST CHAR8 *PrelinkedPlist,
IN OC_DEPENDENCY_INFO_ENTRY *KextInfo,
IN UINT64 KextsVirtual,
IN UINTN KextsPhysical
);
LIST_ENTRY *
InternalRemoveDependency (
IN CONST LIST_ENTRY *Dependencies,
IN UINTN NumRequests,
IN OUT OC_KEXT_REQUEST *Requests,
IN OC_DEPENDENCY_INFO_ENTRY *DepInfo
);
VOID
InternalInvalidateKextRequest (
IN CONST LIST_ENTRY *Dependencies,
IN UINTN NumRequests,
IN OUT OC_KEXT_REQUEST *Requests,
IN OC_KEXT_REQUEST *Request
);
BOOLEAN
InternalConstructDependencyArrays (
IN UINTN NumDependencies,
IN OC_DEPENDENCY_INFO_ENTRY **Dependencies,
OUT OC_DEPENDENCY_DATA *DependencyData
);
VOID
InternalDestructDependencyArrays (
OUT CONST OC_DEPENDENCY_DATA *DependencyData
);
UINT64
InternalGetNewPrelinkedKextLoadAddress (
IN OUT OC_MACHO_CONTEXT *KernelContext,
IN CONST MACH_SEGMENT_COMMAND_64 *PrelinkedKextsSegment,
IN CONST CHAR8 *PrelinkedPlist
);
/**
Fills SymbolTable with the symbols provided in Symbols. For performance
reasons, the C++ symbols are continuously added to the top of the buffer.
Their order is not preserved. SymbolTable->SymbolTable is expected to be
a valid buffer that can store at least NumSymbols symbols.
@param[in] MachoContext Context of the Mach-O.
@param[in] NumSymbols The number of symbols to copy.
@param[in] Symbols The source symbol array.
@param[in,out] SymbolTable The desination Symbol List. Must be able to
hold at least NumSymbols symbols.
**/
VOID
InternalFillSymbolTable64 (
IN OUT OC_MACHO_CONTEXT *MachoContext,
IN UINT32 NumSymbols,
IN CONST MACH_NLIST_64 *Symbols,
IN OUT OC_SYMBOL_TABLE_64 *SymbolTable
);
//
// PLIST
//
CONST CHAR8 *
InternalPlistStrStrSameLevelUp (
IN CONST CHAR8 *AnchorString,
IN CONST CHAR8 *FindString,
IN UINTN FindStringLen
);
CONST CHAR8 *
InternalPlistStrStrSameLevelDown (
IN CONST CHAR8 *AnchorString,
IN CONST CHAR8 *FindString,
IN UINTN FindStringLen
);
CONST CHAR8 *
InternalPlistStrStrSameLevel (
IN CONST CHAR8 *AnchorString,
IN CONST CHAR8 *FindString,
IN UINTN FindStringLen,
IN UINTN DownwardsOffset
);
UINT64
InternalPlistGetIntValue (
IN CONST CHAR8 **Tag
);
//
// VTables
//
UINT32
InternalGetVtableSize64 (
IN CONST UINT64 *VtableData
);
BOOLEAN
InternalGetVtableSizeWithRelocs64 (
IN OUT OC_MACHO_CONTEXT *MachoContext,
IN CONST UINT64 *VtableData,
OUT UINT32 *VtableSize
);
BOOLEAN
InternalPrepareVtableCreationNonPrelinked64 (
IN OUT OC_MACHO_CONTEXT *MachoContext,
IN UINT32 NumSymbols,
IN CONST MACH_NLIST_64 *SymbolTable,
OUT OC_VTABLE_PATCH_ARRAY *PatchData
);
BOOLEAN
InternalCreateVtablesNonPrelinked64 (
IN OUT OC_MACHO_CONTEXT *MachoContext,
IN CONST OC_DEPENDENCY_DATA *DependencyData,
IN OC_VTABLE_PATCH_ARRAY *PatchData,
OUT OC_VTABLE_ARRAY *VtableArray
);
BOOLEAN
InternalPrepareCreateVtablesPrelinked64 (
IN OC_MACHO_CONTEXT *MachoContext,
OUT OC_VTABLE_EXPORT_ARRAY *VtableExport,
IN UINT32 VtableExportSize
);
BOOLEAN
InternalCreateVtablesPrelinked64 (
IN OC_MACHO_CONTEXT *MachoContext,
IN CONST OC_SYMBOL_TABLE_64 *DefinedSymbols,
IN OC_VTABLE_EXPORT_ARRAY *VtableExport,
OUT OC_VTABLE *VtableBuffer
);
//
// Prelink
//
// TODO: Move?
CONST OC_SYMBOL_64 *
InternalOcGetSymbolByName (
IN CONST OC_SYMBOL_TABLE_64 *DefinedSymbols,
IN CONST CHAR8 *Name,
IN BOOLEAN CheckIndirect
);
VOID
InternalSolveSymbolValue64 (
IN UINT64 Value,
OUT MACH_NLIST_64 *Symbol
);
/**
Prelinks the specified KEXT against the specified LinkAddress and the data
of its dependencies.
@param[in,out] MachoContext Mach-O context of the KEXT to prelink.
@param[in] LinkEditSegment __LINKEDIT segment of the KEXT to prelink.
@param[in] LinkAddress The address this KEXT shall be linked
against.
@param[in] DependencyData List of data of all dependencies.
@param[in] ExposeSymbols Whether the symbol table shall be exposed.
@param[out] OutputData Buffer to output data into.
@param[out] ScratchMemory Scratch memory buffer that is at least as big
as the KEXT's __LINKEDIT segment.
@retval Returned is whether the prelinking process has been successful.
The state of the KEXT is undefined in case this routine fails.
**/
BOOLEAN
InternalPrelinkKext64 (
IN OUT OC_MACHO_CONTEXT *MachoContext,
IN MACH_SEGMENT_COMMAND_64 *LinkEditSegment,
IN UINT64 LinkAddress,
IN OC_DEPENDENCY_DATA *DependencyData,
IN BOOLEAN ExposeSymbols,
IN OUT OC_DEPENDENCY_DATA *OutputData,
OUT VOID *ScratchMemory
);
#endif // OC_MACHO_PRELINK_INTERNAL_H_

View File

@ -1,476 +0,0 @@
/** @file
Library handling KEXT prelinking.
Currently limited to Intel 64 architectures.
Copyright (c) 2018, Download-Fritz. All rights reserved.<BR>
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/AppleMachoImage.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/OcGuardLib.h>
#include <Library/OcMachoLib.h>
#include "OcMachoPrelinkInternal.h"
STATIC
BOOLEAN
InternalPrelinkKextsWorker (
IN OC_MACHO_CONTEXT *KernelContext,
IN UINTN NumRequests,
IN OUT OC_KEXT_REQUEST *Requests,
IN LIST_ENTRY *Dependencies,
IN CONST MACH_SEGMENT_COMMAND_64 *PrelinkTextSegment,
IN CONST CHAR8 *PrelinkedPlist
)
{
UINTN Index;
OC_KEXT_REQUEST *Request;
LIST_ENTRY *DependencyEntry;
OC_DEPENDENCY_INFO_ENTRY *DependencyInfo;
OC_DEPENDENCY_INFO *Info;
OC_DEPENDENCY_DATA DependencyData;
UINT64 LoadAddress;
BOOLEAN Result;
BOOLEAN OneLinked;
BOOLEAN OneUnlinked;
MACH_SEGMENT_COMMAND_64 *LinkEdit;
UINTN ScratchSize;
VOID *ScratchMemory;
OC_DEPENDENCY_DATA *CurrentData;
OC_MACHO_CONTEXT MachoContext;
CONST MACH_HEADER_64 *MachHeader;
CONST MACH_NLIST_64 *SymbolTable;
UINT32 NumSymbols;
OC_SYMBOL_TABLE_64 *OcSymbolTable;
OC_VTABLE_ARRAY *Vtables;
OC_VTABLE_EXPORT_ARRAY *VtableExport;
UINT32 VtableOffset;
CONST UINT64 *VtableData;
UINTN VtablesSize;
ASSERT (KernelContext != NULL);
ASSERT (NumRequests > 0);
ASSERT (Requests != NULL);
ASSERT (Dependencies != NULL);
ASSERT (PrelinkTextSegment != NULL);
ASSERT (PrelinkedPlist != NULL);
ScratchSize = 0;
for (Index = 0; Index < NumRequests; ++Index) {
Request = &Requests[Index];
if (Request->Private.Info == NULL) {
continue;
}
//
// Retrieve the __LINKEDIT segment.
//
Result = MachoGetSegmentByName64 (
&Request->Private.MachoContext,
"__LINKEDIT",
&LinkEdit
);
if (!Result || (LinkEdit == NULL)) {
InternalInvalidateKextRequest (
Dependencies,
NumRequests,
Requests,
Request
);
continue;
}
Request->Private.LinkEdit = LinkEdit;
if (ScratchSize < LinkEdit->FileSize) {
ScratchSize = LinkEdit->FileSize;
}
}
if (ScratchSize == 0) {
return FALSE;
}
ScratchMemory = AllocatePool (ScratchSize);
if (ScratchMemory == NULL) {
return FALSE;
}
VtableExport = (OC_VTABLE_EXPORT_ARRAY *)ScratchMemory;
for (
DependencyEntry = GetFirstNode (Dependencies);
!IsNull (DependencyEntry, Dependencies);
DependencyEntry = GetNextNode (Dependencies, DependencyEntry),
InternalDestructDependencyArrays (&DependencyData)
) {
DependencyInfo = OC_DEP_INFO_FROM_LINK (DependencyEntry);
CurrentData = &DependencyInfo->Data;
//
// Try to construct the dependency tree first.
//
Info = &DependencyInfo->Info;
Result = InternalConstructDependencyArrays (
Info->NumDependencies,
Info->Dependencies,
&DependencyData
);
if (!Result) {
//
// Prelinked dependencies can only depend on other already prelinked
// KEXTs.
//
DependencyEntry = InternalRemoveDependency (
Dependencies,
NumRequests,
Requests,
DependencyInfo
);
continue;
}
//
// Create a Mach-O Context for this KEXT.
//
Result = MachoInitializeContext (
&MachoContext,
Info->MachHeader,
Info->MachoSize
);
if (!Result) {
DependencyEntry = InternalRemoveDependency (
Dependencies,
NumRequests,
Requests,
DependencyInfo
);
continue;
}
NumSymbols = MachoGetSymbolTable (
&MachoContext,
&SymbolTable,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
);
if (NumSymbols == 0) {
DependencyEntry = InternalRemoveDependency (
Dependencies,
NumRequests,
Requests,
DependencyInfo
);
continue;
}
OcSymbolTable = AllocatePool (
sizeof (*OcSymbolTable)
+ (NumSymbols * sizeof (*OcSymbolTable->Symbols))
);
if (OcSymbolTable == NULL) {
FreePool (ScratchMemory);
return FALSE;
}
InternalFillSymbolTable64 (
&MachoContext,
NumSymbols,
SymbolTable,
OcSymbolTable
);
Result = InternalPrepareCreateVtablesPrelinked64 (
&MachoContext,
VtableExport,
ScratchSize
);
if (!Result) {
DependencyEntry = InternalRemoveDependency (
Dependencies,
NumRequests,
Requests,
DependencyInfo
);
FreePool (OcSymbolTable);
continue;
}
MachHeader = MachoGetMachHeader64 (&MachoContext);
ASSERT (MachHeader != NULL);
Result = TRUE;
VtablesSize = 0;
for (Index = 0; Index < VtableExport->NumSymbols; ++Index) {
Result = MachoSymbolGetFileOffset64 (
&MachoContext,
VtableExport->Symbols[Index],
&VtableOffset
);
if (!Result) {
break;
}
VtableData = (UINT64 *)((UINTN)MachHeader + VtableOffset);
if (!OC_ALIGNED (VtableData)) {
Result = FALSE;
break;
}
VtablesSize += InternalGetVtableSize64 (VtableData);
}
if (!Result) {
DependencyEntry = InternalRemoveDependency (
Dependencies,
NumRequests,
Requests,
DependencyInfo
);
FreePool (OcSymbolTable);
continue;
}
Vtables = AllocatePool (sizeof (*Vtables) + VtablesSize);
if (Vtables == NULL) {
FreePool (OcSymbolTable);
FreePool (ScratchMemory);
return FALSE;
}
Result = InternalCreateVtablesPrelinked64 (
&MachoContext,
DependencyData.SymbolTable,
VtableExport,
GET_FIRST_OC_VTABLE (Vtables)
);
if (!Result) {
DependencyEntry = InternalRemoveDependency (
Dependencies,
NumRequests,
Requests,
DependencyInfo
);
FreePool (Vtables);
FreePool (OcSymbolTable);
continue;
}
CurrentData->SymbolTable = OcSymbolTable;
CurrentData->Vtables = Vtables;
}
do {
OneLinked = FALSE;
OneUnlinked = FALSE;
for (Index = 0; Index < NumRequests; ++Index) {
Request = &Requests[Index];
DependencyInfo = Request->Private.Info;
if ((DependencyInfo == NULL) || DependencyInfo->Prelinked) {
continue;
}
Info = &DependencyInfo->Info;
Result = InternalConstructDependencyArrays (
Info->NumDependencies,
Info->Dependencies,
&DependencyData
);
if (!Result) {
//
// The KEXT depends on another KEXT staged for prelinking which has not
// been processed yet. Postpone to another iteration.
//
OneUnlinked = TRUE;
continue;
}
//
// Retrieve a loading address for the KEXT.
//
LoadAddress = InternalGetNewPrelinkedKextLoadAddress (
KernelContext,
PrelinkTextSegment,
PrelinkedPlist
);
if (LoadAddress == 0) {
FreePool (ScratchMemory);
return FALSE;
}
//
// Prelink the KEXT.
//
Result = InternalPrelinkKext64 (
&Request->Private.MachoContext,
Request->Private.LinkEdit,
LoadAddress,
&DependencyData,
Request->Private.IsDependedOn,
&DependencyInfo->Data,
ScratchMemory
);
if (Result) {
DependencyInfo->Prelinked = TRUE;
Request->Output.Linked = TRUE;
OneLinked = TRUE;
} else {
InternalInvalidateKextRequest (
Dependencies,
NumRequests,
Requests,
Request
);
}
}
//
// Continue while the loop makes progress and there are still KEXTs to
// link.
//
} while (OneLinked && OneUnlinked);
FreePool (ScratchMemory);
return TRUE;
}
BOOLEAN
OcPrelinkKexts64 (
IN OC_MACHO_CONTEXT *KernelContext,
IN UINTN NumRequests,
IN OUT OC_KEXT_REQUEST *Requests
)
{
CONST MACH_HEADER_64 *KernelHeader;
UINTN Index;
OC_KEXT_REQUEST *Request;
OC_DEPENDENCY_INFO_ENTRY *DependencyInfo;
BOOLEAN Result;
MACH_SEGMENT_COMMAND_64 *PrelinkTextSegment;
CONST CHAR8 *PrelinkedPlist;
LIST_ENTRY Dependencies;
LIST_ENTRY *DependencyEntry;
UINT64 KextAddress;
ASSERT (KernelContext != NULL);
ASSERT (NumRequests > 0);
ASSERT (Requests != NULL);
KernelHeader = MachoGetMachHeader64 (KernelContext);
ASSERT (KernelHeader != NULL);
//
// Collect the KEXT information for all KEXTs to be prelinked. Do not solve
// dependencies yet so we can verify successful dependency against other
// KEXTs to be prelinked in one go.
//
for (Index = 0; Index < NumRequests; ++Index) {
Request = &Requests[Index];
//
// Create a Mach-O Context for this KEXT.
//
Result = MachoInitializeContext (
&Request->Private.MachoContext,
Request->Input.MachHeader,
Request->Input.MachoSize
);
if (Result) {
Request->Output.Linked = FALSE;
Request->Private.IsDependedOn = FALSE;
Request->Private.Info = InternalKextCollectInformation (
Request->Input.Plist,
&Request->Private.MachoContext,
0,
0,
0
);
} else {
Request->Private.Info = NULL;
}
}
PrelinkedPlist = (CHAR8 *)((UINTN)KernelHeader + PrelinkInfoSection->Offset);
//
// Resolve dependencies.
//
InitializeListHead (&Dependencies);
for (Index = 0; Index < NumRequests; ++Index) {
Request = &Requests[Index];
if (Request->Private.Info != NULL) {
Result = FALSE;
KextAddress = (UINTN)Request->Input.MachHeader;
KextAddress += PrelinkTextSegment->FileOffset;
if (KextAddress == (UINTN)KextAddress) {
Result = InternalResolveDependencies (
&Dependencies,
NumRequests,
Requests,
PrelinkedPlist,
Request->Private.Info,
PrelinkTextSegment->VirtualAddress,
(UINTN)KextAddress
);
}
if (!Result) {
//
// Only pass the KEXT Requests that already had their dependencies
// resolved.
//
InternalRemoveDependency (
&Dependencies,
Index,
Requests,
Request->Private.Info
);
}
}
}
//
// Link the KEXTs.
//
Result = InternalPrelinkKextsWorker (
KernelContext,
NumRequests,
Requests,
&Dependencies,
PrelinkTextSegment,
PrelinkedPlist
);
//
// Free all resources.
//
for (Index = 0; Index < NumRequests; ++Index) {
Request = &Requests[Index];
if (Request->Private.Info != NULL) {
InternalFreeDependencyEntry (Request->Private.Info);
}
}
DependencyEntry = GetFirstNode (&Dependencies);
while (!IsNull (DependencyEntry, &Dependencies)) {
DependencyInfo = OC_DEP_INFO_FROM_LINK (DependencyEntry);
DependencyEntry = GetNextNode (&Dependencies, DependencyEntry);
InternalFreeDependencyEntry (DependencyInfo);
}
return Result;
}

View File

@ -1,181 +0,0 @@
/** @file
PLIST helper functions.
Copyright (c) 2018, Download-Fritz. All rights reserved.<BR>
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 <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/OcStringLib.h>
#include "OcMachoPrelinkInternal.h"
CONST CHAR8 *
InternalPlistStrStrSameLevelUp (
IN CONST CHAR8 *AnchorString,
IN CONST CHAR8 *FindString,
IN UINTN FindStringLen
)
{
CONST CHAR8 *Walker;
UINTN DictLevel;
UINTN ShortestMatch;
ASSERT (AnchorString != NULL);
ASSERT (FindString != NULL);
ASSERT (AsciiStrLen (FindString) == FindStringLen);
ShortestMatch = MIN (FindStringLen, L_STR_LEN ("<dict>"));
for (
Walker = (AnchorString - ShortestMatch);
AsciiStrnCmp (Walker, FindString, FindStringLen) != 0;
--Walker
) {
for (DictLevel = 1; TRUE; --Walker) {
if (AsciiStrnCmp (Walker, STR_N_HELPER ("</dict>")) == 0) {
++DictLevel;
Walker -= (ShortestMatch - 1);
continue;
}
if (AsciiStrnCmp (Walker, STR_N_HELPER ("<dict>")) == 0) {
--DictLevel;
if (DictLevel == 0) {
return NULL;
}
}
if (DictLevel == 1) {
break;
}
}
}
return Walker;
}
CONST CHAR8 *
InternalPlistStrStrSameLevelDown (
IN CONST CHAR8 *AnchorString,
IN CONST CHAR8 *FindString,
IN UINTN FindStringLen
)
{
CONST CHAR8 *Walker;
UINTN DictLevel;
ASSERT (AnchorString != NULL);
ASSERT (FindString != NULL);
ASSERT (AsciiStrLen (FindString) == FindStringLen);
for (
Walker = AnchorString;
AsciiStrnCmp (Walker, FindString, FindStringLen) != 0;
++Walker
) {
for (DictLevel = 1; TRUE; ++Walker) {
if (AsciiStrnCmp (Walker, STR_N_HELPER ("<dict>")) == 0) {
++DictLevel;
Walker += (L_STR_LEN ("<dict>") - 1);
continue;
}
if (AsciiStrnCmp (Walker, STR_N_HELPER ("</dict>")) == 0) {
--DictLevel;
if (DictLevel == 0) {
return NULL;
}
}
if (DictLevel == 1) {
break;
}
}
}
return Walker;
}
CONST CHAR8 *
InternalPlistStrStrSameLevel (
IN CONST CHAR8 *AnchorString,
IN CONST CHAR8 *FindString,
IN UINTN FindStringLen,
IN UINTN DownwardsOffset
)
{
CONST CHAR8 *Result;
Result = InternalPlistStrStrSameLevelUp (
AnchorString,
FindString,
FindStringLen
);
if (Result == NULL) {
Result = InternalPlistStrStrSameLevelDown (
(AnchorString + DownwardsOffset),
FindString,
FindStringLen
);
}
return Result;
}
UINT64
InternalPlistGetIntValue (
IN CONST CHAR8 **Tag
)
{
UINT64 Value;
CONST CHAR8 *Walker;
CHAR8 *Walker2;
ASSERT (Tag != NULL);
ASSERT (*Tag != NULL);
ASSERT (AsciiStrnCmp (*Tag, STR_N_HELPER ("<integer")) == 0);
Walker = (*Tag + L_STR_LEN ("<integer"));
//
// Skip "size" and any other attributes.
//
Walker = AsciiStrStr (Walker, ">");
ASSERT (Walker != NULL);
++Walker;
//
// Temporarily terminate after the integer definition.
//
Walker2 = AsciiStrStr (Walker, "<");
ASSERT (Walker2 != NULL);
*Walker2 = '\0';
//
// Assert that the prelinked PLIST always uses hexadecimal integers.
//
while ((*Walker == ' ') || (*Walker == '\t')) {
++Walker;
}
ASSERT ((Walker[1] == 'x') || (Walker[1] == 'X'));
Value = AsciiStrHexToUint64 (Walker);
//
// Restore the previously replaced opening brace.
//
*Walker2 = '<';
//
// Return the first character after the integer tag.
//
*Tag = (Walker2 + L_STR_LEN ("</integer>"));
return Value;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,859 +0,0 @@
/** @file
Library handling KEXT prelinking.
Currently limited to Intel 64 architectures.
Copyright (c) 2018, Download-Fritz. All rights reserved.<BR>
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/AppleMachoImage.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/OcGuardLib.h>
#include <Library/OcMachoLib.h>
#include "OcMachoPrelinkInternal.h"
STATIC
CONST OC_VTABLE *
InternalGetOcVtableByName (
IN CONST OC_VTABLE_ARRAY *Vtables,
IN CONST CHAR8 *Name
)
{
CONST OC_VTABLE_ARRAY *VtableWalker;
CONST OC_VTABLE *Vtable;
UINT32 Index;
INTN Result;
VtableWalker = Vtables;
do {
Vtable = GET_FIRST_OC_VTABLE (VtableWalker);
for (Index = 0; Index < VtableWalker->NumVtables; ++Index) {
Result = AsciiStrCmp (Vtable->Name, Name);
if (Result == 0) {
return Vtable;
}
Vtable = GET_NEXT_OC_VTABLE (Vtable);
}
VtableWalker = GET_OC_VTABLE_ARRAY_FROM_LINK (
GetNextNode (&Vtables->Link, &VtableWalker->Link)
);
} while (!IsNull (&Vtables->Link, &VtableWalker->Link));
return NULL;
}
STATIC
BOOLEAN
InternalConstructVtablePrelinked64 (
IN OUT OC_MACHO_CONTEXT *MachoContext,
IN CONST OC_SYMBOL_TABLE_64 *DefinedSymbols,
IN CONST MACH_NLIST_64 *VtableSymbol,
OUT OC_VTABLE *Vtable
)
{
BOOLEAN Result;
CONST MACH_HEADER_64 *MachHeader;
UINT32 VtableOffset;
CONST UINT64 *VtableData;
UINT64 Value;
CONST OC_SYMBOL_TABLE_64 *SymbolsWalker;
CONST CHAR8 *StringTable;
UINT32 Index;
UINT32 Index2;
UINT32 CxxIndex;
CONST OC_SYMBOL_64 *Symbol;
ASSERT (MachoContext != NULL);
ASSERT (DefinedSymbols != NULL);
ASSERT (VtableSymbol != NULL);
ASSERT (Vtable != NULL);
MachHeader = MachoGetMachHeader64 (MachoContext);
ASSERT (MachHeader != NULL);
Result = MachoSymbolGetFileOffset64 (
MachoContext,
VtableSymbol,
&VtableOffset
);
if (!Result) {
return FALSE;
}
VtableData = (UINT64 *)((UINTN)MachHeader + VtableOffset);
if (!OC_ALIGNED (VtableData)) {
return FALSE;
}
Vtable->Name = MachoGetSymbolName64 (MachoContext, VtableSymbol);
//
// Initialize the VTable by entries.
//
SymbolsWalker = DefinedSymbols;
do {
StringTable = SymbolsWalker->StringTable;
CxxIndex = (
SymbolsWalker->NumSymbols
- SymbolsWalker->NumCxxSymbols
);
//
// Assumption: Not ARM (ARM requires an alignment to the function pointer
// retrieved from VtableData.
//
for (
Index = VTABLE_HEADER_LEN_64;
(Value = VtableData[Index]) != 0;
++Index
) {
for (
Index2 = CxxIndex;
Index2 < SymbolsWalker->NumSymbols;
++Index2
) {
Symbol = &SymbolsWalker->Symbols[Index2];
if (Symbol->Value == Value) {
Vtable->Entries[Index].Address = Value;
Vtable->Entries[Index].Name = (StringTable + Symbol->StringIndex);
break;
}
}
//
// If we can't find the symbol, it means that the virtual function was
// defined inline. There's not much I can do about this; it just means
// I can't patch this function.
//
if (Index == SymbolsWalker->NumCxxSymbols) {
Vtable->Entries[Index].Address = 0;
Vtable->Entries[Index].Name = NULL;
}
}
SymbolsWalker = GET_OC_SYMBOL_TABLE_64_FROM_LINK (
GetNextNode (
&DefinedSymbols->Link,
&SymbolsWalker->Link
)
);
} while (!IsNull (&DefinedSymbols->Link, &SymbolsWalker->Link));
return TRUE;
}
UINT32
InternalGetVtableSize64 (
IN CONST UINT64 *VtableData
)
{
UINT32 Index;
ASSERT (VtableData != NULL);
//
// Assumption: Not ARM (ARM requires an alignment to the function pointer
// retrieved from VtableData.
//
for (Index = VTABLE_HEADER_LEN_64; VtableData[Index] != 0; ++Index) {
;
}
return (Index * VTABLE_ENTRY_SIZE_64);
}
BOOLEAN
InternalGetVtableSizeWithRelocs64 (
IN OUT OC_MACHO_CONTEXT *MachoContext,
IN CONST UINT64 *VtableData,
OUT UINT32 *VtableSize
)
{
UINT32 Size;
MACH_NLIST_64 *Symbol;
Size = InternalGetVtableSize64 (VtableData);
Symbol = MachoGetSymbolByExternRelocationOffset64 (
MachoContext,
((UINTN)VtableData + Size)
);
if (Symbol == NULL) {
return FALSE;
}
*VtableSize = Size;
return TRUE;
}
BOOLEAN
InternalPrepareCreateVtablesPrelinked64 (
IN OC_MACHO_CONTEXT *MachoContext,
OUT OC_VTABLE_EXPORT_ARRAY *VtableExport,
IN UINT32 VtableExportSize
)
{
UINT32 NumVtables;
CONST MACH_NLIST_64 *SymbolTable;
CONST MACH_NLIST_64 *Symbol;
CONST CHAR8 *Name;
UINT32 NumSymbols;
UINT32 Index;
BOOLEAN Result;
ASSERT (MachoContext != NULL);
NumVtables = 0;
NumSymbols = MachoGetSymbolTable (
MachoContext,
&SymbolTable,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
);
for (Index = 0; Index < NumSymbols; ++Index) {
Symbol = &SymbolTable[Index];
Name = MachoGetSymbolName64 (MachoContext, Symbol);
if (MachoSymbolNameIsVtable64 (Name)) {
Result = MachoIsSymbolValueInRange64 (
MachoContext,
VtableExport->Symbols[Index]
);
if (!Result) {
return FALSE;
}
++NumVtables;
if (VtableExportSize < (NumVtables * sizeof (*VtableExport))) {
ASSERT (FALSE);
return FALSE;
}
VtableExport->Symbols[NumVtables] = Symbol;
}
}
VtableExport->NumSymbols = NumVtables;
return TRUE;
}
BOOLEAN
InternalCreateVtablesPrelinked64 (
IN OC_MACHO_CONTEXT *MachoContext,
IN CONST OC_SYMBOL_TABLE_64 *DefinedSymbols,
IN OC_VTABLE_EXPORT_ARRAY *VtableExport,
OUT OC_VTABLE *VtableBuffer
)
{
CONST MACH_NLIST_64 *Symbol;
UINT32 Index;
BOOLEAN Result;
ASSERT (MachoContext != NULL);
ASSERT (DefinedSymbols != NULL);
for (Index = 0; Index < VtableExport->NumSymbols; ++Index) {
Symbol = VtableExport->Symbols[Index];
Result = InternalConstructVtablePrelinked64 (
MachoContext,
DefinedSymbols,
Symbol,
VtableBuffer
);
if (!Result) {
return FALSE;
}
VtableBuffer = GET_NEXT_OC_VTABLE (VtableBuffer);
}
return TRUE;
}
/**
* kxld_vtable_patch
*/
STATIC
BOOLEAN
InternalPatchVtableSymbol (
IN OUT OC_MACHO_CONTEXT *MachoContext,
IN CONST OC_VTABLE_ENTRY *ParentEntry,
IN CONST CHAR8 *VtableName,
OUT MACH_NLIST_64 *Symbol
)
{
CONST CHAR8 *Name;
INTN Result;
BOOLEAN Success;
CONST CHAR8 *ClassName;
CHAR8 FunctionPrefix[SYM_MAX_NAME_LEN];
ASSERT (Symbol != NULL);
ASSERT (ParentEntry != NULL);
//
// The child entry can be NULL when a locally-defined, non-external
// symbol is stripped. We wouldn't patch this entry anyway, so we
// just skip it.
//
if (Symbol == NULL) {
return TRUE;
}
//
// It's possible for the patched parent entry not to have a symbol
// (e.g. when the definition is inlined). We can't patch this entry no
// matter what, so we'll just skip it and die later if it's a problem
// (which is not likely).
//
if (ParentEntry->Name == NULL) {
return MachoIsSymbolValueInRange64 (MachoContext, Symbol);
}
//
// 1) If the symbol is defined locally, do not patch
//
if (MachoSymbolIsLocalDefined (MachoContext, Symbol)) {
return MachoIsSymbolValueInRange64 (MachoContext, Symbol);
}
Name = MachoGetSymbolName64 (MachoContext, Symbol);
//
// 2) If the child is a pure virtual function, do not patch.
// In general, we want to proceed with patching when the symbol is
// externally defined because pad slots fall into this category.
// The pure virtual function symbol is special case, as the pure
// virtual property itself overrides the parent's implementation.
//
if (MachoSymbolNameIsPureVirtual (Name)) {
return MachoIsSymbolValueInRange64 (MachoContext, Symbol);
}
//
// 3) If the symbols are the same, do not patch
//
Result = AsciiStrCmp (Name, ParentEntry->Name);
if (Result == 0) {
return MachoIsSymbolValueInRange64 (MachoContext, Symbol);
}
//
// 4) If the parent vtable entry is a pad slot, and the child does not
// match it, then the child was built against a newer version of the
// libraries, so it is binary-incompatible.
//
if (MachoSymbolNameIsPadslot (ParentEntry->Name)) {
return FALSE;
}
//
// 5) If we are doing strict patching, we prevent kexts from declaring
// virtual functions and not implementing them. We can tell if a
// virtual function is declared but not implemented because we resolve
// symbols before patching; an unimplemented function will still be
// undefined at this point. We then look at whether the symbol has
// the same class prefix as the vtable. If it does, the symbol was
// declared as part of the class and not inherited, which means we
// should not patch it.
//
if (!MachoSymbolIsDefined (MachoContext, Symbol)) {
ClassName = MachoGetClassNameFromVtableName (VtableName);
Success = MachoGetFunctionPrefixFromClassName (
ClassName,
sizeof (FunctionPrefix),
FunctionPrefix
);
if (!Success) {
return FALSE;
}
Result = AsciiStrCmp (Name, FunctionPrefix);
if (Result == 0) {
//
// The VTable's class declares a method without providing an
// implementation.
//
ASSERT (FALSE);
return FALSE;
}
}
//
// 6) The child symbol is unresolved and different from its parent, so
// we need to patch it up. We do this by modifying the relocation
// entry of the vtable entry to point to the symbol of the parent
// vtable entry. If that symbol does not exist (i.e. we got the data
// from a link state object's vtable representation), then we create a
// new symbol in the symbol table and point the relocation entry to
// that.
//
// NOTE: The original logic has been altered significantly. Instead of
// declaring a symbol as "replaced" and either changing the
// associated relocation's index to the parent's or adding a new symbol
// based on a match, the symbol is actually overwritten. This looks
// fine for the rest of the control flow. The symbol name is not
// changed for the symbol value is already resolved and nothing but a
// VTable Relocation should reference it.
//
InternalSolveSymbolValue64 (ParentEntry->Address, Symbol);
//
// The C++ ABI requires that functions be aligned on a 2-byte boundary:
// http://www.codesourcery.com/public/cxx-abi/abi.html#member-pointers
// If the LSB of any virtual function's link address is 1, then the
// compiler has violated that part of the ABI, and we're going to panic
// in _ptmf2ptf() (in OSMetaClass.h). Better to panic here with some
// context.
//
Name = ParentEntry->Name;
ASSERT (MachoSymbolNameIsPureVirtual (Name) || ((Symbol->Value & 1U) == 0));
return TRUE;
}
STATIC
BOOLEAN
InternalInitializeVtableByEntriesAndRelocations64 (
IN OUT OC_MACHO_CONTEXT *MachoContext,
IN CONST OC_SYMBOL_TABLE_64 *DefinedSymbols,
IN CONST OC_VTABLE *SuperVtable,
IN CONST MACH_NLIST_64 *VtableSymbol,
IN CONST UINT64 *VtableData,
OUT OC_VTABLE *Vtable
)
{
UINT32 NumEntries;
UINT32 Index;
UINT32 CxxIndex;
UINT32 EntryOffset;
UINT64 EntryValue;
CONST OC_SYMBOL_TABLE_64 *SymbolsWalker;
CONST OC_SYMBOL_64 *OcSymbol;
CONST CHAR8 *StringTable;
MACH_NLIST_64 *Symbol;
BOOLEAN Result;
CONST CHAR8 *Name;
//
// Assumption: Not ARM (ARM requires an alignment to the function pointer
// retrieved from VtableData.
//
for (
NumEntries = 0, EntryOffset = VTABLE_HEADER_LEN_64;
TRUE;
++NumEntries, ++EntryOffset
) {
EntryValue = VtableData[EntryOffset];
//
// If we can't find a symbol, it means it is a locally-defined,
// non-external symbol that has been stripped. We don't patch over
// locally-defined symbols, so we leave the symbol as NULL and just
// skip it. We won't be able to patch subclasses with this symbol,
// but there isn't much we can do about that.
//
if (EntryValue != 0) {
SymbolsWalker = DefinedSymbols;
CxxIndex = (
SymbolsWalker->NumSymbols
- SymbolsWalker->NumCxxSymbols
);
do {
StringTable = SymbolsWalker->StringTable;
//
// Imported symbols are implicitely locally defined and hence do not
// need to be patched.
//
for (
Index = CxxIndex;
Index < SymbolsWalker->NumSymbols;
++Index
) {
OcSymbol = &SymbolsWalker->Symbols[Index];
if (OcSymbol->Value == EntryValue) {
Name = (StringTable + OcSymbol->StringIndex);
Vtable->Entries[NumEntries].Name = Name;
Vtable->Entries[NumEntries].Address = OcSymbol->Value;
break;
}
}
if (Index != SymbolsWalker->NumSymbols) {
break;
}
SymbolsWalker = GET_OC_SYMBOL_TABLE_64_FROM_LINK (
GetNextNode (
&DefinedSymbols->Link,
&SymbolsWalker->Link
)
);
if (IsNull (&DefinedSymbols->Link, &SymbolsWalker->Link)) {
ASSERT (FALSE);
return FALSE;
}
} while (TRUE);
} else if (NumEntries < SuperVtable->NumEntries) {
Symbol = MachoGetSymbolByExternRelocationOffset64 (
MachoContext,
(VtableSymbol->Value + EntryOffset)
);
if (Symbol == NULL) {
//
// When the VTable entry is 0 and it is not referenced by a Relocation,
// it is the end of the table.
//
break;
}
Result = InternalPatchVtableSymbol (
MachoContext,
&SuperVtable->Entries[NumEntries],
MachoGetSymbolName64 (MachoContext, VtableSymbol),
Symbol
);
if (!Result) {
return FALSE;
}
Name = MachoGetSymbolName64 (MachoContext, Symbol);
Vtable->Entries[NumEntries].Name = Name;
Vtable->Entries[NumEntries].Address = Symbol->Value;
}
}
Vtable->NumEntries = NumEntries;
return TRUE;
}
BOOLEAN
InternalPrepareVtableCreationNonPrelinked64 (
IN OUT OC_MACHO_CONTEXT *MachoContext,
IN UINT32 NumSymbols,
IN CONST MACH_NLIST_64 *SymbolTable,
OUT OC_VTABLE_PATCH_ARRAY *PatchData
)
{
CONST MACH_HEADER_64 *MachHeader;
UINT32 Index;
UINT32 NumTables;
BOOLEAN Result;
CONST MACH_NLIST_64 *Smcp;
CONST CHAR8 *Name;
CONST MACH_NLIST_64 *VtableSymbol;
CONST MACH_NLIST_64 *MetaVtableSymbol;
MachHeader = MachoGetMachHeader64 (MachoContext);
ASSERT (MachHeader != NULL);
NumTables = 0;
for (Index = 0; Index < NumSymbols; ++Index) {
Smcp = &SymbolTable[Index];
Name = MachoGetSymbolName64 (MachoContext, Smcp);
if (MachoSymbolNameIsSmcp64 (MachoContext, Name)) {
//
// We walk over the super metaclass pointer symbols because classes
// with them are the only ones that need patching. Then we double the
// number of vtables we're expecting, because every pointer will have a
// class vtable and a MetaClass vtable.
//
Result = MachoGetVtableSymbolsFromSmcp64 (
MachoContext,
Name,
&VtableSymbol,
&MetaVtableSymbol
);
if (!Result) {
return FALSE;
}
PatchData[NumTables].Entries->Smcp = Smcp;
PatchData[NumTables].Entries->Vtable = VtableSymbol;
PatchData[NumTables].Entries->MetaVtable = MetaVtableSymbol;
++NumTables;
}
}
PatchData->NumEntries = NumTables;
return TRUE;
}
BOOLEAN
InternalCreateVtablesNonPrelinked64 (
IN OUT OC_MACHO_CONTEXT *MachoContext,
IN CONST OC_DEPENDENCY_DATA *DependencyData,
IN OC_VTABLE_PATCH_ARRAY *PatchData,
OUT OC_VTABLE_ARRAY *VtableArray
)
{
OC_VTABLE *VtableBuffer;
CONST MACH_HEADER_64 *MachHeader;
UINT32 Index;
UINT32 NumPatched;
BOOLEAN Result;
CONST MACH_NLIST_64 *Smcp;
CONST CHAR8 *Name;
UINT32 VtableOffset;
CONST MACH_NLIST_64 *VtableSymbol;
CONST MACH_NLIST_64 *MetaVtableSymbol;
CONST MACH_NLIST_64 *MetaClass;
CONST UINT64 *VtableData;
CONST OC_VTABLE *SuperVtable;
CONST OC_VTABLE *MetaVtable;
CONST OC_SYMBOL_64 *OcSymbolDummy;
MACH_NLIST_64 *SymbolDummy;
CHAR8 ClassName[SYM_MAX_NAME_LEN];
CHAR8 SuperClassName[SYM_MAX_NAME_LEN];
CHAR8 VtableName[SYM_MAX_NAME_LEN];
CHAR8 SuperVtableName[SYM_MAX_NAME_LEN];
CHAR8 FinalSymbolName[SYM_MAX_NAME_LEN];
BOOLEAN SuccessfulIteration;
MachHeader = MachoGetMachHeader64 (MachoContext);
ASSERT (MachHeader != NULL);
VtableBuffer = GET_FIRST_OC_VTABLE (VtableArray);
NumPatched = 0;
while (NumPatched < PatchData->NumEntries) {
SuccessfulIteration = FALSE;
for (Index = 0; Index < PatchData->NumEntries; ++Index) {
Smcp = PatchData->Entries[Index].Smcp;
Name = MachoGetSymbolName64 (MachoContext, Smcp);
//
// We walk over the super metaclass pointer symbols because classes
// with them are the only ones that need patching. Then we double the
// number of vtables we're expecting, because every pointer will have a
// class vtable and a MetaClass vtable.
//
ASSERT (MachoSymbolNameIsSmcp64 (MachoContext, Name));
VtableSymbol = PatchData->Entries[Index].Vtable;
MetaVtableSymbol = PatchData->Entries[Index].MetaVtable;
//
// Get the class name from the smc pointer
//
Result = MachoGetClassNameFromSuperMetaClassPointer (
MachoContext,
Name,
sizeof (ClassName),
ClassName
);
if (!Result) {
return FALSE;
}
//
// Get the vtable name from the class name
//
Result = MachoGetVtableNameFromClassName (
ClassName,
sizeof (VtableName),
VtableName
);
if (!Result) {
return FALSE;
}
//
// Find the SMCP's meta class symbol
//
MetaClass = MachoGetMetaclassSymbolFromSmcpSymbol64 (
MachoContext,
Smcp
);
if (MetaClass == NULL) {
return FALSE;
}
//
// Get the super class name from the super metaclass
//
Result = MachoGetClassNameFromMetaClassPointer (
MachoContext,
MachoGetSymbolName64 (MachoContext, MetaClass),
sizeof (SuperClassName),
SuperClassName
);
if (!Result) {
return FALSE;
}
//
// Get the super vtable if it's been patched
//
SuperVtable = InternalGetOcVtableByName (
DependencyData->Vtables,
SuperVtableName
);
if (SuperVtable == NULL) {
continue;
}
//
// Get the final symbol's name from the super vtable
//
Result = MachoGetFinalSymbolNameFromClassName (
SuperClassName,
sizeof (FinalSymbolName),
FinalSymbolName
);
if (!Result) {
return FALSE;
}
//
// Verify that the final symbol does not exist. First check
// all the externally defined symbols, then check locally.
//
OcSymbolDummy = InternalOcGetSymbolByName (
DependencyData->SymbolTable,
FinalSymbolName,
TRUE
);
if (OcSymbolDummy != NULL) {
return FALSE;
}
Result = MachoGetLocalDefinedSymbolByName (
MachoContext,
FinalSymbolName,
&SymbolDummy
);
if (!Result || (SymbolDummy != NULL)) {
return FALSE;
}
//
// Patch the class's vtable
//
Result = MachoSymbolGetFileOffset64 (
MachoContext,
VtableSymbol,
&VtableOffset
);
if (!Result) {
return FALSE;
}
VtableData = (UINT64 *)((UINTN)MachHeader + VtableOffset);
if (!OC_ALIGNED (VtableData)) {
return FALSE;
}
Result = InternalInitializeVtableByEntriesAndRelocations64 (
MachoContext,
DependencyData->SymbolTable,
SuperVtable,
VtableSymbol,
VtableData,
VtableBuffer
);
if (!Result) {
return FALSE;
}
VtableBuffer->Name = MachoGetSymbolName64 (MachoContext, VtableSymbol);
//
// Add the class's vtable to the set of patched vtables
// This is done implicitely as we're operating on an array.
//
VtableBuffer = GET_NEXT_OC_VTABLE (VtableBuffer);
//
// Get the meta vtable name from the class name
//
Result = MachoGetMetaVtableNameFromClassName (
ClassName,
sizeof (VtableName),
VtableName
);
if (!Result) {
return FALSE;
}
MetaVtable = InternalGetOcVtableByName (
DependencyData->Vtables,
VtableName
);
if (MetaVtable != NULL) {
return FALSE;
}
//
// There is no way to look up a metaclass vtable at runtime, but
// we know that every class's metaclass inherits directly from
// OSMetaClass, so we just hardcode that vtable name here.
//
SuperVtable = InternalGetOcVtableByName (
DependencyData->Vtables,
OS_METACLASS_VTABLE_NAME
);
if (SuperVtable == NULL) {
return FALSE;
}
//
// meta_vtable_sym will be null when we don't support strict
// patching and can't find the metaclass vtable. If that's the
// case, we just reduce the expect number of vtables by 1.
// Only i386 does not support strict patchting.
//
Result = MachoSymbolGetFileOffset64 (
MachoContext,
MetaVtableSymbol,
&VtableOffset
);
if (!Result) {
return FALSE;
}
VtableData = (UINT64 *)((UINTN)MachHeader + VtableOffset);
if (!OC_ALIGNED (VtableData)) {
return FALSE;
}
Result = InternalInitializeVtableByEntriesAndRelocations64 (
MachoContext,
DependencyData->SymbolTable,
SuperVtable,
MetaVtableSymbol,
VtableData,
VtableBuffer
);
if (!Result) {
return FALSE;
}
VtableBuffer->Name = MachoGetSymbolName64 (MachoContext, MetaVtableSymbol);
//
// Add the MetaClass's vtable to the set of patched vtables
// This is done implicitely as we're operating on an array.
//
VtableBuffer = GET_NEXT_OC_VTABLE (VtableBuffer);
++NumPatched;
SuccessfulIteration = TRUE;
}
//
// Exit when there are unpatched VTables left, but there are none patched
// in a full iteration.
//
if (!SuccessfulIteration) {
ASSERT (FALSE);
return FALSE;
}
}
VtableArray->NumVtables = (NumPatched * 2);
return TRUE;
}

View File

@ -453,14 +453,25 @@ int main(int argc, char** argv) {
DEBUG ((DEBUG_WARN, "TestDriver.kext injected - %zx\n", Status));
UINT8 *TestData = LiluKextData;
UINT32 TestDataSize = LiluKextDataSize;
if (argc > 2) {
TestData = readFile(argv[2], &TestDataSize);
if (TestData == NULL) {
printf("Read fail\n");
return -1;
}
}
Status = PrelinkedInjectKext (
&Context,
"/Library/Extensions/Lilu.kext",
LiluKextInfoPlistData,
LiluKextInfoPlistDataSize,
"Contents/MacOS/Lilu",
LiluKextData,
LiluKextDataSize
TestData,
TestDataSize
);
DEBUG ((DEBUG_WARN, "Lilu.kext injected - %r\n", Status));

BIN
TestsUser/Prelinked/Prelinked_ Executable file

Binary file not shown.