How to Detect That an iOS App Is Running on a Jailbroken Phone

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

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.

How to detect that the app is running on a jailbroken device?

You can detect through code that if the app is running on a jail broken device or not. Through that way you can pop up an alert and close the app. You can do whatever you want to do. Here is a tutorial for it:

http://thwart-ipa-cracks.blogspot.com/2008/11/detection.html

and here is a Stack Overflow post:

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

Also, if you want a complete solution, you can see in tapjoy sdk code. They are detecting jailbroken iPhone. Here is tapjoy URL https://www.tapjoy.com/

How do I accurately detect the presence and/or absence of a jailbreak in iOS?

Blocking all jailbroken users probably wouldn't help you fight app piracy if you released a game on the App Store because it would force them to get a pirated version of the game to be able to play (instead of giving them the possibility to pay to play the game).

What you'd want is to check if the game is a legit version off the App Store. But even that could be potentially patched by the guys who crack games to release them...

You can check if the currently running executable is encrypted, which is a good way to know if the app has been pirated by looking at this answer.

Otherwise if it's a free game with in-app purchase, doing receipt validation helps block out most tweaks that get around paying for in-app purchases.

But there's definitely no way to absolutely block out app piracy.

You could always mention how had you worked on that game within the game... That could convince a few persons to pay for the legit version of the game.

Detect Jailbroken in iOS 11 or later

Update 2021:
You can use the extension on UIDevice from this article:
https://developerinsider.co/best-way-to-check-if-your-ios-app-is-running-on-a-jailbroken-phone/

I copied all code in here.

You can find the latest version of code on Github here (author is @vineetchoudhary):
https://github.com/developerinsider/isJailBroken/blob/master/IsJailBroken/Extension/UIDevice%2BJailBroken.swift

Whole code:

import Foundation
import UIKit

extension UIDevice {
var isSimulator: Bool {
return TARGET_OS_SIMULATOR != 0
}

var isJailBroken: Bool {
get {
if UIDevice.current.isSimulator { return false }
if JailBrokenHelper.hasCydiaInstalled() { return true }
if JailBrokenHelper.isContainsSuspiciousApps() { return true }
if JailBrokenHelper.isSuspiciousSystemPathsExists() { return true }
return JailBrokenHelper.canEditSystemFiles()
}
}
}

private struct JailBrokenHelper {
static func hasCydiaInstalled() -> Bool {
return UIApplication.shared.canOpenURL(URL(string: "cydia://")!)
}

static func isContainsSuspiciousApps() -> Bool {
for path in suspiciousAppsPathToCheck {
if FileManager.default.fileExists(atPath: path) {
return true
}
}
return false
}

static func isSuspiciousSystemPathsExists() -> Bool {
for path in suspiciousSystemPathsToCheck {
if FileManager.default.fileExists(atPath: path) {
return true
}
}
return false
}

static func canEditSystemFiles() -> Bool {
let jailBreakText = "Developer Insider"
do {
try jailBreakText.write(toFile: jailBreakText, atomically: true, encoding: .utf8)
return true
} catch {
return false
}
}

/**
Add more paths here to check for jail break
*/
static var suspiciousAppsPathToCheck: [String] {
return ["/Applications/Cydia.app",
"/Applications/blackra1n.app",
"/Applications/FakeCarrier.app",
"/Applications/Icy.app",
"/Applications/IntelliScreen.app",
"/Applications/MxTube.app",
"/Applications/RockApp.app",
"/Applications/SBSettings.app",
"/Applications/WinterBoard.app"
]
}

static var suspiciousSystemPathsToCheck: [String] {
return ["/Library/MobileSubstrate/DynamicLibraries/LiveClock.plist",
"/Library/MobileSubstrate/DynamicLibraries/Veency.plist",
"/private/var/lib/apt",
"/private/var/lib/apt/",
"/private/var/lib/cydia",
"/private/var/mobile/Library/SBSettings/Themes",
"/private/var/stash",
"/private/var/tmp/cydia.log",
"/System/Library/LaunchDaemons/com.ikey.bbot.plist",
"/System/Library/LaunchDaemons/com.saurik.Cydia.Startup.plist",
"/usr/bin/sshd",
"/usr/libexec/sftp-server",
"/usr/sbin/sshd",
"/etc/apt",
"/bin/bash",
"/Library/MobileSubstrate/MobileSubstrate.dylib"
]
}
}

