Hook概述
HOOK,中文譯為“掛鉤”或“鉤子”。在iOS逆向中是指改變程序運(yùn)行流程的一種技術(shù)。通過hook可以讓別人的程序執(zhí)行自己所寫的代碼。在逆向中經(jīng)常使用這種技術(shù)。所以在學(xué)習(xí)過程中,我們重點(diǎn)要了解其原理,這樣能夠?qū)阂獯a進(jìn)行有效的防護(hù)。
Hook示意
比如說我們收到紅包消息,我們進(jìn)行hook,執(zhí)行自定義的惡意代碼、看看搶紅包需要哪些參數(shù),準(zhǔn)備好參數(shù)調(diào)用搶紅包代碼。就不需要等待用戶點(diǎn)開紅包,點(diǎn)擊搶紅包等一一系列操作了
iOS中HOOK技術(shù)的幾種方式
Method Swizzle
???
利用OC的Runtime特性,動(dòng)態(tài)改變SEL(方法編號(hào))和IMP(方法實(shí)現(xiàn))的對(duì)應(yīng)關(guān)系,達(dá)到OC方法調(diào)用流程改變的目的。主要用于OC方法。fishhook
???
它是Facebook提供的一個(gè)動(dòng)態(tài)修改鏈接mach-O文件的工具。利用MachO文件加載原理,通過修改懶加載和非懶加載兩個(gè)表的指針達(dá)到C函數(shù)HOOK的目的。Cydia Substrate
Cydia Substrate 原名為 Mobile Substrate ,它的主要作用是針對(duì)OC方法、C函數(shù)以及函數(shù)地址進(jìn)行HOOK操作。當(dāng)然它并不是僅僅針對(duì)iOS而設(shè)計(jì)的,安卓一樣可以用。官方地址:http://www.cydiasubstrate.com/
fishhook
接口函數(shù):
struct rebinding {
const char *name;//需要HOOK的函數(shù)名稱,C字符串
void *replacement;//新函數(shù)的地址
void **replaced;//原始函數(shù)地址的指針!
};
FISHHOOK_VISIBILITY
int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel);
/*
* Rebinds as above, but only in the specified image. The header should point
* to the mach-o header, the slide should be the slide offset. Others as above.
*/
FISHHOOK_VISIBILITY
int rebind_symbols_image(void *header,
intptr_t slide,
struct rebinding rebindings[],
size_t rebindings_nel);
demo:
- (void)viewDidLoad {
[super viewDidLoad];
//創(chuàng)建rebinding 結(jié)構(gòu)體
struct rebinding myNslog;
myNslog.name = "NSLog";
myNslog.replacement = my_NSLog;
myNslog.replaced = (void *)&sysLog;
struct rebinding bds[] = {myNslog};
rebind_symbols(bds, 1);
}
//原函數(shù)
static void (*sysLog)(NSString *format, ...);
//新函數(shù)
void my_NSLog(NSString *format, ...) {
format = [format stringByAppendingFormat:@"\nhook成功~?。。?];
//回到系統(tǒng)的nslog里
sysLog(format);
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
NSLog(@"hello");
}
結(jié)果:

用法比較簡單,那么fishhook是如何hook函數(shù)的呢
我們知道:
OC動(dòng)態(tài)語言特性 方法通過sel找imp
而C語言是靜態(tài)語言 直接通過函數(shù)地址訪問
那么我們猜想一下,fishhook是不是先保存原函數(shù)的地址,然后bl到自己的函數(shù)地址呢?
其實(shí)這個(gè)過程并沒有那么簡單:
MachO Text段(只讀\可執(zhí)行!)
Data段(可讀\可寫) 所以這里面包含了符號(hào),及符號(hào)表
首先編譯時(shí)NSLog的地址我們不知道,NSLog在foundation共享緩存庫中
在訪問NSLog的時(shí)候,使用的其實(shí)是PIC技術(shù)(位置無關(guān)代碼),首先訪問占位符占位符就叫做符號(hào),占8個(gè)字節(jié),dlyd 進(jìn)行對(duì)符號(hào)里的數(shù)據(jù)修改就叫做符號(hào)綁定?。《械姆?hào)列表就叫做符號(hào)表,
因?yàn)橥獠糠?hào)地址不確定所以使用PIC技術(shù)

把符號(hào)里的地址修改了,做到修改符號(hào)對(duì)應(yīng)地址的關(guān)系
外部的c函數(shù)是動(dòng)態(tài)調(diào)用的,通過符號(hào)找地址,也可以重綁定
函數(shù)名變量名,方法名,編譯完就生成一張符號(hào)表
內(nèi)部符號(hào),本mach內(nèi)的符號(hào)
外部符號(hào),也叫間接符號(hào)表
可以通過 symbol table查看
本地符號(hào) 我自己內(nèi)部使用的,去符號(hào)去的是本地符號(hào)
全局符號(hào) 暴露給外界使用的,比如第三方庫,
objcdump --macho -t SymbolDemo 可以看到符號(hào)
l 本地
g 全局
indirect symbols間接符號(hào)表
外部符號(hào)的是在懶加載符號(hào),在第一次調(diào)用才綁定,我們通過匯編看一下流程
首先我們?cè)诘谝徽{(diào)用nslog的地方打一個(gè)斷點(diǎn),看匯編

通過image list 找到macho的偏移是 0x0000000104454000,也就是pagezero+ASLR
此次運(yùn)行時(shí)的ASLR是0x4454000,將0x10445a4a8 - 0x4454000 = 0x00000001000064a8 就是macho文件bl的位置:

發(fā)現(xiàn)是跳到外部符號(hào)的樁,樁里面有一段代碼,匯編CTRL + Stepinto點(diǎn)擊去看匯編:

br x16 0x000000010445a55c - 0x4454000 = 0x000000010000655c:

br去符號(hào)表里的地址執(zhí)行665c,找655c:

發(fā)現(xiàn)執(zhí)行的是上面紅框出來的代碼, 本地的一行代碼 ldr #1008000,即符號(hào)綁定

綁定之后,懶加載符號(hào)表里的data就修改了。
總結(jié)
外部符號(hào)綁定過程
- 外部函數(shù)調(diào)用執(zhí)行樁里面的代碼 (Text stubs)
通過懶加載符號(hào)表里面的地址去執(zhí)行 - 懶加載符號(hào)表里面默認(rèn)存儲(chǔ)的是尋找binder的代碼
binder函數(shù)在非懶加載符號(hào)表里(程序運(yùn)行就綁定好了)