fishHook是Facebook提供的一個動態(tài)修改鏈接mach-O文件的工具。利用MachO文件加載原理,通過修改懶加載表(Lazy Symbol Pointers)和非懶加載表(Non-Lazy Symbol Pointers)這兩個表的指針達(dá)到C函數(shù)HOOK的目的,在逆向中使用比較多,fishHook代碼下載影兽。
一、fishHook原理分析
1. 下載下來后我們會發(fā)現(xiàn)頭文件和簡單,一個結(jié)構(gòu)體和兩個方法莱革。
struct rebinding {
const char *name;//需要HOOK的函數(shù)
void *replacement;//新函數(shù)IMP
void **replaced;//原始函數(shù)IMP
};
FISHHOOK_VISIBILITY
int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel);
FISHHOOK_VISIBILITY
int rebind_symbols_image(void *header,
intptr_t slide,
struct rebinding rebindings[],
size_t rebindings_nel);
- 1.1 接下來我們簡單使用fishHook來hook系統(tǒng)的NSLog方法如下:
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"Qinz");
//rebinding結(jié)構(gòu)體
struct rebinding nslog;
nslog.name = "NSLog";
nslog.replacement = myNslog;
//原始函數(shù)保存在sys_nslog
nslog.replaced = (void *)&sys_nslog;
//rebinding結(jié)構(gòu)體數(shù)組
struct rebinding rebs[1] = {nslog};
/**
* 存放rebinding結(jié)構(gòu)體的數(shù)組
* 數(shù)組的長度
*/
rebind_symbols(rebs, 1);
}
//函數(shù)指針
static void(*sys_nslog)(NSString * format,...);
//定義一個新的函數(shù)
void myNslog(NSString * format,...){
format = [format stringByAppendingString:@"??---hook住了---??"];
//調(diào)用原始的
sys_nslog(format);
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
NSLog(@"--- 點(diǎn)擊屏幕 -------");
}
2. 控制臺會輸出如下
--- 點(diǎn)擊屏幕 -------??---hook住了---??
3. 我們都知道赢笨,C函數(shù)是在編譯的時候就確定了地址,而系統(tǒng)的函數(shù)由于共享緩存庫的存在dyld這篇文章有講解共享緩存庫,運(yùn)行時才由dyld指定地址驮吱,這樣就產(chǎn)生了沖突,為了解決這個問題萧吠,蘋果采用了PIC技術(shù)(位置獨(dú)立代碼)左冬。 通過位置獨(dú)立代碼技術(shù),讓系統(tǒng)函數(shù)NSLog在編譯階段指向一個虛擬地址纸型,該地址存放在macho文件的_DATA段拇砰,這個段專門用來存放指向外部函數(shù)的指針。通過MachOView我們查看到NSLog的虛擬偏移地址如下:
4. 為了證明函數(shù)在調(diào)用前指向的是虛擬地址狰腌,我們通過代碼來證明除破,使用MachO文件首地址加上虛擬偏移地址減去pageZero地址,即可以得到真實內(nèi)存地址琼腔,命令如下:
//MachO文件首地址+NSLog偏移地址-pageZero地址得到真實內(nèi)存地址
x 0x00000001044ac000+0x100008018-0x100000000
5. 前8字節(jié)存放地址瑰枫,通過iOS小端模式從右往左讀取地址查看匯編代碼,通過匯編代碼很容易看出這里并不是一個函數(shù)的調(diào)用代碼,說白了光坝,這段代碼相當(dāng)于蘋果給NSLog開了一個空頭支票尸诽,如下圖:
6. 接下來我們過掉斷點(diǎn),讓NSLog執(zhí)行盯另,然后重新讀取性含,會發(fā)現(xiàn)地址已經(jīng)改變,通過匯編可以看到調(diào)用了NSLog函數(shù)鸳惯,從而證明了編譯時期綁定的地址是一個虛擬地址商蕴。
7. 所以系統(tǒng)函數(shù)調(diào)用過程如下:一但macho文件被dyld加載進(jìn)內(nèi)存的時候,dyld會MachO文件調(diào)用的函數(shù),去共享緩存庫尋找對應(yīng)的函數(shù)指針,然后將_DATA段里面對應(yīng)的IMP地址切換為真實的IMP地址芝发,這樣就間接實現(xiàn)了調(diào)用绪商,從而使用PIC技術(shù)就解決了上面的沖突問題。
8. fishHook就是使用IMP的重新綁定后德,達(dá)到hook的目的部宿。 當(dāng)我們使用fishHook進(jìn)行符號重綁定時,系統(tǒng)的NSLog地址發(fā)生了變化瓢湃,實際上這里的地址IMP指向了我們自己的函數(shù)理张,通過IMP的交換達(dá)到了Hook的目的,如下圖:绵患。
二雾叭、fishHook尋址流程:
1. 通過上面我們知道在懶加載符號表(Lazy Symblo Pointers)中,NSLog的虛擬偏移地址為0x10000b018,Lazy Symblo Pointers與Indirect Symbols表一一對應(yīng)落蝙,如下圖:
2. 我們可以看到NSLog函數(shù)在Symol表中對應(yīng)的下標(biāo)是0000007A织狐,換算為10進(jìn)制的下標(biāo)值為122,如下圖:
3. 接下來找到Symbol表中#122位置筏勒,可以看到NSLog函數(shù)地址對應(yīng)到了Sting Table Index表中0x9B的偏移地址移迫,如下圖:
4. 通過Sting Table標(biāo)的0x9B偏移值加上首地址,即:0000009B+0000CEFC = 0xCF97,如下圖:
5. 最終通過 0xCF97找到NSLog位置管行,如下圖:
6. 官方給的尋址流程如下
總結(jié):fishHook就是根據(jù)PIC技術(shù)和共享緩存庫的關(guān)系厨埋,通過一個一個的表最終找到NSLog虛擬偏移地址,然后在運(yùn)行時交換函數(shù)的IMP來達(dá)到hook目的捐顷。
我的Qinz,希望我的文章對你有幫助荡陷。