iOS的越狱检测和反越狱检测原理剖析
为什么要检测越狱?因为越狱后会大幅降低安全性。对于一些金融类的APP或者游戏类的,因为监管原因、资金安全问题,甚至防止使用越狱分析等,需要进行检测。不过其实越狱与反越狱就像矛与盾一样,都没有完美的方案。用一些反越狱插件可以防99%的越狱检测方式,本质上因为越狱后可以hook已知的所有检测越狱的方法,包括我下面的几种常用的。对于具体的反越狱插件可以用一些特定的方案来辅助检测。
具体代码清参考我的github
一、 越狱检测方案
1. 检测动态库
1.1 判断动态库stat是否是系统的库,并利用stat 来检测一些特定的文件权限
stat 命令时OS系统中用来判断文件信息的,但是对于私有的路径调用命令返回的是-1,如果越狱后,因为权限变化,可以通过stat返回私有目录下的文件信息。具体命令可以参考官方文档
代码实现:
BOOL isStatNotSystemLib() {
if(TARGET_IPHONE_SIMULATOR)return NO;
int ret ;
Dl_info dylib_info;
int (*func_stat)(const char *, struct stat *) = stat;
if ((ret = dladdr(func_stat, &dylib_info))) {
NSString *fName = [NSString stringWithUTF8String: dylib_info.dli_fname];
if(![fName isEqualToString:@"/usr/lib/system/libsystem_kernel.dylib"]){
return YES;
}
}
char *JbPaths[] = {"/Applications/Cydia.app",
"/usr/sbin/sshd",
"/bin/bash",
"/etc/apt",
"/Library/MobileSubstrate",
"/User/Applications/"};
for (int i = 0;i < sizeof(JbPaths) / sizeof(char *);i++) {
struct stat stat_info;
if (0 == stat(JbPaths[i], &stat_info)) {
return YES;
}
}
return NO;
}
1.2 判断是否注入了动态库
利用_dyld_get_image_name来获取动态库的名字,并查看是否有相关的动态库,这个相对来说最为准确,因为这个系统库运行的更早,且很多越狱的也需要依赖这个库的正常运行,所以更难被绕过
BOOL isInjectedWithDynamicLibrary()
{
int i=0;
char *substrate = "/Library/MobileSubstrate/MobileSubstrate.dylib";
while(true){
// hook _dyld_get_image_name方法可以绕过
const char *name = _dyld_get_image_name(i++);
if(name==NULL){
break;
}
if (name != NULL) {
if (strcmp(name,substrate)==0) {
return YES;
}
}
}
return NO;
}
2. 判断是否有越狱相关文件或权限
2.1 判断是否能打开越狱软件
利用URL Scheme来查看是否能够代开比如cydia这些越狱软件
//Check cydia URL hook canOpenURL 来绕过
if([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"cydia://package/com.avl.com"]])
{
return YES;
}
if([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"cydia://package/com.example.package"]])
{
return YES;
}
2.2 判断是否可以访问一些越狱的文件
越狱后会产生额外的文件,通过判断是否存在这些文件来判断是否越狱了,可以用fopen和FileManager两个不同的方法去获取
BOOL fileExist(NSString* path)
{
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL isDirectory = NO;
if([fileManager fileExistsAtPath:path isDirectory:&isDirectory]){
return YES;
}
return NO;
}
BOOL directoryExist(NSString* path)
{
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL isDirectory = YES;
if([fileManager fileExistsAtPath:path isDirectory:&isDirectory]){
return YES;
}
return NO;
}
BOOL canOpen(NSString* path)
{
FILE *file = fopen([path UTF8String], "r");
if(file==nil){
return fileExist(path) || directoryExist(path);
}
fclose(file);
return YES;
}
NSArray* checks = [[NSArray alloc] initWithObjects:@"/Application/Cydia.app",
@"/Library/MobileS