The QiLin (麒麟) Jailbreak Toolkit

What this is

With the rise of open source PoC exploits like Ian Beer's past exploits in 10.1.1, 10.2 and 11.1.2, it's become a simple enough matter to built on existing code obtaining kernel memory read/write access (via the SEND right to the kernel_task port, commonly referred to as "TFP0"). But the kernel_task alone does not a jailbreak make*.

Many developers have already built on Beer's code, adding in elements from various PatchFinders like @Xerub's and others - but the resulting code is often messy, prone to fragmentation, relies on multiple magic values and is insufficiently commented, and - most importantly - unmaintainable in the face of shifting symbol offsets and structure offsets. Further, not all code provided in such jailbreaks passes the tests of stability, as kernel memory overwriting needs to be done with extreme care, so as to avoid locks, data aborts, and other potential causes of panics.

The QiLin Jailbreak ToolKit is a simple code base, which standardizes all the common tasks required for jailbreaking - from breaking out of the sandbox and assuming root capabilities, remounting the root file system, unpacking binaries, and more - in a way that is stable, safe, and reduces the amount of code to about 10 lines. It is aimed at researchers and jailbreak enthusiasts, who wish to learn more about the intricacies of kernel tinkering without being bogged down by the nooks and crannies of setting up a stable work environment.

In other words, the toolkit handles the complicated tasks, and you can build whatever UI/customization/tweaking/modding of the jailbreak you want.

By the way, for those people wondering what a QiLin (in Japanese and western languages, "Kirin") is - it's an auspicious, unique, highly magical and extremely powerful creature which takes the form of a cross between a dragon and lion (and sometimes, horse). Wikipedia gets the gist of it, though I'm more a fan of the AD&D interpretation.


All you have to do in order to build on QiLin is to call: int initQiLin (mach_port_t TFP0, uint64_t KernelBase); with the kernel send right (TFP0) and the kernelbase (i.e address of kernel Mach-O + slide). And now you don't even have to do that anymore since QiLin can figure out the slide with just your own task address (which exploits use anyway). The rest is provided by numerous functions - Let the .h file speak for itself:

//  QiLin
//  Created by JL on 12/7/17.
//  Copyright © 2017 NewOSXBook. All rights reserved.

// Revision 3: Added spawnAndPlatformize(),
//             moved to posix_spawn() implementation for exec() family
//             actually exported the set*Reporter functions (formerly ErrorHandler.. etc -
//             "Reporter" is more accurate, because they allow you to propagate messages to
//             a GUI.
// Revision 4: Added kexec (executeInKernel)
// Revision 5: KMR/KMW (Kernel memory read/write) functions weren't exported! Oops!
// Revision 6: RootFS mount, fixed bug in getting symbols (no longer needs setKernelSymbol)
//             and added respring
//             and also added uint64_t        getVnodeByPathName (char *Path) ;
//             and also can now init from task address alone.
// (Almost) free to use (ABSE) per the license in
//          Remember to give credit where due and please tweet with #QiLin
//          so others can quickly find your JailBreak or other project.

#ifndef qilin_h
#define qilin_h
#include <mach/mach.h>
#include <unistd.h>
#include <stdlib.h>

char *getMachine (void);
char *getOSVer(void);

typedef int  (*KMRFunc)(uint64_t Address, uint64_t Len, void **To);
typedef int  (*KMWFunc)(uint64_t Address, uint64_t Len, void *From);
void setKernelMemoryReadFunction(KMRFunc F);
void setKernelMemoryWriteFunction(KMWFunc F);

// MUST call this first

int initQiLin (mach_port_t TFP0, uint64_t KernelBase);
// OR this
int initQiLinWithKMRW(uint64_t KernelBase, KMRFunc Kmr, KMWFunc Kmw);

// Use this from MPTCP or VFS, which do not provide kernel slide
int initQilinWithTFP0AndMyTaskPortAddr(mach_port_t TFP0, uint64_t MyTaskPortAddr);

