Ben Gottlieb
Ben Gottlieb

Reputation: 85542

How do I detect that an iOS app is running on a jailbroken phone?

If I want my app to behave differently on a jailbroken iPhone, how would I go about determining this?

Upvotes: 184

Views: 100037

Answers (17)

Nike Kov
Nike Kov

Reputation: 13744

Apple don't provide a legal way to check for jailbreak at this moment.

You can try custom tools to do this, like JailbreakDetectionService . And may read a modern way to check for jailbreak.

Also maybe you get an info about validating device via Firebase or Apple

In my experience, the lib IOSSecuritySuite provides falsepositive checks for jailbreak, but it also can help you.

Common jailbreak file paths:

let jailbreakFilePaths = [
        "/usr/sbin/frida-server",
        "/etc/apt/sources.list.d/electra.list",
        "/etc/apt/sources.list.d/sileo.sources",
        "/.bootstrapped_electra",
        "/usr/lib/libjailbreak.dylib",
        "/jb/lzma",
        "/.cydia_no_stash",
        "/.installed_unc0ver",
        "/jb/offsets.plist",
        "/usr/share/jailbreak/injectme.plist",
        "/etc/apt/undecimus/undecimus.list",
        "/var/lib/dpkg/info/mobilesubstrate.md5sums",
        "/Library/MobileSubstrate/MobileSubstrate.dylib",
        "/jb/jailbreakd.plist",
        "/jb/amfid_payload.dylib",
        "/jb/libjailbreak.dylib",
        "/usr/libexec/cydia/firmware.sh",
        "/var/lib/cydia",
        "/etc/apt",
        "/private/var/lib/apt",
        "/private/var/Users/",
        "/var/log/apt",
        "/private/var/stash",
        "/private/var/lib/cydia",
        "/private/var/cache/apt/",
        "/private/var/log/syslog",
        "/private/var/tmp/cydia.log",
        "/Applications/RockApp.app",
        "/Applications/blackra1n.app",
        "/Applications/Cydia.app",
        "/Applications/FakeCarrier.app",
        "/Applications/Icy.app",
        "/Applications/IntelliScreen.app",
        "/Applications/MxTube.app",
        "/Applications/SBSettings.app",
        "/Applications/WinterBoard.app",
        "/private/var/mobile/Library/SBSettings/Themes",
        "/Library/MobileSubstrate/CydiaSubstrate.dylib",
        "/System/Library/LaunchDaemons/com.ikey.bbot.plist",
        "/Library/MobileSubstrate/DynamicLibraries/Veency.plist",
        "/Library/MobileSubstrate/DynamicLibraries/LiveClock.plist",
        "/System/Library/LaunchDaemons/com.saurik.Cydia.Startup.plist"
    ]

Upvotes: 1

Paritosh Pawar
Paritosh Pawar

Reputation: 81

Please use following code for Swift 4 and above: Add the following code in the appdelegate:

private func getJailbrokenStatus() -> Bool {
    if TARGET_IPHONE_SIMULATOR != 1 {
        // Check 1 : existence of files that are common for jailbroken devices
        if FileManager.default.fileExists(atPath: "/Applications/Cydia.app")
            || FileManager.default.fileExists(atPath: "/Library/MobileSubstrate/MobileSubstrate.dylib")
            || FileManager.default.fileExists(atPath: "/bin/bash")
            || FileManager.default.fileExists(atPath: "/usr/sbin/sshd")
            || FileManager.default.fileExists(atPath: "/etc/apt")
            || FileManager.default.fileExists(atPath: "/private/var/lib/apt/")
            || UIApplication.shared.canOpenURL(URL(string:"cydia://package/com.example.package")!) {
            return true
        }
        // Check 2 : Reading and writing in system directories (sandbox violation)
        let stringToWrite = "Jailbreak Test"
        do {
            try stringToWrite.write(toFile:"/private/JailbreakTest.txt", atomically:true, encoding:String.Encoding.utf8)
            //Device is jailbroken
            return true
        } catch {
            return false
        }
    }
    else {
        return false
    }
}

Inside Appdelegate methods, write code as below

func applicationDidBecomeActive (_ application: UIApplication) {
    
    if getJailbrokenStatus() {
        let alert = UIAlertController(title: LocalizedKeys.Errors.jailbreakError, message: LocalizedKeys.Errors.jailbreakErrorMessage, preferredStyle: UIAlertController.Style.alert)
        let jailBrokenView = UIViewController()
        
        jailBrokenView.view.frame = UIScreen.main.bounds
        jailBrokenView.view.backgroundColor = .white
        self.window?.rootViewController = jailBrokenView
        jailBrokenView.present(alert, animated: true, completion: nil)
    }
    
    if #available(iOS 11.0, *) {
        if !UIScreen.main.isCaptured {
            DispatchQueue.main.async {
                self.blockImageView.removeFromSuperview()
            }
        }
    }
}

Upvotes: 5

