iOS脱獄(Jailbreak)検知機能の実装

# はじめに
この文書では、iOSデバイスが脱獄(Jailbreak)されているかどうかを判断するSwiftクラス**JailbreakChecker**について解説します。
このクラスは、いくつかの方法を用いてデバイスが脱獄(Jailbreak)されているかをチェックします。
# 機能概要
**JailbreakChecker**クラスは以下の機能を提供します：
1. **シミュレーターのチェック** - デバイスがシミュレーターかどうかを判断します。
2. **Cydiaのインストールチェック** - CydiaアプリがインストールされているかをURIスキームを用いて判断します。
3. **怪しいパスの存在チェック** - 脱獄(Jailbreak)されたデバイスに特有のファイルパスが存在するかをチェックします。
4. **システムファイルの編集権限チェック** - アプリがシステムファイルを編集できるかどうかをテストします。
# 実装詳細
### 1. シミュレーターのチェック
```swift
private static var isSimulator: Bool {
    return TARGET_OS_SIMULATOR != 0
}
```
このメソッドは、デバイスがシミュレーターかどうかを確認します。
### 2. Cydiaのインストールチェック
```swift
private static func hasCydiaInstalled() -> Bool {
    if let url = URL(string: "cydia://package/com.example.package"), UIApplication.shared.canOpenURL(url) {
        return true
    }
    return false
}
```
Cydiaアプリがインストールされているかを、URIスキームを用いてチェックします。
### 3. 怪しいパスの存在チェック
```swift
private static func isSuspiciousPathExists() -> Bool {
    for path in suspiciousPathsToCheck where FileManager.default.fileExists(atPath: path) {
        return true
    }
    return false
}
```
特定のパスが存在するかどうかをチェックし、それによって脱獄(Jailbreak)の状態を判断します。

