在上篇文章MachO文件解析已經詳細介紹了MachO嫉到,并且由MachO引出了dyld沃暗,再由dyld講述了App的啟動流程,而在App的啟動流程中又說到了一些關鍵的名稱如:LC_LOAD_DYLINKER何恶、LC_LOAD_DYLIB以及objc的回調函數_dyld_objc_notify_register等等孽锥。
這篇文章我們來了解一下,符號表细层、fishhook相關的內容惜辑。
用到的工具:fishhook
接下來本文會從以下幾點進行闡述:
- HOOK概述
- fishHook的簡單使用
- fishHook原理探究
- fishHook源碼分析
1.HOOK概述
HOOK,中文譯為“掛鉤”或“鉤子”疫赎。在iOS逆向中是指改變程序運行流程的一種技術盛撑。通過hook可以讓別人的程序執(zhí)行自己所寫的代碼。在逆向中經常使用這種技術虚缎。所以在學習過程中撵彻,我們重點要了解其原理钓株,這樣能夠對惡意代碼進行有效的防護实牡。
1.1 Hook 流程圖
如上圖,這就是我們HOOK代碼大概流程轴合。
1.2 HOOK的方式
1.****Method Swizzld
利用OC的Runtime特性创坞,動態(tài)改變SEL(方法編號)和IMP(方法實現)的對應關系,達到OC方法調用流程改變的目的受葛。主要用于OC方法题涨。
2.fishhook
它是Facebook提供的一個動態(tài)修改鏈接mach-O文件的工具。利用MachO文件加載原理总滩,通過修改懶加載和非懶加載兩個表的指針達到C函數HOOK的目的纲堵。
3.Cydia Substrate
Cydia Substrate 原名為 Mobile Substrate ,它的主要作用是針對OC方法闰渔、C函數以及函數地址進行HOOK操作席函。當然它并不是僅僅針對iOS而設計的,安卓一樣可以用冈涧。
官方地址:http://www.cydiasubstrate.com/
1.3 Cydia Substrate簡介
Cydia Substrate的原名為MobileHooker茂附,顧名思義用于HOOK。它定義一系列的宏和函數督弓,底層調用objc的runtime和fishhook來替換系統(tǒng)或者目標應用的函數.
其中有兩個函數:
MSHookMessageEx:主要作用于Objective-C方法营曼。
void MSHookMessageEx(Class class, SEL selector, IMP replacement, IMP result)
MSHookFunction:主要作用于C和C++函數。
void MSHookFunction(voidfunction,void* replacement,void** p_original)
Logos語法的%hook 就是對此函數做了一層封裝愚隧。
1.4 Cydia Substrate特點
1.MobileLoader:MobileLoader用于加載第三方dylib在運行的應用程序中蒂阱。啟動時MobileLoader會根據規(guī)則把指定目錄的第三方的動態(tài)庫加載進去,第三方的動態(tài)庫也就是我們寫的破解程序.
2.safe mode: 破解程序本質是dylib,寄生在別人進程里录煤。 系統(tǒng)進程一旦出錯虱痕,可能導致整個進程崩潰,崩潰后就會造成iOS癱瘓。所以CydiaSubstrate引入了安全模式,在安全模 式下所有基于CydiaSubstratede 的三方dylib都會被禁用辐赞,便于查錯與修復部翘。
2.fishHook的簡單使用
fishHook代碼地址:fishhook
struct rebinding {
const char *name;//需要HOOK的函數名稱,C字符串
void *replacement;//新函數的地址
void **replaced;//原始函數地址的指針响委!
};
關鍵函數:
FISHHOOK_VISIBILITY
int rebind_symbols(struct rebinding rebindings[],
size_t rebindings_nel);
參數一:存放rebinding結構體的數組(可以同時交換多個函數)
參數二:rebindings數組的長度
FISHHOOK_VISIBILITY
int rebind_symbols_image(void *header,
intptr_t slide,
struct rebinding rebindings[],
size_t rebindings_nel);
hook系統(tǒng)的NSLog函數fishhook簡單使用代碼
運行代碼新思,點擊屏幕,會發(fā)現我們的代碼執(zhí)行了赘风,
hook C 函數fishhook 系統(tǒng)C函數
運行代碼夹囚,我們會發(fā)現,我們的hook方法失效了邀窃,這是為什么么呢荸哟?下面我們來一起探究下吧。瞬捕。鞍历。。
3.fishHook原理探究
在上篇文章已經提到了在dyld啟動app的第二個步驟就是加載共享緩存庫肪虎,共享緩存庫包括Foundation框架劣砍,NSLog是被包含在Foundation框架的。有了這個貢獻緩存庫扇救,就可以解決多個app共同調用一個庫等問題了刑枝。
3.1 fishHook 加載流程
首先,我們想一下OC為什么可以hook呢迅腔?C為什么不能hook装畅?
OC為什么可以hook? 因為OC底層使用的Runtime技術沧烈,通過運行時去找到方法的實現掠兄,所以OC可以hook。
C為什么不能hook掺出?
C語言函數通常是靜態(tài)的匿级,編譯之后锅尘,從匯編代碼變成了內存地址。它都是通過內存地址去找的,在編譯的時候就綁定了的铭段,所以hook不了筛欢。
那么問題來了闯冷,為什么問題來了,為什么fishhook可以hook到C方法呢铐维?
原因是:iOS系統(tǒng)實現了一個動態(tài)緩存庫技術,一些公共的系統(tǒng)庫放進內存中的某個地方慎菲,當某個iOS項目啟動后嫁蛇,machO文件會在Data段創(chuàng)建一個指針,dyld動態(tài)將machO中Data段中這個指針指向外部函數露该,這里的指針指向內部函數的調用睬棚,指向外部函數的地址,而這個指針也就是我們通常說的符號解幼;這也是為什么fishhook中函數名為rebind_symbols(重新綁定符號)抑党,實際上是修改這個指針指向外部函數的地址,這也就是為什么修改不了內部函數和自定義函數撵摆,只能修改machO外部函數(在符號表中能找到的函數)底靠。由于蘋果實現了ASLR技術(不了解ASLR,看這篇逆向學習筆記8——ASLR),所以這些動態(tài)緩存庫函數在APP項目的內存地址不確定特铝,每次啟動APP的時候都會有相應的變化暑中,這是C語言的動態(tài)表現。
通過MacOView分析鲫剿,我們知道了iOS應用啟動時的啟動流程:
- 啟動APP會執(zhí)行dyld鳄逾,加載程序
- 進入dyld:main函數
- 配置一些環(huán)境
- 加載共享緩存庫
- 實例化主程序
- 加載動態(tài)庫
- 鏈接主程序
通過以上分析,我們可以利用dyld加載的時候對C函數進行修改牵素。
簡單總結下就是:
- 在MachO文件中有個PIC(貢獻緩存庫)严衬,它在Data段創(chuàng)建了一個指針,這個指針指向外部函數笆呆。
- 當我們調用C方法、函數的時候粱挡,這個指針就會起加載(綁定)這個外部的C函數赠幕。
- 而我們的fishhook正是利用了這一步驟進行hook的。
注意:
fishhook 修改的是內存的地址
下面询筏,我們使用MachOView來看一下吧
這里我們代碼寫的少榕堰,所以就直接用 2fishhook hook系統(tǒng)C函數這個案例來講解了。
首先嫌套,我們來看看NSLog的地址在什么時候被加載的逆屡,也就是NSLog到底在哪里。
1.使用MachOView查看踱讨,并使用驗證
// 在viewDidLoad中添加以下兩句代碼魏蔗,斷點斷到第一句代碼
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"測試1");
NSLog(@"測試1");
}
1.在第一個NSLog使用斷點斷住,在Project中痹筛,得到當前APP的 MachO文件莺治。使用MachOView進行分析廓鞠。我們可以找到如下內容。
懶加載表 非懶加載表
2.獲得當前NSLog的偏移量谣旁。
3.在LLDB中使用 image list列出當前加載的鏡像床佳。第一個地址為當前MachO文件在內存中的首地址
4.通過首地址加上當前的偏移量,我們可以獲得如下NSLog在內存中的位置榄审。
5.由于iOS是小端模式砌们,我們的地址從右往左讀,如上圖第二個lldb的內容搁进,最后我們會看到libdyld.dylib`dyld_stub_binder:怨绣。上圖中有體現。
6.我們的斷點在往下走一步拷获,會發(fā)現我們獲取MachO文件的首地址內存發(fā)生了變化篮撑。上圖中有體現。
7.這時我們查看一下當前的地址匆瓜,看看有什么赢笨?,如圖驮吱,我們的NSLog被打印出來了茧妒。
到這里,說明了我們的NSLog在我們的系統(tǒng)共享緩存區(qū)里左冬,第一次沒使用的時候需要綁定桐筏,第二次直接去找地址,不用綁定拇砰。
2.使用匯編的方式查看
1.同上面的第1梅忌,2,3步除破,通過Debug -> Debug WorkFlow -> Always Show Disassembly 調試牧氮,如下圖:
我們同樣可以得到 libdyld.dylib`dyld_stub_binder:函數
4.fishHook源碼分析
4.1、fishhook的總體思路
Facebook的開源庫fishhook就可以完美的實現這個任務瑰枫。
先上一張官網原理圖:
總體來說踱葛,步驟是這樣的:
- 先找到四張表Lazy Symbol Pointer Table、Indirect Symbol Table光坝、Symbol Table尸诽、String Table。
- MachO有個規(guī)律:Lazy Symbol Pointer Table中第index行代表的函數和Indirect Symbol Table中第index行代表的函數是一樣的盯另。
- Indirect Symbol Table中value值表示Symbol Table的index性含。
- 找到Symbol Table的中對應index的對象,其data代表String Table的偏移值土铺。
- 用String Table的基值胶滋,也就是第一行的pFile值板鬓,加上Symbol Table的中取到的偏移值,就能得到Indirect Symbol Table中value(這個value代表函數的偏移值)代表的函數名了究恤。
4.2俭令、源碼分析
fishhook的源碼總共只有250行左右,所以結合MachO慢慢看部宿,其實一點也不費勁抄腔,接下來,我們簡單的分析一下吧理张。
1.fishhook為維護一個鏈表赫蛇,用來儲存需要hook的所有函數。
// 給需要rebinding的方法結構體開辟出對應的空間
// 生成對應的鏈表結構(rebindings_entry)雾叭,并將新的entry插入頭部
static int prepend_rebindings(struct rebindings_entry **rebindings_head,
struct rebinding rebindings[],
size_t nel)
2.根據linkedit的基值悟耘,找到對應的三張表:symbol_table、string_table和indirect_symtab织狐。
// 找到linkedit的頭地址
// linkedit_base其實就是MachO的頭地址T萦住!移迫!可以通過查看linkedit_base值和image list命令查看驗證M摇!3瘛(文末附有驗證圖)
/**********************************************************
Linkedit虛擬地址 = PAGEZERO(64位下1G) + FileOffset
MachO地址 = PAGEZERO + ASLR
上面兩個公式是已知的 得到下面這個公式
MachO文件地址 = Linkedit虛擬地址 - FileOffset + ASLR(slide)
**********************************************************/
uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff;
// 獲取symbol_table的真實地址
nlist_t *symtab = (nlist_t *)(linkedit_base + symtab_cmd->symoff);
// 獲取string_table的真實地址
char *strtab = (char *)(linkedit_base + symtab_cmd->stroff);
// Get indirect symbol table (array of uint32_t indices into symbol table)
// 獲取indirect_symtab的真實地址
uint32_t *indirect_symtab = (uint32_t *)(linkedit_base + dysymtab_cmd->indirectsymoff);
3.最核心的一個步驟邪媳,查找并且替換目標函數。
// 在四張表(section,symtab,strtab,indirect_symtab)中循環(huán)查找
// 直到找到對應的rebindings->name,將原先的函數復制給新的地址荡陷,將新的函數地址賦值給原先的函數
static void perform_rebinding_with_section(struct rebindings_entry *rebindings,
section_t *section,
intptr_t slide,
nlist_t *symtab,
char *strtab,
uint32_t *indirect_symtab)
在了解了fishhook的簡單使用之后雨效,我們就可以做一些基本的防護的內容了,參見代碼
關于幾個名詞的解釋亲善。
幾個名詞(pFile 设易、offset 、File Offset)的解釋
- 首先蛹头,這三個都是表示相對于MachO的內存偏移,只不過其含義被細分了戏溺。
- pFile 和 offset含義相近渣蜗,不過offset更詳細,能夠對應上具體某一個符號(DATA? TEXT?)旷祸。比如文件里面有許多類耕拷,類里面有許多的屬性,pFile就代表各個類的偏移值托享,offset代表各個屬性的偏移值骚烧。
- File Offset 這個存在于Segment的字段中浸赫。用于從Segment快速找到其代表的「表」真正的偏移值。
參考文章:
作者:一縷清風揚萬里
原文地址:http://www.reibang.com/p/95896fb96a03