@_r3ggi
byISS Description
What ISS detects:
- Jailbreak (even the iOS 11+ with brand new indicators!
π₯ ) - Attached debugger π¨π»βπ
- If an app was run in an emulator
π½ - Common reverse engineering tools running on the device
π
Setup
There are 4 ways you can start using IOSSecuritySuite
1. Add source
Add IOSSecuritySuite/*.swift
files to your project
2. Setup with CocoaPods
pod 'IOSSecuritySuite'
3. Setup with Carthage
github "securing/IOSSecuritySuite"
4. Setup with Swift Package Manager
.package(url: "https://github.com/securing/IOSSecuritySuite.git", from: "1.5.0")
Update Info.plist
After adding ISS to your project, you will also need to update your main Info.plist. There is a check in jailbreak detection module that uses canOpenURL(_:)
method and requires specifying URLs that will be queried.
<key>LSApplicationQueriesSchemes</key>
<array>
<string>cydia</string>
<string>undecimus</string>
<string>sileo</string>
<string>zbra</string>
</array>
How to use
Jailbreak detector module
- The simplest method returns True/False if you just want to know if the device is jailbroken or jailed
if IOSSecuritySuite.amIJailbroken() {
print("This device is jailbroken")
} else {
print("This device is not jailbroken")
}
- Verbose, if you also want to know what indicators were identified
let jailbreakStatus = IOSSecuritySuite.amIJailbrokenWithFailMessage()
if jailbreakStatus.jailbroken {
print("This device is jailbroken")
print("Because: \(jailbreakStatus.failMessage)")
} else {
print("This device is not jailbroken")
}
The failMessage is a String containing comma-separated indicators as shown on the example below:
Cydia URL scheme detected, Suspicious file exists: /Library/MobileSubstrate/MobileSubstrate.dylib, Fork was able to create a new process
- Verbose & filterable, if you also want to for example identify devices that were jailbroken in the past, but now are jailed
let jailbreakStatus = IOSSecuritySuite.amIJailbrokenWithFailedChecks()
if jailbreakStatus.jailbroken {
if (jailbreakStatus.failedChecks.contains { $0.check == .existenceOfSuspiciousFiles }) && (jailbreakStatus.failedChecks.contains { $0.check == .suspiciousFilesCanBeOpened }) {
print("This is real jailbroken device")
}
}
Debugger detector module
let amIDebugged = IOSSecuritySuite.amIDebugged() ? true : false
Deny debugger at all
IOSSecuritySuite.denyDebugger()
Emulator detector module
let runInEmulator = IOSSecuritySuite.amIRunInEmulator() ? true : false
Reverse engineering tools detector module
let amIReverseEngineered = IOSSecuritySuite.amIReverseEngineered() ? true : false
Experimental features
Runtime hook detector module
let amIRuntimeHooked = amIRuntimeHook(dyldWhiteList: dylds, detectionClass: SomeClass.self, selector: #selector(SomeClass.someFunction), isClassMethod: false) ? true : false
Symbol hook deny module
// If we want to deny symbol hook of Swift function, we have to pass mangled name of that function
denySymbolHook("$s10Foundation5NSLogyySS_s7CVarArg_pdtF") // denying hooking for the NSLog function
NSLog("Hello Symbol Hook")
denySymbolHook("abort")
abort()
MSHook detector module
// Function declaration
func someFunction(takes: Int) -> Bool {
return false
}
// Defining FunctionType : @convention(thin) indicates a βthinβ function reference, which uses the Swift calling convention with no special βselfβ or βcontextβ parameters.
typealias FunctionType = @convention(thin) (Int) -> (Bool)
// Getting pointer address of function we want to verify
func getSwiftFunctionAddr(_ function: @escaping FunctionType) -> UnsafeMutableRawPointer {
return unsafeBitCast(function, to: UnsafeMutableRawPointer.self)
}
let funcAddr = getSwiftFunctionAddr(someFunction)
let amIMSHooked = IOSSecuritySuite.amIMSHooked(funcAddr)
MSHook deny module
// Function declaration
func denyDebugger(value: Int) {
}
// Defining FunctionType : @convention(thin) indicates a βthinβ function reference, which uses the Swift calling convention with no special βselfβ or βcontextβ parameters.
typealias FunctionType = @convention(thin) (Int)->()
// Getting original function address
let funcDenyDebugger: FunctionType = denyDebugger
let funcAddr = unsafeBitCast(funcDenyDebugger, to: UnsafeMutableRawPointer.self)
if let originalDenyDebugger = denyMSHook(funcAddr) {
// Call the original function with 1337 as Int argument
unsafeBitCast(originalDenyDebugger, to: FunctionType.self)(1337)
} else {
denyDebugger()
}
File integrity verifier module
// Determine if application has been tampered with
if IOSSecuritySuite.amITampered([.bundleID("biz.securing.FrameworkClientApp"),
.mobileProvision("2976c70b56e9ae1e2c8e8b231bf6b0cff12bbbd0a593f21846d9a004dd181be3"),
.machO("IOSSecuritySuite", "6d8d460b9a4ee6c0f378e30f137cebaf2ce12bf31a2eef3729c36889158aa7fc")]).result {
print("I have been Tampered.")
}
else {
print("I have not been Tampered.")
}
// Manually verify SHA256 hash value of a loaded dylib
if let hashValue = IOSSecuritySuite.getMachOFileHashValue(.custom("IOSSecuritySuite")), hashValue == "6d8d460b9a4ee6c0f378e30f137cebaf2ce12bf31a2eef3729c36889158aa7fc" {
print("I have not been Tampered.")
}
else {
print("I have been Tampered.")
}
// Check SHA256 hash value of the main executable
// Tip: Your application may retrieve this value from the server
if let hashValue = IOSSecuritySuite.getMachOFileHashValue(.default), hashValue == "your-application-executable-hash-value" {
print("I have not been Tampered.")
}
else {
print("I have been Tampered.")
}
Security considerations
Before using this and other platform security checkers, you have to understand that:
- Including this tool in your project is not the only thing you should do in order to improve your app security! You can read a general mobile security whitepaper here.
- Detecting if a device is jailbroken is done locally on the device. It means that every jailbreak detector may be bypassed (even this)!
- Swift code is considered to be harder to manipulate dynamically than Objective-C. Since this library was written in pure Swift, the IOSSecuritySuite methods shouldn't be exposed to Objective-C runtime (which makes it more difficult to bypass
β ). You have to know that attacker is still able to MSHookFunction/MSFindSymbol Swift symbols and dynamically change Swift code execution flow. - It's also a good idea to obfuscate the whole project code, including this library. See Swiftshield
β€οΈ
Contribution Yes, please! If you have a better idea or you just want to improve this project, please text me on Twitter or Linkedin. Pull requests are more than welcome!
Special thanks: ππ»
- kubajakowski for pointing out the problem with
canOpenURL(_:)
method - olbartek for code review and pull request
- benbahrenburg for various ISS improvements
- fotiDim for adding new file paths to check
- gcharita for adding the Swift Package Manager support
- rynaardb for creating the
amIJailbrokenWithFailedChecks()
method - undeaDD for various ISS improvements
- fnxpt for adding HideJB detection
- TannerJin for MSHook, RuntimeHook and SymbolHook modules
- NikoXu for adding file integrity module
TODO
- Research Installer5 and Zebra Package Manager detection ( Cydia Alternatives )
License
See the LICENSE file.
References
While creating this tool I used:
π https://github.com/TheSwiftyCoder/JailBreak-Detectionπ https://github.com/abhinashjain/jailbreakdetectionπ https://gist.github.com/ddrccw/8412847π https://gist.github.com/bugaevc/4307eaf045e4b4264d8e395b5878a63bπ "iOS Application Security" by David Thiel