Browse Source

initial commit, builds and somewhat works on tvOS, the bootstrap and everything else are completely unmodified from PsychoTeas original, just want a baseline copy as i modify things to test everything out

Kevin Bradley 3 years ago
parent
commit
55cb733ca3
100 changed files with 7260 additions and 0 deletions
  1. 21 0
      LICENSE.md
  2. 16 0
      Meridian/Meridian/amfi.h
  3. 236 0
      Meridian/Meridian/amfi.m
  4. 22 0
      Meridian/Meridian/bootstrap/create-meridian-bootstrap.sh
  5. BIN
      Meridian/Meridian/bootstrap/cydia-base.tar
  6. BIN
      Meridian/Meridian/bootstrap/dpkgdb-base.tar
  7. BIN
      Meridian/Meridian/bootstrap/installer-base.tar
  8. BIN
      Meridian/Meridian/bootstrap/meridian-bootstrap.tar
  9. BIN
      Meridian/Meridian/bootstrap/meridian-bootstrap/bin/launchctl
  10. BIN
      Meridian/Meridian/bootstrap/meridian-bootstrap/bin/uicache
  11. BIN
      Meridian/Meridian/bootstrap/meridian-bootstrap/meridian/amfid_payload.dylib
  12. BIN
      Meridian/Meridian/bootstrap/meridian-bootstrap/meridian/dropbear/dropbear
  13. 34 0
      Meridian/Meridian/bootstrap/meridian-bootstrap/meridian/dropbear/dropbear.plist
  14. 20 0
      Meridian/Meridian/bootstrap/meridian-bootstrap/meridian/ent.plist
  15. BIN
      Meridian/Meridian/bootstrap/meridian-bootstrap/meridian/jailbreakd/jailbreakd
  16. 37 0
      Meridian/Meridian/bootstrap/meridian-bootstrap/meridian/jailbreakd/jailbreakd.plist
  17. 5 0
      Meridian/Meridian/bootstrap/meridian-bootstrap/meridian/offsets.plist
  18. 0 0
      Meridian/Meridian/bootstrap/meridian-bootstrap/private/var/log/lastlog
  19. BIN
      Meridian/Meridian/bootstrap/meridian-bootstrap/usr/lib/libjailbreak.dylib
  20. BIN
      Meridian/Meridian/bootstrap/meridian-bootstrap/usr/lib/pspawn_hook.dylib
  21. BIN
      Meridian/Meridian/bootstrap/optional-base.tar
  22. BIN
      Meridian/Meridian/bootstrap/system-base.tar
  23. BIN
      Meridian/Meridian/bootstrap/tar.tar
  24. 1 0
      Meridian/Meridian/build_time
  25. 17 0
      Meridian/Meridian/helpers/common.h
  26. 21 0
      Meridian/Meridian/helpers/fucksigningservices.h
  27. 70 0
      Meridian/Meridian/helpers/fucksigningservices.m
  28. 53 0
      Meridian/Meridian/helpers/helpers.h
  29. 544 0
      Meridian/Meridian/helpers/helpers.m
  30. 83 0
      Meridian/Meridian/helpers/kernel.h
  31. 483 0
      Meridian/Meridian/helpers/kernel.m
  32. 16 0
      Meridian/Meridian/helpers/main.m
  33. 235 0
      Meridian/Meridian/helpers/nvpatch.c
  34. 14 0
      Meridian/Meridian/helpers/nvpatch.h
  35. 14 0
      Meridian/Meridian/helpers/untar.h
  36. 227 0
      Meridian/Meridian/helpers/untar.m
  37. 28 0
      Meridian/Meridian/jailbreak.h
  38. 551 0
      Meridian/Meridian/jailbreak.m
  39. 249 0
      Meridian/Meridian/mach/jailbreak_daemonUser.c
  40. 183 0
      Meridian/Meridian/mach/jailbreak_daemonUser.h
  41. 169 0
      Meridian/Meridian/patchfinders/liboffsetfinder64.hpp
  42. 14 0
      Meridian/Meridian/patchfinders/offsetdump.h
  43. 65 0
      Meridian/Meridian/patchfinders/offsetdump.m
  44. 17 0
      Meridian/Meridian/patchfinders/offsetfinder.h
  45. 88 0
      Meridian/Meridian/patchfinders/offsetfinder.mm
  46. 1301 0
      Meridian/Meridian/patchfinders/patchfinder64.c
  47. 37 0
      Meridian/Meridian/patchfinders/patchfinder64.h
  48. 12 0
      Meridian/Meridian/root-rw.h
  49. 166 0
      Meridian/Meridian/root-rw.m
  50. 50 0
      Meridian/Meridian/v0rtex.h
  51. 1765 0
      Meridian/Meridian/v0rtex.m
  52. 17 0
      Meridian/Meridian/views/AppDelegate.h
  53. 51 0
      Meridian/Meridian/views/AppDelegate.m
  54. 158 0
      Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Contents.json
  55. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-App-20x20@1x.png
  56. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-App-20x20@2x.png
  57. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-App-20x20@3x.png
  58. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-App-29x29@1x.png
  59. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-App-29x29@2x.png
  60. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-App-29x29@3x.png
  61. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-App-40x40@1x.png
  62. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-App-40x40@2x.png
  63. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-App-40x40@3x.png
  64. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-App-57x57@1x.png
  65. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-App-57x57@2x.png
  66. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-App-60x60@2x.png
  67. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-App-60x60@3x.png
  68. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-App-72x72@1x.png
  69. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-App-72x72@2x.png
  70. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-App-76x76@1x.png
  71. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-App-76x76@2x.png
  72. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-App-83.5x83.5@2x.png
  73. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-Small-50x50@1x.png
  74. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-Small-50x50@2x.png
  75. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/ItunesArtwork@2x.png
  76. 158 0
      Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Contents.json
  77. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-App-20x20@1x.png
  78. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-App-20x20@2x.png
  79. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-App-20x20@3x.png
  80. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-App-29x29@1x.png
  81. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-App-29x29@2x.png
  82. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-App-29x29@3x.png
  83. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-App-40x40@1x.png
  84. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-App-40x40@2x.png
  85. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-App-40x40@3x.png
  86. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-App-57x57@1x.png
  87. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-App-57x57@2x.png
  88. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-App-60x60@2x.png
  89. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-App-60x60@3x.png
  90. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-App-72x72@1x.png
  91. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-App-72x72@2x.png
  92. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-App-76x76@1x.png
  93. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-App-76x76@2x.png
  94. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-App-83.5x83.5@2x.png
  95. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-Small-50x50@1x.png
  96. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-Small-50x50@2x.png
  97. BIN
      Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/ItunesArtwork@2x.png
  98. 6 0
      Meridian/Meridian/views/Assets.xcassets/Contents.json
  99. 6 0
      Meridian/Meridian/views/Assets.xcassets/Icons/Contents.json
  100. 0 0
      Meridian/Meridian/views/Assets.xcassets/Icons/archive.imageset/Contents.json

+ 21 - 0
LICENSE.md

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 Ben Sparkes
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 16 - 0
Meridian/Meridian/amfi.h

@@ -0,0 +1,16 @@
+//
+//  amfi.h
+//  Meridian
+//
+//  Created by Ben Sparkes on 19/12/2017.
+//  Copyright © 2017 Ben Sparkes. All rights reserved.
+//
+
+int init_amfi(void);
+int patch_amfi(mach_port_t amfi_port);
+int get_kqueue_for_pid(pid_t pid);
+int inject_trust(const char *path);
+
+uint8_t *get_code_directory(const char* file_path, uint64_t file_off);
+uint8_t *get_sha1(uint8_t* code_dir);
+uint32_t swap_uint32(uint32_t val);

+ 236 - 0
Meridian/Meridian/amfi.m

