Fishhook 學(xué)習(xí)筆記

一、Fishhook 是什么命满?

簡單來說Fishhook就是hook函數(shù)的一種工具童芹,當(dāng)然它hook的原理和我們熟知的Method Swizzle 方式是不一樣的涮瞻,它是Facebook提供的一個動態(tài)修改鏈接mach-O文件的工具。

  • Method Swizzle 是利用OC的Runtime特性假褪,動態(tài)改變SEL(方法編號)和IMP(方法實現(xiàn))的對應(yīng)關(guān)系署咽,達(dá)到OC方法調(diào)用流程改變的目的。主要用于OC方法生音。
  • Fishhook 是利用MachO文件加載原理宁否,通過修改懶加載和非懶加載兩個表的指針達(dá)到C函數(shù)HOOK的目的。

二缀遍、Fishhook的簡單使用

首先在github上下載fishhook庫:


image.png

解壓zip包后慕匠,將其中的.c和.h文件拖入你想hook的項目當(dāng)中,我們所用到的就是這兩個文件瑟由,當(dāng)然網(wǎng)上有大把的關(guān)于fishhook的源碼剖析資料絮重,大家可以自行查閱,這里我就直接上代碼了歹苦,拖入步驟不作詳細(xì)介紹。

#import "ViewController.h"
#import "fishhook.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
 
    //定義rebinding 結(jié)構(gòu)體
    struct rebinding rebind = {};
    rebind.name = "NSLog";
    rebind.replacement = hookNSLog;
    rebind.replaced = (void *)&nslogMethod;
    
    //將上面的結(jié)構(gòu)體 放入 reb結(jié)構(gòu)體數(shù)組中
    struct rebinding red[]  = {rebind};
    
    /*
     * arg1 : 結(jié)構(gòu)體數(shù)據(jù)組
     * arg2 : 數(shù)組的長度
     */
    
    rebind_symbols(red, 1);
    
}

//定義一個函數(shù)指針 用于指向原來的NSLog函數(shù)
static void (*nslogMethod)(NSString *format, ...);

void hookNSLog(NSString *format, ...){
    
    format = [format stringByAppendingString:@"被勾住了"];
    
    nslogMethod(format);
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    NSLog(@"原有NSLog函數(shù)");
}

將文件導(dǎo)入項目后督怜,在ViewController.m 文件中寫入代碼如上殴瘦,代碼目的是:將點擊屏幕時執(zhí)行的NSLog函數(shù),替換成執(zhí)行hookNSLog函數(shù)号杠。
點擊屏幕時執(zhí)行結(jié)果:


image.png

我們先不管原理是怎樣蚪腋,大家是不是覺得丰歌,這執(zhí)行結(jié)果和以上代碼干的事,是不是很像runtime的方法交換屉凯。
但是從表面上看立帖,runtime和fishhook的區(qū)別,大家都知道使用runtime交換的是OC的方法悠砚,而fishhook卻是交換C函數(shù)晓勇,這里就會有個疑惑,C語言不是靜態(tài)語言嗎灌旧,為什么fishhook可以直接交換函數(shù)呢绑咱?
ok ! 在疑問之前,我們先將以上代碼稍作修改枢泰,在看一下結(jié)果描融。

#import "ViewController.h"
#import "fishhook.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
 
    //定義rebinding 結(jié)構(gòu)體
    struct rebinding rebind = {};
    rebind.name = "funcDlog";
    rebind.replacement = hookNSLog;
    rebind.replaced = (void *)&nslogMethod;
    
    //將上面的結(jié)構(gòu)體 放入 reb結(jié)構(gòu)體數(shù)組中
    struct rebinding red[]  = {rebind};
    
    /*
     * arg1 : 結(jié)構(gòu)體數(shù)據(jù)組
     * arg2 : 數(shù)組的長度
     */
    
    rebind_symbols(red, 1);
    
}

//定義一個函數(shù)指針 用于指向原來的NSLog函數(shù)
static void (*nslogMethod)(NSString *format, ...);

void hookNSLog(NSString *format, ...){
    
    format = [format stringByAppendingString:@"被勾住了"];
    
    nslogMethod(format);
}

void funcDlog(NSString *format, ...){
    
    NSLog(@"%@", format);
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    
    funcDlog(@"原有NSLog函數(shù)");
}


@end

來我們看下代碼的改動:
我們將原來hook的目標(biāo)轉(zhuǎn)換了,自己定義了一個函數(shù)void funcDlog(NSString *format, ...),在函數(shù)調(diào)用NSLog函數(shù)衡蚂,而我們這次Hook的目標(biāo)變成了 funcDlog函數(shù)窿克,按照之前的運行結(jié)果,猜測這次的運行結(jié)果應(yīng)該還是“ 原有NSLog函數(shù)被勾住了”毛甲,但是實際運行結(jié)果卻是:

image.png

