博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
iOS逆向:在任意app上开启malloc stack追踪内存来源
阅读量:4296 次
发布时间:2019-05-27

本文共 9235 字,大约阅读时间需要 30 分钟。

lldb有一个内存调试工具malloc stack,开启以后就可以查看某个内存地址的malloc和free记录,追踪对象是在哪里创建的。

这个工具可以打印出对象创建的堆栈,而在逆向时,也经常需要追踪某些方法的调用栈,如果可以随时打印出某个对象的创建记录,也就能直接找到其所在的类和方法,不用再花费大量的时间去打log和动态调试追踪了。

malloc stack

在自己的项目中,要开启malloc stack,需要在Product->Scheme->Edit Scheme->Diagnistic里勾选Malloc Stack选项。 注意:平时,请不要勾选,不然APP会将对象的创建、销毁等记录下来,让app的占用空间巨大,达到几十G 都是很随意的事。

效果如下:

测试代码:

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

断点后在lldb中使用lldb.macosx.heap里的malloc_info命令,虽然官网上说是Mac app才能用的命令,但是经测试现在在iOS上也能用了:

(lldb) p/x testString(__NSCFString *) $3 = 0x16eac000 @"string created by 
"(lldb) command script import 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 0x16eac0000x0000000016eac000: malloc( 64) -> 0x16eac000 __NSCFString.NSMutableString.NSString.NSObject.isastack[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

这个工具是继承自gdb的malloc_history,不过malloc_history只能用在模拟器上,而malloc_info在模拟器和真机上都可以使用。另外,新版Xcode又增加了一个新的lldb工具memory history,在Product->Scheme->Edit Scheme->Diagnistic里勾选Address Sanitizer即可,效果类似。

注意,在Xcode8.3以后使用malloc_info会crash,似乎是出bug了,一直没修复。在Xcode8.2上可以正常使用。

在任意app上开启malloc stack

Address Sanitizermemory history需要重新编译app,但是malloc stack只需要在app启动前设置环境变量MallocStackLoggingMallocStackLoggingNoCompact即可。开启后会在系统的/tmp目录下生成一个.index文件,这个文件里的内容是依赖于app的运行时环境的,进程退出以后这个文件也就没用处了。

那么,现在的问题就变成了如何给app设置启动环境变量。

方法一:execve

这是我一开始使用的方法。使用execve函数来运行app的二进制文件。

由于沙盒的限制,需要让app拥有root权限才能使用execve。步骤如下。

1.重签名ipa

重签名需要逆向的app。因为需要对app内容作出修改。重签名后安装到越狱设备上。

2.移动app到系统app目录下,修改权限

只有系统目录下的app才有root权限。

假设需要逆向的app是YOUR_APP.app。把app移动到系统app目录下:mv -f /var/containers/Bundle/Application/xxxxxxxxxxxxx/YOUR_APP.app /Applications/YOUR_APP.app

然后修改文件权限:

cd /Applications

chown -R root:wheel YOUR_APP.app

chmod 4755 YOUR_APP.app/YOUR_APP

移动后,用uicache刷新app图标,用killall SpringBoard重启SpringBoard

3.使用引导程序启动app

最终的目的就是使用引导程序用execve启动app,在启动前设置环境变量。

首先重命名原来的二进制文件:mv YOUR_APP.app/YOUR_APP YOUR_APP.app/YOUR_APP_Orig

然后制作引导程序,随便创建一个iOS工程,替换main.m里的内容为:

