iOS逆向(6)-從fishhook看runtime十厢,hook系統(tǒng)C函數(shù)

轉(zhuǎn)載:原文地址

在上篇文章不知MachO怎敢說(shuō)自己懂DYLD中已經(jīng)詳細(xì)介紹了MachO避乏,并且由MachO引出了dyld贯卦,再由dyld講述了App的啟動(dòng)流程资柔,而在App的啟動(dòng)流程中又說(shuō)到了一些關(guān)鍵的名稱如:LC_LOAD_DYLINKERLC_LOAD_DYLIB以及objc的回調(diào)函數(shù)_dyld_objc_notify_register等等撵割。并且在末尾提出了MachO中還有一些符號(hào)表贿堰,而有哪些符號(hào)表,這些符號(hào)表又有些什么用呢啡彬?筆者在這篇文章就將一一道來(lái)羹与。

老規(guī)矩,片頭先上福利:點(diǎn)擊下載demo庶灿,demo中有筆者給fishhook每句代碼加的詳細(xì)注釋W莞椤!往踢!
這篇文章會(huì)用到的工具有:

在開(kāi)始正文之前腾誉,假設(shè)面試官問(wèn)了一個(gè)問(wèn)題:
都知道Objective-C最大的特性就是runtime,大家可以用使用runtime對(duì)OC的方法進(jìn)行hook菲语,那么C函數(shù)能不能hook?

有興趣回答的朋友可以先行在評(píng)論區(qū)回答惑灵,答完之后再繼續(xù)閱讀或者預(yù)先偷窺一下文末的答案山上,看看這被炒了無(wú)數(shù)次冷飯的runtime自己是否真的了然于胸。

本將從以下幾方面回答上面所提的問(wèn)題:

  • Runtime的Hook原理
  • 為什么C不能hook
  • 如何利用MachO“玩壞”系統(tǒng)C函數(shù)
  • fishhook源碼分析
  • 綁定系統(tǒng)C函數(shù)過(guò)程驗(yàn)證

一英支、Runtime的Hook原理

Runtime佩憾,從名稱上就知道是運(yùn)行時(shí),也是它造就了OC運(yùn)行時(shí)的特性,而要想徹底明白什么是運(yùn)行時(shí)妄帘,那么就需要將之與C語(yǔ)言有相比較楞黄。
今天咱們就從匯編的角度看一看OC和C在調(diào)用方法(函數(shù))上有什么區(qū)別。

注:筆者使用的是iPhone 7征集調(diào)試抡驼,所有一下匯編都是基于arm64鬼廓,所以以下所有匯編默認(rèn)為基于arm64。

新建一個(gè)工程取名為:FishhookDemo
敲入兩個(gè)OC方法mylogmylog2致盟,掛上斷點(diǎn)碎税,如圖:

OC方法

開(kāi)啟匯編斷點(diǎn),如圖:

設(shè)置匯編斷點(diǎn)

運(yùn)行工程馏锡,會(huì)跳轉(zhuǎn)到如下圖的匯編斷點(diǎn):

OC匯編斷點(diǎn)

從上圖可以看的出來(lái)調(diào)用了兩個(gè)objc_msgSend雷蹂,這兩個(gè)很像是
我們的mylogmylog2,但現(xiàn)在還不能確定杯道。
想一想objc_msgSend的定義:

OBJC_EXPORT void
objc_msgSend(void /* id self, SEL op, ... */ )
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);

第一個(gè)參數(shù)是self匪煌,第二個(gè)參數(shù)是SEL,所以可以知道SEL是放在x1的寄存器里面(什么是x1党巾?繼續(xù)關(guān)注作者萎庭,之后的文章會(huì)有相關(guān)的匯編的專門篇章)。

馬不停蹄昧港,掛上兩個(gè)匯編斷點(diǎn)擎椰,查看一下兩個(gè)x1中存放的到底是什么,如圖:

mylog1
mylog2

這也就驗(yàn)證了咱們OC方法都是消息轉(zhuǎn)發(fā)(objc_msgSend)创肥。而同一個(gè)C函數(shù)的地址又都是一樣的(筆者這次運(yùn)行的地址就是0x1026ce130) 达舒。

所以在每次調(diào)用OC方法的時(shí)候就讓我們有了一次改變消息轉(zhuǎn)發(fā)「目標(biāo)」的機(jī)會(huì)。

這里稍微提一下runtime的源碼分析流程:
Step 1叹侄、方法查找
① 匯編快速查找緩存
② C/C++慢速查找:self->super->NSObject->找到換緩存起來(lái)
Step 2巩搏、動(dòng)態(tài)方法解析: _class_resolveMethod
_class_resolveInstanceMethod
_class_resolveClassMethod
Step 3、消息轉(zhuǎn)發(fā)
_forwardingTargetForSelector
_methodSignatureForSelector
_forwardInvocation
_doesNotRecognizeSelector

