1. Hook的方式
Hook是改變程序運行流程的一種方式祭芦,通過Hook可以讓自己的代碼運行在別人的程序中孔厉。需要了解其Hook原理墓律,這樣就能夠?qū)阂獯a攻擊進(jìn)行有效的防護(hù)蒂窒。
1.1 Method Swizzle
Method Swizzle 是利用OC的Runtime的特性躁倒,去動態(tài)改變SEL(方法編號)與IMP(方法實現(xiàn))的對應(yīng)關(guān)系,達(dá)到OC方法調(diào)用流程更改的目的洒琢。也是主要用于OC方法秧秉。
1.2 Cydia Substrate
Cydia Substrate 原名叫做Mobile SubStrate,主要作用為針對C函數(shù)衰抑,OC函數(shù)以及函數(shù)的地址進(jìn)行Hook操作象迎。并且有個很大的優(yōu)勢,Cydia Substrate 并不是僅僅是針對iOS設(shè)計呛踊,Andriod一樣也可以使用砾淌。Cydia Substrate定義了一系列的函數(shù)和宏,底層調(diào)用了objc的runtime和fishHook來替代目標(biāo)函數(shù)或者系統(tǒng)方法谭网。
其中有兩個函數(shù)
MSHookMessageEx主要用于OC方法汪厨;
MSHookFunction主要用于C++和C函數(shù)。
MobileLoader:主要用于加載第三方dylib運行的應(yīng)用程序中蜻底。啟動時MobileLoader會根據(jù)指定的第三方動態(tài)庫加載進(jìn)去骄崩,第三方動態(tài)庫也是我們寫的破解程序聘鳞。
safe mode:破解程序的本質(zhì)在于dylib,寄生于別人程序進(jìn)程中要拂。但是系統(tǒng)進(jìn)程一旦出現(xiàn)錯誤抠璃,可能會導(dǎo)致整個進(jìn)程崩潰,也可能會導(dǎo)致iOS程序崩潰脱惰。在Cydia Substrate 中引入了安全模式搏嗡,如果一旦錯誤,三方的dylib會被禁用拉一,便于查錯和修復(fù)采盒。
1.3 fishHook
fishHook是Facebook提供一種動態(tài)修改鏈接Mach-O文件的工具。此利用Mach-O文件加載原理蔚润,通過修改非懶加載和懶加載兩個表的指針達(dá)到C函數(shù)的Hook的目的磅氨。
今天我們主要介紹第三種方式fishHook達(dá)到更改程序的目的。
2. fishhook的原理及實例
fishhook的源碼地址嫡纠,fishhook的主要方法有兩個還有一個結(jié)構(gòu)體:
查看代碼結(jié)構(gòu)為烦租,將紅色圈起來部分移入到代碼中,即可使用fishhook來hook代碼除盏。
2.1 實例1
// rebinding 結(jié)構(gòu)體的定義
// struct rebinding {
// const char *name; // 需要 HOOK 的函數(shù)名稱叉橱,字符串
// void *replacement; // 替換的新函數(shù)(函數(shù)指針,也就是函數(shù)名稱)
// void **replaced; // 保存原始函數(shù)指針變量/地址的指針(它是一個二級指針!)
// };
// C 語言傳參是值/址傳遞的者蠕,把它的值/址穿過去窃祝,就可以在函數(shù)內(nèi)部修改函數(shù)指針變量的值
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"123");
//rebinding結(jié)構(gòu)體
struct rebinding nslog;
nslog.name = "NSLog";// 函數(shù)名稱
nslog.replacement = myNslog; // 新的函數(shù)指針
nslog.replaced = (void *)&sys_nslog;// 保存原始函數(shù)地址的變量的指針
//rebinding結(jié)構(gòu)體數(shù)組
] = {nslog};
/**
* 存放rebinding結(jié)構(gòu)體的數(shù)組
* 數(shù)組的長度
*/
rebind_symbols(rebs, );
}
//---------------------------------更改NSLog-----------
//函數(shù)指針,用來保存原始的函數(shù)地址 (C 語言語法踱侣,函數(shù)指針類型變量)
static void(*sys_nslog)(NSString * format,...);
//定義一個新的函數(shù)
void myNslog(NSString * format,...){
format = [format stringByAppendingString:@"勾上了粪小!\n"];
//調(diào)用原始的
sys_nslog(format);
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
NSLog(@"點擊了屏幕!泻仙!");
}
上面的代碼運行結(jié)果如下:
2.2 實例2
void func(const char * str){
NSLog(@"%s",str);
}
- (void)viewDidLoad {
[super viewDidLoad];
//rebinding結(jié)構(gòu)體
struct rebinding nslog;
nslog.name = "func";
nslog.replacement = new_func;
nslog.replaced = (void *)&old_func;
//rebinding結(jié)構(gòu)體數(shù)組
] = {nslog};
/**
* 存放rebinding結(jié)構(gòu)體的數(shù)組
* 數(shù)組的長度
*/
rebind_symbols(rebs, );
}
//---------------------------------更改NSLog-----------
//函數(shù)指針
static void(*old_func)(const char * str);
//定義一個新的函數(shù)
void new_func(const char * str){
NSLog(@"%s + 1",str);
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
func("哈哈");
}
運行結(jié)果如下:
從上面可以看出自定義的交換方法為什么交換不了呢糕再?首先可以肯定的是代碼是OK的,下面我們講解原理玉转,為什么自定義的方法不行呢?
3. 原理探究
系統(tǒng)內(nèi)核分別加載Mach-O文件殴蹄、dyld動態(tài)連接器究抓,首先通過ASLR技術(shù)(地址空間布局隨機(jī)化)加載可執(zhí)行文件呀潭,MachO文件被加載的時候是隨機(jī)地址兄猩,加載MachO文件完成后敬惦,dyld開始加載依賴的動態(tài)庫豫领,調(diào)試時通過命令 image List 可看到相關(guān)的類庫鸠窗。
PIC(Promrammable Interrupt Controller)位置代碼獨立擦盾,由外設(shè)發(fā)出中斷請求需要中斷控制器來處理除嘹。如果MachO內(nèi)部需要調(diào)用系統(tǒng)的庫函數(shù)時展鸡,先在_DATA段中建立一個指針,指向外部函數(shù)畅卓,dyld會動態(tài)的進(jìn)行綁定擅腰,將MachO中的DATA段中的指針,指向外部函數(shù)(DYLD會告訴MachO要依賴的外部庫的位置)翁潘。_DATA段中建立的指針就是符號(symbols)趁冈,它是幫你指向內(nèi)部的函數(shù)調(diào)用,指向外部的函數(shù)地址拜马。所以渗勘,fishhook的rebind_symbols(重新綁定符號)函數(shù),它就是將你指向系統(tǒng)的NSLog的符號俩莽,給重新進(jìn)行綁定旺坠,變?yōu)橹赶蚰銉?nèi)部的函數(shù),這樣子就達(dá)到修改的目的了扮超;這也是fishhook的底層原理取刃。這也就是為什么使用fishhook時我們內(nèi)部的函數(shù)修改不了,自定義的函數(shù)修改不了的原因瞒津,只能修改MachO外部的函數(shù)蝉衣。
Mach-O文件內(nèi)部調(diào)用系統(tǒng)函數(shù)時:
1)Mach-O _data段建立了一個指針(也就是符號,實現(xiàn)指向內(nèi)部的函數(shù)調(diào)用巷蚪,指向了外部的函數(shù)地址)病毡,指向了外部函數(shù)(dyld),可讀可寫屁柏,當(dāng)Mach-O被加載進(jìn)去啦膜,就會指向所指的函數(shù)。
2)Dyld會動態(tài)的綁定淌喻,將Mach-O中的data 段中指針指向了外部的函數(shù)僧家,也是Dyld為什么叫做動態(tài)綁定的原因。
這也回答了上面的問題裸删,為什么內(nèi)部/自定義的函數(shù)不能修改八拱,只能修改Mach-O文件的外部函數(shù),如果是另外一個動態(tài)庫或者需要動態(tài)符號綁定的就可以(符號表中能找到才可以實現(xiàn))
利用第一個Demo來測試涯塔,運行起來肌稻,然后查看可執(zhí)行文件,通過MachoView工具:
從圖2看出offset偏移地址為3028匕荸,也就是NSLog函數(shù)文件的偏移地址爹谭,懶加載此表時在Mach-O文件偏移地址+函數(shù)偏移的地址。
下面以Demo1查看榛搔,在Demo1打斷點诺凡,查看Mach-O函數(shù)偏移地址东揣,通過指令image list 第一個就是Mach-O內(nèi)容和地址(本人上篇博客地址即可)
Mach-O在內(nèi)存的偏移地址也就是Mach-O的真實地址,發(fā)現(xiàn)為 0x000000010a9c5000
通過上面紅色加重算法腹泌,計算Mach-O文件Data段的函數(shù)指針
發(fā)現(xiàn)執(zhí)行完只有就會被綁定嘶卧。NSLog函數(shù)文件就會被綁定。
下面再看一下真屯,對于屏幕點擊的脸候,hook如下
前提是我們?nèi)コ齎iewDidLoad方法里面的NSLog(@“123”)這句代碼,運行代碼绑蔫,最后將斷點斷在touchesBegan里面运沦,此時開始看地址和內(nèi)容
截圖的前兩次打印是程序運行時,但是未曾點擊touchesBegan配深,后兩次是點擊屏幕時斷點進(jìn)入到了里面携添,再看內(nèi)容,打印的對象是NSLog還是myNslog篓叶,通過上面發(fā)現(xiàn)是myNslog烈掠,說明Hook成功。
通過上面可看出缸托,fishhook能夠Hook c函數(shù)左敌,是因為Mach-O文件特點,PIC位置代碼獨立造就了靜態(tài)語言C也有動態(tài)的部分俐镐,之后通過Dyld進(jìn)行動態(tài)綁定的時機(jī)矫限,在這其中我們就可以做手腳,替換自定義的方法佩抹。
fishhook是根據(jù)方法字符串的名字“NSLog”叼风,它是怎么找到的呢?下面將講解利用符號表查看函數(shù)名稱字符串棍苹。
4. 符號表查看函數(shù)名稱
再次查看Mach-O文件无宿,查看懶加載表中的NSLog函數(shù)
懶加載表是和動態(tài)符號表是一一對應(yīng)關(guān)系,通過上面發(fā)現(xiàn)NSLog函數(shù)時第一個枢里,而對應(yīng)的Dynamic Symbol table也是第一個孽鸡,打開Dynamic Symbol table
查看Dynamic Symbol Table 第一個也是NSLog,查看Data值為7A栏豺,對應(yīng)的十進(jìn)制為122梭灿,然后到Symbols Table里面查看122,如下:
查看Symbols Table的data值為0000009B冰悠,然后在String Table Index去看函數(shù)偏移值為0000009B的內(nèi)容,如下:
為什么選擇00004F94查看NSLog呢配乱,我們從上面得知Symbols Table的data值為0000009B溉卓,然后加上String Table的函數(shù)第一個地址為00004F04皮迟,然后將0000009B + 00004F04 = 0X4F9F,最后看00004F94里面包含了0X4F9F桑寨,藍(lán)色內(nèi)容看出是NSLog內(nèi)容伏尼,也就是找到啦。完美N疚病1住!
以上過程可以在fishhook中g(shù)ithub上有說明圖:
上面的說明圖也就是通過符號表查看函數(shù)名稱以及反過來也可以逆查的過程沙咏。配上說明圖辨图,方便大家熟悉流程。
5. 總結(jié)
上面講述了Hook的幾種技術(shù)方式以及fishhook的原理探究肢藐,以及如何讓別人的app實現(xiàn)自己的代碼故河。下面我們對此總結(jié)一下,寫了一個本篇博客的整個過程便于大家整理吆豹,希望對大家有所幫助加深理解鱼的。
原文地址:
https://www.bbsmax.com/A/1O5E3rQ3z7/
http://www.reibang.com/p/bc1c000afdba
https://mp.weixin.qq.com/s/-uBmPtpupzoDnqKx9U-P2Q
感謝!