iOS安全防护---越狱检测、二次打包检测、反调试

       最近在调研越狱设备的检测、防止APP被二次打包、防止反调试以及逆向工程,调研期间做了大量的测试来验证方案的可行性,花费了很多时间。所以,在此将调研结果总结一下,供大家参考。

一、越狱环境下,提高App破解难度的方案:

1、检测是否被注入,阻止Cycript等的动态库注入。

2、在 Xcode 编译选项中 other linker flags 中添加 -Wl,- sectcreate,__RESTRICT,__restrict,/dev/null 标识。(注:这个方案只适合iOS10.0以下系统,而且个别系统打出来的包还会发生Crash,和Swift混编的项目时候可能会出问题,所以现在不建议用这种方式)

3、在关键业务上进行越狱检测,如果是越狱手机则进行提示或者直接退出程序,保证业务安全。

二、防止IPA包被二次打包的方案:

1、检测plist文件中是否有SignerIdentity值,SignerIdentity值只有ipa包被反编译后篡改二进制文件再次打包,才会有此值。(注:如果替换资源文件,比如图片、plist文件等是没有SignerIdentity这个值的。猜测只有改了二进制文件才会有此值(待验证) )

2、检测 cryptid 的值来检测二进制文件是否被篡改。网上说这也是一种解决方案,但是cryptid这个值好像在Mach-o中才有,目前还不知道如何获取该值。

3、IPA包上传到TestFlight或者App Store后,计算安装包中重要文件的MD5 值,服务器记录,在应用运行前首先将本地计算的 MD5 值和服务器记录的 MD5 值 进行对比,如不同,则退出应用。(注:该方案已经通过验证,项目已上线)

关于embedded.mobileprovision文件的探索:

      如果二次打包的话,一般篡改者都会更换一下IPA包里的embedded.mobileprovision文件,我想可不可以从这个文件入手来解决问题,通过验证这个文件的MD5值来做验证(CodeResources文件中已经有该文件对应的MD5值了)。经过测试发现,正常用Xcode打出来的包是有这个文件的,但是,IPA包上传到App Store被苹果处理之后,就没有这个文件了,所以,该方案不可行。

更新:      

     看了iOS App签名的原理之后,再对embedded.mobileprovision文件做一下详细说明。为什么IPA包上传到App Store被苹果处理之后就没有这个文件了呢?因为embedded.mobileprovision文件里边存储的是证书相关的公钥私钥信息,苹果会用自己的私钥验证这里边的内容,如果验证通过则说明该APP是安全的合法的,之后就会将该文件删除,因为,App Store的APP苹果会用自己的公钥私钥进行重签名(也就是加壳),这样该文件就失去它的意义了,所以被删除了。这也就是为啥证书过期之后,从App Store上已经下载过的APP还可以继续使用的原因。

     而通过企业证书分发的APP,IPA包里边还是有这个文件的,这时候苹果做安全校验的时候就是通过这个文件去做的,所以,如果企业证书过期了,这时候企业分发的APP就立马不能安装使用了,并且已经下载安装的APP也不能使用。

总结embedded.mobileprovision文件存在的情况:

   不存在该文件:App Store下载的IPA、Cydia商店(越狱手机上的)下载的IPA。

   存在该文件:Xcode打出来的IPA、企业证书分发的IPA、越狱手机上自己二次打包的IPA。

在此,着重对防止 二次打包方案3 做MD5校验的方案做一下总结,因为这里做了太多的尝试:

    这种方案的突破点是找到相关文件计算MD5值,但是具体用哪些文件来进行MD5验证呢?

    通过查看IPA包中的文件,我们会发现有一个CodeResources 文 件,打开这个文件可以看到是一个类似plist的文件,里边记录的是IPA包里各个文件的文件名和hash值。那么我们是不是可以直接对比这个文件来判断是否被二次打包呢?

    经过测试发现,Xcode打出来的包通过这个文件来验证是没有问题的,每次打出来的包这个文件都是一样的。但是,IPA包上传到App Store被苹果处理之后,这个文件就会发生变化,而且不同机型上还会存在不一样的情况,所以,该方案不可行。

   后来,我想要不就自己遍历所有的文件,自己对文件内容做MD5。最后,发现有些文件每次打包之后都会发生变化或者在不同机型上计算出来的MD5值不同,比如:Assets.car、.nib文件、二进制文件等。所以,该方案不可行。其实上边CodeResources文件不一致就是因为个别文件不一致导致的,因为CodeResources文件存储的就是所有文件的MD5值。

