2018-10-14 04:17:50 +02:00

673 lines
16 KiB
C

/** @file
Provides services for C++ symbols.
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/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/OcMachoLib.h>
#define CXX_PREFIX "__Z"
#define VTABLE_PREFIX CXX_PREFIX "TV"
#define OSOBJ_PREFIX CXX_PREFIX "N"
#define RESERVED_TOKEN "_RESERVED"
#define METACLASS_TOKEN "10gMetaClassE"
#define SUPER_METACLASS_POINTER_TOKEN "10superClassE"
#define METACLASS_VTABLE_PREFIX VTABLE_PREFIX "N"
#define METACLASS_VTABLE_SUFFIX "9MetaClassE"
#define CXX_PURE_VIRTUAL "___cxa_pure_virtual"
#define FINAL_CLASS_TOKEN "14__OSFinalClassEv"
#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 SYM_MAX_NAME_LEN 256U
/**
Returns whether Name is pure virtual.
@param[in] Name The name to evaluate.
**/
BOOLEAN
MachoIsNamePureVirtual (
IN CONST CHAR8 *Name
)
{
ASSERT (Name != NULL);
return (AsciiStrCmp (Name, CXX_PURE_VIRTUAL) == 0);
}
/**
Returns whether Name is a Padslot.
@param[in] Name The name to evaluate.
**/
BOOLEAN
MachoIsNamePadslot (
IN CONST CHAR8 *Name
)
{
ASSERT (Name != NULL);
return (AsciiStrStr (Name, RESERVED_TOKEN) != NULL);
}
/**
Returns whether Symbol defines a Super Metaclass Pointer.
@param[in] Symbol The symbol to check.
@param[in] StringTable The KEXT's String Table.
**/
BOOLEAN
MachoSymbolIsSmcp64 (
IN CONST MACH_NLIST_64 *Symbol,
IN CONST CHAR8 *StringTable
)
{
CONST CHAR8 *Name;
ASSERT (Symbol != NULL);
ASSERT (StringTable != NULL);
Name = (StringTable + Symbol->StringIndex);
return (AsciiStrStr (Name, SUPER_METACLASS_POINTER_TOKEN) != NULL);
}
/**
Returns whether Symbol defines a Super Metaclass Pointer.
@param[in] Symbol The symbol to check.
@param[in] StringTable The KEXT's String Table.
**/
BOOLEAN
MachoSymbolIsMetaclassPointer64 (
IN CONST MACH_NLIST_64 *Symbol,
IN CONST CHAR8 *StringTable
)
{
CONST CHAR8 *Name;
ASSERT (Symbol != NULL);
ASSERT (StringTable != NULL);
Name = (StringTable + Symbol->StringIndex);
return (AsciiStrStr (Name, METACLASS_TOKEN) != NULL);
}
/**
Retrieves the class name of a Super Meta Class Pointer.
@param[in] StringTable String Table of the MACH-O.
@param[in] SmcpSymbol SMCP Symbol to get the class name of.
@param[in] ClassNameSize The size of ClassName.
@param[out] ClassName The output buffer for the class name.
@returns Whether the name has been retrieved successfully.
**/
BOOLEAN
MachoGetClassNameFromSuperMetaClassPointer (
IN CONST CHAR8 *StringTable,
IN CONST MACH_NLIST_64 *SmcpSymbol,
IN UINTN ClassNameSize,
OUT CHAR8 *ClassName
)
{
BOOLEAN Result;
CONST CHAR8 *SuperMetaClassName;
UINTN StringLength;
UINTN PrefixLength;
UINTN SuffixLength;
UINTN ClassNameLength;
ASSERT (StringTable != NULL);
ASSERT (SmcpSymbol != NULL);
ASSERT (ClassNameSize > 0);
ASSERT (ClassName != NULL);
Result = MachoSymbolIsSmcp64 (SmcpSymbol, StringTable);
if (!Result) {
return FALSE;
}
SuperMetaClassName = (StringTable + SmcpSymbol->StringIndex);
PrefixLength = (ARRAY_SIZE (OSOBJ_PREFIX) - 1);
StringLength = AsciiStrLen (SuperMetaClassName);
SuffixLength = (ARRAY_SIZE (SUPER_METACLASS_POINTER_TOKEN) - 1);
ASSERT ((StringLength - PrefixLength - SuffixLength) < ClassNameSize);
ClassNameLength = MIN (
(StringLength - PrefixLength - SuffixLength),
(ClassNameSize - 1)
);
CopyMem (ClassName, &SuperMetaClassName[PrefixLength], ClassNameLength);
ClassName[ClassNameLength] = '\0';
return TRUE;
}
/**
Retrieves the class name of a VTable.
@param[out] VtableName The VTable's name.
**/
CONST CHAR8 *
MachoGetClassNameFromVtableName (
IN CONST CHAR8 *VtableName
)
{
UINTN PrefixLength;
ASSERT (VtableName != NULL);
//
// As there is no suffix, just return a pointer from within VtableName.
//
PrefixLength = (ARRAY_SIZE (VTABLE_PREFIX) - 1);
return &VtableName[PrefixLength];
}
/**
Retrieves the function prefix of a class name.
@param[in] ClassName The class name to evaluate.
@param[in] FunctionPrefixSize The size of FunctionPrefix.
@param[out] FunctionPrefix The output buffer for the function prefix.
@returns Whether the name has been retrieved successfully.
**/
BOOLEAN
MachoGetFunctionPrefixFromClassName (
IN CONST CHAR8 *ClassName,
IN UINTN FunctionPrefixSize,
OUT CHAR8 *FunctionPrefix
)
{
UINTN Index;
UINTN BodyLength;
ASSERT (ClassName != NULL);
ASSERT (FunctionPrefixSize > 0);
ASSERT (FunctionPrefix != NULL);
BodyLength = AsciiStrLen (ClassName);
if (FunctionPrefixSize < (sizeof (OSOBJ_PREFIX) + BodyLength)) {
ASSERT (FALSE);
return FALSE;
}
Index = 0;
CopyMem (
&FunctionPrefix[Index],
OSOBJ_PREFIX,
(sizeof (OSOBJ_PREFIX) - sizeof (*OSOBJ_PREFIX))
);
Index += (ARRAY_SIZE (OSOBJ_PREFIX) - 1);
CopyMem (
&FunctionPrefix[Index],
ClassName,
(BodyLength + sizeof (*ClassName))
);
return TRUE;
}
/**
Retrieves the class name of a Meta Class Pointer.
@param[in] StringTable String Table of the MACH-O.
@param[in] MetaClassPtrSymbol MCP Symbol to get the class name of.
@param[in] ClassNameSize The size of ClassName.
@param[out] ClassName The output buffer for the class name.
@returns Whether the name has been retrieved successfully.
**/
BOOLEAN
MachoGetClassNameFromMetaClassPointer (
IN CONST CHAR8 *StringTable,
IN CONST MACH_NLIST_64 *MetaClassPtrSymbol,
IN UINTN ClassNameSize,
OUT CHAR8 *ClassName
)
{
BOOLEAN Result;
CONST CHAR8 *MetaClassName;
UINTN StringLength;
UINTN PrefixLength;
UINTN SuffixLength;
UINTN ClassNameLength;
ASSERT (StringTable != NULL);
ASSERT (MetaClassPtrSymbol != NULL);
ASSERT (ClassNameSize > 0);
ASSERT (ClassName != NULL);
Result = MachoSymbolIsMetaclassPointer64 (
MetaClassPtrSymbol,
StringTable
);
if (!Result) {
return FALSE;
}
MetaClassName = (StringTable + MetaClassPtrSymbol->StringIndex);
PrefixLength = (ARRAY_SIZE (OSOBJ_PREFIX) - 1);
StringLength = AsciiStrLen (MetaClassName);
SuffixLength = (ARRAY_SIZE (METACLASS_TOKEN) - 1);
ASSERT ((StringLength - PrefixLength - SuffixLength) < ClassNameSize);
ClassNameLength = MIN (
(StringLength - PrefixLength - SuffixLength),
(ClassNameSize - 1)
);
CopyMem (ClassName, &MetaClassName[PrefixLength], ClassNameLength);
ClassName[ClassNameLength] = '\0';
return TRUE;
}
/**
Retrieves the VTable name of a class name.
@param[in] ClassName Class name to get the VTable name of.
@param[in] VtableNameSize The size of VtableName.
@param[out] VtableName The output buffer for the VTable name.
@returns Whether the name has been retrieved successfully.
**/
BOOLEAN
MachoGetVtableNameFromClassName (
IN CONST CHAR8 *ClassName,
IN UINTN VtableNameSize,
OUT CHAR8 *VtableName
)
{
UINTN Index;
UINTN BodyLength;
ASSERT (ClassName != NULL);
ASSERT (VtableNameSize > 0);
ASSERT (VtableName != NULL);
BodyLength = AsciiStrLen (ClassName);
if (VtableNameSize < (sizeof (VTABLE_PREFIX) + BodyLength)) {
ASSERT (FALSE);
return FALSE;
}
Index = 0;
CopyMem (
&VtableName[Index],
VTABLE_PREFIX,
(sizeof (VTABLE_PREFIX) - sizeof (*VTABLE_PREFIX))
);
Index += (ARRAY_SIZE (VTABLE_PREFIX) - 1);
CopyMem (&VtableName[Index], ClassName, (BodyLength + sizeof (*ClassName)));
return TRUE;
}
/**
Retrieves the Meta VTable name of a class name.
@param[in] ClassName Class name to get the Meta VTable name of.
@param[in] VtableNameSize The size of VtableName.
@param[out] VtableName The output buffer for the VTable name.
@returns Whether the name has been retrieved successfully.
**/
BOOLEAN
MachoGetMetaVtableNameFromClassName (
IN CONST CHAR8 *ClassName,
IN UINTN VtableNameSize,
OUT CHAR8 *VtableName
)
{
UINTN NameSize;
UINTN BodyLength;
UINTN Index;
ASSERT (ClassName != NULL);
ASSERT (VtableNameSize > 0);
ASSERT (VtableName != NULL);
BodyLength = AsciiStrLen (ClassName);
NameSize = (
sizeof (METACLASS_VTABLE_PREFIX)
+ BodyLength
+ (sizeof (METACLASS_VTABLE_SUFFIX) - 1)
);
if (VtableNameSize < NameSize) {
ASSERT (FALSE);
return FALSE;
}
Index = 0;
CopyMem (
&VtableName[Index],
METACLASS_VTABLE_PREFIX,
(sizeof (METACLASS_VTABLE_PREFIX) - sizeof (*METACLASS_VTABLE_PREFIX))
);
Index += (ARRAY_SIZE (METACLASS_VTABLE_PREFIX) - 1);
CopyMem (
&VtableName[Index],
ClassName,
BodyLength
);
Index += BodyLength;
CopyMem (
&VtableName[Index],
METACLASS_VTABLE_SUFFIX,
sizeof (METACLASS_VTABLE_SUFFIX)
);
return TRUE;
}
/**
Retrieves the final symbol name of a class name.
@param[in] ClassName Class name to get the final symbol name of.
@param[in] FinalSymbolNameSize The size of FinalSymbolName.
@param[out] FinalSymbolName The output buffer for the final symbol name.
@returns Whether the name has been retrieved successfully.
**/
BOOLEAN
MachoGetFinalSymbolNameFromClassName (
IN CONST CHAR8 *ClassName,
IN UINTN FinalSymbolNameSize,
OUT CHAR8 *FinalSymbolName
)
{
UINTN NameSize;
UINTN BodyLength;
UINTN Index;
ASSERT (ClassName != NULL);
ASSERT (FinalSymbolNameSize > 0);
ASSERT (FinalSymbolName != NULL);
BodyLength = AsciiStrLen (ClassName);
NameSize = (
sizeof (OSOBJ_PREFIX)
+ BodyLength
+ (sizeof (FINAL_CLASS_TOKEN) - 1)
);
if (FinalSymbolNameSize < NameSize) {
ASSERT (FALSE);
return FALSE;
}
Index = 0;
CopyMem (
&FinalSymbolName[Index],
OSOBJ_PREFIX,
(sizeof (OSOBJ_PREFIX) - sizeof (*OSOBJ_PREFIX))
);
Index += (ARRAY_SIZE (OSOBJ_PREFIX) - 1);
CopyMem (
&FinalSymbolName[Index],
ClassName,
BodyLength
);
Index += BodyLength;
CopyMem (
&FinalSymbolName[Index],
FINAL_CLASS_TOKEN,
sizeof (FINAL_CLASS_TOKEN)
);
return TRUE;
}
/**
Returns whether Symbol defines a VTable.
@param[in] Symbol The symbol to check.
@param[in] StringTable The KEXT's String Table.
**/
BOOLEAN
MachoSymbolIsVtable64 (
IN CONST MACH_NLIST_64 *Symbol,
IN CONST CHAR8 *StringTable
)
{
CONST CHAR8 *Name;
ASSERT (Symbol != NULL);
ASSERT (StringTable != NULL);
Name = (StringTable + Symbol->StringIndex);
//
// Implicitely checks for METACLASS_VTABLE_PREFIX.
//
return (AsciiStrCmp (Name, VTABLE_PREFIX) == 0);
}
/**
Returns whether the symbol name describes a C++ symbol.
@param[in] Name The name to evaluate.
**/
BOOLEAN
MachoIsSymbolNameCxx (
IN CONST CHAR8 *Name
)
{
ASSERT (Name != NULL);
return (AsciiStrnCmp (Name, CXX_PREFIX, (ARRAY_SIZE (CXX_PREFIX) - 1)) == 0);
}
/**
Returns the number of VTable entires in VtableData.
@param[in] VtableData The VTable's data.
@param[in] MachCpuType CPU Type of the MACH-O.
**/
UINTN
MachoVtableGetNumberOfEntries64 (
IN CONST UINT64 *VtableData,
IN MACH_CPU_TYPE MachCpuType
)
{
UINTN Index;
UINTN NumberOfEntries;
ASSERT (VtableData != NULL);
NumberOfEntries = 0;
//
// Assumption: Not ARM (ARM requires an alignment to the function pointer
// retrieved from VtableData).
//
ASSERT ((MachCpuType != MachCpuTypeArm)
&& (MachCpuType != MachCpuTypeArm64));
for (Index = VTABLE_HEADER_LEN_64; VtableData[Index] != 0; ++Index) {
++NumberOfEntries;
}
return NumberOfEntries;
}
/**
Retrieves Metaclass symbol of a SMCP.
@param[in] MachHeader Header of the MACH-O.
@param[in] NumberOfSymbols Number of symbols in SymbolTable.
@param[in] SymbolTable Symbol Table of the MACH-O.
@param[in] StringTable String Table of the MACH-O.
@param[in] NumberOfRelocations Number of Relocations in Relocations.
@param[in] Relocations The Relocations to evaluate.
@param[in] Smcp The SMCP to evaluate.
@retval NULL NULL is returned on failure.
**/
CONST MACH_NLIST_64 *
MachoGetMetaclassSymbolFromSmcpSymbol64 (
IN CONST MACH_HEADER_64 *MachHeader,
IN UINTN NumberOfSymbols,
IN CONST MACH_NLIST_64 *SymbolTable,
IN CONST CHAR8 *StringTable,
IN UINTN NumberOfRelocations,
IN CONST MACH_RELOCATION_INFO *Relocations,
IN CONST MACH_NLIST_64 *Smcp
)
{
CONST MACH_RELOCATION_INFO *Relocation;
ASSERT (MachHeader != NULL);
ASSERT (SymbolTable != NULL);
ASSERT (StringTable != NULL);
ASSERT (Relocations != NULL);
ASSERT (Smcp != NULL);
Relocation = MachoGetRelocationByOffset (
NumberOfRelocations,
Relocations,
Smcp->Value,
MachHeader->MachCpuType
);
if (Relocation == NULL) {
return NULL;
}
return MachoGetCxxSymbolByRelocation64 (
MachHeader,
NumberOfSymbols,
SymbolTable,
StringTable,
Relocation
);
}
/**
Retrieves VTable and Meta VTable of a SMCP.
Logically matches XNU's get_vtable_syms_from_smcp.
@param[in] SymbolTable Symbol Table of the MACH-O.
@param[in] StringTable String Table of the MACH-O.
@param[in] DySymtab Dynamic Symbol Table of the MACH-O.
@param[in] SmcpSymbol SMCP Symbol to retrieve the VTables from.
@param[out] Vtable Output buffer for the VTable symbol pointer.
@param[out] MetaVtable Output buffer for the Meta VTable symbol pointer.
**/
BOOLEAN
MachoGetVtableSymbolsFromSmcp64 (
IN CONST MACH_NLIST_64 *SymbolTable,
IN CONST CHAR8 *StringTable,
IN CONST MACH_DYSYMTAB_COMMAND *DySymtab,
IN CONST MACH_NLIST_64 *SmcpSymbol,
OUT CONST MACH_NLIST_64 **Vtable,
OUT CONST MACH_NLIST_64 **MetaVtable
)
{
CHAR8 ClassName[SYM_MAX_NAME_LEN];
CHAR8 VtableName[SYM_MAX_NAME_LEN];
CHAR8 MetaVtableName[SYM_MAX_NAME_LEN];
BOOLEAN Result;
CONST MACH_NLIST_64 *VtableSymbol;
CONST MACH_NLIST_64 *MetaVtableSymbol;
ASSERT (SymbolTable != NULL);
ASSERT (SymbolTable != NULL);
ASSERT (DySymtab != NULL);
ASSERT (SmcpSymbol != NULL);
ASSERT (Vtable != NULL);
ASSERT (MetaVtable != NULL);
Result = MachoGetClassNameFromSuperMetaClassPointer (
NULL,
SmcpSymbol,
sizeof (ClassName),
ClassName
);
if (!Result) {
return FALSE;
}
Result = MachoGetVtableNameFromClassName (
ClassName,
sizeof (VtableName),
VtableName
);
if (!Result) {
return FALSE;
}
VtableSymbol = MachoGetLocalDefinedSymbolByName (
SymbolTable,
StringTable,
DySymtab,
VtableName
);
if (VtableSymbol == NULL) {
return FALSE;
}
Result = MachoGetMetaVtableNameFromClassName (
ClassName,
sizeof (MetaVtableName),
MetaVtableName
);
if (!Result) {
return FALSE;
}
MetaVtableSymbol = MachoGetLocalDefinedSymbolByName (
SymbolTable,
StringTable,
DySymtab,
MetaVtableName
);
if (MetaVtableSymbol == NULL) {
return FALSE;
}
*Vtable = VtableSymbol;
*MetaVtable = MetaVtableSymbol;
return TRUE;
}