// v2.0 beta (07/16/2019) - for use with voucher_swap and/or sockpuppet:
// You get the kernel task from the exploit, make it global and export it - QiLin will do the 
// rest to figure out kernproc, etc.

int initQiLinWithTFP0AndKernelTaskPortAddr(mach_port_t TFP0, uint64_t KernelTaskAddr);

// v2.0 beta (07/16/2019) - Network server!
// Default server will listen on Port and accepts 'help', 'get' (pull files),
// 'read'/'write' (kernel memory), 'image' (read block off of /dev/disk0)
// and possibly other stuff I forgot to mention here
pthread_t startQiLinServer (unsigned short Port); // start default server
pthread_t startNetworkServer (unsigned short Port, void *threadFunc); // Start your own server

// System wide effects
int remountRootFS (void);   // Yes, WITH SparkZheng's fix - for the astute :-)
int reSpring (void);        // @FCE365 - this is for you
pid_t execCommand(char *Cmd, char *Arg1, char *Arg2, char *Arg3, char *Arg4, char *Arg5 , int Flags);
int execCommandAndWait(char *Cmd, char *Arg1, char *Arg2, char *Arg3, char *Arg4, char *Arg5);

int setTFP0AsHostSpecialPort4 (void);

// 1/17/18 - This is super useful
int spawnAndPlatformize (char *AmfidebPath, char *Arg1, char *Arg2, char *Arg3 , char *Arg4, char *Arg5);

int moveFileFromAppDir (char *File, char *Dest);
int disableAutoUpdates(void);

// Code signing

// Will set AMFId's exception ports and thereby disable code signing
int castrateAmfid (void);

// Utility function - you probably won't need this directly.
#define ALGORITHM_SHA256    2
#define ALGORITHM_SHA1      1
char *cdHashOfFile(char *fileName,int Algorithm); // Calculate CDHash of a given Mach-O (for messing with AMFI)

// Kernel Memory access (wrappers over kernel_task send right)
uint64_t findKernelSymbol (char *Symbol);
void setKernelSymbol (char *Symbol, uint64_t Address);

int readKernelMemory(uint64_t Address, uint64_t Len, void **To);
int writeKernelMemory(uint64_t Address, uint64_t Len, void *From);

// 06/15/2018 -------
// Will return the address of the kernel vnode representing Path.
uint64_t        getVnodeByPathName (char *Path) ;    // Make sure you updated to latest .o for this!
uint64_t getRootVnodeAddr(void); // Convenience, for rootvnode ("/") instead of _rootvnode sym deref


// Not recommended, but doable: Bestow task port of Pid in TargetPid
mach_port_t task_for_pid_in_kernel (pid_t Pid, pid_t TargetPid);

// Process manipulation functions

// Finds the address of struct proc for this pid_t in kernel memory.
uint64_t getProcStructForPid(pid_t);

// Finds the pid of a process given its (base) name. Note this will only
// work on processes you are the owner of (or all, if root) - this is intentional
pid_t findPidOfProcess (char *ProcName) ;

int setCSFlagsForProcAtAddr(uint64_t ProcStructAddr, int Flags, int Set);
int setCSFlagsForPid (pid_t Whom);
int platformizePid(pid_t Whom);
int rootifyPid(pid_t Whom);
int ShaiHuludPid (pid_t Whom, uint64_t CredAddr); // leave 0 for root creds.
int unShaiHuludPid (pid_t Whom);

uint64_t borrowEntitlementsFromDonor(char *UnwittingDonor, char *Arg);
// By request :-)
uint64_t borrowEntitlementsFromPid(pid_t    Pid);

// Presently, limited to two entitlements, and assumed boolean (true)
int entitlePidWithKernelEnts (pid_t Whom, char *Ent1, char *Ent2);

// Convenience functions - do all the above , but on my process

int platformizeMe (void);
int rootifyMe(void);