解决方案:

   最后,通过多次打包试验,我们采取的是将那些每次打包上传App Store,MD5值都会发生变化的文件过滤掉,这样计算出来的MD5值就是最终用来做验证的MD5值了。

(建议:为了保证线上App运行稳定,最好通过接口来做验证,做一个开关,这样如果线上发生问题的话可以让后台将开关关闭)

下面是几个方法,大家可以直接使用:

1、判断设备是否越狱。(其实越狱的判断有多种判断方式)

//是否越狱
+ (BOOL)isPrisonBreak
{
    if ([self isSimulator]) return NO;
    
    bool pb = NO;
    
    // 常见越狱文件
    NSArray *pathArray = @[
        @"/Applications/Cydia.app",
        @"/Library/MobileSubstrate/MobileSubstrate.dylib",
        @"/bin/bash",
        @"/usr/sbin/sshd",
        @"/etc/apt"
    ];
    for (int i = 0; i < pathArray.count; i++) {
        NSString *path = pathArray[i];
        if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
            pb = YES;
        }
    }

    // 读取系统所有的应用名称
    if ([[NSFileManager defaultManager] fileExistsAtPath:@"/User/Applications/"]){
        pb = YES;
    }
    
    if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"cydia://"]]) {
        pb = YES;
    }
    

    struct stat stat_info;
    //使用stat系列函数检测Cydia等工具
    if (0 == stat("/Applications/Cydia.app", &stat_info)) {
        pb = YES;
    }
    
    // 读取环境变量
    char *checkInsertLib = getenv("DYLD_INSERT_LIBRARIES");
    if (checkInsertLib) {
        pb = YES;
    }
    
    return pb;
}


+ (BOOL)isSimulator {
#if TARGET_OS_SIMULATOR
    return YES;
#else
    return NO;
#endif
}

2、判断IPA包二进制文件是否被更改,被二次打包

+ (BOOL)isSecondIPA{
    
    NSBundle *bundle = [NSBundle mainBundle];
    NSDictionary *info = [bundle infoDictionary];
    if ([info objectForKey: @"SignerIdentity"] != nil){
        //存在这个key,则说明被二次打包了
        return YES;
    }
    
    return NO;
}

3、判断APP是否被反调试、是否被GDB进行动态调试。

// 阻止 gdb/lldb 调试
// 调用 ptrace 设置参数 PT_DENY_ATTACH,如果有调试器依附,则会产生错误并退出
#import <dlfcn.h>
#import <sys/types.h>

typedef int (*ptrace_ptr_t)(int _request, pid_t _pid, caddr_t _addr, int _data);
#if !defined(PT_DENY_ATTACH)
#define PT_DENY_ATTACH 31
#endif

void anti_gdb_debug() {
    void *handle = dlopen(0, RTLD_GLOBAL | RTLD_NOW);
    ptrace_ptr_t ptrace_ptr = dlsym(handle, "ptrace");
    ptrace_ptr(PT_DENY_ATTACH, 0, 0, 0);
    dlclose(handle);
}

int main(int argc, char * argv[]) {
#ifndef DEBUG
    // 非 DEBUG 模式下禁止调试
    anti_gdb_debug();
#endif
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

参考:

逆向开发

iOS App 签名的原理

iOS安全攻与防(总篇)

对 iOS app 进行安全加固

重组 IPA (打包再签名) - 窥打包签名流程

【腾讯Bugly干货分享】iOS 黑客技术大揭秘

iOS如何判断手机是否已越狱

'-Wl,-sectcreate,__RESTRICT,__restrict,/dev/null' 误区

iOS应用安全 - 完整性检测

设备是否越狱检测、ipa文件是否被篡改检测

越狱开发4-越狱开发防护与破解

发布了89 篇原创文章 · 获赞 92 · 访问量 30万+

猜你喜欢

转载自blog.csdn.net/u013602835/article/details/86545106