iOS platform security & anti-tampering Swift library
🌏 iOS Security Suite is an advanced and easy-to-use platform security & anti-tampering library written in pure Swift! If you are developing for iOS and you want to protect your app according to the OWASP MASVS standard, chapter v8, then this library could save you a lot of time. 🚀
What ISS detects:
There are 4 ways you can start using IOSSecuritySuite
Add
IOSSecuritySuite/*.swiftfiles to your project
pod 'IOSSecuritySuite'
github "securing/IOSSecuritySuite"
.package(url: "https://github.com/securing/IOSSecuritySuite.git", from: "1.5.0")
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.
LSApplicationQueriesSchemes cydia undecimus sileo zbra
if IOSSecuritySuite.amIJailbroken() { print("This device is jailbroken") } else { print("This device is not jailbroken") }
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
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") } }
let amIDebugged: Bool = IOSSecuritySuite.amIDebugged()
IOSSecuritySuite.denyDebugger()
let runInEmulator: Bool = IOSSecuritySuite.amIRunInEmulator()
let amIReverseEngineered: Bool = IOSSecuritySuite.amIReverseEngineered()
let amIProxied: Bool = IOSSecuritySuite.amIProxied()
let amIRuntimeHooked: Bool = amIRuntimeHook(dyldWhiteList: dylds, detectionClass: SomeClass.self, selector: #selector(SomeClass.someFunction), isClassMethod: false)
// 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()
// 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)
// 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() }
// 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.") }
func denyDebugger() { // add a breakpoint at here to test }typealias FunctionType = @convention(thin) ()->() let func_denyDebugger: FunctionType = denyDebugger //
: FunctionType
is a must let func_addr = unsafeBitCast(func_denyDebugger, to: UnsafeMutableRawPointer.self) let hasBreakpoint = IOSSecuritySuite.hasBreakpointAt(func_addr, functionSize: nil)if hasBreakpoint { print("Breakpoint found in the specified function") } else { print("Breakpoint not found in the specified function") }
Before using this and other platform security checkers, you have to understand that:
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!
canOpenURL(_:)method
amIJailbrokenWithFailedChecks()method
See the LICENSE file.
While creating this tool I used: