<aside> 🌟

安全检测有很多类型,但现在越狱大多采用root-hide方式,故一些类型检测已失效。

</aside>

root-hide 环境被检测的有如下类型:

  1. 动态调试
  2. 可疑文件
  3. 文件完整性

详细代码参照SecuScope项目

GitHub - cheng-ren/SecuScope: iOS 逆向安全桩App

模拟器

internal class EmulatorChecker {
	
  static func runInEmulator() -> Bool {
    return checkCompile() || checkRuntime()
  }

  private static func checkRuntime() -> Bool {
    return ProcessInfo.processInfo.environment["SIMULATOR_DEVICE_NAME"] != nil
  }

  private static func checkCompile() -> Bool {
#if targetEnvironment(simulator)
    return true
#else
    return false
#endif
  }
}

URL Scheme

typealias CheckResult = (passed: Bool, failMessage: String)

private static func canOpenUrlFromList(urlSchemes: [String]) -> CheckResult {
    for urlScheme in urlSchemes {
      if let url = URL(string: urlScheme) {
        if UIApplication.shared.canOpenURL(url) {
          return(false, "\\(urlScheme) URL scheme detected")
        }
      }
    }
    return (true, "")
  }
  
static func checkURLSchemes() -> CheckResult {
    let urlSchemes = [
      "cydia://",
      "activator://",
      "undecimus://",
      "sileo://",
      "zbra://",
      "filza://"
    ]
    return canOpenUrlFromList(urlSchemes: urlSchemes)
}

可疑文件是否存在