Sanjeev Chavan
Sanjeev Chavan

Reputation: 441

Here's my solutions: Step 1

extension UIDevice {
    func isJailBroken() -> Bool {
        let cydiaPath = "/Applications/Cydia.app"
        let aptPath = "/private/var/lib/apt/"
        if FileManager.default.fileExists(atPath: cydiaPath) || FileManager.default.fileExists(atPath: aptPath) {
            return true
        }
        return false
    }
}

Step 2: Call it inside viewDidLoad() inside your launch screen view controller(or whatever VC you are calling for the first time):

       // show a blank screen or some other view controller
       let viewController = UIDevice.current.isJailBroken() ? JailBrokenViewController() : NextViewController()
       self.navigationController?.present(viewController, animated: true, completion:nil)

Upvotes: 0

Yossi
Yossi

Reputation: 2535

This is a code that combine some answers I found for this need, and will give you much higher success rate :

BOOL isJailbroken()
{
#if !(TARGET_IPHONE_SIMULATOR)

   if ([[NSFileManager defaultManager] fileExistsAtPath:@"/Applications/Cydia.app"] ||
       [[NSFileManager defaultManager] fileExistsAtPath:@"/Library/MobileSubstrate/MobileSubstrate.dylib"] ||
       [[NSFileManager defaultManager] fileExistsAtPath:@"/bin/bash"] ||
       [[NSFileManager defaultManager] fileExistsAtPath:@"/usr/sbin/sshd"] ||
       [[NSFileManager defaultManager] fileExistsAtPath:@"/etc/apt"] ||
       [[NSFileManager defaultManager] fileExistsAtPath:@"/private/var/lib/apt/"] ||
       [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"cydia://package/com.example.package"]])  {
         return YES;
   }

   FILE *f = NULL ;
   if ((f = fopen("/bin/bash", "r")) ||
      (f = fopen("/Applications/Cydia.app", "r")) ||
      (f = fopen("/Library/MobileSubstrate/MobileSubstrate.dylib", "r")) ||
      (f = fopen("/usr/sbin/sshd", "r")) ||
      (f = fopen("/etc/apt", "r")))  {
         fclose(f);
         return YES;
   }
   fclose(f);

   NSError *error;
   NSString *stringToBeWritten = @"This is a test.";
   [stringToBeWritten writeToFile:@"/private/jailbreak.txt" atomically:YES encoding:NSUTF8StringEncoding error:&error];
   [[NSFileManager defaultManager] removeItemAtPath:@"/private/jailbreak.txt" error:nil];
   if(error == nil)
   {
      return YES;
   }

#endif

   return NO;
}

Upvotes: 62

Alex Peda
Alex Peda

Reputation: 4290

I reworked in Swift 2.3 the solution provided by @Yossi

public static func jailbroken(application: UIApplication) -> Bool {
    guard let cydiaUrlScheme = NSURL(string: "cydia://package/com.example.package") else { return isJailbroken() }
    return application.canOpenURL(cydiaUrlScheme) || isJailbroken()
}


static func isJailbroken() -> Bool {

    if isSimulator {
        return false
    }

    let fileManager = NSFileManager.defaultManager()
    if fileManager.fileExistsAtPath("/Applications/Cydia.app") ||
        fileManager.fileExistsAtPath("/Library/MobileSubstrate/MobileSubstrate.dylib") ||
        fileManager.fileExistsAtPath("/bin/bash") ||
        fileManager.fileExistsAtPath("/usr/sbin/sshd") ||
        fileManager.fileExistsAtPath("/etc/apt") ||
        fileManager.fileExistsAtPath("/usr/bin/ssh") {
        return true
    }

    if canOpen("/Applications/Cydia.app") ||
        canOpen("/Library/MobileSubstrate/MobileSubstrate.dylib") ||
        canOpen("/bin/bash") ||
        canOpen("/usr/sbin/sshd") ||
        canOpen("/etc/apt") ||
        canOpen("/usr/bin/ssh") {
        return true
    }

    let path = "/private/" + NSUUID().UUIDString
    do {
        try "anyString".writeToFile(path, atomically: true, encoding: NSUTF8StringEncoding)
        try fileManager.removeItemAtPath(path)
        return true
    } catch {
        return false
    }
}

static func canOpen(path: String) -> Bool {
    let file = fopen(path, "r")
    guard file != nil else { return false }
    fclose(file)
    return true
}

Upvotes: 16

DevC
DevC

Reputation: 7022

Some common files to check for: /Library/MobileSubstrate/MobileSubstrate.dylib

/Applications/Cydia.app

/var/cache/apt

/var/lib/apt

/var/lib/cydia

/var/log/syslog

/var/tmp/cydia.log

/bin/bash

/bin/sh

/usr/sbin/sshd

/usr/libexec/ssh-keysign

/etc/ssh/sshd_config

/etc/apt

Most check for Cydia related files.

Upvotes: 4