二趾代、為什么C不能hook

同樣我們從匯編的角度切入贯底。
敲入代碼一些C函數(shù),掛上斷點(diǎn)撒强,如圖:

C函數(shù)

運(yùn)行工程:
會(huì)看到斷點(diǎn)斷到如下匯編:

匯編斷點(diǎn)

可以看到每個(gè)NSLog對(duì)應(yīng)跳轉(zhuǎn)的地址都是0x10000a010禽捆,每個(gè)printf對(duì)應(yīng)跳轉(zhuǎn)的地址都是0x10000a184,也就是說(shuō)每個(gè)C的函數(shù)都是一一對(duì)應(yīng)著一個(gè)真實(shí)的地址空間飘哨。每次在調(diào)用一個(gè)C函數(shù)的時(shí)候都是執(zhí)行一句匯編bl 0xXXXXXXXX胚想。

所以上面講述到的消息轉(zhuǎn)發(fā)的機(jī)會(huì)沒(méi)有了,也就是沒(méi)有了利用runtime來(lái)Hook的機(jī)會(huì)了芽隆。

三浊服、如何利用MachO“玩壞”系統(tǒng)C函數(shù)

既然如此统屈,那么是否C函數(shù)就真的那么牢不可破,無(wú)法對(duì)他進(jìn)行Hook呢牙躺?
答案肯定是否定的愁憔!
想要從根上理解這個(gè)問(wèn)題,首先要了解:我們的C函數(shù)分為系統(tǒng)C函數(shù)和我們自定義的C函數(shù)孽拷。

1吨掌、自定義的C函數(shù)

在上面的步驟中我們已經(jīng)了解到所有C函數(shù)的調(diào)用都是跳轉(zhuǎn)到一個(gè)「固定的地址」,那么就可以推斷得出這個(gè)「固定的地址」其實(shí)是在編譯期已經(jīng)被生成好了乓搬,所以才能快速思犁、直接的跳轉(zhuǎn)到這個(gè)地址,實(shí)現(xiàn)函數(shù)調(diào)用进肯。
C語(yǔ)言被稱之為是靜態(tài)語(yǔ)言也就是這么個(gè)理激蹲。

2、系統(tǒng)的C函數(shù)

在上篇文章不知MachO怎敢說(shuō)自己懂DYLD已經(jīng)提到了在dyld啟動(dòng)app的第二個(gè)步驟就是加載共享緩存庫(kù)江掩,共享緩存庫(kù)包括Foundation框架学辱,NSLog是被包含在Foundation框架的。那么就可以確定一件事情环形,在我們將自己工程打包出的MachO文件中是不可能預(yù)先確定NSLog的地址的策泣。

但是又因?yàn)镃語(yǔ)言是靜態(tài)的特性,沒(méi)法在運(yùn)行的時(shí)候?qū)崟r(shí)獲取共享緩存庫(kù)中NSLog的地址抬吟。而共享緩存庫(kù)的存在好處太大萨咕,既能節(jié)省大量?jī)?nèi)存,又能加快啟動(dòng)速度提升性能火本,不能棄之而不用危队。

為了解決這個(gè)問(wèn)題,Apple使用了PIC(Position-independent code)技術(shù)钙畔,在第一次使用對(duì)應(yīng)函數(shù)(NSLog)的時(shí)候茫陆,從系統(tǒng)內(nèi)存中將對(duì)函數(shù)(NSLog)的內(nèi)存地址取出,綁定到APP中對(duì)應(yīng)函數(shù)(NSLog)上擎析,就可以實(shí)現(xiàn)正常的C函數(shù)(NSLog)調(diào)用了簿盅。

既然有這么個(gè)過(guò)程,iOS系統(tǒng)可以動(dòng)態(tài)的綁定系統(tǒng)C函數(shù)的地址揍魂,那么咱們就也能桨醋。

四、fishhook源碼分析

1现斋、fishhook的總體思路

Facebook的開(kāi)源庫(kù)fishhook就可以完美的實(shí)現(xiàn)這個(gè)任務(wù)喜最。
先上一張官網(wǎng)原理圖:

fishhook原理圖

總體來(lái)說(shuō),步驟是這樣的:

  • 先找到四張表Lazy Symbol Pointer Table步责、Indirect Symbol Table返顺、Symbol Table、String Table蔓肯。
  • MachO有個(gè)規(guī)律:Lazy Symbol Pointer Table中第index行代表的函數(shù)和Indirect Symbol Table中第index行代表的函數(shù)是一樣的遂鹊。
  • Indirect Symbol Table中value值表示Symbol Table的index。
  • 找到Symbol Table的中對(duì)應(yīng)index的對(duì)象蔗包,其data代表String Table的偏移值秉扑。
  • 用String Table的基值,也就是第一行的pFile值调限,加上Symbol Table的中取到的偏移值舟陆,就能得到Indirect Symbol Table中value(這個(gè)value代表函數(shù)的偏移值)代表的函數(shù)名了。

