1.方法hook
先上代碼
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL originalSelector = @selector(test);
SEL swizzledSelector = @selector(swizzle_test);
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
});
}
- (void)test {
NSLog(@"test");
}
- (void)swizzle_test {
[self swizzle_test];
}
一個方法對應(yīng)一個sel和imp燃少,方法A的imp指針和方法B的imp指針進(jìn)行了替換该默,并且在要替換的方法中調(diào)用了一下原來的方法實(shí)現(xiàn)般贼。這個也就就是開發(fā)中最常用到的方法混淆瘸恼。
2.函數(shù)hook
推薦使用https://github.com/facebook/fishhook 這個庫來做函數(shù)hook梧躺,使用起來簡便似谁。內(nèi)部實(shí)現(xiàn)復(fù)雜傲绣,很強(qiáng)大,源碼基本上看不懂巩踏。大致原理還是要弄清楚的秃诵。例如有2個函數(shù),函數(shù)A塞琼,函數(shù)B菠净,想要將函數(shù)A的實(shí)現(xiàn)替換為函數(shù)B的實(shí)現(xiàn)。fishhook的實(shí)現(xiàn)大致分為2步彪杉,第一步找到目標(biāo)函數(shù)的函數(shù)地址毅往。第二步找到函數(shù)名,進(jìn)行比對派近。匹配成功攀唯,替換目標(biāo)函數(shù)地址為自己的函數(shù)地址。
struct rebinding {
const char *name; // 目標(biāo)函數(shù)名
void *replacement; // 替換函數(shù)
void **replaced; // 二級目標(biāo)函數(shù)指針
};
/*
struct rebinding rebindings[]:要重新綁定的結(jié)構(gòu)體數(shù)組
rebindings_nel:要重新綁定的函數(shù)個數(shù)
*/
int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel);
int rebind_symbols_image(void *header,
intptr_t slide,
struct rebinding rebindings[],
size_t rebindings_nel);
上代碼:
示例1:hook Foundation庫的NSSetUncaughtExceptionHandler()函數(shù)(生效)
static NSUncaughtExceptionHandler *g_vaildUncaughtExceptionHandler;
static void (*ori_NSSetUncaughtExceptionHandler)( NSUncaughtExceptionHandler *_Nullable);
void swizzle_NSSetUncaughtExceptionHandler( NSUncaughtExceptionHandler * handler) {
g_vaildUncaughtExceptionHandler = NSGetUncaughtExceptionHandler();
if (g_vaildUncaughtExceptionHandler != NULL) {
NSLog(@"UncaughtExceptionHandler=%p",g_vaildUncaughtExceptionHandler);
}
ori_NSSetUncaughtExceptionHandler(handler);
g_vaildUncaughtExceptionHandler = NSGetUncaughtExceptionHandler();
}
static void xr_uncaught_exception_handler (NSException *exception) {
NSLog(@"XX");
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
rebind_symbols((struct rebinding [1]){"NSSetUncaughtExceptionHandler",swizzle_NSSetUncaughtExceptionHandler,(void *)&ori_NSSetUncaughtExceptionHandler}, 1);
NSSetUncaughtExceptionHandler(&xr_uncaught_exception_handler);
[@[] objectAtIndex:1];
}
return 0;
}
示例2:hook自己的函數(shù)(不生效)
void test(void) {
NSLog(@"test");
}
void swizzle_test(void) {
NSLog(@"swizzle_test");
}
static void (*funcptr)(void);
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 初始化一個 rebinding 結(jié)構(gòu)體
struct rebinding test_rebinding = { "test", swizzle_test, (void *)&funcptr };
// 將結(jié)構(gòu)體包裝成數(shù)組渴丸,并傳入數(shù)組的大小侯嘀,對原符號 open 進(jìn)行重綁定
rebind_symbols((struct rebinding[1]){test_rebinding}, 1);
// 調(diào)用原函數(shù)
test();
}
return 0;
}
函數(shù)的hook大致原理和方法hook類似。方法調(diào)用其實(shí)在運(yùn)行時(shí)也就是轉(zhuǎn)成一個個函數(shù)調(diào)用曙强。根據(jù)方法名(SEL)去查找對應(yīng)的方法實(shí)現(xiàn)的函數(shù)地址(IMP)残拐,然后執(zhí)行該函數(shù)。只不過函數(shù)和方法的存儲空間不一樣碟嘴,查找函數(shù)地址的實(shí)現(xiàn)方法也就不一樣。但是原理是差不多的囊卜。
示例2中娜扇,發(fā)現(xiàn)hook不生效。而示例1中可以栅组。很奇怪雀瓢。原來fishhook只能hook其他鏡像中函數(shù),不可以hook當(dāng)前鏡像(庫或可執(zhí)行文件)中運(yùn)行的函數(shù)玉掸。在main函數(shù)執(zhí)行之前刃麸,dylb會通過load_images加載項(xiàng)目里所有的鏡像。而Foundation庫和項(xiàng)目本身分別是兩個鏡像司浪。所以示例1中hook是Foundation庫鏡像的函數(shù)泊业。而示例2hook的則是本身鏡像中的函數(shù)。所以也就會不生效啊易。