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 5 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
+#define	min(a,b)  ( ((a) < (b))? (a): (b) )
+#endif	/* min */
+
+#if !defined(_WALIGN_)
+#define _WALIGN_(x) (((x) + 3) & ~3)
+#endif /* !defined(_WALIGN_) */
+
+#if !defined(_WALIGNSZ_)
+#define _WALIGNSZ_(x) _WALIGN_(sizeof(x))
+#endif /* !defined(_WALIGNSZ_) */
+
+#ifndef	UseStaticTemplates
+#define	UseStaticTemplates	0
+#endif	/* UseStaticTemplates */
+
+#ifndef	__MachMsgErrorWithTimeout
+#define	__MachMsgErrorWithTimeout(_R_) { \
+	switch (_R_) { \
+	case MACH_SEND_INVALID_DATA: \
+	case MACH_SEND_INVALID_DEST: \
+	case MACH_SEND_INVALID_HEADER: \
+		mig_put_reply_port(InP->Head.msgh_reply_port); \
+		break; \
+	case MACH_SEND_TIMED_OUT: \
+	case MACH_RCV_TIMED_OUT: \
+	default: \
+		mig_dealloc_reply_port(InP->Head.msgh_reply_port); \
+	} \
+}
+#endif	/* __MachMsgErrorWithTimeout */
+
+#ifndef	__MachMsgErrorWithoutTimeout
+#define	__MachMsgErrorWithoutTimeout(_R_) { \
+	switch (_R_) { \
+	case MACH_SEND_INVALID_DATA: \
+	case MACH_SEND_INVALID_DEST: \
+	case MACH_SEND_INVALID_HEADER: \
+		mig_put_reply_port(InP->Head.msgh_reply_port); \
+		break; \
+	default: \
+		mig_dealloc_reply_port(InP->Head.msgh_reply_port); \
+	} \
+}
+#endif	/* __MachMsgErrorWithoutTimeout */
+
+#ifndef	__DeclareSendRpc
+#define	__DeclareSendRpc(_NUM_, _NAME_)
+#endif	/* __DeclareSendRpc */
+
+#ifndef	__BeforeSendRpc
+#define	__BeforeSendRpc(_NUM_, _NAME_)
+#endif	/* __BeforeSendRpc */
+
+#ifndef	__AfterSendRpc
+#define	__AfterSendRpc(_NUM_, _NAME_)
+#endif	/* __AfterSendRpc */
+
+#ifndef	__DeclareSendSimple
+#define	__DeclareSendSimple(_NUM_, _NAME_)
+#endif	/* __DeclareSendSimple */
+
+#ifndef	__BeforeSendSimple
+#define	__BeforeSendSimple(_NUM_, _NAME_)
+#endif	/* __BeforeSendSimple */
+
+#ifndef	__AfterSendSimple
+#define	__AfterSendSimple(_NUM_, _NAME_)
+#endif	/* __AfterSendSimple */
+
+#define msgh_request_port	msgh_remote_port
+#define msgh_reply_port		msgh_local_port
+
+
+
+#if ( __MigTypeCheck )
+#if __MIG_check__Reply__jailbreak_daemon_subsystem__
+#if !defined(__MIG_check__Reply__call_t__defined)
+#define __MIG_check__Reply__call_t__defined
+
+mig_internal kern_return_t __MIG_check__Reply__call_t(__Reply__call_t *Out0P)
+{
+
+	typedef __Reply__call_t __Reply __attribute__((unused));
+	if (Out0P->Head.msgh_id != 600) {
+	    if (Out0P->Head.msgh_id == MACH_NOTIFY_SEND_ONCE)
+		{ return MIG_SERVER_DIED; }
+	    else
+		{ return MIG_REPLY_MISMATCH; }
+	}
+
+#if	__MigTypeCheck
+	if ((Out0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) ||
+	    (Out0P->Head.msgh_size != (mach_msg_size_t)sizeof(__Reply)))
+		{ return MIG_TYPE_ERROR ; }
+#endif	/* __MigTypeCheck */
+
+	{
+		return Out0P->RetCode;
+	}
+}
+#endif /* !defined(__MIG_check__Reply__call_t__defined) */
+#endif /* __MIG_check__Reply__jailbreak_daemon_subsystem__ */
+#endif /* ( __MigTypeCheck ) */
+
+
+/* Routine call */
+mig_external kern_return_t jbd_call
+(
+	mach_port_t server_port,
+	uint8_t command,
+	uint32_t pid
+)
+{
+
+#ifdef  __MigPackStructs
+#pragma pack(4)
+#endif
+	typedef struct {
+		mach_msg_header_t Head;
+		NDR_record_t NDR;
+		uint8_t command;
+		char commandPad[3];
+		uint32_t pid;
+	} Request __attribute__((unused));
+#ifdef  __MigPackStructs
+#pragma pack()
+#endif
+
+#ifdef  __MigPackStructs
+#pragma pack(4)
+#endif
+	typedef struct {
+		mach_msg_header_t Head;
+		NDR_record_t NDR;
+		kern_return_t RetCode;
+		mach_msg_trailer_t trailer;
+	} Reply __attribute__((unused));
+#ifdef  __MigPackStructs
+#pragma pack()
+#endif
+
+#ifdef  __MigPackStructs
+#pragma pack(4)
+#endif
+	typedef struct {
+		mach_msg_header_t Head;
+		NDR_record_t NDR;
+		kern_return_t RetCode;
+	} __Reply __attribute__((unused));
+#ifdef  __MigPackStructs
+#pragma pack()
+#endif
+	/*
+	 * typedef struct {
+	 * 	mach_msg_header_t Head;
+	 * 	NDR_record_t NDR;
+	 * 	kern_return_t RetCode;
+	 * } mig_reply_error_t;
+	 */
+
+	union {
+		Request In;
+		Reply Out;
+	} Mess;
+
+	Request *InP = &Mess.In;
+	Reply *Out0P = &Mess.Out;
+
+	mach_msg_return_t msg_result;
+
+#ifdef	__MIG_check__Reply__call_t__defined
+	kern_return_t check_result;
+#endif	/* __MIG_check__Reply__call_t__defined */
+
+	__DeclareSendRpc(500, "call")
+
+	InP->NDR = NDR_record;
+
+	InP->command = command;
+
+	InP->pid = pid;
+
+	InP->Head.msgh_bits =
+		MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
+	/* msgh_size passed as argument */
+	InP->Head.msgh_request_port = server_port;
+	InP->Head.msgh_reply_port = mig_get_reply_port();
+	InP->Head.msgh_id = 500;
+	InP->Head.msgh_reserved = 0;
+	
+/* BEGIN VOUCHER CODE */
+
+#ifdef USING_VOUCHERS
+	if (voucher_mach_msg_set != NULL) {
+		voucher_mach_msg_set(&InP->Head);
+	}
+#endif // USING_VOUCHERS
+	
+/* END VOUCHER CODE */
+
+	__BeforeSendRpc(500, "call")
+	msg_result = mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, (mach_msg_size_t)sizeof(Request), (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
+	__AfterSendRpc(500, "call")
+	if (msg_result != MACH_MSG_SUCCESS) {
+		__MachMsgErrorWithoutTimeout(msg_result);
+		{ return msg_result; }
+	}
+
+
+#if	defined(__MIG_check__Reply__call_t__defined)
+	check_result = __MIG_check__Reply__call_t((__Reply__call_t *)Out0P);
+	if (check_result != MACH_MSG_SUCCESS)
+		{ return check_result; }
+#endif	/* defined(__MIG_check__Reply__call_t__defined) */
+
+	return KERN_SUCCESS;
+}

+ 183 - 0
Meridian/Meridian/mach/jailbreak_daemonUser.h

@@ -0,0 +1,183 @@
+#ifndef	_jailbreak_daemon_user_
+#define	_jailbreak_daemon_user_
+
+/* Module jailbreak_daemon */
+
+#include <string.h>
+#include <mach/ndr.h>
+#include <mach/boolean.h>
+#include <mach/kern_return.h>
+#include <mach/notify.h>
+#include <mach/mach_types.h>
+#include <mach/message.h>
+#include <mach/mig_errors.h>
+#include <mach/port.h>
+	
+/* BEGIN VOUCHER CODE */
+
+#ifndef KERNEL
+#if defined(__has_include)
+#if __has_include(<mach/mig_voucher_support.h>)
+#ifndef USING_VOUCHERS
+#define USING_VOUCHERS
+#endif
+#ifndef __VOUCHER_FORWARD_TYPE_DECLS__
+#define __VOUCHER_FORWARD_TYPE_DECLS__
+#ifdef __cplusplus
+extern "C" {
+#endif
+	extern boolean_t voucher_mach_msg_set(mach_msg_header_t *msg) __attribute__((weak_import));
+#ifdef __cplusplus
+}
+#endif
+#endif // __VOUCHER_FORWARD_TYPE_DECLS__
+#endif // __has_include(<mach/mach_voucher_types.h>)
+#endif // __has_include
+#endif // !KERNEL
+	
+/* END VOUCHER CODE */
+
+	
+/* BEGIN MIG_STRNCPY_ZEROFILL CODE */
+
+#if defined(__has_include)
+#if __has_include(<mach/mig_strncpy_zerofill_support.h>)
+#ifndef USING_MIG_STRNCPY_ZEROFILL
+#define USING_MIG_STRNCPY_ZEROFILL
+#endif
+#ifndef __MIG_STRNCPY_ZEROFILL_FORWARD_TYPE_DECLS__
+#define __MIG_STRNCPY_ZEROFILL_FORWARD_TYPE_DECLS__
+#ifdef __cplusplus
+extern "C" {
+#endif
+	extern int mig_strncpy_zerofill(char *dest, const char *src, int len) __attribute__((weak_import));
+#ifdef __cplusplus
+}
+#endif
+#endif /* __MIG_STRNCPY_ZEROFILL_FORWARD_TYPE_DECLS__ */
+#endif /* __has_include(<mach/mig_strncpy_zerofill_support.h>) */
+#endif /* __has_include */
+	
+/* END MIG_STRNCPY_ZEROFILL CODE */
+
+
+#ifdef AUTOTEST
+#ifndef FUNCTION_PTR_T
+#define FUNCTION_PTR_T
+typedef void (*function_ptr_t)(mach_port_t, char *, mach_msg_type_number_t);
+typedef struct {
+        char            *name;
+        function_ptr_t  function;
+} function_table_entry;
+typedef function_table_entry   *function_table_t;
+#endif /* FUNCTION_PTR_T */
+#endif /* AUTOTEST */
+
+#ifndef	jailbreak_daemon_MSG_COUNT
+#define	jailbreak_daemon_MSG_COUNT	1
+#endif	/* jailbreak_daemon_MSG_COUNT */
+
+#include <mach/std_types.h>
+#include <mach/mig.h>
+#include <mach/mig.h>
+#include <mach/mach_types.h>
+
+#ifdef __BeforeMigUserHeader
+__BeforeMigUserHeader
+#endif /* __BeforeMigUserHeader */
+
+#include <sys/cdefs.h>
+__BEGIN_DECLS
+
+
+/* Routine call */
+#ifdef	mig_external
+mig_external
+#else
+extern
+#endif	/* mig_external */
+kern_return_t jbd_call
+(
+	mach_port_t server_port,
+	uint8_t command,
+	uint32_t pid
+);
+
+__END_DECLS
+
+/********************** Caution **************************/
+/* The following data types should be used to calculate  */
+/* maximum message sizes only. The actual message may be */
+/* smaller, and the position of the arguments within the */
+/* message layout may vary from what is presented here.  */
+/* For example, if any of the arguments are variable-    */
+/* sized, and less than the maximum is sent, the data    */
+/* will be packed tight in the actual message to reduce  */
+/* the presence of holes.                                */
+/********************** Caution **************************/
+
+/* typedefs for all requests */
+
+#ifndef __Request__jailbreak_daemon_subsystem__defined
+#define __Request__jailbreak_daemon_subsystem__defined
+
+#ifdef  __MigPackStructs
+#pragma pack(4)
+#endif
+	typedef struct {
+		mach_msg_header_t Head;
+		NDR_record_t NDR;
+		uint8_t command;
+		char commandPad[3];
+		uint32_t pid;
+	} __Request__call_t __attribute__((unused));
+#ifdef  __MigPackStructs
+#pragma pack()
+#endif
+#endif /* !__Request__jailbreak_daemon_subsystem__defined */
+
+/* union of all requests */
+
+#ifndef __RequestUnion__jbd_jailbreak_daemon_subsystem__defined
+#define __RequestUnion__jbd_jailbreak_daemon_subsystem__defined
+union __RequestUnion__jbd_jailbreak_daemon_subsystem {
+	__Request__call_t Request_jbd_call;
+};
+#endif /* !__RequestUnion__jbd_jailbreak_daemon_subsystem__defined */
+/* typedefs for all replies */
+
+#ifndef __Reply__jailbreak_daemon_subsystem__defined
+#define __Reply__jailbreak_daemon_subsystem__defined
+
+#ifdef  __MigPackStructs
+#pragma pack(4)
+#endif
+	typedef struct {
+		mach_msg_header_t Head;
+		NDR_record_t NDR;
+		kern_return_t RetCode;
+	} __Reply__call_t __attribute__((unused));
+#ifdef  __MigPackStructs
+#pragma pack()
+#endif
+#endif /* !__Reply__jailbreak_daemon_subsystem__defined */
+
+/* union of all replies */
+
+#ifndef __ReplyUnion__jbd_jailbreak_daemon_subsystem__defined
+#define __ReplyUnion__jbd_jailbreak_daemon_subsystem__defined
+union __ReplyUnion__jbd_jailbreak_daemon_subsystem {
+	__Reply__call_t Reply_jbd_call;
+};
+#endif /* !__RequestUnion__jbd_jailbreak_daemon_subsystem__defined */
+
+#ifndef subsystem_to_name_map_jailbreak_daemon
+#define subsystem_to_name_map_jailbreak_daemon \
+    { "call", 500 }
+#endif
+
+#ifdef __AfterMigUserHeader
+__AfterMigUserHeader
+#endif /* __AfterMigUserHeader */
+
+#endif	 /* _jailbreak_daemon_user_ */

+ 169 - 0
Meridian/Meridian/patchfinders/liboffsetfinder64.hpp

@@ -0,0 +1,169 @@
+//
+//  offsetfinder64.hpp
+//  offsetfinder64
+//
+//  Created by tihmstar on 10.01.18.
+//  Copyright © 2018 tihmstar. All rights reserved.
+//
+
+#ifndef offsetfinder64_hpp
+#define offsetfinder64_hpp
+
+#include <string>
+#include <stdint.h>
+#include <mach-o/loader.h>
+#include <mach-o/nlist.h>
+#include <mach-o/dyld_images.h>
+#include <vector>
+
+#include <stdlib.h>
+
+typedef uint64_t offset_t;
+
+
+namespace tihmstar {
+    
+    class exception : public std::exception{
+        std::string _err;
+        int _code;
+    public:
+        exception(int code, std::string err) : _err(err), _code(code) {};
+        exception(std::string err) : _err(err), _code(0) {};
+        exception(int code) : _code(code) {};
+        const char *what(){return _err.c_str();}
+        int code(){return _code;}
+    };
+    namespace patchfinder64{
+        typedef uint8_t* loc_t;
+        
+        class patch{
+            bool _slideme;
+            void(*_slidefunc)(class patch *patch, uint64_t slide);
+        public:
+            const loc_t _location;
+            const void *_patch;
+            const size_t _patchSize;
+            patch(loc_t location, const void *patch, size_t patchSize, void(*slidefunc)(class patch *patch, uint64_t slide) = NULL) : _location(location), _patchSize(patchSize), _slidefunc(slidefunc){
+                _patch = malloc(_patchSize);
+                memcpy((void*)_patch, patch, _patchSize);
+                _slideme = (_slidefunc) ? true : false;
+            }
+            patch(const patch& cpy) : _location(cpy._location), _patchSize(cpy._patchSize){
+                _patch = malloc(_patchSize);
+                memcpy((void*)_patch, cpy._patch, _patchSize);
+                _slidefunc = cpy._slidefunc;
+                _slideme = cpy._slideme;
+            }
+            void slide(uint64_t slide){
+                if (!_slideme)
+                    return;
+                printf("sliding with %p\n",(void*)slide);
+                _slidefunc(this,slide);
+                _slideme = false; //only slide once
+            }
+            ~patch(){
+                free((void*)_patch);
+            }
+            
+        };
+    }
+    class offsetfinder64 {
+    public:
+        struct text_t{
+            patchfinder64::loc_t map;
+            size_t size;
+            patchfinder64::loc_t base;
+            bool isExec;
+        };
+        
+    private:
+        bool _freeKernel;
+        uint8_t *_kdata;
+        size_t _ksize;
+        offset_t _kslide;
+        patchfinder64::loc_t _kernel_entry;
+        std::vector<text_t> _segments;
+        
+        struct symtab_command *__symtab;
+        void loadSegments(uint64_t slide);
+        __attribute__((always_inline)) struct symtab_command *getSymtab();
+        
+    public:
+        offsetfinder64(const char *filename);
+        offsetfinder64(void* buf, size_t size, uint64_t base);
+        const void *kdata();
+        patchfinder64::loc_t find_entry();
+        const std::vector<text_t> &segments(){return _segments;};
+        
+        patchfinder64::loc_t memmem(const void *little, size_t little_len);
+        
+        patchfinder64::loc_t find_sym(const char *sym);
+        patchfinder64::loc_t find_syscall0();
+        uint64_t             find_register_value(patchfinder64::loc_t where, int reg, patchfinder64::loc_t startAddr = 0);
+        
+        /*------------------------ v0rtex -------------------------- */
+        patchfinder64::loc_t find_zone_map();
+        patchfinder64::loc_t find_kernel_map();
+        patchfinder64::loc_t find_kernel_task();
+        patchfinder64::loc_t find_realhost();
+        patchfinder64::loc_t find_bzero();
+        patchfinder64::loc_t find_bcopy();
+        patchfinder64::loc_t find_copyout();
+        patchfinder64::loc_t find_copyin();
+        patchfinder64::loc_t find_ipc_port_alloc_special();
+        patchfinder64::loc_t find_ipc_kobject_set();
+        patchfinder64::loc_t find_ipc_port_make_send();
+        patchfinder64::loc_t find_chgproccnt();
+        patchfinder64::loc_t find_kauth_cred_ref();
+        patchfinder64::loc_t find_osserializer_serialize();
+        uint32_t             find_vtab_get_external_trap_for_index();
+        uint32_t             find_vtab_get_retain_count();
+        uint32_t             find_iouserclient_ipc();
+        uint32_t             find_ipc_space_is_task();
+        uint32_t             find_proc_ucred();
+        uint32_t             find_task_bsd_info();
+        uint32_t             find_vm_map_hdr();
+        uint32_t             find_task_itk_self();
+        uint32_t             find_task_itk_registered();
+        uint32_t             find_sizeof_task();
+        
+        patchfinder64::loc_t find_rop_add_x0_x0_0x10();
+        patchfinder64::loc_t find_rop_ldr_x0_x0_0x10();
+        
+        
+        /*------------------------ kernelpatches -------------------------- */
+        patchfinder64::patch find_i_can_has_debugger_patch_off();
+        patchfinder64::patch find_lwvm_patch_offsets();
+        patchfinder64::patch find_remount_patch_offset();
+        std::vector<patchfinder64::patch> find_nosuid_off();
+        patchfinder64::patch find_proc_enforce();
+        patchfinder64::patch find_amfi_patch_offsets();
+        patchfinder64::patch find_cs_enforcement_disable_amfi();
+        patchfinder64::patch find_amfi_substrate_patch();
+//        patchfinder64::patch find_sandbox_patch();
+        patchfinder64::loc_t find_sbops();
+        patchfinder64::patch find_nonceEnabler_patch();
+
+        
+        /*------------------------ KPP bypass -------------------------- */
+        patchfinder64::loc_t find_gPhysBase();
+        patchfinder64::loc_t find_kernel_pmap();
+        patchfinder64::loc_t find_cpacr_write();
+        patchfinder64::loc_t find_idlesleep_str_loc();
+        patchfinder64::loc_t find_deepsleep_str_loc();
+
+        /*------------------------ Util -------------------------- */
+        patchfinder64::loc_t find_rootvnode();
+        
+        ~offsetfinder64();
+    };
+    using segment_t = std::vector<tihmstar::offsetfinder64::text_t>;
+    namespace patchfinder64{
+        
+        loc_t find_literal_ref(segment_t segemts, offset_t kslide, loc_t pos);
+    }
+}
+
+
+
+#endif /* offsetfinder64_hpp */

+ 14 - 0
Meridian/Meridian/patchfinders/offsetdump.h

@@ -0,0 +1,14 @@
+//
+//  offsetdump.h
+//  Meridian
+//
+//  Created by Ben Sparkes on 30/03/2018.
+//  Copyright © 2018 Ben Sparkes. All rights reserved.
+//
+
+#ifndef offsetdump_h
+#define offsetdump_h
+
+void dumpOffsetsToFile(offsets_t *offsets, uint64_t kernel_base, uint64_t kernel_slide);
+
+#endif /* offsetdump_h */

+ 65 - 0
Meridian/Meridian/patchfinders/offsetdump.m

@@ -0,0 +1,65 @@
+//
+//  offsetdump.m
+//  Meridian
+//
+//  Created by Ben Sparkes on 30/03/2018.
+//  Copyright © 2018 Ben Sparkes. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+#include "v0rtex.h"
+
+void dumpOffsetsToFile(offsets_t *offsets, uint64_t kernel_base, uint64_t kernel_slide) {
+    NSData *blob = [NSData dataWithContentsOfFile:@"/meridian/offsets.plist"];
+    NSMutableDictionary *off_file = [NSPropertyListSerialization propertyListWithData:blob
+                                                                              options:NSPropertyListMutableContainers
+                                                                               format:nil
+                                                                                error:nil];
+    
+    // There is probably a better way than doing this all manually, but ¯\_(ツ)_/¯
+    // We don't really need to log *all* of these, but better safe than PR'ing, right?
+    // See the amfid patch for an example of using this (amfid/main.m)
+    
+    off_file[@"Base"]                           = [NSString stringWithFormat:@"0x%016llx", offsets->base];
+    off_file[@"KernelBase"]                     = [NSString stringWithFormat:@"0x%016llx", kernel_base];
+    off_file[@"KernelSlide"]                    = [NSString stringWithFormat:@"0x%016llx", kernel_slide];
+    
+    off_file[@"SizeOfTask"]                     = [NSString stringWithFormat:@"0x%016llx", offsets->sizeof_task];
+    off_file[@"TaskItkSelf"]                    = [NSString stringWithFormat:@"0x%016llx", offsets->task_itk_self];
+    off_file[@"TaskItkRegistered"]              = [NSString stringWithFormat:@"0x%016llx", offsets->task_itk_registered];
+    off_file[@"TaskBsdInfo"]                    = [NSString stringWithFormat:@"0x%016llx", offsets->task_bsd_info];
+    off_file[@"ProcUcred"]                      = [NSString stringWithFormat:@"0x%016llx", offsets->proc_ucred];
+    off_file[@"VmMapHdr"]                       = [NSString stringWithFormat:@"0x%016llx", offsets->vm_map_hdr];
+    off_file[@"IpcSpaceIsTask"]                 = [NSString stringWithFormat:@"0x%016llx", offsets->ipc_space_is_task];
+    off_file[@"RealhostSpecial"]                = [NSString stringWithFormat:@"0x%016llx", offsets->realhost_special];
+    off_file[@"IOUserClientIPC"]                = [NSString stringWithFormat:@"0x%016llx", offsets->iouserclient_ipc];
+    off_file[@"VtabGetRetainCount"]             = [NSString stringWithFormat:@"0x%016llx", offsets->vtab_get_retain_count];
+    off_file[@"VtabGetExternalTrapForIndex"]    = [NSString stringWithFormat:@"0x%016llx", offsets->vtab_get_external_trap_for_index];
+    
+    off_file[@"ZoneMap"]                        = [NSString stringWithFormat:@"0x%016llx", offsets->zone_map];
+    off_file[@"KernelMap"]                      = [NSString stringWithFormat:@"0x%016llx", offsets->kernel_map];
+    off_file[@"KernelTask"]                     = [NSString stringWithFormat:@"0x%016llx", offsets->kernel_task];
+    off_file[@"RealHost"]                       = [NSString stringWithFormat:@"0x%016llx", offsets->realhost];
+    
+    off_file[@"CopyIn"]                         = [NSString stringWithFormat:@"0x%016llx", offsets->copyin];
+    off_file[@"CopyOut"]                        = [NSString stringWithFormat:@"0x%016llx", offsets->copyout];
+    off_file[@"Chgproccnt"]                     = [NSString stringWithFormat:@"0x%016llx", offsets->chgproccnt];
+    off_file[@"KauthCredRef"]                   = [NSString stringWithFormat:@"0x%016llx", offsets->kauth_cred_ref];
+    off_file[@"IpcPortAllocSpecial"]            = [NSString stringWithFormat:@"0x%016llx", offsets->ipc_port_alloc_special];
+    off_file[@"IpcKobjectSet"]                  = [NSString stringWithFormat:@"0x%016llx", offsets->ipc_kobject_set];
+    off_file[@"IpcPortMakeSend"]                = [NSString stringWithFormat:@"0x%016llx", offsets->ipc_port_make_send];
+    off_file[@"OSSerializerSerialize"]          = [NSString stringWithFormat:@"0x%016llx", offsets->osserializer_serialize];
+    off_file[@"RopLDR"]                         = [NSString stringWithFormat:@"0x%016llx", offsets->rop_ldr_x0_x0_0x10];
+    
+    off_file[@"RootVnode"]                      = [NSString stringWithFormat:@"0x%016llx", offsets->root_vnode];
+    
+    off_file[@"VfsContextCurrent"]              = [NSString stringWithFormat:@"0x%016llx", offsets->vfs_context_current];
+    off_file[@"VnodeGetFromFD"]                 = [NSString stringWithFormat:@"0x%016llx", offsets->vnode_getfromfd];
+    off_file[@"VnodeGetAttr"]                   = [NSString stringWithFormat:@"0x%016llx", offsets->vnode_getattr];
+    off_file[@"CSBlobEntDictSet"]               = [NSString stringWithFormat:@"0x%016llx", offsets->csblob_ent_dict_set];
+    off_file[@"SHA1Init"]                       = [NSString stringWithFormat:@"0x%016llx", offsets->sha1_init];
+    off_file[@"SHA1Update"]                     = [NSString stringWithFormat:@"0x%016llx", offsets->sha1_update];
+    off_file[@"SHA1Final"]                      = [NSString stringWithFormat:@"0x%016llx", offsets->sha1_final];
+    
+    [off_file writeToFile:@"/meridian/offsets.plist" atomically:YES];
+}

+ 17 - 0
Meridian/Meridian/patchfinders/offsetfinder.h

@@ -0,0 +1,17 @@
+//
+//  offsetfinder.h
+//  Meridian
+//
+//  Created by Ben Sparkes on 08/03/2018.
+//  Copyright © 2018 Ben Sparkes. All rights reserved.
+//
+
+#ifndef offsetfinder_h
+#define offsetfinder_h
+
+#ifdef __cplusplus
+extern "C"
+#endif
+offsets_t *get_offsets(void);
+
+#endif /* offsetfinder_h */

+ 88 - 0
Meridian/Meridian/patchfinders/offsetfinder.mm

@@ -0,0 +1,88 @@
+//
+//  offsetfinder.mm
+//  Meridian
+//
+//  Created by Ben Sparkes on 08/03/2018.
+//  Copyright © 2018 Ben Sparkes. All rights reserved.
+//
+
+#include "v0rtex.h"
+#include "liboffsetfinder64.hpp"
+#include "ViewController.h"
+#import <Foundation/Foundation.h>
+
+static bool DidInit = false;
+static offsets_t off;
+
+extern "C" offsets_t *get_offsets() {
+    if (DidInit) {
+        return &off;
+    }
+
+    try {
+        NSLog(@"[OFFSET] initializing offsetfinder...");
+        tihmstar::offsetfinder64 fi("/System/Library/Caches/com.apple.kernelcaches/kernelcache");
+        NSLog(@"[OFFSET] initialized offsetfinder");
+
+        off.base = 0xfffffff007004000;
+
+        NSLog(@"[OFFSET] begginning offset finding...");
+        off.sizeof_task                         = (kptr_t)fi.find_sizeof_task();
+        off.task_itk_self                       = (kptr_t)fi.find_task_itk_self();
+        off.task_itk_registered                 = (kptr_t)fi.find_task_itk_registered();
+        off.task_bsd_info                       = (kptr_t)fi.find_task_bsd_info();
+        off.proc_ucred                          = (kptr_t)fi.find_proc_ucred();
+        off.vm_map_hdr                          = (kptr_t)fi.find_vm_map_hdr();
+        off.ipc_space_is_task                   = (kptr_t)fi.find_ipc_space_is_task();
+        off.realhost_special                    = 0x10;
+        off.iouserclient_ipc                    = (kptr_t)fi.find_iouserclient_ipc();
+        off.vtab_get_retain_count               = (kptr_t)fi.find_vtab_get_retain_count();
+        off.vtab_get_external_trap_for_index    = (kptr_t)fi.find_vtab_get_external_trap_for_index();
+        NSLog(@"[OFFSET] grabbed struct offsets");
+
+        off.zone_map                            = (kptr_t)fi.find_zone_map();
+        off.kernel_map                          = (kptr_t)fi.find_kernel_map();
+        off.kernel_task                         = (kptr_t)fi.find_kernel_task();
+        off.realhost                            = (kptr_t)fi.find_realhost();
+        NSLog(@"[OFFSET] grabbed map offsets");
+        
+        off.copyin                              = (kptr_t)fi.find_copyin();
+        off.copyout                             = (kptr_t)fi.find_copyout();
+        off.chgproccnt                          = (kptr_t)fi.find_chgproccnt();
+        off.kauth_cred_ref                      = (kptr_t)fi.find_kauth_cred_ref();
+        off.ipc_port_alloc_special              = (kptr_t)fi.find_ipc_port_alloc_special();
+        off.ipc_kobject_set                     = (kptr_t)fi.find_ipc_kobject_set();
+        off.ipc_port_make_send                  = (kptr_t)fi.find_ipc_port_make_send();
+        off.osserializer_serialize              = (kptr_t)fi.find_osserializer_serialize();
+        off.rop_ldr_x0_x0_0x10                  = (kptr_t)fi.find_rop_ldr_x0_x0_0x10();
+        NSLog(@"[OFFSET] grabbed code offsets");
+        
+        off.root_vnode                          = (kptr_t)fi.find_rootvnode();
+        
+        off.vfs_context_current                 = (kptr_t)fi.find_sym("_vfs_context_current");
+        off.vnode_getfromfd                     = (kptr_t)fi.find_sym("_vnode_getfromfd");
+        off.vnode_getattr                       = (kptr_t)fi.find_sym("_vnode_getattr");
+        off.csblob_ent_dict_set                 = (kptr_t)fi.find_sym("_csblob_entitlements_dictionary_set");
+        off.sha1_init                           = (kptr_t)fi.find_sym("_SHA1Init");
+        off.sha1_update                         = (kptr_t)fi.find_sym("_SHA1Update");
+        off.sha1_final                          = (kptr_t)fi.find_sym("_SHA1Final");
+        NSLog(@"[OFFSET] grabbed amfi offsets");
+        
+        NSLog(@"[OFFSET] sizeof_task = 0x%llx", off.sizeof_task);
+        NSLog(@"[OFFSET] task_itk_self = 0x%llx", off.task_itk_self);
+        NSLog(@"[OFFSET] task_itk_registered = 0x%llx", off.task_itk_registered);
+        NSLog(@"[OFFSET] kernel_task = 0x%llx", off.kernel_task);
+        NSLog(@"[OFFSET] rootvnode = 0x%llx", off.root_vnode);
+        NSLog(@"[OFFSET] sha1_init = 0x%llx", off.sha1_init);
+    } catch (tihmstar::exception &e) {
+        NSLog(@"offsetfinder failure! %d (%s)", e.code(), e.what());
+        return NULL;
+    } catch (std::exception &e) {
+        NSLog(@"fatal offsetfinder failure! %s", e.what());
+        return NULL;
+    }
+        
+    DidInit = true;
+
+    return &off;
+}

File diff suppressed because it is too large
+ 1301 - 0
Meridian/Meridian/patchfinders/patchfinder64.c


+ 37 - 0
Meridian/Meridian/patchfinders/patchfinder64.h

@@ -0,0 +1,37 @@
+#ifndef PATCHFINDER64_H_
+#define PATCHFINDER64_H_
+
+#import "common.h"
+#import <mach/mach.h>
+
+int init_patchfinder(const char *filename);
+void term_kernel(void);
+
+enum { SearchInCore, SearchInPrelink };
+
+uint64_t find_register_value(uint64_t where, int reg);
+uint64_t find_reference(uint64_t to, int n, int prelink);
+uint64_t find_strref(const char *string, int n, int prelink);
+uint64_t find_gPhysBase(void);
+uint64_t find_kernel_pmap(void);
+uint64_t find_amfiret(void);
+uint64_t find_ret_0(void);
+uint64_t find_amfi_memcmpstub(void);
+uint64_t find_sbops(void);
+uint64_t find_lwvm_mapio_patch(void);
+uint64_t find_lwvm_mapio_newj(void);
+
+uint64_t find_entry(void);
+const unsigned char *find_mh(void);
+
+uint64_t find_cpacr_write(void);
+uint64_t find_str(const char *string);
+uint64_t find_amfiops(void);
+uint64_t find_sysbootnonce(void);
+uint64_t find_trustcache(void);
+uint64_t find_amficache(void);
+
+uint64_t find_allproc(void);
+uint64_t find_boot_args(unsigned* cmdline_offset);
+
+#endif

+ 12 - 0
Meridian/Meridian/root-rw.h

@@ -0,0 +1,12 @@
+//
+//  root-rw.h
+//  Meridian
+//
+//  Created by Ben Sparkes on 16/12/2017.
+//  Copyright © 2017 Ben Sparkes. All rights reserved.
+//
+
+#include <mach/mach.h>
+#include <sys/mount.h>
+
+int mount_root(uint64_t kslide, uint64_t root_vnode, int pre130);

+ 166 - 0
Meridian/Meridian/root-rw.m

@@ -0,0 +1,166 @@
+//
+//  root-rw.m
+//  Meridian
+//
+//  Created by Ben Sparkes on 16/12/2017.
+//  Copyright © 2017 Ben Sparkes. All rights reserved.
+//
+
+#include "root-rw.h"
+#include "kernel.h"
+#include "patchfinder64.h"
+#include "helpers.h"
+#include "ViewController.h"
+#include "offsetfinder.h"
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/utsname.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+
+#define MOUNT_MNT_FLAG    0x70
+#define VNODE_V_UN        0xd8
+#define VNODE_V_UN_OTHER  0xd0
+
+const unsigned OFF_LWVM__PARTITIONS = 0x1a0;
+const unsigned OFF_LWVMPART__ISWP = 0x28;
+const unsigned OFF_PROC__TASK = 0x18;
+const unsigned OFF_IPC_PORT__IP_KOBJECT = 0x68;
+const unsigned OFF_IPC_SPACE__IS_TABLE = 0x20;
+const unsigned SIZ_IPC_ENTRY_T = 0x18;
+const unsigned OFF_TASK__ITK_SPACE = 0x300;
+
+#define rkbuffer(w, p, s) kread(w, p, s);
+#define wkbuffer(w, p, s) kwrite(w, p, s);
+
+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 *connect);
+
+bool fix_root_iswriteprotected() {
+    io_service_t service = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("LightweightVolumeManager"));
+    if (!MACH_PORT_VALID(service)) {
+        return false;
+    }
+    
+    uint64_t inkernel = find_port_address(service);
+
+    uint64_t lwvm_kaddr = rk64(inkernel + OFF_IPC_PORT__IP_KOBJECT);
+    uint64_t rootp_kaddr = rk64(lwvm_kaddr + OFF_LWVM__PARTITIONS);
+    uint64_t varp_kaddr = rk64(lwvm_kaddr + OFF_LWVM__PARTITIONS + sizeof(void*));
+
+    uint64_t rootp_iswp_addr = rootp_kaddr + OFF_LWVMPART__ISWP;
+    uint64_t varp_iswp_addr = varp_kaddr + OFF_LWVMPART__ISWP;
+    
+    // Check we found the right values
+    uint64_t varp_iswp = rk64(varp_iswp_addr);
+    if (varp_iswp != 0) {
+        NSLog(@"rk64(varp_iswp_addr) != 0! val: %llx", varp_iswp);
+        return false;
+    }
+    
+    uint64_t rootp_iswp = rk64(rootp_iswp_addr);
+    if (rootp_iswp != 1) {
+        NSLog(@"rk64(rootp_iswp_addr) != 1! val: %llx", rootp_iswp);
+        return false;
+    }
+    
+    wk64(rootp_iswp_addr, 0);
+    return true;
+}
+
+#define BOOTARGS_PATCH "rd=mdx"
+bool fake_rootedramdisk(void) {
+    unsigned cmdline_offset;
+    uint64_t pestate_bootargs = find_boot_args(&cmdline_offset);
+
+    if (pestate_bootargs == 0) {
+        return false;
+    }
+
+    uint64_t struct_boot_args = rk64(pestate_bootargs);
+    uint64_t boot_args_cmdline = struct_boot_args + cmdline_offset;
+
+    // max size is 256 on arm
+    char buf_bootargs[256];
+
+    rkbuffer(boot_args_cmdline, buf_bootargs, sizeof(buf_bootargs));
+    strcat(buf_bootargs, BOOTARGS_PATCH);
+    wkbuffer(boot_args_cmdline, buf_bootargs, sizeof(buf_bootargs));
+
+    bzero(buf_bootargs, sizeof(buf_bootargs));
+    size_t size = sizeof(buf_bootargs);
+    int err = sysctlbyname("kern.bootargs", buf_bootargs, &size, NULL, 0);
+
+    if (err) {
+        NSLog(@"sysctlbyname(kern.bootargs) failed");
+        return false;
+    }
+
+    if (strstr(buf_bootargs, BOOTARGS_PATCH) == NULL) {
+        NSLog(@"kern.bootargs doesn't contain '%s' after patch!", BOOTARGS_PATCH);
+        NSLog(@"kern.bootargs: '%s'", buf_bootargs);
+        return false;
+    }
+
+    return true;
+}
+
+// props to xerub for the original '/' r/w remount code
+int remount_root(uint64_t kslide, uint64_t root_vnode) {
+    uint64_t _rootnode = root_vnode + kslide;
+    
+    NSLog(@"_rootnode = %llx", _rootnode);
+    
+    uint64_t rootfs_vnode = rk64(_rootnode);
+    
+    NSLog(@"roofs_vnode = %llx", rootfs_vnode);
+    
+    uint64_t off = VNODE_V_UN;
+    struct utsname uts;
+    uname(&uts);
+    if (strstr(uts.version, "16.0.0")) {
+        off = VNODE_V_UN_OTHER;
+    }
+    
+    // read the original flags
+    uint64_t v_mount = rk64(rootfs_vnode + off);
+    uint32_t v_flag = rk32(v_mount + MOUNT_MNT_FLAG);
+    
+    v_flag = v_flag & ~MNT_NOSUID;
+    v_flag = v_flag & ~MNT_RDONLY;
+    
+    // unset rootfs flag
+    wk32(v_mount + MOUNT_MNT_FLAG, v_flag & ~MNT_ROOTFS);
+    
+    // remount
+    char *nmz = strdup("/dev/disk0s1s1");
+    kern_return_t rv = mount("hfs", "/", MNT_UPDATE, (void *)&nmz);
+    NSLog(@"remounting: %d", rv);
+    
+    // set original flags back
+    v_mount = rk64(rootfs_vnode + off);
+    wk32(v_mount + MOUNT_MNT_FLAG, v_flag);
+    
+    return rv;
+}
+
+int mount_root(uint64_t kslide, uint64_t root_vnode, int pre130) {
+    if (pre130 == 1) {
+        // further patches are requried on <10.3
+        NSLog(@"pre-10.3 detected: patching lwvm...");
+        if (!fix_root_iswriteprotected()) {
+            NSLog(@"fix_root_iswriteprotected failed!");
+            return -61;
+        }
+        if (!fake_rootedramdisk()) {
+            NSLog(@"fake_rootedramdisk failed!");
+            return -62;
+        }
+    }
+    
+    return remount_root(kslide, root_vnode);
+}

