Bypassing Jailbreak Detection
Recently, a sizable handful of applications in Apple's own App Store have been implementing procedures to check the authenticity of the device on which the app itself is running, forbidding or inhibiting usage of certain features or even the app altogether. Obvious reasons are about the innate security risks of jailbreaking your device (e.g. banking companies don't want the blame for some rogue keylogger disguised as a tweak snagging your account info). However, most of the time, it seems as if companies are worried about the possibility of tweaks bypassing certain restrictions implemented into their apps. Video streaming apps are notorious for this; the companies don't want users bypassing restrictions on when and where you can stream their content, so instead of doing the responsible thing and obfuscating their restriction attempts, they instead block all jailbroken devices, regardless of malicious intent or lack thereof.
How To Detect Jailbroken Devices
For the sake of convenience within this article, a "rogue app" will refer to any app available in the App Store that actively implements jailbreak detection measures.
For the most part, jailbreak detection procedures are a lot less sophisticated that one might imagine. While there are countless ways apps can implement checks for jailbroken devices, they typically boil down to the following:
- Existence of directories - Rogue apps love to check your file system for paths like /Applications/Cydia.app/ and /private/var/stash, amongst a handful of others. Most often, these are checked using the -(BOOL)fileExistsAtPath:(NSString*)path method in NSFileManager, but more sneaky apps like to use lower-level C functions like fopen(), stat(), or access().
- Directory permissions - Similar to checking existence of directories, but checks the Unix file permissions of specific files and directories on the system using NSFileManager methods as well as C functions like statfs(). Far more directories have write access on a jailbroken device than on one still in jail.
- Process forking - sandboxd does not deny App Store applications the ability to use fork(), popen(), or any other C functions to create child processes on devices out of jail. sandboxd explicitly denies process forking on devices in jail. By checking the returned pid on fork(), a rogue app can tell if it has successfully forked or not, at which point it can determine a device's jailbreak status.
- SSH loopback connections - Only a very small number of applications implement this (as it is not nearly as effective as the others). Due to the very large portion of jailbroken devices that have OpenSSH installed, some rogue apps will attempt to make a connection to 127.0.0.1 on port 22. If the connection succeeds, it means OpenSSH is installed and running on the device, which obviously indicates that it is jailbroken.
- system() - Calling the system() function with a NULL argument on a device in jail will return 0; doing the same on a jailbroken device will return 1. This is since the function will check whether
/bin/shexists, and this is only the case on jailbroken devices.
- dyld functions - By far the hardest to get around. Calling functions like _dyld_image_count() and _dyld_get_image_name() to see which dylibs are currently loaded. Very difficult to patch, as patches are themselves part of dylibs.
How To Reverse Engineer An App
- In order to dump or disassemble an app from the App Store, it must first be decrypted (often referred to as "cracking"), even if it is a free application.
- Using class-dump-z on the application's decrypted binary will dump all of the header files. Occasionally, these contain "giveaway" method names, like "deviceIsJailbroken" or "checkDeviceSecurity." Typically, hooking these methods is enough to disable the jailbreak detection measures, but it nearly guarantees that the patch will not work on other apps.
- Tracing methods named like that in IDA using the Objective-C parsing feature can help pinpoint exactly which method is being used to detect the jailbreak.
- If the class-dumped headers don't give away anything, searching the binary for strings like "jail," "cydia," "apt," etc. often lead to the breaking point.
See xCon on its separate page.