### 怪しいパス
脱獄(Jailbreak)されたデバイスでは、以下のような特定のパスが存在することがあります：
```swift
// Path list url: https://github.com/OWASP/owasp-mastg/blob/master/Document/0x06j-Testing-Resiliency-Against-Reverse-Engineering.md
static var suspiciousPathsToCheck: [String] {
    return ["/Applications/Cydia.app",
            "/Applications/FakeCarrier.app",
            "/Applications/Icy.app",
            "/Applications/IntelliScreen.app",
            "/Applications/MxTube.app",
            "/Applications/RockApp.app",
            "/Applications/SBSettings.app",
            "/Applications/WinterBoard.app",
            "/Applications/blackra1n.app",
            "/Library/MobileSubstrate/MobileSubstrate.dylib",
            "/Library/MobileSubstrate/DynamicLibraries/LiveClock.plist",
            "/Library/MobileSubstrate/DynamicLibraries/Veency.plist",
            "/Library/MobileSubstrate/MobileSubstrate.dylib",
            "/System/Library/LaunchDaemons/com.ikey.bbot.plist",
            "/System/Library/LaunchDaemons/com.saurik.Cydia.Startup.plist",
            "/bin/bash",
            "/bin/sh",
            "/etc/apt",
            "/etc/ssh/sshd_config",
            "/private/var/lib/apt",
            "/private/var/lib/cydia",
            "/private/var/mobile/Library/SBSettings/Themes",
            "/private/var/stash",
            "/private/var/tmp/cydia.log",
            "/var/tmp/cydia.log",
            "/usr/bin/sshd",
            "/usr/libexec/sftp-server",
            "/usr/libexec/ssh-keysign",
            "/usr/sbin/sshd",
            "/var/cache/apt",
            "/var/lib/apt",
            "/var/lib/cydia",
            "/usr/sbin/frida-server",
            "/usr/bin/cycript",
            "/usr/local/bin/cycript",
            "/usr/lib/libcycript.dylib",
            "/var/log/syslog"]
}
```
これらのパスの存在をチェックすることで、脱獄(Jailbreak)の有無をより確実に判断することができます。
### 4. システムファイルの編集権限チェック
```swift
private static func canEditSystemFiles() -> Bool {
    do {
        let pathToFileInRestrictedDirectory = "/private/jailbreak.txt"
        try "This is a test.".write(toFile: pathToFileInRestrictedDirectory, atomically: true, encoding: String.Encoding.utf8)
        try FileManager.default.removeItem(atPath: pathToFileInRestrictedDirectory)
        return true
    } catch {
        return false
    }
}
```
このメソッドは、アプリがシステムレベルのファイルを編集できるかをテストします。これが可能な場合、デバイスは脱獄(Jailbreak)されている可能性が高いです。
# 全体像
```swift
class JailbreakChecker {
    // Check if the device is jailbroken
    static var isJailBroken: Bool {
        if JailbreakChecker.isSimulator { return false }
        if JailbreakChecker.hasCydiaInstalled() { return true }
        if JailbreakChecker.isSuspiciousPathExists() { return true }
        return JailbreakChecker.canEditSystemFiles()
    }

    // Check if the device is a simulator
    private static var isSimulator: Bool {
        return TARGET_OS_SIMULATOR != 0
    }

    // Check if Cydia is installed (using URI Scheme)
    private static func hasCydiaInstalled() -> Bool {
        if let url = URL(string: "cydia://package/com.example.package"), UIApplication.shared.canOpenURL(url) {
            return true
        }
        return false
    }

    // Check if suspicious path exists
    private static func isSuspiciousPathExists() -> Bool {
        for path in suspiciousPathsToCheck where FileManager.default.fileExists(atPath: path) {
            return true
        }
        return false
    }

    // Check if the app can edit system files
    private static func canEditSystemFiles() -> Bool {
        do {
            let pathToFileInRestrictedDirectory = "/private/jailbreak.txt"
            try "This is a test.".write(toFile: pathToFileInRestrictedDirectory, atomically: true, encoding: String.Encoding.utf8)
            try FileManager.default.removeItem(atPath: pathToFileInRestrictedDirectory)
            return true
        } catch {
            return false
        }
    }

    // suspicious paths to check
    // Path list url: https://github.com/OWASP/owasp-mastg/blob/master/Document/0x06j-Testing-Resiliency-Against-Reverse-Engineering.md
    static var suspiciousPathsToCheck: [String] {
        return ["/Applications/Cydia.app",
                "/Applications/FakeCarrier.app",
                "/Applications/Icy.app",
                "/Applications/IntelliScreen.app",
                "/Applications/MxTube.app",
                "/Applications/RockApp.app",
                "/Applications/SBSettings.app",
                "/Applications/WinterBoard.app",
                "/Applications/blackra1n.app",
                "/Library/MobileSubstrate/MobileSubstrate.dylib",
                "/Library/MobileSubstrate/DynamicLibraries/LiveClock.plist",
                "/Library/MobileSubstrate/DynamicLibraries/Veency.plist",
                "/Library/MobileSubstrate/MobileSubstrate.dylib",
                "/System/Library/LaunchDaemons/com.ikey.bbot.plist",
                "/System/Library/LaunchDaemons/com.saurik.Cydia.Startup.plist",
                "/bin/bash",
                "/bin/sh",
                "/etc/apt",
                "/etc/ssh/sshd_config",
                "/private/var/lib/apt",
                "/private/var/lib/cydia",
                "/private/var/mobile/Library/SBSettings/Themes",
                "/private/var/stash",
                "/private/var/tmp/cydia.log",
                "/var/tmp/cydia.log",
                "/usr/bin/sshd",
                "/usr/libexec/sftp-server",
                "/usr/libexec/ssh-keysign",
                "/usr/sbin/sshd",
                "/var/cache/apt",
                "/var/lib/apt",
                "/var/lib/cydia",
                "/usr/sbin/frida-server",
                "/usr/bin/cycript",
                "/usr/local/bin/cycript",
                "/usr/lib/libcycript.dylib",
                "/var/log/syslog"]
    }
}
```
# 使用方法
```swift
if JailbreakChecker.isJailBroken {
    // Do something
}
```
このクラスを使用して、アプリが脱獄(Jailbreak)されたデバイス上で実行されているかどうかを判定できます。例えば、アプリの初期化時に**JailbreakChecker.isJailBroken**をチェックして、脱獄(Jailbreak)されている場合は特定の処理を行うことができます。