Runtime —— Method Swizzle

0x01.runtime和Method Swizzle

runtime是Objective C 語(yǔ)言的特殊機(jī)制颜曾。總的來(lái)說(shuō)铐刘,OC通過(guò)runtime聯(lián)系上c和c++夜只,其底層由c語(yǔ)言編寫

根據(jù)OC語(yǔ)言的特性,有很多類和成員變量在編譯時(shí)是系統(tǒng)不知道的闸天,而在運(yùn)行時(shí)暖呕,我們所編寫的代碼才會(huì)轉(zhuǎn)換成完整的確定的代碼去運(yùn)行(運(yùn)行時(shí)鏈接)。因此苞氮,僅僅只有一套編譯器是不夠的湾揽,還需要一套運(yùn)行時(shí)系統(tǒng)(Runtime System)來(lái)處理編譯后的工作,runtime就是這樣一個(gè)系統(tǒng)笼吟。根據(jù)這個(gè)原理钝腺,在運(yùn)行時(shí)交換原有方法和我們自己編寫的新方法的imp指針,可以達(dá)到hook函數(shù)的目的赞厕,是為Method Swizzle。


0x02.簡(jiǎn)單實(shí)現(xiàn)

下面簡(jiǎn)單實(shí)現(xiàn)一個(gè)普普通通的demo


一個(gè)button按鈕定硝,會(huì)調(diào)用很多方法

這些調(diào)用的方法都是簡(jiǎn)單的輸出log皿桑,反正是拿來(lái)湊數(shù)的代碼。不重要

編寫一下Method Swizzle的代碼蔬啡。


代碼

這里把trycheck6函數(shù)和自己編寫的gogo函數(shù)做了交換诲侮,關(guān)鍵的交換函數(shù)就是method_exchangeImplementations函數(shù),用于方法指針的交換箱蟆。

運(yùn)行效果就不看了沟绪,就是該執(zhí)行trycheck6時(shí)打印了gogogogo

這樣方法交換就完成了。相當(dāng)于實(shí)現(xiàn)了對(duì)old函數(shù)也就是trycheck6這個(gè)函數(shù)的hook空猜,改變了它的功能和邏輯绽慈。但其實(shí)它的名字和外皮并沒有改變。


0x03.觀察

以runtime的特性辈毯,我們?cè)谒\(yùn)行的時(shí)候獲取一個(gè)類下所有方法的imp指針坝疼。達(dá)到遍歷所有方法的目的。

之前沒接觸過(guò)Objective C谆沃,就隨便寫寫钝凶。代碼如下:

+(NSArray*)getAllmethods

{

? ? unsigned?int?methodCount =0;

? ? const?char* lastname;

? ? Method* methodList =class_copyMethodList([self?class], &methodCount);

? ? NSMutableArray*methodsArray = [NSMutableArrayarrayWithCapacity:methodCount];

? ? Methodtemp = methodList[i];

? ? IMP imp = method_getImplementation(temp);

? ? const ?char* name_s =sel_getName(method_getName(temp));

? ? //SEL name_f = method_getName(temp);

? ? lastname = name_s;

? ? //int arguments = method_getNumberOfArguments(temp);

? ? //const char* encoding = method_getTypeEncoding(temp);

? ? //NSLog(@"方法名:%@,參數(shù)個(gè)數(shù):%d,編碼方式:%@",[NSString stringWithUTF8String:name_s],arguments,[NSString stringWithUTF8String:encoding]);

? ? ? ? [methods ArrayaddObject:[NSString stringWithUTF8String:name_s]];

? ? }

? ? free(methodList);

? ? return?methodsArray;

}

隨便復(fù)制一手,最后返回的函數(shù)列表


返回的函數(shù)列表

這只是某一個(gè)類中所獲取到的函數(shù)唁影。要獲取所有的函數(shù)需要遍歷類耕陷。

可以看到掂名,就算是方法交換之后返回的函數(shù)列表,trycheck6也還是trycheck6哟沫。他的名字并沒有變成gogo饺蔑。可見這還是有點(diǎn)欺騙性的南用。


0x04.天馬行空的想象

我們?nèi)绾稳z測(cè)一個(gè)函數(shù)是否被hook呢膀钠?

其實(shí)在調(diào)試中是可以看見不一樣的


imp指針的蛛絲馬跡

這里imp指針的值可以看見,就算我把我的new方法改成了trycheck6同名裹虫,前面也會(huì)帶上MSHook肿嘲。人工一眼就能看見是被hook了。

那從antihook的角度來(lái)講筑公,是不是獲取imp里面的值做比較就行了呢雳窟?有兩個(gè)問題:

第一,imp是指針匣屡,指向的是函數(shù)的地址封救。Xcode只是便于讓開發(fā)者具體的看才轉(zhuǎn)化成了這個(gè)樣子,其實(shí)imp的值只是一串十六進(jìn)制數(shù)而已

