有關(guān)fishhook的原理如果看過之前的MachO鏈接過程,那其實就應(yīng)該很簡單了军浆,他就是講懶加載符號表和非懶加載符號表的值改版了棕洋,使它指向我們自己的地址,然后又把原函數(shù)的真實地址保存在了一個指針上乒融, 讓外部可以繼續(xù)調(diào)用原來的函數(shù)掰盘。關(guān)于源碼解析的文章,可以閱讀下面的兩篇赞季。
http://ios.jobbole.com/92918/
http://m.desgard.com/2017/12/17/fishook-1/index.html
這里想說的是一個注意點愧捕。剛剛說到fishhook把原函數(shù)的真實地址保存在了一個指針上, 讓外部可以繼續(xù)調(diào)用原來的函數(shù)申钩〈位妫考慮一種情況,我們在某個系統(tǒng)的A函數(shù)首次調(diào)用前撒遣,使用fishhook去hook它邮偎,在我們的hook函數(shù)里面去調(diào)用了原來的函數(shù)。然后在hook之后义黎,連續(xù)調(diào)用兩次剛剛我們hook的函數(shù)禾进。偽代碼如下,請問會輸出幾個"hook method"廉涕?
static void (*ori_system_method_A)(void);
void hook_system_method_A() {
NSLog(@"hook method");
ori_system_method_A();
}
- (void)viewDidLoad {
rebind_symbols((struct rebinding[]){
{"system_method_A", (void *)&hook_system_method_A, (void **)&ori_system_method_A}
}, 1);
system_method_A();
system_method_A();
}
正確答案是可能1次泻云,也可能2次,這取決于system_method_A具體是什么函數(shù)火的。造成這種差別的原因在于壶愤,該函數(shù)是不是有被其他庫調(diào)用,且其他庫已經(jīng)綁定了真實的函數(shù)地址馏鹤≌鹘罚看了fishhook的源碼之后應(yīng)該知道,fishhook是注冊了_dyld_register_func_for_add_image
函數(shù)來監(jiān)聽image的加載湃累,當(dāng)加載一個每次這個callback回調(diào)之后勃救,就會對傳入的image進(jìn)行處理,拿到image的懶加載表和非懶加載表治力,對其中每一項進(jìn)行遍歷蒙秒,和我們需要hook的函數(shù)名稱做比較,如果找到了名字一樣的函數(shù)宵统,就將我們傳入的原函數(shù)的指針指向該項所指向的函數(shù)地址晕讲。這里面需要注意,我們只傳入了一個原函數(shù)的指針,所以舊的值會被新的值所覆蓋瓢省。
那么考慮一種情況:如果我需要hook的函數(shù)沒有其他image調(diào)用了弄息,那么就只能在自己的image里面找到匹配,然后進(jìn)行替換勤婚,但是這個時候摹量,所替換的原函數(shù)地址值是最終調(diào)用stub_helper的。所以當(dāng)我們調(diào)用了原函數(shù)指針對應(yīng)的函數(shù)之后,就會觸發(fā)stub_helper,然后stub_helper又會把懶加載表里面的值改為真實的函數(shù)地址载弄。所以就會造成我們的hook方法只能執(zhí)行一遍。
考慮第二種情況:我們需要hook的函數(shù)被其他image調(diào)用了睦尽,且該image的懶加載表或非懶加載表里面已經(jīng)是真實的函數(shù)地址,那么fishhook在處理的時候液兽,雖然第一次是拿到了我們image內(nèi)指向stub_helper的地址骂删,但是后面會被其他image里面的真實的函數(shù)地址所覆蓋。所以調(diào)用原函數(shù)指針對應(yīng)的函數(shù)并不影響我們image里面的懶加載表四啰,故我們的hook方法可以執(zhí)行2次。
總結(jié)一下就是:調(diào)用fishhook之前粗恢,最好把這個需要hook函數(shù)調(diào)用一下柑晒,讓其綁定真實的地址。