+ 50 - 0
Meridian/Meridian/v0rtex.h

@@ -0,0 +1,50 @@
+#import "common.h"
+#include <mach/mach.h>
+#include <stdint.h>
+
+typedef struct
+{
+    const char *version;
+    kptr_t base;
+    // Structure offsets
+    kptr_t sizeof_task;
+    kptr_t task_itk_self;
+    kptr_t task_itk_registered;
+    kptr_t task_bsd_info;
+    kptr_t proc_ucred;
+    kptr_t vm_map_hdr;
+    kptr_t ipc_space_is_task;
+    kptr_t realhost_special;
+    kptr_t iouserclient_ipc;
+    kptr_t vtab_get_retain_count;
+    kptr_t vtab_get_external_trap_for_index;
+    // Data
+    kptr_t zone_map;
+    kptr_t kernel_map;
+    kptr_t kernel_task;
+    kptr_t realhost;
+    // Code
+    kptr_t copyin;
+    kptr_t copyout;
+    kptr_t chgproccnt;
+    kptr_t kauth_cred_ref;
+    kptr_t ipc_port_alloc_special;
+    kptr_t ipc_kobject_set;
+    kptr_t ipc_port_make_send;
+    kptr_t osserializer_serialize;
+    kptr_t rop_ldr_x0_x0_0x10;
+    // Remount 
+    kptr_t root_vnode;
+    // AMFID stuff
+    kptr_t vfs_context_current;
+    kptr_t vnode_getfromfd;
+    kptr_t vnode_getattr;
+    kptr_t csblob_ent_dict_set;
+    kptr_t sha1_init;
+    kptr_t sha1_update;
+    kptr_t sha1_final;
+} offsets_t;
+
+typedef kern_return_t (*v0rtex_cb_t)(task_t tfp0, kptr_t kbase, void *cb_data);
+
+kern_return_t v0rtex(offsets_t *off, v0rtex_cb_t callback, void *cb_data);

