動態(tài)注入 dylib到 Mac 應用

最近看了很多書, 學到了不少新姿勢, 原本想寫出來和大家分享一下, 但是發(fā)現(xiàn)在簡書上都有類似的資料, 而且質量都還可以, 所以就只好藏拙了.

這次突然之間想到搞搞 Mac 應用, 是因為Mac 上某個下載應用讓我很煩惱, 明明網(wǎng)速很好就是因為不是會員把下載速度弄的特別慢, 加上看了糖炒小蝦的 tweakQQ, 所以想去逆向一下, 這篇文章只是講一下前期的準備工作, 也算是對糖炒小蝦文章中一些未涉及的點進行補充.

一. 事先準備:

  1. 一些 Objective-C 的 runtime 知識;
  2. 動態(tài)注入庫 yololib, git地址, 源代碼下載下來之后編譯之, 得到一個可執(zhí)行文件 yolokit(可以自己修改工程名換成別的名字)
  3. 一個 Mac demo 工程, 一個 dylib 工程

二. 大致需求
這邊會寫一個 Mac demo, 里面只有一個簡單的類叫 YoloTester, 它的 -description 方法返回的是@"YoloTest", 然后我在 AppDelegate 上面寫了 NSLog(@"hello, %@!", [YoloTester new]), 最終輸出的應該是@"hello, YoloTest".

我的目標是通過動態(tài)注入的方式, 讓最終輸出的 log 變成@"hello, cracker".

三. 注入嘗試
1). 直接重寫類YoloTester來覆蓋:
1.新建一個 dylib 工程:

dylib_create_1.png

  1. 重寫類:
@interface YoloTester : NSObject

@end

@implementation YoloTester

- (NSString *)description
{
    return @"Cracker";
}

@end

為了方便操作, 我們copy 這個 dylib 和 yololib 執(zhí)行文件到 demo 的 可執(zhí)行文件目錄下. 然后執(zhí)行 ./yololib YoloTest libDylib.dylib, 然后就會看到輸入日志:

2017-03-13 21:40:58.936 yololib[2164:81591] dylib path @executable_path/libDylib.dylib
2017-03-13 21:40:58.937 yololib[2164:81591] dylib path @executable_path/libDylib.dylib
Reading binary: YoloTest

2017-03-13 21:40:58.937 yololib[2164:81591] Thin 64bit binary!
2017-03-13 21:40:58.937 yololib[2164:81591] dylib size wow 56
2017-03-13 21:40:58.937 yololib[2164:81591] mach.ncmds 20
2017-03-13 21:40:58.937 yololib[2164:81591] mach.ncmds 21
2017-03-13 21:40:58.937 yololib[2164:81591] Patching mach_header..
2017-03-13 21:40:58.937 yololib[2164:81591] Attaching dylib..

2017-03-13 21:40:58.937 yololib[2164:81591] size 55
2017-03-13 21:40:58.937 yololib[2164:81591] complete!

這樣的輸出代表我們成功注入到了可執(zhí)行文件中, 然后我們執(zhí)行./YoloTest來驗證一下是否真正替換了我們的方法, 結果輸出下面的日志:

objc[2221]: Class YoloTester is implemented in both /Users/ryan/Library/Developer/Xcode/DerivedData/YoloTest-fiuymriohppdctfwvyeqikbxfzgo/Build/Products/Debug/./libDylib.dylib (0x101291120) and /Users/ryan/Library/Developer/Xcode/DerivedData/YoloTest-fiuymriohppdctfwvyeqikbxfzgo/Build/Products/Debug/./YoloTest (0x10128c178). One of the two will be used. Which one is undefined.
2017-03-13 21:41:45.883 YoloTest[2221:84530] Hello, YoloTest!

也就是說, 我們注入之后, 得到了2個 YoloTester 類, 具體使用哪一個沒有被指定, 所以最終系統(tǒng)應該是按先后順序執(zhí)行了非注入的那一個, 導致輸出的還是 Hello, YoloTest!