// Escape sandbox:
// call with 0 to assume kernel cred, else specify value. Will return origCreds
uint64_t ShaiHuludMe(uint64_t OtherCredsOr0ForKernelCreds);
void unShaiHuludMe(uint64_t OrigCreds);
int entitleMe(char *entitlementString);

uint64_t getKernelCredAddr (void);

/// Launchd handling utilities - just for you @launchderp :-)
int makeLaunchdPlist (char *PlistName, char *Program, char *ProgramArguments, char *StandardOutputPath, char *StandardErrorPath, int RunAtLoad);
int launjctlLaunchdPlist(char *Name);

// I use these internally, not sure anyone else would need them
int launjctlPrintSystem (void);
int launjctlDumpState(void);

// This one is still in progress. Don't use it please.
int movePortToPid(mach_port_t PortMoved, pid_t Pid, mach_port_name_t Name);
int spawnJailbreakServer (char *Name, mach_port_t TFP0, mach_port_name_t NameInTarget);

// UI Support:
// Provide status, error and debug print outs to user,
// which may be redirected to GUI views, etc.
// Default implmenentations are NSLog.

typedef void (status_func) (char *,...);
void setStatusReporter (status_func *Func);
void setErrorReporter (status_func *Func);
void setDebugReporter (status_func *Func);

// Utility functions you probably won't need unless you want to do your own debugging
void hexDump(void *Mem, int Len, uint64_t Addr);
void dumpARMThreadState64(_STRUCT_ARM_THREAD_STATE64 *old_state);

// Even more Internal/advanced use:
uint64_t findKernelTask (void);
uint64_t findMyProcStructInKernelMemory(void);  // For other advanced uses I haven't provided already

#endif /* qilin_h */

The code