Maxthon Chan
Maxthon Chan

Reputation: 1189

The most sophisticated method I know is using objc_copyImageNames() function. It returns a list of currently loaded libraries and since most people have MobileSubstrate on jailbroken devices and most iAP crack tools depend on it, at least some MobileSubstrate libraries will show up.

Upvotes: 6

user3088680
user3088680

Reputation: 311

You can detect if a device is JailBroken or not by checking for the following:

  • Cydia is installed
  • Verify some of the system paths
  • Perform a sandbox integrity check
  • Perform symlink verification
  • Verify whether you create and write files outside your Sandbox

There is an open source library I created from various articles and books. Try it out on GitHub!

Upvotes: 14

kurapix
kurapix

Reputation: 41

Try executing unsigned code through your application.

A jailbroken devices usually has the following characteristics:

  • run unsigned code
  • has Cydia installed
  • has jailbreak files
  • full r/w access to the whole filesystem
  • some system files will have been modified (content and so sha1 doesn't match with original files)
  • stuck to specific version (jailbreakable version)

Just checking file existence for jailbreak detection is doomed to fail. These checks are easy to bypass.

Upvotes: 4

karim
karim

Reputation: 15589

Try to find a file which cydia or jailbroken device create. Or try to write in a file outside the app's blackbox. If you succeed to do that, the device is compromised/jailbroken :)

- (BOOL)jailbroken
{
    NSFileManager * fileManager = [NSFileManager defaultManager];
    return [fileManager fileExistsAtPath:@"/private/var/lib/apt/"];
}

Upvotes: 3

Ilam
Ilam

Reputation: 87

What we did is, we already have an RSS feed to communicate with our users (Stocks Live), we put a news item that states something like this:

Some jailbroken devices have problems bla bla bla, we made a hack to solve those issues but we need to know if this a jailbroken device or not, press here so the app fixes the issue. If you ever return to normal, ie removed the jailbreak, press here.

Then you process the user interaction and do what is appropriate, like behaving different etc...

Upvotes: 3

Mark Johnson
Mark Johnson

Reputation: 1115

+(BOOL)isJailbroken {
    NSURL* url = [NSURL URLWithString:@"cydia://package/com.example.package"];
    return [[UIApplication sharedApplication] canOpenURL:url];
}

Checking the file path /Applications/Cydia.app is not allowed on a normal phone? I've never heard of Apple detecting this and rejecting an app for it, but Apple is unpredictable. Cydia has a URL scheme cydia:// which can be legally checked with UIApplication canOpenURL:

Upvotes: 54

Walt Sellers
Walt Sellers

Reputation: 3939

I am not aware of any "APIs" that exist for this. If there were, then a jailbreak-masking product would quickly cover them up.

As lots of people point out, it is a cat-and-mouse game. And after both players become expert, it all comes down to who gets the first move. (Person holding the device.)

I found many good suggestions for detecting jailbreak in Zdziarski's new book "Hacking and Securing iOS Apps". (Personally, I paid more for the O'Reilly eBook because they permit copy-and-paste.)

No, I am not affiliated with the publishers. But I did find it a good book. I don't like to just publish hackers' mistakes so they can fix them, so I thought I'd point to the book.

Upvotes: 4

Richard J. Ross III
Richard J. Ross III

Reputation: 55583

BOOL isJailbroken()
{
#if TARGET_IPHONE_SIMULATOR
    return NO;
#else
    FILE *f = fopen("/bin/bash", "r");

    if (errno == ENOENT)
    {
        // device is NOT jailbroken
        fclose(f);
        return NO;
    }
    else {
        // device IS jailbroken
        fclose(f);
        return YES;
    }
#endif
}

Upvotes: 20

GregH
GregH

Reputation: 782

Checking if the kernel is broken isn't THAT much more involved.

Jailbreaking makes the kernel's signature check of signed code always report that code is signed correctly, unbroken phones cannot run code with a bad signature.

So, include a separate executable in the app with a bad signature. It could just be a 3-line program that has main() and a return value. Compile the executable without code signing (turn it off in Project Settings->Build) and sign it with a different key using the "codesign" commandline utility.

Have your app exec the separate executable. If your program can't get the return value when running the separate executable with the bad sig, it's definitely jailed. If the separate executable returns A-OK, the phone is definitely jailbroken.

Upvotes: 52

wisequark
wisequark

Reputation: 3268

It depends what you mean by jailbreak. In the simple case, you should be able to see if Cydia is installed and go by that - something like

NSString *filePath = @"/Applications/Cydia.app";
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath])
{
   // do something useful
}

For hacked kernels, it's a little (lot) more involved.

Upvotes: 95

Gordon Wilson
Gordon Wilson

Reputation: 26384

I'd suggest looking for files that aren't present on a "vanilla" iPhone. All jailbreak kits I've seen install ssh. That might be a good indicator of a jailbroken phone.

Upvotes: 3

Related Questions