Also, you need to add cydia in LSApplicationQueriesSchemes key inside Info.plist. It's required for canOpenURL to work.

<key>LSApplicationQueriesSchemes</key>
<array>
<string>cydia</string>
</array>

Identify jailbroken device from iOS application

You can detect through code that if the app is running on a jail broken device or not.

Through that way you can pop up a alert and close the app.

You can do whatever you want to do.

Here is a tutorial for it.

Detection

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

Also if you want complete solution you can see in tapjoy sdk code.

They are detecting jailbroken iphone.

Here is tapjoy URL tapjoy

Nativescript - Detect Jailbreak and Dynamic Instrumentation on iOS

Detection can be carried out on multi-levels:

  • Check if URLs are openable via illegal URL schemes
  • Check if files are openable on illegal directories
  • Check if illegal files exist (incl. Cydia, Sileo, HideJB, etc.)
  • Check if files are writable on restricted directories


Code

  public amIJailbroken(): boolean {
let urlSchemes: Array<string> = ['undecimus://', 'cydia://', 'sileo://', 'zbra://', 'filza://', 'activator://'];

// List of suspicious files associated with jailbreak
let paths: Array<string> = [
'/.bootstrapped_electra',
'/.cydia_no_stash',
'/.installed_unc0ver',
'/Applications/blackra1n.app',
'/Applications/Cydia.app',
'/Applications/FakeCarrier.app',
'/Applications/HideJB.app',
'/Applications/Icy.app',
'/Applications/IntelliScreen.app',
'/Applications/MxTube.app',
'/Applications/RockApp.app',
'/Applications/SBSettings.app',
'/Applications/SBSetttings.app',
'/Applications/Sileo.app',
'/Applications/Snoop-itConfig.app',
'/Applications/WinterBoard.app',
'/bin.sh',
'/bin/bash',
'/bin/sh',
'/etc/apt',
'/etc/apt/sources.list.d/electra.list',
'/etc/apt/sources.list.d/sileo.sources',
'/etc/apt/undecimus/undecimus.list',
'/etc/ssh/sshd_config',
'/jb/amfid_payload.dylib',
'/jb/jailbreakd.plist',
'/jb/libjailbreak.dylib',
'/jb/lzma',
'/jb/offsets.plist',
'/Library/dpkg/info/re.frida.server.list',
'/Library/LaunchDaemons/re.frida.server.plist',
'/Library/MobileSubstrate/CydiaSubstrate.dylib',
'/Library/MobileSubstrate/DynamicLibraries/LiveClock.plist',
'/Library/MobileSubstrate/DynamicLibraries/Veency.plist',
'/Library/MobileSubstrate/HideJB.dylib',
'/Library/MobileSubstrate/MobileSubstrate.dylib',
'/Library/PreferenceBundles/ABypassPrefs.bundle',
'/Library/PreferenceBundles/FlyJBPrefs.bundle',
'/Library/PreferenceBundles/HideJBPrefs.bundle',
'/Library/PreferenceBundles/LibertyPref.bundle',
'/Library/PreferenceBundles/ShadowPreferences.bundle',
'/private/etc/apt',
'/private/etc/dpkg/origins/debian',
'/private/etc/ssh/sshd_config',
'/private/var/cache/apt/',
'/private/var/lib/apt',
'/private/var/lib/apt/',
'/private/var/lib/cydia',
'/private/var/log/syslog',
'/private/var/mobile/Library/SBSettings/Themes',
'/private/var/mobileLibrary/SBSettingsThemes/',
'/private/var/stash',
'/private/var/tmp/cydia.log',
'/private/var/Users/',
'/System/Library/LaunchDaemons/com.ikey.bbot.plist',
'/System/Library/LaunchDaemons/com.saurik.Cydia.Startup.plist',
'/usr/bin/cycript',
'/usr/bin/ssh',
'/usr/bin/sshd',
'/usr/lib/libcycript.dylib',
'/usr/lib/libhooker.dylib',
'/usr/lib/libjailbreak.dylib',
'/usr/lib/libsubstitute.dylib',
'/usr/lib/substrate',
'/usr/lib/TweakInject',
'/usr/libexec/cydia/',
'/usr/libexec/cydia/firmware.sh',
'/usr/libexec/sftp-server',
'/usr/libexec/ssh-keysign',
'/usr/local/bin/cycript',
'/usr/sbin/frida-server',
'/usr/sbin/sshd',
'/usr/share/jailbreak/injectme.plist',
'/var/binpack',
'/var/cache/apt',
'/var/checkra1n.dmg',
'/var/lib/apt',
'/var/lib/cydia',
'/var/lib/dpkg/info/mobilesubstrate.md5sums',
'/var/log/apt',
'/var/log/syslog',
'/var/tmp/cydia.log',
];

// Check if target is not an iOS simulator
if (!isIOS || !this.isTarget()) return false;
else {

// Check URL schemes
for (const url of urlSchemes) {
if (this.canOpenIllegalURL(url)) return true;
}

// Check files and directories associated with jailbreaks
for (const path of paths) {
if (this.canOpenIllegalFile(path)) return true;
}

// Check file permissions outside device sandbox, if writtable = jailbroken
if (this.canWriteToRestrictedDirectories()) return true;

return false;
}
}