按道理這里我們有2條路可以走, 第一條, 換一種方式注入, 第二條既然沒有指定, 我能不能想辦法指定它? 因為第二條我沒有找到太多的資料, 但是我認為也是可以走通的, 但是我比較擔心即使走通了, 會不會把這個類其它的方法全部都不加載進來了, 這樣就有點得不償失了, 所以為了穩(wěn)妥起見, 我們還是選擇既簡單有保險的路子.

  1. 寫 category:
    第一條路走不同之后, 自然就想到了用 runtime hook 方法的形式, 然后打算寫 category, 然后在+initialize 里面寫 exchangeMethod, 但是有個問題是, 在 dylib 里面這么寫, 編譯不給過, 認為你給了一個不存在的類寫 category, 即使你@class YoloTest也不會起作用, 繼續(xù)拋棄.

  2. 新增入口:
    依然是打runtime的主意, 問題是, 在哪加?

我們知道, 一般我們寫代碼最早大多數(shù)情況都是在 main 函數(shù)之后執(zhí)行, 但其實有很多比 main 函數(shù)還早執(zhí)行的, 例如類的+load(Apple 已經(jīng)不建議這么寫了, 用后面的+ initialize)和+initialize就要早于 main函數(shù). 但是上面我們已經(jīng)論證了 category 是不可行的, 所以這里再介紹一種更早于main 函數(shù)的入口----__attribute__((constructor)).

__attribute__((constructor)) void myentry(){
    // do something
}

一個 C 函數(shù), 如果用__attribute__((constructor))修飾之后, 就會在imageLoad 時期就會被執(zhí)行到(切記不要濫用, 比較影響啟動性能), 這個符號會被寫在 Mach-O 的 DATA 中生成一條 mod_init_func記錄, 如:

mod_init.png

所以我們就在這里加上我們的代碼試試看:

NSString * my_description()
{
    return @"Cracker";
}

__attribute__((constructor)) void myentry(){
    Class YoloTester = NSClassFromString(@"YoloTester");
    
    class_replaceMethod(YoloTester, @selector(description), (IMP)my_description, "@:v");
}

上面的代碼主要意思是, 把 YoloTesterdescription方法用 C 函數(shù)my_description替換掉.
執(zhí)行./yololib YoloTest libDylib.dylib和'./YoloTest'后輸出:

2017-03-13 22:04:54.239 YoloTest[3661:165402] Hello, Cracker!

證明我們搞定了這個簡單的小需求, 成功把代碼注入到了一個已經(jīng)編譯好的程序上了.

四. 更進一步
在某些情況下, 我們 hook 之后還想拿到原來的實現(xiàn), 這里有2種方法, 第一種是:
class_replaceMethod會返回一個 IMP, 我們都知道, IMP 可以直接強轉為一個函數(shù)指針, 所以我們可以這樣

IMP ret = class_replaceMethod(YoloTester, @selector(description), (IMP)my_description, "@:v");
NSString *(*func)() = (NSString *(*)())ret; // 如果想在 my_description函數(shù)中執(zhí)行, 可以賦值給 static 變量, 在 my_description 里面判斷執(zhí)行即可.

另一種方法是:
我們都知道 Objective-C 的方法最終都會調用 objc_msgSend, 然后第一個參數(shù)是發(fā)消息的對象, 第二個是 SEL, 后續(xù)的則是各個參數(shù), 所以我們可以先調用class_addMethodmethod_exchangeImplementations, 然后在 my_description中,是這樣的:

NSString * my_description(id self, SEL sel)
{
// 不需要返回值用[self performSelector:], 需要返回值用 NSInvocation
}

個人還是覺得第一種更簡單一些.

五. 后記
這里對 Mac 應用做了一個簡單的注入 dylib 介紹, 里面涉及到 runtime 的東西沒有深入展開闡述, 因為網(wǎng)上資源簡直不要太多. 后續(xù)我還會繼續(xù)深入了解一下里面的情況, 希望有一些高質量的產出可以和大家分享.

