diff options
Diffstat (limited to 'machoparse')
-rw-r--r-- | machoparse/LICENSE | 19 | ||||
-rw-r--r-- | machoparse/cdhash.c | 455 | ||||
-rw-r--r-- | machoparse/cdhash.h | 34 | ||||
-rw-r--r-- | machoparse/cs_blobs.h | 248 | ||||
-rw-r--r-- | machoparse/macho.h | 73 |
5 files changed, 829 insertions, 0 deletions
diff --git a/machoparse/LICENSE b/machoparse/LICENSE new file mode 100644 index 0000000..41a58c3 --- /dev/null +++ b/machoparse/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2018 Brandon Azad + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/machoparse/cdhash.c b/machoparse/cdhash.c new file mode 100644 index 0000000..8f7796e --- /dev/null +++ b/machoparse/cdhash.c @@ -0,0 +1,455 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2018 Brandon Azad + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/* + * This is forked from https://github.com/bazad/blanket/blob/master/amfidupe/cdhash.c + * + * Notable changes: + * 1. 32bit binary support + * 2. Endianness handling + * 3. FAT support + */ + +/* + * Cdhash computation + * ------------------ + * + * The amfid patch needs to be able to compute the cdhash of a binary. + * This code is heavily based on the implementation in Ian Beer's triple_fetch project [1] and on + * the source of XNU [2]. + * + * [1]: https://bugs.chromium.org/p/project-zero/issues/detail?id=1247 + * [2]: https://opensource.apple.com/source/xnu/xnu-4570.41.2/bsd/kern/ubc_subr.c.auto.html + * + */ +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +#if COMMONCRYPTO +# include <CommonCrypto/CommonCrypto.h> +# define SHA1 CC_SHA1 +# define SHA_DIGEST_LENGTH CC_SHA1_DIGEST_LENGTH +# define SHA256 CC_SHA256 +# define SHA256_DIGEST_LENGTH CC_SHA256_DIGEST_LENGTH +# define SHA384 CC_SHA384 +# define SHA384_DIGEST_LENGTH CC_SHA384_DIGEST_LENGTH +#else +# include <openssl/sha.h> +#endif + +#if __APPLE__ +# include <libkern/OSByteOrder.h> +# define bswap32(x) OSSwapInt32(x) +# define be32toh(x) OSSwapBigToHostInt32(x) +#elif __has_include(<endian.h>) +# include <endian.h> +#else +# include <sys/endian.h> +#endif + +#include "cdhash.h" +#include "cs_blobs.h" +#include "macho.h" + +#define ERROR(x, ...) +#define DEBUG_TRACE(x, y, ...) + +static uint32_t +swap(const void *h, const void *h2, const uint32_t s) { + uint32_t magic = *((uint32_t*)(h == NULL ? h2 : h)); + if (magic == MH_CIGAM || magic == MH_CIGAM_64) + return bswap32(s); + else + return s; +} + +// Check whether the file looks like a Mach-O file. +static bool +macho_identify(const struct mach_header_64 *mh, const struct mach_header *mh32, size_t size) { + uint32_t magic = mh != NULL ? mh->magic : mh32->magic; + // Check the file size and magic. + if (size < 0x1000 || (magic != MH_MAGIC_64 && + magic != MH_CIGAM_64 && magic != MH_MAGIC && + magic != MH_CIGAM)) { + return false; + } + return true; +} + +// Get the next load command in a Mach-O file. +static const void * +macho_next_load_command(const struct mach_header_64 *mh, const struct mach_header *mh32, size_t size, const void *lc) { + const struct load_command *next = lc; + + if (next == NULL) { + if (mh != NULL) + next = (const struct load_command *)(mh + 1); + else + next = (const struct load_command *)(mh32 + 1); + } else { + next = (const struct load_command *)((uint8_t *)next + swap(mh, mh32, next->cmdsize)); + } + if (mh != NULL) { + if ((uintptr_t)next >= (uintptr_t)(mh + 1) + swap(mh, mh32, mh->sizeofcmds)) { + next = NULL; + } + } else { + if ((uintptr_t)next >= (uintptr_t)(mh32 + 1) + swap(mh, mh32, mh32->sizeofcmds)) { + next = NULL; + } + } + return next; +} + +// Find the next load command in a Mach-O file matching the given type. +static const void * +macho_find_load_command(const struct mach_header_64 *mh, const struct mach_header *mh32, size_t size, + uint32_t command, const void *lc) { + const struct load_command *loadcmd = lc; + for (;;) { + loadcmd = macho_next_load_command(mh, mh32, size, loadcmd); + if (loadcmd == NULL || swap(mh, mh32, loadcmd->cmd) == command) { + return loadcmd; + } + } +} + +// Validate a CS_CodeDirectory and return its true length. +static size_t +cs_codedirectory_validate(CS_CodeDirectory *cd, size_t size) { + // Make sure we at least have a CS_CodeDirectory. There's an end_earliest parameter, but + // XNU doesn't seem to use it in cs_validate_codedirectory(). + if (size < sizeof(*cd)) { + ERROR("CS_CodeDirectory is too small\n"); + return 0; + } + // Validate the magic. + uint32_t magic = be32toh(cd->magic); + if (magic != CSMAGIC_CODEDIRECTORY) { + ERROR("CS_CodeDirectory has incorrect magic\n"); + return 0; + } + // Validate the length. + uint32_t length = be32toh(cd->length); + if (length > size) { + ERROR("CS_CodeDirectory has invalid length\n"); + return 0; + } + return length; +} + +// Validate a CS_SuperBlob and return its true length. +static size_t +cs_superblob_validate(CS_SuperBlob *sb, size_t size) { + // Make sure we at least have a CS_SuperBlob. + if (size < sizeof(*sb)) { + ERROR("CS_SuperBlob is too small\n"); + return 0; + } + // Validate the magic. + uint32_t magic = be32toh(sb->magic); + if (magic != CSMAGIC_EMBEDDED_SIGNATURE) { + ERROR("CS_SuperBlob has incorrect magic\n"); + return 0; + } + // Validate the length. + uint32_t length = be32toh(sb->length); + if (length > size) { + ERROR("CS_SuperBlob has invalid length\n"); + return 0; + } + uint32_t count = be32toh(sb->count); + // Validate the count. + CS_BlobIndex *index = &sb->index[count]; + if (count >= 0x10000 || (uintptr_t)index > (uintptr_t)sb + size) { + ERROR("CS_SuperBlob has invalid count\n"); + return 0; + } + return length; +} + +// Compute the cdhash of a code directory using SHA1. +static void +cdhash_sha1(CS_CodeDirectory *cd, size_t length, void *cdhash) { + uint8_t digest[SHA_DIGEST_LENGTH]; + SHA1((void*)cd, length, digest); + memcpy(cdhash, digest, CS_CDHASH_LEN); +} + +// Compute the cdhash of a code directory using SHA256. +static void +cdhash_sha256(CS_CodeDirectory *cd, size_t length, void *cdhash) { + uint8_t digest[SHA256_DIGEST_LENGTH]; + SHA256((void*)cd, length, digest); + memcpy(cdhash, digest, CS_CDHASH_LEN); +} + +// Compute the cdhash of a code directory using SHA384. +static void +cdhash_sha384(CS_CodeDirectory *cd, size_t length, void *cdhash) { + uint8_t digest[SHA384_DIGEST_LENGTH]; + SHA384((void*)cd, length, digest); + memcpy(cdhash, digest, CS_CDHASH_LEN); +} + +// Compute the cdhash from a CS_CodeDirectory. +static bool +cs_codedirectory_cdhash(CS_CodeDirectory *cd, size_t size, struct hashes *cdhash) { + size_t length = be32toh(cd->length); + switch (cd->hashType) { + case CS_HASHTYPE_SHA1: + DEBUG_TRACE(2, "Using SHA1\n"); + cdhash_sha1(cd, length, cdhash->cdhash); + cdhash->hash_type = CS_HASHTYPE_SHA1; + return true; + case CS_HASHTYPE_SHA256: + DEBUG_TRACE(2, "Using SHA256\n"); + cdhash_sha256(cd, length, cdhash->cdhash); + cdhash->hash_type = CS_HASHTYPE_SHA256; + return true; + case CS_HASHTYPE_SHA384: + DEBUG_TRACE(2, "Using SHA384\n"); + cdhash_sha384(cd, length, cdhash->cdhash); + cdhash->hash_type = CS_HASHTYPE_SHA384; + return true; + } + ERROR("Unsupported hash type %d\n", cd->hashType); + return false; +} + +// Get the rank of a code directory. +static unsigned +cs_codedirectory_rank(CS_CodeDirectory *cd) { + // The supported hash types, ranked from least to most preferred. From XNU's + // bsd/kern/ubc_subr.c. + static uint32_t ranked_hash_types[] = { + CS_HASHTYPE_SHA1, + CS_HASHTYPE_SHA256_TRUNCATED, + CS_HASHTYPE_SHA256, + CS_HASHTYPE_SHA384, + }; + // Define the rank of the code directory as its index in the array plus one. + for (unsigned i = 0; i < sizeof(ranked_hash_types) / sizeof(ranked_hash_types[0]); i++) { + if (ranked_hash_types[i] == cd->hashType) { + return (i + 1); + } + } + return 0; +} + +// Compute the cdhash from a CS_SuperBlob. +static bool +cs_superblob_cdhash(CS_SuperBlob *sb, size_t size, void *cdhash) { + // Iterate through each index searching for the best code directory. + CS_CodeDirectory *best_cd = NULL; + unsigned best_cd_rank = 0; + size_t best_cd_size = 0; + uint32_t count = be32toh(sb->count); + for (size_t i = 0; i < count; i++) { + CS_BlobIndex *index = &sb->index[i]; + uint32_t type = be32toh(index->type); + uint32_t offset = be32toh(index->offset); + // Validate the offset. + if (offset > size) { + ERROR("CS_SuperBlob has out-of-bounds CS_BlobIndex\n"); + return false; + } + // Look for a code directory. + if (type == CSSLOT_CODEDIRECTORY || + (CSSLOT_ALTERNATE_CODEDIRECTORIES <= type && type < CSSLOT_ALTERNATE_CODEDIRECTORY_LIMIT)) { + CS_CodeDirectory *cd = (CS_CodeDirectory *)((uint8_t *)sb + offset); + size_t cd_size = cs_codedirectory_validate(cd, size - offset); + if (cd_size == 0) { + return false; + } + DEBUG_TRACE(2, "CS_CodeDirectory { hashType = %u }\n", cd->hashType); + // Rank the code directory to see if it's better than our previous best. + unsigned cd_rank = cs_codedirectory_rank(cd); + if (cd_rank > best_cd_rank) { + best_cd = cd; + best_cd_rank = cd_rank; + best_cd_size = cd_size; + } + } + } + // If we didn't find a code directory, error. + if (best_cd == NULL) { + ERROR("CS_SuperBlob does not have a code directory\n"); + return false; + } + // Hash the code directory. + return cs_codedirectory_cdhash(best_cd, best_cd_size, cdhash); +} + +// Compute the cdhash from a csblob. +static bool +csblob_cdhash(CS_GenericBlob *blob, size_t size, void *cdhash) { + // Make sure we at least have a CS_GenericBlob. + if (size < sizeof(*blob)) { + ERROR("CSBlob is too small\n"); + return false; + } + uint32_t magic = be32toh(blob->magic); + uint32_t length = be32toh(blob->length); + DEBUG_TRACE(2, "CS_GenericBlob { %08x, %u }, size = %zu\n", magic, length, size); + // Make sure the length is sensible. + if (length > size) { + ERROR("CSBlob has invalid length\n"); + return false; + } + // Handle the blob. + bool ok; + switch (magic) { + case CSMAGIC_EMBEDDED_SIGNATURE: + ok = cs_superblob_validate((CS_SuperBlob *)blob, length); + if (!ok) { + return false; + } + return cs_superblob_cdhash((CS_SuperBlob *)blob, length, cdhash); + case CSMAGIC_CODEDIRECTORY: + ok = cs_codedirectory_validate((CS_CodeDirectory *)blob, length); + if (!ok) { + return false; + } + return cs_codedirectory_cdhash((CS_CodeDirectory *)blob, length, cdhash); + } + ERROR("Unrecognized CSBlob magic 0x%08x\n", magic); + return false; +} + +// Compute the cdhash for a Mach-O file. +static bool +compute_cdhash_macho(const struct mach_header_64 *mh, const struct mach_header *mh32, size_t size, struct hashes *cdhash) { + // Find the code signature command. + const struct linkedit_data_command *cs_cmd = + macho_find_load_command(mh, mh32, size, LC_CODE_SIGNATURE, NULL); + if (cs_cmd == NULL) { + ERROR("No code signature\n"); + return false; + } + const uint8_t *cs_data, *cs_end; + // Check that the code signature is in-bounds. + if (mh != NULL) { + cs_data = (const uint8_t *)mh + swap(mh, NULL, cs_cmd->dataoff); + cs_end = cs_data + swap(mh, NULL, cs_cmd->datasize); + if (!((uint8_t *)mh < cs_data && cs_data < cs_end && cs_end <= (uint8_t *)mh + size)) { + ERROR("Invalid code signature\n"); + return false; + } + } else { + cs_data = (const uint8_t *)mh32 + swap(mh32, NULL, cs_cmd->dataoff); + cs_end = cs_data + swap(mh32, NULL, cs_cmd->datasize); + if (!((uint8_t *)mh32 < cs_data && cs_data < cs_end && cs_end <= (uint8_t *)mh32 + size)) { + ERROR("Invalid code signature\n"); + return false; + } + } + // Check that the code signature data looks correct. + return csblob_cdhash((CS_GenericBlob *)cs_data, cs_end - cs_data, cdhash); +} + +bool +compute_cdhash(const void *file, size_t size, struct hashes *cdhash) { + // Try to compute the cdhash for a Mach-O file. + const struct mach_header_64 *mh = file; + const struct mach_header *mh32 = file; + if (mh->magic == MH_MAGIC_64 || mh->magic == MH_CIGAM_64) { + mh32 = NULL; + } else { + mh = NULL; + } + + if (macho_identify(mh, mh32, size)) { + //if (!macho_validate(mh, mh32, size)) { + // ERROR("Bad Mach-O file\n"); + // return false; + //} + return compute_cdhash_macho(mh, mh32, size, cdhash); + } + // What is it? + ERROR("Unrecognized file format\n"); + return false; +} + +void +compute_cdhashes(const void *file, size_t size, struct cdhashes *h) { + const struct fat_header *fh = NULL; + if (*((uint32_t*)file) == FAT_MAGIC || *((uint32_t*)file) == FAT_CIGAM) + fh = file; + + if (fh != NULL) { + struct fat_arch *fa = (struct fat_arch *)(fh + 1); + h->h = malloc(sizeof(struct hashes) * be32toh(fh->nfat_arch)); + for (uint32_t i = 0; i < be32toh(fh->nfat_arch); i++) { + if (compute_cdhash(file + be32toh(fa->offset), be32toh(fa->size), &h->h[h->count])) { + h->count++; + } else { + // If any slice is not signed we will just skip the whole binary + h->count = 0; + return; + } + fa++; + } + } else { + h->h = malloc(sizeof(struct hashes)); + h->count = compute_cdhash(file, size, &h->h[0]); + } +} + +int +find_cdhash(const char *path, const struct stat *sb, struct cdhashes *h) { + int success = 0; + size_t fileoff = 0; + + int fd; + fd = open(path, O_RDONLY); + if (fd < 0) { + ERROR("Could not open \"%s\"\n", path); + goto fail_0; + } + size_t size = sb->st_size; + // Map the file into memory. + DEBUG_TRACE(2, "Mapping %s size %zu offset %zu\n", path, size, fileoff); + size -= fileoff; + uint8_t *file = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, fileoff); + if (file == MAP_FAILED) { + ERROR("Could not map \"%s\"\n", path); + goto fail_1; + } + DEBUG_TRACE(3, "file[0] = %lx\n", *(uint64_t *)file); + // Compute the cdhash. + compute_cdhashes(file, size, h); + success = true; + + munmap(file, size); +fail_1: + close(fd); +fail_0: + return success; +} diff --git a/machoparse/cdhash.h b/machoparse/cdhash.h new file mode 100644 index 0000000..6cc559a --- /dev/null +++ b/machoparse/cdhash.h @@ -0,0 +1,34 @@ +#ifndef BLANKET__AMFID__CDHASH_H_ +#define BLANKET__AMFID__CDHASH_H_ + +#include <stdbool.h> +#include <stdlib.h> +#include "cs_blobs.h" + +struct hashes { + uint32_t hash_type; + uint8_t cdhash[20]; +}; + +struct cdhashes { + int count; + struct hashes *h; +}; + +/* + * compute_cdhash + * + * Description: + * Compute the cdhash of a Mach-O file. + * + * Parameters: + * file The contents of the Mach-O file. + * size The size of the Mach-O file. + * cdhash out On return, contains the cdhash of the file. Must be + * CS_CDHASH_LEN bytes. + */ +//bool compute_cdhash(const void *file, size_t size, struct cdhash *cdhash); + +int find_cdhash(const char *path, const struct stat *sb, struct cdhashes *h); + +#endif diff --git a/machoparse/cs_blobs.h b/machoparse/cs_blobs.h new file mode 100644 index 0000000..562a8b4 --- /dev/null +++ b/machoparse/cs_blobs.h @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2017 Apple Computer, Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#ifndef _KERN_CODESIGN_H_ +#define _KERN_CODESIGN_H_ + +#include <stdint.h> + +/* code signing attributes of a process */ +#define CS_VALID 0x00000001 /* dynamically valid */ +#define CS_ADHOC 0x00000002 /* ad hoc signed */ +#define CS_GET_TASK_ALLOW 0x00000004 /* has get-task-allow entitlement */ +#define CS_INSTALLER 0x00000008 /* has installer entitlement */ + +#define CS_FORCED_LV 0x00000010 /* Library Validation required by Hardened System Policy */ +#define CS_INVALID_ALLOWED 0x00000020 /* (macOS Only) Page invalidation allowed by task port policy */ + +#define CS_HARD 0x00000100 /* don't load invalid pages */ +#define CS_KILL 0x00000200 /* kill process if it becomes invalid */ +#define CS_CHECK_EXPIRATION 0x00000400 /* force expiration checking */ +#define CS_RESTRICT 0x00000800 /* tell dyld to treat restricted */ + +#define CS_ENFORCEMENT 0x00001000 /* require enforcement */ +#define CS_REQUIRE_LV 0x00002000 /* require library validation */ +#define CS_ENTITLEMENTS_VALIDATED 0x00004000 /* code signature permits restricted entitlements */ +#define CS_NVRAM_UNRESTRICTED 0x00008000 /* has com.apple.rootless.restricted-nvram-variables.heritable entitlement */ + +#define CS_RUNTIME 0x00010000 /* Apply hardened runtime policies */ +#define CS_LINKER_SIGNED 0x00020000 /* Automatically signed by the linker */ + +#define CS_ALLOWED_MACHO (CS_ADHOC | CS_HARD | CS_KILL | CS_CHECK_EXPIRATION | \ + CS_RESTRICT | CS_ENFORCEMENT | CS_REQUIRE_LV | CS_RUNTIME | CS_LINKER_SIGNED) + +#define CS_EXEC_SET_HARD 0x00100000 /* set CS_HARD on any exec'ed process */ +#define CS_EXEC_SET_KILL 0x00200000 /* set CS_KILL on any exec'ed process */ +#define CS_EXEC_SET_ENFORCEMENT 0x00400000 /* set CS_ENFORCEMENT on any exec'ed process */ +#define CS_EXEC_INHERIT_SIP 0x00800000 /* set CS_INSTALLER on any exec'ed process */ + +#define CS_KILLED 0x01000000 /* was killed by kernel for invalidity */ +#define CS_NO_UNTRUSTED_HELPERS 0x02000000 /* kernel did not load a non-platform-binary dyld or Rosetta runtime */ +#define CS_DYLD_PLATFORM CS_NO_UNTRUSTED_HELPERS /* old name */ +#define CS_PLATFORM_BINARY 0x04000000 /* this is a platform binary */ +#define CS_PLATFORM_PATH 0x08000000 /* platform binary by the fact of path (osx only) */ + +#define CS_DEBUGGED 0x10000000 /* process is currently or has previously been debugged and allowed to run with invalid pages */ +#define CS_SIGNED 0x20000000 /* process has a signature (may have gone invalid) */ +#define CS_DEV_CODE 0x40000000 /* code is dev signed, cannot be loaded into prod signed code (will go away with rdar://problem/28322552) */ +#define CS_DATAVAULT_CONTROLLER 0x80000000 /* has Data Vault controller entitlement */ + +#define CS_ENTITLEMENT_FLAGS (CS_GET_TASK_ALLOW | CS_INSTALLER | CS_DATAVAULT_CONTROLLER | CS_NVRAM_UNRESTRICTED) + +/* executable segment flags */ + +#define CS_EXECSEG_MAIN_BINARY 0x1 /* executable segment denotes main binary */ +#define CS_EXECSEG_ALLOW_UNSIGNED 0x10 /* allow unsigned pages (for debugging) */ +#define CS_EXECSEG_DEBUGGER 0x20 /* main binary is debugger */ +#define CS_EXECSEG_JIT 0x40 /* JIT enabled */ +#define CS_EXECSEG_SKIP_LV 0x80 /* OBSOLETE: skip library validation */ +#define CS_EXECSEG_CAN_LOAD_CDHASH 0x100 /* can bless cdhash for execution */ +#define CS_EXECSEG_CAN_EXEC_CDHASH 0x200 /* can execute blessed cdhash */ + +/* + * Magic numbers used by Code Signing + */ +enum { + CSMAGIC_REQUIREMENT = 0xfade0c00, /* single Requirement blob */ + CSMAGIC_REQUIREMENTS = 0xfade0c01, /* Requirements vector (internal requirements) */ + CSMAGIC_CODEDIRECTORY = 0xfade0c02, /* CodeDirectory blob */ + CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0, /* embedded form of signature data */ + CSMAGIC_EMBEDDED_SIGNATURE_OLD = 0xfade0b02, /* XXX */ + CSMAGIC_EMBEDDED_ENTITLEMENTS = 0xfade7171, /* embedded entitlements */ + CSMAGIC_EMBEDDED_DER_ENTITLEMENTS = 0xfade7172, /* embedded DER encoded entitlements */ + CSMAGIC_DETACHED_SIGNATURE = 0xfade0cc1, /* multi-arch collection of embedded signatures */ + CSMAGIC_BLOBWRAPPER = 0xfade0b01, /* CMS Signature, among other things */ + + CS_SUPPORTSSCATTER = 0x20100, + CS_SUPPORTSTEAMID = 0x20200, + CS_SUPPORTSCODELIMIT64 = 0x20300, + CS_SUPPORTSEXECSEG = 0x20400, + CS_SUPPORTSRUNTIME = 0x20500, + CS_SUPPORTSLINKAGE = 0x20600, + + CSSLOT_CODEDIRECTORY = 0, /* slot index for CodeDirectory */ + CSSLOT_INFOSLOT = 1, + CSSLOT_REQUIREMENTS = 2, + CSSLOT_RESOURCEDIR = 3, + CSSLOT_APPLICATION = 4, + CSSLOT_ENTITLEMENTS = 5, + CSSLOT_DER_ENTITLEMENTS = 7, + + CSSLOT_ALTERNATE_CODEDIRECTORIES = 0x1000, /* first alternate CodeDirectory, if any */ + CSSLOT_ALTERNATE_CODEDIRECTORY_MAX = 5, /* max number of alternate CD slots */ + CSSLOT_ALTERNATE_CODEDIRECTORY_LIMIT = CSSLOT_ALTERNATE_CODEDIRECTORIES + CSSLOT_ALTERNATE_CODEDIRECTORY_MAX, /* one past the last */ + + CSSLOT_SIGNATURESLOT = 0x10000, /* CMS Signature */ + CSSLOT_IDENTIFICATIONSLOT = 0x10001, + CSSLOT_TICKETSLOT = 0x10002, + + CSTYPE_INDEX_REQUIREMENTS = 0x00000002, /* compat with amfi */ + CSTYPE_INDEX_ENTITLEMENTS = 0x00000005, /* compat with amfi */ + + CS_HASHTYPE_SHA1 = 1, + CS_HASHTYPE_SHA256 = 2, + CS_HASHTYPE_SHA256_TRUNCATED = 3, + CS_HASHTYPE_SHA384 = 4, + + CS_SHA1_LEN = 20, + CS_SHA256_LEN = 32, + CS_SHA256_TRUNCATED_LEN = 20, + + CS_CDHASH_LEN = 20, /* always - larger hashes are truncated */ + CS_HASH_MAX_SIZE = 48, /* max size of the hash we'll support */ + +/* + * Currently only to support Legacy VPN plugins, and Mac App Store + * but intended to replace all the various platform code, dev code etc. bits. + */ + CS_SIGNER_TYPE_UNKNOWN = 0, + CS_SIGNER_TYPE_LEGACYVPN = 5, + CS_SIGNER_TYPE_MAC_APP_STORE = 6, + + CS_SUPPL_SIGNER_TYPE_UNKNOWN = 0, + CS_SUPPL_SIGNER_TYPE_TRUSTCACHE = 7, + CS_SUPPL_SIGNER_TYPE_LOCAL = 8, +}; + +#define KERNEL_HAVE_CS_CODEDIRECTORY 1 +#define KERNEL_CS_CODEDIRECTORY_HAVE_PLATFORM 1 + +/* + * C form of a CodeDirectory. + */ +typedef struct __CodeDirectory { + uint32_t magic; /* magic number (CSMAGIC_CODEDIRECTORY) */ + uint32_t length; /* total length of CodeDirectory blob */ + uint32_t version; /* compatibility version */ + uint32_t flags; /* setup and mode flags */ + uint32_t hashOffset; /* offset of hash slot element at index zero */ + uint32_t identOffset; /* offset of identifier string */ + uint32_t nSpecialSlots; /* number of special hash slots */ + uint32_t nCodeSlots; /* number of ordinary (code) hash slots */ + uint32_t codeLimit; /* limit to main image signature range */ + uint8_t hashSize; /* size of each hash in bytes */ + uint8_t hashType; /* type of hash (cdHashType* constants) */ + uint8_t platform; /* platform identifier; zero if not platform binary */ + uint8_t pageSize; /* log2(page size in bytes); 0 => infinite */ + uint32_t spare2; /* unused (must be zero) */ + + char end_earliest[0]; + + /* Version 0x20100 */ + uint32_t scatterOffset; /* offset of optional scatter vector */ + char end_withScatter[0]; + + /* Version 0x20200 */ + uint32_t teamOffset; /* offset of optional team identifier */ + char end_withTeam[0]; + + /* Version 0x20300 */ + uint32_t spare3; /* unused (must be zero) */ + uint64_t codeLimit64; /* limit to main image signature range, 64 bits */ + char end_withCodeLimit64[0]; + + /* Version 0x20400 */ + uint64_t execSegBase; /* offset of executable segment */ + uint64_t execSegLimit; /* limit of executable segment */ + uint64_t execSegFlags; /* executable segment flags */ + char end_withExecSeg[0]; + /* Version 0x20500 */ + uint32_t runtime; + uint32_t preEncryptOffset; + char end_withPreEncryptOffset[0]; + + /* Version 0x20600 */ + uint8_t linkageHashType; + uint8_t linkageTruncated; + uint16_t spare4; + uint32_t linkageOffset; + uint32_t linkageSize; + char end_withLinkage[0]; + + + /* followed by dynamic content as located by offset fields above */ +} CS_CodeDirectory +__attribute__ ((aligned(1))); + +/* + * Structure of an embedded-signature SuperBlob + */ + +typedef struct __BlobIndex { + uint32_t type; /* type of entry */ + uint32_t offset; /* offset of entry */ +} CS_BlobIndex +__attribute__ ((aligned(1))); + +typedef struct __SC_SuperBlob { + uint32_t magic; /* magic number */ + uint32_t length; /* total length of SuperBlob */ + uint32_t count; /* number of index entries following */ + CS_BlobIndex index[]; /* (count) entries */ + /* followed by Blobs in no particular order as indicated by offsets in index */ +} CS_SuperBlob +__attribute__ ((aligned(1))); + +#define KERNEL_HAVE_CS_GENERICBLOB 1 +typedef struct __SC_GenericBlob { + uint32_t magic; /* magic number */ + uint32_t length; /* total length of blob */ + char data[]; +} CS_GenericBlob +__attribute__ ((aligned(1))); + +typedef struct __SC_Scatter { + uint32_t count; // number of pages; zero for sentinel (only) + uint32_t base; // first page number + uint64_t targetOffset; // offset in target + uint64_t spare; // reserved +} SC_Scatter +__attribute__ ((aligned(1))); + + +#endif /* _KERN_CODESIGN_H */ diff --git a/machoparse/macho.h b/machoparse/macho.h new file mode 100644 index 0000000..858fded --- /dev/null +++ b/machoparse/macho.h @@ -0,0 +1,73 @@ +#include <sys/types.h> + +typedef int cpu_type_t; +typedef int cpu_subtype_t; + +struct mach_header { + uint32_t magic; + cpu_type_t cputype; + cpu_subtype_t cpusubtype; + uint32_t filetype; + uint32_t ncmds; + uint32_t sizeofcmds; + uint32_t flags; +}; + + +#define MH_MAGIC 0xfeedface +#define MH_CIGAM 0xcefaedfe + +struct mach_header_64 { + uint32_t magic; + cpu_type_t cputype; + cpu_subtype_t cpusubtype; + uint32_t filetype; + uint32_t ncmds; + uint32_t sizeofcmds; + uint32_t flags; + uint32_t reserved; +}; + +#define MH_MAGIC_64 0xfeedfacf +#define MH_CIGAM_64 0xcffaedfe + +struct load_command { + uint32_t cmd; + uint32_t cmdsize; +}; + +#define LC_CODE_SIGNATURE 0x1d + +struct linkedit_data_command { + uint32_t cmd; + uint32_t cmdsize; + uint32_t dataoff; + uint32_t datasize; +}; + +struct fat_header { + uint32_t magic; + uint32_t nfat_arch; +}; + +#define FAT_MAGIC 0xcafebabe +#define FAT_CIGAM 0xbebafeca + +struct fat_arch { + cpu_type_t cputype; + cpu_subtype_t cpusubtype; + uint32_t offset; + uint32_t size; + uint32_t align; +}; + +#define FAT_MAGIC_64 0xcafebabf +#define FAT_CIGAM_64 0xbfbafeca + +struct fat_arch_64 { + cpu_type_t cputype; + cpu_subtype_t cpusubtype; + uint32_t offset; + uint32_t size; + uint32_t align; +}; |