/private directory. If the file is created successfully, the device has been jailbroken.
JailbreakDetectionVC. You may want to decompile the functions to see what they are doing and, in particular, what they return.
+[SFAntiPiracy isTheDeviceJailbroken]) and an instance method (
-[JailbreakDetectionVC isJailbroken]). The main difference is that we can inject Cycript in the app and call the class method directly, whereas the instance method requires first looking for instances of the target class. The function
choosewill look in the memory heap for known signatures of a given class and return an array of instances. Putting an application into a desired state (so that the class is indeed instantiated) is important.
-[JailbreakDetectionVC isJailbroken]instance method. First, we have to call the
choosefunction to look for instances of the
MSHookMessageEx, and compile the tweak. There are good sources for how to do this; however, by using Frida, we can more easily perform early instrumentation and we can build on our gathered skills from previous tests.
frida-serveris running on your iOS Device.
frida-traceon your host computer:
onLeavecallback functions. Now, replacing the return value via
value.replaceis trivial, as shown in the following example:
-[JailbreakDetectionVC isJailbroken], which correspond to two physical taps on the app's GUI.
ptracesystem call that's lacking most of the functionality required to properly debug a process (e.g. it allows attaching/stepping but not read/write of memory and registers).
ptracesyscall contains a nonstandard and very useful feature: preventing the debugging of processes. This feature is implemented as the
PT_DENY_ATTACHrequest, as described in the official BSD System Calls Manual. In simple words, it ensures that no other debugger can attach to the calling process; if a debugger attempts to attach, the process will terminate. Using
PT_DENY_ATTACHis a fairly well-known anti-debugging technique, so you may encounter it often during iOS pentests.
Before diving into the details, it is important to know that
ptraceis not part of the public iOS API. Non-public APIs are prohibited, and the App Store may reject apps that include them. Because of this,
ptraceis not directly called in the code; it's called when a
ptracefunction pointer is obtained via
dlsymis called with
ptraceas the second argument (register R1). The return value in register R0 is moved to register R6 at offset 0x1908A. At offset 0x19098, the pointer value in register R6 is called using the BLX R6 instruction. To disable the
ptracecall, we need to replace the instruction
0xB0 0x47in Little Endian) with the
0x00 0xBFin Little Endian) instruction. After patching, the code will be similar to the following:
sysctl. According to the Apple documentation, it allows processes to set system information (if having the appropriate privileges) or simply to retrieve system information (such as whether or not the process is being debugged). However, note that just the fact that an app uses
sysctlmight be an indicator of anti-debugging controls, though this won't be always be the case.
MOVNE R0, #1is patched and changed to
MOVNE R0, #0(0x00 0x20 in in bytecode), the patched code is similar to the following:
getppidreturns a PID different than 1. This detection technique can be implemented in native code (via syscalls), using Objective-C or Swift as shown here:
NSUserDefaults, a SQLite database, or a Realm database, their integrity should be protected.
mach_headeris parsed to calculate the start of the instruction data, which is used to generate the signature. Next, the signature is compared to the given signature. Make sure that the generated signature is stored or coded somewhere else.
/proc/<pid>/maps). However, on iOS the
procdirectory is not available, but you can list the loaded dynamic libraries in an app with the function
<Application>.appdirectory. If you inspect the content of the
/var/containers/Bundle/Application/<UUID>/<Application>.appdirectory, you'll find the embedded frida-gadget as FridaGadget.dylib.
frida-serveruses the D-Bus protocol to communicate, so you can expect it to respond to D-Bus AUTH. Send a D-Bus AUTH message to every open port and check for an answer, hoping that
frida-serverwill reveal itself.
frida-server, but Frida offers alternative modes of operation that don't require frida-server.
It is important to note that these controls are only increasing the complexity of the reverse engineering process. If used, the best approach is to combine the controls cleverly instead of using them individually. However, none of them can assure a 100% effectiveness, as the reverse engineer will always have full access to the device and will therefore always win! You also have to consider that integrating some of the controls into your app might increase the complexity of your app and even have an impact on its performance.
Note: All presented techniques below may not stop reverse engineers, but combining all of those techniques will make their job significantly harder. The aim of those techniques is to discourage reverse engineers from performing further analysis.
JailbreakDetectionViewController.jailbreakTest4Tappedfrom the Damn Vulnerable iOS App (DVIA-v2).
x = a + bcan be represented as
x = -(-a) - (-b). However, using the same replacement representation could be easily reversed, so it is recommended to add multiple substitution techniques for a single case and introduce a random factor. This technique is vulnerable to deobfuscation, but depending on the complexity and depth of the substitutions, applying it can still be time consuming.
Warning: SwiftShield irreversibly overwrites all your source files. Ideally, you should have it run only on your CI server, and on release builds.
swiftshielda new directory will be created called
swiftshield-output. In this directory another directory is created with a timestamp in the folder name. This directory contains a text file called
conversionMap.txt, that maps the encrypted strings to their original values.
CTL_NET(a network subsystem) or
NET_RT_IFLIST(getting the configured interfaces) or when the mac-address gets formatted, you'll often see formatting code for printing, such as
[[[UIDevice currentDevice] identifierForVendor] UUIDString];and
SecAccessControlCreateFlagsor and doesn't use protection classes, such as
po NSHomeDirectory()on that point, which will reveal the location of the simulator's stored contents. You can also execute
find ~/Library/Developer/CoreSimulator/Devices/ | grep <appname>for the suspected plist file.
~/Library/Developer/CoreSimulator/Devices/<Simulator Device ID>/data/Library/Keychains.
find /private/var/mobile/Containers/Data/Application/ |grep <name of app>). The directory is in
scp <ipaddress>:/<folder_found_in_previous_step> targetfolder) to copy the folders and it's data. You can use an FTP client like Filezilla as well.
[[UIDevice currentDevice] identifierForVendor](in Objective-C),
UIDevice.current.identifierForVendor?.uuidString(in Swift3), or
UIDevice.currentDevice().identifierForVendor?.UUIDString(in Swift2). The value of
identifierForVendormay not be the same if you reinstall the app after other apps from the same vendor are installed and it may change when you update your app bundle's name. Therefore it is best to combine it with something in the Keychain.
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly(if you want to secure the data and properly enforce a passcode or Touch ID requirement),
kSecAttrAccessibleWhenUnlockedThisDeviceOnly), and the
SecAccessControlCreateFlagsis set either with
kSecAccessControlUserPresence(passcode, Face ID or Touch ID),
kSecAccessControlBiometryAny(Face ID or Touch ID) or
kSecAccessControlBiometryCurrentSet(Face ID / Touch ID: but current enrolled biometrics only).