結(jié)果顯示我們的代碼并沒有hook成功年叮,綜合以上兩次運行結(jié)果,我們發(fā)現(xiàn)了丽啡,fishhook可以實現(xiàn)runtime做不到的將C函數(shù)進行交換谋右,但是我們自己定義的C函數(shù)卻不能交換,只能hook系統(tǒng)的函數(shù)补箍,這是為什么呢改执?我們來看下一個章節(jié)。

三坑雅、Fishhook的原理探究

首先在探究fishhook的原理之前辈挂,我要清楚幾個問題:

  • MachO是怎么加載的
    • 首先我們要知道,MachO文件是被dyld加載的裹粤,dyld是動態(tài)加載终蒂,也負(fù)責(zé)動態(tài)庫的加載。我們都知道動態(tài)庫加載時并不在我們自己的MachO文件內(nèi)部遥诉,是存在系統(tǒng)動態(tài)緩存區(qū)拇泣,當(dāng)我們MachO文件加載過后會動態(tài)加載我們所依賴的那些動態(tài)庫。
  • ASLR技術(shù)
    • 我們的MachO文件在內(nèi)存當(dāng)中每次的運行地址是不一樣的矮锈,MachO文件加載的時地址是隨機的霉翔,這就是ASLR技術(shù)。蘋果每次加載MachO文件時會隨機分配一個地址苞笨,來降低緩沖區(qū)溢出债朵。

那問題來了子眶,每次MachO文件加載的地址不一樣,我們大家都知道序芦,系統(tǒng)的動態(tài)庫緩存區(qū)的地址也是變化的臭杰,那每次dyld加載動態(tài)庫的時候是怎樣找到依賴動態(tài)庫進行加載的呢?

  • PIC 位置代碼獨立

    • 當(dāng)MachO內(nèi)部需要調(diào)用系統(tǒng)的庫函數(shù)時谚中,會現(xiàn)在MachO文件_DATA段中建立一個指針渴杆,指向外部函數(shù),dyld就會將指針與函數(shù)進行動態(tài)綁定藏杖,將MachO中的DATA段中的指針指向外部函數(shù)将塑,從而實現(xiàn)加載。

我們來看下第一份代碼的MachO文件蝌麸,如圖:


image.png

當(dāng)MachO內(nèi)部有兩個指針用來加載動態(tài)庫:

  • Non-Lazy Symbol Pointers :不懶加載指針
  • Lazy Symbol Pointers :懶加載指針

這兩個指針就是dyld用來在MachO內(nèi)部調(diào)用系統(tǒng)庫函數(shù)時來指向外部函數(shù)進行動態(tài)綁定加載点寥。從上圖我們可以看出在Lazy Symbol Pointers指針表中NSLog的位置,以及偏移地址是0x8018来吩。

那我們根據(jù)這個偏移地址在第一份代碼中進行LLDB調(diào)試看看結(jié)果如何:

  • 首先我們先在 fishhook的代碼執(zhí)行前設(shè)置斷點:


    image.png
  • 然后我們先拿到MachO文件在內(nèi)存中的首地址:0x0000000100154000


    image.png
  • 根據(jù)偏移地址拿到指針指向的地址值:


    image.png
  • 根據(jù)地址值中的地址進行反匯編敢辩,我看下結(jié)果:


    image.png

反匯編后結(jié)果清楚顯示地址指向的是Foundation下的 NSLog函數(shù)

ok! 這是還沒有進行fishhook前的NSLog ,那我們將斷點斷到fishhook代碼執(zhí)行后在看結(jié)果:


image.png

同樣的偏移地址弟疆,地址值卻發(fā)生了變化戚长,反匯編后卻發(fā)現(xiàn),地址指向的是fishhookDemo1下的hookNSLog函數(shù)怠苔。

這就是Fishhook的原理:在dyld加載MachO文件所需要的系統(tǒng)函數(shù)時同廉,通過改變MachO文件中的DATA段中指向外部函數(shù)的指針?biāo)赶虻牡刂罚瑏韺崿F(xiàn)動態(tài)交換柑司。

這里就可以解釋我們在兩次運行結(jié)果對比留下的兩個疑惑:

  • 為什么fishhook可以交換C函數(shù)
    • fishhook 是通過改變dyld加載動態(tài)庫所用的指針指向的函數(shù)地址迫肖,來實現(xiàn)的函數(shù)交換 ,所以C函數(shù)通過dyld動態(tài)加載動態(tài)庫也展現(xiàn)出C函數(shù)動態(tài)的一面攒驰。
  • 為什么我們自己寫的C函數(shù)fishhook交換不了
    • 我們自己寫的C函數(shù)不在系統(tǒng)動態(tài)庫緩存區(qū)蟆湖,而是存在我們自己的MachO文件當(dāng)中,不經(jīng)過dyld加載玻粪,直接編譯成匯編語言隅津,在MachO中的TEXT段中。所以fishhook的原理不能交換我們自己寫的C函數(shù)劲室。

