摘要:return dicHash。我们对Plist文件以及App 的icon资源文件做hash值校验。

为什么要应用完整性校验

大家可能听过马甲包类似的概念。如果恶意攻击者搞你的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:@"<string>"].location+8;

NSInteger toPosition = [[embeddedProvisioningLines objectAtIndex:i+1] rangeOfString:@"</string>"].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:

相关文章