時(shí)間緊迫,直接開始
首先查看Mach-O文件,看能否找到能判斷的信息。
-
看到有個(gè)UUID字段,驗(yàn)證一下是否能拿來判斷。
image.png
/**
獲取指令中的uuid
@return uuid
*/
NSString *executableUUID()
{
const uint8_t *command = (const uint8_t *)(&_mh_execute_header + 1);
for (uint32_t idx = 0; idx < _mh_execute_header.ncmds; ++idx) {
if (((const struct load_command *)command)->cmd == LC_UUID) {
command += sizeof(struct load_command);
return [NSString stringWithFormat:@"%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
command[0], command[1], command[2], command[3],
command[4], command[5],
command[6], command[7],
command[8], command[9],
command[10], command[11], command[12], command[13], command[14], command[15]];
} else {
command += ((const struct load_command *)command)->cmdsize;
}
}
return nil;
}
結(jié)論:由于驗(yàn)證時(shí)忘了截圖,就不貼驗(yàn)證過程了,直接寫結(jié)論吧。LC_UUID會(huì)在每次修改代碼重新編譯運(yùn)行時(shí),LC_UUID的值都會(huì)改變,但是直接對(duì)app進(jìn)行重簽名和篡改之后,發(fā)現(xiàn)是不會(huì)改變LC_UUID的值。
- 另外有個(gè)猜想但沒驗(yàn)證,可以利用Load Commands中的代碼簽名數(shù)據(jù)偏移找到代碼簽名數(shù)據(jù)的地址,但是這簽名數(shù)據(jù)的結(jié)構(gòu)比較復(fù)雜,有興趣的可以研究一下。
image.png
image.png
直接從Mach-O文件著手,驗(yàn)證Mach-O文件。
- 研究如何能在運(yùn)行時(shí)獲取Mach-O文件
/**
App Mach-O文件路徑
@return Mach-O文件路徑
*/
-(NSString *) machOPath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *excutableName = [[NSBundle mainBundle] infoDictionary][@"CFBundleExecutable"];
NSString *tmpPath = [documentsDirectory stringByDeletingLastPathComponent];
NSString *appPath = [[tmpPath stringByAppendingPathComponent:excutableName]
stringByAppendingPathExtension:@"app"];
NSString *machOPath = [appPath stringByAppendingPathComponent:excutableName];
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) {
NSString *bundlePath = [[NSBundle mainBundle] bundlePath];
machOPath = [bundlePath stringByAppendingPathComponent:excutableName];
}
return machOPath;
}
- 用SHA256執(zhí)行摘要
/**
數(shù)據(jù)SHA256
@param keyData 需要hash的數(shù)據(jù)
@return hash后的數(shù)據(jù)
*/
+ (NSString *)SHA256:(NSData *)keyData {
// const char *s = [self cStringUsingEncoding:NSASCIIStringEncoding];
// NSData *keyData = [NSData dataWithBytes:s length:strlen(s)];
uint8_t digest[CC_SHA256_DIGEST_LENGTH] = {0};
CC_SHA256(keyData.bytes, (CC_LONG)keyData.length, digest);
NSData *out = [NSData dataWithBytes:digest length:CC_SHA256_DIGEST_LENGTH];
NSString *hash = [out description];
hash = [hash stringByReplacingOccurrencesOfString:@" " withString:@""];
hash = [hash stringByReplacingOccurrencesOfString:@"<" withString:@""];
hash = [hash stringByReplacingOccurrencesOfString:@">" withString:@""];
return hash;
}
/**
* MachO使用SHA256執(zhí)行摘要
*/
-(void) sha256MachO
{
//加密
NSString *encryptResult = [ViewController SHA256:[NSData dataWithContentsOfFile:[ViewController binaryPath]]];
if (encryptResult) {
NSLog(@"\nSHA256 成功-->%@",encryptResult);
}else{
NSLog(@"\nSHA256 失敗");
}
}
-
驗(yàn)證修改代碼SHA256的值是否改變(修改輸出語句)
修改輸出語句前
修改輸出語句后
重新修改輸出語句為開始的語句
結(jié)論:修改代碼摘要會(huì)改變,即使重新寫回原來的代碼,也無法變回原來的SHA256的值。
- 重點(diǎn)來了,驗(yàn)證重簽名和篡改后SHA256的值。
未重簽名前:image.png
重簽名之后:image.png
結(jié)論:重簽名和篡改后SHA256的值會(huì)改變。
最終結(jié)論:可以對(duì)Mach-O執(zhí)行hash運(yùn)算或者簽名驗(yàn)簽等等操作來驗(yàn)證App是否完整。
PS:此次研究沒有考慮手機(jī)越獄的情況,越獄的話可以直接使用Tweak以動(dòng)態(tài)庫插入,可能SHA256的值就不會(huì)改變,因?yàn)镸ach-O數(shù)據(jù)沒有改變,不過這只是猜想,并沒有驗(yàn)證。