iOS 逆向開發(fā)17:HOOK原理下(HOOK OC方法)

iOS 逆向開發(fā) 文章匯總

目錄


一、符號表之間的關(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重裝一次就可以了)

MonkeyDev

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é)

  1. 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
    • 一個強大的框架。
  1. 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的目的。
  1. 反H0OK基本防護
  • 利用fishhook修改Methodswizzle 相關(guān)函數(shù)來檢測是否被HOOK
  • 防護代碼要最先被加載,否則先HOOK就修改完畢了,防護無效。
  • 原始工程編寫的Framework庫會優(yōu)先于注入庫加載。所以適合寫防護代碼。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容