/private directory
. If the file is created successfully, the device has been jailbroken.SFAntiPiracy
and 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 choose
will 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.top
):-[JailbreakDetectionVC isJailbroken]
instance method. First, we have to call the choose
function to look for instances of the JailbreakDetectionVC
class.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-server
is running on your iOS Device.frida-trace
on your host computer:-[JailbreakDetectionVC isJailbroken]
, and create a JavaScript hook with the onEnter
and onLeave
callback functions. Now, replacing the return value via value.replace
is trivial, as shown in the following example:-[JailbreakDetectionVC isJailbroken]
, which correspond to two physical taps on the app's GUI.ptrace
system 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).ptrace
syscall contains a nonstandard and very useful feature: preventing the debugging of processes. This feature is implemented as the PT_DENY_ATTACH
request, 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_ATTACH
is 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 thatptrace
is 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,ptrace
is not directly called in the code; it's called when aptrace
function pointer is obtained viadlsym
.
dlsym
is called with ptrace
as 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 ptrace
call, we need to replace the instruction BLX R6
(0xB0 0x47
in Little Endian) with the NOP
(0x00 0xBF
in 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 sysctl
might be an indicator of anti-debugging controls, though this won't be always be the case.info.kp_proc.p_flag
flag returned by the call to sysctl
with the appropriate parameters:MOVNE R0, #1
is patched and changed to MOVNE R0, #0
(0x00 0x20 in in bytecode), the patched code is similar to the following:sysctl
check by using the debugger itself and setting a breakpoint at the call to sysctl
. This approach is demonstrated in iOS Anti-Debugging Protections #2.getppid
returns a PID different than 1. This detection technique can be implemented in native code (via syscalls), using Objective-C or Swift as shown here:UserDefaults
/NSUserDefaults
, a SQLite database, or a Realm database, their integrity should be protected.mach_header
is 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.NSMutableData
.NSData
.NSData
.proc
directory (/proc/<pid>/maps
). However, on iOS the proc
directory is not available, but you can list the loaded dynamic libraries in an app with the function _dyld_image_count
.<Application>.app
directory. If you inspect the content of the /var/containers/Bundle/Application/<UUID>/<Application>.app
directory, you'll find the embedded frida-gadget as FridaGadget.dylib._dyld_get_image_name
.frida-server
uses 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-server
will 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.jailbreakTest4Tapped
from the Damn Vulnerable iOS App (DVIA-v2).x = a + b
can 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.
/usr/local/bin
:swiftshield
a 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.identifierForVendor
, storing something in the Keychain, or using Google's InstanceID for iOS. See the "Remediation" section for more details.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 "%x:%x:%x:%x:%x:%x"
.[[[UIDevice currentDevice] identifierForVendor] UUIDString];
and UIDevice.current.identifierForVendor?.uuidString
in Swift3.SecAccessControlCreateFlags
or and doesn't use protection classes, such as kSecAttrAccessibleAlways
and kSecAttrAccessibleAlwaysThisDeviceOnly
.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 /private/var/mobile/Containers/Data/Application/<Application uuid>
.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./private/var/Keychains/keychain-2.db
, which you can retrieve using Keychain-dumper.[[UIDevice currentDevice] identifierForVendor]
(in Objective-C), UIDevice.current.identifierForVendor?.uuidString
(in Swift3), or UIDevice.currentDevice().identifierForVendor?.UUIDString
(in Swift2). The value of identifierForVendor
may 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), kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
, or kSecAttrAccessibleWhenUnlockedThisDeviceOnly
.kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
and kSecAttrAccessibleWhenUnlockedThisDeviceOnly
), and the SecAccessControlCreateFlags
is set either with kSecAccessControlDevicePasscode
(for passcodes), kSecAccessControlUserPresence
(passcode, Face ID or Touch ID), kSecAccessControlBiometryAny
(Face ID or Touch ID) or kSecAccessControlBiometryCurrentSet
(Face ID / Touch ID: but current enrolled biometrics only).