【Swift】脱獄を検知する

# はじめに
iOS端末は脱獄(JailBreak)するとappleからの制限を解除する事ができます。
制限が解除されるとアプリ開発者が触れて欲しくない値にもアクセスされる可能性があり、よろしくないです。
このような行為により、チートが発生します。

今回は脱獄端末を検知してみようと思います。
ただ、今回紹介する方法では脱獄検知回避ツールを使われてしまうと簡単に破られます笑

# 実装
`Jailbreak`というフラグを作って脱獄判定を行います
```swift
import SwiftUI

struct ContentView: View {
    @State var isDeviceJailBroken: Bool = false
    var body: some View {
        Text(isDeviceJailBroken ? "❌: 脱獄あり" : "✅: 脱獄なし")
    }
}
```

脱獄判定にはこちらの関数を使用します。
```swift
    // 脱獄判定
    func checkDeviceJailBroken() {
        if access("/Applications/Cydia.app", F_OK) != -1 ||
            access("/Applications/blackra1n.app", F_OK) != -1 ||
            access("/Applications/FakeCarrier.app", F_OK) != -1 ||
            access("/Applications/Icy.app", F_OK) != -1 ||
            access("/Applications/IntelliScreen.app", F_OK) != -1 ||
            access("/Applications/MxTube.app", F_OK) != -1 ||
            access("/Applications/RockApp.app", F_OK) != -1 ||
            access("/Applications/SBSettings.app", F_OK) != -1 ||
            access("/Applications/WinterBoard.app", F_OK) != -1 ||
            access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) != -1 ||
            access("/Library/MobileSubstrate/DynamicLibraries/LiveClock.plist", F_OK) != -1 ||
            access("/Library/MobileSubstrate/DynamicLibraries/Veency.plist", F_OK) != -1 ||
            access("/private/var/lib/apt", F_OK) != -1 ||
            access("/private/var/lib/cydia", F_OK) != -1 ||
            access("/private/var/mobile/Library/SBSettings/Themes", F_OK) != -1 ||
            access("/private/var/stash", F_OK) != -1 ||
            access("/private/var/tmp/cydia.log", F_OK) != -1 ||
            access("/System/Library/LaunchDaemons/com.ikey.bbot.plist", F_OK) != -1 ||
            access("/System/Library/LaunchDaemons/com.saurik.Cydia.Startup.plist", F_OK) != -1 ||
            access("/usr/bin/sshd", F_OK) != -1 ||
            access("/usr/libexec/sftp-server", F_OK) != -1 ||
            access("/usr/sbin/sshd", F_OK) != -1 ||
            access("/bin/bash", F_OK) != -1 ||
            access("/etc/apt", F_OK) != -1
        {
            isDeviceJailBroken = true
        } else if let url = URL(string: "cydia://package/com.example.package") {
            if UIApplication.shared.canOpenURL(url) {
                isDeviceJailBroken = true
            } else {
                isDeviceJailBroken = false
            }
        } else {
            isDeviceJailBroken = false
        }
    }
```
`onAppear`で表示時に判定します
```swift
import SwiftUI

struct ContentView: View {
    @State var isDeviceJailBroken: Bool = false
    var body: some View {
        Text(isDeviceJailBroken ? "❌: 脱獄あり" : "✅: 脱獄なし")
            .onAppear() {
                checkDeviceJailBroken()
            }
    }
    // 脱獄判定
    func checkDeviceJailBroken() {
        if access("/Applications/Cydia.app", F_OK) != -1 ||
            access("/Applications/blackra1n.app", F_OK) != -1 ||
            access("/Applications/FakeCarrier.app", F_OK) != -1 ||
            access("/Applications/Icy.app", F_OK) != -1 ||
            access("/Applications/IntelliScreen.app", F_OK) != -1 ||
            access("/Applications/MxTube.app", F_OK) != -1 ||
            access("/Applications/RockApp.app", F_OK) != -1 ||
            access("/Applications/SBSettings.app", F_OK) != -1 ||
            access("/Applications/WinterBoard.app", F_OK) != -1 ||
            access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) != -1 ||
            access("/Library/MobileSubstrate/DynamicLibraries/LiveClock.plist", F_OK) != -1 ||
            access("/Library/MobileSubstrate/DynamicLibraries/Veency.plist", F_OK) != -1 ||
            access("/private/var/lib/apt", F_OK) != -1 ||
            access("/private/var/lib/cydia", F_OK) != -1 ||
            access("/private/var/mobile/Library/SBSettings/Themes", F_OK) != -1 ||
            access("/private/var/stash", F_OK) != -1 ||
            access("/private/var/tmp/cydia.log", F_OK) != -1 ||
            access("/System/Library/LaunchDaemons/com.ikey.bbot.plist", F_OK) != -1 ||
            access("/System/Library/LaunchDaemons/com.saurik.Cydia.Startup.plist", F_OK) != -1 ||
            access("/usr/bin/sshd", F_OK) != -1 ||
            access("/usr/libexec/sftp-server", F_OK) != -1 ||
            access("/usr/sbin/sshd", F_OK) != -1 ||
            access("/bin/bash", F_OK) != -1 ||
            access("/etc/apt", F_OK) != -1
        {
            isDeviceJailBroken = true
        } else if let url = URL(string: "cydia://package/com.example.package") {
            if UIApplication.shared.canOpenURL(url) {
                isDeviceJailBroken = true
            } else {
                isDeviceJailBroken = false
            }
        } else {
            isDeviceJailBroken = false
        }
    }
}
```