@@ -0,0 +1,236 @@
+//
+//  amfi.m
+//  Meridian
+//
+//  Created by Ben Sparkes on 19/12/2017.
+//  Copyright © 2017 Ben Sparkes. All rights reserved.
+//
+
+#import "patchfinder64.h"
+#import "kernel.h"
+#import "amfi.h"
+#import "helpers.h"
+#import "ViewController.h"
+#import <Foundation/Foundation.h>
+#import <CommonCrypto/CommonDigest.h>
+#import <mach-o/loader.h>
+#import <mach-o/dyld_images.h>
+#import <mach-o/fat.h>
+#import <mach-o/swap.h>
+#import <sys/stat.h>
+#import <sys/event.h>
+#import <dlfcn.h>
+#import <pthread.h>
+#import <sys/spawn.h>
+
+uint64_t trust_cache;
+uint64_t amficache;
+
+int init_amfi() {
+    trust_cache = find_trustcache();
+    amficache = find_amficache();
+    
+    NSLog(@"[amfi] trust_cache = 0x%llx \n", trust_cache);
+    NSLog(@"[amfi] amficache = 0x%llx \n", amficache);
+    
+    if (trust_cache == 0 ||
+        amficache == 0) {
+        return -1;
+    }
+    
+    return 0;
+}
+
+// creds to stek29(?)
+int inject_trust(const char *path) {
+    if (file_exists(path) != 0) {
+        NSLog(@"[amfi] you wanka, %s doesn't exist!", path);
+        return -1;
+    }
+    
+    typedef char hash_t[20];
+    
+    struct trust_chain {
+        uint64_t next;
+        unsigned char uuid[16];
+        unsigned int count;
+        hash_t hash[1];
+    };
+    
+    struct trust_chain fake_chain;
+    
+    fake_chain.next = rk64(trust_cache);
+    *(uint64_t *)&fake_chain.uuid[0] = 0xabadbabeabadbabe;
+    *(uint64_t *)&fake_chain.uuid[8] = 0xabadbabeabadbabe;
+    fake_chain.count = 1;
+    
+    uint8_t *codeDir = get_code_directory(path, 0);
+    if (codeDir == NULL) {
+        NSLog(@"[amfi] was given null code dir for %s!", path);
+        return -2;
+    }
+    
+    uint8_t *hash = get_sha1(codeDir);
+    memmove(fake_chain.hash[0], hash, 20);
+    
+    free(hash);
+    
+    uint64_t kernel_trust = 0;
+    mach_vm_allocate(tfp0, &kernel_trust, sizeof(fake_chain), VM_FLAGS_ANYWHERE);
+    
+    kwrite(kernel_trust, &fake_chain, sizeof(fake_chain));
+    wk64(trust_cache, kernel_trust);
+    
+    NSLog(@"[amfi] signed %s \n", path);
+    return 0;
+}
+
+uint8_t *get_code_directory(const char* file_path, uint64_t file_off) {
+    FILE* fd = fopen(file_path, "r");
+    
+    if (fd == NULL) {
+        NSLog(@"[amfi] couldn't open file %s", file_path);
+        fclose(fd);
+        return NULL;
+    }
+    
+    fseek(fd, 0L, SEEK_END);
+    uint64_t file_len = ftell(fd);
+    fseek(fd, 0L, SEEK_SET);
+    
+    if (file_off > file_len){
+        NSLog(@"[amfi] file offset greater than length");
+        fclose(fd);
+        return NULL;
+    }
+    
+    uint32_t magic;
+    fread(&magic, sizeof(magic), 1, fd);
+    fseek(fd, 0, SEEK_SET);
+    
+    int is_swap = (magic == MH_CIGAM || magic == MH_CIGAM_64 || magic == FAT_CIGAM);
+    
+    uint64_t off = file_off;
+    int ncmds = 0;
+    
+    if (magic == MH_MAGIC_64) {
+        struct mach_header_64 mh64;
+        fread(&mh64, sizeof(mh64), 1, fd);
+        off += sizeof(mh64);
+        ncmds = mh64.ncmds;
+    } else if (magic == MH_MAGIC) {
+        struct mach_header mh;
+        fread(&mh, sizeof(mh), 1, fd);
+        off += sizeof(mh);
+        ncmds = mh.ncmds;
+    } else if (magic == FAT_CIGAM || magic == FAT_CIGAM_64) {
+        struct fat_header header;
+        fread(&header, sizeof(header), 1, fd);
+        if (is_swap) swap_fat_header(&header, 0);
+        
+        int arch_offset = sizeof(header);
+        for (int i = 0; i < header.nfat_arch; i++) {
+            struct fat_arch arch;
+            fseek(fd, arch_offset, 0);
+            fread(&arch, sizeof(struct fat_arch), 1, fd);
+            if (is_swap) swap_fat_arch(&arch, 1, 0);
+            
+            if (arch.cputype != CPU_TYPE_ARM64) continue;
+            
+            fseek(fd, arch.offset, 0);
+            
+            uint32_t magic;
+            fread(&magic, sizeof(magic), 1, fd);
+            
+            if (magic == MH_MAGIC) {
+                struct mach_header mh;
+                fread(&mh, sizeof(mh), 1, fd);
+                off += arch.offset + sizeof(mh);
+                ncmds = mh.ncmds;
+            } else if (magic == MH_MAGIC_64) {
+                struct mach_header_64 mh64;
+                fread(&mh64, sizeof(mh64), 1, fd);
+                off += arch.offset + sizeof(mh64);
+                ncmds = mh64.ncmds;
+            }
+            
+            arch_offset += sizeof(arch);
+        }
+    } else {
+        NSLog(@"[amfi] your magic is not valid in these lands! %ux", magic);
+        fclose(fd);
+        return NULL;
+    }
+    
+    if (off > file_len) {
+        NSLog(@"[amfi] unexpected end of file");
+        fclose(fd);
+        return NULL;
+    }
+    
+    fseek(fd, off, SEEK_SET);
+    
+    for (int i = 0; i < ncmds; i++) {
+        if (off + sizeof(struct load_command) > file_len) {
+            NSLog(@"[amfi] unexpected end of file");
+            fclose(fd);
+            return NULL;
+        }
+        
+        const struct load_command cmd;
+        fseek(fd, off, SEEK_SET);
+        fread((void*)&cmd, sizeof(struct load_command), 1, fd);
+        if (cmd.cmd == LC_CODE_SIGNATURE) {
+            uint32_t off_cs;
+            fread(&off_cs, sizeof(uint32_t), 1, fd);
+            uint32_t size_cs;
+            fread(&size_cs, sizeof(uint32_t), 1, fd);
+            
+            if (off_cs + file_off + size_cs > file_len) {
+                NSLog(@"[amfi] unexpected end of file");
+                fclose(fd);
+                return NULL;
+            }
+            
+            uint8_t *cd = malloc(size_cs);
+            fseek(fd, off_cs + file_off, SEEK_SET);
+            fread(cd, size_cs, 1, fd);
+            return cd;
+        } else {
+            off += cmd.cmdsize;
+            if (off > file_len) {
+                NSLog(@"[amfi] unexpected end of file");
+                fclose(fd);
+                return NULL;
+            }
+        }
+    }
+    
+    NSLog(@"[amfi] couldn't find the code sig for %s", file_path);
+    fclose(fd);
+    return NULL;
+}
+
+// creds to nullpixel
+uint8_t *get_sha1(uint8_t* code_dir) {
+    uint8_t *out = malloc(CC_SHA1_DIGEST_LENGTH);
+    
+    uint32_t* code_dir_int = (uint32_t*)code_dir;
+    
+    uint32_t realsize = 0;
+    for (int j = 0; j < 10; j++) {
+        if (swap_uint32(code_dir_int[j]) == 0xfade0c02) {
+            realsize = swap_uint32(code_dir_int[j+1]);
+            code_dir += 4*j;
+        }
+    }
+    
+    CC_SHA1(code_dir, realsize, out);
+    
+    return out;
+}
+
+uint32_t swap_uint32(uint32_t val) {
+    val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF);
+    return (val << 16) | (val >> 16);
+}

+ 22 - 0
Meridian/Meridian/bootstrap/create-meridian-bootstrap.sh

@@ -0,0 +1,22 @@
+#!/bin/bash
+
+currDir=$(dirname $0)
+meridianDir=$currDir/../..
+baseDir=$currDir/meridian-bootstrap
+
+# amfid_payload.dylib 
+cp $meridianDir/amfid/bin/* $baseDir/meridian/
+
+# pspawn_hook.dylib
+cp $meridianDir/pspawn_hook/bin/* $baseDir/usr/lib/
+
+# jailbreakd
+cp $meridianDir/jailbreakd/bin/* $baseDir/meridian/jailbreakd/
+
+# remove all .DS_Store files
+find $baseDir -name '.DS_Store' -delete
+
+# create tar archive
+cd $baseDir
+COPYFILE_DISABLE=1 tar -cf meridian-bootstrap.tar ./*
+mv meridian-bootstrap.tar $currDir

BIN
Meridian/Meridian/bootstrap/cydia-base.tar


BIN
Meridian/Meridian/bootstrap/dpkgdb-base.tar


BIN
Meridian/Meridian/bootstrap/installer-base.tar


BIN
Meridian/Meridian/bootstrap/meridian-bootstrap.tar


BIN
Meridian/Meridian/bootstrap/meridian-bootstrap/bin/launchctl


BIN
Meridian/Meridian/bootstrap/meridian-bootstrap/bin/uicache


BIN
Meridian/Meridian/bootstrap/meridian-bootstrap/meridian/amfid_payload.dylib


BIN
Meridian/Meridian/bootstrap/meridian-bootstrap/meridian/dropbear/dropbear


+ 34 - 0
Meridian/Meridian/bootstrap/meridian-bootstrap/meridian/dropbear/dropbear.plist

@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>Label</key>
+	<string>dropbear</string>
+	<key>Program</key>
+	<string>/meridian/dropbear/dropbear</string>
+	<key>UserName</key>
+	<string>root</string>
+	<key>RunAtLoad</key>
+	<true/>
+	<key>KeepAlive</key>
+	<true/>
+	<key>StandardErrorPath</key>
+	<string>/var/log/dropbear-stderr.log</string>
+	<key>ProgramArguments</key>
+	<array>
+		<string>/meridian/dropbear/dropbear</string>
+		<string>-p</string>
+		<string>22</string>
+		<string>-p</string>
+		<string>2222</string>
+		<string>-F</string>
+		<string>-R</string>
+		<string>-E</string>
+		<string>-m</string>
+		<string>-S</string>
+		<string>/</string>
+	</array>
+	<key>StandardOutPath</key>
+	<string>/var/log/dropbear-stdout.log</string>
+</dict>
+</plist>

+ 20 - 0
Meridian/Meridian/bootstrap/meridian-bootstrap/meridian/ent.plist

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>com.apple.system-task-ports</key>
+	<true/>
+	<key>task_for_pid-allow</key>
+	<true/>
+	<key>com.apple.private.security.no-container</key>
+	<true/>
+	<key>platform-application</key>
+	<true/>
+	<key>get-task-allow</key>
+	<true/>
+	<key>com.apple.private.skip-library-validation</key>
+	<true/>
+	<key>com.apple.lsapplicationworkspace.rebuildappdatabases</key>
+	<true/>
+</dict>
+</plist>

BIN
Meridian/Meridian/bootstrap/meridian-bootstrap/meridian/jailbreakd/jailbreakd


+ 37 - 0
Meridian/Meridian/bootstrap/meridian-bootstrap/meridian/jailbreakd/jailbreakd.plist

@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>Label</key>
+	<string>jailbreakd</string>
+	<key>Program</key>
+	<string>/meridian/jailbreakd/jailbreakd</string>
+	<key>EnvironmentVariables</key>
+	<dict>
+		<key>ZoneMapOffset</key>
+		<string>0x0000000000000000</string>
+		<key>KernProcAddr</key>
+		<string>0x0000000000000000</string>
+		<key>KernelBase</key>
+		<string>0x0000000000000000</string>
+	</dict>
+	<key>UserName</key>
+	<string>root</string>
+	<key>MachServices</key>
+	<dict>
+		<key>zone.sparkes.jailbreakd</key>
+		<dict>
+			<key>HostSpecialPort</key>
+			<integer>15</integer>
+		</dict>
+	</dict>
+	<key>RunAtLoad</key>
+	<true/>
+	<key>KeepAlive</key>
+	<true/>
+	<key>StandardErrorPath</key>
+	<string>/var/log/jailbreakd-stderr.log</string>
+	<key>StandardOutPath</key>
+	<string>/var/log/jailbreakd-stdout.log</string>
+</dict>
+</plist>

+ 5 - 0
Meridian/Meridian/bootstrap/meridian-bootstrap/meridian/offsets.plist

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict/>
+</plist>

+ 0 - 0
Meridian/Meridian/bootstrap/meridian-bootstrap/private/var/log/lastlog


BIN
Meridian/Meridian/bootstrap/meridian-bootstrap/usr/lib/libjailbreak.dylib


BIN
Meridian/Meridian/bootstrap/meridian-bootstrap/usr/lib/pspawn_hook.dylib


BIN
Meridian/Meridian/bootstrap/optional-base.tar


BIN
Meridian/Meridian/bootstrap/system-base.tar


BIN
Meridian/Meridian/bootstrap/tar.tar


+ 1 - 0
Meridian/Meridian/build_time

@@ -0,0 +1 @@
+Mon, 25 Jun 2018 23:15:03 -0700

+ 17 - 0
Meridian/Meridian/helpers/common.h

@@ -0,0 +1,17 @@
+#ifndef COMMON_H
+#define COMMON_H
+
+#include <stdint.h>             // uint*_t
+#include <Foundation/Foundation.h>
+
+#define LOG(str, args...) do { NSLog(@str "\n", ##args); } while(0)
+
+#ifdef __LP64__
+#   define ADDR "0x%016llx"
+    typedef uint64_t kptr_t;
+#else
+#   define ADDR "0x%08x"
+    typedef uint32_t kptr_t;
+#endif
+
+#endif

+ 21 - 0
Meridian/Meridian/helpers/fucksigningservices.h

@@ -0,0 +1,21 @@
+//
+//  fucksigningservices.h
+//  Meridian
+//
+//  Created by Ben Sparkes on 07/01/2018.
+//  Copyright © 2018 Ben Sparkes. All rights reserved.
+//
+
+#ifndef fucksigningservices_h
+#define fucksigningservices_h
+
+#import "ViewController.h"
+#import <Foundation/Foundation.h>
+
+@interface fucksigningservices : NSObject
+
++ (Boolean)appIsPirated:(NSString *)profilePath;
+
+@end
+
+#endif /* fucksigningservices_h */

