IOS 驗(yàn)證App完整性探究

時(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)證。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。