int main(int argc, char * argv[]) {    @autoreleasepool {        NSString* string = [[NSBundle mainBundle] pathForResource:@"YOUR_APP_Orig" ofType:nil];//YOUR_APP_Orig是所要启动的二进制文件名        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;    }}

编译后,取出二进制文件,重命名为YOUR_APP,复制到越狱设备的/Application/YOUR_APP.app/目录下。

给引导程序设置执行权限:chmod +x /Application/YOUR_APP.app/YOUR_APP

最后重启SpringBoard:killall SpringBoard

这样,每次启动app就都会使用引导程序间接启动app。

缺点

  • 步骤繁琐。
  • 有些app重签名很麻烦。
  • 越狱后的系统分区容量很小,很容易就被占满了,想要测试大一点的app就麻烦了。
  • 无法使用debugserver唤醒app,调试启动过程。因为YOUR_APPYOUR_APP_Orig是两个进程,第一个在execve执行完就退出了。
  • 把app放到系统目录下有时候会引起crash。

方法2:debugserver参数

方法1实在是太麻烦了,有时候遇上重签名失败的app就更麻烦了。但其实还有另一个更直接的方法。就是使用debugserver的命令。

debugserver是动态调试工具,参考:。

安装好后,在越狱设备上输入debugserver *:1234 /var/containers/Bundle/Application/589822B6-BFDA-4A3D-A71C-AD0D30BA6077/WeChat.app/WeChat就能唤醒app进行调试。

但是网上的教程都没有提到,其实debugserver还有一个隐藏的参数--env(-env,-e都可以),就是用来设置进程的环境变量的:

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

当时我想debugserver会不会有设置环境变量的功能,没想到随便试了个-env就成功了。后来在debugserver的源码里也发现了它的存在:(搜索g_long_options可以找到env)。

这样,即使app没有重签名,也可以直接调试了。

缺点

debugserver无法启动调试extension app,因为extension app是依赖于宿主app而存在的,不能单独运行。这种情况就只能使用方法1了。

测试

这里使用一个重签名,并且恢复了符号表的微信进行测试。

比如找到微信查看表情的界面,打印出内存地址为0x108795c20

>

第一次使用malloc_info需要在lldb里导入lldb.macosx.heap

(lldb) command script import 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.

使用malloc_info打印创建堆栈:

(lldb) malloc_info -s 0x108795c200x0000000108795c20: malloc(   480) -> 0x108795c20 MMEmoticonView.UIView.UIResponder.NSObject.isastack[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

这样就直接找到表情界面所在的类,以及在哪里初始化了。

这样的话,只要能找到一个对象,就能快速定位到其所在模块。比原来打log,打断点一步步回溯高效多了。

恢复符号表

建议在对app重签名时恢复符号表。恢复符号表后,就能直接在堆栈中看到方法名,免去了计算偏移量然后在hopper里查找的麻烦。

参考:, 。

其他几个调试命令

ptr_refs

可以在内存中找出哪些地址引用了某个指针,也就相当于查看某个变量在哪里被引用。

cstr_refs

在内存中寻找某个C String在哪里被引用。

find_variable

在当前栈帧上寻找某个局部变量在哪里被引用。

objc_refs

在内存中寻找某个类的实例。

转到Xcode中调试

如果想要在Xcode中调试并开启malloc stack,则需要先用debugserver启动app,在终端的lldb里连接上以后,再用process detach断开连接。接下来用Xcode的Attach to Process就可以了,参考:。

转载地址:http://tkdws.baihongyu.com/

你可能感兴趣的文章
Oracle设置密码永不过期
查看>>
[Windows-bat] Jeecg-boot一键启动或重启
查看>>
[Windows] Anacoda安装和使用
查看>>
MySQL根据日期统计数据和查询数据
查看>>
VMware虚拟机安装MacOS
查看>>
Windows环境部署Wordpress
查看>>
Selenium+ChromeDriver使用记录
查看>>
cdp4j使用记录
查看>>
SpringBoot集成thymeleaf模板引擎
查看>>
ArcMap使用记录
查看>>
Java-Comparator比较器-中文排序
查看>>
nginx入门和使用实践
查看>>
吉他换弦
查看>>
软考报名入口及流程
查看>>
OpenResty入门和使用实践
查看>>
java+ffmpeg使用记录
查看>>
vue集成天地图
查看>>
vue集成统计图-DataV-Echarts-Highcharts
查看>>
MySQL在SQL中调用系统命令
查看>>
Java项目加入maven构建工具
查看>>