+ 70 - 0
Meridian/Meridian/helpers/fucksigningservices.m

@@ -0,0 +1,70 @@
+//
+//  fuck-signing-services.m
+//  Meridian
+//
+//  Created by Ben Sparkes on 07/01/2018.
+//  Copyright © 2018 Ben Sparkes. All rights reserved.
+//
+
+#import "fucksigningservices.h"
+
+@interface NSString (profileHelper)
+- (id)dictionaryFromString;
+@end
+
+@implementation NSString (profileHelper)
+
+// convert basic XML plist string from the profile and convert it into a mutable nsdictionary
+- (id)dictionaryFromString
+{
+    NSData *theData = [self dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
+    id theDict = [NSPropertyListSerialization propertyListWithData:theData
+                                                           options:NSPropertyListMutableContainersAndLeaves
+                                                            format:nil
+                                                             error:nil];
+    return theDict;
+}
+
+@end
+
+@implementation fucksigningservices : NSObject
+
+// creds @nitoTV/lechium the fuckin' madman
+// https://github.com/lechium/ProvisioningProfileCleaner/blob/master/ProvisioningProfileCleaner/KBProfileHelper.m#L648
++ (Boolean)appIsPirated:(NSString *)profilePath
+{
+    NSString *fileContents = [NSString stringWithContentsOfFile:profilePath
+                                             encoding:NSUTF8StringEncoding
+                                                error:nil];
+    NSUInteger fileLength = [fileContents length];
+    
+    if (fileLength == 0) return false;
+    
+    // find NSRange location of <?xml to pass by all the "garbage" data before our plist
+    NSUInteger startingLocation = [fileContents rangeOfString:@"<?xml"].location;
+    // find NSRange of the end of the plist (there is "junk" cert data after our plist info as well
+    NSRange endingRange = [fileContents rangeOfString:@"</plist>"];
+    
+    // adjust the location of endingRange to include </plist> into our newly trimmed string.
+    NSUInteger endingLocation = endingRange.location + endingRange.length;
+    
+    // offset the ending location to trim out the "garbage" before <?xml
+    NSUInteger endingLocationAdjusted = endingLocation - startingLocation;
+    
+    // create the final range of the string data from <?xml to </plist>
+    NSRange plistRange = NSMakeRange(startingLocation, endingLocationAdjusted);
+    
+    NSString *plistString = [fileContents substringWithRange:plistRange];
+    
+    NSMutableDictionary *dict = [plistString dictionaryFromString];
+    
+    // Grab provisioning entries
+    NSObject *provisionsAllDevices = [dict objectForKey:@"ProvisionsAllDevices"];
+    NSArray *provisionedDevices = [dict objectForKey:@"ProvisionedDevices"];
+    
+    // Check whether keys are present & evaluate
+    return (provisionsAllDevices != nil &&
+            provisionedDevices == nil);
+}
+
+@end

+ 53 - 0
Meridian/Meridian/helpers/helpers.h

@@ -0,0 +1,53 @@
+//
+//  helpers.h
+//  Meridian
+//
+//  Created by Ben Sparkes on 30/12/2017.
+//  Copyright © 2017 Ben Sparkes. All rights reserved.
+//
+
+#ifndef helpers_h
+#define helpers_h
+
+#include <stdio.h>
+
+#define CS_GET_TASK_ALLOW       0x0000004    /* has get-task-allow entitlement */
+#define CS_INSTALLER            0x0000008    /* has installer entitlement      */
+#define CS_HARD                 0x0000100    /* don't load invalid pages       */
+#define CS_RESTRICT             0x0000800    /* tell dyld to treat restricted  */
+#define CS_PLATFORM_BINARY      0x4000000    /* this is a platform binary      */
+
+#define JAILBREAKD_COMMAND_ENTITLE 1
+#define JAILBREAKD_COMMAND_ENTITLE_AND_SIGCONT 2
+#define JAILBREAKD_COMMAND_ENTITLE_AND_SIGCONT_FROM_XPCPROXY 3
+#define JAILBREAKD_COMMAND_FIXUP_SETUID 4
+
+int call_jailbreakd(int command, pid_t pid);
+uint64_t find_proc_by_name(char* name);
+uint64_t find_proc_by_pid(uint32_t pid);
+uint32_t get_pid_for_name(char* name);
+int uicache(void);
+int start_launchdaemon(const char *path);
+int respring(void);
+int inject_library(pid_t pid, const char *path);
+int killall(const char *procname, const char *kill);
+int check_for_jailbreak(void);
+char *itoa(long n);
+int file_exists(const char *path);
+void read_file(const char* path);
+int cp(const char *from, const char *to);
+int num_files(const char *path);
+char* bundled_file(const char *filename);
+char* bundle_path(void);
+int extract_bundle(const char* bundle_name, const char* directory);
+int extract_bundle_tar(const char *bundle_name);
+void touch_file(char *path);
+char* concat(const char *s1, const char *s2);
+void grant_csflags(pid_t pd);
+int execprog(const char *prog, const char* args[]);
+void restart_device(void);
+double uptime(void);
+void suspend_all_threads(void);
+void resume_all_threads(void);
+
+#endif

+ 544 - 0
Meridian/Meridian/helpers/helpers.m

@@ -0,0 +1,544 @@
+//
+//  helpers.m
+//  Meridian
+//
+//  Created by Ben Sparkes on 30/12/2017.
+//  Copyright © 2017 Ben Sparkes. All rights reserved.
+//
+
+#include "helpers.h"
+#include "ViewController.h"
+#include "kernel.h"
+#include "untar.h"
+#include "amfi.h"
+#include "jailbreak_daemonUser.h"
+#include <dirent.h>
+#include <unistd.h>
+#include <dlfcn.h>
+#include <sys/fcntl.h>
+#include <sys/spawn.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#import <Foundation/Foundation.h>
+
+int call_jailbreakd(int command, pid_t pid) {
+    mach_port_t jbd_port;
+    if (bootstrap_look_up(bootstrap_port, "zone.sparkes.jailbreakd", &jbd_port) != 0) {
+        return -1;
+    }
+    
+    return jbd_call(jbd_port, command, pid);
+}
+
+uint64_t find_proc_by_name(char* name) {
+    uint64_t proc = rk64(kernprocaddr + 0x08);
+    
+    while (proc) {
+        char proc_name[40] = { 0 };
+        
+        kread(proc + 0x26c, proc_name, 40);
+        
+        if (!strcmp(name, proc_name)) {
+            return proc;
+        }
+        
+        proc = rk64(proc + 0x08);
+    }
+    
+    return 0;
+}
+
+uint64_t find_proc_by_pid(uint32_t pid) {
+    uint64_t proc = rk64(kernprocaddr + 0x08);
+    
+    while (proc) {
+        uint32_t proc_pid = rk32(proc + 0x10);
+        
+        if (pid == proc_pid) {
+            return proc;
+        }
+        
+        proc = rk64(proc + 0x08);
+    }
+    
+    return 0;
+}
+
+uint32_t get_pid_for_name(char* name) {
+    uint64_t proc = find_proc_by_name(name);
+    if (proc == 0) {
+        return 0;
+    }
+    
+    return rk32(proc + 0x10);
+}
+
+int uicache() {
+    return execprog("/bin/uicache", NULL);
+}
+
+int start_launchdaemon(const char *path) {
+    int ret = inject_trust("/bin/launchctl");
+    if (ret != 0) {
+        NSLog(@"Failed to inject trust to /bin/launchctl: %d", ret);
+        return -30;
+    }
+    
+    chmod(path, 0755);
+    chown(path, 0, 0);
+    return execprog("/bin/launchctl", (const char **)&(const char*[]) {
+        "/bin/launchctl",
+        "load",
+        "-w",
+        path,
+        NULL
+    });
+}
+
+int respring() {
+    pid_t springBoard = get_pid_for_name("backboardd");
+    if (springBoard == 0) {
+        return 1;
+    }
+    
+    kill(springBoard, 9);
+    return 0;
+}
+
+int inject_library(pid_t pid, const char *path) {
+    mach_port_t task_port;
+    kern_return_t ret = task_for_pid(mach_task_self(), pid, &task_port);
+    if (ret != KERN_SUCCESS || task_port == MACH_PORT_NULL) {
+        task_port = task_for_pid_workaround(pid);
+        if (task_port == MACH_PORT_NULL) {
+            NSLog(@"[injector] failed to get task for pid %d", pid);
+            return ret;
+        }
+    }
+    
+    NSLog(@"[injector] got task port: %x", task_port);
+    
+    call_remote(task_port, dlopen, 2, REMOTE_CSTRING(path), REMOTE_LITERAL(RTLD_NOW));
+    uint64_t error = call_remote(task_port, dlerror, 0);
+    if (error != 0) {
+        uint64_t len = call_remote(task_port, strlen, 1, REMOTE_LITERAL(error));
+        char* local_cstring = malloc(len +  1);
+        remote_read_overwrite(task_port, error, (uint64_t)local_cstring, len + 1);
+        
+        NSLog(@"[injector] error: %s", local_cstring);
+        return -1;
+    }
+    
+    return 0;
+}
+
+int killall(const char *procname, const char *kill) {
+    return execprog("/usr/bin/killall", (const char **)&(const char *[]) {
+        "/usr/bin/killall",
+        kill,
+        procname,
+        NULL
+    });
+}
+
+int check_for_jailbreak() {
+    int csops(pid_t pid, unsigned int ops, void *useraddr, size_t usersize);
+    
+    uint32_t flags;
+    csops(getpid(), 0, &flags, 0);
+    
+    return flags & CS_PLATFORM_BINARY;
+}
+
+char *itoa(long n) {
+    int len = n==0 ? 1 : floor(log10l(labs(n)))+1;
+    if (n<0) len++; // room for negative sign '-'
+    
+    char    *buf = calloc(sizeof(char), len+1); // +1 for null
+    snprintf(buf, len+1, "%ld", n);
+    return   buf;
+}
+
+// remember: returns 0 if file exists
+int file_exists(const char *path) {
+    return access(path, F_OK);
+}
+
+void read_file(const char *path) {
+    char buf[65] = {0};
+    int fd = open(path, O_RDONLY);
+    if (fd == -1) {
+        perror("open path");
+        return;
+    }
+    
+    printf("contents of %s: \n ------------------------- \n", path);
+    while(read(fd, buf, sizeof(buf) - 1) == sizeof(buf) - 1) {
+        printf("%s", buf);
+    }
+    printf("%s", buf);
+    printf("\n-------------------------\n");
+    
+    close(fd);
+}
+
+int cp(const char *from, const char *to) {
+    int fd_to, fd_from;
+    char buf[4096];
+    ssize_t nread;
+    int saved_errno;
+    
+    fd_from = open(from, O_RDONLY);
+    if (fd_from < 0)
+        return -1;
+    
+    fd_to = open(to, O_WRONLY | O_CREAT | O_EXCL, 0666);
+    if (fd_to < 0)
+        goto out_error;
+    
+    while ((nread = read(fd_from, buf, sizeof buf)) > 0)
+    {
+        char *out_ptr = buf;
+        ssize_t nwritten;
+        
+        do {
+            nwritten = write(fd_to, out_ptr, nread);
+            
+            if (nwritten >= 0)
+            {
+                nread -= nwritten;
+                out_ptr += nwritten;
+            }
+            else if (errno != EINTR)
+            {
+                goto out_error;
+            }
+        } while (nread > 0);
+    }
+    
+    if (nread == 0)
+    {
+        if (close(fd_to) < 0)
+        {
+            fd_to = -1;
+            goto out_error;
+        }
+        close(fd_from);
+        
+        /* Success! */
+        return 0;
+    }
+    
+out_error:
+    saved_errno = errno;
+    
+    close(fd_from);
+    if (fd_to >= 0)
+        close(fd_to);
+    
+    errno = saved_errno;
+    return -1;
+}
+
+// https://stackoverflow.com/questions/1121383/counting-the-number-of-files-in-a-directory-using-c
+int num_files(const char *path) {
+    if (file_exists(path) != 0) {
+        return -1;
+    }
+    
+    int file_count = 0;
+    DIR * dirp;
+    struct dirent * entry;
+
+    dirp = opendir(path);
+    while ((entry = readdir(dirp)) != NULL) {
+        if (entry->d_type == DT_REG) {
+            file_count++;
+        }
+    }
+    closedir(dirp);
+    
+    return file_count;
+}
+
+char* bundled_file(const char *filename) {
+    return concat(bundle_path(), filename);
+}
+
+char* bundle_path() {
+    CFBundleRef mainBundle = CFBundleGetMainBundle();
+    CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(mainBundle);
+    int len = 4096;
+    char* path = malloc(len);
+    
+    CFURLGetFileSystemRepresentation(resourcesURL, TRUE, (UInt8*)path, len);
+    
+    return concat(path, "/");
+}
+
+int extract_bundle(const char* bundle_name, const char* directory) {
+    int ret;
+    
+    char *tarFile = NULL;
+    asprintf(&tarFile, "%s/%s", directory, bundle_name);
+    
+    ret = file_exists(bundled_file(bundle_name));
+    if (ret != 0) {
+        NSLog(@"file does not exist: %s", bundled_file(bundle_name));
+        return -1;
+    }
+    
+    ret = file_exists(directory);
+    if (file_exists(directory) != 0) {
+        NSLog(@"directory does not exist: %s", directory);
+        return -2;
+    }
+    
+    ret = cp(bundled_file(bundle_name), tarFile);
+    if (ret != 0) {
+        NSLog(@"cp has failed: %d", ret);
+        return -3;
+    }
+    
+    ret = chdir(directory);
+    if (ret != 0) {
+        NSLog(@"failed to chdir *rolls eyes* code %d", ret);
+        return -4;
+    }
+    
+    ret = untar(fopen(tarFile, "r"), bundle_name);
+    NSLog(@"untar returned: %d", ret);
+    if (ret != 0) {
+        return -5;
+    }
+    
+    ret = unlink(tarFile);
+    if (ret != 0) {
+        NSLog(@"now fucking `unlink` is failing tooo? %d", ret);
+        return -6;
+    }
+    
+    free(tarFile);
+    return 0;
+}
+
+int extract_bundle_tar(const char *bundle_name) {
+    const char *file_path = bundled_file(bundle_name);
+    
+    if (file_exists(file_path) != 0) {
+        log_message([NSString stringWithFormat:@"Error, bundle file %s was not found at path %s!",
+                     bundle_name, file_path]);
+        return -1;
+    }
+    
+    return execprog("/meridian/tar", (const char **)&(const char*[]) {
+        "/meridian/tar",
+        "--preserve-permissions",
+        "--no-overwrite-dir",
+        "-C",
+        "/",
+        "-xvf",
+        file_path,
+        NULL
+    });
+}
+
+void touch_file(char *path) {
+    fclose(fopen(path, "w+"));
+}
+
+// https://stackoverflow.com/questions/8465006/how-do-i-concatenate-two-strings-in-c
+char* concat(const char *s1, const char *s2) {
+    char *result = malloc(strlen(s1)+strlen(s2)+1);
+    strcpy(result, s1);
+    strcat(result, s2);
+    return result;
+}
+
+void grant_csflags(pid_t pid) {    
+    int tries = 3;
+    while (tries-- > 0) {
+        uint64_t proc = find_proc_by_pid(pid);
+        if (proc == 0) {
+            sleep(1);
+            continue;
+        }
+        
+        uint32_t csflags = rk32(proc + 0x2a8);
+        csflags = (csflags |
+                   CS_PLATFORM_BINARY |
+                   CS_INSTALLER |
+                   CS_GET_TASK_ALLOW)
+                   & ~(CS_RESTRICT | CS_HARD);
+        wk32(proc + 0x2a8, csflags);
+        break;
+    }
+}
+
+// creds to stek29 on this one
+int execprog(const char *prog, const char* args[]) {
+    if (args == NULL) {
+        args = (const char **)&(const char*[]){ prog, NULL };
+    }
+    
+    if (file_exists("/meridian") != 0) {
+        mkdir("/meridian", 0755);
+    }
+    if (file_exists("/meridian/logs") != 0) {
+        mkdir("/meridian/logs", 0755);
+    }
+    
+    const char *logfile = [NSString stringWithFormat:@"/meridian/logs/%@-%lu",
+                           [[NSMutableString stringWithUTF8String:prog] stringByReplacingOccurrencesOfString:@"/" withString:@"_"],
+                           time(NULL)].UTF8String;
+    
+    NSString *prog_args = @"";
+    for (const char **arg = args; *arg != NULL; ++arg) {
+        prog_args = [prog_args stringByAppendingString:[NSString stringWithFormat:@"%s ", *arg]];
+    }
+    NSLog(@"[execprog] Spawning [ %@ ] to logfile [ %s ]", prog_args, logfile);
+    
+    int rv;
+    posix_spawn_file_actions_t child_fd_actions;
+    if ((rv = posix_spawn_file_actions_init (&child_fd_actions))) {
+        perror ("posix_spawn_file_actions_init");
+        return rv;
+    }
+    if ((rv = posix_spawn_file_actions_addopen (&child_fd_actions, STDOUT_FILENO, logfile,
+                                                O_WRONLY | O_CREAT | O_TRUNC, 0666))) {
+        perror ("posix_spawn_file_actions_addopen");
+        return rv;
+    }
+    if ((rv = posix_spawn_file_actions_adddup2 (&child_fd_actions, STDOUT_FILENO, STDERR_FILENO))) {
+        perror ("posix_spawn_file_actions_adddup2");
+        return rv;
+    }
+    
+    pid_t pd;
+    if ((rv = posix_spawn(&pd, prog, &child_fd_actions, NULL, (char**)args, NULL))) {
+        printf("posix_spawn error: %d (%s)\n", rv, strerror(rv));
+        return rv;
+    }
+    
+    NSLog(@"[execprog] Process spawned with pid %d", pd);
+    
+    grant_csflags(pd);
+    
+    int ret, status;
+    do {
+        ret = waitpid(pd, &status, 0);
+        if (ret > 0) {
+            NSLog(@"'%s' exited with %d (sig %d)\n", prog, WEXITSTATUS(status), WTERMSIG(status));
+        } else if (errno != EINTR) {
+            NSLog(@"waitpid error %d: %s\n", ret, strerror(errno));
+        }
+    } while (ret < 0 && errno == EINTR);
+    
+    char buf[65] = {0};
+    int fd = open(logfile, O_RDONLY);
+    if (fd == -1) {
+        perror("open logfile");
+        return 1;
+    }
+    
+    NSLog(@"contents of %s:", logfile);
+    NSLog(@"-------------------------");
+    NSString *outputString = @"";
+    while(read(fd, buf, sizeof(buf) - 1) == sizeof(buf) - 1) {
+        outputString = [outputString stringByAppendingString:[NSString stringWithFormat:@"%s", buf]];
+    }
+    NSLog(@"%@", outputString);
+    NSLog(@"-------------------------");
+    
+    close(fd);
+    remove(logfile);
+    return (int8_t)WEXITSTATUS(status);
+}
+
+// too lazy to find & add IOKit headers so here we are
+typedef mach_port_t io_service_t;
+typedef mach_port_t io_connect_t;
+extern const mach_port_t kIOMasterPortDefault;
+CFMutableDictionaryRef IOServiceMatching(const char *name) CF_RETURNS_RETAINED;
+io_service_t IOServiceGetMatchingService(mach_port_t masterPort, CFDictionaryRef matching CF_RELEASES_ARGUMENT);
+kern_return_t IOServiceOpen(io_service_t service, task_port_t owningTask, uint32_t type, io_connect_t *client);
+kern_return_t IOConnectCallAsyncStructMethod(mach_port_t connection, uint32_t selector, mach_port_t wake_port, uint64_t *reference, uint32_t referenceCnt, const void *inputStruct, size_t inputStructCnt, void *outputStruct, size_t *outputStructCnt);
+
+// credits to tihmstar
+void restart_device() {
+    // open user client
+    CFMutableDictionaryRef matching = IOServiceMatching("IOSurfaceRoot");
+    io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, matching);
+    io_connect_t connect = 0;
+    IOServiceOpen(service, mach_task_self(), 0, &connect);
+    
+    // add notification port with same refcon multiple times
+    mach_port_t port = 0;
+    mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
+    uint64_t references;
+    uint64_t input[3] = {0};
+    input[1] = 1234;  // keep refcon the same value
+    while (1) {
+        IOConnectCallAsyncStructMethod(connect, 17, port, &references, 1, input, sizeof(input), NULL, NULL);
+    }
+}
+
+// credits to tihmstar
+double uptime() {
+    struct timeval boottime;
+    size_t len = sizeof(boottime);
+    int mib[2] = { CTL_KERN, KERN_BOOTTIME };
+    if (sysctl(mib, 2, &boottime, &len, NULL, 0) < 0) {
+        return -1.0;
+    }
+    
+    time_t bsec = boottime.tv_sec, csec = time(NULL);
+    
+    return difftime(csec, bsec);
+}
+
+// credits to tihmstar
+void suspend_all_threads() {
+    thread_act_t other_thread, current_thread;
+    unsigned int thread_count;
+    thread_act_array_t thread_list;
+    
+    current_thread = mach_thread_self();
+    int result = task_threads(mach_task_self(), &thread_list, &thread_count);
+    if (result == -1) {
+        exit(1);
+    }
+    if (!result && thread_count) {
+        for (unsigned int i = 0; i < thread_count; ++i) {
+            other_thread = thread_list[i];
+            if (other_thread != current_thread) {
+                int kr = thread_suspend(other_thread);
+                if (kr != KERN_SUCCESS) {
+                    mach_error("thread_suspend:", kr);
+                    exit(1);
+                }
+            }
+        }
+    }
+}
+
+// credits to tihmstar
+void resume_all_threads() {
+    thread_act_t other_thread, current_thread;
+    unsigned int thread_count;
+    thread_act_array_t thread_list;
+    
+    current_thread = mach_thread_self();
+    int result = task_threads(mach_task_self(), &thread_list, &thread_count);
+    if (!result && thread_count) {
+        for (unsigned int i = 0; i < thread_count; ++i) {
+            other_thread = thread_list[i];
+            if (other_thread != current_thread) {
+                int kr = thread_resume(other_thread);
+                if (kr != KERN_SUCCESS) {
+                    mach_error("thread_suspend:", kr);
+                }
+            }
+        }
+    }
+}

