├── lib ├── libchoma.a └── ios │ ├── libchoma.a │ └── MobileInBoxUpdate.tbd ├── .gitignore ├── include └── choma │ ├── Base64.h │ ├── Host.h │ ├── BufferedStream.h │ ├── MachOLoadCommand.h │ ├── FileStream.h │ ├── Util.h │ ├── FAT.h │ ├── CodeDirectory.h │ ├── PatchFinder_arm64.h │ ├── PatchFinder.h │ ├── MemoryStream.h │ ├── MachO.h │ ├── CSBlob.h │ ├── arm64.h │ └── MachOByteOrder.h ├── Makefile ├── LICENSE.md ├── README.md ├── main.c └── CoreTrust.h /lib/libchoma.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfiecg24/coretrust_cli/HEAD/lib/libchoma.a -------------------------------------------------------------------------------- /lib/ios/libchoma.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alfiecg24/coretrust_cli/HEAD/lib/ios/libchoma.a -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore all 2 | * 3 | 4 | # Unignore all with extensions 5 | !*.* 6 | 7 | # Unignore Makefile 8 | !Makefile 9 | 10 | # Unignore all dirs 11 | !*/ 12 | 13 | nocommit-* 14 | *.dylib 15 | .DS_Store -------------------------------------------------------------------------------- /include/choma/Base64.h: -------------------------------------------------------------------------------- 1 | #ifndef BASE64_H 2 | #define BASE64_H 3 | 4 | #include 5 | #include 6 | 7 | char *base64_encode(const unsigned char *data, 8 | size_t input_length, 9 | size_t *output_length); 10 | 11 | #endif // BASE64_H -------------------------------------------------------------------------------- /include/choma/Host.h: -------------------------------------------------------------------------------- 1 | #ifndef HOST_H 2 | #define HOST_H 3 | 4 | #include "FAT.h" 5 | 6 | #define CPU_SUBTYPE_ARM64E_ABI_V2 0x80000000 7 | 8 | int host_get_cpu_information(cpu_type_t *cputype, cpu_subtype_t *cpusubtype); 9 | 10 | // Retrieve the preferred MachO slice from a FAT 11 | // Preferred slice as in the slice that the kernel would use when loading the file 12 | MachO *fat_find_preferred_slice(FAT *fat); 13 | 14 | #endif // HOST_H -------------------------------------------------------------------------------- /include/choma/BufferedStream.h: -------------------------------------------------------------------------------- 1 | #ifndef BUFFERED_STREAM_H 2 | #define BUFFERED_STREAM_H 3 | 4 | #include "MemoryStream.h" 5 | #include 6 | 7 | #define BUFFERED_STREAM_FLAG_AUTO_EXPAND (1 << 0) 8 | 9 | typedef struct BufferedStreamContext { 10 | uint8_t *buffer; 11 | size_t bufferSize; 12 | uint32_t subBufferStart; 13 | size_t subBufferSize; 14 | } BufferedStreamContext; 15 | 16 | MemoryStream *buffered_stream_init_from_buffer_nocopy(void *buffer, size_t bufferSize, uint32_t flags); 17 | MemoryStream *buffered_stream_init_from_buffer(void *buffer, size_t bufferSize, uint32_t flags); 18 | 19 | #endif // BUFFERED_STREAM_H -------------------------------------------------------------------------------- /include/choma/MachOLoadCommand.h: -------------------------------------------------------------------------------- 1 | #ifndef MACHO_LOAD_COMMAND_H 2 | #define MACHO_LOAD_COMMAND_H 3 | 4 | #include 5 | #include "MachO.h" 6 | #include "FileStream.h" 7 | #include "MachOByteOrder.h" 8 | #include "CSBlob.h" 9 | 10 | // Convert load command to load command name 11 | char *load_command_to_string(int loadCommand); 12 | void update_segment_command_64(MachO *macho, const char *segmentName, uint64_t vmaddr, uint64_t vmsize, uint64_t fileoff, uint64_t filesize); 13 | void update_lc_code_signature(MachO *macho, uint64_t size); 14 | int update_load_commands_for_coretrust_bypass(MachO *macho, CS_SuperBlob *superblob, uint64_t originalCodeSignatureSize, uint64_t originalMachOSize); 15 | 16 | #endif // MACHO_LOAD_COMMAND_H -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC = xcrun clang 2 | LDID = ldid -S 3 | SDK_PATH_MACOS := $(shell xcrun --sdk macosx --show-sdk-path) 4 | SDK_PATH_IOS := $(shell xcrun --sdk iphoneos --show-sdk-path) 5 | CFLAGS = -Iinclude 6 | LDFLAGS = -Llib 7 | LDFLAGS_IOS = -Llib/ios 8 | LIBS = -lchoma 9 | 10 | .PHONY: all clean 11 | 12 | all: dirs macos ios 13 | 14 | dirs: 15 | mkdir -p output/ios 16 | 17 | macos: main.c 18 | $(CC) -isysroot $(SDK_PATH_MACOS) $^ -o output/coretrust_cli -Wl,-force_load,lib/AuthKit.tbd $(CFLAGS) $(LDFLAGS) $(LIBS) 19 | $(LDID) output/coretrust_cli 20 | 21 | ios: main.c 22 | $(CC) -arch arm64 -isysroot $(SDK_PATH_IOS) $^ -o output/ios/coretrust_cli -Wl,-force_load,lib/ios/MobileInBoxUpdate.tbd $(CFLAGS) $(LDFLAGS_IOS) $(LIBS) 23 | $(LDID) output/ios/coretrust_cli 24 | 25 | clean: 26 | @rm -rf output -------------------------------------------------------------------------------- /include/choma/FileStream.h: -------------------------------------------------------------------------------- 1 | #ifndef FILE_STREAM_H 2 | #define FILE_STREAM_H 3 | 4 | #include "MemoryStream.h" 5 | 6 | #define FILE_STREAM_SIZE_AUTO 0 7 | #define FILE_STREAM_FLAG_WRITABLE (1 << 0) 8 | #define FILE_STREAM_FLAG_AUTO_EXPAND (1 << 1) 9 | 10 | typedef struct FileStreamContext { 11 | int fd; 12 | size_t fileSize; 13 | uint32_t bufferStart; 14 | size_t bufferSize; 15 | } FileStreamContext; 16 | 17 | MemoryStream *file_stream_init_from_file_descriptor_nodup(int fd, uint32_t bufferStart, size_t bufferSize, uint32_t flags); 18 | MemoryStream *file_stream_init_from_file_descriptor(int fd, uint32_t bufferStart, size_t bufferSize, uint32_t flags); 19 | MemoryStream *file_stream_init_from_path(const char *path, uint32_t bufferStart, size_t bufferSize, uint32_t flags); 20 | 21 | #endif // FILE_STREAM_H -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2024 Alfie CG 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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/choma/Util.h: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_H 2 | #define UTIL_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | typedef struct s_optional_uint64 { 9 | bool isSet; 10 | uint64_t value; 11 | } optional_uint64_t; 12 | #define OPT_UINT64_IS_SET(x) (x.isSet) 13 | #define OPT_UINT64_GET_VAL(x) (x.value) 14 | #define OPT_UINT64_NONE (optional_uint64_t){.isSet = false, .value = 0} 15 | #define OPT_UINT64(x) (optional_uint64_t){.isSet = true, .value = x} 16 | 17 | 18 | typedef struct s_optional_bool { 19 | bool isSet; 20 | bool value; 21 | } optional_bool; 22 | #define OPT_BOOL_IS_SET(x) (x.isSet) 23 | #define OPT_BOOL_GET_VAL(x) (x.value) 24 | #define OPT_BOOL_NONE (optional_bool){.isSet = false, .value = false} 25 | #define OPT_BOOL(x) (optional_bool){.isSet = true, .value = x} 26 | 27 | int64_t sxt64(int64_t value, uint8_t bits); 28 | int memcmp_masked(const void *str1, const void *str2, unsigned char* mask, size_t n); 29 | uint64_t align_to_size(int size, int alignment); 30 | int count_digits(int64_t num); 31 | void print_hash(uint8_t *hash, size_t size); 32 | void enumerate_range(uint64_t start, uint64_t end, uint16_t alignment, size_t nbytes, bool (^enumerator)(uint64_t cur)); 33 | 34 | #endif -------------------------------------------------------------------------------- /include/choma/FAT.h: -------------------------------------------------------------------------------- 1 | #ifndef MACHO_H 2 | #define MACHO_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "MemoryStream.h" 12 | typedef struct MachO MachO; 13 | 14 | // A FAT structure can either represent a FAT file with multiple slices, in which the slices will be loaded into the slices attribute 15 | // Or a single slice MachO, in which case it serves as a compatibility layer and the single slice will also be loaded into the slices attribute 16 | typedef struct FAT 17 | { 18 | MemoryStream *stream; 19 | MachO **slices; 20 | uint32_t slicesCount; 21 | int fileDescriptor; 22 | } FAT; 23 | 24 | int fat_read_at_offset(FAT *fat, uint64_t offset, size_t size, void *outBuf); 25 | 26 | MemoryStream *fat_get_stream(FAT *fat); 27 | 28 | // Initialise a FAT structure from a memory stream 29 | FAT *fat_init_from_memory_stream(MemoryStream *stream); 30 | 31 | // Initialise a FAT structure using the path to the file 32 | FAT *fat_init_from_path(const char *filePath); 33 | 34 | // Find macho with cputype and cpusubtype in FAT, returns NULL if not found 35 | MachO *fat_find_slice(FAT *fat, cpu_type_t cputype, cpu_subtype_t cpusubtype); 36 | 37 | // Create a FAT structure from an array of MachO structures 38 | FAT *fat_create_for_macho_array(char *firstInputPath, MachO **machoArray, int machoArrayCount); 39 | 40 | // Add a MachO to the FAT structure 41 | int fat_add_macho(FAT *fat, MachO *macho); 42 | 43 | // Free all elements of the FAT structure 44 | void fat_free(FAT *fat); 45 | 46 | #endif // MACHO_H -------------------------------------------------------------------------------- /include/choma/CodeDirectory.h: -------------------------------------------------------------------------------- 1 | #ifndef CODE_DIRECTORY_H 2 | #define CODE_DIRECTORY_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "MachO.h" 9 | #include "CSBlob.h" 10 | #include "FAT.h" 11 | #include "MachOByteOrder.h" 12 | #include "MachOLoadCommand.h" 13 | #include "MemoryStream.h" 14 | 15 | // Code directory blob header 16 | typedef struct __CodeDirectory { 17 | uint32_t magic; 18 | uint32_t length; 19 | uint32_t version; 20 | uint32_t flags; 21 | uint32_t hashOffset; 22 | uint32_t identOffset; 23 | uint32_t nSpecialSlots; 24 | uint32_t nCodeSlots; 25 | uint32_t codeLimit; 26 | uint8_t hashSize; 27 | uint8_t hashType; 28 | uint8_t platform; 29 | uint8_t pageSize; 30 | uint32_t spare2; 31 | 32 | /* Version 0x20100 */ 33 | uint32_t scatterOffset; 34 | uint32_t teamOffset; 35 | } CS_CodeDirectory 36 | __attribute__ ((aligned(1))); 37 | 38 | #define CS_CDHASH_LEN 20 39 | enum CS_HashType { 40 | CS_HASHTYPE_SHA160_160 = 1, 41 | CS_HASHTYPE_SHA256_256 = 2, 42 | CS_HASHTYPE_SHA256_160 = 3, 43 | CS_HASHTYPE_SHA384_384 = 4, 44 | }; 45 | 46 | char *csd_code_directory_copy_identifier(CS_DecodedBlob *codeDirBlob, uint32_t *offsetOut); 47 | char *csd_code_directory_copy_team_id(CS_DecodedBlob *codeDirBlob, uint32_t *offsetOut); 48 | int csd_code_directory_set_team_id(CS_DecodedBlob *codeDirBlob, char *newTeamID); 49 | uint32_t csd_code_directory_get_flags(CS_DecodedBlob *codeDirBlob); 50 | void csd_code_directory_set_flags(CS_DecodedBlob *codeDirBlob, uint32_t flags); 51 | uint8_t csd_code_directory_get_hash_type(CS_DecodedBlob *codeDirBlob); 52 | void csd_code_directory_set_hash_type(CS_DecodedBlob *codeDirBlob, uint8_t hashType); 53 | unsigned csd_code_directory_calculate_rank(CS_DecodedBlob *codeDirBlob); 54 | int csd_code_directory_calculate_hash(CS_DecodedBlob *codeDirBlob, void *cdhashOut); 55 | int csd_code_directory_print_content(CS_DecodedBlob *codeDirBlob, MachO *macho, bool printSlots, bool verifySlots); 56 | void csd_code_directory_update(CS_DecodedBlob *codeDirBlob, MachO *macho); 57 | 58 | #endif // CODE_DIRECTORY_H -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # coretrust_cli 2 | 3 | A simple CLI tool that directly calls CoreTrust functions for code signature validation. It uses frameworks inside the dyld shared cache that export the CoreTrust functions (MobileInBoxUpdate.framework on iOS, AuthKit.framework on macOS) in order to call them from userland. Said functions are identical to the ones used in the main CoreTrust kernel extension. 4 | 5 | This is useful if you're testing CoreTrust functionality or evaluation. You can call any of the symbols exported by the frameworks directly from the CLI, as long as you define them in `CoreTrust.h` (you can find a lot of function signatures [here](https://github.com/apple-oss-distributions/xnu/blob/main/EXTERNAL_HEADERS/CoreTrust/CTEvaluate.h)). By default, the tool will call `CTEvaluateAmfiCodeSignatureCMS` and is configured to extract the necessary components from the binary to pass to the function. 6 | 7 | This tool also enables you to use LLDB to debug and step through the CoreTrust flow. This allows you to modify memory, inspect structures, set breakpoints and understand the CoreTrust flow better. 8 | 9 | `coretrust_cli` will return the result of the CoreTrust evaluation and, if successful, the policy flags returned by CoreTrust, and the CD hash that AMFI would check against after CoreTrust evaluation. 10 | 11 | Running `make` will build for both macOS and iOS. macOS binaries will be placed in `output/coretrust_cli` and iOS binaries will be placed in `output/ios/coretrust_cli`. 12 | 13 | This project depends on [ChOma](https://github.com/opa334/ChOma) for the Mach-O and code signature parsing. The necessary files are in the `include` and `lib` directories, in compiled form. ChOma, like `coretrust_cli`, is licensed under the MIT license. 14 | 15 | ```sh 16 | ➜ coretrust_cli git:(main) ✗ output/coretrust_cli -i /sbin/reboot 17 | CoreTrust evaluation was successful! 18 | CoreTrust policy flags (0x8): 19 | - Mac Platform 20 | 21 | CMS uses Apple Hash Agility V2, chosen hash type is SHA-256. 22 | AMFI will expect CD hash of SHA-256 code directory to be 6deaa31d0c5b0209bb11254cc6d445bc94b50bd0ed09db26375e678d600528e5. 23 | ``` 24 | 25 | ## Usage 26 | 27 | ```sh 28 | Options: 29 | -i: input file 30 | -h: print this help message 31 | Examples: 32 | ./coretrust_cli -i 33 | ``` -------------------------------------------------------------------------------- /include/choma/PatchFinder_arm64.h: -------------------------------------------------------------------------------- 1 | #ifndef PATCHFINDER_ARM64_H 2 | #define PATCHFINDER_ARM64_H 3 | 4 | #include "PatchFinder.h" 5 | 6 | typedef enum { 7 | ARM64_XREF_TYPE_BL = 0, 8 | ARM64_XREF_TYPE_B, 9 | ARM64_XREF_TYPE_B_COND, 10 | ARM64_XREF_TYPE_BC_COND, 11 | ARM64_XREF_TYPE_CBZ, 12 | ARM64_XREF_TYPE_CBNZ, 13 | ARM64_XREF_TYPE_TBZ, 14 | ARM64_XREF_TYPE_TBNZ, 15 | ARM64_XREF_TYPE_ADR, 16 | ARM64_XREF_TYPE_ADRP_ADD, 17 | ARM64_XREF_TYPE_ADRP_LDR, 18 | ARM64_XREF_TYPE_ADRP_STR, 19 | } Arm64XrefType; 20 | 21 | typedef enum { 22 | ARM64_XREF_TYPE_MASK_BL = (1 << ARM64_XREF_TYPE_BL), 23 | ARM64_XREF_TYPE_MASK_CALL = (ARM64_XREF_TYPE_MASK_BL), 24 | 25 | ARM64_XREF_TYPE_MASK_B = (1 << ARM64_XREF_TYPE_B), 26 | ARM64_XREF_TYPE_MASK_B_COND = (1 << ARM64_XREF_TYPE_B_COND), 27 | ARM64_XREF_TYPE_MASK_BC_COND = (1 << ARM64_XREF_TYPE_BC_COND), 28 | ARM64_XREF_TYPE_MASK_CBZ = (1 << ARM64_XREF_TYPE_CBZ), 29 | ARM64_XREF_TYPE_MASK_CBNZ = (1 << ARM64_XREF_TYPE_CBNZ), 30 | ARM64_XREF_TYPE_MASK_TBZ = (1 << ARM64_XREF_TYPE_TBZ), 31 | ARM64_XREF_TYPE_MASK_TBNZ = (1 << ARM64_XREF_TYPE_TBNZ), 32 | ARM64_XREF_TYPE_MASK_JUMP = (ARM64_XREF_TYPE_B | ARM64_XREF_TYPE_MASK_B_COND | ARM64_XREF_TYPE_MASK_BC_COND | ARM64_XREF_TYPE_MASK_CBZ | ARM64_XREF_TYPE_MASK_CBNZ | ARM64_XREF_TYPE_MASK_TBZ | ARM64_XREF_TYPE_MASK_TBNZ), 33 | 34 | ARM64_XREF_TYPE_MASK_ADR = (1 << ARM64_XREF_TYPE_ADR), 35 | ARM64_XREF_TYPE_MASK_ADRP_ADD = (1 << ARM64_XREF_TYPE_ADRP_ADD), 36 | ARM64_XREF_TYPE_MASK_ADRP_LDR = (1 << ARM64_XREF_TYPE_ADRP_LDR), 37 | ARM64_XREF_TYPE_MASK_ADRP_STR = (1 << ARM64_XREF_TYPE_ADRP_STR), 38 | ARM64_XREF_TYPE_MASK_REFERENCE = (ARM64_XREF_TYPE_MASK_ADR | ARM64_XREF_TYPE_MASK_ADRP_ADD | ARM64_XREF_TYPE_MASK_ADRP_LDR | ARM64_XREF_TYPE_MASK_ADRP_STR), 39 | 40 | ARM64_XREF_TYPE_ALL = (ARM64_XREF_TYPE_MASK_CALL | ARM64_XREF_TYPE_MASK_JUMP | ARM64_XREF_TYPE_MASK_REFERENCE), 41 | } Arm64XrefTypeMask; 42 | 43 | uint64_t pfsec_arm64_resolve_adrp_ldr_str_add_reference(PFSection *section, uint64_t adrpAddr, uint64_t ldrStrAddAddr); 44 | uint64_t pfsec_arm64_resolve_adrp_ldr_str_add_reference_auto(PFSection *section, uint64_t ldrStrAddAddr); 45 | uint64_t pfsec_arm64_resolve_stub(PFSection *section, uint64_t stubAddr); 46 | void pfsec_arm64_enumerate_xrefs(PFSection *section, Arm64XrefTypeMask types, void (^xrefBlock)(Arm64XrefType type, uint64_t source, uint64_t target, bool *stop)); 47 | #endif -------------------------------------------------------------------------------- /include/choma/PatchFinder.h: -------------------------------------------------------------------------------- 1 | #ifndef PATCHFINDER_H 2 | #define PATCHFINDER_H 3 | 4 | #include 5 | #include "MachO.h" 6 | 7 | enum { 8 | PF_METRIC_TYPE_PATTERN, 9 | PF_METRIC_TYPE_STRING, 10 | PF_METRIC_TYPE_XREF, 11 | }; 12 | 13 | typedef struct s_PFSection { 14 | MachO *macho; 15 | uint64_t fileoff; 16 | uint64_t vmaddr; 17 | uint64_t size; 18 | uint8_t *cache; 19 | bool ownsCache; 20 | } PFSection; 21 | 22 | PFSection *pfsec_init_from_macho(MachO *macho, const char *filesetEntryId, const char *segName, const char *sectName); 23 | int pfsec_read_reloff(PFSection *section, uint64_t rel, size_t size, void *outBuf); 24 | uint32_t pfsec_read32_reloff(PFSection *section, uint64_t rel); 25 | int pfsec_read_at_address(PFSection *section, uint64_t vmaddr, void *outBuf, size_t size); 26 | uint32_t pfsec_read32(PFSection *section, uint64_t vmaddr); 27 | uint64_t pfsec_read64(PFSection *section, uint64_t vmaddr); 28 | int pfsec_read_string(PFSection *section, uint64_t vmaddr, char **outString); 29 | int pfsec_set_cached(PFSection *section, bool cached); 30 | uint64_t pfsec_find_prev_inst(PFSection *section, uint64_t startAddr, uint32_t searchCount, uint32_t inst, uint32_t mask); 31 | uint64_t pfsec_find_next_inst(PFSection *section, uint64_t startAddr, uint32_t searchCount, uint32_t inst, uint32_t mask); 32 | uint64_t pfsec_find_function_start(PFSection *section, uint64_t midAddr); 33 | void pfsec_free(PFSection *section); 34 | 35 | 36 | typedef struct s_MetricShared { 37 | uint32_t type; 38 | } MetricShared; 39 | 40 | typedef struct s_PFPatternMetric { 41 | MetricShared shared; 42 | 43 | void *bytes; 44 | void *mask; 45 | size_t nbytes; 46 | uint16_t alignment; 47 | } PFPatternMetric; 48 | 49 | typedef struct s_PFStringMetric { 50 | MetricShared shared; 51 | 52 | char *string; 53 | } PFStringMetric; 54 | 55 | typedef enum { 56 | XREF_TYPE_MASK_CALL = (1 << 0), 57 | XREF_TYPE_MASK_JUMP = (1 << 1), 58 | XREF_TYPE_MASK_REFERENCE = (1 << 2), 59 | XREF_TYPE_MASK_ALL = (XREF_TYPE_MASK_CALL | XREF_TYPE_MASK_REFERENCE), 60 | } PFXrefTypeMask; 61 | 62 | typedef struct s_PFXrefMetric { 63 | MetricShared shared; 64 | 65 | uint64_t address; 66 | PFXrefTypeMask typeMask; 67 | } PFXrefMetric; 68 | 69 | PFPatternMetric *pfmetric_pattern_init(void *bytes, void *mask, size_t nbytes, uint16_t alignment); 70 | PFStringMetric *pfmetric_string_init(const char *string); 71 | PFXrefMetric *pfmetric_xref_init(uint64_t address, PFXrefTypeMask types); 72 | void pfmetric_free(void *metric); 73 | 74 | void pfmetric_run_in_range(PFSection *section, uint64_t startAddr, uint64_t endAddr, void *metric, void (^matchBlock)(uint64_t vmaddr, bool *stop)); 75 | void pfmetric_run(PFSection *section, void *metric, void (^matchBlock)(uint64_t vmaddr, bool *stop)); 76 | #endif -------------------------------------------------------------------------------- /include/choma/MemoryStream.h: -------------------------------------------------------------------------------- 1 | #ifndef MEMORY_STREAM_H 2 | #define MEMORY_STREAM_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define MEMORY_STREAM_FLAG_OWNS_DATA (1 << 0) 13 | #define MEMORY_STREAM_FLAG_MUTABLE (1 << 1) 14 | #define MEMORY_STREAM_FLAG_AUTO_EXPAND (1 << 2) 15 | 16 | #define MEMORY_STREAM_SIZE_INVALID (size_t)-1 17 | 18 | // A generic memory IO interface that is used throughout this project 19 | // Can be backed by anything, just the functions have to be implemented 20 | typedef struct s_MemoryStream { 21 | void *context; 22 | uint32_t flags; 23 | 24 | int (*read)(struct s_MemoryStream *stream, uint64_t offset, size_t size, void *outBuf); 25 | int (*write)(struct s_MemoryStream *stream, uint64_t offset, size_t size, const void *inBuf); 26 | int (*getSize)(struct s_MemoryStream *stream, size_t *sizeOut); 27 | uint8_t *(*getRawPtr)(struct s_MemoryStream *stream); 28 | 29 | int (*trim)(struct s_MemoryStream *stream, size_t trimAtStart, size_t trimAtEnd); 30 | int (*expand)(struct s_MemoryStream *stream, size_t expandAtStart, size_t expandAtEnd); 31 | 32 | struct s_MemoryStream *(*hardclone)(struct s_MemoryStream *stream); 33 | struct s_MemoryStream *(*softclone)(struct s_MemoryStream *stream); 34 | void (*free)(struct s_MemoryStream *stream); 35 | } MemoryStream; 36 | 37 | int memory_stream_read(MemoryStream *stream, uint64_t offset, size_t size, void *outBuf); 38 | int memory_stream_write(MemoryStream *stream, uint64_t offset, size_t size, const void *inBuf); 39 | 40 | int memory_stream_insert(MemoryStream *stream, uint64_t offset, size_t size, const void *inBuf); 41 | int memory_stream_delete(MemoryStream *stream, uint64_t offset, size_t size); 42 | 43 | int memory_stream_read_string(MemoryStream *stream, uint64_t offset, char **outString); 44 | int memory_stream_write_string(MemoryStream *stream, uint64_t offset, const char *string); 45 | 46 | size_t memory_stream_get_size(MemoryStream *stream); 47 | uint8_t *memory_stream_get_raw_pointer(MemoryStream *stream); 48 | uint32_t memory_stream_get_flags(MemoryStream *stream); 49 | 50 | MemoryStream *memory_stream_softclone(MemoryStream *stream); 51 | MemoryStream *memory_stream_hardclone(MemoryStream *stream); 52 | int memory_stream_trim(MemoryStream *stream, size_t trimAtStart, size_t trimAtEnd); 53 | int memory_stream_expand(MemoryStream *stream, size_t expandAtStart, size_t expandAtEnd); 54 | 55 | void memory_stream_free(MemoryStream *stream); 56 | 57 | int memory_stream_copy_data(MemoryStream *originStream, uint64_t originOffset, MemoryStream *targetStream, uint64_t targetOffset, size_t size); 58 | int memory_stream_find_memory(MemoryStream *stream, uint64_t searchStartOffset, uint64_t searchEndOffset, void *bytes, void *mask, size_t nbytes, uint16_t alignment, uint64_t *foundOffsetOut); 59 | 60 | #endif // MEMORY_STREAM_H -------------------------------------------------------------------------------- /include/choma/MachO.h: -------------------------------------------------------------------------------- 1 | #ifndef MACHO_SLICE_H 2 | #define MACHO_SLICE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "MemoryStream.h" 8 | #include "FAT.h" 9 | 10 | typedef struct MachOSegment 11 | { 12 | struct segment_command_64 command; 13 | struct section_64 sections[]; 14 | } __attribute__((__packed__)) MachOSegment; 15 | 16 | typedef struct FilesetMachO { 17 | char *entry_id; 18 | uint64_t vmaddr; 19 | uint64_t fileoff; 20 | FAT *underlyingMachO; 21 | } FilesetMachO; 22 | 23 | typedef struct MachO { 24 | MemoryStream *stream; 25 | bool isSupported; 26 | struct mach_header_64 machHeader; 27 | struct fat_arch_64 archDescriptor; 28 | 29 | uint32_t filesetCount; 30 | FilesetMachO *filesetMachos; 31 | 32 | uint32_t segmentCount; 33 | MachOSegment **segments; 34 | } MachO; 35 | 36 | // Read data from a MachO at a specified offset 37 | int macho_read_at_offset(MachO *macho, uint64_t offset, size_t size, void *outBuf); 38 | 39 | // Write data from a MachO at a specified offset, auto expands, only works if opened via macho_init_for_writing 40 | int macho_write_at_offset(MachO *macho, uint64_t offset, size_t size, const void *inBuf); 41 | 42 | MemoryStream *macho_get_stream(MachO *macho); 43 | uint32_t macho_get_filetype(MachO *macho); 44 | 45 | // Perform translation between file offsets and virtual addresses 46 | int macho_translate_fileoff_to_vmaddr(MachO *macho, uint64_t fileoff, uint64_t *vmaddrOut, MachOSegment **segmentOut); 47 | int macho_translate_vmaddr_to_fileoff(MachO *macho, uint64_t vmaddr, uint64_t *fileoffOut, MachOSegment **segmentOut); 48 | 49 | // Wrappers to deal with virtual addresses 50 | int macho_read_at_vmaddr(MachO *macho, uint64_t vmaddr, size_t size, void *outBuf); 51 | int macho_write_at_vmaddr(MachO *macho, uint64_t vmaddr, size_t size, const void *inBuf); 52 | 53 | int macho_enumerate_load_commands(MachO *macho, void (^enumeratorBlock)(struct load_command loadCommand, uint64_t offset, void *cmd, bool *stop)); 54 | int macho_enumerate_symbols(MachO *macho, void (^enumeratorBlock)(const char *name, uint8_t type, uint64_t vmaddr, bool *stop)); 55 | int macho_enumerate_dependencies(MachO *macho, void (^enumeratorBlock)(const char *dylibPath, uint32_t cmd, struct dylib* dylib, bool *stop)); 56 | int macho_enumerate_rpaths(MachO *macho, void (^enumeratorBlock)(const char *rpath, bool *stop)); 57 | 58 | // Initialise a MachO object from a MemoryStream and it's corresponding FAT arch descriptor 59 | MachO *macho_init(MemoryStream *stream, struct fat_arch_64 archDescriptor); 60 | 61 | // Initialize a single slice macho for writing to it 62 | MachO *macho_init_for_writing(const char *filePath); 63 | 64 | // Create an array of MachO objects from an array of paths 65 | MachO **macho_array_create_for_paths(char **inputPaths, int inputPathsCount); 66 | 67 | // Check if a MachO is encrypted 68 | bool macho_is_encrypted(MachO *macho); 69 | 70 | void macho_free(MachO *macho); 71 | 72 | #endif // MACHO_SLICE_H -------------------------------------------------------------------------------- /include/choma/CSBlob.h: -------------------------------------------------------------------------------- 1 | #ifndef CS_BLOB_H 2 | #define CS_BLOB_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "FAT.h" 10 | #include "MachO.h" 11 | #include "MemoryStream.h" 12 | 13 | // Blob index 14 | typedef struct __BlobIndex { 15 | uint32_t type; 16 | uint32_t offset; 17 | } CS_BlobIndex; 18 | 19 | // CMS superblob 20 | typedef struct __SuperBlob { 21 | uint32_t magic; 22 | uint32_t length; 23 | uint32_t count; 24 | CS_BlobIndex index[]; 25 | } CS_SuperBlob; 26 | 27 | typedef struct __GenericBlob { 28 | uint32_t magic; /* magic number */ 29 | uint32_t length; /* total length of blob */ 30 | char data[]; 31 | } CS_GenericBlob; 32 | 33 | // CMS blob magic types 34 | typedef enum { 35 | CSMAGIC_REQUIREMENT = 0xfade0c00, 36 | CSMAGIC_REQUIREMENTS = 0xfade0c01, 37 | CSMAGIC_CODEDIRECTORY = 0xfade0c02, 38 | CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0, 39 | CSMAGIC_EMBEDDED_SIGNATURE_OLD = 0xfade0b02, 40 | CSMAGIC_EMBEDDED_ENTITLEMENTS = 0xfade7171, 41 | CSMAGIC_EMBEDDED_DER_ENTITLEMENTS = 0xfade7172, 42 | CSMAGIC_DETACHED_SIGNATURE = 0xfade0cc1, 43 | CSMAGIC_BLOBWRAPPER = 0xfade0b01, 44 | CSMAGIC_EMBEDDED_LAUNCH_CONSTRAINT = 0xfade8181, 45 | } CS_BlobMagic; 46 | 47 | typedef enum { 48 | CSSLOT_CODEDIRECTORY = 0, 49 | CSSLOT_INFOSLOT = 1, 50 | CSSLOT_REQUIREMENTS = 2, 51 | CSSLOT_RESOURCEDIR = 3, 52 | CSSLOT_APPLICATION = 4, 53 | CSSLOT_ENTITLEMENTS = 5, 54 | CSSLOT_DER_ENTITLEMENTS = 7, 55 | CSSLOT_LAUNCH_CONSTRAINT_SELF = 8, 56 | CSSLOT_LAUNCH_CONSTRAINT_PARENT = 9, 57 | CSSLOT_LAUNCH_CONSTRAINT_RESPONSIBLE = 10, 58 | CSSLOT_LIBRARY_CONSTRAINT = 11, 59 | 60 | CSSLOT_ALTERNATE_CODEDIRECTORIES = 0x1000, /* first alternate CodeDirectory, if any */ 61 | CSSLOT_ALTERNATE_CODEDIRECTORY_MAX = 5, /* max number of alternate CD slots */ 62 | CSSLOT_ALTERNATE_CODEDIRECTORY_LIMIT = CSSLOT_ALTERNATE_CODEDIRECTORIES + CSSLOT_ALTERNATE_CODEDIRECTORY_MAX, /* one past the last */ 63 | 64 | CSSLOT_SIGNATURESLOT = 0x10000, 65 | CSSLOT_IDENTIFICATIONSLOT = 0x10001, 66 | CSSLOT_TICKETSLOT = 0x10002, 67 | } CS_SlotType; 68 | 69 | typedef struct s_CS_DecodedBlob { 70 | struct s_CS_DecodedBlob *next; 71 | uint32_t type; 72 | MemoryStream *stream; 73 | } CS_DecodedBlob; 74 | 75 | typedef struct s_CS_DecodedSuperBlob { 76 | uint32_t magic; 77 | struct s_CS_DecodedBlob *firstBlob; 78 | } CS_DecodedSuperBlob; 79 | 80 | // Convert blob magic to readable blob type string 81 | const char *cs_blob_magic_to_string(uint32_t magic); 82 | const char *cs_slot_type_to_string(uint32_t slotType); 83 | 84 | // Extract Code Signature to file 85 | int macho_extract_cs_to_file(MachO *macho, CS_SuperBlob *superblob); 86 | 87 | int macho_find_code_signature_bounds(MachO *macho, uint32_t *offsetOut, uint32_t *sizeOut); 88 | 89 | CS_SuperBlob *macho_read_code_signature(MachO *macho); 90 | 91 | int macho_replace_code_signature(MachO *macho, CS_SuperBlob *superblob); 92 | 93 | CS_DecodedBlob *csd_blob_init(uint32_t type, CS_GenericBlob *blobData); 94 | int csd_blob_read(CS_DecodedBlob *blob, uint64_t offset, size_t size, void *outBuf); 95 | int csd_blob_write(CS_DecodedBlob *blob, uint64_t offset, size_t size, const void *inBuf); 96 | int csd_blob_insert(CS_DecodedBlob *blob, uint64_t offset, size_t size, const void *inBuf); 97 | int csd_blob_delete(CS_DecodedBlob *blob, uint64_t offset, size_t size); 98 | int csd_blob_read_string(CS_DecodedBlob *blob, uint64_t offset, char **outString); 99 | int csd_blob_write_string(CS_DecodedBlob *blob, uint64_t offset, const char *string); 100 | size_t csd_blob_get_size(CS_DecodedBlob *blob); 101 | uint32_t csd_blob_get_type(CS_DecodedBlob *blob); 102 | void csd_blob_set_type(CS_DecodedBlob *blob, uint32_t type); 103 | void csd_blob_free(CS_DecodedBlob *blob); 104 | 105 | CS_DecodedSuperBlob *csd_superblob_init(void); 106 | CS_DecodedSuperBlob *csd_superblob_decode(CS_SuperBlob *superblob); 107 | CS_SuperBlob *csd_superblob_encode(CS_DecodedSuperBlob *decodedSuperblob); 108 | CS_DecodedBlob *csd_superblob_find_blob(CS_DecodedSuperBlob *superblob, uint32_t type, uint32_t *indexOut); 109 | int csd_superblob_insert_blob_after_blob(CS_DecodedSuperBlob *superblob, CS_DecodedBlob *blobToInsert, CS_DecodedBlob *afterBlob); 110 | int csd_superblob_insert_blob_at_index(CS_DecodedSuperBlob *superblob, CS_DecodedBlob *blobToInsert, uint32_t atIndex); 111 | int csd_superblob_append_blob(CS_DecodedSuperBlob *superblob, CS_DecodedBlob *blobToAppend); 112 | int csd_superblob_remove_blob(CS_DecodedSuperBlob *superblob, CS_DecodedBlob *blobToRemove); // <- Important: When calling this, caller is responsible for freeing blobToRemove 113 | int csd_superblob_remove_blob_at_index(CS_DecodedSuperBlob *superblob, uint32_t atIndex); 114 | int csd_superblob_calculate_best_cdhash(CS_DecodedSuperBlob *decodedSuperblob, void *cdhashOut); 115 | int csd_superblob_print_content(CS_DecodedSuperBlob *decodedSuperblob, MachO *macho, bool printAllSlots, bool verifySlots); 116 | void csd_superblob_free(CS_DecodedSuperBlob *decodedSuperblob); 117 | 118 | #endif // CS_BLOB_H -------------------------------------------------------------------------------- /include/choma/arm64.h: -------------------------------------------------------------------------------- 1 | #ifndef ARM64_H 2 | #define ARM64_H 3 | 4 | #include "Util.h" 5 | 6 | typedef enum { 7 | // registers 8 | ARM64_REG_TYPE_X, 9 | ARM64_REG_TYPE_W, 10 | 11 | // vector shit 12 | ARM64_REG_TYPE_Q, 13 | ARM64_REG_TYPE_D, 14 | ARM64_REG_TYPE_S, 15 | ARM64_REG_TYPE_H, 16 | ARM64_REG_TYPE_B, 17 | } arm64_register_type; 18 | 19 | enum { 20 | ARM64_REG_MASK_ANY_FLAG = (1 << 0), 21 | ARM64_REG_MASK_X_W = (1 << 1), 22 | ARM64_REG_MASK_VECTOR = (1 << 2), 23 | ARM64_REG_MASK_ALL = (ARM64_REG_MASK_X_W | ARM64_REG_MASK_VECTOR), 24 | 25 | ARM64_REG_MASK_ANY_X_W = (ARM64_REG_MASK_X_W | ARM64_REG_MASK_ANY_FLAG), 26 | ARM64_REG_MASK_ANY_VECTOR = (ARM64_REG_MASK_VECTOR | ARM64_REG_MASK_ANY_FLAG), 27 | ARM64_REG_MASK_ANY_ALL = (ARM64_REG_MASK_ALL | ARM64_REG_MASK_ANY_FLAG), 28 | }; 29 | 30 | typedef enum { 31 | LDR_STR_TYPE_ANY, // NOTE: "ANY" will inevitably also match STUR and LDUR instructions 32 | LDR_STR_TYPE_POST_INDEX, 33 | LDR_STR_TYPE_PRE_INDEX, 34 | LDR_STR_TYPE_UNSIGNED, 35 | } arm64_ldr_str_type; 36 | 37 | typedef struct s_arm64_register { 38 | uint8_t mask; 39 | arm64_register_type type; 40 | uint8_t num; 41 | } arm64_register; 42 | 43 | #define ARM64_REG(type_, num_) (arm64_register){.mask = ARM64_REG_MASK_ALL, .type = type_, .num = num_} 44 | #define ARM64_REG_X(x) ARM64_REG(ARM64_REG_TYPE_X, x) 45 | #define ARM64_REG_W(x) ARM64_REG(ARM64_REG_TYPE_W, x) 46 | #define ARM64_REG_Q(x) ARM64_REG(ARM64_REG_TYPE_Q, x) 47 | #define ARM64_REG_S(x) ARM64_REG(ARM64_REG_TYPE_S, x) 48 | #define ARM64_REG_H(x) ARM64_REG(ARM64_REG_TYPE_H, x) 49 | #define ARM64_REG_B(x) ARM64_REG(ARM64_REG_TYPE_B, x) 50 | #define ARM64_REG_ANY (arm64_register){.mask = ARM64_REG_MASK_ANY_ALL, .type = 0, .num = 0} 51 | #define ARM64_REG_ANY_X_W (arm64_register){.mask = ARM64_REG_MASK_ANY_X_W, .type = 0, .num = 0} 52 | #define ARM64_REG_ANY_VECTOR (arm64_register){.mask = ARM64_REG_MASK_ANY_VECTOR, .type = 0, .num = 0} 53 | #define ARM64_REG_GET_TYPE(x) (x.type) 54 | #define ARM64_REG_IS_X(x) (x.type == ARM64_REG_TYPE_X) 55 | #define ARM64_REG_IS_W(x) (x.type == ARM64_REG_TYPE_W) 56 | #define ARM64_REG_IS_VECTOR(x) (x.type == ARM64_REG_TYPE_Q || x.type == ARM64_REG_TYPE_D || x.type == ARM64_REG_TYPE_S || x.type == ARM64_REG_TYPE_H || x.type == ARM64_REG_TYPE_B) 57 | #define ARM64_REG_GET_NUM(x) (x.num & 0x1f) 58 | #define ARM64_REG_IS_ANY(x) (x.mask == ARM64_REG_MASK_ANY_ALL) 59 | #define ARM64_REG_IS_ANY_X_W(x) (x.mask == ARM64_REG_MASK_ANY_X_W) 60 | #define ARM64_REG_IS_ANY_VECTOR(x) (x.mask == ARM64_REG_MASK_ANY_VECTOR) 61 | uint8_t arm64_reg_type_get_width(arm64_register_type type); 62 | const char *arm64_reg_type_get_string(arm64_register_type type); 63 | const char *arm64_reg_get_type_string(arm64_register reg); 64 | 65 | #define ARM64_REG_NUM_SP 31 66 | 67 | typedef struct s_arm64_cond { 68 | bool isSet; 69 | uint8_t value; 70 | } arm64_cond; 71 | #define ARM64_COND(x) (arm64_cond){.isSet = true, .value = x} 72 | #define ARM64_COND_ANY (arm64_cond){.isSet = false, .value = 0} 73 | #define ARM64_COND_GET_VAL(x) (x.value & 0xf) 74 | #define ARM64_COND_IS_SET(x) x.isSet 75 | 76 | int arm64_gen_b_l(optional_bool optIsBl, optional_uint64_t optOrigin, optional_uint64_t optTarget, uint32_t *bytesOut, uint32_t *maskOut); 77 | int arm64_dec_b_l(uint32_t inst, uint64_t origin, uint64_t *targetOut, bool *isBlOut); 78 | int arm64_gen_b_c_cond(optional_bool optIsBc, optional_uint64_t optOrigin, optional_uint64_t optTarget, arm64_cond optCond, uint32_t *bytesOut, uint32_t *maskOut); 79 | int arm64_dec_b_c_cond(uint32_t inst, uint64_t origin, uint64_t *targetOut, arm64_cond *condOut, bool *isBcOut); 80 | int arm64_gen_adr_p(optional_bool optIsAdrp, optional_uint64_t optOrigin, optional_uint64_t optTarget, arm64_register reg, uint32_t *bytesOut, uint32_t *maskOut); 81 | int arm64_dec_adr_p(uint32_t inst, uint64_t origin, uint64_t *targetOut, arm64_register *registerOut, bool *isAdrpOut); 82 | int arm64_gen_mov_imm(char type, arm64_register destinationReg, optional_uint64_t optImm, optional_uint64_t optShift, uint32_t *bytesOut, uint32_t *maskOut); 83 | int arm64_dec_mov_imm(uint32_t inst, arm64_register *destinationRegOut, uint64_t *immOut, uint64_t *shiftOut, char *typeOut); 84 | int arm64_gen_add_imm(arm64_register destinationReg, arm64_register sourceReg, optional_uint64_t optImm, uint32_t *bytesOut, uint32_t *maskOut); 85 | int arm64_dec_add_imm(uint32_t inst, arm64_register *destinationRegOut, arm64_register *sourceRegOut, uint16_t *immOut); 86 | int arm64_gen_ldr_imm(char type, arm64_ldr_str_type instType, arm64_register destinationReg, arm64_register addrReg, optional_uint64_t optImm, uint32_t *bytesOut, uint32_t *maskOut); 87 | int arm64_dec_ldr_imm(uint32_t inst, arm64_register *destinationReg, arm64_register *addrReg, uint64_t *immOut, char *typeOut, arm64_ldr_str_type *instTypeOut); 88 | int arm64_gen_ldrs_imm(char type, arm64_ldr_str_type instType, arm64_register destinationReg, arm64_register addrReg, optional_uint64_t optImm, uint32_t *bytesOut, uint32_t *maskOut); 89 | int arm64_dec_ldrs_imm(uint32_t inst, arm64_register *destinationRegOut, arm64_register *addrRegOut, uint64_t *immOut, char *typeOut, arm64_ldr_str_type *instTypeOut); 90 | int arm64_gen_str_imm(char type, arm64_ldr_str_type instType, arm64_register sourceReg, arm64_register addrReg, optional_uint64_t optImm, uint32_t *bytesOut, uint32_t *maskOut); 91 | int arm64_dec_str_imm(uint32_t inst, arm64_register *sourceRegOut, arm64_register *addrRegOut, uint64_t *immOut, char *typeOut, arm64_ldr_str_type *instTypeOut); 92 | int arm64_gen_ldr_lit(arm64_register destinationReg, optional_uint64_t optOrigin, optional_uint64_t optTarget, uint32_t *bytesOut, uint32_t *maskOut); 93 | int arm64_dec_ldr_lit(uint32_t inst, uint64_t origin, uint64_t *targetOut, arm64_register *destinationReg); 94 | int arm64_gen_cb_n_z(optional_bool isCbnz, arm64_register reg, optional_uint64_t optTarget, uint32_t *bytesOut, uint32_t *maskOut); 95 | int arm64_dec_cb_n_z(uint32_t inst, uint64_t origin, bool *isCbnzOut, arm64_register *regOut, uint64_t *targetOut); 96 | int arm64_gen_tb_n_z(optional_bool isTbnz, arm64_register reg, optional_uint64_t optTarget, optional_uint64_t optBit, uint32_t *bytesOut, uint32_t *maskOut); 97 | int arm64_dec_tb_n_z(uint32_t inst, uint64_t origin, bool *isTbnzOut, arm64_register *regOut, uint64_t *targetOut, uint64_t *bitOut); 98 | #endif -------------------------------------------------------------------------------- /include/choma/MachOByteOrder.h: -------------------------------------------------------------------------------- 1 | #ifndef MACHO_BYTE_ORDER_H 2 | #define MACHO_BYTE_ORDER_H 3 | 4 | #include 5 | #include 6 | 7 | // 8-bit integers needed for CodeDirectory 8 | #define BIG_TO_HOST(n) _Generic((n), \ 9 | int8_t: n, \ 10 | uint8_t: n, \ 11 | int16_t: OSSwapBigToHostInt16(n), \ 12 | uint16_t: OSSwapBigToHostInt16(n), \ 13 | int32_t: OSSwapBigToHostInt32(n), \ 14 | uint32_t: OSSwapBigToHostInt32(n), \ 15 | int64_t: OSSwapBigToHostInt64(n), \ 16 | uint64_t: OSSwapBigToHostInt64(n) \ 17 | ) 18 | 19 | #define HOST_TO_BIG(n) _Generic((n), \ 20 | int8_t: n, \ 21 | uint8_t: n, \ 22 | uint16_t: OSSwapHostToBigInt16(n), \ 23 | int16_t: OSSwapHostToBigInt16(n), \ 24 | int32_t: OSSwapHostToBigInt32(n), \ 25 | uint32_t: OSSwapHostToBigInt32(n), \ 26 | int64_t: OSSwapHostToBigInt64(n), \ 27 | uint64_t: OSSwapHostToBigInt64(n) \ 28 | ) 29 | 30 | #define LITTLE_TO_HOST(n) _Generic((n), \ 31 | int8_t: n, \ 32 | uint8_t: n, \ 33 | int16_t: OSSwapLittleToHostInt16(n), \ 34 | uint16_t: OSSwapLittleToHostInt16(n), \ 35 | int32_t: OSSwapLittleToHostInt32(n), \ 36 | uint32_t: OSSwapLittleToHostInt32(n), \ 37 | int64_t: OSSwapLittleToHostInt64(n), \ 38 | uint64_t: OSSwapLittleToHostInt64(n) \ 39 | ) 40 | 41 | #define HOST_TO_LITTLE(n) _Generic((n), \ 42 | int8_t: n, \ 43 | uint8_t: n, \ 44 | int16_t: OSSwapHostToLittleInt16(n), \ 45 | uint16_t: OSSwapHostToLittleInt16(n), \ 46 | int32_t: OSSwapHostToLittleInt32(n), \ 47 | uint32_t: OSSwapHostToLittleInt32(n), \ 48 | int64_t: OSSwapHostToLittleInt64(n), \ 49 | uint64_t: OSSwapHostToLittleInt64(n) \ 50 | ) 51 | 52 | #define HOST_TO_LITTLE_APPLIER(instance, member) \ 53 | (instance)->member = HOST_TO_LITTLE((instance)->member) 54 | 55 | #define HOST_TO_BIG_APPLIER(instance, member) \ 56 | (instance)->member = HOST_TO_BIG((instance)->member) 57 | 58 | #define LITTLE_TO_HOST_APPLIER(instance, member) \ 59 | (instance)->member = LITTLE_TO_HOST((instance)->member) 60 | 61 | #define BIG_TO_HOST_APPLIER(instance, member) \ 62 | (instance)->member = BIG_TO_HOST((instance)->member) 63 | 64 | #define FAT_HEADER_APPLY_BYTE_ORDER(fh, applier) \ 65 | applier(fh, magic); \ 66 | applier(fh, nfat_arch); 67 | 68 | #define FAT_ARCH_APPLY_BYTE_ORDER(arch, applier) \ 69 | applier(arch, cputype); \ 70 | applier(arch, cpusubtype); \ 71 | applier(arch, offset); \ 72 | applier(arch, size); \ 73 | applier(arch, align); \ 74 | 75 | #define FAT_ARCH_64_APPLY_BYTE_ORDER(arch, applier) \ 76 | applier(arch, cputype); \ 77 | applier(arch, cpusubtype); \ 78 | applier(arch, offset); \ 79 | applier(arch, size); \ 80 | applier(arch, align); \ 81 | applier(arch, reserved); \ 82 | 83 | #define MACH_HEADER_APPLY_BYTE_ORDER(mh, applier) \ 84 | applier(mh, magic); \ 85 | applier(mh, cputype); \ 86 | applier(mh, cpusubtype); \ 87 | applier(mh, filetype); \ 88 | applier(mh, ncmds); \ 89 | applier(mh, sizeofcmds); \ 90 | applier(mh, reserved); 91 | 92 | #define LOAD_COMMAND_APPLY_BYTE_ORDER(lc, applier) \ 93 | applier(lc, cmd); \ 94 | applier(lc, cmdsize); 95 | 96 | #define LINKEDIT_DATA_COMMAND_APPLY_BYTE_ORDER(lc, applier) \ 97 | applier(lc, cmd); \ 98 | applier(lc, cmdsize); \ 99 | applier(lc, dataoff); \ 100 | applier(lc, datasize); 101 | 102 | #define ENCRYPTION_INFO_COMMAND_APPLY_BYTE_ORDER(eic, applier) \ 103 | applier(eic, cmd); \ 104 | applier(eic, cmdsize); \ 105 | applier(eic, cryptoff); \ 106 | applier(eic, cryptsize); \ 107 | applier(eic, cryptid); 108 | 109 | #define BLOB_INDEX_APPLY_BYTE_ORDER(bi, applier) \ 110 | applier(bi, type); \ 111 | applier(bi, offset); 112 | 113 | #define SUPERBLOB_APPLY_BYTE_ORDER(sb, applier) \ 114 | applier(sb, magic); \ 115 | applier(sb, length); \ 116 | applier(sb, count); 117 | 118 | #define GENERIC_BLOB_APPLY_BYTE_ORDER(gb, applier) \ 119 | applier(gb, magic); \ 120 | applier(gb, length); 121 | 122 | #define CODE_DIRECTORY_APPLY_BYTE_ORDER(cd, applier) \ 123 | applier(cd, magic); \ 124 | applier(cd, length); \ 125 | applier(cd, version); \ 126 | applier(cd, flags); \ 127 | applier(cd, hashOffset); \ 128 | applier(cd, identOffset); \ 129 | applier(cd, nSpecialSlots); \ 130 | applier(cd, nCodeSlots); \ 131 | applier(cd, codeLimit); \ 132 | applier(cd, hashSize); \ 133 | applier(cd, hashType); \ 134 | applier(cd, platform); \ 135 | applier(cd, pageSize); \ 136 | applier(cd, spare2); \ 137 | applier(cd, scatterOffset); \ 138 | applier(cd, teamOffset); 139 | 140 | #define SEGMENT_COMMAND_64_APPLY_BYTE_ORDER(sc64, applier) \ 141 | applier(sc64, cmd); \ 142 | applier(sc64, cmdsize); \ 143 | applier(sc64, fileoff); \ 144 | applier(sc64, filesize); \ 145 | applier(sc64, vmaddr); \ 146 | applier(sc64, vmsize); \ 147 | applier(sc64, flags); \ 148 | applier(sc64, initprot); \ 149 | applier(sc64, maxprot); \ 150 | applier(sc64, nsects); 151 | 152 | #define SECTION_64_APPLY_BYTE_ORDER(sc64, applier) \ 153 | applier(sc64, addr); \ 154 | applier(sc64, align); \ 155 | applier(sc64, flags); \ 156 | applier(sc64, nreloc); \ 157 | applier(sc64, offset); \ 158 | applier(sc64, reserved1); \ 159 | applier(sc64, reserved2); \ 160 | applier(sc64, reserved3); \ 161 | applier(sc64, size); 162 | 163 | #define FILESET_ENTRY_COMMAND_APPLY_BYTE_ORDER(fse, applier) \ 164 | applier(fse, cmd); \ 165 | applier(fse, cmdsize); \ 166 | applier(fse, vmaddr); \ 167 | applier(fse, fileoff); \ 168 | applier(fse, entry_id.offset); \ 169 | applier(fse, reserved); \ 170 | 171 | #define SYMTAB_COMMAND_APPLY_BYTE_ORDER(symt, applier) \ 172 | applier(symt, cmd); \ 173 | applier(symt, cmdsize); \ 174 | applier(symt, nsyms); \ 175 | applier(symt, stroff); \ 176 | applier(symt, strsize); \ 177 | applier(symt, symoff); 178 | 179 | #define NLIST_64_APPLY_BYTE_ORDER(nl, applier) \ 180 | applier(nl, n_un.n_strx); \ 181 | applier(nl, n_type); \ 182 | applier(nl, n_sect); \ 183 | applier(nl, n_desc); \ 184 | applier(nl, n_value); 185 | 186 | #define DYLIB_APPLY_BYTE_ORDER(dylib, applier) \ 187 | applier(dylib, name.offset); \ 188 | applier(dylib, timestamp); \ 189 | applier(dylib, current_version); \ 190 | applier(dylib, compatibility_version); 191 | 192 | #define DYLIB_COMMAND_APPLY_BYTE_ORDER(dycmd, applier) \ 193 | LOAD_COMMAND_APPLY_BYTE_ORDER(dycmd, applier); \ 194 | DYLIB_APPLY_BYTE_ORDER((&dycmd->dylib), applier); 195 | 196 | #define RPATH_COMMAND_APPLY_BYTE_ORDER(rpcmd, applier) \ 197 | LOAD_COMMAND_APPLY_BYTE_ORDER(rpcmd, applier); \ 198 | applier(rpcmd, path.offset); 199 | 200 | #endif // MACHO_BYTE_ORDER_H -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "CoreTrust.h" 15 | 16 | char *get_argument_value(int argc, char *argv[], const char *flag) { 17 | for (int i = 0; i < argc; i++) { 18 | if (!strcmp(argv[i], flag)) { 19 | if (i + 1 < argc) { 20 | return argv[i + 1]; 21 | } 22 | } 23 | } 24 | return NULL; 25 | } 26 | 27 | bool argument_exists(int argc, char *argv[], const char *flag) { 28 | for (int i = 0; i < argc; i++) { 29 | if (!strcmp(argv[i], flag)) { 30 | return true; 31 | } 32 | } 33 | return false; 34 | } 35 | 36 | char *extract_preferred_slice(const char *fatPath) 37 | { 38 | FAT *fat = fat_init_from_path(fatPath); 39 | if (!fat) return NULL; 40 | MachO *macho = fat_find_preferred_slice(fat); 41 | 42 | #if TARGET_OS_MAC && !TARGET_OS_IPHONE 43 | if (!macho) { 44 | // Check for arm64v8 first 45 | macho = fat_find_slice(fat, CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_V8); 46 | if (!macho) { 47 | // If that fails, check for regular arm64 48 | macho = fat_find_slice(fat, CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL); 49 | if (!macho) { 50 | // If that fails, check for arm64e with ABI v2 51 | macho = fat_find_slice(fat, CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64E | CPU_SUBTYPE_ARM64E_ABI_V2); 52 | if (!macho) { 53 | // If that fails, check for arm64e 54 | macho = fat_find_slice(fat, CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64E); 55 | if (!macho) { 56 | fat_free(fat); 57 | return NULL; 58 | } 59 | } 60 | } 61 | } 62 | } 63 | #else 64 | if (!macho) { 65 | fat_free(fat); 66 | return NULL; 67 | } 68 | #endif // TARGET_OS_MAC && !TARGET_OS_IPHONE 69 | 70 | if (macho->machHeader.filetype == MH_OBJECT) { 71 | printf("Error: MachO is an object file, please use a MachO executable or dynamic library!\n"); 72 | fat_free(fat); 73 | return NULL; 74 | } 75 | 76 | if (macho->machHeader.filetype == MH_DSYM) { 77 | printf("Error: MachO is a dSYM file, please use a MachO executable or dynamic library!\n"); 78 | fat_free(fat); 79 | return NULL; 80 | } 81 | 82 | char *temp = strdup("/tmp/XXXXXX"); 83 | int fd = mkstemp(temp); 84 | 85 | MemoryStream *outStream = file_stream_init_from_path(temp, 0, 0, FILE_STREAM_FLAG_WRITABLE | FILE_STREAM_FLAG_AUTO_EXPAND); 86 | MemoryStream *machoStream = macho_get_stream(macho); 87 | memory_stream_copy_data(machoStream, 0, outStream, 0, memory_stream_get_size(machoStream)); 88 | 89 | fat_free(fat); 90 | memory_stream_free(outStream); 91 | close(fd); 92 | return temp; 93 | } 94 | 95 | void print_usage(const char *self) { 96 | printf("Options: \n"); 97 | printf("\t-i: input file\n"); 98 | printf("\t-c: input CMS\n"); 99 | printf("\t-C: input code directory\n"); 100 | printf("\t-h: print this help message\n"); 101 | printf("Examples:\n"); 102 | printf("\t%s -i \n", self); 103 | printf("\t%s -c -C \n", self); 104 | exit(-1); 105 | } 106 | 107 | void evaluate_code_signature(CT_uint8_t *cmsData, CT_size_t cmsLen, 108 | CT_uint8_t *codeDirectoryData, 109 | CT_size_t codeDirectoryLen, 110 | CS_DecodedSuperBlob *superblob) { 111 | const CT_uint8_t *leafCert = NULL; 112 | CT_size_t leafCertLen = 0; 113 | CoreTrustPolicyFlags policyFlags = 0; 114 | CoreTrustDigestType cmsDigestType = 0; 115 | CoreTrustDigestType hashAgilityDigestType = 0; 116 | const CT_uint8_t *digestData = NULL; 117 | CT_size_t digestLen = 0; 118 | 119 | CT_int result = CTEvaluateAMFICodeSignatureCMS( 120 | cmsData, cmsLen, codeDirectoryData, codeDirectoryLen, false, &leafCert, 121 | &leafCertLen, &policyFlags, &cmsDigestType, &hashAgilityDigestType, 122 | &digestData, &digestLen); 123 | 124 | if (result == 0) { 125 | if (policyFlags == 0) { 126 | printf("CoreTrust evaluation was successful, but there were no matching " 127 | "policies found for the certificate.\n"); 128 | return; 129 | } 130 | 131 | printf("CoreTrust evaluation was successful!\n"); 132 | printPolicyInformation(policyFlags); 133 | 134 | if (hashAgilityDigestType != 0) { 135 | printf("CMS uses Apple Hash Agility V2, chosen hash type is "); 136 | printDigestType(hashAgilityDigestType); 137 | printf(".\n"); 138 | 139 | } else if (digestLen != 0) { 140 | printf("CMS uses Apple Hash Agility v1.\n"); 141 | } else { 142 | printf("CMS does not use Apple Hash Agility!\n"); 143 | return; 144 | } 145 | 146 | printf("AMFI will expect CD hash of "); 147 | printDigestType(cmsDigestType); 148 | printf(" code directory to be "); 149 | for (CT_size_t i = 0; i < digestLen; i++) { 150 | printf("%02x", digestData[i]); 151 | } 152 | printf(".\n"); 153 | 154 | if (superblob) { 155 | void *cdhashOut = malloc(CS_CDHASH_LEN); 156 | csd_superblob_calculate_best_cdhash(superblob, cdhashOut); 157 | 158 | if (memcmp(cdhashOut, digestData, CS_CDHASH_LEN) == 0) { 159 | printf("CD hash matches the expected hash.\n"); 160 | } else { 161 | printf("CD hash does not match the expected hash.\n"); 162 | } 163 | 164 | free(cdhashOut); 165 | } 166 | 167 | } else { 168 | printf("Error: CTEvaluateAMFICodeSignatureCMS returned 0x%x.\n", result); 169 | } 170 | } 171 | 172 | void* get_file_data(const char* filename, size_t *sizeOut) { 173 | FILE *file = fopen(filename, "r"); 174 | if (file == NULL) { 175 | fprintf(stderr, "Error opening file: %s\n", filename); 176 | return NULL; 177 | } 178 | 179 | // Seek to the end of the file to determine its size 180 | fseek(file, 0, SEEK_END); 181 | long file_size = ftell(file); 182 | fseek(file, 0, SEEK_SET); 183 | 184 | // Allocate memory to store the file contents 185 | void *data = malloc(file_size); 186 | if (data == NULL) { 187 | fprintf(stderr, "Memory allocation error\n"); 188 | fclose(file); 189 | return NULL; 190 | } 191 | 192 | // Read the file contents into the allocated memory 193 | size_t bytes_read = fread(data, 1, file_size, file); 194 | if (bytes_read != file_size) { 195 | fprintf(stderr, "Error reading file: %s\n", filename); 196 | fclose(file); 197 | free(data); 198 | return NULL; 199 | } 200 | 201 | *sizeOut = bytes_read; 202 | 203 | fclose(file); 204 | return data; 205 | } 206 | 207 | int main(int argc, char *argv[]) { 208 | const char *inputPath = get_argument_value(argc, argv, "-i"); 209 | if (!inputPath) { 210 | 211 | 212 | void *inputCMS = get_argument_value(argc, argv, "-c"); 213 | void *inputCD = get_argument_value(argc, argv, "-C"); 214 | if (!inputCMS || !inputCD) { 215 | print_usage(argv[0]); 216 | } 217 | size_t cmsSize, cdSize; 218 | void *cms = get_file_data(inputCMS, &cmsSize); 219 | void *cd = get_file_data(inputCD, &cdSize); 220 | 221 | evaluate_code_signature(cms, cmsSize, cd, cdSize, NULL); 222 | 223 | return 0; 224 | } 225 | 226 | char *preferredSlice = extract_preferred_slice(inputPath); 227 | if (!preferredSlice) { 228 | printf("Error: failed to extract preferred slice!\n"); 229 | return -1; 230 | } 231 | 232 | MachO *macho = macho_init_for_writing(preferredSlice); 233 | if (!macho) { 234 | free(preferredSlice); 235 | return -1; 236 | } 237 | 238 | CS_SuperBlob *superblob = macho_read_code_signature(macho); 239 | if (!superblob) { 240 | printf("Error: no code signature found, please fake-sign the binary at minimum before running the bypass.\n"); 241 | free(preferredSlice); 242 | return -1; 243 | } 244 | 245 | CS_DecodedSuperBlob *decodedSuperblob = csd_superblob_decode(superblob); 246 | CS_DecodedBlob *signatureBlob = csd_superblob_find_blob(decodedSuperblob, CSSLOT_SIGNATURESLOT, NULL); 247 | if (!signatureBlob) { 248 | printf("Error: no signature blob found!\n"); 249 | free(preferredSlice); 250 | return -1; 251 | } 252 | 253 | size_t sigBlobLen = csd_blob_get_size(signatureBlob) - 8; 254 | uint8_t *sigBlob = malloc(sigBlobLen); 255 | csd_blob_read(signatureBlob, 8, sigBlobLen, sigBlob); 256 | 257 | CS_DecodedBlob *codeDirectory = csd_superblob_find_blob(decodedSuperblob, CSSLOT_CODEDIRECTORY, NULL); 258 | if (!codeDirectory) { 259 | printf("Error: no code directory found!\n"); 260 | free(preferredSlice); 261 | return -1; 262 | } 263 | 264 | size_t codeDirectoryBlobLen = csd_blob_get_size(codeDirectory); 265 | uint8_t *codeDirectoryBlob = malloc(codeDirectoryBlobLen); 266 | csd_blob_read(codeDirectory, 0, codeDirectoryBlobLen, codeDirectoryBlob); 267 | 268 | evaluate_code_signature(sigBlob, sigBlobLen, codeDirectoryBlob, codeDirectoryBlobLen, decodedSuperblob); 269 | 270 | csd_superblob_free(decodedSuperblob); 271 | free(preferredSlice); 272 | free(sigBlob); 273 | free(codeDirectoryBlob); 274 | 275 | return 0; 276 | } 277 | -------------------------------------------------------------------------------- /lib/ios/MobileInBoxUpdate.tbd: -------------------------------------------------------------------------------- 1 | --- !tapi-tbd 2 | tbd-version: 4 3 | targets: [ arm64-ios, arm64e-ios ] 4 | install-name: '/System/Library/PrivateFrameworks/MobileInBoxUpdate.framework/MobileInBoxUpdate' 5 | exports: 6 | - targets: [ arm64-ios, arm64e-ios ] 7 | symbols: [ _ACRTRootPublicKey, 8 | _AppleRootCA, 9 | _AppleRootCAG2, 10 | _AppleRootCAG3, 11 | _AppleRootCASPKI, 12 | _AppleRootG2SPKI, 13 | _AppleRootG3SPKI, 14 | _AppleRoots, 15 | _BAARoots, 16 | _BASystemRoot, 17 | _BASystemRootPublicKey, 18 | _BASystemRootSPKI, 19 | _BAUserRoot, 20 | _BAUserRootPublicKey, 21 | _BAUserRootSPKI, 22 | _BlockedYonkersSPKI, 23 | _CMSBuildPath, 24 | _CMSParseContentInfoSignedData, 25 | _CMSParseContentInfoSignedDataWithOptions, 26 | _CMSParseSignerInfos, 27 | _CMSVerifyAndReturnSignedData, 28 | _CMSVerifySignedData, 29 | _CMSVerifySignedDataWithLeaf, 30 | _CTCopyDeviceIdentifiers, 31 | _CTEvaluateAMFICodeSignatureCMS, 32 | _CTEvaluateAMFICodeSignatureCMSPubKey, 33 | _CTEvaluateAMFICodeSignatureCMS_MaxDigestType, 34 | _CTEvaluateAccessoryCert, 35 | _CTEvaluateAcrt, 36 | _CTEvaluateAppleSSL, 37 | _CTEvaluateAppleSSLWithOptionalTemporalCheck, 38 | _CTEvaluateBAAAccessory, 39 | _CTEvaluateBAASystem, 40 | _CTEvaluateBAASystemTestRoot, 41 | _CTEvaluateBAASystemWithId, 42 | _CTEvaluateBAAUser, 43 | _CTEvaluateBAAUserTestRoot, 44 | _CTEvaluateCertsForPolicy, 45 | _CTEvaluateKDLSignatureCMS, 46 | _CTEvaluatePragueSignatureCMS, 47 | _CTEvaluateProvisioningProfile, 48 | _CTEvaluateSatori, 49 | _CTEvaluateSavageCerts, 50 | _CTEvaluateSavageCertsWithUID, 51 | _CTEvaluateSensorCerts, 52 | _CTEvaluateUcrt, 53 | _CTEvaluateUcrtTestRoot, 54 | _CTEvaluateYonkersCerts, 55 | _CTOctetServerAuthEKU, 56 | _CTOidAlgorithmProtection, 57 | _CTOidAppleAAICAG3, 58 | _CTOidAppleASICA4, 59 | _CTOidAppleCertifiedChipCA, 60 | _CTOidAppleCertifiedChipLeaf, 61 | _CTOidAppleCloudManaged, 62 | _CTOidAppleCodeSigningCA, 63 | _CTOidAppleComponentAuth, 64 | _CTOidAppleDeveloperID, 65 | _CTOidAppleDeveloperIDCA, 66 | _CTOidAppleDeviceAttestationDeviceOSInformation, 67 | _CTOidAppleDeviceAttestationHardwareProperties, 68 | _CTOidAppleDeviceAttestationIdentity, 69 | _CTOidAppleDeviceAttestationKeyUsageProperties, 70 | _CTOidAppleDeviceAttestationNonce, 71 | _CTOidAppleExtensionArc, 72 | _CTOidAppleGenericSSLMarker, 73 | _CTOidAppleHashAgility, 74 | _CTOidAppleHashAgilityV2, 75 | _CTOidAppleHaven, 76 | _CTOidAppleImg4Manifest, 77 | _CTOidAppleKextDenyListSigning, 78 | _CTOidAppleMFI4Properties, 79 | _CTOidAppleMFIAuthv3, 80 | _CTOidAppleMFISWAuth, 81 | _CTOidAppleMacAppStore, 82 | _CTOidAppleMacDeveloper, 83 | _CTOidAppleMacPlatform, 84 | _CTOidAppleMacSubmission, 85 | _CTOidApplePragueSigning, 86 | _CTOidAppleProvisioningProfileSigner, 87 | _CTOidAppleSatoriExternalEncryption, 88 | _CTOidAppleServerAuthCA, 89 | _CTOidAppleServerAuthExtensionPrefix, 90 | _CTOidAppleServerAuthExtensionPrefixLen, 91 | _CTOidAppleServerAuthExtensionPrefixString, 92 | _CTOidAppleTVOSAppStoreDev, 93 | _CTOidAppleTVOSAppStoreProd, 94 | _CTOidAppleTestFlightDev, 95 | _CTOidAppleTestFlightProd, 96 | _CTOidAppleUniqueDeviceIdentifiers, 97 | _CTOidAppleWWDRCA, 98 | _CTOidAppleiPhoneAppStoreDev, 99 | _CTOidAppleiPhoneAppStoreProd, 100 | _CTOidAppleiPhoneCA, 101 | _CTOidAppleiPhoneDeveloper, 102 | _CTOidAppleiPhoneDistribution, 103 | _CTOidAppleiPhoneVPNAppDev, 104 | _CTOidAppleiPhoneVPNAppProd, 105 | _CTOidAuthorityKeyID, 106 | _CTOidBasicConstraints, 107 | _CTOidCommonName, 108 | _CTOidContentType, 109 | _CTOidExtendedKeyUsage, 110 | _CTOidItemAppleDeviceAttestationDeviceOSInformation, 111 | _CTOidItemAppleDeviceAttestationHardwareProperties, 112 | _CTOidItemAppleDeviceAttestationKeyUsageProperties, 113 | _CTOidItemAppleDeviceAttestationNonce, 114 | _CTOidItemAppleImg4Manifest, 115 | _CTOidKeyUsage, 116 | _CTOidMessageDigest, 117 | _CTOidSECP256r1, 118 | _CTOidSECP384r1, 119 | _CTOidSECP521r1, 120 | _CTOidSMIMECapabilities, 121 | _CTOidServerAuthEKU, 122 | _CTOidSha1, 123 | _CTOidSha224, 124 | _CTOidSha256, 125 | _CTOidSha384, 126 | _CTOidSha512, 127 | _CTOidSigningTime, 128 | _CTOidSubjectAltName, 129 | _CTOidSubjectKeyID, 130 | _CTParseAccessoryCerts, 131 | _CTParseAmfiCMS, 132 | _CTParseCertificateSet, 133 | _CTParseExtensionValue, 134 | _CTParseFlagsForAccessoryCerts, 135 | _CTParseKey, 136 | _CTVerifyAmfiCMS, 137 | _CTVerifyAmfiCertificateChain, 138 | _CTVerifyAppleMarkerExtension, 139 | _CTVerifyHostname, 140 | _CodeSigningCAName, 141 | _CodeSigningCAName_str, 142 | _MFICommonNamePrefix, 143 | _MFICommonNamePrefix_str, 144 | _MFi4AccessoryCAName, 145 | _MFi4AccessoryCAName_str, 146 | _MFi4AttestationCAName, 147 | _MFi4AttestationCAName_str, 148 | _MFi4ProvisioningCAName, 149 | _MFi4ProvisioningCAName_str, 150 | _MFi4ProvisioningHostNamePrefix, 151 | _MFi4ProvisioningHostNamePrefix_str, 152 | _MFi4RootPublicKey, 153 | _MFi4RootSpki, 154 | _MIBUConnObj, 155 | _MIBUOnceToken, 156 | _MobileInBoxUpdateVersionNumber, 157 | _MobileInBoxUpdateVersionString, 158 | _MobileInBoxUpdaterErrorDomain, 159 | _OBJC_CLASS_$_MIBUCertHelper, 160 | _OBJC_CLASS_$_MIBUClient, 161 | _OBJC_CLASS_$_MIBUEAPConfiguartion, 162 | _OBJC_CLASS_$_MIBUTestPreferences, 163 | _OBJC_METACLASS_$_MIBUCertHelper, 164 | _OBJC_METACLASS_$_MIBUClient, 165 | _OBJC_METACLASS_$_MIBUEAPConfiguartion, 166 | _OBJC_METACLASS_$_MIBUTestPreferences, 167 | _TestAppleRootCA, 168 | _TestAppleRootCASPKI, 169 | _TestAppleRootECC, 170 | _TestAppleRootECCSPKI, 171 | _TestAppleRootG2, 172 | _TestAppleRootG2SPKI, 173 | _TestAppleRootG3, 174 | _TestAppleRootG3SPKI, 175 | _UcrtRootPublicKey, 176 | _UcrtRootSpki, 177 | _X509CertificateCheckSignature, 178 | _X509CertificateCheckSignatureDigest, 179 | _X509CertificateCheckSignatureWithPublicKey, 180 | _X509CertificateGetNotAfter, 181 | _X509CertificateGetNotBefore, 182 | _X509CertificateIsValid, 183 | _X509CertificateParse, 184 | _X509CertificateParseGeneralNamesContent, 185 | _X509CertificateParseImplicit, 186 | _X509CertificateParseKey, 187 | _X509CertificateParseSPKI, 188 | _X509CertificateParseValidity, 189 | _X509CertificateParseWithExtension, 190 | _X509CertificateSubjectNameGetCommonName, 191 | _X509CertificateValidAtTime, 192 | _X509ChainBuildPath, 193 | _X509ChainCheckPath, 194 | _X509ChainCheckPathWithOptions, 195 | _X509ChainGetCertificateUsingKeyIdentifier, 196 | _X509ChainParseCertificateSet, 197 | _X509ChainResetChain, 198 | _X509PolicyCheckForBlockedKeys, 199 | _X509PolicySetFlagsForCommonNames, 200 | _X509PolicySetFlagsForMFI, 201 | _X509PolicySetFlagsForRoots, 202 | __createError, 203 | _asciiNibbleToByte, 204 | _ccder_blob_check_null, 205 | _ccder_blob_decode_AlgorithmIdentifierNULL, 206 | _ccder_blob_decode_GeneralName, 207 | _ccder_blob_decode_Time, 208 | _compare_octet_string, 209 | _compare_octet_string_partial, 210 | _compare_octet_string_raw, 211 | _compressECPublicKey, 212 | _decompressECPublicKey, 213 | _ecPublicKey, 214 | _find_digest, 215 | _find_digestOID_for_signingOID, 216 | _find_digest_by_type, 217 | _iPhoneCAName, 218 | _iPhoneCAName_str, 219 | _null_octet, 220 | _numAppleProdRoots, 221 | _numAppleRoots, 222 | _numBAARoots, 223 | _oidForPubKeyLength, 224 | _pkcs7_data_oid_str, 225 | _pkcs7_signedData_oid_str, 226 | _powersOfTen, 227 | _rsaEncryption, 228 | _safeAssignError, 229 | _sha1WithECDSA_oid, 230 | _sha1WithRSA_oid, 231 | _sha256WithECDSA_oid, 232 | _sha256WithRSA_oid, 233 | _sha384WithECDSA_oid, 234 | _sha384WithRSA_oid, 235 | _sha512WithECDSA_oid, 236 | _sha512WithRSA_oid, 237 | _suCertPaths, 238 | _validateSignatureEC, 239 | _validateSignatureRSA, 240 | _validateSignerInfo, 241 | _validateSignerInfoAndChain ] 242 | objc-classes: [ MIBUCertHelper, 243 | MIBUClient, 244 | MIBUEAPConfiguartion, 245 | MIBUTestPreferences ] 246 | objc-ivars: [ MIBUClient._connection, 247 | MIBUEAPConfiguartion._tlsCertificateChain, 248 | MIBUEAPConfiguartion._tlsKey ] 249 | ... 250 | 251 | -------------------------------------------------------------------------------- /CoreTrust.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | typedef uint8_t CT_uint8_t; 6 | typedef uint32_t CT_uint32_t; 7 | typedef uint64_t CT_uint64_t; 8 | typedef size_t CT_size_t; 9 | typedef int CT_int; 10 | typedef bool CT_bool; 11 | 12 | typedef struct x509_octet_string { 13 | const CT_uint8_t *data; 14 | CT_size_t length; 15 | } CTAsn1Item; 16 | 17 | typedef CT_uint64_t CoreTrustPolicyFlags; 18 | enum { 19 | CORETRUST_POLICY_BASIC = 0, 20 | CORETRUST_POLICY_SAVAGE_DEV = 1 << 0, 21 | CORETRUST_POLICY_SAVAGE_PROD = 1 << 1, 22 | CORETRUST_POLICY_MFI_AUTHV3 = 1 << 2, 23 | CORETRUST_POLICY_MAC_PLATFORM = 1 << 3, 24 | CORETRUST_POLICY_MAC_DEVELOPER = 1 << 4, 25 | CORETRUST_POLICY_DEVELOPER_ID = 1 << 5, 26 | CORETRUST_POLICY_MAC_APP_STORE = 1 << 6, 27 | CORETRUST_POLICY_IPHONE_DEVELOPER = 1 << 7, 28 | CORETRUST_POLICY_IPHONE_APP_PROD = 1 << 8, 29 | CORETRUST_POLICY_IPHONE_APP_DEV = 1 << 9, 30 | CORETRUST_POLICY_IPHONE_VPN_PROD = 1 << 10, 31 | CORETRUST_POLICY_IPHONE_VPN_DEV = 1 << 11, 32 | CORETRUST_POLICY_TVOS_APP_PROD = 1 << 12, 33 | CORETRUST_POLICY_TVOS_APP_DEV = 1 << 13, 34 | CORETRUST_POLICY_TEST_FLIGHT_PROD = 1 << 14, 35 | CORETRUST_POLICY_TEST_FLIGHT_DEV = 1 << 15, 36 | CORETRUST_POLICY_IPHONE_DISTRIBUTION = 1 << 16, 37 | CORETRUST_POLICY_MAC_SUBMISSION = 1 << 17, 38 | CORETRUST_POLICY_YONKERS_DEV = 1 << 18, 39 | CORETRUST_POLICY_YONKERS_PROD = 1 << 19, 40 | CORETRUST_POLICY_MAC_PLATFORM_G2 = 1 << 20, 41 | CORETRUST_POLICY_ACRT = 1 << 21, 42 | CORETRUST_POLICY_SATORI = 1 << 22, 43 | CORETRUST_POLICY_BAA = 1 << 23, 44 | CORETRUST_POLICY_BAA_SYSTEM = 1 << 23, // BAA and BAA_SYSTEM are the same 45 | CORETRUST_POLICY_UCRT = 1 << 24, 46 | CORETRUST_POLICY_PRAGUE = 1 << 25, 47 | CORETRUST_POLICY_KDL = 1 << 26, 48 | CORETRUST_POLICY_MFI_AUTHV2 = 1 << 27, 49 | CORETRUST_POLICY_MFI_SW_AUTH_PROD = 1 << 28, 50 | CORETRUST_POLICY_MFI_SW_AUTH_DEV = 1 << 29, 51 | CORETRUST_POLICY_COMPONENT = 1 << 30, 52 | CORETRUST_POLICY_IMG4 = 1ULL << 31, 53 | CORETRUST_POLICY_SERVER_AUTH = 1ULL << 32, 54 | CORETRUST_POLICY_SERVER_AUTH_STRING = 1ULL << 33, 55 | CORETRUST_POLICY_MFI_AUTHV4_ACCESSORY = 1ULL << 34, 56 | CORETRUST_POLICY_MFI_AUTHV4_ATTESTATION = 1ULL << 35, 57 | CORETRUST_POLICY_MFI_AUTHV4_PROVISIONING = 1ULL << 36, 58 | CORETRUST_POLICY_WWDR_CLOUD_MANAGED = 1ULL << 37, 59 | CORETRUST_POLICY_HAVEN = 1ULL << 38, 60 | CORETRUST_POLICY_PROVISIONING_PROFILE = 1ULL << 39, 61 | CORETRUST_POLICY_SENSOR_PROD = 1ULL << 40, 62 | CORETRUST_POLICY_SENSOR_DEV = 1ULL << 41, 63 | CORETRUST_POLICY_BAA_USER = 1ULL << 42, 64 | }; 65 | 66 | void printPolicyInformation(CoreTrustPolicyFlags policyFlags) { 67 | printf("CoreTrust policy flags (0x%llx):\n", policyFlags); 68 | if (policyFlags & CORETRUST_POLICY_BASIC) { 69 | printf(" - Basic\n"); 70 | } 71 | if (policyFlags & CORETRUST_POLICY_SAVAGE_DEV) { 72 | printf(" - Savage (Development)\n"); 73 | } 74 | if (policyFlags & CORETRUST_POLICY_SAVAGE_PROD) { 75 | printf(" - Savage (Production)\n"); 76 | } 77 | if (policyFlags & CORETRUST_POLICY_MFI_AUTHV3) { 78 | printf(" - MFi Auth v3\n"); 79 | } 80 | if (policyFlags & CORETRUST_POLICY_MAC_PLATFORM) { 81 | printf(" - Mac Platform\n"); 82 | } 83 | if (policyFlags & CORETRUST_POLICY_MAC_DEVELOPER) { 84 | printf(" - Mac Developer\n"); 85 | } 86 | if (policyFlags & CORETRUST_POLICY_DEVELOPER_ID) { 87 | printf(" - Developer ID\n"); 88 | } 89 | if (policyFlags & CORETRUST_POLICY_MAC_APP_STORE) { 90 | printf(" - Mac App Store\n"); 91 | } 92 | if (policyFlags & CORETRUST_POLICY_IPHONE_DEVELOPER) { 93 | printf(" iPhone Developer\n"); 94 | } 95 | if (policyFlags & CORETRUST_POLICY_IPHONE_APP_PROD) { 96 | printf(" - iPhone App Store\n"); 97 | } 98 | if (policyFlags & CORETRUST_POLICY_IPHONE_APP_DEV) { 99 | printf(" - iPhone App (Development)\n"); 100 | } 101 | if (policyFlags & CORETRUST_POLICY_IPHONE_VPN_PROD) { 102 | printf(" - iPhone VPN (Production)\n"); 103 | } 104 | if (policyFlags & CORETRUST_POLICY_IPHONE_VPN_DEV) { 105 | printf(" - iPhone VPN (Development)\n"); 106 | } 107 | if (policyFlags & CORETRUST_POLICY_TVOS_APP_PROD) { 108 | printf(" - tvOS App Store\n"); 109 | } 110 | if (policyFlags & CORETRUST_POLICY_TVOS_APP_DEV) { 111 | printf(" - tvOS App (Development)\n"); 112 | } 113 | if (policyFlags & CORETRUST_POLICY_TEST_FLIGHT_PROD) { 114 | printf(" - TestFlight (Production)\n"); 115 | } 116 | if (policyFlags & CORETRUST_POLICY_TEST_FLIGHT_DEV) { 117 | printf(" - TestFlight (Development)\n"); 118 | } 119 | if (policyFlags & CORETRUST_POLICY_IPHONE_DISTRIBUTION) { 120 | printf(" - iPhone (Distribution)\n"); 121 | } 122 | if (policyFlags & CORETRUST_POLICY_MAC_SUBMISSION) { 123 | printf(" - Mac Submission\n"); 124 | } 125 | if (policyFlags & CORETRUST_POLICY_YONKERS_DEV) { 126 | printf(" - Yonkers (Development)\n"); 127 | } 128 | if (policyFlags & CORETRUST_POLICY_YONKERS_PROD) { 129 | printf(" - Yonkers (Production)\n"); 130 | } 131 | if (policyFlags & CORETRUST_POLICY_MAC_PLATFORM_G2) { 132 | printf(" - Mac Platform G2\n"); 133 | } 134 | if (policyFlags & CORETRUST_POLICY_ACRT) { 135 | printf(" - ACRT\n"); 136 | } 137 | if (policyFlags & CORETRUST_POLICY_SATORI) { 138 | printf(" - Satori\n"); 139 | } 140 | if (policyFlags & CORETRUST_POLICY_BAA) { 141 | printf(" - BAA\n"); 142 | } 143 | if (policyFlags & CORETRUST_POLICY_UCRT) { 144 | printf(" - UCRT\n"); 145 | } 146 | if (policyFlags & CORETRUST_POLICY_PRAGUE) { 147 | printf(" - Prague\n"); 148 | } 149 | if (policyFlags & CORETRUST_POLICY_KDL) { 150 | printf(" - KDL\n"); 151 | } 152 | if (policyFlags & CORETRUST_POLICY_MFI_AUTHV2) { 153 | printf(" - MFi Auth v2\n"); 154 | } 155 | if (policyFlags & CORETRUST_POLICY_MFI_SW_AUTH_PROD) { 156 | printf(" - MFi SW Auth (Production)\n"); 157 | } 158 | if (policyFlags & CORETRUST_POLICY_MFI_SW_AUTH_DEV) { 159 | printf(" - MFi SW Auth (Development)\n"); 160 | } 161 | if (policyFlags & CORETRUST_POLICY_COMPONENT) { 162 | printf(" - Component\n"); 163 | } 164 | if (policyFlags & CORETRUST_POLICY_IMG4) { 165 | printf(" - IMG4\n"); 166 | } 167 | if (policyFlags & CORETRUST_POLICY_SERVER_AUTH) { 168 | printf(" - Server Auth\n"); 169 | } 170 | if (policyFlags & CORETRUST_POLICY_SERVER_AUTH_STRING) { 171 | printf(" - Server Auth String\n"); 172 | } 173 | if (policyFlags & CORETRUST_POLICY_MFI_AUTHV4_ACCESSORY) { 174 | printf(" - MFi Auth v4 Accessory\n"); 175 | } 176 | if (policyFlags & CORETRUST_POLICY_MFI_AUTHV4_ATTESTATION) { 177 | printf(" - MFi Auth v4 Attestation\n"); 178 | } 179 | if (policyFlags & CORETRUST_POLICY_MFI_AUTHV4_PROVISIONING) { 180 | printf(" - MFi Auth v4 Provisioning\n"); 181 | } 182 | if (policyFlags & CORETRUST_POLICY_WWDR_CLOUD_MANAGED) { 183 | printf(" - WWDR (Cloud Managed)\n"); 184 | } 185 | if (policyFlags & CORETRUST_POLICY_HAVEN) { 186 | printf(" - Haven\n"); 187 | } 188 | if (policyFlags & CORETRUST_POLICY_PROVISIONING_PROFILE) { 189 | printf(" Provisioning Profile\n"); 190 | } 191 | if (policyFlags & CORETRUST_POLICY_SENSOR_PROD) { 192 | printf(" - Sensor (Production)\n"); 193 | } 194 | if (policyFlags & CORETRUST_POLICY_SENSOR_DEV) { 195 | printf(" - Sensor (Development)\n"); 196 | } 197 | if (policyFlags & CORETRUST_POLICY_BAA_USER) { 198 | printf(" - BAA User\n"); 199 | } 200 | printf("\n"); 201 | } 202 | 203 | typedef CT_uint32_t CoreTrustDigestType; 204 | enum { 205 | CORETRUST_DIGEST_TYPE_SHA1 = 1, 206 | CORETRUST_DIGEST_TYPE_SHA224 = 2, 207 | CORETRUST_DIGEST_TYPE_SHA256 = 4, 208 | CORETRUST_DIGEST_TYPE_SHA384 = 8, 209 | CORETRUST_DIGEST_TYPE_SHA512 = 16 210 | }; 211 | 212 | void printDigestType(CoreTrustDigestType digestType) { 213 | switch (digestType) { 214 | case CORETRUST_DIGEST_TYPE_SHA1: 215 | printf("SHA-1"); 216 | break; 217 | case CORETRUST_DIGEST_TYPE_SHA224: 218 | printf("SHA-224"); 219 | break; 220 | case CORETRUST_DIGEST_TYPE_SHA256: 221 | printf("SHA-256"); 222 | break; 223 | case CORETRUST_DIGEST_TYPE_SHA384: 224 | printf("SHA-384"); 225 | break; 226 | case CORETRUST_DIGEST_TYPE_SHA512: 227 | printf("SHA-512"); 228 | break; 229 | default: 230 | printf("unknown"); 231 | break; 232 | } 233 | } 234 | 235 | /*! @function CTEvaluateAMFICodeSignatureCMS 236 | @abstract Verify CMS signature and certificates against the AMFI policies 237 | @param cmsData pointer to beginning of the binary (BER-encoded) CMS object 238 | @param cmsLen the length of the CMS object 239 | @param detachedData pointer to data that is signed by the CMS object 240 | @param detachedDataLen the length of the signed data 241 | @param allow_test_hierarchy allow the Test Apple roots to be used as anchors in addition to the production roots 242 | @param leafCert return value, pointer to the verified leaf certificate 243 | @param leafCertLen return value, length of the verified leaf certificate 244 | @param policyFlags return value, the CoreTrust policies that the certificate chain met 245 | @param cmsDigestType return value, the digest type used to sign the CMS object 246 | @param hashAgilityDigestType return value, the highest strength digest type available in the hash agility attribute 247 | @param digestData return value, pointer to the hash agility value 248 | @param digestLen return value, length of the hash agility value 249 | @return 0 upon success, a parsing or validation error (see CTErrors.h) 250 | @discussion 251 | Returns non-zero if there's a standards-based problem with the CMS or certificates. 252 | Policy matching of the certificates is only reflected in the policyFlags output. Namely, if the only problem is that 253 | the certificates don't match a policy, the returned integer will be 0 (success) and the policyFlags will be 0 (no matching policies). 254 | Some notes about hash agility outputs: 255 | - hashAgilityDigestType is only non-zero for HashAgilityV2 256 | - If hashAgilityDigestType is non-zero, digestData/Len provides the digest value 257 | - If hashAgilityDigestType is zero, digestData/Len provides the content of the HashAgilityV1 attribute (if present) 258 | - If neither HashAgilityV1 nor HashAgilityV2 attributes are found, these outputs will all be NULL. 259 | */ 260 | CT_int CTEvaluateAMFICodeSignatureCMS( 261 | const CT_uint8_t *cmsData, CT_size_t cmsLen, 262 | const CT_uint8_t *detachedData, CT_size_t detachedDataLen, 263 | CT_bool allow_test_hierarchy, 264 | const CT_uint8_t **leafCert, CT_size_t *leafCertLen, 265 | CoreTrustPolicyFlags *policyFlags, 266 | CoreTrustDigestType *cmsDigestType, 267 | CoreTrustDigestType *hashAgilityDigestType, 268 | const CT_uint8_t **digestData, CT_size_t *digestLen); 269 | 270 | /*! @function CTVerifyAmfiCMS 271 | @abstract Verify CMS signed data signature 272 | @param cmsData pointer to beginning of the binary (BER-encoded) CMS object 273 | @param cmsLen the length of the CMS object 274 | @param digestData pointer to beginning of the content data hash 275 | @param digestLen the length of the content data hash 276 | @param maxDigestType maximum digest type supported by the client 277 | @param hashAgilityDigestType return value, the highest strength digest type available in the hash agility attribute 278 | @param hashAgilityDigestData return value, pointer to the hash agility value 279 | @param hashAgilityDigestLen return value, length of the hash agility value 280 | @return 0 upon success, a parsing or validation error (see CTErrors.h) 281 | @discussion 282 | Returns non-zero if there's a standards-based problem with the CMS or certificates. 283 | Some notes about hash agility outputs: 284 | - hashAgilityDigestType is only non-zero for HashAgilityV2 285 | - If hashAgilityDigestType is non-zero, digestData/Len provides the digest value 286 | - If hashAgilityDigestType is zero, digestData/Len provides the content of the HashAgilityV1 attribute (if present) 287 | - If neither HashAgilityV1 nor HashAgilityV2 attributes are found, these outputs will all be NULL. 288 | */ 289 | CT_int CTVerifyAmfiCMS( 290 | const CT_uint8_t *cmsData, CT_size_t cmsLen, 291 | const CT_uint8_t *digestData, CT_size_t digestLen, 292 | CoreTrustDigestType maxDigestType, 293 | CoreTrustDigestType *hashAgilityDigestType, 294 | const CT_uint8_t **hashAgilityDigestData, CT_size_t *hashAgilityDigestLen); 295 | --------------------------------------------------------------------------------