fishhook-動態修改MachO文件

學習hook,不是要攻擊別人,破壞別人的應用場景,而是爲了更好的防禦,讓本身的應用更堅固更安全。git

1、動態庫注入回顧

《動態庫注入》中使用了yololib對自定義動態庫在WX應用中插入,既然能插入自定義庫,咱們就利用hook技術作了小小的改動,替換了登陸按鈕的方法,並攔截了微信步數上傳的方法,修改了步數。hook是改變程序執行流程的技術,可以修改其餘應用,也能對本身應用作防禦。github

一般替換方法有以下:編程

  1. 使用method_exchange交換selimp指針;
  2. 經過getImp獲取原有函數指針,經過setImp設置原sel指向的指針爲自定義函數指針;
  3. 經過getImp獲取原有函數指針,經過replace替換原sel指向的指針爲自定義函數指針,通setImp同樣。

本身項目中使用:數組

一般在本身項目中使用,創建分類,在分類load中來替換方法,以達到監聽方法調用的目的。緩存

hook視圖的出現消失:跟蹤頁面位置; hook析構函數deallocdeinit):跟蹤對象釋放情況; hook消息轉發方法(forword):避免調用未實現方法而產生奔潰; hook數組objectAtIndexedSubscript方法:避免數組越界奔潰; ……安全

只要是OC的方法均可以根據業務需求進行監聽修改或替換。bash

在其餘應用中使用:微信

經過動態庫注入的方式,來插入本身的load,讓本身的代碼能和其餘項目一塊兒執行,一樣使用咱們常規的替換方法便可。經過查看視圖頁面屬性肯定要hook的目標對象。函數

hook微信步數獲取方法:實現微信步數修改; hook微信搶紅包相關方法:實現自動搶紅包功能; hook微信消息撤回方法:實現拒絕消息撤回功能; (ps:學習中,實戰很少,有方法就有更多的可能)工具

在其餘應用中是經過動態庫來進行hook的,所以在目標對象所在類的方法列表中並無要替換的方法,迴歸原有方法調用會出現崩潰,這裏可使用獲取原有方法的函數地址,直接調用函數便可實現原有方法的調用。

以上是針對OC的動態特性來hook的,咱們可以hook動態語言下的函數,那麼能不能直接hook系統的靜態函數呢,下面來看一下fishhook是如何hook的。

2、fishhook概述

fishhookFaceBook的開源庫,可以動態的修改MachO的符號表,來hook系統的C函數。 C函數是在編譯時來肯定函數地址在內存中的偏移量,編譯後該偏移量是肯定的,爲何說是偏移量是肯定的呢?

在不少系統中都採用了 ASLR 技術,對堆、棧、共享庫等線性區佈局進行隨機化,來避免攻擊者直接獲取攻擊代碼位置。Mach0的首地址是隨機變化的,找到首地址就能找到對應的函數地址,即 Offset + Mach0

靜態語言:編譯時肯定函數地址

動態語言:運行時肯定函數地址

3、fishhook簡單使用

使用步驟:

一、直接將.c、.h加入到工程; 二、建立結構體指明被替換的函數、替換的函數、接收原函數地址的指針; 三、綁定符號表。

使用庫函數交換系統NSLog函數:

#pragma mark - 交換NSLog
-(void)exchangeLog {
     NSLog(@"我來了");
    //hook NSLog函數
    struct rebinding imp;
    imp.name = "NSLog";
    imp.replacement = my_NSLog;
    imp.replaced = &sys_nslog;
    
    //存放rebinding結構體數組,一次能夠交換多個函數
    struct rebinding rebs[1] = {imp};
    rebind_symbols(rebs, 1);
    NSLog(@"點擊了屏幕");
}
//實現一個函數來替換原有函數-函數名稱便是函數的指針
void my_NSLog(NSString *format, ...) {
    printf("攔截打印\n");
    sys_nslog(format);
}
//定義指針來接收原始函數的指針
static void (*sys_nslog)(NSString *format,...);
複製代碼

打印以下:

攔截打印
點擊了屏幕
複製代碼

運行輸出,方法被攔截,說明NSLog函數已被替換。

上面有提到,全部的函數地址在編譯後都是肯定,在OC中可以交換方法是由於有selimp的鏈接過程,函數與用戶之間有一箇中間者,那麼fishhook應該也是如此,修改了中間者的imp指向,不然直接調用函數,就沒有交換的可能,在MachO中這個中間者叫符號。

動態替換:user -> sel -> imp

靜態替換:user -> symbol -> imp

hook自定義函數

-(void)exchangeFun{
    struct rebinding imp;
    imp.name = "old_func";
    imp.replacement = new_func;
    imp.replaced = &sys_func;
    struct rebinding rebs[1] = {imp};
    rebind_symbols(rebs, 1);
    old_func();
}
void (*sys_func);
void old_func(){
    printf("old_func\n");
}
void new_func(){
    printf("new_func\n");
}
複製代碼

打印以下:

old_func
複製代碼

打印仍是原來的方法,這裏並無替換。這裏能夠猜測自定義函數調用沒有產生中間者,這裏是直接調用的,因此沒法交換。

4、fishhook的實現原理

fishhook主要利用了共享緩存功能和PIC技術來實現hook功能。

動態共享緩存

iOS系統爲節省內存資源,將系統的動態庫資源統一放在了系統的共享區域,該區域其餘應用均可以訪問。