+ 83 - 0
Meridian/Meridian/helpers/kernel.h

@@ -0,0 +1,83 @@
+//
+//  kernel.h
+//  Meridian
+//
+//  Created by Ben Sparkes on 16/12/2017.
+//  Copyright © 2017 Ben Sparkes. All rights reserved.
+//
+
+#include <mach/mach.h>
+
+enum arg_type {
+    ARG_LITERAL,
+    ARG_BUFFER,
+    ARG_BUFFER_PERSISTENT, // don't free the buffer after the call
+    ARG_OUT_BUFFER,
+    ARG_INOUT_BUFFER
+};
+
+typedef struct _arg_desc {
+    uint64_t type;
+    uint64_t value;
+    uint64_t length;
+} arg_desc;
+
+#define REMOTE_LITERAL(val) &(arg_desc){ARG_LITERAL, (uint64_t)val, (uint64_t)0}
+#define REMOTE_BUFFER(ptr, size) &(arg_desc){ARG_BUFFER, (uint64_t)ptr, (uint64_t)size}
+#define REMOTE_CSTRING(str) &(arg_desc){ARG_BUFFER, (uint64_t)str, (uint64_t)(strlen(str)+1)}
+
+task_t tfp0;
+uint64_t kslide;
+uint64_t kernel_base;
+uint64_t kern_ucred;
+uint64_t kernprocaddr;
+
+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);
+
+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);
+
+kern_return_t mach_vm_allocate(vm_map_t,
+                               mach_vm_address_t *,
+                               mach_vm_size_t, int);
+
+kern_return_t mach_vm_deallocate(vm_map_t target,
+                                 mach_vm_address_t address,
+                                 mach_vm_size_t size);
+
+kern_return_t mach_vm_region(vm_map_t target_task,
+                             mach_vm_address_t *address,
+                             mach_vm_size_t *size,
+                             vm_region_flavor_t flavor,
+                             vm_region_info_t info,
+                             mach_msg_type_number_t *infoCnt,
+                             mach_port_t *object_name);
+
+kern_return_t bootstrap_look_up(mach_port_t port, const char *service, mach_port_t *server_port);
+
+size_t kread(uint64_t where, void *p, size_t size);
+size_t kwrite(uint64_t where, const void *p, size_t size);
+uint64_t rk64(uint64_t kaddr);
+uint32_t rk32(uint64_t kaddr);
+void wk64(uint64_t kaddr, uint64_t val);
+void wk32(uint64_t kaddr, uint32_t val);
+uint64_t remote_alloc(mach_port_t task_port, uint64_t size);
+uint64_t alloc_and_fill_remote_buffer(mach_port_t task_port,
+                                      uint64_t local_address,
+                                      uint64_t length);
+void remote_free(mach_port_t task_port, uint64_t base, uint64_t size);
+void remote_read_overwrite(mach_port_t task_port,
+                           uint64_t remote_address,
+                           uint64_t local_address,
+                           uint64_t length);
+uint64_t binary_load_address(mach_port_t tp);
+uint64_t ktask_self_addr(void);
+mach_port_t task_for_pid_workaround(int pid);
+uint64_t find_port_address(mach_port_name_t port);
+uint64_t call_remote(mach_port_t task_port, void* fptr, int n_params, ...);