File diff suppressed because it is too large
+ 1765 - 0
Meridian/Meridian/v0rtex.m


+ 17 - 0
Meridian/Meridian/views/AppDelegate.h

@@ -0,0 +1,17 @@
+//
+//  AppDelegate.h
+//  Meridian
+//
+//  Created by Ben Sparkes on 22/12/2017.
+//  Copyright © 2017 Ben Sparkes. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@interface AppDelegate : UIResponder <UIApplicationDelegate>
+
+@property (strong, nonatomic) UIWindow *window;
+
+
+@end
+

+ 51 - 0
Meridian/Meridian/views/AppDelegate.m

@@ -0,0 +1,51 @@
+//
+//  AppDelegate.m
+//  Meridian
+//
+//  Created by Ben Sparkes on 22/12/2017.
+//  Copyright © 2017 Ben Sparkes. All rights reserved.
+//
+
+#import "AppDelegate.h"
+
+@interface AppDelegate ()
+
+@end
+
+@implementation AppDelegate
+
+
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+    // Override point for customization after application launch.
+    return YES;
+}
+
+
+- (void)applicationWillResignActive:(UIApplication *)application {
+    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
+    // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
+}
+
+
+- (void)applicationDidEnterBackground:(UIApplication *)application {
+    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
+    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
+}
+
+
+- (void)applicationWillEnterForeground:(UIApplication *)application {
+    // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
+}
+
+
+- (void)applicationDidBecomeActive:(UIApplication *)application {
+    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
+}
+
+
+- (void)applicationWillTerminate:(UIApplication *)application {
+    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
+}
+
+
+@end

