iOS逆向:在任意app上開(kāi)啟malloc stack追蹤內(nèi)存來(lái)源

lldb有一個(gè)內(nèi)存調(diào)試工具malloc stack,開(kāi)啟以后就可以查看某個(gè)內(nèi)存地址的malloc和free記錄,追蹤對(duì)象是在哪里創(chuàng)建的驯遇。

這個(gè)工具可以打印出對(duì)象創(chuàng)建的堆棧,而在逆向時(shí)蓄髓,也經(jīng)常需要追蹤某些方法的調(diào)用棧叉庐,如果可以隨時(shí)打印出某個(gè)對(duì)象的創(chuàng)建記錄,也就能直接找到其所在的類(lèi)和方法会喝,不用再花費(fèi)大量的時(shí)間去打log和動(dòng)態(tài)調(diào)試追蹤了陡叠。

malloc stack

在自己的項(xiàng)目中,要開(kāi)啟malloc stack肢执,需要在Product->Scheme->Edit Scheme->Diagnistic里勾選Malloc Stack選項(xiàng)枉阵。

效果如下:

測(cè)試代碼:

- (IBAction)create:(id)sender {
    NSString *testString = [NSString stringWithFormat:@"string created by %@",self];
    
}

斷點(diǎn)后在lldb中使用lldb.macosx.heap里的malloc_info命令,雖然官網(wǎng)上說(shuō)是Mac app才能用的命令预茄,但是經(jīng)測(cè)試現(xiàn)在在iOS上也能用了:

(lldb) p/x testString
(__NSCFString *) $3 = 0x16eac000 @"string created by <ViewController: 0x16e9d7c0>"
(lldb) command script import lldb.macosx.heap //加載lldb.macosx.heap
"malloc_info", "ptr_refs", "cstr_refs", "find_variable", and "objc_refs" commands have been installed, use the "--help" options on these commands for detailed help.
(lldb) malloc_info -s 0x16eac000
0x0000000016eac000: malloc(    64) -> 0x16eac000 __NSCFString.NSMutableString.NSString.NSObject.isa
stack[0]: addr = 0x16eac000, type=malloc, frames:
     [0] 0x00000000242948ab libsystem_malloc.dylib`malloc_zone_malloc + 123
     [1] 0x00000000244e3bc1 CoreFoundation`_CFRuntimeCreateInstance + 237
     [2] 0x00000000245a6ffd CoreFoundation`__CFStringCreateImmutableFunnel3 + 1657
     [3] 0x00000000244ee0f7 CoreFoundation`CFStringCreateCopy + 359
     [4] 0x00000000245a725d CoreFoundation`_CFStringCreateWithFormatAndArgumentsAux2 + 89
     [5] 0x0000000024d17dd3 Foundation`-[NSPlaceholderString initWithFormat:locale:arguments:] + 139
     [6] 0x0000000024d17cd1 Foundation`+[NSString stringWithFormat:] + 61
     [7] 0x00000000000d7343 testMallocStack`-[ViewController create:] + 97 at ViewController.m:23:28
     [8] 0x00000000287a5771 UIKit`-[UIApplication sendAction:to:from:forEvent:] + 81
     [9] 0x00000000287a5701 UIKit`-[UIControl sendAction:to:forEvent:] + 65
     [10] 0x000000002878d61f UIKit`-[UIControl _sendActionsForEvents:withEvent:] + 447
     [11] 0x00000000287a5051 UIKit`-[UIControl touchesEnded:withEvent:] + 617
     [12] 0x00000000287a4cbf UIKit`-[UIWindow _sendTouchesForEvent:] + 647
     [13] 0x000000002879d5d7 UIKit`-[UIWindow sendEvent:] + 643
     [14] 0x000000002876e119 UIKit`-[UIApplication sendEvent:] + 205
     [15] 0x000000002876c757 UIKit`_UIApplicationHandleEventQueue + 5135
     [16] 0x0000000024599257 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 15
     [17] 0x0000000024598e47 CoreFoundation`__CFRunLoopDoSources0 + 455
     [18] 0x00000000245971af CoreFoundation`__CFRunLoopRun + 807
     [19] 0x00000000244e9bb9 CoreFoundation`CFRunLoopRunSpecific + 517
     [20] 0x00000000244e99ad CoreFoundation`CFRunLoopRunInMode + 109
     [21] 0x0000000025763af9 GraphicsServices`GSEventRunModal + 161
     [22] 0x00000000287d5fb5 UIKit`UIApplicationMain + 145
     [23] 0x00000000000d7587 testMallocStack`main + 107 at main.m:14:9
     [24] 0x000000002419c873 libdyld.dylib`start + 3
     [25] 0x000000003a9c0001 libsystem_pthread.dylib`_thread + 1

這個(gè)工具是繼承自gdb的malloc_history兴溜,不過(guò)malloc_history只能用在模擬器上,而malloc_info在模擬器和真機(jī)上都可以使用。另外拙徽,新版Xcode又增加了一個(gè)新的lldb工具memory history刨沦,在Product->Scheme->Edit Scheme->Diagnistic里勾選Address Sanitizer即可,效果類(lèi)似膘怕。

使用非官方版的heap.py

注意想诅,在Xcode8.3以后使用malloc_info會(huì)crash,似乎是出bug了岛心,一直沒(méi)修復(fù)来破。在Xcode8.2上可以正常使用。

所以我們需要替換一下lldb自帶的lldb.macosx.heap模塊鹉梨。使用這個(gè)非官方的版本:heap.py讳癌。

lldb可以加載自定義的pthon腳本。只需要在lldb中輸入:

command script import python腳本的地址

因此把上面的heap.py下載到本地后存皂,輸入:

command script import /你的路徑/lldb/examples/darwin/heap_find/heap.py

即可晌坤。

在任意app上開(kāi)啟malloc stack

Address Sanitizermemory history需要重新編譯app,但是malloc stack只需要在app啟動(dòng)前設(shè)置環(huán)境變量MallocStackLoggingMallocStackLoggingNoCompact即可旦袋。開(kāi)啟后會(huì)在系統(tǒng)的/tmp目錄下生成一個(gè).index文件骤菠,這個(gè)文件里的內(nèi)容是依賴于app的運(yùn)行時(shí)環(huán)境的,進(jìn)程退出以后這個(gè)文件也就沒(méi)用處了疤孕。

那么商乎,現(xiàn)在的問(wèn)題就變成了如何給app設(shè)置啟動(dòng)環(huán)境變量。

方法一:execve

這是我一開(kāi)始使用的方法祭阀。使用execve函數(shù)來(lái)運(yùn)行app的二進(jìn)制文件鹉戚。

由于沙盒的限制,需要讓app擁有root權(quán)限才能使用execve专控。步驟如下抹凳。

1.重簽名ipa

重簽名需要逆向的app。因?yàn)樾枰獙?duì)app內(nèi)容作出修改伦腐。重簽名后安裝到越獄設(shè)備上赢底。

2.移動(dòng)app到系統(tǒng)app目錄下,修改權(quán)限

只有系統(tǒng)目錄下的app才有root權(quán)限柏蘑。

假設(shè)需要逆向的app是YOUR_APP.app幸冻。把a(bǔ)pp移動(dòng)到系統(tǒng)app目錄下:mv -f /var/containers/Bundle/Application/xxxxxxxxxxxxx/YOUR_APP.app /Applications/YOUR_APP.app

然后修改文件權(quán)限:

cd /Applications

chown -R root:wheel YOUR_APP.app

chmod 4755 YOUR_APP.app/YOUR_APP

移動(dòng)后咳焚,用uicache刷新app圖標(biāo)洽损,用killall SpringBoard重啟SpringBoard

3.使用引導(dǎo)程序啟動(dòng)app

最終的目的就是使用引導(dǎo)程序用execve啟動(dòng)app黔攒,在啟動(dòng)前設(shè)置環(huán)境變量趁啸。

首先重命名原來(lái)的二進(jìn)制文件:mv YOUR_APP.app/YOUR_APP YOUR_APP.app/YOUR_APP_Orig强缘。

然后制作引導(dǎo)程序,隨便創(chuàng)建一個(gè)iOS工程不傅,替換main.m里的內(nèi)容為:

int main(int argc, char * argv[]) {
    @autoreleasepool {
        NSString* string = [[NSBundle mainBundle] pathForResource:@"YOUR_APP_Orig" ofType:nil];//YOUR_APP_Orig是所要啟動(dòng)的二進(jìn)制文件名
        argv[0] = (char*)[string UTF8String];
        char *envp[] =
        {
            "HOME=/var/root",
            "LOGNAME=root",
            "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/bin/X11:/usr/games",
            "USER=root",
            "MallocStackLogging=1",
            "MallocStackLoggingNoCompact=1"
            0
        };
        execve([string UTF8String], argv, envp);
        return 0;
    }
}

編譯后旅掂,取出二進(jìn)制文件,重命名為YOUR_APP访娶,復(fù)制到越獄設(shè)備的/Application/YOUR_APP.app/目錄下商虐。

給引導(dǎo)程序設(shè)置執(zhí)行權(quán)限:chmod +x /Application/YOUR_APP.app/YOUR_APP

最后重啟SpringBoard:killall SpringBoard崖疤。

這樣秘车,每次啟動(dòng)app就都會(huì)使用引導(dǎo)程序間接啟動(dòng)app。

缺點(diǎn)

  • 步驟繁瑣劫哼。
  • 有些app重簽名很麻煩叮趴。
  • 越獄后的系統(tǒng)分區(qū)容量很小,很容易就被占滿了权烧,想要測(cè)試大一點(diǎn)的app就麻煩了眯亦。
  • 無(wú)法使用debugserver喚醒a(bǔ)pp,調(diào)試啟動(dòng)過(guò)程般码。因?yàn)?code>YOUR_APP和YOUR_APP_Orig是兩個(gè)進(jìn)程妻率,第一個(gè)在execve執(zhí)行完就退出了。
  • 把a(bǔ)pp放到系統(tǒng)目錄下有時(shí)候會(huì)引起crash板祝。

方法2:debugserver參數(shù)

方法1實(shí)在是太麻煩了宫静,有時(shí)候遇上重簽名失敗的app就更麻煩了。但其實(shí)還有另一個(gè)更直接的方法券时。就是使用debugserver的命令孤里。

debugserver是動(dòng)態(tài)調(diào)試工具,參考:IOS平臺(tái)lldb動(dòng)態(tài)調(diào)試介紹橘洞。

安裝好后扭粱,在越獄設(shè)備上輸入debugserver *:1234 /var/containers/Bundle/Application/589822B6-BFDA-4A3D-A71C-AD0D30BA6077/WeChat.app/WeChat就能喚醒a(bǔ)pp進(jìn)行調(diào)試。

但是網(wǎng)上的教程都沒(méi)有提到震檩,其實(shí)debugserver還有一個(gè)隱藏的參數(shù)--env(-env,-e都可以),就是用來(lái)設(shè)置進(jìn)程的環(huán)境變量的:

debugserver *:1234 /var/containers/Bundle/Application/589822B6-BFDA-4A3D-A71C-AD0D30BA6077/WeChat.app/WeChat -env MallocStackLogging=1 -env MallocStackLoggingNoCompact=1

當(dāng)時(shí)我想debugserver會(huì)不會(huì)有設(shè)置環(huán)境變量的功能蜓堕,沒(méi)想到隨便試了個(gè)-env就成功了抛虏。后來(lái)在debugserver的源碼里也發(fā)現(xiàn)了它的存在:debugserver.cpp(搜索g_long_options可以找到env)。

這樣套才,即使app沒(méi)有重簽名迂猴,也可以直接調(diào)試了。

缺點(diǎn)

debugserver無(wú)法啟動(dòng)調(diào)試extension app背伴,因?yàn)閑xtension app是依賴于宿主app而存在的沸毁,不能單獨(dú)運(yùn)行峰髓。這種情況就只能使用方法1了。

測(cè)試

這里使用一個(gè)重簽名息尺,并且恢復(fù)了符號(hào)表的微信進(jìn)行測(cè)試携兵。

比如找到微信查看表情的界面,打印出內(nèi)存地址為0x108795c20

<MMEmoticonView: 0x108795c20; frame = (276.25 404.25; 215.5 215.5); autoresize = LM+RM+TM+BM; layer = <CALayer: 0x170828700>>

第一次使用malloc_info需要在lldb里導(dǎo)入lldb.macosx.heap搂誉,這里需要導(dǎo)入非官方版本的heap.py

(lldb) command script import heap.py的路徑
"malloc_info", "ptr_refs", "cstr_refs", "find_variable", and "objc_refs" commands have been installed, use the "--help" options on these commands for detailed help.

使用malloc_info打印創(chuàng)建堆棧:

(lldb) malloc_info -s 0x108795c20
0x0000000108795c20: malloc(   480) -> 0x108795c20 MMEmoticonView.UIView.UIResponder.NSObject.isa
stack[0]: addr = 0x108795c20, type=malloc, frames:
     [0] 0x000000018374e0ac libsystem_malloc.dylib`calloc + 40
     [1] 0x000000018318b624 libobjc.A.dylib`class_createInstance + 76
     [2] 0x0000000183199ae4 libobjc.A.dylib`_objc_rootAlloc + 52
     [3] 0x00000001026d8fd4 WeChat`-[MMImageBrowseView InitEmoticonView:] + 432
     [4] 0x000000010245e950 WeChat`-[MMEmotionMsgBrowseViewController initImageViewWithFrame:] + 404
     [5] 0x000000010245ea74 WeChat`-[MMEmotionMsgBrowseViewController setupImageView] + 156
     [6] 0x000000010245e024 WeChat`-[MMEmotionMsgBrowseViewController initView] + 224
     [7] 0x000000010245d76c WeChat`-[MMEmotionMsgBrowseViewController viewDidLoad] + 112
     [8] 0x000000018a5f7924 UIKit`-[UIViewController loadViewIfRequired] + 1056
     [9] 0x000000018a60f4b4 UIKit`-[UIViewController __viewWillAppear:] + 132
     [10] 0x00000001026e05f8 WeChat`-[MMUIViewController beginAppearanceTransition:animated:] + 92
     [11] 0x000000018a7975b4 UIKit`-[UINavigationController _startCustomTransition:] + 1136
     [12] 0x000000018a6afe74 UIKit`-[UINavigationController _startDeferredTransitionIfNeeded:] + 676
     [13] 0x000000018a6afadc UIKit`-[UINavigationController __viewWillLayoutSubviews] + 64
     [14] 0x000000018a6afa40 UIKit`-[UILayoutContainerView layoutSubviews] + 188
     [15] 0x000000018a5f4a80 UIKit`-[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1196
     [16] 0x0000000187aa29d8 QuartzCore`-[CALayer layoutSublayers] + 148
     [17] 0x0000000187a974cc QuartzCore`CA::Layer::layout_if_needed(CA::Transaction*) + 292
     [18] 0x0000000187a9738c QuartzCore`CA::Layer::layout_and_display_if_needed(CA::Transaction*) + 32
     [19] 0x0000000187a143e0 QuartzCore`CA::Context::commit_transaction(CA::Transaction*) + 252
     [20] 0x0000000187a3ba68 QuartzCore`CA::Transaction::commit() + 512
     [21] 0x0000000187a3c488 QuartzCore`CA::Transaction::observer_callback(__CFRunLoopObserver*, unsigned long, void*) + 120
     [22] 0x00000001846f60c0 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ + 32
     [23] 0x00000001846f3cf0 CoreFoundation`__CFRunLoopDoObservers + 372
     [24] 0x00000001846f4180 CoreFoundation`__CFRunLoopRun + 1024
     [25] 0x00000001846222b8 CoreFoundation`CFRunLoopRunSpecific + 444
     [26] 0x00000001860d6198 GraphicsServices`GSEventRunModal + 180
     [27] 0x000000018a6627fc UIKit`-[UIApplication _run] + 684
     [28] 0x000000018a65d534 UIKit`UIApplicationMain + 208
     [29] 0x00000001000ebea4 WeChat`-[WATemplateMsgMngSwitchCell .cxx_destruct] + 372
     [30] 0x00000001836055b8 libdyld.dylib`start + 4

這樣就直接找到表情界面所在的類(lèi)徐紧,以及在哪里初始化了。

這樣的話炭懊,只要能找到一個(gè)對(duì)象并级,就能快速定位到其所在模塊。比原來(lái)打log侮腹,打斷點(diǎn)一步步回溯高效多了嘲碧。

恢復(fù)符號(hào)表

建議在對(duì)app重簽名時(shí)恢復(fù)符號(hào)表「缸瑁恢復(fù)符號(hào)表后愈涩,就能直接在堆棧中看到方法名,免去了計(jì)算偏移量然后在hopper里查找的麻煩至非。

參考:iOS符號(hào)表恢復(fù)&逆向支付寶, restore-symbol钠署。

其他幾個(gè)調(diào)試命令

ptr_refs

可以在內(nèi)存中找出哪些地址引用了某個(gè)指針,也就相當(dāng)于查看某個(gè)變量在哪里被引用荒椭。

cstr_refs

在內(nèi)存中尋找某個(gè)C String在哪里被引用谐鼎。

find_variable

在當(dāng)前棧幀上尋找某個(gè)局部變量在哪里被引用。

objc_refs

在內(nèi)存中尋找某個(gè)類(lèi)的實(shí)例趣惠。

轉(zhuǎn)到Xcode中調(diào)試

如果想要在Xcode中調(diào)試并開(kāi)啟malloc stack狸棍,則需要先用debugserver啟動(dòng)app,在終端的lldb里連接上以后味悄,再用process detach斷開(kāi)連接草戈。接下來(lái)用Xcode的Attach to Process就可以了,參考:iOS逆向:用Xcode直接調(diào)試第三方app侍瑟。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末唐片,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子涨颜,更是在濱河造成了極大的恐慌费韭,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件庭瑰,死亡現(xiàn)場(chǎng)離奇詭異星持,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)弹灭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)督暂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)揪垄,“玉大人,你說(shuō)我怎么就攤上這事逻翁〖⑴” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵卢未,是天一觀的道長(zhǎng)肪凛。 經(jīng)常有香客問(wèn)我,道長(zhǎng)辽社,這世上最難降的妖魔是什么伟墙? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮滴铅,結(jié)果婚禮上戳葵,老公的妹妹穿的比我還像新娘。我一直安慰自己汉匙,他們只是感情好拱烁,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著噩翠,像睡著了一般戏自。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上伤锚,一...
    開(kāi)封第一講書(shū)人閱讀 49,821評(píng)論 1 290
  • 那天擅笔,我揣著相機(jī)與錄音,去河邊找鬼屯援。 笑死猛们,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的狞洋。 我是一名探鬼主播弯淘,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼吉懊!你這毒婦竟也來(lái)了庐橙?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤借嗽,失蹤者是張志新(化名)和其女友劉穎怕午,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體淹魄,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年堡距,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了甲锡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片兆蕉。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖缤沦,靈堂內(nèi)的尸體忽然破棺而出虎韵,到底是詐尸還是另有隱情,我是刑警寧澤缸废,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布包蓝,位于F島的核電站,受9級(jí)特大地震影響企量,放射性物質(zhì)發(fā)生泄漏测萎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一届巩、第九天 我趴在偏房一處隱蔽的房頂上張望硅瞧。 院中可真熱鬧,春花似錦恕汇、人聲如沸腕唧。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)枣接。三九已至,卻和暖如春缺谴,著一層夾襖步出監(jiān)牢的瞬間但惶,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工瓣赂, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留榆骚,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓煌集,卻偏偏與公主長(zhǎng)得像妓肢,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子苫纤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349

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