I'm working on stabilizing a few things and bullet-proofing them, and as soon as I do QiLin will be fully open source and NOT BE OPEN SOURCE because of nasty folk (see LICENSE below) but will forever be FREE, and - for the time being - maintained by me . In the meanwhile, Here's the object file you can drop into your project to start using it! And the above .h file is here as well

  • For iOS 12, you need this file, since structures have changed. THIS FILE IS RELATIVELY STABLE AT THE MOMENT (July 15th, 2019). I'm working on making a universal 10-12 one. See forum post for details.
  • You no longer need sha1.o and/or sha256.o.
  • The writeup Here.


    Look no further than LiberTV:

            int rc = 0;
       rc = rootifyMe();       // rootifyPID(getpid());     /* setuid (0); */
        if (rc) {return rc;}
         // Don't need this anymore
            rc = entitleMe("\tplatform-application\n"
                           "\ttask_for_pid-allow\n\t\n"                        // Old habits die hard.
                           "\\n\t\n"               // fix processor_set_tasks on me, why don'tcha?
                           "\\n\t"); // I Own you @launchderp :-)
       myOriginalCredAddr =     ShaiHuludMe(0);     //   /* Escape Sandbox */
       FILE * f = fopen("/var/mobile/foo", "w");
        if (!f) {fprintf(stderr,"Still sandboxed\n");}
        else {NSLog(CFSTR("Freeeeeeee\n"));}
        platformizeMe();   // platformizePID(getpid()); /* blob->csb_flags | CS_PLATFORM_BINARY  and the secret sauce */
     int sdPID = execCommand("/usr/bin/sysdiagnose", "-u", NULL, NULL,NULL,NULL);
                uint64_t sdProcStruct = getProcStructForPid(sdPID);
                struct proc *sdProc;
                readKernelMemory(sdProcStruct,sizeof(struct proc),&sdProc);
                printf("SYSDIAGNOSE (PID %d) PROC STRUCT IS AT %llx. CREDS (0x%llx) are 0x%llx\n", sdProc->p_pid,
                      sdProcStruct + offsetof(struct proc, p_ucred),
                sdCredAddr =  sdProc->p_ucred;
                free (sdProc);
                if (sdCredAddr)
                    printf("got cred addr %llx\n", sdCredAddr);
                   rc = kill (sdPID, SIGSTOP);
                    rc = kill (sdPID, SIGSTOP);sleep(1);rc = kill (sdPID, SIGSTOP);
                    printf("RC ON KILL of PID %d - %d\n", sdPID, rc);
            // We now have Task_for_pid.
        unpackBinariesToPath("binpack64-256.tar", "/jb", "tar");
    // Do updates
       mkdir ("/etc/dropbear",0755);
        rc = execCommand("/jb/usr/local/bin/dropbear", "-R", "--shell" ,"/jb/bin/bash", NULL,NULL);
      // If you want to spawn amfidebilitate:
      status ("***** Launching amfidebilitate******\n");
             pid_t amfidebPid =execCommand("/jb/amfidebilitate", "", NULL, NULL,NULL,NULL);
            printf("AMFIDEB PID: %d\n", amfidebPid);
            uint64_t amfideb_proc =  getProcStructForPid(amfidebPid);
            if (!amfideb_proc) {
                fprintf(stderr, "can't find amfideb\n");
                return 1;
            printf("amfideb is now 0x%llx - platformizing\n", amfideb_proc);
            // Now do platformize
            platformizeProcAtAddr(amfideb_proc);        ShaiHuludProcessAtAddr(amfideb_proc,sdCredAddr);

    Code samples

    Both are essentially identical, replacing just one line of code. Seriously, it's that easy:

    To compile: Drop qilin.o and sha256.o in same directory. Then gcc -arch arm64 ...c qilin.o sha256.o -o .... and that's it (assuming you fix the broken iOS headers by copying them over from MacOS first!).



    Johnny's (semi) open source license, v0.4
    This is (well, will be, at the time of writing) open source, and I can't but appeal to your sense of decency. 
    You might try compile this and try to pass it as your own. Heck, you might even try to run it through llvm-obfuscator. 
    But that would be stealing code. And obfuscate as you will, you can't obfuscate enough to hide the methods. 
    So, primum non nocere. Do no harm, and do not steal.
    To be fully clear:
     - Yes, you may use this source or code library as you see fit, PROVIDED THAT:
    	- IT IS NOT USED COMMERCIALLY IN ANY WAY. For this, I ask that you contact my company, @Technologeeks, 
    		and ask for proper licensing - they'll also provide official support.
    		(NSO/Hackin9/Finfisher/Equus/etc - that means you)
    	- WHEN YOU DO USE IT, I ASK THAT YOU MENTION THAT YOUR TOOL IS "powered by the QiLin Toolkit", 
              or otherwise provide a user facing indication that it is using this code. 
    	   I'd appreciate it if you tweeted with #QiLin, too.
    	- If you spread lies about other people, propaganda or false claims, while using this toolkit, 
    then you must renounce your ways, and apologize. Then you can use it freely. - There are no limitation on nationality, specific people exclusions (i.e. this is AISE, subject to last condition, above ;-), or any other race, color or creed - provided the above are met. - QiLin comes with NO LIABILITY WHATSOEVER. YOU USE THIS AT YOUR OWN RISK.
    - Remember I'm doing this AS A FAVOR. I AM NO IN WAY INDEBTED OR COMMITTED TO SUPPORT THIS, OR ANY OTHER OF MY TOOLS. You don't have to thank for this (you're welcome) but please don't slander me either. - Should you wish to contribute/donate, you may do so in one of the following ways: - Monetary: Pick a charity. Any charity. Of your choice. Pay them however money you want. Optionally, tweet/fb/insta/snap-whatever a screen capture stating "#QiLin". - Development: Through - you are welcome to ask (proper technical, not lame wen eta) questions and engage in discussions First, do no harm. Next, have fun :-) Changelog: - v0.1 Was AISE but SE is being more of an ass than usual and slandering fake claims directly attacking me.
    So this was updated with new condition excluding him until he grows up and behaves like the decent,
    talented researcher he can be. - v0.3 adds request to tweet #QiLin. - v0.4 states what should be obvious - NO LIABILITY WHATSOEVER

    * - No, it's not a typo. It's idiomatic use.