其實一開始想到逆向 Mac 應用, 我腦子里最先冒出來的是直接用 Hopper 改匯編代碼, 后面覺得太麻煩, 然后翻到了糖炒小蝦的文章覺得這是一個更加"人性化"的方法...不過最近比較迷匯編, 也看了不少書, 不知道有沒有同道中人可以一起學習進步的.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末搞莺,一起剝皮案震驚了整個濱河市茵肃,隨后出現(xiàn)的幾起案子抒巢,更是在濱河造成了極大的恐慌拂封,老刑警劉巖箕母,帶你破解...
    沈念sama閱讀 223,126評論 6 520
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件呜达,死亡現(xiàn)場離奇詭異诽表,居然都是意外死亡猎塞,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,421評論 3 400
  • 文/潘曉璐 我一進店門蔓罚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來椿肩,“玉大人,你說我怎么就攤上這事豺谈「埠担” “怎么了?”我有些...
    開封第一講書人閱讀 169,941評論 0 366
  • 文/不壞的土叔 我叫張陵核无,是天一觀的道長。 經(jīng)常有香客問我藕坯,道長团南,這世上最難降的妖魔是什么噪沙? 我笑而不...
    開封第一講書人閱讀 60,294評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮吐根,結果婚禮上正歼,老公的妹妹穿的比我還像新娘。我一直安慰自己拷橘,他們只是感情好局义,可當我...
    茶點故事閱讀 69,295評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著冗疮,像睡著了一般萄唇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上术幔,一...
    開封第一講書人閱讀 52,874評論 1 314
  • 那天另萤,我揣著相機與錄音,去河邊找鬼诅挑。 笑死四敞,一個胖子當著我的面吹牛,可吹牛的內容都是我干的拔妥。 我是一名探鬼主播忿危,決...
    沈念sama閱讀 41,285評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼没龙!你這毒婦竟也來了铺厨?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 40,249評論 0 277
  • 序言:老撾萬榮一對情侶失蹤兜畸,失蹤者是張志新(化名)和其女友劉穎努释,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體咬摇,經(jīng)...
    沈念sama閱讀 46,760評論 1 321
  • 正文 獨居荒郊野嶺守林人離奇死亡伐蒂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,840評論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了肛鹏。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片逸邦。...
    茶點故事閱讀 40,973評論 1 354
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖在扰,靈堂內的尸體忽然破棺而出缕减,到底是詐尸還是另有隱情,我是刑警寧澤芒珠,帶...
    沈念sama閱讀 36,631評論 5 351
  • 正文 年R本政府宣布桥狡,位于F島的核電站,受9級特大地震影響,放射性物質發(fā)生泄漏裹芝。R本人自食惡果不足惜部逮,卻給世界環(huán)境...
    茶點故事閱讀 42,315評論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望嫂易。 院中可真熱鬧兄朋,春花似錦、人聲如沸怜械。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,797評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽缕允。三九已至峡扩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間灼芭,已是汗流浹背有额。 一陣腳步聲響...
    開封第一講書人閱讀 33,926評論 1 275
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留彼绷,地道東北人巍佑。 一個月前我還...
    沈念sama閱讀 49,431評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像寄悯,于是被迫代替她去往敵國和親萤衰。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,982評論 2 361

推薦閱讀更多精彩內容

  • 轉至元數(shù)據(jù)結尾創(chuàng)建: 董瀟偉猜旬,最新修改于: 十二月 23, 2016 轉至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,735評論 0 9
  • 這篇文章完全是基于南峰子老師博客的轉載 這篇文章完全是基于南峰子老師博客的轉載 這篇文章完全是基于南峰子老師博客的...
    西木閱讀 30,569評論 33 466
  • 如何看待剩女: 一脆栋、提倡素質模式多樣化 給“剩女”找出路,必須變革擇偶模式洒擦,即改變傳統(tǒng)的“男高女低”單一模式為多元...
    孤獨的長跑者閱讀 872評論 0 0
  • 故事開始于瓜陰洲一個破敗的傅家園子里椿争,這是個曾經(jīng)聲名顯赫的大家族,后來遷徙異地熟嫩,只剩一個七十多歲的老園丁和傅家的一...
    Ltt2683閱讀 4,084評論 0 0
  • 樹活一張皮秦踪,人活一張臉。有時間人簡單點掸茅,或許更快樂椅邓,走入贏家發(fā)現(xiàn)無論說話做事都是有意義和價值,走著走著昧狮,如何在平凡...
    好彩妹閱讀 345評論 0 0