四伦仍、通過符號找到字符串

在上面我們知道了系統(tǒng)的動態(tài)庫與我們的MachO文件是通過dyld在加載時在MachO文件中的一個指針指向了外部函數(shù)來進行加載,但是這只是個地址很洋,我們在代碼中調(diào)用函數(shù)時通過函數(shù)名稱來調(diào)用的呢铆,那這個地址和我們調(diào)用的函數(shù)名稱是如何建立起聯(lián)系的呢?我們先來看下剛才的MachO文件蹲缠,如圖:

圖1:


image.png

圖2:


image.png

從圖1棺克、圖2中我們可以看出Lazy Symbol Pointers 表 和 Dyamic Symbol Table 表中的關(guān)系是一一對應(yīng)的,遍歷前一張表的index就可以對應(yīng)到后一張表线定。注意圖2中的對應(yīng)index行中的 Data 值為0x7F娜谊。

圖3:


image.png

在Symbol Table 表中先拿到第一條的偏移地址0xC500。

圖4:


image.png

在圖2中index中獲取的Data值為0x7F斤讥,這值是圖4中index的標(biāo)號纱皆,由于圖4中index的偏移為0x10,所以偏移地址+Data對應(yīng)的偏移地址為0xCCF0,找到當(dāng)前index的Data值是0xA7芭商。

圖5:


image.png

最后在String Table 表中 偏移首地址+0xA7 的值為0x0000D02B派草,在表中差得字符串以“_”開頭,以“.”結(jié)尾铛楣。所得的字符串就是外部函數(shù)地址所對應(yīng)的函數(shù)名近迁。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市簸州,隨后出現(xiàn)的幾起案子鉴竭,更是在濱河造成了極大的恐慌,老刑警劉巖岸浑,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件搏存,死亡現(xiàn)場離奇詭異,居然都是意外死亡矢洲,警方通過查閱死者的電腦和手機璧眠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來读虏,“玉大人责静,你說我怎么就攤上這事【蚱” “怎么了泰演?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長葱轩。 經(jīng)常有香客問我睦焕,道長,這世上最難降的妖魔是什么靴拱? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任垃喊,我火速辦了婚禮,結(jié)果婚禮上袜炕,老公的妹妹穿的比我還像新娘本谜。我一直安慰自己,他們只是感情好偎窘,可當(dāng)我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布乌助。 她就那樣靜靜地躺著溜在,像睡著了一般。 火紅的嫁衣襯著肌膚如雪他托。 梳的紋絲不亂的頭發(fā)上掖肋,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天,我揣著相機與錄音赏参,去河邊找鬼志笼。 笑死,一個胖子當(dāng)著我的面吹牛把篓,可吹牛的內(nèi)容都是我干的纫溃。 我是一名探鬼主播,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼韧掩,長吁一口氣:“原來是場噩夢啊……” “哼紊浩!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起揍很,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤郎楼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后窒悔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體呜袁,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年简珠,在試婚紗的時候發(fā)現(xiàn)自己被綠了阶界。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡聋庵,死狀恐怖膘融,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情祭玉,我是刑警寧澤氧映,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站脱货,受9級特大地震影響岛都,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜振峻,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一臼疫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧扣孟,春花似錦烫堤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拔创。三九已至,卻和暖如春湾盗,著一層夾襖步出監(jiān)牢的瞬間伏蚊,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工格粪, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人氛改。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓帐萎,卻偏偏與公主長得像,于是被迫代替她去往敵國和親胜卤。 傳聞我的和親對象是個殘疾皇子疆导,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,925評論 2 344

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

  • 13. Hook原理介紹 13.1 Objective-C消息傳遞(Messaging) 對于C/C++這類靜態(tài)語...
    Flonger閱讀 1,402評論 0 3
  • 13.1 Objective-C消息傳遞(Messaging) 對于C/C++這類靜態(tài)語言,調(diào)用一個方法其實就是跳...
    泰克2008閱讀 1,977評論 1 6
  • 一葛躏、溫故而知新 1. 內(nèi)存不夠怎么辦 內(nèi)存簡單分配策略的問題地址空間不隔離內(nèi)存使用效率低程序運行的地址不確定 關(guān)于...
    SeanCST閱讀 7,779評論 0 27
  • [TOC] 回顧 注入的相關(guān)要素: 注入的形式:利用動態(tài)庫的特性進行注入澈段,包括Framework、Dylib舰攒“芨唬可以...
    _順_1896閱讀 847評論 0 0
  • 2016年的12月,很冷很冷摩窃。我從讀書的城市輾轉(zhuǎn)去到廣州南站兽叮,跨過1458.9km去見你。 那時候猾愿,我讀書的城市還...
    莫名墨閱讀 258評論 7 7