+ 483 - 0
Meridian/Meridian/helpers/kernel.m

@@ -0,0 +1,483 @@
+//
+//  kernel.m
+//  v0rtex
+//
+//  Created by Ben Sparkes on 16/12/2017.
+//  Copyright © 2017 Ben Sparkes. All rights reserved.
+//
+
+#include "kernel.h"
+#include "common.h"
+#include "helpers.h"
+#include <mach/mach.h>
+
+size_t kread(uint64_t where, void *p, size_t size)
+{
+    int rv;
+    size_t offset = 0;
+    while (offset < size) {
+        mach_vm_size_t sz, chunk = 2048;
+        if (chunk > size - offset) {
+            chunk = size - offset;
+        }
+        rv = mach_vm_read_overwrite(tfp0, where + offset, chunk, (mach_vm_address_t)p + offset, &sz);
+        
+        if (rv || sz == 0) {
+            break;
+        }
+        
+        offset += sz;
+    }
+    return offset;
+}
+
+size_t kwrite(uint64_t where, const void *p, size_t size) {
+    int rv;
+    size_t offset = 0;
+    while (offset < size) {
+        size_t chunk = 2048;
+        if (chunk > size - offset) {
+            chunk = size - offset;
+        }
+        rv = mach_vm_write(tfp0,
+                           where + offset,
+                           (mach_vm_offset_t)p + offset,
+                           (mach_msg_type_number_t)chunk);
+        
+        if (rv) {
+            printf("[kernel] error copying buffer into region: @%p \n", (void *)(offset + where));
+            break;
+        }
+        
+        offset +=chunk;
+    }
+    
+    return offset;
+}
+
+uint64_t rk64(uint64_t kaddr) {
+    uint64_t lower = rk32(kaddr);
+    uint64_t higher = rk32(kaddr + 4);
+    return ((higher << 32) | lower);
+}
+
+uint32_t rk32(uint64_t kaddr) {
+    kern_return_t err;
+    uint32_t val = 0;
+    mach_vm_size_t outsize = 0;
+    
+    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);
+
+    err = mach_vm_read_overwrite(tfp0,
+                                 (mach_vm_address_t)kaddr,
+                                 (mach_vm_size_t)sizeof(uint32_t),
+                                 (mach_vm_address_t)&val,
+                                 &outsize);
+    
+    if (err != KERN_SUCCESS) {
+        return 0;
+    }
+    
+    if (outsize != sizeof(uint32_t)) {
+        return 0;
+    }
+    
+    return val;
+}
+
+void wk64(uint64_t kaddr, uint64_t val) {
+    uint32_t lower = (uint32_t)(val & 0xffffffff);
+    uint32_t higher = (uint32_t)(val >> 32);
+    wk32(kaddr, lower);
+    wk32(kaddr + 4, higher);
+}
+
+void wk32(uint64_t kaddr, uint32_t val) {
+    if (tfp0 == MACH_PORT_NULL) {
+        return;
+    }
+    
+    kern_return_t err;
+    err = mach_vm_write(tfp0,
+                        (mach_vm_address_t)kaddr,
+                        (vm_offset_t)&val,
+                        (mach_msg_type_number_t)sizeof(uint32_t));
+    
+    if (err != KERN_SUCCESS) {
+        return;
+    }
+}
+
+uint64_t remote_alloc(mach_port_t task_port, uint64_t size) {
+    kern_return_t err;
+    
+    mach_vm_offset_t remote_addr = 0;
+    mach_vm_size_t remote_size = (mach_vm_size_t)size;
+    err = mach_vm_allocate(task_port, &remote_addr, remote_size, VM_FLAGS_ANYWHERE);
+    if (err != KERN_SUCCESS){
+        printf("unable to allocate buffer in remote process\n");
+        return 0;
+    }
+    
+    return (uint64_t)remote_addr;
+}
+
+uint64_t alloc_and_fill_remote_buffer(mach_port_t task_port,
+                                      uint64_t local_address,
+                                      uint64_t length) {
+    kern_return_t err;
+    
+    uint64_t remote_address = remote_alloc(task_port, length);
+    
+    err = mach_vm_write(task_port, remote_address, (mach_vm_offset_t)local_address, (mach_msg_type_number_t)length);
+    if (err != KERN_SUCCESS){
+        printf("unable to write to remote memory \n");
+        return 0;
+    }
+    
+    return remote_address;
+}
+
+void remote_free(mach_port_t task_port, uint64_t base, uint64_t size) {
+    kern_return_t err;
+    
+    err = mach_vm_deallocate(task_port, (mach_vm_address_t)base, (mach_vm_size_t)size);
+    if (err !=  KERN_SUCCESS){
+        printf("unabble to deallocate remote buffer\n");
+        return;
+    }
+}
+
+void remote_read_overwrite(mach_port_t task_port,
+                           uint64_t remote_address,
+                           uint64_t local_address,
+                           uint64_t length) {
+    kern_return_t err;
+    
+    mach_vm_size_t outsize = 0;
+    err = mach_vm_read_overwrite(task_port, (mach_vm_address_t)remote_address, (mach_vm_size_t)length, (mach_vm_address_t)local_address, &outsize);
+    if (err != KERN_SUCCESS){
+        printf("remote read failed\n");
+        return;
+    }
+    
+    if (outsize != length){
+        printf("remote read was short (expected %llx, got %llx\n", length, outsize);
+        return;
+    }
+}
+
+uint64_t binary_load_address(mach_port_t tp) {
+    kern_return_t err;
+    mach_msg_type_number_t region_count = VM_REGION_BASIC_INFO_COUNT_64;
+    memory_object_name_t object_name = MACH_PORT_NULL;
+    mach_vm_size_t target_first_size = 0x1000;
+    mach_vm_address_t target_first_addr = 0x0;
+    struct vm_region_basic_info_64 region = {0};
+    err = mach_vm_region(tp,
+                         &target_first_addr,
+                         &target_first_size,
+                         VM_REGION_BASIC_INFO_64,
+                         (vm_region_info_t)&region,
+                         &region_count,
+                         &object_name);
+    
+    if (err != KERN_SUCCESS) {
+        printf("failed to get the region\n");
+        return -1;
+    }
+    
+    return target_first_addr;
+}
+
+uint64_t ktask_self_addr() {
+    uint64_t self_proc = find_proc_by_pid(getpid());
+    return rk64(self_proc + 0x18);
+}
+
+// credits to Jonathan Levin (Morpheus) for this awesome workaround
+// http://newosxbook.com/articles/PST2.html
+mach_port_t task_for_pid_workaround(int pid) {
+    host_t myhost = mach_host_self();
+    mach_port_t psDefault;
+    mach_port_t psDefault_control;
+    
+    task_array_t tasks;
+    mach_msg_type_number_t numTasks;
+    
+    kern_return_t kr;
+    
+    kr = processor_set_default(myhost, &psDefault);
+    
+    kr = host_processor_set_priv(myhost, psDefault, &psDefault_control);
+    if (kr != KERN_SUCCESS) {
+        fprintf(stderr, "host_processor_set_priv failed with error %x\n", kr);
+        mach_error("host_processor_set_priv",kr);
+        exit(1);
+    }
+    
+    kr = processor_set_tasks(psDefault_control, &tasks, &numTasks);
+    if (kr != KERN_SUCCESS) {
+        fprintf(stderr,"processor_set_tasks failed with error %x\n",kr);
+        exit(1);
+    }
+    
+    for (int i = 0; i < numTasks; i++) {
+        int t_pid;
+        pid_for_task(tasks[i], &t_pid);
+        if (pid == t_pid) return (tasks[i]);
+    }
+    
+    return MACH_PORT_NULL;
+}
+
+// from Ian Beer's find_port.c
+uint64_t find_port_address(mach_port_name_t port) {
+    uint64_t task_addr = ktask_self_addr();
+    uint64_t itk_space = rk64(task_addr + 0x300);
+    uint64_t is_table = rk64(itk_space + 0x20);
+    
+    uint32_t port_index = port >> 8;
+    uint64_t port_addr = rk64(is_table + (port_index * 0x18));
+    return port_addr;
+}
+
+uint64_t find_gadget_candidate(char** alternatives, size_t gadget_length) {
+    void* haystack_start = (void*)atoi;    // will do...
+    size_t haystack_size = 100*1024*1024; // likewise...
+    
+    for (char* candidate = *alternatives; candidate != NULL; alternatives++) {
+        void* found_at = memmem(haystack_start, haystack_size, candidate, gadget_length);
+        if (found_at != NULL) {
+            return (uint64_t)found_at;
+        }
+    }
+    
+    return 0;
+}
+
+uint64_t blr_x19_addr = 0;
+uint64_t find_blr_x19_gadget() {
+    if (blr_x19_addr != 0) {
+        return blr_x19_addr;
+    }
+    
+    char* blr_x19 = "\x60\x02\x3f\xd6";
+    char* candidates[] = {blr_x19, NULL};
+    blr_x19_addr = find_gadget_candidate(candidates, 4);
+    return blr_x19_addr;
+}
+
+// no support for non-register args
+#define MAX_REMOTE_ARGS 8
+
+// not in iOS SDK headers:
+extern void _pthread_set_self(pthread_t p);
+
+uint64_t call_remote(mach_port_t task_port, void* fptr, int n_params, ...) {
+    if (n_params > MAX_REMOTE_ARGS || n_params < 0){
+        NSLog(@"unsupported number of arguments to remote function (%d)\n", n_params);
+        return 0;
+    }
+    
+    kern_return_t err;
+    
+    uint64_t remote_stack_base = 0;
+    uint64_t remote_stack_size = 4*1024*1024;
+    
+    remote_stack_base = remote_alloc(task_port, remote_stack_size);
+    
+    uint64_t remote_stack_middle = remote_stack_base + (remote_stack_size/2);
+    
+    // create a new thread in the target
+    // just using the mach thread API doesn't initialize the pthread thread-local-storage
+    // which means that stuff which relies on that will crash
+    // we can sort-of make that work by calling _pthread_set_self(NULL) in the target process
+    // which will give the newly created thread the same TLS region as the main thread
+    
+    
+    _STRUCT_ARM_THREAD_STATE64 thread_state = {0};
+    mach_msg_type_number_t thread_stateCnt = sizeof(thread_state)/4;
+    
+    // we'll start the thread running and call _pthread_set_self first:
+    thread_state.__sp = remote_stack_middle;
+    thread_state.__pc = (uint64_t)_pthread_set_self;
+    
+    // set these up to put us into a predictable state we can monitor for:
+    uint64_t loop_lr = find_blr_x19_gadget();
+    thread_state.__x[19] = loop_lr;
+    thread_state.__lr = loop_lr;
+    
+    // set the argument to NULL:
+    thread_state.__x[0] = 0;
+    
+    mach_port_t thread_port = MACH_PORT_NULL;
+    
+    err = thread_create_running(task_port, ARM_THREAD_STATE64, (thread_state_t)&thread_state, thread_stateCnt, &thread_port);
+    if (err != KERN_SUCCESS){
+        NSLog(@"error creating thread in child: %s\n", mach_error_string(err));
+        return 0;
+    }
+    // NSLog(@"new thread running in child: %x\n", thread_port);
+    
+    // wait for it to hit the loop:
+    while(1){
+        // monitor the thread until we see it's in the infinite loop indicating it's done:
+        err = thread_get_state(thread_port, ARM_THREAD_STATE64, (thread_state_t)&thread_state, &thread_stateCnt);
+        if (err != KERN_SUCCESS){
+            NSLog(@"error getting thread state: %s\n", mach_error_string(err));
+            return 0;
+        }
+        
+        if (thread_state.__pc == loop_lr && thread_state.__x[19] == loop_lr){
+            // thread has returned from the target function
+            break;
+        }
+    }
+    
+    // the thread should now have pthread local storage
+    // pause it:
+    
+    err = thread_suspend(thread_port);
+    if (err != KERN_SUCCESS){
+        NSLog(@"unable to suspend target thread\n");
+        return 0;
+    }
+    
+    /*
+     err = thread_abort(thread_port);
+     if (err != KERN_SUCCESS){
+     NSLog(@"unable to get thread out of any traps\n");
+     return 0;
+     }
+     */
+    
+    // set up for the actual target call:
+    thread_state.__sp = remote_stack_middle;
+    thread_state.__pc = (uint64_t)fptr;
+    
+    // set these up to put us into a predictable state we can monitor for:
+    thread_state.__x[19] = loop_lr;
+    thread_state.__lr = loop_lr;
+    
+    va_list ap;
+    va_start(ap, n_params);
+    
+    arg_desc* args[MAX_REMOTE_ARGS] = {0};
+    
+    uint64_t remote_buffers[MAX_REMOTE_ARGS] = {0};
+    //uint64_t remote_buffer_sizes[MAX_REMOTE_ARGS] = {0};
+    
+    for (int i = 0; i < n_params; i++){
+        arg_desc* arg = va_arg(ap, arg_desc*);
+        
+        args[i] = arg;
+        
+        switch(arg->type){
+                case ARG_LITERAL:
+            {
+                thread_state.__x[i] = arg->value;
+                break;
+            }
+                
+                case ARG_BUFFER:
+                case ARG_BUFFER_PERSISTENT:
+                case ARG_INOUT_BUFFER:
+            {
+                uint64_t remote_buffer = alloc_and_fill_remote_buffer(task_port, arg->value, arg->length);
+                remote_buffers[i] = remote_buffer;
+                thread_state.__x[i] = remote_buffer;
+                break;
+            }
+                
+                case ARG_OUT_BUFFER:
+            {
+                uint64_t remote_buffer = remote_alloc(task_port, arg->length);
+                // NSLog(@"allocated a remote out buffer: %llx\n", remote_buffer);
+                remote_buffers[i] = remote_buffer;
+                thread_state.__x[i] = remote_buffer;
+                break;
+            }
+                
+            default:
+            {
+                NSLog(@"invalid argument type!\n");
+            }
+        }
+    }
+    
+    va_end(ap);
+    
+    err = thread_set_state(thread_port, ARM_THREAD_STATE64, (thread_state_t)&thread_state, thread_stateCnt);
+    if (err != KERN_SUCCESS){
+        NSLog(@"error setting new thread state: %s\n", mach_error_string(err));
+        return 0;
+    }
+    // NSLog(@"thread state updated in target: %x\n", thread_port);
+    
+    err = thread_resume(thread_port);
+    if (err != KERN_SUCCESS){
+        NSLog(@"unable to resume target thread\n");
+        return 0;
+    }
+    
+    while(1){
+        // monitor the thread until we see it's in the infinite loop indicating it's done:
+        err = thread_get_state(thread_port, ARM_THREAD_STATE64, (thread_state_t)&thread_state, &thread_stateCnt);
+        if (err != KERN_SUCCESS){
+            NSLog(@"error getting thread state: %s\n", mach_error_string(err));
+            return 0;
+        }
+        
+        if (thread_state.__pc == loop_lr/*&& thread_state.__x[19] == loop_lr*/){
+            // thread has returned from the target function
+            break;
+        }
+        
+        // thread isn't in the infinite loop yet, let it continue
+    }
+    
+    // deallocate the remote thread
+    err = thread_terminate(thread_port);
+    if (err != KERN_SUCCESS){
+        NSLog(@"failed to terminate thread\n");
+        return 0;
+    }
+    mach_port_deallocate(mach_task_self(), thread_port);
+    
+    // handle post-call argument cleanup/copying:
+    for (int i = 0; i < MAX_REMOTE_ARGS; i++){
+        arg_desc* arg = args[i];
+        if (arg == NULL){
+            break;
+        }
+        switch (arg->type){
+                case ARG_BUFFER:
+            {
+                remote_free(task_port, remote_buffers[i], arg->length);
+                break;
+            }
+                
+                case ARG_INOUT_BUFFER:
+                case ARG_OUT_BUFFER:
+            {
+                // copy the contents back:
+                remote_read_overwrite(task_port, remote_buffers[i], arg->value, arg->length);
+                remote_free(task_port, remote_buffers[i], arg->length);
+                break;
+            }
+        }
+    }
+    
+    uint64_t ret_val = thread_state.__x[0];
+    
+    // NSLog(@"remote function call return value: %llx\n", ret_val);
+    
+    // deallocate the stack in the target:
+    remote_free(task_port, remote_stack_base, remote_stack_size);
+    
+    return ret_val;
+}

