在上一篇文章《iOS教程:对App的内存堆内容进行转储》中,我们讨论了如何使用GDB从iOS应用程序的堆中转储敏感信息。在本文中,我们将介绍如何使用Cycript实现相同的目的,并使用class-dump-z专门输出类属性或者实例变量。这一次,我们将以更加自动化的方式来自动解析二进制文件的类转储,并创建必要的Cycript脚本来从内存中获取特定的属性。此外,我以后还将给出实现以上功能的另一工具。留意我们的GitHub账号,以获取最新的工具和脚本。
对于一个iOS应用程序,如果我们不能获取它的源代码,那么我们就必须首先解密二进制文件。首先,我们转储该文件的类信息。虽然已经有很多教程讲解如何进行解密,但是Clutch是我常用的工具,因为它使用起来很容易,而且它还能生成一个包含解密后的二进制文件信息的IPA文件,所以有需要时你就可以在其他设备上重新安装它。在我们提取和安装解密后的二进制文件后,我们可以运行class-dump-z来获得所有类的头文件信息、属性、类方法、实例方法等等。
MAPen-iPad-000314:~ root# ./class-dump-z -z TestApp [TRUNCATED] @interface CryptoManager : XXUnknownSuperclass { @private NSData* key; } @property(retain, nonatomic) NSData* key; +(id)CryptoManager; -(id)init; -(id)cipher:(id)cipher key:(id)key context:(unsigned)context; -(id)cipher:(id)cipher key:(id)key context:(unsigned)context withIV:(BOOL)iv; -(id)cipher:(id)cipher key:(id)key context:(unsigned)context withIV:(BOOL)iv usingIV:(id)iv5; -(id)cipher:(id)cipher key:(id)key context:(unsigned)context withIV:(BOOL)iv usingIV:(id)iv5 withPad-ding:(BOOL)padding; -(void)clearKey; -(void)dealloc; -(id)decryptData:(id)data; -(id)decryptData:(id)data usingIV:(id)iv; -(id)decryptData:(id)data usingIV:(id)iv withPadding:(BOOL)padding; -(id)decryptData:(id)data withIV:(BOOL)iv; -(id)decryptData:(id)data withIV:(BOOL)iv withHeader:(BOOL)header; -(id)decryptData:(id)data withKey:(id)key; -(id)decryptString:(id)string; -(id)decryptString:(id)string withIV:(BOOL)iv; -(id)decryptString:(id)string withIV:(BOOL)iv withHeader:(BOOL)header; -(id)decryptString:(id)string withIV:(BOOL)iv withHeader:(BOOL)header withKey:(id)key; -(id)decryptString:(id)string withKey:(id)key; -(id)encryptData:(id)data; -(int)encryptData:(id)data AndAppendToFileAtPath:(id)path initiatedByUnlockOperation:(BOOL)operation error:(id*)error; -(id)encryptData:(id)data usingIV:(id)iv; -(id)encryptData:(id)data withKey:(id)key; -(id)encryptString:(id)string; -(id)encryptString:(id)string withKey:(id)key; -(id)hashString:(id)string; -(id)hashString:(id)string salt:(id)salt; -(BOOL)isHashOfString:(id)string equalToHash:(id)hash; -(BOOL)isHeaderValid:(id)valid; -(id)newHeader; -(unsigned long)readEncryptedData:(void**)data atPath:(id)path offset:(long)offset length:(unsigned long)length initiatedByUnlockOperation:(BOOL)operation error:(id*)error; @end [TRUNCATED]
所以从上面代码中可以看出,TestApp中有一个类“CryptoManager”,并且它有一个属性“key”。这一点看起来很有趣,因为从上面信息中看出内存中可能会有一个加密密钥。接下来,我们使用Cycript从内存中抓取特定的属性。需要注意的是,在运行时期间,在登陆之前,类“CryptoManager”就被实例化了,但只有用户曾经在该设备上成功登录过才可以。此外,即使该类不再需要(例如,用户退出账号)了,它也并没有被清理掉,而这正是漏洞所在。在这个例子中,在前面的会话中我们已经成功登录,所以在下一次用户登录之前类已经存在于内存中。
首先,我们通过一个SSH会话hook 正在运行的TestApp进程,这样我们就可以不用管运行在iOS设备上的应用程序了。
MAPen-iPad-000314:~ root# cycript -p TestApp cy#
既然我们已经hook成功,下面我们接着讨论Cycript中的choose方法。Choose方法扫描堆以匹配类名字,并返回一个匹配目标类结构的对象数组。所以,如果我们输入“choose(MyClass)”,那么它将包含一个索引数组,该数组中包含着当前内存中(或者匹配结构的)类MyClass所有的实例化对象。下面的输出仅仅是第一个索引对象,它的索引值是“0”,并将其存储在一个变量“a”中。如果你更喜欢使用GDB,我们也可以返回内存位置,然后利用GDB转储内存的子区域中的所有内容,或者设置断点并观察寄存器。至于如何扫描堆,你可以查看上一篇文章《iOS教程:对App的内存堆内容进行转储》。然而,需要注意的是,该数组中可能有不止一个类的实例对象,那么你就需要遍历每个索引来得到目标类实例的属性。
cy# a=choose(CryptoManager) [#"< CryptoManager: 0x17dcc340>",#"< CryptoManager: 0x17f42ba0>"]
接下来,为了方便我们以后抓取密钥并解密应用中的任何数据,现在我们从内存中转储属性“key”。
cy# a[0].key.hexString @"6D2268CFFDDC16E890B365910543833190C9C02C4DCA2342A9AEED68428EF9B6"
Bingo!现在我们已经拥有了十六进制的密钥,后面我们将可以利用此密钥来解密应用程序想要加密的任何东西。
现在,我们讨论下如何对上面操作实现自动化,并理清我们所了解的以及如何通过编程实现自动化。我们知道,class-dump-z输出中包含了所以类和它们属性。但我们不知道的是那些类目前是否已经实例化,而且我们也不知道内存中那些类实例化了多少次。所以,我们能做的就是解析class-dump-z的输出,并创建一个类和它们属性的映射图。既然我们有了映射图,那么现在我们就可以创建Cycript脚本来获取相关信息。然而,需要注意的是,这种技术只是针对已经实例化的类,我们并不会涉及在Cycript中如何创建一个新的实例,因为已经有很多教程和书籍介绍过了。
所以,我们必须从choose方法中阅读Cycript的输出内容,以此来确定对象已经在内存中实例化了多少次。为了实现这一点,我们可以使用JavaScript来获得数组长度:
cy# choose(CryptoManager).length 2 cy#
很酷吧,现在我们知道需要对数组循环多少次来获取“CryptoManager”所有的实例对象。现在我们就可以继续cycript脚本的编写。
Cycript可以将脚本作为参数,一个基本的脚本必须包含我们想运行的命令,就像下面一样:
MAPen-iPad-000314:~ root# cat dump.cy a=choose(CryptoManager)[0] a.key.hexString MAPen-iPad-000314:~ root# cycript -p TestApp dump.cy @"6D2268CFFDDC16E890B365910543833190C9C02C4DCA2342A9AEED68428EF9B6"
不过,有一个我似乎无法解决的问题是,当你运行一个脚本时,Cycript只返回最后一行输出到终端,而不是所有的输出。所以,要从终端输出多个类以及它们的属性,你就不得不为每个类及属性创建一个新脚本。如果谁知道如何突破这个限制,请告诉我如何实现,或者你也可以用Cycript JavaScript(如果你喜欢这种语言)写出来。
【原文:iOS Tutorial – Dumping the Application Memory Part 2 安全脉搏PulseO0O翻译整理发布 】