目錄
- 一、符號表之間的關(guān)聯(lián)
- 二媳溺、去符號
- 三碍讯、恢復(fù)符號
- 四捉兴、使用fishhook HOOK系統(tǒng)MethodSwizzle方法,以檢測APP是否被HOOK
- 五难衰、使用MonkeyDev進行重新簽名和代碼注入
- 六逗栽、總結(jié)
一彼宠、符號表之間的關(guān)聯(lián)
懶加載符號表凭峡、間接符號表、符號表倍踪、字符串表之間的關(guān)聯(lián)
懶加載符號表
和間接符號表
index一一對應(yīng)
接符號表
保存了在符號表
中的index
符號表
中保存了在字符串表
中的index
上面的流程和fishhook查找符號的流程圖一致:The process of looking up the name of a given entry in the lazy or non-lazy pointer tables looks like this:
二扩借、去符號
一般情況下我們的APP會去掉所有符號(不包括間接符號)缤至、動態(tài)庫會保留全局符號供外部調(diào)用领斥。
Xcode 默認是去掉所有符號的,但是Deployment Postprocessing 需要設(shè)置為Yes何恶,否者只有在打包時才會去掉所有符號细层。
符號表中只有間接符號了今艺,本地符號和全局符號都去掉了
添加如下代碼:
運行APP后斷點是不起作用的虚缎,因為去掉了所有符號钓株。添加NSLog
符號斷點可以斷住轴合,但是去掉符號后函數(shù)名會變?yōu)?code>___lldb_unnamed_symbol2
如果調(diào)用的是OC方法可以添加符號斷點并通過參數(shù)寄存器查看方法名和參數(shù)
還可以通過地址斷點(ASLR+地址偏移值)受葛、恢復(fù)符號表
等方法查看方法名
三、恢復(fù)符號
- (void)differtest {
}
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"第一次外部函數(shù)的調(diào)用!");
NSLog(@"第二次外部函數(shù)的調(diào)用!");
}
- (void)test1 {
NSLog(@"第一次外部函數(shù)的調(diào)用!");
}
- (void)test {
[self test1];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self test];
}
雖然去掉所有符號后符號表中沒有符號了,但是類的方法列表中還是存在闰渔。因此通過方法列表冈涧,類名列表等信息可以恢復(fù)出相關(guān)的符號
通過restore-symbol工具恢復(fù)符號(C函數(shù)正蛙、靜態(tài)函數(shù)不能恢復(fù)乒验,只能恢復(fù)Runtime相關(guān)的)
執(zhí)行代碼./restore-symbol -o symbolDemo2 symbolDemo
后即可恢復(fù)符號徊件,恢復(fù)符號的MachO還需要重新簽名打包
四蒜危、使用fishhook HOOK系統(tǒng)MethodSwizzle方法辐赞,以檢測APP是否被HOOK
目標:APP中可以MethodSwizzle方法硝训,破解APP時直接調(diào)用MethodSwizzle方法會失敗窖梁。
4.1 新建項目AntiHook模擬我們自己的APP、添加兩個按鈕并關(guān)聯(lián)方法
代碼如下:
- (IBAction)btnClick1:(id)sender {
NSLog(@"按鈕1調(diào)用!");
}
- (IBAction)btnClick2:(id)sender {
NSLog(@"按鈕2調(diào)用了!");
}
防護代碼需要寫在Framework中邀窃,因為Framework加載比主工程中的代碼加載更快瞬捕,別人注入的HOOK代碼在Framework加載之后舵抹、在主工程之前惧蛹。
添加主Framework
// AntiHookCode.m
#import "fishhook.h"
@implementation AntiHookCode
+ (void)load {
//exchange方法防護
struct rebinding exchange;
exchange.name = "method_exchangeImplementations";
exchange.replacement = my_exchange;
exchange.replaced = (void *)&exchangeP;
//set香嗓、get方法防護
//setIMP
struct rebinding setIMP;
setIMP.name = "method_setImplementation";
setIMP.replacement = my_exchange;
setIMP.replaced = (void *)&setIMP_p;
//getIMP
struct rebinding getIMP;
getIMP.name = "method_getImplementation";
getIMP.replacement = my_exchange;
getIMP.replaced = (void *)&getIMP_p;
struct rebinding bds[] = {exchange,setIMP,getIMP};
rebind_symbols(bds, 3);
}
//指針!這個可以暴露給外接!我自己的工程使用!!
void (*exchangeP)(Method _Nonnull m1, Method _Nonnull m2);
IMP _Nonnull (*setIMP_p)(Method _Nonnull m, IMP _Nonnull imp);
IMP _Nonnull (*getIMP_p)(Method _Nonnull m);
void my_exchange(Method _Nonnull m1, Method _Nonnull m2){
NSLog(@"檢測到了HOOK!");
}
@end
HookMgr.h中暴露AntiHookCode.h頭文件
HookMgr.h設(shè)置為Public供外界使用
ViewController.m:自己項目交換按鈕2的實現(xiàn)
#import <HookMgr/HookMgr.h>
- (void)viewDidLoad {
[super viewDidLoad];
//HOOK
exchangeP(class_getInstanceMethod(self.class, @selector(btnClick2:)), class_getInstanceMethod(self.class, @selector(test)));
}
- (void)test {
NSLog(@"本工程的HOOK!很順利!!!");
}
4.2 新建項目HookDemo用來HOOK我們的APP
將編譯好的AntiHook拷貝到HookDemo項目中的APP/Payload目錄中(真機編譯)
終端進入APP目錄
zip -ry WeChat.ipa Payload (ipa的名字這里沿用之前的)
刪除Payload目錄
HookDemo項目編譯運行到手機中
添加注入代碼:模擬別人HOOK按鈕1
添加腳本執(zhí)行
運行項目將APP安裝到手機、控制臺會直接輸出:檢測到了HOOK!
點擊按鈕1也不會執(zhí)行test
方法掺出。這樣就達到了HOOK APP方法失敗的目的
點擊按鈕2會顯示本工程的HOOK!很順利!!!
五汤锨、使用MonkeyDev進行重新簽名和代碼注入
安裝好后重新打開Xcode就有如下功能(安裝MonkeyDev后Xcode如果打開崩潰牍汹,刪除Xcode重裝一次就可以了)
5.1 重簽名
- 選擇MonkeyApp模板創(chuàng)建項目MonkeyDemo并
運行安裝APP
- 將需要重簽名的ipa or app 拖到TargetApp目錄中
- 運行項目MonkeyDemo到手機即可進行重簽名
5.2 代碼注入:OC方法HOOK
修改MonkeyDemoDylib.xm文件的type
HOOK APP中ViewController的BtnClick1方法
Monkey實際上使用的是MethodSwizzle 中的method_getImplementation/method_setImplementation方法
六、總結(jié)
- HOOK:鉤子露该,改變程序的執(zhí)行流程的一種技術(shù)
- MethodSwizzle
- 利用OC的運行時(Runtime) 特性修改SEL和IMP (函數(shù)指針) 對應(yīng)關(guān)系第煮。達到HOOK OC方法的目的
-
method_exchangelMP...
交換兩個IMP -
class_replaceMethod
替換某個SEL的IMP (如果沒有該方法包警,就添加。相當于替換掉這個方法) -
method_getlmplementation特铝、method_setlmplementation
獲取和設(shè)置某個方法的IMP (很多三方框架都使用)
- fishhook
- Facebook提供的一個工具篱瞎,利用MachO文件的加載原理,動態(tài)修改懶加載和非懶加載兩個符號表!
- Cydia Substrate
- 一個強大的框架牵素。
- fishhook
- 可以HOOK
系統(tǒng)的函數(shù)
笆呆,但是無法HOOK自定義的函數(shù)
- 原理:
- 共享緩存
- iOS系統(tǒng)有一塊特殊的位置粱挡,存放公用動態(tài)庫询筏。動態(tài)庫共享緩存(dyld shared cache)
- PIC技術(shù)
- 由于外部的函數(shù)調(diào)用, 在我們編譯時刻是沒法確定其內(nèi)存地址的逆屡。
- 蘋果就采用了
PIC
技術(shù)(位置無關(guān)代碼)。在MachO文件DATA段砍的,建立兩張表莺治,懶加載和非懶加載表谣旁,里面存放執(zhí)行外部函數(shù)的指針! - 首次調(diào)用懶加載函數(shù), 會去找
樁
執(zhí)行代碼夕土。首次執(zhí)行會執(zhí)行binder
函數(shù)!
- 共享緩存
- 通過字符找到懶加載表
- fshhook利用
stringTable
-->Symbols
-->indirect Symbols
-->懶加載符號表
之間的對應(yīng)關(guān)系瘟判。通過重綁定修改指針的值達到HOOK的目的拷获。
- fshhook利用
- 反H0OK基本防護
- 利用fishhook修改Methodswizzle 相關(guān)函數(shù)來檢測是否被HOOK
- 防護代碼要最先被加載减细,否則先HOOK就修改完畢了,防護無效驮吱。
- 原始工程編寫的Framework庫會優(yōu)先于注入庫加載左冬。所以適合寫防護代碼纸型。