これで完成のように思いますが、このままではシュミレーターで実行した際にも脱獄判定されてしまいます。

<img width="1182" alt="スクリーンショット 2022-08-08 20 22 37" src="https://user-images.githubusercontent.com/84154073/183407099-19899047-62e3-4a85-bc6e-9156cb951451.png">

シュミレーターで実行した際は脱獄判定を行わないように変更します。
シュミレーター判定はこんな感じでできます。
```swift
#if !targetEnvironment(simulator)
// 実機のみ実行されます
#endif
```
こちらを`onAppear`に追加します

# 完成
```swift
import SwiftUI

struct ContentView: View {
    @State var isDeviceJailBroken: Bool = false
    var body: some View {
        Text(isDeviceJailBroken ? "❌: 脱獄あり" : "✅: 脱獄なし")
            .onAppear() {
                #if !targetEnvironment(simulator)
                checkDeviceJailBroken()
                #endif
            }
    }
    // 脱獄判定
    func checkDeviceJailBroken() {
        if access("/Applications/Cydia.app", F_OK) != -1 ||
            access("/Applications/blackra1n.app", F_OK) != -1 ||
            access("/Applications/FakeCarrier.app", F_OK) != -1 ||
            access("/Applications/Icy.app", F_OK) != -1 ||
            access("/Applications/IntelliScreen.app", F_OK) != -1 ||
            access("/Applications/MxTube.app", F_OK) != -1 ||
            access("/Applications/RockApp.app", F_OK) != -1 ||
            access("/Applications/SBSettings.app", F_OK) != -1 ||
            access("/Applications/WinterBoard.app", F_OK) != -1 ||
            access("/Library/MobileSubstrate/MobileSubstrate.dylib", F_OK) != -1 ||
            access("/Library/MobileSubstrate/DynamicLibraries/LiveClock.plist", F_OK) != -1 ||
            access("/Library/MobileSubstrate/DynamicLibraries/Veency.plist", F_OK) != -1 ||
            access("/private/var/lib/apt", F_OK) != -1 ||
            access("/private/var/lib/cydia", F_OK) != -1 ||
            access("/private/var/mobile/Library/SBSettings/Themes", F_OK) != -1 ||
            access("/private/var/stash", F_OK) != -1 ||
            access("/private/var/tmp/cydia.log", F_OK) != -1 ||
            access("/System/Library/LaunchDaemons/com.ikey.bbot.plist", F_OK) != -1 ||
            access("/System/Library/LaunchDaemons/com.saurik.Cydia.Startup.plist", F_OK) != -1 ||
            access("/usr/bin/sshd", F_OK) != -1 ||
            access("/usr/libexec/sftp-server", F_OK) != -1 ||
            access("/usr/sbin/sshd", F_OK) != -1 ||
            access("/bin/bash", F_OK) != -1 ||
            access("/etc/apt", F_OK) != -1
        {
            isDeviceJailBroken = true
        } else if let url = URL(string: "cydia://package/com.example.package") {
            if UIApplication.shared.canOpenURL(url) {
                isDeviceJailBroken = true
            } else {
                isDeviceJailBroken = false
            }
        } else {
            isDeviceJailBroken = false
        }
    }
}
```

# 追記(2022/08/17)
以下を設定しないと`canOpenURL(url)`が常にfalseになる事みたいです
![スクリーンショット 2022-08-17 16.46.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/c9615734-429a-4d01-e5a5-03519463da05.png)

```Info.plist
	<key>LSApplicationQueriesSchemes</key>
	<array>
		<string>cydia</string>
	</array>
```


# おわり
今回作成したプロジェクトです

https://github.com/SNQ-2001/JailBreak-Check-Demo