#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <sysexits.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <mach/mach.h>
#include <mach/mach_error.h>
#include <mach-o/arch.h>
#include <System/libkern/mkext.h>
#include <CoreFoundation/CFBundlePriv.h>
#include <IOKit/kext/OSKextPrivate.h>
#include "kext_tools_util.h"
#include "compression.h"
typedef struct {
CFMutableDataRef mkext;
uint32_t kextIndex;
uint32_t compressOffset;
const NXArchInfo * arch;
Boolean fatal;
Boolean compress;
} Mkext1Context;
void addToMkext1(
const void * vKey,
const void * vValue,
void * vContext);
Boolean addDataToMkext(
CFDataRef data,
Mkext1Context * context,
char * kextPath,
Boolean isInfoDict);
CFDataRef createMkext1ForArch(const NXArchInfo * arch, CFArrayRef archiveKexts,
boolean_t compress)
{
CFMutableDataRef result = NULL;
CFMutableDictionaryRef kextsByIdentifier = NULL;
Mkext1Context context;
mkext1_header * mkextHeader = NULL; const uint8_t * adler_point = 0;
CFIndex count, i;
result = CFDataCreateMutable(kCFAllocatorDefault, 0);
if (!result || !createCFMutableDictionary(&kextsByIdentifier)) {
OSKextLogMemError();
goto finish;
}
count = CFArrayGetCount(archiveKexts);
for (i = 0; i < count; i++) {
OSKextRef theKext = (OSKextRef)CFArrayGetValueAtIndex(archiveKexts, i);
CFStringRef bundleIdentifier = OSKextGetIdentifier(theKext);
OSKextRef savedKext = (OSKextRef)CFDictionaryGetValue(kextsByIdentifier,
bundleIdentifier);
OSKextVersion thisVersion, savedVersion;
if (!OSKextSupportsArchitecture(theKext, arch)) {
continue;
}
if (!savedKext) {
CFDictionarySetValue(kextsByIdentifier, bundleIdentifier, theKext);
continue;
}
thisVersion = OSKextGetVersion(theKext);
savedVersion = OSKextGetVersion(savedKext);
if (thisVersion > savedVersion) {
CFDictionarySetValue(kextsByIdentifier, bundleIdentifier, theKext);
}
}
CFDataSetLength(result, sizeof(mkext1_header) +
CFDictionaryGetCount(kextsByIdentifier) * sizeof(mkext_kext));
context.mkext = result;
context.kextIndex = 0;
context.compressOffset = (uint32_t)CFDataGetLength(result);
context.arch = arch;
context.fatal = false;
context.compress = compress;
CFDictionaryApplyFunction(kextsByIdentifier, addToMkext1, &context);
if (context.fatal) {
SAFE_RELEASE_NULL(result);
goto finish;
}
mkextHeader = (mkext1_header *)CFDataGetBytePtr(result);
mkextHeader->magic = OSSwapHostToBigInt32(MKEXT_MAGIC);
mkextHeader->signature = OSSwapHostToBigInt32(MKEXT_SIGN);
mkextHeader->version = OSSwapHostToBigInt32(0x01008000); mkextHeader->numkexts =
OSSwapHostToBigInt32(CFDictionaryGetCount(kextsByIdentifier));
mkextHeader->cputype = OSSwapHostToBigInt32(arch->cputype);
mkextHeader->cpusubtype = OSSwapHostToBigInt32(arch->cpusubtype);
mkextHeader->length = OSSwapHostToBigInt32(CFDataGetLength(result));
adler_point = (UInt8 *)&mkextHeader->version;
mkextHeader->adler32 = OSSwapHostToBigInt32(local_adler32(
(UInt8 *)&mkextHeader->version,
(int)(CFDataGetLength(result) - (adler_point - (uint8_t *)mkextHeader))));
OSKextLog( NULL, kOSKextLogProgressLevel | kOSKextLogArchiveFlag,
"Created mkext for %s containing %lu kexts.",
arch->name,
CFDictionaryGetCount(kextsByIdentifier));
finish:
SAFE_RELEASE(kextsByIdentifier);
return result;
}
void addToMkext1(
const void * vKey __unused,
const void * vValue,
void * vContext)
{
OSKextRef aKext = (OSKextRef)vValue;
Mkext1Context * context = (Mkext1Context *)vContext;
CFBundleRef kextBundle = NULL; CFURLRef infoDictURL = NULL; CFDataRef rawInfoDict = NULL; CFDataRef executable = NULL; char kextPath[PATH_MAX];
if (context->fatal) {
goto finish;
}
if (!CFURLGetFileSystemRepresentation(OSKextGetURL(aKext),
false, (UInt8 *)kextPath, sizeof(kextPath))) {
strlcpy(kextPath, "(unknown)", sizeof(kextPath));
}
OSKextLog(aKext,
kOSKextLogProgressLevel | kOSKextLogArchiveFlag,
"Adding %s to mkext.", kextPath);
OSKextLog(aKext,
kOSKextLogStepLevel | kOSKextLogArchiveFlag | kOSKextLogFileAccessFlag,
"Opening CFBundle for %s.", kextPath);
kextBundle = CFBundleCreate(kCFAllocatorDefault, OSKextGetURL(aKext));
if (!kextBundle) {
OSKextLog(aKext,
kOSKextLogStepLevel | kOSKextLogArchiveFlag | kOSKextLogFileAccessFlag,
"Can't open bundle for %s.", kextPath);
context->fatal = true;
goto finish;
}
infoDictURL = _CFBundleCopyInfoPlistURL(kextBundle);
if (!infoDictURL) {
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Can't get URL for info dict of %s.", kextPath);
context->fatal = true;
goto finish;
}
char infoDictPath[PATH_MAX];
if (!CFURLGetFileSystemRepresentation(infoDictURL,
true,
(uint8_t *)infoDictPath,
sizeof(infoDictPath))) {
OSKextLogStringError( NULL);
context->fatal = true;
goto finish;
}
if (!createCFDataFromFile(&rawInfoDict,
infoDictPath)) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"%s: Can't read info dictoionary file '%s'",
__func__, infoDictPath);
context->fatal = true;
goto finish;
}
if (!addDataToMkext(rawInfoDict, context, kextPath, true)) {
context->fatal = true;
goto finish;
}
executable = OSKextCopyExecutableForArchitecture(aKext, context->arch);
if (executable) {
if (!addDataToMkext(executable, context, kextPath, false)) {
context->fatal = true;
goto finish;
}
} else if (OSKextDeclaresExecutable(aKext)) {
OSKextLog(aKext,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Can't get executable for %s (architecture %s).", kextPath,
context->arch->name);
context->fatal = true;
goto finish;
}
context->kextIndex++;
finish:
if (kextBundle) {
OSKextLog(aKext,
kOSKextLogStepLevel | kOSKextLogArchiveFlag | kOSKextLogFileAccessFlag,
"Releaseing CFBundle for %s.", kextPath);
SAFE_RELEASE(kextBundle);
}
SAFE_RELEASE(infoDictURL);
SAFE_RELEASE(rawInfoDict);
SAFE_RELEASE(executable);
return;
}
Boolean addDataToMkext(
CFDataRef data,
Mkext1Context * context,
char * kextPath,
Boolean isInfoDict)
{
Boolean result = false;
CFIndex newMkextLength;
uint32_t origCompressOffset;
const UInt8 * mkextStart = NULL; UInt8 * addedDataStart = NULL; UInt8 * addedDataEnd = NULL; uint32_t addedDataFullLength;
uint32_t compressedLength;
uint8_t * checkBuffer = NULL;
mkext1_header * mkextHeader = NULL; mkext_kext * mkextKextEntry = NULL; mkext_file * mkextFileEntry = NULL;
addedDataFullLength = (uint32_t)CFDataGetLength(data);
newMkextLength = CFDataGetLength(context->mkext) + addedDataFullLength;
CFDataSetLength(context->mkext, newMkextLength);
if (CFDataGetLength(context->mkext) != newMkextLength) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"Can't resize mkext buffer.");
goto finish;
}
mkextStart = CFDataGetBytePtr(context->mkext);
origCompressOffset = context->compressOffset;
addedDataStart = (UInt8 *)mkextStart + origCompressOffset;
if (context->compress) {
addedDataEnd = compress_lzss((uint8_t *)addedDataStart, addedDataFullLength,
(uint8_t *)CFDataGetBytePtr(data), addedDataFullLength);
if (!addedDataEnd) {
OSKextLog( NULL,
kOSKextLogWarningLevel | kOSKextLogArchiveFlag,
"%s did not compress; copying file (%d bytes).",
isInfoDict ? "info dictionary" : "executable",
addedDataFullLength);
}
}
if (!addedDataEnd) {
memcpy(addedDataStart, (const void *)CFDataGetBytePtr(data),
addedDataFullLength);
addedDataEnd = addedDataStart + addedDataFullLength;
compressedLength = 0;
context->compressOffset += addedDataFullLength;
} else {
size_t checkLength;
compressedLength = (uint32_t)(addedDataEnd - addedDataStart);
context->compressOffset += compressedLength;
checkBuffer = (uint8_t *)malloc(addedDataFullLength);
if (!checkBuffer) {
OSKextLogMemError();
goto finish;
}
checkLength = decompress_lzss(checkBuffer, addedDataFullLength,
addedDataStart, compressedLength);
if (checkLength != addedDataFullLength) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"%s - %s decompressed size %d differs from original size %d",
kextPath,
isInfoDict ? "info dictionary" : "executable",
(int)checkLength, (int)addedDataFullLength);
goto finish;
}
if (0 != memcmp(checkBuffer, CFDataGetBytePtr(data), checkLength)) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogArchiveFlag,
"%s - %s decompressed data differs from input",
kextPath,
isInfoDict ? "info dictionary" : "executable");
goto finish;
}
OSKextLog( NULL,
kOSKextLogDetailLevel | kOSKextLogArchiveFlag,
"Compressed %s from %u to %u bytes (%.2f%%).",
isInfoDict ? "info dict" : "executable",
addedDataFullLength, compressedLength,
(100.0 * (float)compressedLength/(float)addedDataFullLength));
}
CFDataSetLength(context->mkext, addedDataEnd - mkextStart);
mkextHeader = (mkext1_header *)CFDataGetBytePtr(context->mkext);
mkextKextEntry = &(mkextHeader->kext[context->kextIndex]);
if (isInfoDict) {
mkextFileEntry = &(mkextKextEntry->plist);
} else {
mkextFileEntry = &(mkextKextEntry->module);
}
mkextFileEntry->offset = OSSwapHostToBigInt32(origCompressOffset);
mkextFileEntry->realsize = OSSwapHostToBigInt32(addedDataFullLength);
mkextFileEntry->compsize = OSSwapHostToBigInt32(compressedLength);
mkextFileEntry->modifiedsecs = 0;
result = true;
finish:
SAFE_FREE(checkBuffer);
return result;
}