第二捣作,就算第一條能實(shí)現(xiàn)誉结。要檢測(cè)同名的替換函數(shù),勢(shì)必要檢測(cè)類名券躁,一個(gè)真正的工程有很多類名惩坑,要遍歷類還要遍歷每個(gè)類下的函數(shù),太耗時(shí)了也拜。

綜上直接去比較imp指針是行不通的以舒。但是要檢測(cè)一個(gè)函數(shù)是否被hook也肯定離不開imp指針的。

我們來(lái)看一下慢哈,old和new在內(nèi)存上的位置


上面的是old蔓钟,下面的是new

用于方法交換的new的地址是比old的地址高的。這里只是我的demo的情況卵贱。在真正的項(xiàng)目中滥沫,注入動(dòng)態(tài)庫(kù)來(lái)進(jìn)行方法交換的話,動(dòng)態(tài)庫(kù)會(huì)被注入到Load_command段末尾而不是像我一樣直接寫在項(xiàng)目中键俱,可能會(huì)有old和new地址偏移量大的情況佣谐,這個(gè)留待后面驗(yàn)證。

基于這個(gè)思路方妖,既然是交換了指向方法的imp指針狭魂,imp指針的值肯定有變化。同一個(gè)類中函數(shù)的地址是由低到高線性變化的。


這里

從沒實(shí)現(xiàn)Method Swizzle時(shí)上面打印的log可以發(fā)現(xiàn)雌澄,同一個(gè)類中函數(shù)的地址是由低到高線性變化的斋泄,而且若函數(shù)體一樣大偏移也是一樣的。

那么我們實(shí)現(xiàn)了Method Swizzle之后呢镐牺,我們可以看看


出現(xiàn)了

很驚訝炫掐,trycheck7的地址要比trycheck6的地址低,所以偏移出現(xiàn)了負(fù)數(shù)睬涧。這很好理解募胃,因?yàn)閚ew的地址比old的地址高,交換過(guò)來(lái)trycheck6的地址也變高了畦浓。

那么這樣可以不可以作為一個(gè)判斷trycheck6被hook的依據(jù)呢痹束?我覺得是可以的,但是我自己寫的demo只有幾十kb大小讶请,函數(shù)調(diào)用也并不夠復(fù)雜祷嘶。

我弄了幾個(gè)更大的app工程,發(fā)現(xiàn)machO文件越大夺溢,new和old的距離是越大的论巍,單個(gè)函數(shù)體越復(fù)雜越大,函數(shù)直接的偏移也是越大的风响,在實(shí)際工程中應(yīng)該可以取一個(gè)閥值嘉汰,當(dāng)兩個(gè)在內(nèi)存中線性的函數(shù)偏移量絕對(duì)值大于了這個(gè)閾值,那么一般是可以認(rèn)定為有問題的状勤≈O郑或者對(duì)于小的工程,也可以按比例取閾值荧降,這個(gè)就有點(diǎn)天馬行空的想象了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末攒读,一起剝皮案震驚了整個(gè)濱河市朵诫,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌薄扁,老刑警劉巖剪返,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異邓梅,居然都是意外死亡脱盲,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門日缨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)钱反,“玉大人,你說(shuō)我怎么就攤上這事∶娓纾” “怎么了哎壳?”我有些...
    開封第一講書人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)尚卫。 經(jīng)常有香客問我归榕,道長(zhǎng),這世上最難降的妖魔是什么吱涉? 我笑而不...
    開封第一講書人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任刹泄,我火速辦了婚禮,結(jié)果婚禮上怎爵,老公的妹妹穿的比我還像新娘特石。我一直安慰自己,他們只是感情好疙咸,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開白布县匠。 她就那樣靜靜地躺著,像睡著了一般撒轮。 火紅的嫁衣襯著肌膚如雪乞旦。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,730評(píng)論 1 289
  • 那天题山,我揣著相機(jī)與錄音兰粉,去河邊找鬼。 笑死顶瞳,一個(gè)胖子當(dāng)著我的面吹牛玖姑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播慨菱,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼焰络,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了符喝?” 一聲冷哼從身側(cè)響起闪彼,我...
    開封第一講書人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎协饲,沒想到半個(gè)月后畏腕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡茉稠,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年描馅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片而线。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡铭污,死狀恐怖恋日,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情况凉,我是刑警寧澤谚鄙,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站刁绒,受9級(jí)特大地震影響闷营,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜知市,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一傻盟、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧嫂丙,春花似錦娘赴、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至隅肥,卻和暖如春竿奏,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背腥放。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工泛啸, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人秃症。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓候址,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親种柑。 傳聞我的和親對(duì)象是個(gè)殘疾皇子岗仑,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348