aboutsummaryrefslogtreecommitdiffstats
path: root/machoparse
diff options
context:
space:
mode:
Diffstat (limited to 'machoparse')
-rw-r--r--machoparse/LICENSE19
-rw-r--r--machoparse/cdhash.c455
-rw-r--r--machoparse/cdhash.h34
-rw-r--r--machoparse/cs_blobs.h248
-rw-r--r--machoparse/macho.h73
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;
+};