+ 16 - 0
Meridian/Meridian/helpers/main.m

@@ -0,0 +1,16 @@
+//
+//  main.m
+//  Meridian
+//
+//  Created by Ben Sparkes on 22/12/2017.
+//  Copyright © 2017 Ben Sparkes. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+#import "AppDelegate.h"
+
+int main(int argc, char * argv[]) {
+    @autoreleasepool {
+        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
+    }
+}

+ 235 - 0
Meridian/Meridian/helpers/nvpatch.c

@@ -0,0 +1,235 @@
+/*
+ * nvpatch.c - Patch kernel to unrestrict NVRAM variables
+ *
+ * Copyright (c) 2014 Samuel Groß
+ * Copyright (c) 2016 Pupyshev Nikita
+ * Copyright (c) 2017 Siguza
+ */
+
+#include <errno.h>              // errno
+#include <stdio.h>              // fprintf, stderr
+#include <stdlib.h>             // free, malloc
+#include <string.h>             // memmem, strcmp, strnlen
+#include <mach/vm_types.h>      // vm_address_t
+#include <mach-o/loader.h>
+
+#include "kernel.h"
+
+#define MAX_HEADER_SIZE 0x4000
+
+#define STRING_SEG  "__TEXT"
+#define STRING_SEC  "__cstring"
+#define OFVAR_SEG   "__DATA"
+#define OFVAR_SEC   "__data"
+
+enum
+{
+    kOFVarTypeBoolean = 1,
+    kOFVarTypeNumber,
+    kOFVarTypeString,
+    kOFVarTypeData,
+};
+
+enum
+{
+    kOFVarPermRootOnly = 0,
+    kOFVarPermUserRead,
+    kOFVarPermUserWrite,
+    kOFVarPermKernelOnly,
+};
+
+typedef struct
+{
+    vm_address_t name;
+    uint32_t type;
+    uint32_t perm;
+    int32_t offset;
+} OFVar;
+
+typedef struct
+{
+    vm_address_t addr;
+    vm_size_t len;
+    char *buf;
+} segment_t;
+
+int nvpatch(const char *target) {
+    struct mach_header_64 *hdr = malloc(MAX_HEADER_SIZE);
+    if (hdr == NULL) return -1;
+    memset(hdr, 0, MAX_HEADER_SIZE);
+    
+    kread(kernel_base, hdr, MAX_HEADER_SIZE);
+    
+    segment_t cstring = {
+        .addr = 0,
+        .len = 0,
+        .buf = NULL,
+    },
+    data = {
+        .addr = 0,
+        .len = 0,
+        .buf = NULL,
+    };
+    
+    for (struct load_command *cmd = (struct load_command *)(hdr + 1),
+                             *end = (struct load_command *)((char *)cmd + hdr->sizeofcmds);
+         cmd < end;
+         cmd = (struct load_command *)((char *)cmd + cmd->cmdsize)) {
+        switch (cmd->cmd) {
+            case LC_SEGMENT_64:
+            {
+                struct segment_command_64 *seg = (struct segment_command_64 *)cmd;
+                struct section_64 *sec = (struct section_64 *)(seg + 1);
+                
+                for (size_t i = 0; i < seg->nsects; ++i) {
+                    if (strcmp(sec[i].segname, STRING_SEG) == 0 &&
+                        strcmp(sec[i].sectname, STRING_SEC) == 0) {
+                        cstring.addr = sec[i].addr;
+                        cstring.len = sec[i].size;
+                        cstring.buf = malloc(cstring.len);
+                        
+                        kread(cstring.addr, cstring.buf, cstring.len);
+                    } else if (strcmp(sec[i].segname, OFVAR_SEG) == 0 &&
+                               strcmp(sec[i].sectname, OFVAR_SEC) == 0) {
+                        data.addr = sec[i].addr;
+                        data.len = sec[i].size;
+                        data.buf = malloc(data.len);
+                        
+                        kread(data.addr, data.buf, data.len);
+                    }
+                }
+            }
+                
+            default:
+                break;
+        }
+    }
+    
+    if (cstring.buf == NULL) {
+        printf("failed to find %s.%s section \n", STRING_SEG, STRING_SEC);
+        return -2;
+    }
+    
+    if (data.buf == NULL) {
+        printf("failed to find %s.%s section \n", OFVAR_SEG, OFVAR_SEC);
+        return -3;
+    }
+    
+    char first[] = "little-endian?";
+    char *str = memmem(cstring.buf, cstring.len, first, sizeof(first));
+    if (str == NULL) {
+        printf("failed to find string %s \n", first);
+        return -4;
+    }
+    
+    vm_address_t str_addr = (str - cstring.buf) + cstring.addr;
+    printf("found string %s at %lx \n", first, str_addr);
+    
+    OFVar *gOFVars = NULL;
+    for (vm_address_t *ptr = (vm_address_t *)data.buf,
+                      *end = (vm_address_t *)&data.buf[data.len];
+         ptr < end;
+         ++ptr) {
+        if (*ptr == str_addr) {
+            gOFVars = (OFVar *)ptr;
+            break;
+        }
+    }
+    
+    if (gOFVars == NULL) {
+        printf("failed to find gOFVariables \n");
+        return -5;
+    }
+    
+    vm_address_t gOFAddr = ((char *)gOFVars - data.buf) + data.addr;
+    printf("found gOFVariables at %lx \n", gOFAddr);
+    
+    size_t numvars = 0;
+    size_t longest_name = 0;
+    
+    for (OFVar *var = gOFVars; (char *)var < &data.buf[data.len]; ++var) {
+        if (var->name == 0) {
+            break;
+        }
+        
+        if (var->name < cstring.addr || var->name >= cstring.addr + cstring.len) {
+            printf("gOFVariables[%lu].name is out of bounds \n", numvars);
+            return -6;
+        }
+        
+        char *name = &cstring.buf[var->name - cstring.addr];
+        size_t maxlen = cstring.len - (name - cstring.buf);
+        size_t namelen = strnlen(name, maxlen);
+        if (namelen == maxlen) {
+            printf("gOFVariables[%lu].name exceeds __cstring size \n", numvars);
+            return -7;
+        }
+        
+        for (size_t i = 0; i < namelen; ++i) {
+            if (name[i] < 0x20 || name[i] > 0x7f) {
+                printf("gOFVariables[%lu].name contains non-printable character: 0x%02x \n", numvars, name[i]);
+                return -8;
+            }
+        }
+        
+        longest_name = namelen > longest_name ? namelen : longest_name;
+        
+        switch (var->type) {
+            case kOFVarTypeBoolean:
+            case kOFVarTypeNumber:
+            case kOFVarTypeString:
+            case kOFVarTypeData:
+                break;
+                
+            default:
+                printf("gOFVariables[%lu] has unknown type: 0x%x \n", numvars, var->type);
+                return -9;
+        }
+        
+        switch (var->perm) {
+            case kOFVarPermRootOnly:
+            case kOFVarPermUserRead:
+            case kOFVarPermUserWrite:
+            case kOFVarPermKernelOnly:
+                break;
+                
+            default:
+                printf("gOFVariables[%lu] has unknown permissions: 0x%x \n", numvars, var->perm);
+                return -10;
+        }
+        
+        ++numvars;
+    }
+    
+    if (numvars <= 0) {
+        printf("gOFVariables contains zero entries \n");
+        return -11;
+    }
+    
+    for (size_t i = 0; i < numvars; ++i) {
+        char *name = &cstring.buf[gOFVars[i].name - cstring.addr];
+        if (strcmp(name, target) == 0) {
+            if (gOFVars[i].perm != kOFVarPermKernelOnly) {
+                printf("target var %s is not set as kernel-only \n", target);
+                goto done;
+            }
+            
+            vm_size_t off = ((char *)&gOFVars[i].perm) - data.buf;
+            uint32_t newperm = kOFVarPermUserWrite;
+            kwrite(data.addr + off, &newperm, sizeof(newperm));
+            printf("great success for var %s! \n", target);
+            goto done;
+        }
+    }
+    printf("failed to find variable %s! \n", target);
+    return -13;
+    
+done:;
+    
+    free(cstring.buf);
+    free(data.buf);
+    free(hdr);
+    
+    return 0;
+}
+

