iOS安全檢測:是否越獄、動態(tài)庫注入咧七、重簽名

前沿

公司項目衰齐,最新的一個版本,bugly崩潰率突然升高继阻,而且發(fā)生在一個未知的動態(tài)庫耻涛。觀察了幾天發(fā)現(xiàn):每天發(fā)生有固定的時間斷,手機設備也很類似瘟檩,所以就懷疑是被攻擊了抹缕,然后又分析埋點統(tǒng)計數(shù)據(jù),奈何統(tǒng)計數(shù)據(jù)沒有上報關鍵的信息墨辛,還是無法拿出確鑿證據(jù)卓研。于是就網(wǎng)上找了些方法,添加一些日志到bugly和埋點統(tǒng)計里面睹簇,再繼續(xù)跟進一下奏赘。本文主要參考鏈接:https://github.com/SmileZXLee/ZXHookDetection

Demo下載:https://github.com/ZhangJingHao/ZJHSafeCheckDemo.git

一、是否越獄

1太惠、使用NSFileManager檢測關鍵文件

使用NSFileManager通過檢測一些越獄后的關鍵文件/路徑是否可以訪問來判斷是否越獄 常見的文件/路徑有

static char *JailbrokenPathArr[] = {"/Applications/Cydia.app","/usr/sbin/sshd","/bin/bash","/etc/apt","/Library/MobileSubstrate","/User/Applications/"};

判斷是否越獄(使用NSFileManager)

/// 使用NSFileManager通過檢測一些越獄后的關鍵文件是否可以訪問來判斷是否越獄
+ (BOOL)isJailbroken1 {
    if(TARGET_IPHONE_SIMULATOR) return NO;
    
    for (int i = 0;i < sizeof(JailbrokenPathArr) / sizeof(char *);i++) {
        if([[NSFileManager defaultManager] fileExistsAtPath:[NSString stringWithUTF8String:JailbrokenPathArr[i]]]){
            return YES;
        }
    }
    return NO;
}

但是攻擊者可以通過hook NSFileManager的fileExistsAtPath方法來繞過檢測

//繞過使用NSFileManager判斷特定文件是否存在的越獄檢測磨淌,此時直接返回NO勢必會影響程序中對這個方法的正常使用,因此可以先打印一下path凿渊,然后判斷如果path是用來判斷是否越獄則返回NO梁只,否則按照正常邏輯返回
%hook NSFileManager
- (BOOL)fileExistsAtPath:(NSString *)path{
    if(TARGET_IPHONE_SIMULATOR)return NO;
    for (int i = 0;i < sizeof(JailbrokenPathArr) / sizeof(char *);i++) {
        NSString *jPath = [NSString stringWithUTF8String:JailbrokenPathArr[i]];
        if([path isEqualToString:jPath]){
            return NO;
        }
    }
    return %orig;
}
%end

2缚柳、使用C語言函數(shù)stat判斷文件是否存在

使用C語言函數(shù)stat判斷文件是否存在(注:stat函數(shù)用于獲取對應文件信息,返回0則為獲取成功敛纲,-1為獲取失敗)

/// 使用stat通過檢測一些越獄后的關鍵文件是否可以訪問來判斷是否越獄
+ (BOOL)isJailbroken2{
    if(TARGET_IPHONE_SIMULATOR)return NO;
    for (int i = 0;i < sizeof(JailbrokenPathArr) / sizeof(char *);i++) {
        struct stat stat_info;
        if (0 == stat(JailbrokenPathArr[i], &stat_info)) {
            return YES;
        }
    }
    return NO;
}

但是使用fishhook可hook C函數(shù)喂击,fishhook通過在mac-o文件中查找并替換函數(shù)地址達到hook的目的

static int (*orig_stat)(char *c, struct stat *s);
int hook_stat(char *c, struct stat *s){
    for (int i = 0;i < sizeof(JailbrokenPathArr) / sizeof(char *);i++) {
        if(0 == strcmp(c, JailbrokenPathArr[i])){
            return 0;
        }
    }
    return orig_stat(c,s);
}
+(void)statHook{
    struct rebinding stat_rebinding = {"stat", hook_stat, (void *)&orig_stat};
    rebind_symbols((struct rebinding[1]){stat_rebinding}, 1);
}

在動態(tài)庫加載的時候,調(diào)用statHook

%ctor{
    [StatHook statHook];
}

判斷stat的來源是否來自于系統(tǒng)庫淤翔,因為fishhook通過交換函數(shù)地址來實現(xiàn)hook翰绊,若hook了stat,則stat來源將指向攻擊者注入的動態(tài)庫中 因此我們可以完善上方的isJailbroken2判斷規(guī)則旁壮,若stat來源非系統(tǒng)庫监嗜,則直接返回已越獄

+ (BOOL)isJailbroken2{
    if(TARGET_IPHONE_SIMULATOR)return NO;
    int ret ;
    Dl_info dylib_info;
    int (*func_stat)(const char *, struct stat *) = stat;
    if ((ret = dladdr(func_stat, &dylib_info))) {
        NSString *fName = [NSString stringWithUTF8String:dylib_info.dli_fname];
        NSLog(@"fname--%@",fName);
        if(![fName isEqualToString:@"/usr/lib/system/libsystem_kernel.dylib"]){
            return YES;
        }
    }
    
    for (int i = 0;i < sizeof(JailbrokenPathArr) / sizeof(char *);i++) {
        struct stat stat_info;
        if (0 == stat(JailbrokenPathArr[i], &stat_info)) {
            return YES;
        }
    }
    
    return NO;
}

