// // jailbreak.m // Meridian // // Created by Ben Sparkes on 16/02/2018. // Copyright © 2018 Ben Sparkes. All rights reserved. // #include "v0rtex.h" #include "kernel.h" #include "helpers.h" #include "root-rw.h" #include "amfi.h" #include "offsetfinder.h" #include "jailbreak.h" #include "ViewController.h" #include "patchfinder64.h" #include "patchfinders/offsetdump.h" #include "nvpatch.h" #include #include #import NSFileManager *fileMgr; offsets_t offsets; BOOL great_success = FALSE; int makeShitHappen(ViewController *view) { int ret; fileMgr = [NSFileManager defaultManager]; // run v0rtex [view writeText:@"running v0rtex..."]; suspend_all_threads(); ret = runV0rtex(); resume_all_threads(); if (ret != 0) { [view writeText:@"failed!"]; if (ret == -420) { [view writeTextPlain:@"failed to load offsets!"]; } return 1; } [view writeTextPlain:@"succeeded! praize siguza!"]; // set up stuff init_patchfinder(NULL); ret = init_amfi(); if (ret != 0) { [view writeTextPlain:@"failed to initialize amfi class!"]; return 1; } // patch containermanager [view writeText:@"patching containermanager..."]; ret = patchContainermanagerd(); if (ret != 0) { [view writeText:@"failed!"]; return 1; } [view writeText:@"done!"]; // remount root fs [view writeText:@"remounting rootfs as r/w..."]; ret = remountRootFs(); if (ret != 0) { [view writeText:@"failed!"]; return 1; } [view writeText:@"done!"]; /* Begin the filesystem fuckery */ [view writeText:@"some filesytem fuckery..."]; // Remove /meridian in the case of PB's if (file_exists("/meridian") == 0 && file_exists("/meridian/.bootstrap") != 0) { [fileMgr removeItemAtPath:@"/meridian" error:nil]; } if (file_exists("/meridian") != 0) { ret = mkdir("/meridian", 0755); if (ret != 0) { [view writeText:@"failed!"]; [view writeTextPlain:@"creating /meridian failed with error %d: %s", errno, strerror(errno)]; return 1; } } if (file_exists("/meridian/logs") != 0) { ret = mkdir("/meridian/logs", 0755); if (ret != 0) { [view writeText:@"failed!"]; [view writeTextPlain:@"creating /meridian/logs failed with error %d: %s", errno, strerror(errno)]; return 1; } } if (file_exists("/meridian/tar") == 0) { ret = unlink("/meridian/tar"); if (ret != 0) { [view writeText:@"failed!"]; [view writeTextPlain:@"removing /meridian/tar failed with error %d: %s", errno, strerror(errno)]; return 1; } } if (file_exists("/meridian/tar.tar") == 0) { ret = unlink("/meridian/tar.tar"); if (ret != 0) { [view writeText:@"failed!"]; [view writeTextPlain:@"deleting /meridian/tar.tar failed with error %d: %s", errno, strerror(errno)]; return 1; } } ret = extract_bundle("tar.tar", "/meridian"); if (ret != 0) { [view writeText:@"failed!"]; [view writeTextPlain:@"failed to extract tar.tar bundle! ret: %d, errno: %d: %s", ret, errno, strerror(errno)]; return 1; } if (file_exists("/meridian/tar") != 0) { [view writeText:@"failed!"]; [view writeTextPlain:@"/meridian/tar was not found :("]; return 1; } ret = chmod("/meridian/tar", 0755); if (ret != 0) { [view writeText:@"failed!"]; [view writeTextPlain:@"chmod(755)'ing /meridian/tar failed with error %d: %s", errno, strerror(errno)]; return 1; } ret = inject_trust("/meridian/tar"); if (ret != 0) { [view writeText:@"failed!"]; [view writeTextPlain:@"injecting trust to /meridian/tar failed with retcode %d", ret]; return 1; } [view writeText:@"done!"]; // extract meridian-bootstrap [view writeText:@"extracting meridian files..."]; ret = extractMeridianData(); if (ret != 0) { [view writeText:@"failed!"]; [view writeTextPlain:[NSString stringWithFormat:@"error code: %d", ret]]; return 1; } [view writeText:@"done!"]; // dump offsets to file for later use (/meridian/offsets.plist) dumpOffsetsToFile(&offsets, kernel_base, kslide); // patch amfid [view writeText:@"patching amfid..."]; ret = defecateAmfi(); if (ret != 0) { [view writeText:@"failed!"]; if (ret > 0) { [view writeTextPlain:[NSString stringWithFormat:@"failed to patch - %d tries", ret]]; } return 1; } [view writeText:@"done!"]; // touch .cydia_no_stash touch_file("/.cydia_no_stash"); // symlink /Library/MobileSubstrate/DynamicLibraries -> /usr/lib/tweaks setUpSymLinks(); // remove Substrate's SafeMode (MobileSafety) if it's installed // removing from dpkg will be handled by Cydia conflicts later if (file_exists("/usr/lib/tweaks/MobileSafety.dylib") == 0) { unlink("/usr/lib/tweaks/MobileSafety.dylib"); } if (file_exists("/usr/lib/tweaks/MobileSafety.plist") == 0) { unlink("/usr/lib/tweaks/MobileSafety.plist"); } // extract bootstrap (if not already extracted) if (file_exists("/meridian/.bootstrap") != 0) { [view writeText:@"extracting bootstrap..."]; int exitCode = 0; ret = extractBootstrap(&exitCode); if (ret != 0) { [view writeText:@"failed!"]; switch (ret) { case 1: [view writeTextPlain:@"failed to extract system-base.tar"]; break; case 2: [view writeTextPlain:@"failed to extract installer-base.tar"]; break; case 3: [view writeTextPlain:@"failed to extract dpkgdb-base.tar"]; break; case 4: [view writeTextPlain:@"failed to extract cydia-base.tar"]; break; case 5: [view writeTextPlain:@"failed to extract optional-base.tar"]; break; case 6: [view writeTextPlain:@"failed to run uicache!"]; break; } [view writeTextPlain:@"exit code: %d", exitCode]; return 1; } [view writeText:@"done!"]; } // add the midnight repo if (file_exists("/etc/apt/sources.list.d/meridian.list") != 0) { FILE *fd = fopen("/etc/apt/sources.list.d/meridian.list", "w+"); const char *text = "deb http://repo.midnight.team ./"; fwrite(text, strlen(text) + 1, 1, fd); fclose(fd); } // launch dropbear [view writeText:@"launching dropbear..."]; ret = launchDropbear(); if (ret != 0) { [view writeText:@"failed!"]; [view writeTextPlain:@"exit code: %d", ret]; return 1; } [view writeText:@"done!"]; // link substitute stuff setUpSubstitute(); // start jailbreakd [view writeText:@"starting jailbreakd..."]; ret = startJailbreakd(); if (ret != 0) { [view writeText:@"failed"]; if (ret > 1) { [view writeTextPlain:@"failed to launch - %d tries", ret]; } return 1; } [view writeText:@"done!"]; // patch com.apple.System.boot-nonce [view writeText:@"patching boot-nonce..."]; ret = nvpatch("com.apple.System.boot-nonce"); if (ret != 0) { [view writeText:@"failed!"]; return 1; } [view writeText:@"done!"]; // load launchdaemons [view writeText:@"loading launchdaemons..."]; ret = loadLaunchDaemons(); if (ret != 0) { [view writeText:@"failed!"]; return 1; } [view writeText:@"done!"]; great_success = TRUE; return 0; } kern_return_t callback(task_t kern_task, kptr_t kbase, void *cb_data) { tfp0 = kern_task; kernel_base = kbase; kslide = kernel_base - 0xFFFFFFF007004000; return KERN_SUCCESS; } int runV0rtex() { offsets_t *offs = get_offsets(); if (offs == NULL) { return -420; } offsets = *offs; int ret = v0rtex(&offsets, &callback, NULL); uint64_t kernel_task_addr = rk64(offs->kernel_task + kslide); kernprocaddr = rk64(kernel_task_addr + offs->task_bsd_info); kern_ucred = rk64(kernprocaddr + offs->proc_ucred); if (ret == 0) { NSLog(@"tfp0: 0x%x", tfp0); NSLog(@"kernel_base: 0x%llx", kernel_base); NSLog(@"kslide: 0x%llx", kslide); NSLog(@"kern_ucred: 0x%llx", kern_ucred); NSLog(@"kernprocaddr: 0x%llx", kernprocaddr); } return ret; } int patchContainermanagerd() { uint64_t cmgr = find_proc_by_name("containermanager"); if (cmgr == 0) { NSLog(@"unable to find containermanager!"); return 1; } wk64(cmgr + 0x100, kern_ucred); return 0; } int remountRootFs() { NSOperatingSystemVersion osVersion = [[NSProcessInfo processInfo] operatingSystemVersion]; int pre130 = osVersion.minorVersion < 2 ? 1 : 0; int rv = mount_root(kslide, offsets.root_vnode, pre130); if (rv != 0) { return 1; } return 0; } int extractMeridianData() { return extract_bundle_tar("meridian-bootstrap.tar"); } void setUpSymLinks() { struct stat file; stat("/Library/MobileSubstrate/DynamicLibraries", &file); if (file_exists("/Library/MobileSubstrate/DynamicLibraries") == 0 && file_exists("/usr/lib/tweaks") == 0 && S_ISLNK(file.st_mode)) { return; } // By the end of this check, /usr/lib/tweaks should exist containing any // tweaks (if applicable), and /Lib/MobSub/DynLib should NOT exist if (file_exists("/Library/MobileSubstrate/DynamicLibraries") == 0 && file_exists("/usr/lib/tweaks") != 0) { // Move existing tweaks folder to /usr/lib/tweaks [fileMgr moveItemAtPath:@"/Library/MobileSubstrate/DynamicLibraries" toPath:@"/usr/lib/tweaks" error:nil]; } else if (file_exists("/Library/MobileSubstrate/DynamicLibraries") == 0 && file_exists("/usr/lib/tweaks") == 0) { // Move existing tweaks to /usr/lib/tweaks and delete the MobSub folder NSArray *fileList = [fileMgr contentsOfDirectoryAtPath:@"/Library/MobileSubstrate/DynamicLibraries" error:nil]; for (NSString *item in fileList) { NSString *fullPath = [NSString stringWithFormat:@"/Library/MobileSubstrate/DynamicLibraries/%@", item]; [fileMgr moveItemAtPath:fullPath toPath:@"/usr/lib/tweaks" error:nil]; } [fileMgr removeItemAtPath:@"/Library/MobileSubstrate/DynamicLibraries" error:nil]; } else if (file_exists("/Library/MobileSubstrate/DynamicLibraries") != 0 && file_exists("/usr/lib/tweaks") != 0) { // Just create /usr/lib/tweaks - /Lib/MobSub/DynLibs doesn't exist mkdir("/Library/MobileSubstrate", 0755); mkdir("/usr/lib/tweaks", 0755); } else if (file_exists("/Library/MobileSubstrate/DynamicLibraries") != 0 && file_exists("/usr/lib/tweaks") == 0) { // We should be fine in this case mkdir("/Library/MobileSubstrate", 0755); } // Symlink it! symlink("/usr/lib/tweaks", "/Library/MobileSubstrate/DynamicLibraries"); } int extractBootstrap(int *exitCode) { int rv; // extract system-base.tar rv = extract_bundle_tar("system-base.tar"); if (rv != 0) { *exitCode = rv; return 1; } // extract installer-base.tar rv = extract_bundle_tar("installer-base.tar"); if (rv != 0) { *exitCode = rv; return 2; } if (file_exists("/private/var/lib/dpkg/status") != 0) { rv = extract_bundle_tar("dpkgdb-base.tar"); if (rv != 0) { *exitCode = rv; return 3; } } // extract cydia-base.tar rv = extract_bundle_tar("cydia-base.tar"); if (rv != 0) { *exitCode = rv; return 4; } // extract optional-base.tar rv = extract_bundle_tar("optional-base.tar"); if (rv != 0) { *exitCode = rv; return 5; } enableHiddenApps(); touch_file("/meridian/.bootstrap"); rv = uicache(); if (rv != 0) { *exitCode = rv; return 6; } return 0; } int defecateAmfi() { // write kslide to file unlink("/meridian/kernel_slide"); FILE *fd = fopen("/meridian/kernel_slide", "w"); fprintf(fd, "%016llx", kslide); fclose(fd); // trust our payload int ret = inject_trust("/meridian/amfid_payload.dylib"); if (ret != 0) return -1; unlink("/var/tmp/amfid_payload.alive"); pid_t pid = get_pid_for_name("amfid"); if (pid == 0) { return -2; } ret = inject_library(pid, "/meridian/amfid_payload.dylib"); if (ret != 0) return -2; int tries = 0; while (file_exists("/var/tmp/amfid_payload.alive") != 0) { if (tries >= 100) { NSLog(@"failed to patch amfid (%d tries)", tries); return tries; } NSLog(@"waiting for amfid patch..."); usleep(100000); // 0.1 sec tries++; } return 0; } int launchDropbear() { return start_launchdaemon("/meridian/dropbear/dropbear.plist"); } void setUpSubstitute() { // link CydiaSubstrate.framework -> /usr/lib/libsubstrate.dylib if (file_exists("/Library/Frameworks/CydiaSubstrate.framework") == 0) { [fileMgr removeItemAtPath:@"/Library/Frameworks/CydiaSubstrate.framework" error:nil]; } mkdir("/Library/Frameworks", 0755); mkdir("/Library/Frameworks/CydiaSubstrate.framework", 0755); symlink("/usr/lib/libsubstrate.dylib", "/Library/Frameworks/CydiaSubstrate.framework/CydiaSubstrate"); } int startJailbreakd() { unlink("/var/tmp/jailbreakd.pid"); NSData *blob = [NSData dataWithContentsOfFile:@"/meridian/jailbreakd/jailbreakd.plist"]; NSMutableDictionary *job = [NSPropertyListSerialization propertyListWithData:blob options:NSPropertyListMutableContainers format:nil error:nil]; job[@"EnvironmentVariables"][@"KernelBase"] = [NSString stringWithFormat:@"0x%16llx", kernel_base]; job[@"EnvironmentVariables"][@"KernProcAddr"] = [NSString stringWithFormat:@"0x%16llx", kernprocaddr]; job[@"EnvironmentVariables"][@"ZoneMapOffset"] = [NSString stringWithFormat:@"0x%16llx", offsets.zone_map]; [job writeToFile:@"/meridian/jailbreakd/jailbreakd.plist" atomically:YES]; chmod("/meridian/jailbreakd/jailbreakd.plist", 0600); chown("/meridian/jailbreakd/jailbreakd.plist", 0, 0); int rv = start_launchdaemon("/meridian/jailbreakd/jailbreakd.plist"); if (rv != 0) return 1; int tries = 0; while (file_exists("/var/tmp/jailbreakd.pid") != 0) { printf("Waiting for jailbreakd \n"); tries++; usleep(300000); // 300ms if (tries >= 100) { NSLog(@"too many tries for jbd - %d", tries); return tries; } } usleep(100000); // tell jailbreakd to platformize launchd // this adds skip-lib-val to MACF slot and allows us // to inject pspawn without it being in trust cache // (plus FAT/multiarch in trust cache is a pain to code, i'm lazy) rv = call_jailbreakd(JAILBREAKD_COMMAND_ENTITLE, 1); if (rv != 0) return 2; // inject pspawn_hook.dylib to launchd rv = inject_library(1, "/usr/lib/pspawn_hook.dylib"); if (rv != 0) return 3; return 0; } int loadLaunchDaemons() { NSArray *daemons = [fileMgr contentsOfDirectoryAtPath:@"/Library/LaunchDaemons" error:nil]; for (NSString *file in daemons) { NSString *path = [NSString stringWithFormat:@"/Library/LaunchDaemons/%@", file]; NSLog(@"found launchdaemon: %@", path); chmod([path UTF8String], 0755); chown([path UTF8String], 0, 0); } return start_launchdaemon("/Library/LaunchDaemons"); } void enableHiddenApps() { // enable showing of system apps on springboard // this is some funky killall stuff tho killall("cfprefsd", "-SIGSTOP"); NSMutableDictionary* md = [[NSMutableDictionary alloc] initWithContentsOfFile:@"/var/mobile/Library/Preferences/com.apple.springboard.plist"]; [md setObject:[NSNumber numberWithBool:YES] forKey:@"SBShowNonDefaultSystemApps"]; [md writeToFile:@"/var/mobile/Library/Preferences/com.apple.springboard.plist" atomically:YES]; killall("cfprefsd", "-9"); }