├── .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 | 
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
--------------------------------------------------------------------------------