3、通過環(huán)境變量DYLD_INSERT_LIBRARIES判斷

通過環(huán)境變量DYLD_INSERT_LIBRARIES判斷是否越獄抡谐,若獲取到的為NULL裁奇,則未越獄

+ (BOOL)isJailbroken3{
    if(TARGET_IPHONE_SIMULATOR)return NO;
    return !(NULL == getenv("DYLD_INSERT_LIBRARIES"));
}

此時依然可以使用fishhook hook函數(shù)getenv,攻防方法同上麦撵,此處不再贅述刽肠。

二、是否動態(tài)庫注入

注入檢測可以判斷加載模塊中有沒有一些不在正常加載列表中的模塊免胃,使用 _dyld_get_image_name 獲取模塊名音五,然后進行對比,具體如下

/// 是否注入動態(tài)庫:返回nil則未注入羔沙,有值表示已注入
/// @return 有值時躺涝,會返回首次獲取的動態(tài)庫名,方便bugly查看日志
+ (NSString *)isInjectDylib {
    if(TARGET_IPHONE_SIMULATOR) return nil;
    
    // 通過遍歷dyld_image檢測非法注入的動態(tài)庫
    int dyld_count = _dyld_image_count();
    for (int i = 0; i < dyld_count; i++) {
        const char * imageName = _dyld_get_image_name(i);
        NSString *res = [NSString stringWithUTF8String:imageName];
                
        // 過濾非dylib后綴的路徑
        if(![res hasSuffix:@".dylib"]){
            continue;
        }
        
        // 越獄設備動態(tài)庫
        if ([res containsString:@"/Library/MobileSubstrate/DynamicLibraries"]) {
            return [res lastPathComponent];
        }
        // 非越獄設備動態(tài)庫
        else if([res containsString:@"/var/containers/Bundle/Application"])  {
            // 這邊還需要過濾掉自己項目中本身有的動態(tài)庫
            return [res lastPathComponent];
        }
    }
    
    return nil;
}

三扼雏、是否重簽名

通過檢測ipa中的embedded.mobileprovision中的我們打包Mac的公鑰來確定是否簽名被修改坚嗜,但是需要注意的是此方法只適用于Ad Hoc或企業(yè)證書打包的情況,App Store上應用由蘋果私鑰統(tǒng)一打包诗充,不存在embedded.mobileprovision文件

/// 是否重簽名:返回nil表示未重簽苍蔬,有值表示已重簽名
/// @param publicKey 打包時的公鑰
/// @return 有值時,會返回對檢測出來的公鑰值
+ (NSString *)isResignWithPublicKey:(NSString *)publicKey {
    if(TARGET_IPHONE_SIMULATOR) return nil;

    /* 通過檢測ipa中的embedded.mobileprovision中的我們打包Mac的公鑰來確定是否簽名被修改蝴蜓,
       但是需要注意的是此方法只適用于Ad Hoc或企業(yè)證書打包的情況银室,
       App Store上應用由蘋果私鑰統(tǒng)一打包,不存在embedded.mobileprovision文件
       來源于http://www.reibang.com/p/a3fc10c70a29
    */
    NSString *embeddedPath = [[NSBundle mainBundle] pathForResource:@"embedded"
                                                             ofType:@"mobileprovision"];
    if (!embeddedPath) {
        return nil;
    }
    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[i] rangeOfString:@"application-identifier"].location != NSNotFound) {
            NSInteger fromPosition =
            [embeddedProvisioningLines[i+1] rangeOfString:@"<string>"].location+8;
            
            NSInteger toPosition = [embeddedProvisioningLines[i+1] rangeOfString:@"</string>"].location;
            NSRange range;
            range.location = fromPosition;
            range.length = toPosition - fromPosition;
            NSString *fullIdentifier = [embeddedProvisioningLines[i+1] substringWithRange:range];
            NSArray *identifierComponents = [fullIdentifier componentsSeparatedByString:@"."];
            NSString *appIdentifier = [identifierComponents firstObject];
            if (![appIdentifier isEqualToString:publicKey]) {
                return appIdentifier;
            } else {
                return nil;
            }
        }
    }
    return nil;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末励翼,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子辜荠,更是在濱河造成了極大的恐慌汽抚,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件伯病,死亡現(xiàn)場離奇詭異造烁,居然都是意外死亡否过,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門惭蟋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來苗桂,“玉大人,你說我怎么就攤上這事告组∶何埃” “怎么了?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵木缝,是天一觀的道長便锨。 經(jīng)常有香客問我,道長我碟,這世上最難降的妖魔是什么放案? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮矫俺,結(jié)果婚禮上吱殉,老公的妹妹穿的比我還像新娘。我一直安慰自己厘托,他們只是感情好友雳,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著催烘,像睡著了一般沥阱。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上伊群,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天考杉,我揣著相機與錄音,去河邊找鬼舰始。 笑死崇棠,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的丸卷。 我是一名探鬼主播枕稀,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼谜嫉!你這毒婦竟也來了萎坷?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤沐兰,失蹤者是張志新(化名)和其女友劉穎哆档,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體住闯,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡瓜浸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年澳淑,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片插佛。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡杠巡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出雇寇,到底是詐尸還是另有隱情氢拥,我是刑警寧澤,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布谢床,位于F島的核電站兄一,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏识腿。R本人自食惡果不足惜出革,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望渡讼。 院中可真熱鬧骂束,春花似錦、人聲如沸成箫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蹬昌。三九已至混驰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間皂贩,已是汗流浹背栖榨。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留明刷,地道東北人婴栽。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像辈末,于是被迫代替她去往敵國和親愚争。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內(nèi)容