目錄
一墓怀、 HOOK概述
HOOK,中文譯為“掛鉤”或“鉤子”抬驴。在iOS逆向中是指改變程序運(yùn)行流程的一種技術(shù)炼七。通過(guò)hook可以讓別人的程序執(zhí)行自己所寫的代碼。在逆向中經(jīng)常使用這種技術(shù)布持。所以在學(xué)習(xí)過(guò)程中豌拙,我們重點(diǎn)要了解其原理,這樣能夠?qū)阂獯a進(jìn)行有效的防護(hù)题暖。
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文件加載原理,通過(guò)修改懶加載和非懶加載兩個(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的簡(jiǎn)單使用
它是Facebook
提供的一個(gè)動(dòng)態(tài)修改鏈接mach-O
文件的工具粪牲。利用MachO
文件加載原理,通過(guò)修改懶加載和非懶加載兩個(gè)表的指針達(dá)到C
函數(shù)HOOK
的目的止剖。
獲取代碼:git clone https:github.com/facebook/fishhook.git
關(guān)鍵函數(shù):
// 用來(lái)重 新綁定符號(hào)表的函數(shù)腺阳。使用它來(lái)交換
FISHHOOK_VISIBILITY
int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel);
參數(shù)一 存放rebinding結(jié)構(gòu)體的數(shù)組(可以同時(shí)交換多個(gè)函數(shù))
參數(shù)二 rebindings數(shù)組的長(zhǎng)度
struct rebinding {
const char *name; // 需要HOOK的函數(shù)名稱,字符串
void *replacement; // 替換到哪個(gè)新的函數(shù)上(函數(shù)指針,也就是函數(shù)名稱)
void **replaced; // 保存原始函數(shù)指針變量的指針(它是一個(gè)二級(jí)指針)
};
2.1 HOOK NSLog
#import "ViewController.h"
#import "fishhook.h"
- (void)viewDidLoad {
[super viewDidLoad];
//------------HOOK NSLog------------
//創(chuàng)建rebinding 結(jié)構(gòu)體
struct rebinding nslog;
nslog.name = "NSLog";
nslog.replacement = my_NSLog;
//保存NSLog系統(tǒng)函數(shù)地址的指針!
nslog.replaced = (void *)&sys_nslog;
//需求:HOOK NSLog
struct rebinding bds[] = {nslog};
rebind_symbols(bds, 1);
}
//函數(shù)指針!
static void (*sys_nslog)(NSString *format, ...);
//新函數(shù)!
void my_NSLog(NSString *format, ...) {
format = [format stringByAppendingString:@"\n我HOOK到了!"];
//走到系統(tǒng)的NSLog里面去!
sys_nslog(format);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
NSLog(@"hello");
}
點(diǎn)擊屏幕輸出:hello
????????? 我HOOK到了!
2.2 HOOK Func
#import "ViewController.h"
#import "fishhook.h"
void func(const char * str) {
NSLog(@"%s",str);
}
- (void)viewDidLoad {
[super viewDidLoad];
//----------HOOK Func -------
//創(chuàng)建rebinding 結(jié)構(gòu)體
struct rebinding func;
func.name = "func";
func.replacement = my_func;
//保存NSLog系統(tǒng)函數(shù)地址的指針!
func.replaced = (void *)&func_p;
//需求:HOOK NSLog
struct rebinding bds[] = {func};
rebind_symbols(bds, 1);
}
static void (*func_p)(const char * str);
void my_func(const char * str) {
NSLog(@"HOOK了!!");
func_p(str);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
func("hello");
}
點(diǎn)擊屏幕輸出:hello
從2.1和2.2可以看到fishHook只能HOOK系統(tǒng)的函數(shù)不能HOOK自定義的函數(shù)
因?yàn)镹SLog不是本Mach-O文件中的,func是本Mach-O文件中的滴须。編譯的時(shí)候舌狗,并不知道NSLog 的真實(shí)地址!因此可以HOOK NSLog函數(shù)叽奥。
三扔水、fishHook原理探究
How it works
dyld binds lazy and non-lazy symbols by updating pointers in particular sections of the __DATAsegment of a Mach-O binary. fishhook re-binds these symbols by determining the locations to update for each of the symbol names passed to rebind_symbols and then writing out the corresponding replacements.
For a given image, the __DATA segment may contain two sections that are relevant for dynamic symbol bindings: __nl_symbol_ptr and __la_symbol_ptr. __nl_symbol_ptr is an array of pointers to non-lazily bound data (these are bound at the time a library is loaded) and __la_symbol_ptr is an array of pointers to imported functions that is generally filled by a routine called dyld_stub_binder during the first call to that symbol (it's also possible to tell dyld to bind these at launch). In order to find the name of the symbol that corresponds to a particular location in one of these sections, we have to jump through several layers of indirection. For the two relevant sections, the section headers (struct sections from <mach-o/loader.h>) provide an offset (in the reserved1 field) into what is known as the indirect symbol table. The indirect symbol table, which is located in the __LINKEDIT segment of the binary, is just an array of indexes into the symbol table (also in __LINKEDIT) whose order is identical to that of the pointers in the non-lazy and lazy symbol sections. So, given struct section nl_symbol_ptr, the corresponding index in the symbol table of the first address in that section is indirect_symbol_table[nl_symbol_ptr->reserved1]. The symbol table itself is an array of struct nlists (see <mach-o/nlist.h>), and each nlist contains an index into the string table in __LINKEDIT which where the actual symbol names are stored. So, for each pointer __nl_symbol_ptrand __la_symbol_ptr, we are able to find the corresponding symbol and then the corresponding string to compare against the requested symbol names, and if there is a match, we replace the pointer in the section with the replacement.
3.1設(shè)置并進(jìn)入第一個(gè)斷點(diǎn):
編譯查看Mach-O文件
拿到ASLR的值:
ASLR + MachO中的 NSLog Data中的值就是當(dāng)前NSLog的實(shí)際地址:
0x0000000100de0000+0x22a4 = 0x100de22a4
通過(guò)ASLR + MachO中的 NSLog Offset中的值也能看到NSLog的實(shí)際地址:
由于還未進(jìn)行NSLog的符號(hào)綁定,因此此時(shí)NSLog的實(shí)際地址并不是NSLog真實(shí)函數(shù)的地址:
3.2進(jìn)入第二個(gè)斷點(diǎn)
由于是懶加載符號(hào)朝氓,所有在調(diào)用NSLog時(shí)才進(jìn)行了符號(hào)綁定魔市。
3.3進(jìn)入第三個(gè)斷點(diǎn)
經(jīng)過(guò)fishhook將NSLog符號(hào)重新綁定了
C語(yǔ)言是靜態(tài)語(yǔ)言,但不是所有的C函數(shù)調(diào)用都是靜態(tài)的赵哲。外部的C函數(shù)調(diào)用是動(dòng)態(tài)的待德。
四、NSLog間接符號(hào)綁定的流程
什么是符號(hào)表?
對(duì)于函數(shù)名枫夺、變量名将宪、方法名等編譯完都會(huì)生成一張符號(hào)表,符號(hào)分為內(nèi)部符號(hào)和外部符號(hào)橡庞。外部符號(hào)(本Mach-O以外的符號(hào))也稱為間接符號(hào)较坛。
符號(hào)也分為本地符號(hào)和全局符號(hào)
本地符號(hào):自己內(nèi)部使用的
全局符號(hào):外部也可以使用
// 全局符號(hào) -- 暴露給外界使用
void test() {
}
// 本地符號(hào) -- 作用域?yàn)楸镜胤?hào)
static void test1() {
NSLog(@"test1");
}
- (void)viewDidLoad {
[super viewDidLoad];
test1();
}
編譯進(jìn)入可執(zhí)行文件目錄:
objdump --macho -t 文件名
可以看到test1是本地符號(hào),test和main是全局符號(hào)
4.1 NSLog調(diào)用前進(jìn)行符號(hào)綁定
添加如下代碼:
運(yùn)行查看匯編代碼:
0x1047562d0-ASLR(0x0000000104754000)=0x22D0(NSLog的樁
)
因此調(diào)用NSLog時(shí)是bl到NSLog的樁(一塊代碼)扒最。
樁的值為:1F2003D570E9025800021FD6 -- 即如下代碼:
因此NSLog的執(zhí)行流程為:bl-->樁代碼(去符號(hào)表里面的綁定地址執(zhí)行)-->符號(hào)表
0x0000000104756384(x16)-0x0000000104754000(ALSR)=0x2384
這里可以看到樁執(zhí)行符號(hào)表中初始的地址
(此時(shí)NSLog的真實(shí)地址還未綁定)
匯編繼續(xù)執(zhí)行236c的代碼(重新運(yùn)行了一次丑勤,ASLR和上面的不一樣了
):
經(jīng)過(guò)執(zhí)行dyld_stub_binder
,就將NSLog的符號(hào)綁定了吧趣。
在非懶加載符號(hào)表中可以看到dyld_stub_binder
的符號(hào)位置法竞,非懶加載符號(hào)剛開始運(yùn)行就綁定了(上篇文章可以看到非懶加載符號(hào)綁定的時(shí)機(jī))耙厚。
4.2 NSLog符號(hào)綁定后的調(diào)用流程
0x0000000102890000(ALSR)+0x8000(offset) = 0x0102898000
現(xiàn)在樁執(zhí)行的就是0x0102898000指向的NSLog的真實(shí)地址了。
間接符號(hào)表的綁定流程:
總結(jié)
- 符號(hào)綁定的過(guò)程
- 外部函數(shù)調(diào)用是執(zhí)行樁里面的代碼! TEXT, stubs
- 通過(guò)懶加載符號(hào)表里面的地址去執(zhí)行!
- 懶加載符號(hào)表里面默認(rèn)保存的是尋找binder的代碼
- binder函數(shù)在非 懶加載符號(hào)表里面(程序運(yùn)行就綁定好了! )
- 外部函數(shù)調(diào)用是執(zhí)行樁里面的代碼! TEXT, stubs
- HOOK: 改變程序原有執(zhí)行流程
- iOS中的HOOK技術(shù)
- OC方法: MethodSwizzle
- 系統(tǒng)函數(shù): fishhook
- iOS中的HOOK技術(shù)
- fishhook:
- 重新綁定符號(hào)達(dá)到HOOK的目的
- 外部符號(hào)岔霸,會(huì)在懶加載和非懶加載表中保存函數(shù)地址
- fishhook就是找到這兩張表薛躬,并且修改里面的地址做到HOOK!
- 重新綁定符號(hào)達(dá)到HOOK的目的
參考
符號(hào)表Symbol Table
iOS-開發(fā)進(jìn)階02:鏈接與Symbol(上)
iOS-開發(fā)進(jìn)階03:鏈接與Symbol(下)