SecurityKit 是一个轻量级、易于使用的 Swift 库,它根据 OWASP MASVS 标准的 v8 章节,帮助保护 iOS 应用,提供高级的安全和防篡改层。
SecurityKit 可通过 Swift Package Manager 获取。
Swift Package Manager 是一种用于自动化 Swift 代码分发的工具,并已集成到 swift
编译器中。
一旦你设置好了你的 Swift 包,添加 SecurityKit 作为依赖项就像将其添加到你的 Package.swift
的 dependencies
值中一样简单。
dependencies: [
.package(url: "https://github.com/FuturraGroup/SecurityKit.git", .branch("main"))
]
为了使越狱检测能够正确工作,你需要更新你的主 Info.plist。
<key>LSApplicationQueriesSchemes</key>
<array>
<string>cydia</string>
<string>undecimus</string>
<string>sileo</string>
<string>zbra</string>
<string>filza</string>
</array>
if SecurityKit.isJailBroken() {
print("This device is jailbroken")
} else {
print("This device is not jailbroken")
}
let jailbreakStatus = SecurityKit.isJailBrokenWithErrorMessage()
if jailbreakStatus.jailbroken {
print("This device is jailbroken")
print("Because: \(jailbreakStatus.errorMessage)")
} else {
print("This device is not jailbroken")
}
let jailbreakStatus = SecurityKit.isJailBrokenWithErrorDetects()
if jailbreakStatus.jailbroken {
print("This device is jailbroken")
print("The following checks failed: \(jailbreakStatus.errorDetects)")
}
if SecurityKit.isSimulator() {
print("app is running on the simulator")
} else {
print("app is not running on the simulator")
}
if SecurityKit.isReverseEngineered() {
print("This device has reverse engineering tools")
} else {
print("This device does not have reverse engineering tools")
}
let reStatus = SecurityKit.isReverseEngineeredWithErrorDetect()
if reStatus.reverseEngineered {
print("SecurityKit: This device has evidence of reverse engineering")
print("SecurityKit: The following detects failed: \(reStatus.errorDetect)")
}
let isDebugged: Bool = SecurityKit.isDebugged()
SecurityKit.denyDebugger()
let isNotLaunchD: Bool = SecurityKit.isParentPidUnexpected()
func denyDebugger() {
// add a breakpoint at here to test
}
typealias FunctionType = @convention(thin) ()->()
let func_denyDebugger: FunctionType = denyDebugger // `: FunctionType` is a must
let func_addr = unsafeBitCast(func_denyDebugger, to: UnsafeMutableRawPointer.self)
let hasBreakpoint: Bool = SecurityKit.hasBreakpointAt(func_addr, functionSize: nil)
// Set a breakpoint at the testWatchpoint function
func testWatchpoint() -> Bool{
// lldb: watchpoint set expression ptr
var ptr = malloc(9)
// lldb: watchpoint set variable count
var count = 3
return SecurityKit.hasWatchpoint()
}
if SecurityKit.isTampered(
[.bundleID("com.app.bundle"),
.mobileProvision("your-mobile-provision-sha256-value")]
).result {
print("SecurityKit: I have been Tampered.")
} else {
print("SecurityKit: I have not been Tampered.")
}
// Manually verify SHA256 hash value of a loaded dylib
if let hashValue = SecurityKit.getMachOFileHashValue(.custom("SecurityKit")),
hashValue == "6d8d460b9a4ee6c0f378e30f137cebaf2ce12bf31a2eef3729c36889158aa7fc" {
print("SecurityKit: I have not been Tampered.")
} else {
print("SecurityKit: I have been Tampered.")
}
if let loadedDylib = SecurityKit.findLoadedDylibs() {
print("SecurityKit: Loaded dylibs: \(loadedDylib)")
}
function_address
是否已被 MSHook
钩住func denyDebugger() { ... }
typealias FunctionType = @convention(thin) ()->()
let func_denyDebugger: FunctionType = denyDebugger // `: FunctionType` is must
let func_addr = unsafeBitCast(func_denyDebugger, to: UnsafeMutableRawPointer.self)
let isMSHooked: Bool = SecurityKit.isMSHooked(func_addr)
MSHook
钩住的原始 function_address
func denyDebugger(value: Int) { ... }
typealias FunctionType = @convention(thin) (Int)->()
let funcDenyDebugger: FunctionType = denyDebugger
let funcAddr = unsafeBitCast(funcDenyDebugger, to: UnsafeMutableRawPointer.self)
if let originalDenyDebugger = SecurityKit.denyMSHook(funcAddr) {
// Call orignal function with 1337 as Int argument
unsafeBitCast(originalDenyDebugger, to: FunctionType.self)(1337)
} else {
denyDebugger()
}
fishhook
钩住的 symbol
SecurityKit.denySymbolHook("$s10Foundation5NSLogyySS_s7CVarArg_pdtF") // Foudation's NSlog of Swift
NSLog("Hello Symbol Hook")
SecurityKit.denySymbolHook("abort")
abort()
fishhook
在某个镜像中钩住的 symbol
for i in 0..<_dyld_image_count() {
if let imageName = _dyld_get_image_name(i) {
let name = String(cString: imageName)
if name.contains("SecurityKit"), let image = _dyld_get_image_header(i) {
SecurityKit.denySymbolHook("dlsym", at: image, imageSlide: _dyld_get_image_vmaddr_slide(i))
break
}
}
}
objc call
是否已被 RuntimeHook 钩住,例如被 Flex
钩住class SomeClass {
@objc dynamic func someFunction() { ... }
}
let dylds = ["SecurityKit", ...]
let isRuntimeHook: Bool = SecurityKit.isRuntimeHook(
dyldAllowList: dylds,
detectionClass: SomeClass.self,
selector: #selector(SomeClass.someFunction),
isClassMethod: false
)
let isProxied: Bool = SecurityKit.isProxied()
let isLockdownModeEnable: Bool = SecurityKit.isLockdownModeEnable()
欢迎为改进做出贡献。 请随时提交 pull request 以帮助扩展该库。如果您有任何问题、功能建议或错误报告,请将其发送到 Issues。
MIT License
Copyright (c) 2025 Futurra Group
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.