├── .gitattributes ├── .gitignore ├── .swift-version ├── FrameworkClientApp ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── FishHook.swift ├── FrameworkClientApp.entitlements ├── Info.plist └── ViewController.swift ├── IOSSecuritySuite.podspec ├── IOSSecuritySuite.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── xcshareddata │ └── xcschemes │ ├── FrameworkClientApp.xcscheme │ └── IOSSecuritySuite.xcscheme ├── IOSSecuritySuite ├── DebuggerChecker.swift ├── EmulatorChecker.swift ├── FishHookChecker.swift ├── IOSSecuritySuite.h ├── IOSSecuritySuite.swift ├── Info.plist ├── IntegrityChecker.swift ├── JailbreakChecker.swift ├── MSHookFunctionChecker.swift ├── ProxyChecker.swift ├── ReverseEngineeringToolsChecker.swift └── RuntimeHookChecker.swift ├── LICENSE ├── Package.swift ├── README.md └── logo.png /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | .swiftlint.yml 25 | .code 26 | .DS_Store 27 | 28 | ## Obj-C/Swift specific 29 | *.hmap 30 | *.ipa 31 | *.dSYM.zip 32 | *.dSYM 33 | 34 | ## Playgrounds 35 | timeline.xctimeline 36 | playground.xcworkspace 37 | 38 | # Swift Package Manager 39 | # 40 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 41 | # Packages/ 42 | # Package.pins 43 | # Package.resolved 44 | .build/ 45 | .swiftpm/xcode 46 | 47 | # CocoaPods 48 | # 49 | # We recommend against adding the Pods directory to your .gitignore. However 50 | # you should judge for yourself, the pros and cons are mentioned at: 51 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 52 | # 53 | # Pods/ 54 | 55 | # Carthage 56 | # 57 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 58 | # Carthage/Checkouts 59 | 60 | Carthage/Build 61 | 62 | # fastlane 63 | # 64 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 65 | # screenshots whenever they are needed. 66 | # For more information about the recommended setup visit: 67 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 68 | 69 | fastlane/report.xml 70 | fastlane/Preview.html 71 | fastlane/screenshots/**/*.png 72 | fastlane/test_output 73 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 5.0 -------------------------------------------------------------------------------- /FrameworkClientApp/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // FrameworkClientApp 4 | // 5 | // Created by wregula on 23/04/2019. 6 | // Copyright © 2019 wregula. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | internal class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | } 17 | -------------------------------------------------------------------------------- /FrameworkClientApp/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /FrameworkClientApp/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /FrameworkClientApp/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /FrameworkClientApp/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /FrameworkClientApp/FishHook.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FishHook.swift 3 | // FishHookProtect 4 | // 5 | // Created by jintao on 2019/3/28. 6 | // Copyright © 2019 jintao. All rights reserved. 7 | // 8 | // swiftlint:disable all 9 | 10 | import Foundation 11 | import MachO 12 | 13 | #if arch(arm64) 14 | @inline(__always) // just for Swift 15 | public func replaceSymbol(_ symbol: String, 16 | newMethod: UnsafeMutableRawPointer, 17 | oldMethod: inout UnsafeMutableRawPointer?) 18 | { 19 | for i in 0..<_dyld_image_count() { 20 | if let image = _dyld_get_image_header(i) { 21 | replaceSymbol(symbol, at: image, imageSlide: _dyld_get_image_vmaddr_slide(i), newMethod: newMethod, oldMethod: &oldMethod) 22 | } 23 | } 24 | } 25 | 26 | private func replaceSymbol(_ symbol: String, 27 | at image: UnsafePointer, 28 | imageSlide slide: Int, 29 | newMethod: UnsafeMutableRawPointer, 30 | oldMethod: inout UnsafeMutableRawPointer?) { 31 | replaceSymbolAtImage(image, imageSlide: slide, symbol: symbol, newMethod: newMethod, oldMethod: &oldMethod) 32 | } 33 | 34 | @inline(__always) 35 | private func replaceSymbolAtImage(_ image: UnsafePointer, 36 | imageSlide slide: Int, 37 | symbol: String, 38 | newMethod: UnsafeMutableRawPointer, 39 | oldMethod: inout UnsafeMutableRawPointer?) { 40 | var linkeditCmd: UnsafeMutablePointer! 41 | var dataCmd: UnsafeMutablePointer! 42 | var symtabCmd: UnsafeMutablePointer! 43 | var dynamicSymtabCmd: UnsafeMutablePointer! 44 | 45 | guard var curCmdPointer = UnsafeMutableRawPointer(bitPattern: UInt(bitPattern: image)+UInt(MemoryLayout.size)) else { return } 46 | 47 | for _ in 0..(OpaquePointer(curCmd)) 61 | } else if curCmd.pointee.cmd == LC_DYSYMTAB { 62 | dynamicSymtabCmd = UnsafeMutablePointer(OpaquePointer(curCmd)) 63 | } 64 | 65 | curCmdPointer += Int(curCmd.pointee.cmdsize) 66 | } 67 | 68 | if linkeditCmd == nil || symtabCmd == nil || dynamicSymtabCmd == nil || dataCmd == nil { 69 | return 70 | } 71 | 72 | let linkedBase = slide + Int(linkeditCmd.pointee.vmaddr) - Int(linkeditCmd.pointee.fileoff) 73 | let symtab = UnsafeMutablePointer(bitPattern: linkedBase + Int(symtabCmd.pointee.symoff)) 74 | let strtab = UnsafeMutablePointer(bitPattern: linkedBase + Int(symtabCmd.pointee.stroff)) 75 | let indirectsym = UnsafeMutablePointer(bitPattern: linkedBase + Int(dynamicSymtabCmd.pointee.indirectsymoff)) 76 | 77 | if symtab == nil || strtab == nil || indirectsym == nil { 78 | return 79 | } 80 | 81 | for tmp in 0...size + MemoryLayout.size*Int(tmp)).assumingMemoryBound(to: section_64.self) 83 | 84 | // symbol_pointers sections 85 | if curSection.pointee.flags == S_LAZY_SYMBOL_POINTERS { 86 | replaceSymbolPointerAtSection(curSection, symtab: symtab!, strtab: strtab!, indirectsym: indirectsym!, slide: slide, symbolName: symbol, newMethod: newMethod, oldMethod: &oldMethod) 87 | } 88 | if curSection.pointee.flags == S_NON_LAZY_SYMBOL_POINTERS { 89 | replaceSymbolPointerAtSection(curSection, symtab: symtab!, strtab: strtab!, indirectsym: indirectsym!, slide: slide, symbolName: symbol, newMethod: newMethod, oldMethod: &oldMethod) 90 | } 91 | } 92 | } 93 | 94 | @inline(__always) 95 | private func replaceSymbolPointerAtSection(_ section: UnsafeMutablePointer, 96 | symtab: UnsafeMutablePointer, 97 | strtab: UnsafeMutablePointer, 98 | indirectsym: UnsafeMutablePointer, 99 | slide: Int, 100 | symbolName: String, 101 | newMethod: UnsafeMutableRawPointer, 102 | oldMethod: inout UnsafeMutableRawPointer?) { 103 | let indirectSymVmAddr = indirectsym.advanced(by: Int(section.pointee.reserved1)) 104 | let sectionVmAddr = UnsafeMutablePointer(bitPattern: slide+Int(section.pointee.addr)) 105 | 106 | if sectionVmAddr == nil { 107 | return 108 | } 109 | 110 | for tmp in 0...size { 111 | let curIndirectSym = indirectSymVmAddr.advanced(by: tmp) 112 | if curIndirectSym.pointee == INDIRECT_SYMBOL_ABS || curIndirectSym.pointee == INDIRECT_SYMBOL_LOCAL { 113 | continue 114 | } 115 | let curStrTabOff = symtab.advanced(by: Int(curIndirectSym.pointee)).pointee.n_un.n_strx 116 | let curSymbolName = strtab.advanced(by: Int(curStrTabOff+1)) 117 | if String(cString: curSymbolName) == symbolName { 118 | oldMethod = sectionVmAddr!.advanced(by: tmp).pointee 119 | sectionVmAddr!.advanced(by: tmp).initialize(to: newMethod) 120 | break 121 | } 122 | } 123 | } 124 | #endif 125 | -------------------------------------------------------------------------------- /FrameworkClientApp/FrameworkClientApp.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /FrameworkClientApp/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSApplicationQueriesSchemes 22 | 23 | zbra 24 | cydia 25 | undecimus 26 | sileo 27 | filza 28 | activator 29 | 30 | LSRequiresIPhoneOS 31 | 32 | UILaunchStoryboardName 33 | LaunchScreen 34 | UIMainStoryboardFile 35 | Main 36 | UIRequiredDeviceCapabilities 37 | 38 | armv7 39 | 40 | UISupportedInterfaceOrientations 41 | 42 | UIInterfaceOrientationPortrait 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | UISupportedInterfaceOrientations~ipad 47 | 48 | UIInterfaceOrientationPortrait 49 | UIInterfaceOrientationPortraitUpsideDown 50 | UIInterfaceOrientationLandscapeLeft 51 | UIInterfaceOrientationLandscapeRight 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /FrameworkClientApp/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // FrameworkClientApp 4 | // 5 | // Created by wregula on 23/04/2019. 6 | // Copyright © 2019 wregula. All rights reserved. 7 | // 8 | //swiftlint:disable all 9 | 10 | import UIKit 11 | import IOSSecuritySuite 12 | 13 | class RuntimeClass { 14 | @objc dynamic func runtimeModifiedFunction()-> Int { 15 | return 1 16 | } 17 | } 18 | 19 | internal class ViewController: UIViewController { 20 | 21 | func testHookPrint() { 22 | typealias MyPrint = @convention(thin) (Any..., String, String) ->Void 23 | func myPrint(_ items: Any..., separator: String = " ", terminator: String = "\n") { 24 | NSLog("print has been hooked") 25 | } 26 | let myprint: MyPrint = myPrint 27 | let myPrintPointer = unsafeBitCast(myprint, to: UnsafeMutableRawPointer.self) 28 | var oldMethod: UnsafeMutableRawPointer? 29 | 30 | // hook 31 | replaceSymbol("$ss5print_9separator10terminatoryypd_S2StF", newMethod: myPrintPointer, oldMethod: &oldMethod) 32 | print("print hasn't been hooked") 33 | 34 | // antiHook 35 | IOSSecuritySuite.denySymbolHook("$ss5print_9separator10terminatoryypd_S2StF") 36 | print("print has been antiHooked") 37 | } 38 | 39 | override func viewDidAppear(_ animated: Bool) { 40 | // testHookPrint() 41 | 42 | // Runtime Check 43 | let test = RuntimeClass.init() 44 | test.runtimeModifiedFunction() 45 | let dylds = ["UIKit"] 46 | let amIRuntimeHooked = IOSSecuritySuite.amIRuntimeHooked(dyldWhiteList: dylds, detectionClass: RuntimeClass.self, selector: #selector(RuntimeClass.runtimeModifiedFunction), isClassMethod: false) 47 | // MSHook Check 48 | func msHookReturnFalse(takes: Int) -> Bool { 49 | /// add breakpoint at here to test `IOSSecuritySuite.hasBreakpointAt` 50 | return false 51 | } 52 | typealias FunctionType = @convention(thin) (Int) -> (Bool) 53 | func getSwiftFunctionAddr(_ function: @escaping FunctionType) -> UnsafeMutableRawPointer { 54 | return unsafeBitCast(function, to: UnsafeMutableRawPointer.self) 55 | } 56 | let funcAddr = getSwiftFunctionAddr(msHookReturnFalse) 57 | 58 | let jailbreakStatus = IOSSecuritySuite.amIJailbrokenWithFailMessage() 59 | let title = jailbreakStatus.jailbroken ? "Jailbroken" : "Jailed" 60 | let message = """ 61 | Jailbreak: \(jailbreakStatus.failMessage), 62 | Run in emulator?: \(IOSSecuritySuite.amIRunInEmulator()) 63 | Debugged?: \(IOSSecuritySuite.amIDebugged()) 64 | HasBreakpoint?: \(IOSSecuritySuite.hasBreakpointAt(funcAddr, functionSize: nil)) 65 | HasWatchpoint?: \(IOSSecuritySuite.hasWatchpoint()) 66 | Reversed?: \(IOSSecuritySuite.amIReverseEngineered()) 67 | Am I MSHooked: \(IOSSecuritySuite.amIMSHooked(funcAddr)) 68 | Am I runtime hooked: \(amIRuntimeHooked) 69 | Am I tempered with: \(IOSSecuritySuite.amITampered([.bundleID("biz.securing.FrameworkClientApp")]).result) 70 | Application executable file hash value: \(IOSSecuritySuite.getMachOFileHashValue() ?? "") 71 | IOSSecuritySuite executable file hash value: \(IOSSecuritySuite.getMachOFileHashValue(.custom("IOSSecuritySuite")) ?? "") 72 | Am I proxied: \(IOSSecuritySuite.amIProxied()) 73 | """ 74 | 75 | let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) 76 | alert.addAction(UIAlertAction(title: "Dismiss", style: .default)) 77 | 78 | print("FailMessage: \(message)") 79 | present(alert, animated: false) 80 | 81 | let checks = IOSSecuritySuite.amIJailbrokenWithFailedChecks() 82 | print("The failed checks are: \(checks)") 83 | 84 | #if arch(arm64) 85 | print("Loaded libs: \(IOSSecuritySuite.findLoadedDylibs() ?? [])") 86 | #endif 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /IOSSecuritySuite.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "IOSSecuritySuite" 3 | s.version = "1.9.1" 4 | s.summary = "iOS platform security & anti-tampering Swift library" 5 | s.homepage = "https://github.com/securing/IOSSecuritySuite" 6 | s.license = "bsd-2-clause" 7 | s.author = "Wojciech Reguła" 8 | s.social_media_url = "https://twitter.com/_r3ggi" 9 | s.platform = :ios, "10.0" 10 | s.ios.frameworks = 'UIKit', 'Foundation' 11 | s.source = { :git => "https://github.com/securing/IOSSecuritySuite.git", :tag => "#{s.version}" } 12 | s.source_files = "IOSSecuritySuite/*.swift" 13 | s.swift_version = '5.0' 14 | s.requires_arc = true 15 | s.pod_target_xcconfig = { 'SWIFT_VERSION' => '5.0' } 16 | end 17 | -------------------------------------------------------------------------------- /IOSSecuritySuite.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 703F74E222704E0F000635D8 /* ReverseEngineeringToolsChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 703F74E122704E0F000635D8 /* ReverseEngineeringToolsChecker.swift */; }; 11 | 706B0E2A226F445D0059AEA9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 706B0E29226F445D0059AEA9 /* AppDelegate.swift */; }; 12 | 706B0E2C226F445D0059AEA9 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 706B0E2B226F445D0059AEA9 /* ViewController.swift */; }; 13 | 706B0E2F226F445D0059AEA9 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 706B0E2D226F445D0059AEA9 /* Main.storyboard */; }; 14 | 706B0E31226F445F0059AEA9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 706B0E30226F445F0059AEA9 /* Assets.xcassets */; }; 15 | 706B0E34226F445F0059AEA9 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 706B0E32226F445F0059AEA9 /* LaunchScreen.storyboard */; }; 16 | 706B0E39226F44830059AEA9 /* IOSSecuritySuite.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 70B0BBBC226F3A4D000CFB39 /* IOSSecuritySuite.framework */; }; 17 | 706B0E3B226F59AA0059AEA9 /* IOSSecuritySuite.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 70B0BBBC226F3A4D000CFB39 /* IOSSecuritySuite.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 18 | 70B0BBC1226F3A4D000CFB39 /* IOSSecuritySuite.h in Headers */ = {isa = PBXBuildFile; fileRef = 70B0BBBF226F3A4D000CFB39 /* IOSSecuritySuite.h */; settings = {ATTRIBUTES = (Public, ); }; }; 19 | 70B0BBC9226F3A74000CFB39 /* DebuggerChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70B0BBC8226F3A74000CFB39 /* DebuggerChecker.swift */; }; 20 | 70B0BBCB226F3A86000CFB39 /* JailbreakChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70B0BBCA226F3A86000CFB39 /* JailbreakChecker.swift */; }; 21 | 70B0BBCD226F3A90000CFB39 /* EmulatorChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70B0BBCC226F3A90000CFB39 /* EmulatorChecker.swift */; }; 22 | 70B0BBCF226F3AB2000CFB39 /* IOSSecuritySuite.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70B0BBCE226F3AB2000CFB39 /* IOSSecuritySuite.swift */; }; 23 | 70B8E16C257E528D00917097 /* ProxyChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 70B8E16B257E528D00917097 /* ProxyChecker.swift */; }; 24 | 7A12583D24EFA8D40071460D /* IntegrityChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A12583C24EFA8D40071460D /* IntegrityChecker.swift */; }; 25 | A90FD5FE24528925007212BF /* MSHookFunctionChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = A90FD5FD24528925007212BF /* MSHookFunctionChecker.swift */; }; 26 | A90FD60024528A94007212BF /* FishHookChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = A90FD5FF24528A94007212BF /* FishHookChecker.swift */; }; 27 | A90FD60224528FD1007212BF /* RuntimeHookChecker.swift in Sources */ = {isa = PBXBuildFile; fileRef = A90FD60124528FD1007212BF /* RuntimeHookChecker.swift */; }; 28 | FF2FF25D2499F7590050D02F /* FishHook.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF2FF25C2499F7590050D02F /* FishHook.swift */; }; 29 | /* End PBXBuildFile section */ 30 | 31 | /* Begin PBXCopyFilesBuildPhase section */ 32 | 706B0E3A226F59900059AEA9 /* CopyFiles */ = { 33 | isa = PBXCopyFilesBuildPhase; 34 | buildActionMask = 2147483647; 35 | dstPath = ""; 36 | dstSubfolderSpec = 10; 37 | files = ( 38 | 706B0E3B226F59AA0059AEA9 /* IOSSecuritySuite.framework in CopyFiles */, 39 | ); 40 | runOnlyForDeploymentPostprocessing = 0; 41 | }; 42 | /* End PBXCopyFilesBuildPhase section */ 43 | 44 | /* Begin PBXFileReference section */ 45 | 703F74E122704E0F000635D8 /* ReverseEngineeringToolsChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReverseEngineeringToolsChecker.swift; sourceTree = ""; }; 46 | 706B0E27226F445D0059AEA9 /* FrameworkClientApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FrameworkClientApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 47 | 706B0E29226F445D0059AEA9 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 48 | 706B0E2B226F445D0059AEA9 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 49 | 706B0E2E226F445D0059AEA9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 50 | 706B0E30226F445F0059AEA9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 51 | 706B0E33226F445F0059AEA9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 52 | 706B0E35226F445F0059AEA9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 53 | 70B0BBBC226F3A4D000CFB39 /* IOSSecuritySuite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = IOSSecuritySuite.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 54 | 70B0BBBF226F3A4D000CFB39 /* IOSSecuritySuite.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IOSSecuritySuite.h; sourceTree = ""; }; 55 | 70B0BBC0226F3A4D000CFB39 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 56 | 70B0BBC8226F3A74000CFB39 /* DebuggerChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebuggerChecker.swift; sourceTree = ""; }; 57 | 70B0BBCA226F3A86000CFB39 /* JailbreakChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JailbreakChecker.swift; sourceTree = ""; }; 58 | 70B0BBCC226F3A90000CFB39 /* EmulatorChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmulatorChecker.swift; sourceTree = ""; }; 59 | 70B0BBCE226F3AB2000CFB39 /* IOSSecuritySuite.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IOSSecuritySuite.swift; sourceTree = ""; }; 60 | 70B8E16B257E528D00917097 /* ProxyChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyChecker.swift; sourceTree = ""; }; 61 | 70DDFD5A2284AA9600D95EE7 /* FrameworkClientApp.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = FrameworkClientApp.entitlements; sourceTree = ""; }; 62 | 7A12583C24EFA8D40071460D /* IntegrityChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntegrityChecker.swift; sourceTree = ""; }; 63 | A90FD5FD24528925007212BF /* MSHookFunctionChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSHookFunctionChecker.swift; sourceTree = ""; }; 64 | A90FD5FF24528A94007212BF /* FishHookChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FishHookChecker.swift; sourceTree = ""; }; 65 | A90FD60124528FD1007212BF /* RuntimeHookChecker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RuntimeHookChecker.swift; sourceTree = ""; }; 66 | FF2FF25C2499F7590050D02F /* FishHook.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FishHook.swift; sourceTree = ""; }; 67 | /* End PBXFileReference section */ 68 | 69 | /* Begin PBXFrameworksBuildPhase section */ 70 | 706B0E24226F445D0059AEA9 /* Frameworks */ = { 71 | isa = PBXFrameworksBuildPhase; 72 | buildActionMask = 2147483647; 73 | files = ( 74 | 706B0E39226F44830059AEA9 /* IOSSecuritySuite.framework in Frameworks */, 75 | ); 76 | runOnlyForDeploymentPostprocessing = 0; 77 | }; 78 | 70B0BBB9226F3A4D000CFB39 /* Frameworks */ = { 79 | isa = PBXFrameworksBuildPhase; 80 | buildActionMask = 2147483647; 81 | files = ( 82 | ); 83 | runOnlyForDeploymentPostprocessing = 0; 84 | }; 85 | /* End PBXFrameworksBuildPhase section */ 86 | 87 | /* Begin PBXGroup section */ 88 | 706B0E28226F445D0059AEA9 /* FrameworkClientApp */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | FF2FF25C2499F7590050D02F /* FishHook.swift */, 92 | 70DDFD5A2284AA9600D95EE7 /* FrameworkClientApp.entitlements */, 93 | 706B0E29226F445D0059AEA9 /* AppDelegate.swift */, 94 | 706B0E2B226F445D0059AEA9 /* ViewController.swift */, 95 | 706B0E2D226F445D0059AEA9 /* Main.storyboard */, 96 | 706B0E30226F445F0059AEA9 /* Assets.xcassets */, 97 | 706B0E32226F445F0059AEA9 /* LaunchScreen.storyboard */, 98 | 706B0E35226F445F0059AEA9 /* Info.plist */, 99 | ); 100 | path = FrameworkClientApp; 101 | sourceTree = ""; 102 | }; 103 | 70B0BBB2226F3A4D000CFB39 = { 104 | isa = PBXGroup; 105 | children = ( 106 | 70B0BBBE226F3A4D000CFB39 /* IOSSecuritySuite */, 107 | 706B0E28226F445D0059AEA9 /* FrameworkClientApp */, 108 | 70B0BBBD226F3A4D000CFB39 /* Products */, 109 | 70F861BF226F3F2F00B01041 /* Frameworks */, 110 | ); 111 | sourceTree = ""; 112 | }; 113 | 70B0BBBD226F3A4D000CFB39 /* Products */ = { 114 | isa = PBXGroup; 115 | children = ( 116 | 70B0BBBC226F3A4D000CFB39 /* IOSSecuritySuite.framework */, 117 | 706B0E27226F445D0059AEA9 /* FrameworkClientApp.app */, 118 | ); 119 | name = Products; 120 | sourceTree = ""; 121 | }; 122 | 70B0BBBE226F3A4D000CFB39 /* IOSSecuritySuite */ = { 123 | isa = PBXGroup; 124 | children = ( 125 | 70B0BBBF226F3A4D000CFB39 /* IOSSecuritySuite.h */, 126 | 70B0BBC0226F3A4D000CFB39 /* Info.plist */, 127 | 70B0BBC8226F3A74000CFB39 /* DebuggerChecker.swift */, 128 | 70B0BBCA226F3A86000CFB39 /* JailbreakChecker.swift */, 129 | 70B0BBCC226F3A90000CFB39 /* EmulatorChecker.swift */, 130 | 7A12583C24EFA8D40071460D /* IntegrityChecker.swift */, 131 | 70B0BBCE226F3AB2000CFB39 /* IOSSecuritySuite.swift */, 132 | 703F74E122704E0F000635D8 /* ReverseEngineeringToolsChecker.swift */, 133 | A90FD5FD24528925007212BF /* MSHookFunctionChecker.swift */, 134 | A90FD5FF24528A94007212BF /* FishHookChecker.swift */, 135 | A90FD60124528FD1007212BF /* RuntimeHookChecker.swift */, 136 | 70B8E16B257E528D00917097 /* ProxyChecker.swift */, 137 | ); 138 | path = IOSSecuritySuite; 139 | sourceTree = ""; 140 | }; 141 | 70F861BF226F3F2F00B01041 /* Frameworks */ = { 142 | isa = PBXGroup; 143 | children = ( 144 | ); 145 | name = Frameworks; 146 | sourceTree = ""; 147 | }; 148 | /* End PBXGroup section */ 149 | 150 | /* Begin PBXHeadersBuildPhase section */ 151 | 70B0BBB7226F3A4D000CFB39 /* Headers */ = { 152 | isa = PBXHeadersBuildPhase; 153 | buildActionMask = 2147483647; 154 | files = ( 155 | 70B0BBC1226F3A4D000CFB39 /* IOSSecuritySuite.h in Headers */, 156 | ); 157 | runOnlyForDeploymentPostprocessing = 0; 158 | }; 159 | /* End PBXHeadersBuildPhase section */ 160 | 161 | /* Begin PBXNativeTarget section */ 162 | 706B0E26226F445D0059AEA9 /* FrameworkClientApp */ = { 163 | isa = PBXNativeTarget; 164 | buildConfigurationList = 706B0E38226F445F0059AEA9 /* Build configuration list for PBXNativeTarget "FrameworkClientApp" */; 165 | buildPhases = ( 166 | 706B0E23226F445D0059AEA9 /* Sources */, 167 | 706B0E24226F445D0059AEA9 /* Frameworks */, 168 | 706B0E25226F445D0059AEA9 /* Resources */, 169 | 706B0E3A226F59900059AEA9 /* CopyFiles */, 170 | ); 171 | buildRules = ( 172 | ); 173 | dependencies = ( 174 | ); 175 | name = FrameworkClientApp; 176 | productName = FrameworkClientApp; 177 | productReference = 706B0E27226F445D0059AEA9 /* FrameworkClientApp.app */; 178 | productType = "com.apple.product-type.application"; 179 | }; 180 | 70B0BBBB226F3A4D000CFB39 /* IOSSecuritySuite */ = { 181 | isa = PBXNativeTarget; 182 | buildConfigurationList = 70B0BBC4226F3A4D000CFB39 /* Build configuration list for PBXNativeTarget "IOSSecuritySuite" */; 183 | buildPhases = ( 184 | 70B0BBB7226F3A4D000CFB39 /* Headers */, 185 | 70B0BBB8226F3A4D000CFB39 /* Sources */, 186 | 70B0BBB9226F3A4D000CFB39 /* Frameworks */, 187 | 70B0BBBA226F3A4D000CFB39 /* Resources */, 188 | 70B0BBC7226F3A5F000CFB39 /* SwiftLint */, 189 | ); 190 | buildRules = ( 191 | ); 192 | dependencies = ( 193 | ); 194 | name = IOSSecuritySuite; 195 | productName = IOSSecuritySuite; 196 | productReference = 70B0BBBC226F3A4D000CFB39 /* IOSSecuritySuite.framework */; 197 | productType = "com.apple.product-type.framework"; 198 | }; 199 | /* End PBXNativeTarget section */ 200 | 201 | /* Begin PBXProject section */ 202 | 70B0BBB3226F3A4D000CFB39 /* Project object */ = { 203 | isa = PBXProject; 204 | attributes = { 205 | LastSwiftUpdateCheck = 1010; 206 | LastUpgradeCheck = 1010; 207 | ORGANIZATIONNAME = wregula; 208 | TargetAttributes = { 209 | 706B0E26226F445D0059AEA9 = { 210 | CreatedOnToolsVersion = 10.1; 211 | LastSwiftMigration = 1130; 212 | SystemCapabilities = { 213 | com.apple.Multipath = { 214 | enabled = 0; 215 | }; 216 | }; 217 | }; 218 | 70B0BBBB226F3A4D000CFB39 = { 219 | CreatedOnToolsVersion = 10.1; 220 | LastSwiftMigration = 1110; 221 | }; 222 | }; 223 | }; 224 | buildConfigurationList = 70B0BBB6226F3A4D000CFB39 /* Build configuration list for PBXProject "IOSSecuritySuite" */; 225 | compatibilityVersion = "Xcode 9.3"; 226 | developmentRegion = en; 227 | hasScannedForEncodings = 0; 228 | knownRegions = ( 229 | en, 230 | Base, 231 | ); 232 | mainGroup = 70B0BBB2226F3A4D000CFB39; 233 | productRefGroup = 70B0BBBD226F3A4D000CFB39 /* Products */; 234 | projectDirPath = ""; 235 | projectRoot = ""; 236 | targets = ( 237 | 70B0BBBB226F3A4D000CFB39 /* IOSSecuritySuite */, 238 | 706B0E26226F445D0059AEA9 /* FrameworkClientApp */, 239 | ); 240 | }; 241 | /* End PBXProject section */ 242 | 243 | /* Begin PBXResourcesBuildPhase section */ 244 | 706B0E25226F445D0059AEA9 /* Resources */ = { 245 | isa = PBXResourcesBuildPhase; 246 | buildActionMask = 2147483647; 247 | files = ( 248 | 706B0E34226F445F0059AEA9 /* LaunchScreen.storyboard in Resources */, 249 | 706B0E31226F445F0059AEA9 /* Assets.xcassets in Resources */, 250 | 706B0E2F226F445D0059AEA9 /* Main.storyboard in Resources */, 251 | ); 252 | runOnlyForDeploymentPostprocessing = 0; 253 | }; 254 | 70B0BBBA226F3A4D000CFB39 /* Resources */ = { 255 | isa = PBXResourcesBuildPhase; 256 | buildActionMask = 2147483647; 257 | files = ( 258 | ); 259 | runOnlyForDeploymentPostprocessing = 0; 260 | }; 261 | /* End PBXResourcesBuildPhase section */ 262 | 263 | /* Begin PBXShellScriptBuildPhase section */ 264 | 70B0BBC7226F3A5F000CFB39 /* SwiftLint */ = { 265 | isa = PBXShellScriptBuildPhase; 266 | buildActionMask = 2147483647; 267 | files = ( 268 | ); 269 | inputFileListPaths = ( 270 | ); 271 | inputPaths = ( 272 | ); 273 | name = SwiftLint; 274 | outputFileListPaths = ( 275 | ); 276 | outputPaths = ( 277 | ); 278 | runOnlyForDeploymentPostprocessing = 0; 279 | shellPath = /bin/sh; 280 | shellScript = "if which swiftlint >/dev/null; then\nswiftlint\nelse\necho \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; 281 | }; 282 | /* End PBXShellScriptBuildPhase section */ 283 | 284 | /* Begin PBXSourcesBuildPhase section */ 285 | 706B0E23226F445D0059AEA9 /* Sources */ = { 286 | isa = PBXSourcesBuildPhase; 287 | buildActionMask = 2147483647; 288 | files = ( 289 | FF2FF25D2499F7590050D02F /* FishHook.swift in Sources */, 290 | 706B0E2C226F445D0059AEA9 /* ViewController.swift in Sources */, 291 | 706B0E2A226F445D0059AEA9 /* AppDelegate.swift in Sources */, 292 | ); 293 | runOnlyForDeploymentPostprocessing = 0; 294 | }; 295 | 70B0BBB8226F3A4D000CFB39 /* Sources */ = { 296 | isa = PBXSourcesBuildPhase; 297 | buildActionMask = 2147483647; 298 | files = ( 299 | 70B0BBC9226F3A74000CFB39 /* DebuggerChecker.swift in Sources */, 300 | 70B0BBCD226F3A90000CFB39 /* EmulatorChecker.swift in Sources */, 301 | 70B8E16C257E528D00917097 /* ProxyChecker.swift in Sources */, 302 | A90FD60224528FD1007212BF /* RuntimeHookChecker.swift in Sources */, 303 | A90FD5FE24528925007212BF /* MSHookFunctionChecker.swift in Sources */, 304 | 70B0BBCF226F3AB2000CFB39 /* IOSSecuritySuite.swift in Sources */, 305 | 703F74E222704E0F000635D8 /* ReverseEngineeringToolsChecker.swift in Sources */, 306 | A90FD60024528A94007212BF /* FishHookChecker.swift in Sources */, 307 | 7A12583D24EFA8D40071460D /* IntegrityChecker.swift in Sources */, 308 | 70B0BBCB226F3A86000CFB39 /* JailbreakChecker.swift in Sources */, 309 | ); 310 | runOnlyForDeploymentPostprocessing = 0; 311 | }; 312 | /* End PBXSourcesBuildPhase section */ 313 | 314 | /* Begin PBXVariantGroup section */ 315 | 706B0E2D226F445D0059AEA9 /* Main.storyboard */ = { 316 | isa = PBXVariantGroup; 317 | children = ( 318 | 706B0E2E226F445D0059AEA9 /* Base */, 319 | ); 320 | name = Main.storyboard; 321 | sourceTree = ""; 322 | }; 323 | 706B0E32226F445F0059AEA9 /* LaunchScreen.storyboard */ = { 324 | isa = PBXVariantGroup; 325 | children = ( 326 | 706B0E33226F445F0059AEA9 /* Base */, 327 | ); 328 | name = LaunchScreen.storyboard; 329 | sourceTree = ""; 330 | }; 331 | /* End PBXVariantGroup section */ 332 | 333 | /* Begin XCBuildConfiguration section */ 334 | 706B0E36226F445F0059AEA9 /* Debug */ = { 335 | isa = XCBuildConfiguration; 336 | buildSettings = { 337 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 338 | CODE_SIGN_IDENTITY = "Apple Development"; 339 | CODE_SIGN_STYLE = Automatic; 340 | DEVELOPMENT_TEAM = 4HTEC659ZR; 341 | INFOPLIST_FILE = FrameworkClientApp/Info.plist; 342 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 343 | LD_RUNPATH_SEARCH_PATHS = ( 344 | "$(inherited)", 345 | "@executable_path/Frameworks", 346 | ); 347 | PRODUCT_BUNDLE_IDENTIFIER = biz.securing.FrameworkClientApp; 348 | PRODUCT_NAME = "$(TARGET_NAME)"; 349 | PROVISIONING_PROFILE_SPECIFIER = ""; 350 | SWIFT_VERSION = 5.0; 351 | TARGETED_DEVICE_FAMILY = "1,2"; 352 | }; 353 | name = Debug; 354 | }; 355 | 706B0E37226F445F0059AEA9 /* Release */ = { 356 | isa = XCBuildConfiguration; 357 | buildSettings = { 358 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 359 | CODE_SIGN_IDENTITY = "Apple Development"; 360 | CODE_SIGN_STYLE = Automatic; 361 | DEVELOPMENT_TEAM = 4HTEC659ZR; 362 | INFOPLIST_FILE = FrameworkClientApp/Info.plist; 363 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 364 | LD_RUNPATH_SEARCH_PATHS = ( 365 | "$(inherited)", 366 | "@executable_path/Frameworks", 367 | ); 368 | PRODUCT_BUNDLE_IDENTIFIER = biz.securing.FrameworkClientApp; 369 | PRODUCT_NAME = "$(TARGET_NAME)"; 370 | PROVISIONING_PROFILE_SPECIFIER = ""; 371 | SWIFT_VERSION = 5.0; 372 | TARGETED_DEVICE_FAMILY = "1,2"; 373 | }; 374 | name = Release; 375 | }; 376 | 70B0BBC2226F3A4D000CFB39 /* Debug */ = { 377 | isa = XCBuildConfiguration; 378 | buildSettings = { 379 | ALWAYS_SEARCH_USER_PATHS = NO; 380 | CLANG_ANALYZER_NONNULL = YES; 381 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 382 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 383 | CLANG_CXX_LIBRARY = "libc++"; 384 | CLANG_ENABLE_MODULES = YES; 385 | CLANG_ENABLE_OBJC_ARC = YES; 386 | CLANG_ENABLE_OBJC_WEAK = YES; 387 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 388 | CLANG_WARN_BOOL_CONVERSION = YES; 389 | CLANG_WARN_COMMA = YES; 390 | CLANG_WARN_CONSTANT_CONVERSION = YES; 391 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 392 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 393 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 394 | CLANG_WARN_EMPTY_BODY = YES; 395 | CLANG_WARN_ENUM_CONVERSION = YES; 396 | CLANG_WARN_INFINITE_RECURSION = YES; 397 | CLANG_WARN_INT_CONVERSION = YES; 398 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 399 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 400 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 401 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 402 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 403 | CLANG_WARN_STRICT_PROTOTYPES = YES; 404 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 405 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 406 | CLANG_WARN_UNREACHABLE_CODE = YES; 407 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 408 | CODE_SIGN_IDENTITY = "iPhone Developer"; 409 | COPY_PHASE_STRIP = NO; 410 | CURRENT_PROJECT_VERSION = 1; 411 | DEBUG_INFORMATION_FORMAT = dwarf; 412 | ENABLE_STRICT_OBJC_MSGSEND = YES; 413 | ENABLE_TESTABILITY = YES; 414 | GCC_C_LANGUAGE_STANDARD = gnu11; 415 | GCC_DYNAMIC_NO_PIC = NO; 416 | GCC_NO_COMMON_BLOCKS = YES; 417 | GCC_OPTIMIZATION_LEVEL = 0; 418 | GCC_PREPROCESSOR_DEFINITIONS = ( 419 | "DEBUG=1", 420 | "$(inherited)", 421 | ); 422 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 423 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 424 | GCC_WARN_UNDECLARED_SELECTOR = YES; 425 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 426 | GCC_WARN_UNUSED_FUNCTION = YES; 427 | GCC_WARN_UNUSED_VARIABLE = YES; 428 | IPHONEOS_DEPLOYMENT_TARGET = 12.1; 429 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 430 | MTL_FAST_MATH = YES; 431 | ONLY_ACTIVE_ARCH = YES; 432 | SDKROOT = iphoneos; 433 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 434 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 435 | VERSIONING_SYSTEM = "apple-generic"; 436 | VERSION_INFO_PREFIX = ""; 437 | }; 438 | name = Debug; 439 | }; 440 | 70B0BBC3226F3A4D000CFB39 /* Release */ = { 441 | isa = XCBuildConfiguration; 442 | buildSettings = { 443 | ALWAYS_SEARCH_USER_PATHS = NO; 444 | CLANG_ANALYZER_NONNULL = YES; 445 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 446 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 447 | CLANG_CXX_LIBRARY = "libc++"; 448 | CLANG_ENABLE_MODULES = YES; 449 | CLANG_ENABLE_OBJC_ARC = YES; 450 | CLANG_ENABLE_OBJC_WEAK = YES; 451 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 452 | CLANG_WARN_BOOL_CONVERSION = YES; 453 | CLANG_WARN_COMMA = YES; 454 | CLANG_WARN_CONSTANT_CONVERSION = YES; 455 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 456 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 457 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 458 | CLANG_WARN_EMPTY_BODY = YES; 459 | CLANG_WARN_ENUM_CONVERSION = YES; 460 | CLANG_WARN_INFINITE_RECURSION = YES; 461 | CLANG_WARN_INT_CONVERSION = YES; 462 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 463 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 464 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 465 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 466 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 467 | CLANG_WARN_STRICT_PROTOTYPES = YES; 468 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 469 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 470 | CLANG_WARN_UNREACHABLE_CODE = YES; 471 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 472 | CODE_SIGN_IDENTITY = "iPhone Developer"; 473 | COPY_PHASE_STRIP = NO; 474 | CURRENT_PROJECT_VERSION = 1; 475 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 476 | ENABLE_NS_ASSERTIONS = NO; 477 | ENABLE_STRICT_OBJC_MSGSEND = YES; 478 | GCC_C_LANGUAGE_STANDARD = gnu11; 479 | GCC_NO_COMMON_BLOCKS = YES; 480 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 481 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 482 | GCC_WARN_UNDECLARED_SELECTOR = YES; 483 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 484 | GCC_WARN_UNUSED_FUNCTION = YES; 485 | GCC_WARN_UNUSED_VARIABLE = YES; 486 | IPHONEOS_DEPLOYMENT_TARGET = 12.1; 487 | MTL_ENABLE_DEBUG_INFO = NO; 488 | MTL_FAST_MATH = YES; 489 | SDKROOT = iphoneos; 490 | SWIFT_COMPILATION_MODE = wholemodule; 491 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 492 | VALIDATE_PRODUCT = YES; 493 | VERSIONING_SYSTEM = "apple-generic"; 494 | VERSION_INFO_PREFIX = ""; 495 | }; 496 | name = Release; 497 | }; 498 | 70B0BBC5226F3A4D000CFB39 /* Debug */ = { 499 | isa = XCBuildConfiguration; 500 | buildSettings = { 501 | CLANG_ENABLE_MODULES = YES; 502 | CODE_SIGN_IDENTITY = "Apple Development"; 503 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; 504 | CODE_SIGN_STYLE = Automatic; 505 | DEFINES_MODULE = YES; 506 | DEVELOPMENT_TEAM = ""; 507 | DYLIB_COMPATIBILITY_VERSION = 1; 508 | DYLIB_CURRENT_VERSION = 1; 509 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 510 | INFOPLIST_FILE = IOSSecuritySuite/Info.plist; 511 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 512 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 513 | LD_RUNPATH_SEARCH_PATHS = ( 514 | "$(inherited)", 515 | "@executable_path/Frameworks", 516 | "@loader_path/Frameworks", 517 | ); 518 | PRODUCT_BUNDLE_IDENTIFIER = biz.securing.IOSSecuritySuite; 519 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 520 | PROVISIONING_PROFILE_SPECIFIER = ""; 521 | "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; 522 | SKIP_INSTALL = YES; 523 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 524 | SWIFT_VERSION = 5.0; 525 | TARGETED_DEVICE_FAMILY = "1,2"; 526 | }; 527 | name = Debug; 528 | }; 529 | 70B0BBC6226F3A4D000CFB39 /* Release */ = { 530 | isa = XCBuildConfiguration; 531 | buildSettings = { 532 | CLANG_ENABLE_MODULES = YES; 533 | CODE_SIGN_IDENTITY = "Apple Development"; 534 | "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; 535 | CODE_SIGN_STYLE = Automatic; 536 | DEFINES_MODULE = YES; 537 | DEVELOPMENT_TEAM = ""; 538 | DYLIB_COMPATIBILITY_VERSION = 1; 539 | DYLIB_CURRENT_VERSION = 1; 540 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 541 | INFOPLIST_FILE = IOSSecuritySuite/Info.plist; 542 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 543 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 544 | LD_RUNPATH_SEARCH_PATHS = ( 545 | "$(inherited)", 546 | "@executable_path/Frameworks", 547 | "@loader_path/Frameworks", 548 | ); 549 | PRODUCT_BUNDLE_IDENTIFIER = biz.securing.IOSSecuritySuite; 550 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 551 | PROVISIONING_PROFILE_SPECIFIER = ""; 552 | "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; 553 | SKIP_INSTALL = YES; 554 | SWIFT_VERSION = 5.0; 555 | TARGETED_DEVICE_FAMILY = "1,2"; 556 | }; 557 | name = Release; 558 | }; 559 | /* End XCBuildConfiguration section */ 560 | 561 | /* Begin XCConfigurationList section */ 562 | 706B0E38226F445F0059AEA9 /* Build configuration list for PBXNativeTarget "FrameworkClientApp" */ = { 563 | isa = XCConfigurationList; 564 | buildConfigurations = ( 565 | 706B0E36226F445F0059AEA9 /* Debug */, 566 | 706B0E37226F445F0059AEA9 /* Release */, 567 | ); 568 | defaultConfigurationIsVisible = 0; 569 | defaultConfigurationName = Release; 570 | }; 571 | 70B0BBB6226F3A4D000CFB39 /* Build configuration list for PBXProject "IOSSecuritySuite" */ = { 572 | isa = XCConfigurationList; 573 | buildConfigurations = ( 574 | 70B0BBC2226F3A4D000CFB39 /* Debug */, 575 | 70B0BBC3226F3A4D000CFB39 /* Release */, 576 | ); 577 | defaultConfigurationIsVisible = 0; 578 | defaultConfigurationName = Release; 579 | }; 580 | 70B0BBC4226F3A4D000CFB39 /* Build configuration list for PBXNativeTarget "IOSSecuritySuite" */ = { 581 | isa = XCConfigurationList; 582 | buildConfigurations = ( 583 | 70B0BBC5226F3A4D000CFB39 /* Debug */, 584 | 70B0BBC6226F3A4D000CFB39 /* Release */, 585 | ); 586 | defaultConfigurationIsVisible = 0; 587 | defaultConfigurationName = Release; 588 | }; 589 | /* End XCConfigurationList section */ 590 | }; 591 | rootObject = 70B0BBB3226F3A4D000CFB39 /* Project object */; 592 | } 593 | -------------------------------------------------------------------------------- /IOSSecuritySuite.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /IOSSecuritySuite.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /IOSSecuritySuite.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /IOSSecuritySuite.xcodeproj/xcshareddata/xcschemes/FrameworkClientApp.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 69 | 71 | 77 | 78 | 79 | 80 | 82 | 83 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /IOSSecuritySuite.xcodeproj/xcshareddata/xcschemes/IOSSecuritySuite.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /IOSSecuritySuite/DebuggerChecker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DebuggerChecker.swift 3 | // IOSSecuritySuite 4 | // 5 | // Created by wregula on 23/04/2019. 6 | // Copyright © 2019 wregula. All rights reserved. 7 | // 8 | //swiftlint:disable line_length 9 | 10 | import Foundation 11 | 12 | internal class DebuggerChecker { 13 | 14 | // https://developer.apple.com/library/archive/qa/qa1361/_index.html 15 | static func amIDebugged() -> Bool { 16 | 17 | var kinfo = kinfo_proc() 18 | var mib: [Int32] = [CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()] 19 | var size = MemoryLayout.stride 20 | let sysctlRet = sysctl(&mib, UInt32(mib.count), &kinfo, &size, nil, 0) 21 | 22 | if sysctlRet != 0 { 23 | print("Error occured when calling sysctl(). The debugger check may not be reliable") 24 | } 25 | 26 | return (kinfo.kp_proc.p_flag & P_TRACED) != 0 27 | } 28 | 29 | static func denyDebugger() { 30 | 31 | // bind ptrace() 32 | let pointerToPtrace = UnsafeMutableRawPointer(bitPattern: -2) 33 | let ptracePtr = dlsym(pointerToPtrace, "ptrace") 34 | typealias PtraceType = @convention(c) (CInt, pid_t, CInt, CInt) -> CInt 35 | let ptrace = unsafeBitCast(ptracePtr, to: PtraceType.self) 36 | 37 | // PT_DENY_ATTACH == 31 38 | let ptraceRet = ptrace(31, 0, 0, 0) 39 | 40 | if ptraceRet != 0 { 41 | print("Error occured when calling ptrace(). Denying debugger may not be reliable") 42 | } 43 | } 44 | 45 | #if arch(arm64) 46 | static func hasBreakpointAt(_ functionAddr: UnsafeRawPointer, functionSize: vm_size_t?) -> Bool { 47 | let funcAddr = vm_address_t(UInt(bitPattern: functionAddr)) 48 | 49 | var vmStart: vm_address_t = funcAddr 50 | var vmSize: vm_size_t = 0 51 | let vmRegionInfo = UnsafeMutablePointer.allocate(capacity: MemoryLayout.size/4) 52 | defer { 53 | vmRegionInfo.deallocate() 54 | } 55 | var vmRegionInfoCount: mach_msg_type_number_t = mach_msg_type_number_t(VM_REGION_BASIC_INFO_64) 56 | var objectName: mach_port_t = 0 57 | 58 | let ret = vm_region_64(mach_task_self_, &vmStart, &vmSize, VM_REGION_BASIC_INFO_64, vmRegionInfo, &vmRegionInfoCount, &objectName) 59 | if ret != KERN_SUCCESS { 60 | return false 61 | } 62 | 63 | let vmRegion = vmRegionInfo.withMemoryRebound(to: vm_region_basic_info_64.self, capacity: 1, { $0 }) 64 | 65 | if vmRegion.pointee.protection == (VM_PROT_READ | VM_PROT_EXECUTE) { 66 | let armBreakpointOpcode = 0xe7ffdefe 67 | let arm64BreakpointOpcode = 0xd4200000 68 | let instructionBegin = functionAddr.bindMemory(to: UInt32.self, capacity: 1) 69 | var judgeSize = (vmSize - (funcAddr - vmStart)) 70 | if let size = functionSize, size < judgeSize { 71 | judgeSize = size 72 | } 73 | 74 | for valueToOffset in 0..<(judgeSize / 4) { 75 | if (instructionBegin.advanced(by: Int(valueToOffset)).pointee == armBreakpointOpcode) || (instructionBegin.advanced(by: Int(valueToOffset)).pointee == arm64BreakpointOpcode) { 76 | return true 77 | } 78 | } 79 | } 80 | 81 | return false 82 | } 83 | 84 | static func hasWatchpoint() -> Bool { 85 | var threads: thread_act_array_t? 86 | var threadCount: mach_msg_type_number_t = 0 87 | var hasWatchpoint = false 88 | 89 | if (task_threads(mach_task_self_, &threads, &threadCount) == KERN_SUCCESS) { 90 | var threadStat = arm_debug_state64_t() 91 | let capacity = MemoryLayout.size / MemoryLayout.size 92 | let threadStatPointer = withUnsafeMutablePointer(to: &threadStat, { $0.withMemoryRebound(to: natural_t.self, capacity: capacity, { $0 }) }) 93 | var count = mach_msg_type_number_t(MemoryLayout.size / MemoryLayout.size) 94 | 95 | for threadIndex in 0...size))) 102 | } 103 | 104 | return hasWatchpoint 105 | } 106 | #endif 107 | 108 | } 109 | -------------------------------------------------------------------------------- /IOSSecuritySuite/EmulatorChecker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EmulatorChecker.swift 3 | // IOSSecuritySuite 4 | // 5 | // Created by wregula on 23/04/2019. 6 | // Copyright © 2019 wregula. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | internal class EmulatorChecker { 12 | 13 | static func amIRunInEmulator() -> Bool { 14 | 15 | return checkCompile() || checkRuntime() 16 | } 17 | 18 | private static func checkRuntime() -> Bool { 19 | 20 | return ProcessInfo().environment["SIMULATOR_DEVICE_NAME"] != nil 21 | } 22 | 23 | private static func checkCompile() -> Bool { 24 | 25 | #if targetEnvironment(simulator) 26 | return true 27 | #else 28 | return false 29 | #endif 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /IOSSecuritySuite/FishHookChecker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FishHookChecker.swift 3 | // IOSSecuritySuite 4 | // 5 | // Created by jintao on 2020/4/24. 6 | // Copyright © 2020 wregula. All rights reserved. 7 | // https://github.com/TannerJin/anti-fishhook 8 | // swiftlint:disable trailing_whitespace control_statement line_length cyclomatic_complexity type_body_length function_body_length 9 | import Foundation 10 | import MachO 11 | 12 | /* 13 | Lazy_Symbol_Ptr: 14 | 15 | call symbol2 16 | | 17 | | 18 | | stubs(TEXT) 19 | | *--------------* stub_symbol: 20 | | | stub_symbol1 | ldr x16 ptr (ptr = pointer of lazy_symbol_ptr) 21 | | | | br x16 22 | *---> stub_symbol2 | 23 | | ... | 24 | *--------------* 25 | 26 | 27 | lazy_symbol_ptr(DATA) stub_helper(TEXT) 28 | *--------------* *---------------------------* 29 | | ptr1 | | br dyld_stub_binder | <-------------------* 30 | | ptr2 ---------* | symbol_binder_code_1 | | 31 | | ptr3 | *-------------------> symbol_binder_code_2 | | 32 | | ... | | ... | | 33 | *--------------* *---------------------------* | 34 | | 35 | symbol_binder_code: | 36 | ldr w16, #8(.byte) | 37 | b br_dyld_stub_binder ---* 38 | .byte 39 | 40 | 41 | .byte of the symbol is offset from beginning of lazy_binding_info to beginning of symbol_info 42 | 43 | lazy_binding_info(LINKEDIT -> DYLD_INFO -> LazyBindingInfo) 44 | *-----------------* 45 | | symbol_info_1 | symbol_info: 46 | | symbol_info_2 | bind_opcode_done 47 | | symbol_info_3 | bind_opcode_set_segment_and_offset_uleb 48 | | ... | uleb128 49 | *-----------------* BIND_OPCODE_SET_DYLIB_ORDINAL_IMM 50 | BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM 51 | **SymbolName** 52 | bind_opcode_do_bind 53 | 54 | 55 | 56 | The `denyFishHook` will look for code of `symbol_binder_code` of the symbol, and then make `lazy_symbol_ptr` of the symbol pointee to it 57 | 58 | Non_Lazy_Symbol_Ptr: 59 | wait to do based on export_info and binding_info 60 | */ 61 | 62 | #if arch(arm64) 63 | @inline(__always) 64 | private func readUleb128(ptr: inout UnsafeMutablePointer, end: UnsafeMutablePointer) -> UInt64 { 65 | var result: UInt64 = 0 66 | var bit = 0 67 | var readNext = true 68 | 69 | repeat { 70 | if ptr == end { 71 | assert(false, "malformed uleb128") 72 | } 73 | let slice = UInt64(ptr.pointee & 0x7f) 74 | if bit > 63 { 75 | assert(false, "uleb128 too big for uint64") 76 | } else { 77 | result |= (slice << bit) 78 | bit += 7 79 | } 80 | readNext = ((ptr.pointee & 0x80) >> 7) == 1 81 | ptr += 1 82 | } while (readNext) 83 | return result 84 | } 85 | 86 | @inline(__always) 87 | private func readSleb128(ptr: inout UnsafeMutablePointer, end: UnsafeMutablePointer) -> Int64 { 88 | var result: Int64 = 0 89 | var bit: Int = 0 90 | var byte: UInt8 91 | 92 | repeat { 93 | if (ptr == end) { 94 | assert(false, "malformed sleb128") 95 | } 96 | byte = ptr.pointee 97 | result |= (((Int64)(byte & 0x7f)) << bit) 98 | bit += 7 99 | ptr += 1 100 | } while (byte & 0x80) == 1 101 | 102 | // sign extend negative numbers 103 | if ( (byte & 0x40) != 0 ) { 104 | result |= -1 << bit 105 | } 106 | return result 107 | } 108 | 109 | internal class FishHookChecker { 110 | @inline(__always) 111 | static func denyFishHook(_ symbol: String) { 112 | var symbolAddress: UnsafeMutableRawPointer? 113 | 114 | for imgIndex in 0..<_dyld_image_count() { 115 | if let image = _dyld_get_image_header(imgIndex) { 116 | if symbolAddress == nil { 117 | _ = SymbolFound.lookSymbol(symbol, at: image, imageSlide: _dyld_get_image_vmaddr_slide(imgIndex), symbolAddress: &symbolAddress) 118 | } 119 | if let symbolPointer = symbolAddress { 120 | var oldMethod: UnsafeMutableRawPointer? 121 | FishHook.replaceSymbol(symbol, at: image, imageSlide: _dyld_get_image_vmaddr_slide(imgIndex), newMethod: symbolPointer, oldMethod: &oldMethod) 122 | } 123 | } 124 | } 125 | } 126 | 127 | @inline(__always) 128 | static func denyFishHook(_ symbol: String, at image: UnsafePointer, imageSlide slide: Int) { 129 | var symbolAddress: UnsafeMutableRawPointer? 130 | 131 | if SymbolFound.lookSymbol(symbol, at: image, imageSlide: slide, symbolAddress: &symbolAddress), let symbolPointer = symbolAddress { 132 | var oldMethod: UnsafeMutableRawPointer? 133 | FishHook.replaceSymbol(symbol, at: image, imageSlide: slide, newMethod: symbolPointer, oldMethod: &oldMethod) 134 | } 135 | } 136 | } 137 | 138 | // MARK: - SymbolFound 139 | internal class SymbolFound { 140 | static private let BindTypeThreadedRebase = 102 141 | 142 | @inline(__always) 143 | static func lookSymbol(_ symbol: String, at image: UnsafePointer, imageSlide slide: Int, symbolAddress: inout UnsafeMutableRawPointer?) -> Bool { 144 | // target cmd 145 | var linkeditCmd: UnsafeMutablePointer! 146 | var dyldInfoCmd: UnsafeMutablePointer! 147 | var allLoadDylds = [String]() 148 | 149 | guard var curCmdPointer = UnsafeMutableRawPointer(bitPattern: UInt(bitPattern: image)+UInt(MemoryLayout.size)) else { 150 | return false 151 | } 152 | // all cmd 153 | for _ in 0.. 0) { 184 | if let lazyBindInfoCmd = UnsafeMutablePointer(bitPattern: UInt(linkeditBase + UInt64(dyldInfoCmd.pointee.lazy_bind_off))), 185 | lookLazyBindSymbol(symbol, symbolAddr: &symbolAddress, lazyBindInfoCmd: lazyBindInfoCmd, lazyBindInfoSize: lazyBindSize, allLoadDylds: allLoadDylds) { 186 | return true 187 | } 188 | } 189 | 190 | // look by NonLazyBindInfo 191 | let bindSize = Int(dyldInfoCmd.pointee.bind_size) 192 | if (bindSize > 0) { 193 | if let bindCmd = UnsafeMutablePointer(bitPattern: UInt(linkeditBase + UInt64(dyldInfoCmd.pointee.bind_off))), 194 | lookBindSymbol(symbol, symbolAddr: &symbolAddress, bindInfoCmd: bindCmd, bindInfoSize: bindSize, allLoadDylds: allLoadDylds) { 195 | return true 196 | } 197 | } 198 | 199 | return false 200 | } 201 | 202 | // LazySymbolBindInfo 203 | @inline(__always) 204 | private static func lookLazyBindSymbol(_ symbol: String, symbolAddr: inout UnsafeMutableRawPointer?, lazyBindInfoCmd: UnsafeMutablePointer, lazyBindInfoSize: Int, allLoadDylds: [String]) -> Bool { 205 | var ptr = lazyBindInfoCmd 206 | let lazyBindingInfoEnd = lazyBindInfoCmd.advanced(by: Int(lazyBindInfoSize)) 207 | var ordinal: Int = -1 208 | var foundSymbol = false 209 | var addend = 0 210 | var type: Int32 = 0 211 | 212 | Label: while ptr < lazyBindingInfoEnd { 213 | let immediate = Int32(ptr.pointee) & BIND_IMMEDIATE_MASK 214 | let opcode = Int32(ptr.pointee) & BIND_OPCODE_MASK 215 | ptr += 1 216 | 217 | switch opcode { 218 | case BIND_OPCODE_DONE: 219 | continue 220 | // ORDINAL DYLIB 221 | case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: 222 | ordinal = Int(immediate) 223 | case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: 224 | ordinal = Int(readUleb128(ptr: &ptr, end: lazyBindingInfoEnd)) 225 | case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: 226 | if immediate == 0 { 227 | ordinal = 0 228 | } else { 229 | ordinal = Int(BIND_OPCODE_MASK | immediate) 230 | } 231 | // symbol 232 | case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: 233 | let symbolName = String(cString: ptr + 1) 234 | if (symbolName == symbol) { 235 | foundSymbol = true 236 | } 237 | while ptr.pointee != 0 { 238 | ptr += 1 239 | } 240 | ptr += 1 // '00' 241 | case BIND_OPCODE_SET_TYPE_IMM: 242 | type = immediate 243 | continue 244 | // sleb 245 | case BIND_OPCODE_SET_ADDEND_SLEB: 246 | addend = Int(readSleb128(ptr: &ptr, end: lazyBindingInfoEnd)) 247 | // uleb 248 | case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB, BIND_OPCODE_ADD_ADDR_ULEB: 249 | _ = readUleb128(ptr: &ptr, end: lazyBindingInfoEnd) 250 | // bind action 251 | case BIND_OPCODE_DO_BIND: 252 | if (foundSymbol) { 253 | break Label 254 | } else { 255 | continue 256 | } 257 | default: 258 | assert(false, "bad lazy bind opcode") 259 | return false 260 | } 261 | } 262 | 263 | assert(ordinal <= allLoadDylds.count) 264 | 265 | if (foundSymbol && ordinal >= 0 && allLoadDylds.count > 0), ordinal <= allLoadDylds.count, type != BindTypeThreadedRebase { 266 | let imageName = allLoadDylds[ordinal-1] 267 | var tmpSymbolAddress: UnsafeMutableRawPointer? 268 | if lookExportedSymbol(symbol, exportImageName: imageName, symbolAddress: &tmpSymbolAddress), let symbolPointer = tmpSymbolAddress { 269 | symbolAddr = symbolPointer + addend 270 | return true 271 | } 272 | } 273 | 274 | return false 275 | } 276 | 277 | // NonLazySymbolBindInfo 278 | @inline(__always) 279 | private static func lookBindSymbol(_ symbol: String, symbolAddr: inout UnsafeMutableRawPointer?, bindInfoCmd: UnsafeMutablePointer, bindInfoSize: Int, allLoadDylds: [String]) -> Bool { 280 | var ptr = bindInfoCmd 281 | let bindingInfoEnd = bindInfoCmd.advanced(by: Int(bindInfoSize)) 282 | var ordinal: Int = -1 283 | var foundSymbol = false 284 | var addend = 0 285 | var type: Int32 = 0 286 | 287 | Label: while ptr < bindingInfoEnd { 288 | let immediate = Int32(ptr.pointee) & BIND_IMMEDIATE_MASK 289 | let opcode = Int32(ptr.pointee) & BIND_OPCODE_MASK 290 | ptr += 1 291 | 292 | switch opcode { 293 | case BIND_OPCODE_DONE: 294 | break Label 295 | // ORDINAL DYLIB 296 | case BIND_OPCODE_SET_DYLIB_ORDINAL_IMM: 297 | ordinal = Int(immediate) 298 | case BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB: 299 | ordinal = Int(readUleb128(ptr: &ptr, end: bindingInfoEnd)) 300 | case BIND_OPCODE_SET_DYLIB_SPECIAL_IMM: 301 | if immediate == 0 { 302 | ordinal = 0 303 | } else { 304 | ordinal = Int(Int8(BIND_OPCODE_MASK | immediate)) 305 | } 306 | // symbol 307 | case BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM: 308 | let symbolName = String(cString: ptr + 1) 309 | if (symbolName == symbol) { 310 | foundSymbol = true 311 | } 312 | while ptr.pointee != 0 { 313 | ptr += 1 314 | } 315 | ptr += 1 // '00' 316 | case BIND_OPCODE_SET_TYPE_IMM: 317 | type = immediate 318 | continue 319 | // sleb 320 | case BIND_OPCODE_SET_ADDEND_SLEB: 321 | addend = Int(readSleb128(ptr: &ptr, end: bindingInfoEnd)) 322 | // uleb 323 | case BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB, BIND_OPCODE_ADD_ADDR_ULEB: 324 | _ = readUleb128(ptr: &ptr, end: bindingInfoEnd) 325 | // do bind action 326 | case BIND_OPCODE_DO_BIND, BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED: 327 | if (foundSymbol) { 328 | break Label 329 | } 330 | case BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB: 331 | if (foundSymbol) { 332 | break Label 333 | } else { 334 | _ = readUleb128(ptr: &ptr, end: bindingInfoEnd) 335 | } 336 | case BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB: 337 | if (foundSymbol) { 338 | break Label 339 | } else { 340 | _ = readUleb128(ptr: &ptr, end: bindingInfoEnd) // count 341 | _ = readUleb128(ptr: &ptr, end: bindingInfoEnd) // skip 342 | } 343 | case BIND_OPCODE_THREADED: 344 | switch immediate { 345 | case BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB: 346 | _ = readUleb128(ptr: &ptr, end: bindingInfoEnd) 347 | case BIND_SUBOPCODE_THREADED_APPLY: 348 | if (foundSymbol) { 349 | // ImageLoaderMachO::bindLocation case BIND_TYPE_THREADED_REBASE 350 | assert(false, "maybe bind_type is BIND_TYPE_THREADED_REBASE, don't handle") 351 | return false 352 | } 353 | continue Label 354 | default: 355 | assert(false, "bad bind subopcode") 356 | return false 357 | } 358 | default: 359 | assert(false, "bad bind opcode") 360 | return false 361 | } 362 | } 363 | 364 | assert(ordinal <= allLoadDylds.count) 365 | if (foundSymbol && ordinal >= 0 && allLoadDylds.count > 0), ordinal <= allLoadDylds.count, type != BindTypeThreadedRebase { 366 | let imageName = allLoadDylds[ordinal-1] 367 | var tmpSymbolAddress: UnsafeMutableRawPointer? 368 | if lookExportedSymbol(symbol, exportImageName: imageName, symbolAddress: &tmpSymbolAddress), let symbolPointer = tmpSymbolAddress { 369 | symbolAddr = symbolPointer + addend 370 | return true 371 | } 372 | } 373 | 374 | return false 375 | } 376 | 377 | // ExportSymbol 378 | @inline(__always) 379 | private static func lookExportedSymbol(_ symbol: String, exportImageName: String, symbolAddress: inout UnsafeMutableRawPointer?) -> Bool { 380 | var rpathImage: String? 381 | // @rpath 382 | if (exportImageName.contains("@rpath")) { 383 | rpathImage = exportImageName.components(separatedBy: "/").last 384 | } 385 | 386 | for index in 0..<_dyld_image_count() { 387 | // imageName 388 | let currentImageName = String(cString: _dyld_get_image_name(index)) 389 | if let tmpRpathImage = rpathImage { 390 | if (!currentImageName.contains(tmpRpathImage)) { 391 | continue 392 | } 393 | } else if (String(cString: _dyld_get_image_name(index)) != exportImageName) { 394 | continue 395 | } 396 | 397 | if let pointer = _lookExportedSymbol(symbol, image: _dyld_get_image_header(index), imageSlide: _dyld_get_image_vmaddr_slide(index)) { 398 | // found 399 | symbolAddress = UnsafeMutableRawPointer(mutating: pointer) 400 | return true 401 | } else { 402 | // not found, look at ReExport dylibs 403 | var allReExportDylibs = [String]() 404 | 405 | if let currentImage = _dyld_get_image_header(index), 406 | var curCmdPointer = UnsafeMutableRawPointer(bitPattern: UInt(bitPattern: currentImage)+UInt(MemoryLayout.size)) { 407 | 408 | for _ in 0.., imageSlide slide: Int) -> UnsafeMutableRawPointer? { 437 | // target cmd 438 | var linkeditCmd: UnsafeMutablePointer! 439 | var dyldInfoCmd: UnsafeMutablePointer! 440 | var exportCmd: UnsafeMutablePointer! 441 | 442 | guard var curCmdPointer = UnsafeMutableRawPointer(bitPattern: UInt(bitPattern: image)+UInt(MemoryLayout.size)) else { 443 | return nil 444 | } 445 | // cmd 446 | for _ in 0.. UnsafeMutableRawPointer in 488 | let machO = image.withMemoryRebound(to: Int8.self, capacity: 1, { $0 }) 489 | let symbolAddress = machO.advanced(by: Int(readUleb128(ptr: &symbolLocation, end: end))) 490 | return UnsafeMutableRawPointer(mutating: symbolAddress) 491 | } 492 | 493 | switch flags & UInt64(EXPORT_SYMBOL_FLAGS_KIND_MASK) { 494 | case UInt64(EXPORT_SYMBOL_FLAGS_KIND_REGULAR): 495 | // runResolver is false by bind or lazyBind 496 | return returnSymbolAddress() 497 | case UInt64(EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL): 498 | if (flags & UInt64(EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) != 0) { 499 | return nil 500 | } 501 | return returnSymbolAddress() 502 | case UInt64(EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE): 503 | if (flags & UInt64(EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) != 0) { 504 | return nil 505 | } 506 | return UnsafeMutableRawPointer(bitPattern: UInt(readUleb128(ptr: &symbolLocation, end: end))) 507 | default: 508 | break 509 | } 510 | } 511 | 512 | return nil 513 | } 514 | 515 | // ExportSymbol 516 | @inline(__always) 517 | static private func lookExportedSymbolByTrieWalk(targetSymbol: String, start: UnsafeMutablePointer, end: UnsafeMutablePointer, currentLocation location: UnsafeMutablePointer, currentSymbol: String) -> UnsafeMutablePointer? { 518 | var ptr = location 519 | 520 | while ptr <= end { 521 | // terminalSize 522 | var terminalSize = UInt64(ptr.pointee) 523 | ptr += 1 524 | if terminalSize > 127 { 525 | ptr -= 1 526 | terminalSize = readUleb128(ptr: &ptr, end: end) 527 | } 528 | if terminalSize != 0 { 529 | return currentSymbol == targetSymbol ? ptr : nil 530 | } 531 | 532 | // children 533 | let children = ptr.advanced(by: Int(terminalSize)) 534 | if children >= end { 535 | // end 536 | return nil 537 | } 538 | let childrenCount = children.pointee 539 | ptr = children + 1 540 | 541 | // nodes 542 | for _ in 0.., 576 | imageSlide slide: Int, 577 | newMethod: UnsafeMutableRawPointer, 578 | oldMethod: inout UnsafeMutableRawPointer?) { 579 | replaceSymbolAtImage(image, imageSlide: slide, symbol: symbol, newMethod: newMethod, oldMethod: &oldMethod) 580 | } 581 | 582 | @inline(__always) 583 | private static func replaceSymbolAtImage(_ image: UnsafePointer, 584 | imageSlide slide: Int, 585 | symbol: String, 586 | newMethod: UnsafeMutableRawPointer, 587 | oldMethod: inout UnsafeMutableRawPointer?) { 588 | var linkeditCmd: UnsafeMutablePointer! 589 | var dataCmd: UnsafeMutablePointer! 590 | var symtabCmd: UnsafeMutablePointer! 591 | var dynamicSymtabCmd: UnsafeMutablePointer! 592 | 593 | guard var curCmdPointer = UnsafeMutableRawPointer(bitPattern: UInt(bitPattern: image)+UInt(MemoryLayout.size)) else { return } 594 | 595 | for _ in 0..(OpaquePointer(curCmd)) 609 | } else if curCmd.pointee.cmd == LC_DYSYMTAB { 610 | dynamicSymtabCmd = UnsafeMutablePointer(OpaquePointer(curCmd)) 611 | } 612 | 613 | curCmdPointer += Int(curCmd.pointee.cmdsize) 614 | } 615 | 616 | if linkeditCmd == nil || symtabCmd == nil || dynamicSymtabCmd == nil || dataCmd == nil { 617 | return 618 | } 619 | 620 | let linkedBase = slide + Int(linkeditCmd.pointee.vmaddr) - Int(linkeditCmd.pointee.fileoff) 621 | let symtab = UnsafeMutablePointer(bitPattern: linkedBase + Int(symtabCmd.pointee.symoff)) 622 | let strtab = UnsafeMutablePointer(bitPattern: linkedBase + Int(symtabCmd.pointee.stroff)) 623 | let indirectsym = UnsafeMutablePointer(bitPattern: linkedBase + Int(dynamicSymtabCmd.pointee.indirectsymoff)) 624 | 625 | if symtab == nil || strtab == nil || indirectsym == nil { 626 | return 627 | } 628 | 629 | for tmp in 0...size + MemoryLayout.size*Int(tmp)).assumingMemoryBound(to: section_64.self) 631 | 632 | // symbol_pointers sections 633 | if curSection.pointee.flags == S_LAZY_SYMBOL_POINTERS { 634 | replaceSymbolPointerAtSection(curSection, symtab: symtab!, strtab: strtab!, indirectsym: indirectsym!, slide: slide, symbolName: symbol, newMethod: newMethod, oldMethod: &oldMethod) 635 | } 636 | if curSection.pointee.flags == S_NON_LAZY_SYMBOL_POINTERS { 637 | replaceSymbolPointerAtSection(curSection, symtab: symtab!, strtab: strtab!, indirectsym: indirectsym!, slide: slide, symbolName: symbol, newMethod: newMethod, oldMethod: &oldMethod) 638 | } 639 | } 640 | } 641 | 642 | @inline(__always) 643 | private static func replaceSymbolPointerAtSection(_ section: UnsafeMutablePointer, 644 | symtab: UnsafeMutablePointer, 645 | strtab: UnsafeMutablePointer, 646 | indirectsym: UnsafeMutablePointer, 647 | slide: Int, 648 | symbolName: String, 649 | newMethod: UnsafeMutableRawPointer, 650 | oldMethod: inout UnsafeMutableRawPointer?) { 651 | let indirectSymVmAddr = indirectsym.advanced(by: Int(section.pointee.reserved1)) 652 | let sectionVmAddr = UnsafeMutablePointer(bitPattern: slide+Int(section.pointee.addr)) 653 | 654 | if sectionVmAddr == nil { 655 | return 656 | } 657 | 658 | for tmp in 0...size { 659 | let curIndirectSym = indirectSymVmAddr.advanced(by: tmp) 660 | if curIndirectSym.pointee == INDIRECT_SYMBOL_ABS || curIndirectSym.pointee == INDIRECT_SYMBOL_LOCAL { 661 | continue 662 | } 663 | let curStrTabOff = symtab.advanced(by: Int(curIndirectSym.pointee)).pointee.n_un.n_strx 664 | let curSymbolName = strtab.advanced(by: Int(curStrTabOff+1)) 665 | 666 | if String(cString: curSymbolName) == symbolName { 667 | oldMethod = sectionVmAddr!.advanced(by: tmp).pointee 668 | sectionVmAddr!.advanced(by: tmp).initialize(to: newMethod) 669 | break 670 | } 671 | } 672 | } 673 | } 674 | #endif 675 | -------------------------------------------------------------------------------- /IOSSecuritySuite/IOSSecuritySuite.h: -------------------------------------------------------------------------------- 1 | // 2 | // IOSSecuritySuite.h 3 | // IOSSecuritySuite 4 | // 5 | // Created by wregula on 23/04/2019. 6 | // Copyright © 2019 wregula. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for IOSSecuritySuite. 12 | FOUNDATION_EXPORT double IOSSecuritySuiteVersionNumber; 13 | 14 | //! Project version string for IOSSecuritySuite. 15 | FOUNDATION_EXPORT const unsigned char IOSSecuritySuiteVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /IOSSecuritySuite/IOSSecuritySuite.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IOSSecuritySuite.swift 3 | // IOSSecuritySuite 4 | // 5 | // Created by wregula on 23/04/2019. 6 | // Copyright © 2019 wregula. All rights reserved. 7 | // 8 | //swiftlint:disable line_length 9 | 10 | import Foundation 11 | import MachO 12 | 13 | @available(iOSApplicationExtension, unavailable) 14 | public class IOSSecuritySuite { 15 | 16 | /** 17 | This type method is used to determine the true/false jailbreak status 18 | 19 | Usage example 20 | ``` 21 | let isDeviceJailbroken: Bool = IOSSecuritySuite.amIJailbroken() 22 | ``` 23 | */ 24 | public static func amIJailbroken() -> Bool { 25 | return JailbreakChecker.amIJailbroken() 26 | } 27 | 28 | /** 29 | This type method is used to determine the jailbreak status with a message which jailbreak indicator was detected 30 | 31 | Usage example 32 | ``` 33 | let jailbreakStatus = IOSSecuritySuite.amIJailbrokenWithFailMessage() 34 | if jailbreakStatus.jailbroken { 35 | print("This device is jailbroken") 36 | print("Because: \(jailbreakStatus.failMessage)") 37 | } else { 38 | print("This device is not jailbroken") 39 | } 40 | ``` 41 | 42 | - Returns: Tuple with with the jailbreak status *Bool* labeled *jailbroken* and *String* labeled *failMessage* 43 | to determine check that failed 44 | */ 45 | public static func amIJailbrokenWithFailMessage() -> (jailbroken: Bool, failMessage: String) { 46 | return JailbreakChecker.amIJailbrokenWithFailMessage() 47 | } 48 | 49 | /** 50 | This type method is used to determine the jailbreak status with a list of failed checks 51 | 52 | Usage example 53 | ``` 54 | let jailbreakStatus = IOSSecuritySuite.amIJailbrokenWithFailedChecks() 55 | if jailbreakStatus.jailbroken { 56 | print("This device is jailbroken") 57 | print("The following checks failed: \(jailbreakStatus.failedChecks)") 58 | } 59 | ``` 60 | 61 | - Returns: Tuple with with the jailbreak status *Bool* labeled *jailbroken* and *[FailedCheck]* labeled *failedChecks* 62 | for the list of failed checks 63 | */ 64 | public static func amIJailbrokenWithFailedChecks() -> (jailbroken: Bool, failedChecks: [FailedCheck]) { 65 | return JailbreakChecker.amIJailbrokenWithFailedChecks() 66 | } 67 | 68 | /** 69 | This type method is used to determine if application is run in emulator 70 | 71 | Usage example 72 | ``` 73 | let runInEmulator: Bool = IOSSecuritySuite.amIRunInEmulator() 74 | ``` 75 | */ 76 | public static func amIRunInEmulator() -> Bool { 77 | return EmulatorChecker.amIRunInEmulator() 78 | } 79 | 80 | /** 81 | This type method is used to determine if application is being debugged 82 | 83 | Usage example 84 | ``` 85 | let amIDebugged: Bool = IOSSecuritySuite.amIDebugged() 86 | ``` 87 | */ 88 | public static func amIDebugged() -> Bool { 89 | return DebuggerChecker.amIDebugged() 90 | } 91 | 92 | /** 93 | This type method is used to deny debugger and improve the application resillency 94 | 95 | Usage example 96 | ``` 97 | IOSSecuritySuite.denyDebugger() 98 | ``` 99 | */ 100 | public static func denyDebugger() { 101 | return DebuggerChecker.denyDebugger() 102 | } 103 | 104 | /** 105 | This type method is used to determine if application has been tampered with 106 | 107 | Usage example 108 | ``` 109 | if IOSSecuritySuite.amITampered([.bundleID("biz.securing.FrameworkClientApp"), .mobileProvision("your-mobile-provision-sha256-value")]).result { 110 | print("I have been Tampered.") 111 | } 112 | else { 113 | print("I have not been Tampered.") 114 | } 115 | ``` 116 | 117 | - Parameter checks: The file Integrity checks you want 118 | - Returns: The file Integrity checker result 119 | */ 120 | public static func amITampered(_ checks: [FileIntegrityCheck]) -> FileIntegrityCheckResult { 121 | return IntegrityChecker.amITampered(checks) 122 | } 123 | 124 | /** 125 | This type method is used to determine if there are any popular reverse engineering tools installed on the device 126 | 127 | Usage example 128 | ``` 129 | let amIReverseEngineered: Bool = IOSSecuritySuite.amIReverseEngineered() 130 | ``` 131 | */ 132 | public static func amIReverseEngineered() -> Bool { 133 | return ReverseEngineeringToolsChecker.amIReverseEngineered() 134 | } 135 | 136 | /** 137 | This type method is used to determine if `objc call` has been RuntimeHooked by for example `Flex` 138 | 139 | Usage example 140 | ``` 141 | class SomeClass { 142 | @objc dynamic func someFunction() { 143 | } 144 | } 145 | 146 | let dylds = ["IOSSecuritySuite", ...] 147 | 148 | let amIRuntimeHook: Bool = amIRuntimeHook(dyldWhiteList: dylds, detectionClass: SomeClass.self, selector: #selector(SomeClass.someFunction), isClassMethod: false) 149 | ``` 150 | */ 151 | public static func amIRuntimeHooked(dyldWhiteList: [String], detectionClass: AnyClass, selector: Selector, isClassMethod: Bool) -> Bool { 152 | return RuntimeHookChecker.amIRuntimeHook(dyldWhiteList: dyldWhiteList, detectionClass: detectionClass, selector: selector, isClassMethod: isClassMethod) 153 | } 154 | 155 | /** 156 | This type method is used to determine if HTTP proxy was set in the iOS Settings. 157 | 158 | Usage example 159 | ``` 160 | let amIProxied: Bool = IOSSecuritySuite.amIProxied() 161 | ``` 162 | */ 163 | public static func amIProxied() -> Bool { 164 | return ProxyChecker.amIProxied() 165 | } 166 | } 167 | 168 | #if arch(arm64) 169 | @available(iOSApplicationExtension, unavailable) 170 | public extension IOSSecuritySuite { 171 | /** 172 | This type method is used to determine if `function_address` has been hooked by `MSHook` 173 | 174 | Usage example 175 | ``` 176 | func denyDebugger() { 177 | } 178 | 179 | typealias FunctionType = @convention(thin) ()->() 180 | 181 | let func_denyDebugger: FunctionType = denyDebugger // `: FunctionType` is must 182 | let func_addr = unsafeBitCast(func_denyDebugger, to: UnsafeMutableRawPointer.self) 183 | let amIMSHookFunction: Bool = amIMSHookFunction(func_addr) 184 | ``` 185 | */ 186 | static func amIMSHooked(_ functionAddress: UnsafeMutableRawPointer) -> Bool { 187 | return MSHookFunctionChecker.amIMSHooked(functionAddress) 188 | } 189 | 190 | /** 191 | This type method is used to get original `function_address` which has been hooked by `MSHook` 192 | 193 | Usage example 194 | ``` 195 | func denyDebugger(value: Int) { 196 | } 197 | 198 | typealias FunctionType = @convention(thin) (Int)->() 199 | 200 | let funcDenyDebugger: FunctionType = denyDebugger 201 | let funcAddr = unsafeBitCast(funcDenyDebugger, to: UnsafeMutableRawPointer.self) 202 | 203 | if let originalDenyDebugger = denyMSHook(funcAddr) { 204 | unsafeBitCast(originalDenyDebugger, to: FunctionType.self)(1337) //Call orignal function with 1337 as Int argument 205 | } else { 206 | denyDebugger() 207 | } 208 | ``` 209 | */ 210 | static func denyMSHook(_ functionAddress: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer? { 211 | return MSHookFunctionChecker.denyMSHook(functionAddress) 212 | } 213 | 214 | /** 215 | This type method is used to rebind `symbol` which has been hooked by `fishhook` 216 | 217 | Usage example 218 | ``` 219 | denySymbolHook("$s10Foundation5NSLogyySS_s7CVarArg_pdtF") // Foudation's NSlog of Swift 220 | NSLog("Hello Symbol Hook") 221 | 222 | denySymbolHook("abort") 223 | abort() 224 | ``` 225 | */ 226 | static func denySymbolHook(_ symbol: String) { 227 | FishHookChecker.denyFishHook(symbol) 228 | } 229 | 230 | /** 231 | This type method is used to rebind `symbol` which has been hooked at one of image by `fishhook` 232 | 233 | Usage example 234 | ``` 235 | for i in 0..<_dyld_image_count() { 236 | if let imageName = _dyld_get_image_name(i) { 237 | let name = String(cString: imageName) 238 | if name.contains("IOSSecuritySuite"), let image = _dyld_get_image_header(i) { 239 | denySymbolHook("dlsym", at: image, imageSlide: _dyld_get_image_vmaddr_slide(i)) 240 | break 241 | } 242 | } 243 | } 244 | ``` 245 | */ 246 | static func denySymbolHook(_ symbol: String, at image: UnsafePointer, imageSlide slide: Int) { 247 | FishHookChecker.denyFishHook(symbol, at: image, imageSlide: slide) 248 | } 249 | 250 | /** 251 | This type method is used to get the SHA256 hash value of the executable file in a specified image 252 | 253 | **Dylib only.** This means you should set Mach-O type as `Dynamic Library` in your *Build Settings*. 254 | 255 | Calculate the hash value of the `__TEXT.__text` data of the specified image Mach-O file. 256 | 257 | Usage example 258 | ``` 259 | // Manually verify SHA256 hash value of a loaded dylib 260 | if let hashValue = IOSSecuritySuite.getMachOFileHashValue(.custom("IOSSecuritySuite")), hashValue == "6d8d460b9a4ee6c0f378e30f137cebaf2ce12bf31a2eef3729c36889158aa7fc" { 261 | print("I have not been Tampered.") 262 | } 263 | else { 264 | print("I have been Tampered.") 265 | } 266 | ``` 267 | 268 | - Parameter target: The target image 269 | - Returns: A hash value of the executable file. 270 | */ 271 | static func getMachOFileHashValue(_ target: IntegrityCheckerImageTarget = .default) -> String? { 272 | return IntegrityChecker.getMachOFileHashValue(target) 273 | } 274 | 275 | /** 276 | This type method is used to find all loaded dylibs in the specified image 277 | 278 | **Dylib only.** This means you should set Mach-O type as `Dynamic Library` in your *Build Settings*. 279 | 280 | Usage example 281 | ``` 282 | if let loadedDylib = IOSSecuritySuite.findLoadedDylibs() { 283 | print("Loaded dylibs: \(loadedDylib)") 284 | } 285 | ``` 286 | 287 | - Parameter target: The target image 288 | - Returns: An Array with all loaded dylib names 289 | */ 290 | static func findLoadedDylibs(_ target: IntegrityCheckerImageTarget = .default) -> [String]? { 291 | return IntegrityChecker.findLoadedDylibs(target) 292 | } 293 | /** 294 | This type method is used to determine if there are any breakpoints at the function 295 | 296 | Usage example 297 | ``` 298 | func denyDebugger() { 299 | // add a breakpoint at here to test 300 | } 301 | 302 | typealias FunctionType = @convention(thin) ()->() 303 | 304 | let func_denyDebugger: FunctionType = denyDebugger // `: FunctionType` is a must 305 | let func_addr = unsafeBitCast(func_denyDebugger, to: UnsafeMutableRawPointer.self) 306 | let hasBreakpoint: Bool = IOSSecuritySuite.hasBreakpointAt(func_addr, functionSize: nil) 307 | ``` 308 | */ 309 | static func hasBreakpointAt(_ functionAddr: UnsafeRawPointer, functionSize: vm_size_t?) -> Bool { 310 | return DebuggerChecker.hasBreakpointAt(functionAddr, functionSize: functionSize) 311 | } 312 | 313 | static func hasWatchpoint() -> Bool { 314 | return DebuggerChecker.hasWatchpoint() 315 | } 316 | } 317 | #endif 318 | -------------------------------------------------------------------------------- /IOSSecuritySuite/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | 22 | 23 | -------------------------------------------------------------------------------- /IOSSecuritySuite/IntegrityChecker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IntegrityChecker.swift 3 | // IOSSecuritySuite 4 | // 5 | // Created by NikoXu on 2020/8/21. 6 | // Copyright © 2020 wregula. All rights reserved. 7 | // 8 | // swiftlint:disable line_length large_tuple force_cast 9 | 10 | import Foundation 11 | import MachO 12 | import CommonCrypto 13 | 14 | protocol Explainable { 15 | var description: String { get } 16 | } 17 | 18 | public enum FileIntegrityCheck { 19 | // Compare current bundleID with a specified bundleID. 20 | case bundleID(String) 21 | 22 | // Compare current hash value(SHA256 hex string) of `embedded.mobileprovision` with a specified hash value. 23 | // Use command `"shasum -a 256 /path/to/embedded.mobileprovision"` to get SHA256 value on your macOS. 24 | case mobileProvision(String) 25 | 26 | // Compare current hash value(SHA256 hex string) of executable file with a specified (Image Name, Hash Value). 27 | // Only work on dynamic library and arm64. 28 | case machO(String, String) 29 | } 30 | 31 | extension FileIntegrityCheck: Explainable { 32 | public var description: String { 33 | switch self { 34 | case .bundleID(let exceptedBundleID): 35 | return "The expected bundle identify was \(exceptedBundleID)" 36 | case .mobileProvision(let expectedSha256Value): 37 | return "The expected hash value of Mobile Provision file was \(expectedSha256Value)" 38 | case .machO(let imageName, let expectedSha256Value): 39 | return "The expected hash value of \"__TEXT.__text\" data of \(imageName) Mach-O file was \(expectedSha256Value)" 40 | } 41 | } 42 | } 43 | 44 | public typealias FileIntegrityCheckResult = (result: Bool, hitChecks: [FileIntegrityCheck]) 45 | 46 | internal class IntegrityChecker { 47 | 48 | // Check if the application has been tampered with the specified checks 49 | static func amITampered(_ checks: [FileIntegrityCheck]) -> FileIntegrityCheckResult { 50 | 51 | var hitChecks: [FileIntegrityCheck] = [] 52 | var result = false 53 | 54 | for check in checks { 55 | switch check { 56 | case .bundleID(let exceptedBundleID): 57 | if checkBundleID(exceptedBundleID) { 58 | result = true 59 | hitChecks.append(check) 60 | } 61 | case .mobileProvision(let expectedSha256Value): 62 | if checkMobileProvision(expectedSha256Value.lowercased()) { 63 | result = true 64 | hitChecks.append(check) 65 | } 66 | case .machO(let imageName, let expectedSha256Value): 67 | if checkMachO(imageName, with: expectedSha256Value.lowercased()) { 68 | result = true 69 | hitChecks.append(check) 70 | } 71 | } 72 | } 73 | 74 | return (result, hitChecks) 75 | } 76 | 77 | private static func checkBundleID(_ expectedBundleID: String) -> Bool { 78 | if expectedBundleID != Bundle.main.bundleIdentifier { 79 | return true 80 | } 81 | 82 | return false 83 | } 84 | 85 | private static func checkMobileProvision(_ expectedSha256Value: String) -> Bool { 86 | 87 | guard let path = Bundle.main.path(forResource: "embedded", ofType: "mobileprovision") else { return false } 88 | 89 | let url = URL(fileURLWithPath: path) 90 | 91 | if FileManager.default.fileExists(atPath: url.path) { 92 | if let data = FileManager.default.contents(atPath: url.path) { 93 | 94 | // Hash: SHA256 95 | var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH)) 96 | data.withUnsafeBytes { 97 | _ = CC_SHA256($0.baseAddress, CC_LONG(data.count), &hash) 98 | } 99 | 100 | if Data(hash).hexEncodedString() != expectedSha256Value { 101 | return true 102 | } 103 | } 104 | } 105 | 106 | return false 107 | } 108 | 109 | private static func checkMachO(_ imageName: String, with expectedSha256Value: String) -> Bool { 110 | #if arch(arm64) 111 | if let hashValue = getMachOFileHashValue(.custom(imageName)), hashValue != expectedSha256Value { 112 | return true 113 | } 114 | #endif 115 | return false 116 | } 117 | 118 | } 119 | 120 | #if arch(arm64) 121 | 122 | public enum IntegrityCheckerImageTarget { 123 | // Default image 124 | case `default` 125 | 126 | // Custom image with a specified name 127 | case custom(String) 128 | } 129 | 130 | extension IntegrityChecker { 131 | 132 | // Get hash value of Mach-O "__TEXT.__text" data with a specified image target 133 | static func getMachOFileHashValue(_ target: IntegrityCheckerImageTarget = .default) -> String? { 134 | switch target { 135 | case .custom(let imageName): 136 | return MachOParse(imageName: imageName).getTextSectionDataSHA256Value() 137 | case .default: 138 | return MachOParse().getTextSectionDataSHA256Value() 139 | } 140 | } 141 | 142 | // Find loaded dylib with a specified image target 143 | static func findLoadedDylibs(_ target: IntegrityCheckerImageTarget = .default) -> [String]? { 144 | switch target { 145 | case .custom(let imageName): 146 | return MachOParse(imageName: imageName).findLoadedDylibs() 147 | case .default: 148 | return MachOParse().findLoadedDylibs() 149 | } 150 | } 151 | } 152 | 153 | // MARK: - MachOParse 154 | 155 | private struct SectionInfo { 156 | var section: UnsafePointer 157 | var addr: UInt64 158 | } 159 | 160 | private struct SegmentInfo { 161 | var segment: UnsafePointer 162 | var addr: UInt64 163 | } 164 | 165 | // Convert (Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8) to String 166 | @inline(__always) 167 | private func convert16BitInt8TupleToString(int8Tuple: (Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8)) -> String { 168 | let mirror = Mirror(reflecting: int8Tuple) 169 | 170 | return mirror.children.map { 171 | String(UnicodeScalar(UInt8($0.value as! Int8))) 172 | }.joined().replacingOccurrences(of: "\0", with: "") 173 | } 174 | 175 | private class MachOParse { 176 | private var base: UnsafePointer? 177 | private var slide: Int? 178 | 179 | init() { 180 | base = _dyld_get_image_header(0) 181 | slide = _dyld_get_image_vmaddr_slide(0) 182 | } 183 | 184 | init(header: UnsafePointer, slide: Int) { 185 | self.base = header 186 | self.slide = slide 187 | } 188 | 189 | init(imageName: String) { 190 | for index in 0..<_dyld_image_count() { 191 | if let cImgName = _dyld_get_image_name(index), String(cString: cImgName).contains(imageName), 192 | let header = _dyld_get_image_header(index) { 193 | self.base = header 194 | self.slide = _dyld_get_image_vmaddr_slide(index) 195 | } 196 | } 197 | } 198 | 199 | private func vm2real(_ vmaddr: UInt64) -> UInt64? { 200 | guard let slide = slide else { 201 | return nil 202 | } 203 | 204 | return UInt64(slide) + vmaddr 205 | } 206 | 207 | func findLoadedDylibs() -> [String]? { 208 | guard let header = base else { 209 | return nil 210 | } 211 | 212 | guard var curCmd = UnsafeMutablePointer(bitPattern: UInt(bitPattern: header) + UInt(MemoryLayout.size)) else { 213 | return nil 214 | } 215 | 216 | var array: [String] = Array() 217 | var segCmd: UnsafeMutablePointer! 218 | 219 | for _ in 0.. SegmentInfo? { 236 | guard let header = base else { 237 | return nil 238 | } 239 | 240 | guard var curCmd = UnsafeMutablePointer(bitPattern: UInt(bitPattern: header)+UInt(MemoryLayout.size)) else { 241 | return nil 242 | } 243 | 244 | var segCmd: UnsafeMutablePointer! 245 | 246 | for _ in 0.. SectionInfo? { 265 | guard let header = base else { 266 | return nil 267 | } 268 | 269 | guard var curCmd = UnsafeMutablePointer(bitPattern: UInt(bitPattern: header)+UInt(MemoryLayout.size)) else { 270 | return nil 271 | } 272 | 273 | var segCmd: UnsafeMutablePointer! 274 | 275 | for _ in 0..(bitPattern: UInt(bitPattern: curCmd) + UInt(MemoryLayout.size) + UInt(sectionID)) else { 283 | return nil 284 | } 285 | 286 | let secName = convert16BitInt8TupleToString(int8Tuple: sect.pointee.sectname) 287 | 288 | if secName == secname, 289 | let addr = vm2real(sect.pointee.addr) { 290 | let sectionInfo = SectionInfo(section: sect, addr: addr) 291 | return sectionInfo 292 | } 293 | } 294 | } 295 | } 296 | 297 | curCmd = UnsafeMutableRawPointer(curCmd).advanced(by: Int(curCmd.pointee.cmdsize)).assumingMemoryBound(to: segment_command_64.self) 298 | } 299 | 300 | return nil 301 | } 302 | 303 | func getTextSectionDataSHA256Value() -> String? { 304 | guard let sectionInfo = findSection(SEG_TEXT, secname: SECT_TEXT) else { 305 | return nil 306 | } 307 | 308 | guard let startAddr = UnsafeMutablePointer(bitPattern: Int(sectionInfo.addr)) else { 309 | return nil 310 | } 311 | 312 | let size = sectionInfo.section.pointee.size 313 | 314 | // Hash: SHA256 315 | var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH)) 316 | _ = CC_SHA256(startAddr, CC_LONG(size), &hash) 317 | 318 | return Data(hash).hexEncodedString() 319 | } 320 | } 321 | 322 | #endif 323 | 324 | extension Data { 325 | fileprivate func hexEncodedString() -> String { 326 | return map { String(format: "%02hhx", $0) }.joined() 327 | } 328 | } 329 | -------------------------------------------------------------------------------- /IOSSecuritySuite/JailbreakChecker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JailbreakChecker.swift 3 | // IOSSecuritySuite 4 | // 5 | // Created by wregula on 23/04/2019. 6 | // Copyright © 2019 wregula. All rights reserved. 7 | // 8 | //swiftlint:disable cyclomatic_complexity function_body_length type_body_length 9 | 10 | import Foundation 11 | import UIKit 12 | import Darwin // fork 13 | import MachO // dyld 14 | 15 | public typealias FailedCheck = (check: JailbreakCheck, failMessage: String) 16 | 17 | public enum JailbreakCheck: CaseIterable { 18 | case urlSchemes 19 | case existenceOfSuspiciousFiles 20 | case suspiciousFilesCanBeOpened 21 | case restrictedDirectoriesWriteable 22 | case fork 23 | case symbolicLinks 24 | case dyld 25 | } 26 | 27 | internal class JailbreakChecker { 28 | typealias CheckResult = (passed: Bool, failMessage: String) 29 | 30 | struct JailbreakStatus { 31 | let passed: Bool 32 | let failMessage: String // Added for backwards compatibility 33 | let failedChecks: [FailedCheck] 34 | } 35 | 36 | static func amIJailbroken() -> Bool { 37 | return !performChecks().passed 38 | } 39 | 40 | static func amIJailbrokenWithFailMessage() -> (jailbroken: Bool, failMessage: String) { 41 | let status = performChecks() 42 | return (!status.passed, status.failMessage) 43 | } 44 | 45 | static func amIJailbrokenWithFailedChecks() -> (jailbroken: Bool, failedChecks: [FailedCheck]) { 46 | let status = performChecks() 47 | return (!status.passed, status.failedChecks) 48 | } 49 | 50 | private static func performChecks() -> JailbreakStatus { 51 | var passed = true 52 | var failMessage = "" 53 | var result: CheckResult = (true, "") 54 | var failedChecks: [FailedCheck] = [] 55 | 56 | for check in JailbreakCheck.allCases { 57 | switch check { 58 | case .urlSchemes: 59 | result = checkURLSchemes() 60 | case .existenceOfSuspiciousFiles: 61 | result = checkExistenceOfSuspiciousFiles() 62 | case .suspiciousFilesCanBeOpened: 63 | result = checkSuspiciousFilesCanBeOpened() 64 | case .restrictedDirectoriesWriteable: 65 | result = checkRestrictedDirectoriesWriteable() 66 | case .fork: 67 | if !EmulatorChecker.amIRunInEmulator() { 68 | result = checkFork() 69 | } else { 70 | print("App run in the emulator, skipping the fork check.") 71 | result = (true, "") 72 | } 73 | case .symbolicLinks: 74 | result = checkSymbolicLinks() 75 | case .dyld: 76 | result = checkDYLD() 77 | } 78 | 79 | passed = passed && result.passed 80 | 81 | if !result.passed { 82 | failedChecks.append((check: check, failMessage: result.failMessage)) 83 | 84 | if !failMessage.isEmpty { 85 | failMessage += ", " 86 | } 87 | } 88 | 89 | failMessage += result.failMessage 90 | } 91 | 92 | return JailbreakStatus(passed: passed, failMessage: failMessage, failedChecks: failedChecks) 93 | } 94 | 95 | private static func canOpenUrlFromList(urlSchemes: [String]) -> CheckResult { 96 | for urlScheme in urlSchemes { 97 | if let url = URL(string: urlScheme) { 98 | if UIApplication.shared.canOpenURL(url) { 99 | return(false, "\(urlScheme) URL scheme detected") 100 | } 101 | } 102 | } 103 | return (true, "") 104 | } 105 | 106 | private static func checkURLSchemes() -> CheckResult { 107 | var flag: (passed: Bool, failMessage: String) = (true, "") 108 | let urlSchemes = [ 109 | "undecimus://", 110 | "cydia://", 111 | "sileo://", 112 | "zbra://", 113 | "filza://", 114 | "activator://" 115 | ] 116 | 117 | if Thread.isMainThread { 118 | flag = canOpenUrlFromList(urlSchemes: urlSchemes) 119 | } else { 120 | let semaphore = DispatchSemaphore(value: 0) 121 | DispatchQueue.main.async { 122 | flag = canOpenUrlFromList(urlSchemes: urlSchemes) 123 | semaphore.signal() 124 | } 125 | semaphore.wait() 126 | } 127 | return flag 128 | } 129 | 130 | private static func checkExistenceOfSuspiciousFiles() -> CheckResult { 131 | var paths = [ 132 | "/usr/sbin/frida-server", // frida 133 | "/etc/apt/sources.list.d/electra.list", // electra 134 | "/etc/apt/sources.list.d/sileo.sources", // electra 135 | "/.bootstrapped_electra", // electra 136 | "/usr/lib/libjailbreak.dylib", // electra 137 | "/jb/lzma", // electra 138 | "/.cydia_no_stash", // unc0ver 139 | "/.installed_unc0ver", // unc0ver 140 | "/jb/offsets.plist", // unc0ver 141 | "/usr/share/jailbreak/injectme.plist", // unc0ver 142 | "/etc/apt/undecimus/undecimus.list", // unc0ver 143 | "/var/lib/dpkg/info/mobilesubstrate.md5sums", // unc0ver 144 | "/Library/MobileSubstrate/MobileSubstrate.dylib", 145 | "/jb/jailbreakd.plist", // unc0ver 146 | "/jb/amfid_payload.dylib", // unc0ver 147 | "/jb/libjailbreak.dylib", // unc0ver 148 | "/usr/libexec/cydia/firmware.sh", 149 | "/var/lib/cydia", 150 | "/etc/apt", 151 | "/private/var/lib/apt", 152 | "/private/var/Users/", 153 | "/var/log/apt", 154 | "/Applications/Cydia.app", 155 | "/private/var/stash", 156 | "/private/var/lib/apt/", 157 | "/private/var/lib/cydia", 158 | "/private/var/cache/apt/", 159 | "/private/var/log/syslog", 160 | "/private/var/tmp/cydia.log", 161 | "/Applications/Icy.app", 162 | "/Applications/MxTube.app", 163 | "/Applications/RockApp.app", 164 | "/Applications/blackra1n.app", 165 | "/Applications/SBSettings.app", 166 | "/Applications/FakeCarrier.app", 167 | "/Applications/WinterBoard.app", 168 | "/Applications/IntelliScreen.app", 169 | "/private/var/mobile/Library/SBSettings/Themes", 170 | "/Library/MobileSubstrate/CydiaSubstrate.dylib", 171 | "/System/Library/LaunchDaemons/com.ikey.bbot.plist", 172 | "/Library/MobileSubstrate/DynamicLibraries/Veency.plist", 173 | "/Library/MobileSubstrate/DynamicLibraries/LiveClock.plist", 174 | "/System/Library/LaunchDaemons/com.saurik.Cydia.Startup.plist", 175 | "/Applications/Sileo.app", 176 | "/var/binpack", 177 | "/Library/PreferenceBundles/LibertyPref.bundle", 178 | "/Library/PreferenceBundles/ShadowPreferences.bundle", 179 | "/Library/PreferenceBundles/ABypassPrefs.bundle", 180 | "/Library/PreferenceBundles/FlyJBPrefs.bundle", 181 | "/usr/lib/libhooker.dylib", 182 | "/usr/lib/libsubstitute.dylib", 183 | "/usr/lib/substrate", 184 | "/usr/lib/TweakInject", 185 | ] 186 | 187 | // These files can give false positive in the emulator 188 | if !EmulatorChecker.amIRunInEmulator() { 189 | paths += [ 190 | "/bin/bash", 191 | "/usr/sbin/sshd", 192 | "/usr/libexec/ssh-keysign", 193 | "/bin/sh", 194 | "/etc/ssh/sshd_config", 195 | "/usr/libexec/sftp-server", 196 | "/usr/bin/ssh" 197 | ] 198 | } 199 | 200 | for path in paths { 201 | if FileManager.default.fileExists(atPath: path) { 202 | return (false, "Suspicious file exists: \(path)") 203 | } 204 | } 205 | 206 | return (true, "") 207 | } 208 | 209 | private static func checkSuspiciousFilesCanBeOpened() -> CheckResult { 210 | 211 | var paths = [ 212 | "/.installed_unc0ver", 213 | "/.bootstrapped_electra", 214 | "/Applications/Cydia.app", 215 | "/Library/MobileSubstrate/MobileSubstrate.dylib", 216 | "/etc/apt", 217 | "/var/log/apt" 218 | ] 219 | 220 | // These files can give false positive in the emulator 221 | if !EmulatorChecker.amIRunInEmulator() { 222 | paths += [ 223 | "/bin/bash", 224 | "/usr/sbin/sshd", 225 | "/usr/bin/ssh" 226 | ] 227 | } 228 | 229 | for path in paths { 230 | 231 | if FileManager.default.isReadableFile(atPath: path) { 232 | return (false, "Suspicious file can be opened: \(path)") 233 | } 234 | } 235 | 236 | return (true, "") 237 | } 238 | 239 | private static func checkRestrictedDirectoriesWriteable() -> CheckResult { 240 | 241 | let paths = [ 242 | "/", 243 | "/root/", 244 | "/private/", 245 | "/jb/" 246 | ] 247 | 248 | // If library won't be able to write to any restricted directory the return(false, ...) is never reached 249 | // because of catch{} statement 250 | for path in paths { 251 | do { 252 | let pathWithSomeRandom = path+UUID().uuidString 253 | try "AmIJailbroken?".write(toFile: pathWithSomeRandom, atomically: true, encoding: String.Encoding.utf8) 254 | try FileManager.default.removeItem(atPath: pathWithSomeRandom) // clean if succesfully written 255 | return (false, "Wrote to restricted path: \(path)") 256 | } catch {} 257 | } 258 | 259 | return (true, "") 260 | } 261 | 262 | private static func checkFork() -> CheckResult { 263 | 264 | let pointerToFork = UnsafeMutableRawPointer(bitPattern: -2) 265 | let forkPtr = dlsym(pointerToFork, "fork") 266 | typealias ForkType = @convention(c) () -> pid_t 267 | let fork = unsafeBitCast(forkPtr, to: ForkType.self) 268 | let forkResult = fork() 269 | 270 | if forkResult >= 0 { 271 | if forkResult > 0 { 272 | kill(forkResult, SIGTERM) 273 | } 274 | return (false, "Fork was able to create a new process (sandbox violation)") 275 | } 276 | 277 | return (true, "") 278 | } 279 | 280 | private static func checkSymbolicLinks() -> CheckResult { 281 | 282 | let paths = [ 283 | "/var/lib/undecimus/apt", // unc0ver 284 | "/Applications", 285 | "/Library/Ringtones", 286 | "/Library/Wallpaper", 287 | "/usr/arm-apple-darwin9", 288 | "/usr/include", 289 | "/usr/libexec", 290 | "/usr/share" 291 | ] 292 | 293 | for path in paths { 294 | do { 295 | let result = try FileManager.default.destinationOfSymbolicLink(atPath: path) 296 | if !result.isEmpty { 297 | return (false, "Non standard symbolic link detected: \(path) points to \(result)") 298 | } 299 | } catch {} 300 | } 301 | 302 | return (true, "") 303 | } 304 | 305 | private static func checkDYLD() -> CheckResult { 306 | 307 | let suspiciousLibraries = [ 308 | "SubstrateLoader.dylib", 309 | "SSLKillSwitch2.dylib", 310 | "SSLKillSwitch.dylib", 311 | "MobileSubstrate.dylib", 312 | "TweakInject.dylib", 313 | "CydiaSubstrate", 314 | "cynject", 315 | "CustomWidgetIcons", 316 | "PreferenceLoader", 317 | "RocketBootstrap", 318 | "WeeLoader", 319 | "/.file", // HideJB (2.1.1) changes full paths of the suspicious libraries to "/.file" 320 | "libhooker", 321 | "SubstrateInserter", 322 | "SubstrateBootstrap", 323 | "ABypass", 324 | "FlyJB", 325 | "Substitute", 326 | "Cephei", 327 | "Electra", 328 | ] 329 | 330 | for libraryIndex in 0..<_dyld_image_count() { 331 | 332 | // _dyld_get_image_name returns const char * that needs to be casted to Swift String 333 | guard let loadedLibrary = String(validatingUTF8: _dyld_get_image_name(libraryIndex)) else { continue } 334 | 335 | for suspiciousLibrary in suspiciousLibraries { 336 | if loadedLibrary.lowercased().contains(suspiciousLibrary.lowercased()) { 337 | return(false, "Suspicious library loaded: \(loadedLibrary)") 338 | } 339 | } 340 | } 341 | 342 | return (true, "") 343 | } 344 | } 345 | -------------------------------------------------------------------------------- /IOSSecuritySuite/MSHookFunctionChecker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MSHookFunctionCherker.swift 3 | // IOSSecuritySuite 4 | // 5 | // Created by jintao on 2020/4/24. 6 | // Copyright © 2020 wregula. All rights reserved. 7 | // https://github.com/TannerJin/AntiMSHookFunction 8 | // swiftlint:disable cyclomatic_complexity function_body_length trailing_whitespace 9 | 10 | import Foundation 11 | 12 | /* 13 | Original: 14 | 15 | * original function address (example) 16 | stp x22, x21, [sp, #-0x10] 17 | stp x20, x19, [sp, #-0x20] 18 | stp x29, x30, [sp, #-0x30] 19 | . 20 | . 21 | . 22 | 23 | * vm_regions 24 | 25 | vm_region_0 vm_region_n 26 | *-----------* *-----------* 27 | | | | | 28 | | | -----> ... -----> | | 29 | | | <----- <----- | | 30 | *-----------* *-----------* 31 | | 32 | | 33 | V 34 | 35 | After MSHookFunction(mmap): 36 | 37 | * original function address 38 | ldr x16 #8 (4 bytes for arm64) 39 | br x16 (4 bytes for arm64) 40 | address (8 bytes for arm64) address = hook_function_address 41 | *-> . 42 | | . 43 | | . 44 | | 45 | | * vm_regions 46 | | 47 | | vm_region_0 vm_region_new vm_region_n+1 48 | | *-----------* *-----------* *-----------* 49 | | | | | | | | 50 | | | | -----> ... -----> | | -----> ... -----> | | 51 | | | | <----- <----- | | <----- <----- | | 52 | | *-----------* *-----------* *-----------* 53 | | 54 | | 55 | | 1. The vm_region_new is created by MSHookFunction with VM_PROT that is VM_PROT_READ and VM_PROT_EXECUTE 56 | | 57 | | 2. Instructions that can call original function are stored at the beginning of vm_region_new 58 | | 59 | | 3. The beginning of vm_region_new should look like instructions below 60 | | ... (>= 16 bytes for arm64) >=4 hooked instructions 61 | | ldr x16 #8 (4 bytes for arm64) 62 | | br x16 (4 bytes for arm64) 63 | *------ address (8 bytes for arm64) address = original_function_address + 16 64 | 65 | */ 66 | 67 | #if arch(arm64) 68 | internal class MSHookFunctionChecker { 69 | // come from ARM® Architecture Reference Manual, ARMv8 for ARMv8-A architecture profile 70 | private enum MSHookInstruction { 71 | // swiftlint:disable identifier_name line_length 72 | case ldr_x16 73 | case br_x16 74 | case adrp_x17(pageBase: UInt64) 75 | case add_x17(pageOffset: UInt64) 76 | case br_x17 77 | 78 | @inline(__always) 79 | static fileprivate func translateInstruction(at functionAddr: UnsafeMutableRawPointer) -> MSHookInstruction? { 80 | let arm = functionAddr.assumingMemoryBound(to: UInt32.self).pointee 81 | // ldr xt, #imm (C4.4.5 and C6.2.84) 82 | let ldr_register_litetal = (arm & (255 << 24)) >> 24 83 | if ldr_register_litetal == 0b01011000 { 84 | let rt = arm & 31 85 | let imm19 = (arm & ((1 << 19 - 1) << 5)) >> 5 86 | // ldr x16, #8 87 | if rt == 16 && (imm19 << 2) == 8 { 88 | return ldr_x16 89 | } 90 | } 91 | // br 92 | let br = arm >> 10 93 | if br == 0b1101011000011111000000 { 94 | let br_rn = (arm & (31 << 5)) >> 5 95 | if br_rn == 16 { 96 | return .br_x16 97 | } 98 | if br_rn == 17 { 99 | return .br_x17 100 | } 101 | } 102 | // adrp (C6.2.10) 103 | let adrp_op = arm >> 31 104 | let adrp = (arm & (31 << 24)) >> 24 105 | let rd = arm & (31 << 0) 106 | if adrp_op == 1 && adrp == 16 { 107 | let pageBase = getAdrpPageBase(functionAddr) 108 | // adrp x17, pageBase 109 | if rd == 17 { 110 | return .adrp_x17(pageBase: pageBase) 111 | } 112 | } 113 | // add (C4.2.1 and C6.2.4) 114 | let add = arm >> 24 115 | if add == 0b10010001 { // 32-bit: 0b00010001 116 | let add_rn = (arm & (31 << 5)) >> 5 117 | let add_rd = arm & 31 118 | let add_imm12 = UInt32((arm & ((1 << 12-1) << 10)) >> 10) 119 | var imm = UInt64(add_imm12) 120 | let shift = (arm & (3 << 22)) >> 22 121 | if shift == 0 { 122 | imm = UInt64(add_imm12) 123 | } else if shift == 1 { 124 | imm = UInt64(add_imm12 << 12) 125 | } else { 126 | // AArch64.UndefinedFault 127 | return nil 128 | } 129 | // add x17, x17, add_im 130 | if add_rn == 17 && add_rd == 17 { 131 | return .add_x17(pageOffset: imm) 132 | } 133 | } 134 | return nil 135 | } 136 | 137 | // pageBase 138 | @inline(__always) 139 | static private func getAdrpPageBase(_ functionAddr: UnsafeMutableRawPointer) -> UInt64 { 140 | let arm = functionAddr.assumingMemoryBound(to: UInt32.self).pointee 141 | func singExtend(_ value: Int64) -> Int64 { 142 | var result = value 143 | let sing = value >> (33-1) == 1 144 | if sing { 145 | result = ((1<<31-1) << 33) | value 146 | } 147 | return result 148 | } 149 | // +/- 4GB 150 | let immlo = (arm >> 29) & 3 151 | let immhiMask = UInt32((1 << 19 - 1) << 5) 152 | let immhi = (arm & immhiMask) >> 5 153 | let imm = (Int64((immhi << 2 | immlo)) << 12) 154 | let pcBase = (UInt(bitPattern: functionAddr) >> 12) << 12 155 | return UInt64(Int64(pcBase) + singExtend(imm)) 156 | } 157 | } 158 | 159 | @inline(__always) 160 | static func amIMSHooked(_ functionAddr: UnsafeMutableRawPointer) -> Bool { 161 | guard let firstInstruction = MSHookInstruction.translateInstruction(at: functionAddr) else { 162 | return false 163 | } 164 | switch firstInstruction { 165 | case .ldr_x16: 166 | let secondInstructionAddr = functionAddr + 4 167 | if case .br_x16 = MSHookInstruction.translateInstruction(at: secondInstructionAddr) { 168 | return true 169 | } 170 | return false 171 | case .adrp_x17: 172 | let secondInstructionAddr = functionAddr + 4 173 | let thridInstructionAddr = functionAddr + 8 174 | if case .add_x17 = MSHookInstruction.translateInstruction(at: secondInstructionAddr), 175 | case .br_x17 = MSHookInstruction.translateInstruction(at: thridInstructionAddr) { 176 | return true 177 | } 178 | return false 179 | default: 180 | return false 181 | } 182 | } 183 | 184 | @inline(__always) 185 | static func denyMSHook(_ functionAddr: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer? { 186 | if !amIMSHooked(functionAddr) { 187 | return nil 188 | } 189 | // size of replaced instructions 190 | guard let firstInstruction = MSHookInstruction.translateInstruction(at: functionAddr) else { 191 | assert(false, "amIMSHookFunction has judged") 192 | return nil 193 | } 194 | var origFunctionBeginAddr = functionAddr 195 | switch firstInstruction { 196 | case .ldr_x16: 197 | origFunctionBeginAddr += 16 198 | case .adrp_x17: 199 | origFunctionBeginAddr += 12 200 | default: 201 | assert(false, "amIMSHookFunction has judged") 202 | return nil 203 | } 204 | // look up vm_region 205 | let vmRegionInfo = UnsafeMutablePointer.allocate(capacity: MemoryLayout.size/4) 206 | defer { 207 | vmRegionInfo.deallocate() 208 | } 209 | var vmRegionAddress: vm_address_t = 1 210 | var vmRegionSize: vm_size_t = 0 211 | var vmRegionInfoCount: mach_msg_type_number_t = mach_msg_type_number_t(VM_REGION_BASIC_INFO_64) 212 | var objectName: mach_port_t = 0 213 | 214 | while true { 215 | if vmRegionAddress == 0 { 216 | return nil 217 | } 218 | let ret = vm_region_64(mach_task_self_, &vmRegionAddress, &vmRegionSize, VM_REGION_BASIC_INFO_64, vmRegionInfo, &vmRegionInfoCount, &objectName) 219 | if ret == KERN_SUCCESS { 220 | let regionInfo = UnsafeMutableRawPointer(vmRegionInfo).assumingMemoryBound(to: vm_region_basic_info_64.self) 221 | // vm region of code 222 | if regionInfo.pointee.protection == (VM_PROT_READ|VM_PROT_EXECUTE) { 223 | // ldr 224 | if case .ldr_x16 = firstInstruction { 225 | // 20: max_buffer_insered_Instruction 226 | for i in 4..<20 { 227 | if let instructionAddr = UnsafeMutablePointer(bitPattern: Int(vmRegionAddress) + i * 4), 228 | case .ldr_x16 = MSHookInstruction.translateInstruction(at: instructionAddr), 229 | case .br_x16 = MSHookInstruction.translateInstruction(at: UnsafeMutableRawPointer(instructionAddr) + 4), 230 | (instructionAddr + 1).pointee == origFunctionBeginAddr { 231 | return UnsafeMutableRawPointer(bitPattern: Int(vmRegionAddress)) 232 | } 233 | } 234 | } 235 | // adrp 236 | if case .adrp_x17 = firstInstruction { 237 | // 20: max_buffer_insered_Instruction 238 | for i in 3..<20 { 239 | if let instructionAddr = UnsafeMutableRawPointer(bitPattern: Int(vmRegionAddress) + i * 4), 240 | case let .adrp_x17(pageBase: pageBase) = MSHookInstruction.translateInstruction(at: instructionAddr), 241 | case let .add_x17(pageOffset: pageOffset) = MSHookInstruction.translateInstruction(at: instructionAddr + 4), 242 | case .br_x17 = MSHookInstruction.translateInstruction(at: instructionAddr + 8), 243 | pageBase+pageOffset == UInt(bitPattern: origFunctionBeginAddr) { 244 | return UnsafeMutableRawPointer(bitPattern: Int(vmRegionAddress)) 245 | } 246 | } 247 | } 248 | } 249 | vmRegionAddress += vmRegionSize 250 | } else { 251 | return nil 252 | } 253 | } 254 | } 255 | } 256 | #endif 257 | -------------------------------------------------------------------------------- /IOSSecuritySuite/ProxyChecker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProxyChecker.swift 3 | // IOSSecuritySuite 4 | // 5 | // Created by Wojciech Reguła on 07/12/2020. 6 | // Copyright © 2020 wregula. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | internal class ProxyChecker { 12 | 13 | static func amIProxied() -> Bool { 14 | 15 | guard let unmanagedSettings = CFNetworkCopySystemProxySettings() else { 16 | return false 17 | } 18 | 19 | let settingsOptional = unmanagedSettings.takeRetainedValue() as? [String: Any] 20 | 21 | guard let settings = settingsOptional else { 22 | return false 23 | } 24 | 25 | return (settings.keys.contains("HTTPProxy") || settings.keys.contains("HTTPSProxy")) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /IOSSecuritySuite/ReverseEngineeringToolsChecker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReverseEngineeringToolsChecker.swift 3 | // IOSSecuritySuite 4 | // 5 | // Created by wregula on 24/04/2019. 6 | // Copyright © 2019 wregula. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import MachO // dyld 11 | 12 | internal class ReverseEngineeringToolsChecker { 13 | 14 | static func amIReverseEngineered() -> Bool { 15 | return (checkDYLD() || checkExistenceOfSuspiciousFiles() || checkOpenedPorts() || checkPSelectFlag()) 16 | } 17 | 18 | private static func checkDYLD() -> Bool { 19 | 20 | let suspiciousLibraries = [ 21 | "FridaGadget", 22 | "frida", // Needle injects frida-somerandom.dylib 23 | "cynject", 24 | "libcycript" 25 | ] 26 | 27 | for libraryIndex in 0..<_dyld_image_count() { 28 | 29 | // _dyld_get_image_name returns const char * that needs to be casted to Swift String 30 | guard let loadedLibrary = String(validatingUTF8: _dyld_get_image_name(libraryIndex)) else { continue } 31 | 32 | for suspiciousLibrary in suspiciousLibraries { 33 | if loadedLibrary.lowercased().contains(suspiciousLibrary.lowercased()) { 34 | return true 35 | } 36 | } 37 | } 38 | 39 | return false 40 | } 41 | 42 | private static func checkExistenceOfSuspiciousFiles() -> Bool { 43 | 44 | let paths = [ 45 | "/usr/sbin/frida-server" 46 | ] 47 | 48 | for path in paths { 49 | if FileManager.default.fileExists(atPath: path) { 50 | return true 51 | } 52 | } 53 | 54 | return false 55 | } 56 | 57 | private static func checkOpenedPorts() -> Bool { 58 | 59 | let ports = [ 60 | 27042, // default Frida 61 | 4444 // default Needle 62 | ] 63 | 64 | for port in ports { 65 | 66 | if canOpenLocalConnection(port: port) { 67 | return true 68 | } 69 | } 70 | 71 | return false 72 | } 73 | 74 | private static func canOpenLocalConnection(port: Int) -> Bool { 75 | 76 | func swapBytesIfNeeded(port: in_port_t) -> in_port_t { 77 | let littleEndian = Int(OSHostByteOrder()) == OSLittleEndian 78 | return littleEndian ? _OSSwapInt16(port) : port 79 | } 80 | 81 | var serverAddress = sockaddr_in() 82 | serverAddress.sin_family = sa_family_t(AF_INET) 83 | serverAddress.sin_addr.s_addr = inet_addr("127.0.0.1") 84 | serverAddress.sin_port = swapBytesIfNeeded(port: in_port_t(port)) 85 | let sock = socket(AF_INET, SOCK_STREAM, 0) 86 | 87 | let result = withUnsafePointer(to: &serverAddress) { 88 | $0.withMemoryRebound(to: sockaddr.self, capacity: 1) { 89 | connect(sock, $0, socklen_t(MemoryLayout.stride)) 90 | } 91 | } 92 | 93 | defer { 94 | close(sock) 95 | } 96 | 97 | if result != -1 { 98 | return true // Port is opened 99 | } 100 | 101 | return false 102 | } 103 | 104 | // EXPERIMENTAL 105 | private static func checkPSelectFlag() -> Bool { 106 | var kinfo = kinfo_proc() 107 | var mib: [Int32] = [CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()] 108 | var size = MemoryLayout.stride 109 | let sysctlRet = sysctl(&mib, UInt32(mib.count), &kinfo, &size, nil, 0) 110 | 111 | if sysctlRet != 0 { 112 | print("Error occured when calling sysctl(). This check may not be reliable") 113 | } 114 | 115 | return (kinfo.kp_proc.p_flag & P_SELECT) != 0 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /IOSSecuritySuite/RuntimeHookChecker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RuntimeHookChecker.swift 3 | // IOSSecuritySuite 4 | // 5 | // Created by jintao on 2020/4/24. 6 | // Copyright © 2020 wregula. All rights reserved. 7 | // 8 | //swiftlint:disable line_length 9 | 10 | import Foundation 11 | import MachO 12 | 13 | internal class RuntimeHookChecker { 14 | 15 | static private let swiftOnceDenyFishHooK: Void = { 16 | #if arch(arm64) 17 | FishHookChecker.denyFishHook("dladdr") 18 | #endif 19 | }() 20 | 21 | static func amIRuntimeHook(dyldWhiteList: [String], detectionClass: AnyClass, selector: Selector, isClassMethod: Bool) -> Bool { 22 | var method: Method? 23 | if isClassMethod { 24 | method = class_getClassMethod(detectionClass, selector) 25 | } else { 26 | method = class_getInstanceMethod(detectionClass, selector) 27 | } 28 | 29 | if method == nil { 30 | // method not found 31 | return true 32 | } 33 | 34 | let imp = method_getImplementation(method!) 35 | var info = Dl_info() 36 | 37 | _ = swiftOnceDenyFishHooK 38 | 39 | // dladdr will look through vm range of allImages for vm range of an Image that contains pointer of method and return info of the Image 40 | if dladdr(UnsafeRawPointer(imp), &info) != 1 { 41 | return false 42 | } 43 | 44 | let impDyldPath = String(cString: info.dli_fname).lowercased() 45 | 46 | // at system framework 47 | if impDyldPath.contains("/System/Library".lowercased()) { 48 | return false 49 | } 50 | 51 | // at binary of app 52 | let binaryPath = String(cString: _dyld_get_image_name(0)).lowercased() 53 | if impDyldPath.contains(binaryPath) { 54 | return false 55 | } 56 | 57 | // at whiteList 58 | if let impFramework = impDyldPath.components(separatedBy: "/").last { 59 | return !dyldWhiteList.map({ $0.lowercased() }).contains(impFramework) 60 | } 61 | 62 | // at injected framework 63 | return true 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2019, SecuRing spółka z ograniczoną odpowiedzialnością spółka komandytowa 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "IOSSecuritySuite", 7 | platforms: [ 8 | .iOS(.v10) 9 | ], 10 | products: [ 11 | .library(name: "IOSSecuritySuite", targets: ["IOSSecuritySuite"]) 12 | ], 13 | targets: [ 14 | .target(name: "IOSSecuritySuite", path: "./IOSSecuritySuite", exclude: ["IOSSecuritySuite.h", "Info.plist"]) 15 | ], 16 | swiftLanguageVersions: [.v4_2, .v5] 17 | ) 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![ISS logo](./logo.png) 2 | ### by [@_r3ggi](https://twitter.com/_r3ggi) 3 | 4 | ## ISS Description 5 | 🌏 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](https://github.com/OWASP/owasp-masvs) standard, chapter v8, then this library could save you a lot of time. 🚀 6 | 7 | What ISS detects: 8 | 9 | * Jailbreak (even the iOS 11+ with brand new indicators! 🔥) 10 | * Attached debugger 👨🏻‍🚀 11 | * If an app was run in an emulator 👽 12 | * Common reverse engineering tools running on the device 🔭 13 | 14 | ## Setup 15 | There are 4 ways you can start using IOSSecuritySuite 16 | 17 | ### 1. Add source 18 | Add `IOSSecuritySuite/*.swift` files to your project 19 | 20 | ### 2. Setup with CocoaPods 21 | `pod 'IOSSecuritySuite'` 22 | 23 | ### 3. Setup with Carthage 24 | `github "securing/IOSSecuritySuite"` 25 | 26 | ### 4. Setup with Swift Package Manager 27 | ```swift 28 | .package(url: "https://github.com/securing/IOSSecuritySuite.git", from: "1.5.0") 29 | ``` 30 | 31 | ### Update Info.plist 32 | 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](https://developer.apple.com/documentation/uikit/uiapplication/1622952-canopenurl) specifying URLs that will be queried. 33 | 34 | ```xml 35 | LSApplicationQueriesSchemes 36 | 37 | cydia 38 | undecimus 39 | sileo 40 | zbra 41 | filza 42 | activator 43 | 44 | ``` 45 | 46 | ## How to use 47 | 48 | ### Jailbreak detector module 49 | 50 | * **The simplest method** returns True/False if you just want to know if the device is jailbroken or jailed 51 | 52 | ```Swift 53 | if IOSSecuritySuite.amIJailbroken() { 54 | print("This device is jailbroken") 55 | } else { 56 | print("This device is not jailbroken") 57 | } 58 | ``` 59 | 60 | * **Verbose**, if you also want to know what indicators were identified 61 | 62 | ```Swift 63 | let jailbreakStatus = IOSSecuritySuite.amIJailbrokenWithFailMessage() 64 | if jailbreakStatus.jailbroken { 65 | print("This device is jailbroken") 66 | print("Because: \(jailbreakStatus.failMessage)") 67 | } else { 68 | print("This device is not jailbroken") 69 | } 70 | ``` 71 | The failMessage is a String containing comma-separated indicators as shown on the example below: 72 | `Cydia URL scheme detected, Suspicious file exists: /Library/MobileSubstrate/MobileSubstrate.dylib, Fork was able to create a new process` 73 | 74 | * **Verbose & filterable**, if you also want to for example identify devices that were jailbroken in the past, but now are jailed 75 | 76 | ```Swift 77 | let jailbreakStatus = IOSSecuritySuite.amIJailbrokenWithFailedChecks() 78 | if jailbreakStatus.jailbroken { 79 | if (jailbreakStatus.failedChecks.contains { $0.check == .existenceOfSuspiciousFiles }) && (jailbreakStatus.failedChecks.contains { $0.check == .suspiciousFilesCanBeOpened }) { 80 | print("This is real jailbroken device") 81 | } 82 | } 83 | ``` 84 | 85 | ### Debugger detector module 86 | ```Swift 87 | let amIDebugged: Bool = IOSSecuritySuite.amIDebugged() 88 | ``` 89 | 90 | ### Deny debugger at all 91 | ```Swift 92 | IOSSecuritySuite.denyDebugger() 93 | ``` 94 | 95 | ### Emulator detector module 96 | ```Swift 97 | let runInEmulator: Bool = IOSSecuritySuite.amIRunInEmulator() 98 | ``` 99 | 100 | ### Reverse engineering tools detector module 101 | ```Swift 102 | let amIReverseEngineered: Bool = IOSSecuritySuite.amIReverseEngineered() 103 | ``` 104 | 105 | ### System proxy detector module 106 | ```Swift 107 | let amIProxied: Bool = IOSSecuritySuite.amIProxied() 108 | ``` 109 | 110 | ## Experimental features 111 | 112 | ### Runtime hook detector module 113 | ```Swift 114 | let amIRuntimeHooked: Bool = amIRuntimeHook(dyldWhiteList: dylds, detectionClass: SomeClass.self, selector: #selector(SomeClass.someFunction), isClassMethod: false) 115 | ``` 116 | ### Symbol hook deny module 117 | ```Swift 118 | // If we want to deny symbol hook of Swift function, we have to pass mangled name of that function 119 | denySymbolHook("$s10Foundation5NSLogyySS_s7CVarArg_pdtF") // denying hooking for the NSLog function 120 | NSLog("Hello Symbol Hook") 121 | 122 | denySymbolHook("abort") 123 | abort() 124 | ``` 125 | 126 | ### MSHook detector module 127 | ```Swift 128 | // Function declaration 129 | func someFunction(takes: Int) -> Bool { 130 | return false 131 | } 132 | 133 | // Defining FunctionType : @convention(thin) indicates a “thin” function reference, which uses the Swift calling convention with no special “self” or “context” parameters. 134 | typealias FunctionType = @convention(thin) (Int) -> (Bool) 135 | 136 | // Getting pointer address of function we want to verify 137 | func getSwiftFunctionAddr(_ function: @escaping FunctionType) -> UnsafeMutableRawPointer { 138 | return unsafeBitCast(function, to: UnsafeMutableRawPointer.self) 139 | } 140 | 141 | let funcAddr = getSwiftFunctionAddr(someFunction) 142 | let amIMSHooked = IOSSecuritySuite.amIMSHooked(funcAddr) 143 | ``` 144 | 145 | ### MSHook deny module 146 | ```Swift 147 | // Function declaration 148 | func denyDebugger(value: Int) { 149 | } 150 | 151 | // Defining FunctionType : @convention(thin) indicates a “thin” function reference, which uses the Swift calling convention with no special “self” or “context” parameters. 152 | typealias FunctionType = @convention(thin) (Int)->() 153 | 154 | // Getting original function address 155 | let funcDenyDebugger: FunctionType = denyDebugger 156 | let funcAddr = unsafeBitCast(funcDenyDebugger, to: UnsafeMutableRawPointer.self) 157 | 158 | 159 | if let originalDenyDebugger = denyMSHook(funcAddr) { 160 | // Call the original function with 1337 as Int argument 161 | unsafeBitCast(originalDenyDebugger, to: FunctionType.self)(1337) 162 | } else { 163 | denyDebugger() 164 | } 165 | ``` 166 | 167 | ### File integrity verifier module 168 | 169 | ```Swift 170 | // Determine if application has been tampered with 171 | if IOSSecuritySuite.amITampered([.bundleID("biz.securing.FrameworkClientApp"), 172 | .mobileProvision("2976c70b56e9ae1e2c8e8b231bf6b0cff12bbbd0a593f21846d9a004dd181be3"), 173 | .machO("IOSSecuritySuite", "6d8d460b9a4ee6c0f378e30f137cebaf2ce12bf31a2eef3729c36889158aa7fc")]).result { 174 | print("I have been Tampered.") 175 | } 176 | else { 177 | print("I have not been Tampered.") 178 | } 179 | 180 | // Manually verify SHA256 hash value of a loaded dylib 181 | if let hashValue = IOSSecuritySuite.getMachOFileHashValue(.custom("IOSSecuritySuite")), hashValue == "6d8d460b9a4ee6c0f378e30f137cebaf2ce12bf31a2eef3729c36889158aa7fc" { 182 | print("I have not been Tampered.") 183 | } 184 | else { 185 | print("I have been Tampered.") 186 | } 187 | 188 | // Check SHA256 hash value of the main executable 189 | // Tip: Your application may retrieve this value from the server 190 | if let hashValue = IOSSecuritySuite.getMachOFileHashValue(.default), hashValue == "your-application-executable-hash-value" { 191 | print("I have not been Tampered.") 192 | } 193 | else { 194 | print("I have been Tampered.") 195 | } 196 | ``` 197 | 198 | ### Breakpoint detection module 199 | 200 | ```Swift 201 | func denyDebugger() { 202 | // add a breakpoint at here to test 203 | } 204 | 205 | typealias FunctionType = @convention(thin) ()->() 206 | let func_denyDebugger: FunctionType = denyDebugger // `: FunctionType` is a must 207 | let func_addr = unsafeBitCast(func_denyDebugger, to: UnsafeMutableRawPointer.self) 208 | let hasBreakpoint = IOSSecuritySuite.hasBreakpointAt(func_addr, functionSize: nil) 209 | 210 | if hasBreakpoint { 211 | print("Breakpoint found in the specified function") 212 | } else { 213 | print("Breakpoint not found in the specified function") 214 | } 215 | ``` 216 | 217 | ## Security considerations 218 | Before using this and other platform security checkers, you have to understand that: 219 | 220 | * 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](https://www.securing.biz/en/mobile-application-security-best-practices/index.html). 221 | * Detecting if a device is jailbroken is done locally on the device. It means that every jailbreak detector may be bypassed (even this)! 222 | * 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. 223 | * It's also a good idea to obfuscate the whole project code, including this library. See [Swiftshield](https://github.com/rockbruno/swiftshield) 224 | 225 | ## Contribution ❤️ 226 | Yes, please! If you have a better idea or you just want to improve this project, please text me on [Twitter](https://twitter.com/_r3ggi) or [Linkedin](https://www.linkedin.com/in/wojciech-regula/). Pull requests are more than welcome! 227 | 228 | ### Special thanks: 👏🏻 229 | 230 | * [kubajakowski](https://github.com/kubajakowski) for pointing out the problem with ```canOpenURL(_:)``` method 231 | * [olbartek](https://github.com/olbartek) for code review and pull request 232 | * [benbahrenburg](https://github.com/benbahrenburg) for various ISS improvements 233 | * [fotiDim](https://github.com/fotiDim) for adding new file paths to check 234 | * [gcharita](https://github.com/gcharita) for adding the Swift Package Manager support 235 | * [rynaardb](https://github.com/rynaardb) for creating the `amIJailbrokenWithFailedChecks()` method 236 | * [undeaDD](https://github.com/undeaDD) for various ISS improvements 237 | * [fnxpt](https://github.com/fnxpt) for adding multiple JB detections 238 | * [TannerJin](https://github.com/TannerJin) for MSHook, RuntimeHook and SymbolHook modules 239 | * [NikoXu](https://github.com/NikoXu) for adding file integrity module 240 | * [hellpf](https://github.com/hellpf) for fixing a dangling socket problem 241 | 242 | ## TODO 243 | 244 | * [ ] Research Installer5 and Zebra Package Manager detection ( Cydia Alternatives ) 245 | 246 | ## License 247 | See the LICENSE file. 248 | 249 | ## References 250 | While creating this tool I used: 251 | 252 | * 🔗 https://github.com/TheSwiftyCoder/JailBreak-Detection 253 | * 🔗 https://github.com/abhinashjain/jailbreakdetection 254 | * 🔗 https://gist.github.com/ddrccw/8412847 255 | * 🔗 https://gist.github.com/bugaevc/4307eaf045e4b4264d8e395b5878a63b 256 | * 📚 "iOS Application Security" by David Thiel 257 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TannerJin/IOSSecuritySuite/f4dc436d3d9cfb917956275947a100ff1c123b0b/logo.png --------------------------------------------------------------------------------