+ 158 - 0
Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Contents.json

@@ -0,0 +1,158 @@
+{
+  "images" : [
+    {
+      "size" : "20x20",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-20x20@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "20x20",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-20x20@3x.png",
+      "scale" : "3x"
+    },
+    {
+      "size" : "29x29",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-29x29@1x.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "29x29",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-29x29@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "29x29",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-29x29@3x.png",
+      "scale" : "3x"
+    },
+    {
+      "size" : "40x40",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-40x40@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "40x40",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-40x40@3x.png",
+      "scale" : "3x"
+    },
+    {
+      "size" : "57x57",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-57x57@1x.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "57x57",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-57x57@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "60x60",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-60x60@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "60x60",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-60x60@3x.png",
+      "scale" : "3x"
+    },
+    {
+      "size" : "20x20",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-20x20@1x.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "20x20",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-20x20@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "29x29",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-29x29@1x.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "29x29",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-29x29@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "40x40",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-40x40@1x.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "40x40",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-40x40@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "50x50",
+      "idiom" : "ipad",
+      "filename" : "Icon-Small-50x50@1x.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "50x50",
+      "idiom" : "ipad",
+      "filename" : "Icon-Small-50x50@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "72x72",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-72x72@1x.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "72x72",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-72x72@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "76x76",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-76x76@1x.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "76x76",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-76x76@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "83.5x83.5",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-83.5x83.5@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "1024x1024",
+      "idiom" : "ios-marketing",
+      "filename" : "ItunesArtwork@2x.png",
+      "scale" : "1x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}

BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-App-20x20@1x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-App-20x20@2x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-App-20x20@3x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-App-29x29@1x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-App-29x29@2x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-App-29x29@3x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-App-40x40@1x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-App-40x40@2x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-App-40x40@3x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-App-57x57@1x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-App-57x57@2x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-App-60x60@2x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-App-60x60@3x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-App-72x72@1x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-App-72x72@2x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-App-76x76@1x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-App-76x76@2x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-App-83.5x83.5@2x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-Small-50x50@1x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Icon-Small-50x50@2x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/ItunesArtwork@2x.png


+ 158 - 0
Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Contents.json

@@ -0,0 +1,158 @@
+{
+  "images" : [
+    {
+      "size" : "20x20",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-20x20@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "20x20",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-20x20@3x.png",
+      "scale" : "3x"
+    },
+    {
+      "size" : "29x29",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-29x29@1x.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "29x29",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-29x29@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "29x29",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-29x29@3x.png",
+      "scale" : "3x"
+    },
+    {
+      "size" : "40x40",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-40x40@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "40x40",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-40x40@3x.png",
+      "scale" : "3x"
+    },
+    {
+      "size" : "57x57",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-57x57@1x.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "57x57",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-57x57@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "60x60",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-60x60@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "60x60",
+      "idiom" : "iphone",
+      "filename" : "Icon-App-60x60@3x.png",
+      "scale" : "3x"
+    },
+    {
+      "size" : "20x20",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-20x20@1x.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "20x20",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-20x20@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "29x29",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-29x29@1x.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "29x29",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-29x29@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "40x40",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-40x40@1x.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "40x40",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-40x40@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "50x50",
+      "idiom" : "ipad",
+      "filename" : "Icon-Small-50x50@1x.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "50x50",
+      "idiom" : "ipad",
+      "filename" : "Icon-Small-50x50@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "72x72",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-72x72@1x.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "72x72",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-72x72@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "76x76",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-76x76@1x.png",
+      "scale" : "1x"
+    },
+    {
+      "size" : "76x76",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-76x76@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "83.5x83.5",
+      "idiom" : "ipad",
+      "filename" : "Icon-App-83.5x83.5@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "size" : "1024x1024",
+      "idiom" : "ios-marketing",
+      "filename" : "ItunesArtwork@2x.png",
+      "scale" : "1x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}

BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-App-20x20@1x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-App-20x20@2x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-App-20x20@3x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-App-29x29@1x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-App-29x29@2x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-App-29x29@3x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-App-40x40@1x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-App-40x40@2x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-App-40x40@3x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-App-57x57@1x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-App-57x57@2x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-App-60x60@2x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-App-60x60@3x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-App-72x72@1x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-App-72x72@2x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-App-76x76@1x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-App-76x76@2x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-App-83.5x83.5@2x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-Small-50x50@1x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Icon-Small-50x50@2x.png


BIN
Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/ItunesArtwork@2x.png


+ 6 - 0
Meridian/Meridian/views/Assets.xcassets/Contents.json

@@ -0,0 +1,6 @@
+{
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}

+ 6 - 0
Meridian/Meridian/views/Assets.xcassets/Icons/Contents.json

@@ -0,0 +1,6 @@
+{
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}

+ 0 - 0
Meridian/Meridian/views/Assets.xcassets/Icons/archive.imageset/Contents.json


Some files were not shown because too many files changed in this diff