private static func checkExistenceOfSuspiciousFiles() -> CheckResult {
    var paths = [
      "/var/mobile/Library/Preferences/ABPattern", // A-Bypass
      "/usr/lib/ABDYLD.dylib", // A-Bypass,
      "/usr/lib/ABSubLoader.dylib", // A-Bypass
      "/usr/sbin/frida-server", // frida
      "/etc/apt/sources.list.d/electra.list", // electra
      "/etc/apt/sources.list.d/sileo.sources", // electra
      "/.bootstrapped_electra", // electra
      "/usr/lib/libjailbreak.dylib", // electra
      "/jb/lzma", // electra
      "/.cydia_no_stash", // unc0ver
      "/.installed_unc0ver", // unc0ver
      "/jb/offsets.plist", // unc0ver
      "/usr/share/jailbreak/injectme.plist", // unc0ver
      "/etc/apt/undecimus/undecimus.list", // unc0ver
      "/var/lib/dpkg/info/mobilesubstrate.md5sums", // unc0ver
      "/Library/MobileSubstrate/MobileSubstrate.dylib",
      "/jb/jailbreakd.plist", // unc0ver
      "/jb/amfid_payload.dylib", // unc0ver
      "/jb/libjailbreak.dylib", // unc0ver
      "/usr/libexec/cydia/firmware.sh",
      "/var/lib/cydia",
      "/etc/apt",
      "/private/var/lib/apt",
      "/var/log/apt",
      "/Applications/Cydia.app",
      "/private/var/stash",
      "/private/var/lib/apt/",
      "/private/var/lib/cydia",
      "/private/var/cache/apt/",
      "/private/var/log/syslog",
      "/private/var/tmp/cydia.log",
      "/Applications/Icy.app",
      "/Applications/MxTube.app",
      "/Applications/RockApp.app",
      "/Applications/blackra1n.app",
      "/Applications/SBSettings.app",
      "/Applications/FakeCarrier.app",
      "/Applications/WinterBoard.app",
      "/Applications/IntelliScreen.app",
      "/private/var/mobile/Library/SBSettings/Themes",
      "/Library/MobileSubstrate/CydiaSubstrate.dylib",
      "/System/Library/LaunchDaemons/com.ikey.bbot.plist",
      "/Library/MobileSubstrate/DynamicLibraries/Veency.plist",
      "/Library/MobileSubstrate/DynamicLibraries/LiveClock.plist",
      "/System/Library/LaunchDaemons/com.saurik.Cydia.Startup.plist",
      "/Applications/Sileo.app",
      "/var/binpack",
      "/Library/PreferenceBundles/LibertyPref.bundle",
      "/Library/PreferenceBundles/ShadowPreferences.bundle",
      "/Library/PreferenceBundles/ABypassPrefs.bundle",
      "/Library/PreferenceBundles/FlyJBPrefs.bundle",
      "/Library/PreferenceBundles/Cephei.bundle",
      "/Library/PreferenceBundles/SubstitutePrefs.bundle",
      "/Library/PreferenceBundles/libhbangprefs.bundle",
      "/usr/lib/libhooker.dylib",
      "/usr/lib/libsubstitute.dylib",
      "/usr/lib/substrate",
      "/usr/lib/TweakInject",
      "/var/binpack/Applications/loader.app", // checkra1n
      "/Applications/FlyJB.app", // Fly JB X
      "/Applications/Zebra.app", // Zebra
      "/Library/BawAppie/ABypass", // ABypass
      "/Library/MobileSubstrate/DynamicLibraries/SSLKillSwitch2.plist", // SSL Killswitch
      "/Library/MobileSubstrate/DynamicLibraries/PreferenceLoader.plist", // PreferenceLoader
      "/Library/MobileSubstrate/DynamicLibraries/PreferenceLoader.dylib", // PreferenceLoader
      "/Library/MobileSubstrate/DynamicLibraries", // DynamicLibraries directory in general
      "/var/mobile/Library/Preferences/me.jjolano.shadow.plist"
    ]
    
    // These files can give false positive in the emulator
    if !EmulatorChecker.runInEmulator() {
      paths += [
        "/bin/bash",
        "/usr/sbin/sshd",
        "/usr/libexec/ssh-keysign",
        "/bin/sh",
        "/etc/ssh/sshd_config",
        "/usr/libexec/sftp-server",
        "/usr/bin/ssh"
      ]
    }
    
    for path in paths {
      if FileManager.default.fileExists(atPath: path) {
        return (false, "Suspicious file exists: \\(path)")
      } else if let result = FileChecker.checkExistenceOfSuspiciousFilesViaStat(path: path) {
        return result
      } else if let result = FileChecker.checkExistenceOfSuspiciousFilesViaFOpen(
        path: path,
        mode: .readable
      ) {
        return result
      } else if let result = FileChecker.checkExistenceOfSuspiciousFilesViaAccess(
        path: path,
        mode: .readable
      ) {
        return result
      }
    }
    
    return (true, "")

FileChecker.swift

可疑文件是否能打开

static func checkSuspiciousFilesCanBeOpened() -> CheckResult {
    var paths = [
      "/.installed_unc0ver",
      "/.bootstrapped_electra",
      "/Applications/Cydia.app",
      "/Library/MobileSubstrate/MobileSubstrate.dylib",
      "/etc/apt",
      "/var/log/apt"
    ]
    
    // These files can give false positive in the emulator
    if !EmulatorChecker.runInEmulator() {
      paths += [
        "/bin/bash",
        "/usr/sbin/sshd",
        "/usr/bin/ssh"
      ]
    }
    
    for path in paths {
      if FileManager.default.isReadableFile(atPath: path) {
        return (false, "Suspicious file can be opened: \\(path)")
      } else if let result = FileChecker.checkExistenceOfSuspiciousFilesViaFOpen(
        path: path,
        mode: .writable
      ) {
        return result
      } else if let result = FileChecker.checkExistenceOfSuspiciousFilesViaAccess(
        path: path,
        mode: .writable
      ) {
        return result
      }
    }
    
    return (true, "")
  }

受限目录是否可写

static func checkRestrictedDirectoriesWriteable() -> CheckResult {
    let paths = [
      "/",
      "/root/",
      "/private/",
      "/jb/"
    ]
    
    if FileChecker.checkRestrictedPathIsReadonlyViaStatvfs(path: "/") == false {
      return (false, "Restricted path '/' is not Read-Only")
    } else if FileChecker.checkRestrictedPathIsReadonlyViaStatfs(path: "/") == false {
      return (false, "Restricted path '/' is not Read-Only")
    } else if FileChecker.checkRestrictedPathIsReadonlyViaGetfsstat(name: "/") == false {
      return (false, "Restricted path '/' is not Read-Only")
    }
    
    // If library won't be able to write to any restricted directory the return(false, ...) is never reached
    // because of catch{} statement
    for path in paths {
      do {
        let pathWithSomeRandom = path + UUID().uuidString
        try "Jailbroken?".write(
          toFile: pathWithSomeRandom,
          atomically: true,
          encoding: String.Encoding.utf8
        )
        // clean if succesfully written
        try FileManager.default.removeItem(atPath: pathWithSomeRandom)
        return (false, "Wrote to restricted path: \\(path)")
      } catch {}
    }
    
    return (true, "")
  }