2耻矮、驗(yàn)證NSLog地址

下面就來(lái)驗(yàn)證一下在NSLog的地址是不是真的就存在Indirect Symbol Table中秦躯。
同樣在NSLog處下好斷點(diǎn),打開(kāi)匯編斷點(diǎn)裆装,運(yùn)行代碼踱承。會(huì)發(fā)現(xiàn)斷點(diǎn)斷在如下入位置:

NSLog斷點(diǎn)

注:筆者的工程重新build了,MachO也重新生成哨免,所以此處的截圖和上文中斷住NSLog的截圖的地址不一樣茎活,這是正常情況。

可以發(fā)現(xiàn)NSLog的地址是0x104d36010琢唾,先記住這個(gè)值载荔。

然后查看我們APP在內(nèi)存中的偏移值。
利用image list命令列出所有image采桃,第一個(gè)image就是我們APP的偏移值懒熙,也就是內(nèi)存地址。

APP在內(nèi)存中的偏移值

可以看到APP在內(nèi)存中的偏移值為0x104d30000芍碧。
接著打開(kāi)MachOView查看MachO中的Indirect Symbol Table中的value煌珊,如圖:

函數(shù)偏移地址

其值為0x100006010,去除最高位得到的0x6010就是NSLog在MachO中的偏移值泌豆。
最后將NSLog在MachO中的偏移值于APP在內(nèi)存中的偏移值相加就得到NSLog真實(shí)的內(nèi)存地址:
0x6010+0x104d30000=0x104d36010

最終證明定庵,在Indirect Symbol Table的value中的值就是其對(duì)應(yīng)的函數(shù)的地址!W傥!蔬浙!

3、根據(jù)MachO的表查找對(duì)應(yīng)的函數(shù)名和函數(shù)地址

咱們還是用NSLog來(lái)距離查找贞远。

1畴博、Indirect Symbol Table

取出其data值0000010A,用10進(jìn)制表示蓝仲,結(jié)果為266俱病,如圖:

Indirect Symbols Table
2官疲、Symbol Table

在Symbol Table中找到下標(biāo)(offset)為266的的對(duì)象,取出其data0x124亮隙,如圖:

Symbols Table
3途凫、String Table

將在Symbols中得到的偏移值0x124加上String Table的首個(gè)地址DC6C,得到值DD90溢吻,然后找到pFile為DD90的值维费,如下兩圖:

String Table 1
String Table 2

上述就是根據(jù)MachO的表查找對(duì)應(yīng)的函數(shù)名和函數(shù)地址全過(guò)程了。

4促王、源碼分析

fishhook的源碼總共只有250行左右犀盟,所以結(jié)合MachO慢慢看,其實(shí)一點(diǎn)也不費(fèi)勁蝇狼,在筆者的demo中有對(duì)其每一句函數(shù)的詳細(xì)注釋阅畴。當(dāng)然也有對(duì)fishhook使用的demo。

所以筆者就不在此處對(duì)fishhook做太過(guò)詳細(xì)的介紹了迅耘。只對(duì)其中一些關(guān)鍵參數(shù)和關(guān)鍵函數(shù)做介紹恶阴。

  • fishhook為維護(hù)一個(gè)鏈表,用來(lái)儲(chǔ)存需要hook的所有函數(shù)
// 給需要rebinding的方法結(jié)構(gòu)體開(kāi)辟出對(duì)應(yīng)的空間
// 生成對(duì)應(yīng)的鏈表結(jié)構(gòu)(rebindings_entry)豹障,并將新的entry插入頭部
static int prepend_rebindings(struct rebindings_entry **rebindings_head,
                              struct rebinding rebindings[],
                              size_t nel)

  • 根據(jù)linkedit的基值冯事,找到對(duì)應(yīng)的三張表:symbol_table、string_table和indirect_symtab :
// 找到linkedit的頭地址
uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff;
// 獲取symbol_table的真實(shí)地址
nlist_t *symtab = (nlist_t *)(linkedit_base + symtab_cmd->symoff);
// 獲取string_table的真實(shí)地址
char *strtab = (char *)(linkedit_base + symtab_cmd->stroff);
// Get indirect symbol table (array of uint32_t indices into symbol table)
// 獲取indirect_symtab的真實(shí)地址
uint32_t *indirect_symtab = (uint32_t *)(linkedit_base + dysymtab_cmd->indirectsymoff);

  • 最核心的一個(gè)步驟血公,查找并且替換目標(biāo)函數(shù):
