iOS 安全之应用完整性校验的解决方案

为什么要应用完整性校验

大家可能听过马甲包类似的概念。如果恶意攻击者搞你的App,直接换个App Icon,App名字 以及皮肤直接上架了就很尴尬了。

怎么做

从安全攻防角度讲,你了解攻击的方式,更容易知道怎么防,但是也是相对而言,只是不断消磨攻击者的意志,但愿他们放弃。

方式一:越狱检测

这种方式最简单暴力,我们可以检测当前设备是否越狱,在关键性业务判断给出提示强制退出以免造成安全问题,这里的关键性业务可能是需要自己定义范围,比如牵扯到用户敏感信息等业务。下面贴出关键性代码:

const char* jailbreak_tool_pathes[] = {

       "/Applications/Cydia.app",

       "/Library/MobileSubstrate/MobileSubstrate.dylib",

       "/bin/bash",

       "/usr/sbin/sshd",

       "/etc/apt"

       };

       #define ARRAY_SIZE(a) sizeof(a)/sizeof(a[0])

+ (BOOL)isJailBroken

{

    if ([self isSimulator] == YES)

    {

        return NO;

    }

    

    for (int i=0; i<ARRAY_SIZE(jailbreak_tool_pathes); i++) {

           if ([[NSFileManager defaultManager] fileExistsAtPath:[NSString stringWithUTF8String:jailbreak_tool_pathes[i]]]) {

               NSLog(@"The device is jail broken!");

               return YES;

            }

       }

      NSLog(@"The device is NOT jail broken!");

      return NO;

}



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

这种方式其实是非常间接的方式避免了这个话题

方式二:判断Mach-O文件否被篡改

通过检测SignerIdentity判断是Mach-O文件否被篡改。原理是:SignerIdentity的值在info.plist中是不存在的,开发者不会加上去,苹果也不会,只是当ipa包被反编译后篡改文件再次打包,需要伪造SignerIdentity。所以只要被攻击篡改东西如果重新运行到手机上就会出现这个东西。


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


方式三:重签名检测

由于要篡改App必然重签名,至于为什么重签名,是因为苹果做了校验改动了任何东西校验失败是直接闪退的,其实原理也是校验文件的hash值。签名打包过程会出现这个embedded.mobileprovision文件,这个文件有teamID的一个东西我们可以校验是否是我们自己的团队的teamID来判断。或者判断BundleID 是否被修改。

+ (BOOL)checkCodeSignWithProvisionID:(NSString *)provisionID

{

     // 描述文件路径

        NSString *embeddedPath = [[NSBundle mainBundle] pathForResource:@"embedded" ofType:@"mobileprovision"];

        if ([[NSFileManager defaultManager] fileExistsAtPath:embeddedPath]) {

            

            // 读取application-identifier

            NSString *embeddedProvisioning = [NSString stringWithContentsOfFile:embeddedPath encoding:NSASCIIStringEncoding error:nil];

            NSArray *embeddedProvisioningLines = [embeddedProvisioning componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];

            

            for (int i = 0; i < [embeddedProvisioningLines count]; i++) {

                if ([[embeddedProvisioningLines objectAtIndex:i] rangeOfString:@"application-identifier"].location != NSNotFound) {

                    

                    NSInteger fromPosition = [[embeddedProvisioningLines objectAtIndex:i+1] rangeOfString:@""].location+8;

                    

                    NSInteger toPosition = [[embeddedProvisioningLines objectAtIndex:i+1] rangeOfString:@""].location;

                    

                    NSRange range;

                    range.location = fromPosition;

                    range.length = toPosition - fromPosition;

                    

                    NSString *fullIdentifier = [[embeddedProvisioningLines objectAtIndex:i+1] substringWithRange:range];

                    

    //                NSLog(@"%@", fullIdentifier);

                    

                    NSArray *identifierComponents = [fullIdentifier componentsSeparatedByString:@"."];

                    NSString *appIdentifier = [identifierComponents firstObject];

                    

                    // 对比签名ID

                    if (![appIdentifier isEqual:provisionID])

                    {

                        return NO;

                    }

                    else

                    {

                        return YES;

                    }

                }

            }

        }

        return YES;


}

了解签名的原理有利于防止App被重签名。

方式四:关键资源hash值检测

我们对Plist文件以及App 的icon资源文件做hash值校验。网上一些对_CodeSignature的CodeResources以及App二进制文件的校验做法有问题。因为Xcode打包过程不同环境造成的hash值不一样,通过下图可以看出不同环境打包过程造成的hash值不一样的选项。所以我们必须过滤掉变化的文件。检测Plist文件以及App Icon资源文件这些东西。


关键性代码:

//生成资源文件名及对应的hash的字典

+(NSDictionary *)getBundleFileHash{

    NSMutableDictionary * dicHash = [NSMutableDictionary dictionary];

    NSArray * fileArr = [self allFilesAtPath:[[NSBundle mainBundle]resourcePath]];

    for (NSString * fileName in fileArr) {

        //对应的文件生成hash

        NSString * HashString = [FileHash md5HashOfFileAtPath:[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:fileName]];

        if (HashString != nil) {

            [dicHash setObject:HashString forKey:fileName];

        }

    }

 //所有资源文件的hash就保存在这数组里

  return dicHash;

}


有些加密工具为了放进加固SDK放在了本地校验,但是通过服务器校验比较安全点。

总结:

通过下面链接了解:

  • Xcode build 对二进制文件以及_CodeSignature的CodeResources造成变化的原理。

LLVM怎么做Deterministic Build

  • 签名的原理

iOS逆向(五)-ipa包重签名

如果感觉这篇文章不错可以点击在看:point_down: