iOS安全 – 越狱检测
越狱是什么
越狱是指利用iOS系统的默写漏洞,通过指令取得了iOS的root权限.可以自己优化系统,获得系统权限可以修改系统文件,可以安装更多拥有高系统权限的软件,实现更多高级功能!例如:与其他设备蓝牙发送文件、短信回执、来电归属地、文件管理、浏览器下载插件、flash插件、内容管理等等。
所以在越狱情况下,难免会有一些恶意应用会危害到我们的应用,所以我们需要检测当前是否为越狱环境,从而禁用或者关闭一些功能(如截获我们的网络请求/响应 进行修改,获取我们沙盒的文件,,,,).
越狱检测方法
检测沙盒机制和越狱工具
在越狱环境下,iOS的沙盒机制将被破坏,所以可以去访问本不能访问的路径.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| - (BOOL)checkPath { BOOL jailBroken = NO; NSString * cydiaPath = @"/Applications/Cydia.app"; NSString * aptPath = @"/private/var/lib/apt"; if ([[NSFileManager defaultManager] fileExistsAtPath:cydiaPath]) { jailBroken = YES; } if ([[NSFileManager defaultManager] fileExistsAtPath:aptPath]) { jailBroken = YES; } return jailBroken; } · 上述路径也可替换为以下常见越狱工具路径: /Library/MobileSubstrate/MobileSubstrate.dylib /Applications/Cydia.app /var/lib/cydia/ /var/cache/apt /var/lib/apt /etc/apt /bin/bash /bin/sh /usr/sbin/sshd /usr/libexec/ssh-keysign /etc/ssh/sshd_config
|
因为有时NSFileManger函数会被hock掉,所以可以使用C语言stat函数进行路径检测
1 2 3 4 5 6 7 8 9
| - (BOOL)checkCydia { struct stat stat_info; //路径也可以进行替换 if (0 == stat("/Applications/Cydia.app", &stat_info)) { return YES; } return NO; }
|
程序运行环境变量检测
如果设置了 DYLD_INSERT_LIBRARIES 环境变量,那么在程序运行时,动态链接器会先加载该环境变量所指定的动态库;也就是说,这个动态库的加载优先于任何其它的库,包括 libc。
由于这个环境变量指定的动态库加载的时机实在是太早了,所以对于 app来说,除了代码混淆外,无良策;
但是我们可以在代码中通过判断环境变量来检测是不是被注入:
1 2 3 4 5 6 7 8
| - (BOOL)checkEnv{ char *env = getenv("DYLD_INSERT_LIBRARIES"); NSLog(@"%s", env); if (env) { return YES; } return NO; }
|
验证函数地址
使用dladdr方法可以获得一个函数所在的模块.从而判断该函数是否被替换掉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include <dlfcn.h> #include <objc/objc.h> #include <objc/runtime.h> #include <stdio.h> #include <string.h>
Dl_info info; IMP imp; Method orginalMethod = class_getClassMethod([NSArray class], @selector(description)); imp = method_getImplementation(orginalMethod); if (dladdr(imp, &info)) { printf("dli_fname: %s\n", info.dli_fname); printf("dli_sname: %s\n", info.dli_sname); printf("dli_fbase: %p\n", info.dli_fbase); printf("dli_saddr: %p\n", info.dli_saddr); } else { printf("error: can't find that symbol.\n"); }
|
通过该方法验证指定类的方法是否都来自指定模块(可以根据实际情况自定义修改),建议使用inline方式编译,像这样以内联函数的形式编译,攻击者必须修改每一处调用该函数的的地方
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| #include <dlfcn.h> #include <objc/objc.h> #include <objc/runtime.h> #include <stdio.h> #include <string.h>
static inline BOOL validate_methods(const char *cls,const char *fnamePre) __attribute__ ((always_inline));
BOOL validate_methods(const char *cls,const char *fnamePre){ Class aClass = objc_getClass(cls); Method *methods; unsigned int nMethods; Dl_info info; IMP imp; Method m; if(!aClass) return NO; methods = class_copyMethodList(aClass, &nMethods); while (nMethods--) { m = methods[nMethods]; imp = method_getImplementation(m); if(!imp){ free(methods); return NO; } if(!dladdr(imp, &info)){ free(methods); return NO; } /*Validate image path*/ if(!strstr(info.dli_fname, fnamePre)){ goto FAIL; } } return YES;
FAIL: printf("method %s failed integrity test:\n", (const char *)method_getName(m)); printf(" dli_fname:%s\n",info.dli_fname); printf(" dli_sname:%s\n",info.dli_sname); printf(" dli_fbase:%p\n",info.dli_fbase); printf(" dli_saddr:%p\n",info.dli_saddr); free(methods); return NO; }
|
最后,这种检查方式只能减少被攻击的可能性,不是肯定安全的,也是有可能被绕过的.