/*
********** Helper Methods **********
*/

/* Check if environment is being run as a RELEASE build */
private isTarget() {
return process.env.RELEASE_ENV;
}

/* Check if we can open illegal URL schemes */
private canOpenIllegalURL(url): boolean {
return UIApplication.sharedApplication.canOpenURL(NSURL.URLWithString(url + 'package/com.example.app'));
}

/* Check if file is openable */
private canOpenIllegalFile(path): boolean {
const file = fopen(path, 'r');
if (!file) {
fclose(file);
return this.fileExists(path) || this.directoryExists(path);
}
fclose(file);
return true;
}

/* Check if file exists at path */
private fileExists(path): boolean {
return NSFileManager.defaultManager.fileExistsAtPath(path);
}

/* Check if directory exists at path */
private directoryExists(path): boolean {
return NSFileManager.defaultManager.fileExistsAtPathIsDirectory(path, new interop.Reference());
}

/* Check if file is writtable to illegal directory */
private canWriteToRestrictedDirectories(): boolean {
let error;
try {
const stringToBeWritten = NSString.stringWithString('I am evil.');
stringToBeWritten.writeToFileAtomicallyEncodingError('/private/jailbreak.txt', true, NSUTF8StringEncoding);
stringToBeWritten.writeToFileAtomicallyEncodingError('/root/jailbreak.txt', true, NSUTF8StringEncoding);
NSFileManager.defaultManager.removeItemAtPathError('/private/jailbreak.txt');
NSFileManager.defaultManager.removeItemAtPathError('/root/jailbreak.txt');
} catch (e) {
error = e;
}
return !error ? true : false;
}


References

The research comes from a consolidation of the following ideas:

  • https://stackoverflow.com/a/26712383/2192332
  • https://mobile-security.gitbook.io/mobile-security-testing-guide/ios-testing-guide/0x06j-testing-resiliency-against-reverse-engineering
  • https://github.com/securing/IOSSecuritySuite/blob/master/IOSSecuritySuite/JailbreakChecker.swift
  • https://github.com/avltree9798/isJailbroken/blob/master/isJailbroken/JB.m


Improvements

Please feel free to suggest!

E.g. Checking for illegal dynamic libraries in memory using _dyld_get_image_name



Related Topics



Leave a reply



Submit