學習hook
,不是要攻擊別人,破壞別人的應用場景,而是爲了更好的防禦,讓本身的應用更堅固更安全。git
在 《動態庫注入》中使用了
yololib
對自定義動態庫在WX
應用中插入,既然能插入自定義庫,咱們就利用hook
技術作了小小的改動,替換了登陸按鈕的方法,並攔截了微信步數上傳的方法,修改了步數。hook
是改變程序執行流程的技術,可以修改其餘應用,也能對本身應用作防禦。github
一般替換方法有以下:編程
method_exchange
交換sel
的imp
指針;getImp
獲取原有函數指針,經過setImp
設置原sel
指向的指針爲自定義函數指針;getImp
獲取原有函數指針,經過replace
替換原sel
指向的指針爲自定義函數指針,通setImp
同樣。本身項目中使用:數組
一般在本身項目中使用,創建分類,在分類
load
中來替換方法,以達到監聽方法調用的目的。緩存
hook
視圖的出現消失:跟蹤頁面位置;hook
析構函數dealloc
(deinit
):跟蹤對象釋放情況;hook
消息轉發方法(forword):避免調用未實現方法而產生奔潰;hook
數組objectAtIndexedSubscript
方法:避免數組越界奔潰; ……安全只要是
OC
的方法均可以根據業務需求進行監聽修改或替換。bash
在其餘應用中使用:微信
經過動態庫注入的方式,來插入本身的
load
,讓本身的代碼能和其餘項目一塊兒執行,一樣使用咱們常規的替換方法便可。經過查看視圖頁面屬性肯定要hook
的目標對象。函數
hook
微信步數獲取方法:實現微信步數修改;hook
微信搶紅包相關方法:實現自動搶紅包功能;hook
微信消息撤回方法:實現拒絕消息撤回功能; (ps:學習中,實戰很少,有方法就有更多的可能)工具在其餘應用中是經過動態庫來進行
hook
的,所以在目標對象所在類的方法列表中並無要替換的方法,迴歸原有方法調用會出現崩潰,這裏可使用獲取原有方法的函數地址,直接調用函數便可實現原有方法的調用。
以上是針對OC
的動態特性來hook
的,咱們可以hook
動態語言下的函數,那麼能不能直接hook
系統的靜態函數呢,下面來看一下fishhook
是如何hook
的。
fishhook 是FaceBook
的開源庫,可以動態的修改MachO
的符號表,來hook
系統的C
函數。 C
函數是在編譯時來肯定函數地址在內存中的偏移量,編譯後該偏移量是肯定的,爲何說是偏移量是肯定的呢?
在不少系統中都採用了 ASLR 技術,對堆、棧、共享庫等線性區佈局進行隨機化,來避免攻擊者直接獲取攻擊代碼位置。Mach0
的首地址是隨機變化的,找到首地址就能找到對應的函數地址,即 Offset + Mach0
。
靜態語言:編譯時肯定函數地址
動態語言:運行時肯定函數地址
使用步驟:
一、直接將.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
中可以交換方法是由於有sel
和imp
的鏈接過程,函數與用戶之間有一箇中間者,那麼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
複製代碼
打印仍是原來的方法,這裏並無替換。這裏能夠猜測自定義函數調用沒有產生中間者,這裏是直接調用的,因此沒法交換。
fishhook
主要利用了共享緩存功能和PIC
技術來實現hook
功能。
動態共享緩存
iOS
系統爲節省內存資源,將系統的動態庫資源統一放在了系統的共享區域,該區域其餘應用均可以訪問。
PIC技術
PIC
技術叫位置代碼獨立,在MachO
文件中會預留出一段空間,這一段空間叫作符號表,在MachO
文件的數據段中。dyld
在加載MachO
文件到內存中後,會將共享區的系統函數地址綁定到對應的符號上,並插入到符號表中。這樣在項目中調用相關係統函數時,實際上調用的是對應的符號,經過符號來找到具體的系統函數地址。
到這裏思路應該清晰不少,fishhook
實現以下:
- 根據符號(字符)獲取系統函數地址;
- 替換符號指向的地址爲用戶聲明的函數地址(符號綁定);
- 對外部聲明的指針進行系統函數地址賦值。
上面所提到的自定義函數沒法hook
也就瞭然了,自定義函數地址肯定在代碼段中,缺乏dyld
的符號綁定階段,而且應用中調用的是最原始的函數地址,所以沒法交換。
data段:程序運行期間可讀可寫 代碼段:程序運行期間只讀
這裏使用 MachOView 工具,對以上給出的代碼生成的MachO
文件進行分析。
*獲取符號表地址
一、machO
符號表中有懶加載表(_la_symbol_ptr
)和非懶加載表(_nl_symbol_ptr
)的_data
段,在表中存放着與外部綁定的函數指針,在懶加載端有offset
地址,以下:
MachO
起始地址的偏移量offset
,實際地址便是系統函數所在的內存地址這裏觀察NSLog
函數,記住offset=0x4030
這個偏移量。
二、在打印函數調用前下斷點,執行image list
獲取模塊列表,以下:
image
就是一個個模塊,一個個MachO
文件找到第一個模塊地址:0x00000001081f4000
,該地址爲應用程序的起始地址,在程序運行期間是固定,從新啓動該地址則會隨機變化,即上面所提到的 ASLR 技術。
三、起始地址與偏移量相加:0x00000001081f4000 + 4030 = 0x1081F8030
(這裏使用mac
計算器的編程器),即是符號表的地址,記住這個值。
讀內存
一、讀取符號表地址
memory read 0x1081F8030
複製代碼
取第一行,從右向左取8位數據:0x01081f6984
,改地址便是系統函數NSLog
地址,使用命令:
dis -s 0x01081f6984
複製代碼
打印彙編,查看綁定狀況。打印以下:
因爲是查看懶加載表的偏移量,此時函數尚未調用,並無綁定。
二、斷點到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
複製代碼
打印以下:
符號表有內容了,說明NSLog
爲懶加載,運行後,將Foundation
中的NSLog
加入到符號表中。所以只要該函數被加載過,就能夠找到綁定的符號地址。
三、繼續執行,使用fishhook
進行hook,再查看符號表地址內容:
memory read 0x1081F8030
複製代碼
打印以下:
因爲fishhook
對系統NSLog
函數的替換,以上地址發生了變化,取地址查看內容:
dis -s 0x01081f5c60
複製代碼
打印以下:
此時發現符號表綁定了fishhookDemo
的my_NSLog
,說明此處的符號綁定被替換了。
經過以上實驗可以知道,dyld
會對系統函數進行符號綁定,符號表在數據段,可讀可寫,dyld
能夠綁定,fishhook
也能夠經過系統函數進行綁定。
經過符號表查找系統函數
回到MachO文件,來看看Lazy Symbol、Dynamic Symbol Table、Symbol Table、String Table
表關係:
一、Lazy Symbol
和Dynamic Symbol Tabel
一一對應(在數組的下標一致)這兩個表包含了全部與動態庫相關的符號;
二、Dynamic Symbol Tabel
和Symbol Table
關聯,Dynamic Symbol Table
中的Data
字段是Symbol Table
數組的下標;
三、Symbol Table
中的data
字段地址 + String Table
表的起始地址,就是目標函數對應字符的位置。
以NSLog爲例驗證
value
一一對應找到indirect Symbols
表中的_NSLog
項:
以NSLog
函數爲例,找到Data
地址0x94,等於十進制148,即爲Symbol Table
表中NSLog
對應的下標,以下:
經過下標找到了對應的符號,主要上面的標註0xBA爲String Table
中的偏移量,表的起始地址加上偏移量便是函數名所在的位置。
經過首地址獲取最終函數名,0xBA + 0x6180 = 0x623A,找到0x623A地址處,以下:
最終找到了系統的函數。
小結:
OC
的運行時替換,和fishhook
對系統函數的替換,都是因爲有中間者的存在,纔有hook
的機會。
……