怪现象:iOS完整性校验

前言

安全厂商要求iOS APP要做资源和Mach-o的完整性校验,就是要求做hash校验,保证用户安装的app是没有篡改的。同时也可以保护收费APP,不可以被脱壳然后发布到其他平台

很多iOS开发对于这个检测项和修复建议无法接受。

其实这个出发点是好的。毕竟现在下载app的渠道很多,无法保证APP不会被插入恶意代码。只是觉得修复建议不应该是校验hash
这个方法比较笨。

修复办法

为什么说是比较笨呢,因为app被脱壳后,重新发布,还是需要签名的。不然的话,正常的用户无法安装。

根据证书ID的唯一性,我们可以从provision中读取证书ID,和我们预设的做比对,我们就知道App被Hack,可以进行退出程序等操作。

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#import "AppDelegate.h"

// 证书ID
#define kIdentifier @"55R3QD4LZ2"

@interface AppDelegate ()

@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

// 反重签名
[self checkCodesign];

return YES;
}

- (void)checkCodesign {

// 描述文件路径
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:kIdentifier]) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:@"codesign verification failed." delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
[alert show];
}
break;
}
}
}


}

#pragma mark - UIAlertViewDelegate

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
exit(0);
}

@end

有人会说,这个可以绕过的。是的,这个的确是可以绕过,那么,你那个完整性校验的就无法绕过吗?校验本身就在客户端校验的。客户端校验的操作很可能都是可以绕过的。别和我说校验在服务器端完成,因为服务端校验完,还不是要把结果返回给客户端,让客户端提示是否被篡改。

能做到的只是防止dylib加载,防止动态分析。这个就是iOS APP安全里面也是要做的。

也就是说完整性校验这个就是一个伪安全需求,应该是改为签名校验。

参考链接