summaryrefslogtreecommitdiff
path: root/view/kernelcache/core/transformers/libimg4
diff options
context:
space:
mode:
authorkat <kat@vector35.com>2025-03-19 09:12:52 -0400
committerkat <kat@vector35.com>2025-03-19 15:04:23 -0400
commit7f3f394c8bb50c987a5237bc74aa503486571058 (patch)
tree9f5ada34f6226562b79f0dce70bfebab124a1bdb /view/kernelcache/core/transformers/libimg4
parente6d4d38e2a5dc66d3c8004cc917bc08e39337482 (diff)
Add iOS/macOS MH_FILESET KernelCache View and loader.
This loader is inspired by/based on our dyld_shared_cache loader, following the same design language. It targets primarily the latest kernels, but should support any with the MH_FILESET format. It allows you to decide which images you would like to map in (the kernel itself included), resulting in targeted analysis when you may only need to load a singular image. It also supports dropping in compressed KernelCaches, directly from the ipsw. This is an early solution and we have many more changes and improvements planned. We look forward to your feedback
Diffstat (limited to 'view/kernelcache/core/transformers/libimg4')
-rw-r--r--view/kernelcache/core/transformers/libimg4/img4.c383
-rw-r--r--view/kernelcache/core/transformers/libimg4/img4.h132
2 files changed, 515 insertions, 0 deletions
diff --git a/view/kernelcache/core/transformers/libimg4/img4.c b/view/kernelcache/core/transformers/libimg4/img4.c
new file mode 100644
index 00000000..9ad91049
--- /dev/null
+++ b/view/kernelcache/core/transformers/libimg4/img4.c
@@ -0,0 +1,383 @@
+/*
+ * pongoOS - https://checkra.in
+ *
+ * Copyright (C) 2021 checkra1n team
+ *
+ * This file is part of pongoOS.
+ *
+ * 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.
+ *
+ */
+#include <stdlib.h>
+#include <stddef.h>
+#include "img4.h"
+
+// ==================== ==================== ==================== Data ==================== ==================== ====================
+
+const DERItemSpec DERImg4ItemSpecs[] =
+ {
+ {0 * sizeof(DERItem), ASN1_IA5_STRING, 0}, // IMG4
+ {1 * sizeof(DERItem), ASN1_CONSTR_SEQUENCE, DER_DEC_SAVE_DER | DER_ENC_WRITE_DER}, // Payload
+ {2 * sizeof(DERItem), ASN1_CONSTR_CONT | 0, DER_DEC_OPTIONAL}, // Manifest
+ {3 * sizeof(DERItem), ASN1_CONSTR_CONT | 1, DER_DEC_OPTIONAL}, // RestoreInfo
+ };
+const DERItemSpec DERImg4PayloadItemSpecs[] =
+ {
+ {offsetof(Img4Payload, magic), ASN1_IA5_STRING, 0}, // IM4P
+ {offsetof(Img4Payload, type), ASN1_IA5_STRING, 0}, // Type
+ {offsetof(Img4Payload, version), ASN1_IA5_STRING, 0}, // Version
+ {offsetof(Img4Payload, payload), ASN1_OCTET_STRING, 0}, // Payload
+ {offsetof(Img4Payload, keybag), ASN1_OCTET_STRING, DER_DEC_OPTIONAL}, // Keybag
+ {offsetof(Img4Payload, compression), ASN1_CONSTR_SEQUENCE, DER_DEC_OPTIONAL}, // Compression
+ };
+const DERItemSpec DERImg4ManifestItemSpecs[] =
+ {
+ {offsetof(Img4Manifest, magic), ASN1_IA5_STRING, 0}, // IM4M
+ {offsetof(Img4Manifest, zero), ASN1_INTEGER, 0}, // 0
+ {offsetof(Img4Manifest, properties), ASN1_CONSTR_SET,
+ DER_DEC_SAVE_DER | DER_ENC_WRITE_DER}, // Properties
+ {offsetof(Img4Manifest, signature), ASN1_OCTET_STRING, 0}, // Signature
+ {offsetof(Img4Manifest, certificates), ASN1_CONSTR_SEQUENCE, 0}, // Certificates
+ };
+const DERItemSpec DERImg4RestoreInfoItemSpecs[] =
+ {
+ {offsetof(Img4RestoreInfo, magic), ASN1_IA5_STRING, 0}, // IM4R
+ {offsetof(Img4RestoreInfo, nonce), ASN1_CONSTR_SET, 0}, // Nonce
+ };
+const DERItemSpec DERImg4NonceItemSpecs[] =
+ {
+ {0 * sizeof(DERItem), ASN1_IA5_STRING, 0}, // BNCN
+ {1 * sizeof(DERItem), ASN1_OCTET_STRING, 0}, // Nonce
+ };
+const DERItemSpec DERImg4CompressionItemSpecs[] =
+ {
+ {0 * sizeof(DERItem), ASN1_INTEGER, 0},
+ {1 * sizeof(DERItem), ASN1_INTEGER, 0},
+ };
+
+// ==================== ==================== ==================== Code ==================== ==================== ====================
+
+DERReturn DERImg4DecodeFindInSequence(DERByte *nextItem, DERByte *end, DERTag tag, DERItem *out) {
+ DERDecodedInfo decoded;
+ DERSequence seq = {nextItem, end};
+ do {
+ DERReturn ret = DERDecodeSeqNext(&seq, &decoded);
+ if (ret != DR_Success) {
+ return ret;
+ }
+ } while (decoded.tag != tag);
+ *out = decoded.content;
+ return DR_Success;
+}
+
+DERReturn DERImg4DecodeContentFindItemWithTag(const DERItem *der, DERTag tag, DERItem *out) {
+ DERSequence seq;
+ DERReturn ret = DERDecodeSeqContentInit(der, &seq);
+ if (ret != DR_Success) {
+ return ret;
+ }
+ return DERImg4DecodeFindInSequence(seq.nextItem, seq.end, tag, out);
+}
+
+DERReturn DERImg4DecodeTagCompare(const DERItem *der, uint32_t name) {
+ if (der->length < 4) {
+ return -1;
+ }
+ if (der->length > 4) {
+ return 1;
+ }
+ uint32_t value;
+ if (DERParseInteger(der, &value) != DR_Success) {
+ return -2;
+ }
+ if (value < name) {
+ return -1;
+ }
+ if (value > name) {
+ return 1;
+ }
+ return 0;
+}
+
+DERReturn DERImg4Decode(const DERItem *der, DERItem *items) {
+ DERReturn ret;
+ DERDecodedInfo decoded;
+ if (!der || !items) {
+ return DR_ParamErr;
+ }
+ ret = DERDecodeItem(der, &decoded);
+ if (ret != DR_Success) {
+ return ret;
+ }
+ if (decoded.tag != ASN1_CONSTR_SEQUENCE) {
+ return DR_UnexpectedTag;
+ }
+ if (der->data + der->length != decoded.content.data + decoded.content.length) {
+ return DR_BufOverflow;
+ }
+ ret = DERParseSequenceContent(&decoded.content, sizeof(DERImg4ItemSpecs) / sizeof(DERImg4ItemSpecs[0]),
+ DERImg4ItemSpecs, items, 0);
+ if (ret != DR_Success) {
+ return ret;
+ }
+ if (DERImg4DecodeTagCompare(&items[0], 'IMG4') != 0) {
+ return DR_UnexpectedTag;
+ }
+ return DR_Success;
+}
+DERReturn DERImg4DecodePayload(const DERItem *der, Img4Payload *payload) {
+ if (!der || !payload) {
+ return DR_ParamErr;
+ }
+ DERReturn ret = DERParseSequence(der, sizeof(DERImg4PayloadItemSpecs) / sizeof(DERImg4PayloadItemSpecs[0]),
+ DERImg4PayloadItemSpecs, payload, 0);
+ if (ret != DR_Success) {
+ return ret;
+ }
+ if (DERImg4DecodeTagCompare(&payload->magic, 'IM4P') != 0) {
+ return DR_UnexpectedTag;
+ }
+ if (!payload->compression.data) {
+ return DR_Success;
+ }
+ uint32_t val = -1;
+
+ DERItem items[2];
+ ret = DERParseSequenceContent(&payload->compression,
+ sizeof(DERImg4CompressionItemSpecs) / sizeof(DERImg4CompressionItemSpecs[0]),
+ DERImg4CompressionItemSpecs, items, 0);
+ if (ret != DR_Success) {
+ return ret;
+ }
+ ret = DERParseInteger(&items[0], &val);
+ if (ret != DR_Success) {
+ return ret;
+ }
+ if (val >= 2) {
+ return DR_ParamErr;
+ }
+ return DR_Success;
+}
+
+DERReturn DERImg4DecodeManifest(const DERItem *der, Img4Manifest *manifest) {
+ if (!der || !manifest) {
+ return DR_ParamErr;
+ }
+ if (!der->data || !der->length) {
+ return DR_Success;
+ }
+ DERReturn ret = DERParseSequence(der, sizeof(DERImg4ManifestItemSpecs) / sizeof(DERImg4ManifestItemSpecs[0]),
+ DERImg4ManifestItemSpecs, manifest, 0);
+ if (ret != DR_Success) {
+ return ret;
+ }
+ if (DERImg4DecodeTagCompare(&manifest->magic, 'IM4M') != 0) {
+ return DR_UnexpectedTag;
+ }
+ uint32_t zero;
+ ret = DERParseInteger(&manifest->zero, &zero);
+ if (ret != DR_Success) {
+ return ret;
+ }
+ if (zero != 0) {
+ return DR_UnexpectedTag;
+ }
+ return DR_Success;
+}
+
+DERReturn DERImg4DecodeRestoreInfo(const DERItem *der, Img4RestoreInfo *restoreInfo) {
+ if (!der) {
+ return DR_Success;
+ }
+ if (!restoreInfo) {
+ return DR_ParamErr;
+ }
+ if (!der->data || !der->length) {
+ return DR_Success;
+ }
+ DERReturn ret = DERParseSequence(der, sizeof(DERImg4RestoreInfoItemSpecs) / sizeof(DERImg4RestoreInfoItemSpecs[0]),
+ DERImg4RestoreInfoItemSpecs, restoreInfo, 0);
+ if (ret != DR_Success) {
+ return ret;
+ }
+ if (DERImg4DecodeTagCompare(&restoreInfo->magic, 'IM4R') != 0) {
+ return DR_UnexpectedTag;
+ }
+ return DR_Success;
+}
+
+DERReturn DERImg4DecodeFindProperty(const DERItem *der, DERTag ktag, DERTag vtag, Img4Property *prop) {
+ DERItem key;
+ DERReturn ret = DERImg4DecodeContentFindItemWithTag(der, ktag, &key);
+ if (ret != DR_Success) {
+ return ret;
+ }
+ DERItemSpec spec[] =
+ {
+ {0 * sizeof(Img4Property), ASN1_IA5_STRING, 0},
+ {1 * sizeof(Img4Property), vtag, 0},
+ };
+ ret = DERParseSequence(&key, sizeof(spec) / sizeof(DERItemSpec), spec, prop, 0);
+ if (ret != DR_Success) {
+ return ret;
+ }
+ uint32_t tag;
+ ret = DERParseInteger(&prop[0].content, &tag);
+ if (ret != DR_Success) {
+ return ret;
+ }
+ if ((ASN1_CONSTR_PRIVATE | tag) != ktag) {
+ return DR_UnexpectedTag;
+ }
+ prop[0].tag = ASN1_CONSTR_PRIVATE | ktag;
+ prop[1].tag = vtag;
+ return DR_Success;
+}
+
+DERReturn Img4DecodeGetPayload(const Img4 *img4, DERItem *item) {
+ if (!img4 || !item) {
+ return DR_ParamErr;
+ }
+ if (!img4->payload.payload.data || !img4->payload.payload.length) {
+ return DR_EndOfSequence;
+ }
+ *item = img4->payload.payload;
+ return DR_Success;
+}
+
+DERReturn Img4DecodeGetPayloadType(const Img4 *img4, uint32_t *type) {
+ if (!img4 || !type) {
+ return DR_ParamErr;
+ }
+ if (!img4->payload.payload.data || !img4->payload.payload.length) {
+ return DR_EndOfSequence;
+ }
+ return DERParseInteger(&img4->payload.type, type);
+}
+
+DERReturn Img4DecodeGetPayloadKeybag(const Img4 *img4, DERItem *kbag) {
+ if (!img4 || !kbag) {
+ return DR_ParamErr;
+ }
+ if (!img4->payload.payload.data || !img4->payload.payload.length) {
+ return DR_EndOfSequence;
+ }
+ *kbag = img4->payload.keybag;
+ return DR_Success;
+}
+
+DERReturn Img4DecodeInit(const DERByte *data, DERSize length, Img4 *img4) {
+ DERReturn ret;
+ if (!data || !img4) {
+ return DR_ParamErr;
+ }
+ DERItem der = {.data = (DERByte *) data, .length = length};
+ DERItem items[4] = {0};
+ memset(img4, 0, sizeof(Img4));
+ ret = DERImg4Decode(&der, items);
+ if (ret != DR_Success) {
+ return ret;
+ }
+ ret = DERImg4DecodePayload(&items[1], &img4->payload);
+ if (ret != DR_Success) {
+ return ret;
+ }
+ ret = DERImg4DecodeManifest(&items[2], &img4->manifest);
+ if (ret != DR_Success) {
+ return ret;
+ }
+ ret = DERImg4DecodeRestoreInfo(&items[3], &img4->restoreInfo);
+ if (ret != DR_Success) {
+ return ret;
+ }
+ img4->payloadRaw = items[1];
+ img4->manifestRaw = items[2];
+ return DR_Success;
+}
+
+DERReturn Img4Encode(DERItem *der, const DERItem *items) {
+ return Img4EncodeSequence(ASN1_CONSTR_SEQUENCE, items, sizeof(DERImg4ItemSpecs) / sizeof(DERImg4ItemSpecs[0]),
+ DERImg4ItemSpecs, der);
+}
+
+DERReturn Img4EncodeRestoreInfo(DERItem *der, void *bytes, size_t len) {
+ Img4RestoreInfo restoreInfo =
+ {
+ .magic = {.data = (DERByte *) "IM4R", .length = 4},
+ };
+ DERItem bncn;
+ DERItem items[] =
+ {
+ {.data = (DERByte *) "BNCN", .length = 4},
+ {.data = bytes, .length = len},
+ };
+ DERReturn ret = Img4EncodeSequence(ASN1_CONSTR_SEQUENCE, items,
+ sizeof(DERImg4NonceItemSpecs) / sizeof(DERImg4NonceItemSpecs[0]),
+ DERImg4NonceItemSpecs, &bncn);
+ if (ret == DR_Success) {
+ DERSize inOutLen = 20 + bncn.length;
+ DERByte *buf = malloc(inOutLen);
+ if (!buf) {
+ ret = -1;
+ } else {
+ ret = DEREncodeItem(ASN1_CONSTR_PRIVATE | 'BNCN', bncn.length, bncn.data, buf, &inOutLen);
+ if (ret == DR_Success) {
+ restoreInfo.nonce.data = buf;
+ restoreInfo.nonce.length = inOutLen;
+ ret = Img4EncodeSequence(ASN1_CONSTR_SEQUENCE, &restoreInfo,
+ sizeof(DERImg4RestoreInfoItemSpecs) / sizeof(DERImg4RestoreInfoItemSpecs[0]),
+ DERImg4RestoreInfoItemSpecs, der);
+ }
+ free(buf);
+ }
+ free(bncn.data);
+ }
+ return ret;
+}
+
+DERReturn
+Img4EncodeSequence(DERTag tag, const void *src, DERShort numItems, const DERItemSpec *itemSpecs, DERItem *der) {
+ if (!tag || !src || !numItems || !itemSpecs || !der) {
+ return DR_ParamErr;
+ }
+ DERSize inOutLen = 20;
+ for (DERShort i = 0; i < numItems; ++i) {
+ const DERItemSpec *spec = &itemSpecs[i];
+ const DERItem *item = (const DERItem *) ((uintptr_t) src + spec->offset);
+ if (spec->options & DER_ENC_WRITE_DER) {
+ inOutLen += item->length;
+ } else if (item->length == 0 && (spec->options & DER_DEC_OPTIONAL)) {
+ // Skip
+ } else {
+ inOutLen += item->length + 20; /* 1+9 tag, 1+8 size, 1 null pad */
+ }
+ }
+ DERByte *buf = malloc(inOutLen);
+ if (!buf) {
+ return -1;
+ }
+ DERReturn ret = DEREncodeSequence(tag, src, numItems, itemSpecs, buf, &inOutLen);
+ if (ret == DR_Success) {
+ der->data = buf;
+ der->length = inOutLen;
+ } else {
+ free(buf);
+ }
+ return ret;
+} \ No newline at end of file
diff --git a/view/kernelcache/core/transformers/libimg4/img4.h b/view/kernelcache/core/transformers/libimg4/img4.h
new file mode 100644
index 00000000..81bcbb02
--- /dev/null
+++ b/view/kernelcache/core/transformers/libimg4/img4.h
@@ -0,0 +1,132 @@
+/*
+ * pongoOS - https://checkra.in
+ *
+ * Copyright (C) 2021 checkra1n team
+ *
+ * This file is part of pongoOS.
+ *
+ * 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.
+ *
+ */
+#include <stdbool.h>
+#include <stdint.h>
+#include "libDER/libDER_config.h"
+#include "libDER/asn1Types.h" // This include MUST come after libDER_config.h
+#include "libDER/libDER.h"
+#include "libDER/DER_Decode.h"
+#include "libDER/DER_Encode.h"
+
+#if !DER_MULTIBYTE_TAGS
+# error "DER_MULTIBYTE_TAGS not set"
+#endif
+#if DER_TAG_SIZE != 8
+# error "DER_TAG_SIZE != 8"
+#endif
+
+// ==================== ==================== ==================== Constants ==================== ==================== ====================
+
+#define ASN1_CONSTR_CONT (ASN1_CONSTRUCTED | ASN1_CONTEXT_SPECIFIC)
+#define ASN1_CONSTR_PRIVATE (ASN1_CONSTRUCTED | ASN1_PRIVATE)
+
+// ==================== ==================== ==================== Types ==================== ==================== ====================
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+typedef struct {
+ DERItem content;
+ DERTag tag;
+} Img4Property;
+
+typedef struct {
+ DERItem magic;
+ DERItem type;
+ DERItem version;
+ DERItem payload;
+ DERItem keybag;
+ DERItem compression;
+ uint8_t hash[0x30];
+} Img4Payload;
+
+typedef struct {
+ DERItem magic;
+ DERItem zero;
+ DERItem properties;
+ DERItem signature;
+ DERItem certificates;
+ DERItem embedded;
+ uint8_t full_hash[0x30];
+ uint8_t prop_hash[0x30];
+} Img4Manifest;
+
+typedef struct {
+ DERItem magic;
+ DERItem nonce;
+} Img4RestoreInfo;
+
+typedef struct {
+ bool payloadHashValid;
+ bool manifestHashValid;
+ DERItem payloadRaw;
+ DERItem manifestRaw;
+ DERItem manb;
+ DERItem manp;
+ DERItem objp;
+ Img4Payload payload;
+ Img4Manifest manifest;
+ Img4RestoreInfo restoreInfo;
+} Img4;
+
+// ==================== ==================== ==================== Functions ==================== ==================== ====================
+
+DERReturn DERImg4DecodeFindInSequence(DERByte *nextItem, DERByte *end, DERTag tag, DERItem *out);
+
+DERReturn DERImg4DecodeContentFindItemWithTag(const DERItem *der, DERTag tag, DERItem *out);
+
+DERReturn DERImg4DecodeTagCompare(const DERItem *der, uint32_t name);
+
+DERReturn DERImg4Decode(const DERItem *der, DERItem *items);
+
+DERReturn DERImg4DecodePayload(const DERItem *der, Img4Payload *payload);
+
+DERReturn DERImg4DecodeManifest(const DERItem *der, Img4Manifest *manifest);
+
+DERReturn DERImg4DecodeRestoreInfo(const DERItem *der, Img4RestoreInfo *restoreInfo);
+
+DERReturn DERImg4DecodeFindProperty(const DERItem *der, DERTag ktag, DERTag vtag, Img4Property *prop);
+
+DERReturn Img4DecodeGetPayload(const Img4 *img4, DERItem *item);
+
+DERReturn Img4DecodeGetPayloadType(const Img4 *img4, uint32_t *type);
+
+DERReturn Img4DecodeGetPayloadKeybag(const Img4 *img4, DERItem *kbag);
+
+DERReturn Img4DecodeInit(const DERByte *data, DERSize length, Img4 *img4);
+
+DERReturn Img4Encode(DERItem *der, const DERItem *items);
+
+DERReturn Img4EncodeRestoreInfo(DERItem *der, void *bytes, size_t len);
+
+DERReturn
+Img4EncodeSequence(DERTag tag, const void *src, DERShort numItems, const DERItemSpec *itemSpecs, DERItem *der);
+
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file