PIC技術

PIC技術叫位置代碼獨立,在MachO文件中會預留出一段空間,這一段空間叫作符號表,在MachO文件的數據段中。dyld在加載MachO文件到內存中後,會將共享區的系統函數地址綁定到對應的符號上,並插入到符號表中。這樣在項目中調用相關係統函數時,實際上調用的是對應的符號,經過符號來找到具體的系統函數地址。

symbol.png

到這裏思路應該清晰不少,fishhook實現以下:

  1. 根據符號(字符)獲取系統函數地址;
  2. 替換符號指向的地址爲用戶聲明的函數地址(符號綁定);
  3. 對外部聲明的指針進行系統函數地址賦值。

上面所提到的自定義函數沒法hook也就瞭然了,自定義函數地址肯定在代碼段中,缺乏dyld的符號綁定階段,而且應用中調用的是最原始的函數地址,所以沒法交換。

data段:程序運行期間可讀可寫 代碼段:程序運行期間只讀

5、fishhook符號綁定分析

這裏使用 MachOView 工具,對以上給出的代碼生成的MachO文件進行分析。

*獲取符號表地址

一、machO符號表中有懶加載表(_la_symbol_ptr)和非懶加載表(_nl_symbol_ptr)的_data段,在表中存放着與外部綁定的函數指針,在懶加載端有offset地址,以下:

lazy_symbol.png

  • 這裏都是咱們熟悉的名稱,這些函數的使用是須要進行懶加載綁定的
  • 提供了相對於MachO起始地址的偏移量offset,實際地址便是系統函數所在的內存地址

這裏觀察NSLog函數,記住offset=0x4030這個偏移量。

二、在打印函數調用前下斷點,執行image list獲取模塊列表,以下:

image.png

  • 這裏的image就是一個個模塊,一個個MachO文件

找到第一個模塊地址:0x00000001081f4000,該地址爲應用程序的起始地址,在程序運行期間是固定,從新啓動該地址則會隨機變化,即上面所提到的 ASLR 技術。

三、起始地址與偏移量相加:0x00000001081f4000 + 4030 = 0x1081F8030(這裏使用mac計算器的編程器),即是符號表的地址,記住這個值。

讀內存

一、讀取符號表地址

memory read 0x1081F8030
複製代碼

offset.png

取第一行,從右向左取8位數據:0x01081f6984,改地址便是系統函數NSLog地址,使用命令:

dis -s 0x01081f6984
複製代碼

打印彙編,查看綁定狀況。打印以下:

dis.png

因爲是查看懶加載表的偏移量,此時函數尚未調用,並無綁定。

二、斷點到rebind_symbols(rebs, 1)處,運行再打印符號表地址:

memory read 0x1081F8030
複製代碼

打印以下:

0x1081f8030: be e1 58 08 01 00 00 00 5d a5 57 08 01 00 00 00  ..X.....].W.....
0x1081f8040: 16 6b 29 0c 01 00 00 00 ca 69 1f 08 01 00 00 00  .k)......i......
複製代碼

同上從右向左取8位,再來查看彙編內容:

dis -s 0x010858e1be
複製代碼

打印以下:

func.png

符號表有內容了,說明NSLog爲懶加載,運行後,將Foundation中的NSLog加入到符號表中。所以只要該函數被加載過,就能夠找到綁定的符號地址。

三、繼續執行,使用fishhook進行hook,再查看符號表地址內容:

memory read 0x1081F8030
複製代碼

打印以下:

hook_func.png

因爲fishhook對系統NSLog函數的替換,以上地址發生了變化,取地址查看內容:

dis -s 0x01081f5c60
複製代碼

打印以下:

replace.png

此時發現符號表綁定了fishhookDemomy_NSLog,說明此處的符號綁定被替換了。

經過以上實驗可以知道,dyld會對系統函數進行符號綁定,符號表在數據段,可讀可寫,dyld能夠綁定,fishhook也能夠經過系統函數進行綁定。

經過符號表查找系統函數

回到MachO文件,來看看Lazy Symbol、Dynamic Symbol Table、Symbol Table、String Table表關係:

一、Lazy SymbolDynamic Symbol Tabel一一對應(在數組的下標一致)這兩個表包含了全部與動態庫相關的符號;

二、Dynamic Symbol TabelSymbol Table關聯,Dynamic Symbol Table中的Data字段是Symbol Table數組的下標;

三、Symbol Table中的data字段地址 + String Table表的起始地址,就是目標函數對應字符的位置。

以NSLog爲例驗證

lazy.png

indirect.png

  • 上面兩張圖的下標與value一一對應

找到indirect Symbols表中的_NSLog項:

indirect_data.png

NSLog函數爲例,找到Data地址0x94,等於十進制148,即爲Symbol Table表中NSLog對應的下標,以下:

symbol.png

經過下標找到了對應的符號,主要上面的標註0xBA爲String Table中的偏移量,表的起始地址加上偏移量便是函數名所在的位置。

string_table.png

經過首地址獲取最終函數名,0xBA + 0x6180 = 0x623A,找到0x623A地址處,以下:

string.png

最終找到了系統的函數。

小結:

OC的運行時替換,和fishhook對系統函數的替換,都是因爲有中間者的存在,纔有hook的機會。

6、fishhook源碼分析

……

相關文章
相關標籤/搜索