// 在四張表(section,symtab,strtab,indirect_symtab)中循環(huán)查找
// 直到找到對(duì)應(yīng)的rebindings->name,將原先的函數(shù)復(fù)制給新的地址昵仅,將新的函數(shù)地址賦值給原先的函數(shù)
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)

五、綁定系統(tǒng)C函數(shù)過(guò)程驗(yàn)證

上面說(shuō)了這么多累魔,那么咱們來(lái)驗(yàn)證一下系統(tǒng)C函數(shù)是不是真的會(huì)這樣被綁定起來(lái)摔笤,并且看一看,是在什么時(shí)候綁定的垦写。

同樣吕世,在第一次敲入NSLog函數(shù)的地方加上斷點(diǎn),在第二個(gè)NSLog處也加上斷點(diǎn):

兩個(gè)NSLog斷點(diǎn)

運(yùn)行工程后梯投,使用dis -s命令查看該函數(shù)的匯編代碼命辖,并且繼續(xù)查看其中第一次b指令,也就是函數(shù)調(diào)用的匯編分蓖,如圖:

第一次NSLog匯編斷點(diǎn)+dis -s

從上圖就可以看到尔艇,在我們第一次調(diào)用NSLog的時(shí)候,系統(tǒng)確實(shí)會(huì)默認(rèn)的調(diào)用dyld_stub_binder函數(shù)對(duì)NSLog進(jìn)行綁定么鹤。

繼續(xù)跳過(guò)這個(gè)斷點(diǎn)终娃,進(jìn)入下一個(gè)NSLog的匯編斷點(diǎn)處,同樣利用dis -s命令查看該匯編:

第二次NSLog匯編斷點(diǎn)+dis -s

得到答案:
系統(tǒng)確實(shí)會(huì)在第一次調(diào)用系統(tǒng)C函數(shù)的時(shí)候?qū)ζ溥M(jìn)行綁定蒸甜!

還記得正文開(kāi)始的時(shí)候的那個(gè)問(wèn)題嗎棠耕?
那么是不是系統(tǒng)C函數(shù)可以hook余佛,而自定義的C函數(shù)就絕對(duì)不能hook了呢?
很顯然窍荧,國(guó)內(nèi)外大神那么多衙熔,肯定是能做到的,有興趣的讀者可以自行查閱Cydia Substrate搅荞。

這篇文章利用了一些LLDB命令行看了許多我們想看的內(nèi)容,如image list框咙,register read還有dis -s咕痛,在我們正向開(kāi)發(fā)中,LLDB就是一把利器喇嘱,而在我們玩逆向的時(shí)候茉贡,LLDB就成為了我們某些是后的唯一途徑了!所以者铜,在下一篇文章中腔丧,筆者將會(huì)對(duì)LLDB進(jìn)行更加詳細(xì)的講解,讓大家看到LLBD的偉大作烟。

轉(zhuǎn)載:原文地址
經(jīng)原作者同意:想交流iOS開(kāi)發(fā)愉粤,逆向等技術(shù),可加iOS技術(shù)交流群:624212887拿撩,進(jìn)行交流衣厘!


推薦文集

* 抖音效果實(shí)現(xiàn)

* BAT—最新iOS面試題總結(jié)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市压恒,隨后出現(xiàn)的幾起案子影暴,更是在濱河造成了極大的恐慌,老刑警劉巖探赫,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件型宙,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡伦吠,警方通過(guò)查閱死者的電腦和手機(jī)妆兑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)毛仪,“玉大人箭跳,你說(shuō)我怎么就攤上這事√肚В” “怎么了谱姓?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)刨晴。 經(jīng)常有香客問(wèn)我屉来,道長(zhǎng)路翻,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任茄靠,我火速辦了婚禮茂契,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘慨绳。我一直安慰自己掉冶,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布脐雪。 她就那樣靜靜地躺著厌小,像睡著了一般。 火紅的嫁衣襯著肌膚如雪战秋。 梳的紋絲不亂的頭發(fā)上璧亚,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音脂信,去河邊找鬼癣蟋。 笑死,一個(gè)胖子當(dāng)著我的面吹牛狰闪,可吹牛的內(nèi)容都是我干的疯搅。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼埋泵,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼秉撇!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起秋泄,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤琐馆,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后恒序,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體瘦麸,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年歧胁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了滋饲。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡喊巍,死狀恐怖屠缭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情崭参,我是刑警寧澤呵曹,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響奄喂,放射性物質(zhì)發(fā)生泄漏铐殃。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一跨新、第九天 我趴在偏房一處隱蔽的房頂上張望富腊。 院中可真熱鬧,春花似錦域帐、人聲如沸赘被。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)民假。三九已至,卻和暖如春许饿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背舵盈。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工陋率, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人秽晚。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓瓦糟,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親赴蝇。 傳聞我的和親對(duì)象是個(gè)殘疾皇子菩浙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容