+ 14 - 0
Meridian/Meridian/helpers/nvpatch.h

@@ -0,0 +1,14 @@
+//
+//  nvpatch.h
+//  Meridian
+//
+//  Created by Ben Sparkes on 11/05/2018.
+//  Copyright © 2018 Ben Sparkes. All rights reserved.
+//
+
+#ifndef nvpatch_h
+#define nvpatch_h
+
+int nvpatch(const char *target);
+
+#endif /* nvpatch_h */

+ 14 - 0
Meridian/Meridian/helpers/untar.h

@@ -0,0 +1,14 @@
+//
+//  untar.h
+//  Meridian
+//
+//  Created by Ben Sparkes on 16/02/2018.
+//  Copyright © 2018 Ben Sparkes. All rights reserved.
+//
+
+#ifndef untar_h
+#define untar_h
+
+int untar(FILE *a, const char *path);
+
+#endif /* untar_h */

+ 227 - 0
Meridian/Meridian/helpers/untar.m

@@ -0,0 +1,227 @@
+/*
+ * "untar" is an extremely simple tar extractor:
+ *  * A single C source file, so it should be easy to compile
+ *    and run on any system with a C compiler.
+ *  * Extremely portable standard C.  The only non-ANSI function
+ *    used is mkdir().
+ *  * Reads basic ustar tar archives.
+ *  * Does not require libarchive or any other special library.
+ *
+ * To compile: cc -o untar untar.c
+ *
+ * Usage:  untar <archive>
+ *
+ * In particular, this program should be sufficient to extract the
+ * distribution for libarchive, allowing people to bootstrap
+ * libarchive on systems that do not already have a tar program.
+ *
+ * To unpack libarchive-x.y.z.tar.gz:
+ *    * gunzip libarchive-x.y.z.tar.gz
+ *    * untar libarchive-x.y.z.tar
+ *
+ * Written by Tim Kientzle, March 2009.
+ * Modified by xerub, sometime in 2017.
+ *
+ * Released into the public domain.
+ */
+
+/* These are all highly standard and portable headers. */
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+/* This is for mkdir(); this may need to be changed for some platforms. */
+#include <sys/stat.h>  /* For mkdir() */
+
+#include <Foundation/Foundation.h>
+
+/* Parse an octal number, ignoring leading and trailing nonsense. */
+static int
+parseoct(const char *p, size_t n)
+{
+	int i = 0;
+
+	while (*p < '0' || *p > '7') {
+		++p;
+		--n;
+	}
+	while (*p >= '0' && *p <= '7' && n > 0) {
+		i *= 8;
+		i += *p - '0';
+		++p;
+		--n;
+	}
+	return (i);
+}
+
+/* Returns true if this is 512 zero bytes. */
+static int
+is_end_of_archive(const char *p)
+{
+	int n;
+	for (n = 511; n >= 0; --n)
+		if (p[n] != '\0')
+			return (0);
+	return (1);
+}
+
+/* Create a directory, including parent directories as necessary. */
+static void
+create_dir(char *pathname, int mode, int owner, int group)
+{
+	char *p;
+	int r;
+
+	struct stat st;
+	r = stat(pathname, &st);
+	if (r == 0) {
+		return;
+	}
+
+	/* Strip trailing '/' */
+	if (pathname[strlen(pathname) - 1] == '/')
+		pathname[strlen(pathname) - 1] = '\0';
+
+	/* Try creating the directory. */
+	r = mkdir(pathname, mode);
+
+	if (r != 0) {
+		/* On failure, try creating parent directory. */
+		p = strrchr(pathname, '/');
+		if (p != NULL) {
+			*p = '\0';
+			create_dir(pathname, 0755, -1, -1);
+			*p = '/';
+			r = mkdir(pathname, mode);
+		}
+	}
+	if (r != 0)
+		NSLog(@"Could not create directory %s", pathname);
+	else if (owner >= 0 && group >= 0)
+		chown(pathname, owner, group);
+}
+
+/* Create a file, including parent directory as necessary. */
+static int
+create_file(char *pathname, int mode, int owner, int group)
+{
+	int f;
+	if (unlink(pathname) && errno != ENOENT) {
+		return -1;
+	}
+	f = creat(pathname, mode);
+	if (f < 0) {
+		/* Try creating parent dir and then creating file. */
+		char *p = strrchr(pathname, '/');
+		if (p != NULL) {
+			*p = '\0';
+			create_dir(pathname, 0755, -1, -1);
+			*p = '/';
+			f = creat(pathname, mode);
+		}
+	}
+	fchown(f, owner, group);
+	return (f);
+}
+
+/* Verify the tar checksum. */
+static int
+verify_checksum(const char *p)
+{
+	int n, u = 0;
+	for (n = 0; n < 512; ++n) {
+		if (n < 148 || n > 155)
+			/* Standard tar checksum adds unsigned bytes. */
+			u += ((unsigned char *)p)[n];
+		else
+			u += 0x20;
+
+	}
+	return (u == parseoct(p + 148, 8));
+}
+
+/* Extract a tar archive. */
+int
+untar(FILE *a, const char *path)
+{
+	char buff[512];
+	int f = -1;
+	size_t bytes_read;
+	int filesize;
+
+	NSLog(@"Extracting from %s", path);
+	for (;;) {
+		bytes_read = fread(buff, 1, 512, a);
+		if (bytes_read < 512) {
+			NSLog(@"Short read on %s: expected 512, got %d", path, (int)bytes_read);
+			return -1;
+		}
+		if (is_end_of_archive(buff)) {
+			NSLog(@"End of %s", path);
+			return 0;
+		}
+		if (!verify_checksum(buff)) {
+			NSLog(@"Checksum failure");
+			return -2;
+		}
+		filesize = parseoct(buff + 124, 12);
+		switch (buff[156]) {
+		case '1':
+			NSLog(@" Ignoring hardlink %s", buff);
+			break;
+		case '2':
+			NSLog(@" Extracting symlink %s -> %s", buff, buff + 157);
+			if (unlink(buff) && errno != ENOENT) {
+				break;
+			}
+			symlink(buff + 157, buff);
+			break;
+		case '3':
+			NSLog(@" Ignoring character device %s", buff);
+				break;
+		case '4':
+			NSLog(@" Ignoring block device %s", buff);
+			break;
+		case '5':
+			NSLog(@" Extracting dir %s", buff);
+			create_dir(buff, parseoct(buff + 100, 8), parseoct(buff + 108, 8), parseoct(buff + 116, 8));
+			filesize = 0;
+			break;
+		case '6':
+			NSLog(@" Ignoring FIFO %s", buff);
+			break;
+		default:
+			NSLog(@" Extracting file %s", buff);
+			f = create_file(buff, parseoct(buff + 100, 8), parseoct(buff + 108, 8), parseoct(buff + 116, 8));
+			break;
+		}
+		while (filesize > 0) {
+			bytes_read = fread(buff, 1, 512, a);
+			if (bytes_read < 512) {
+				NSLog(@"Short read on %s: Expected 512, got %zd", path, bytes_read);
+				return -3;
+			}
+			if (filesize < 512)
+				bytes_read = filesize;
+			if (f >= 0) {
+				if (write(f, buff, bytes_read)
+				    != bytes_read)
+				{
+					NSLog(@"Failed write");
+					close(f);
+					f = -1;
+				}
+			}
+			filesize -= bytes_read;
+		}
+		if (f >= 0) {
+			close(f);
+			f = -1;
+		}
+	}
+    
+    return 0;
+}

+ 28 - 0
Meridian/Meridian/jailbreak.h

@@ -0,0 +1,28 @@
+//
+//  jailbreak.h
+//  Meridian
+//
+//  Created by Ben Sparkes on 16/02/2018.
+//  Copyright © 2018 Ben Sparkes. All rights reserved.
+//
+
+#ifndef jailbreak_h
+#define jailbreak_h
+
+BOOL great_success;
+
+int makeShitHappen(id view);
+int runV0rtex(void);
+int patchContainermanagerd(void);
+int remountRootFs(void);
+int extractMeridianData(void);
+void setUpSymLinks(void);
+int extractBootstrap(int *exitCode);
+int defecateAmfi(void);
+int launchDropbear(void);
+void setUpSubstitute(void);
+int startJailbreakd(void);
+int loadLaunchDaemons(void);
+void enableHiddenApps(void);
+
+#endif /* jailbreak_h */

+ 551 - 0
Meridian/Meridian/jailbreak.m

@@ -0,0 +1,551 @@
+//
+//  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 <mach/mach_types.h>
+#include <sys/stat.h>
+#import <Foundation/Foundation.h>
+
+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");
+}

+ 249 - 0
Meridian/Meridian/mach/jailbreak_daemonUser.c

@@ -0,0 +1,249 @@
+/*
+ * IDENTIFICATION:
+ * stub generated Tue Mar  6 23:07:11 2018
+ * with a MiG generated by bootstrap_cmds-96.20.2
+ * OPTIONS: 
+ */
+#define	__MIG_check__Reply__jailbreak_daemon_subsystem__ 1
+
+#include "jailbreak_daemonUser.h"
+
+
+#ifndef	mig_internal
+#define	mig_internal	static __inline__
+#endif	/* mig_internal */
+
+#ifndef	mig_external
+#define mig_external
+#endif	/* mig_external */
+
+#if	!defined(__MigTypeCheck) && defined(TypeCheck)
+#define	__MigTypeCheck		TypeCheck	/* Legacy setting */
+#endif	/* !defined(__MigTypeCheck) */
+
+#if	!defined(__MigKernelSpecificCode) && defined(_MIG_KERNEL_SPECIFIC_CODE_)
+#define	__MigKernelSpecificCode	_MIG_KERNEL_SPECIFIC_CODE_	/* Legacy setting */
+#endif	/* !defined(__MigKernelSpecificCode) */
+
+#ifndef	LimitCheck
+#define	LimitCheck 0
+#endif	/* LimitCheck */
+
+#ifndef	min