内容简介:1
一、前言
在正文开始之前,先提出几个问题:我们经常说的砸壳到底砸的是什么?壳有什么作用? iOS 是如何做签名验证的?苹果在应用审核的机审阶段都能获取到我们哪些信息?带着这些问题我们进入到正文。
二、 ipa内容介绍
首先来介绍下 ipa 包。 ipa 实际上是一个压缩包,我们从 App Store 下载的应用实际上都是压缩包。压缩包中包含 .app 的文件, .app 文件实际上是一个带后缀的文件夹。在 app 中,存在如下文件:
1 )资源文件
资源文件包括我们常用的内置文件,如图片、 plist 以及生成的 .car 文件等。
2 )可执行程序
可执行程序是最核心的文件,除了代码和数据外,里面包含 code signature 和 ENCRYPTION 。
上图中展示的是 code signature 和 ENCRYPTION 在 LoadCommad 中的索引。展开 ENCRYPTION 后可以看到 ENCRYPTION 的偏移地址和大小。 Crypt ID 标记该 Mach-O 文件是否被加密,如果加密则 Crypt ID = 1 ,否则为 0 。
那么这个 ENCRYPTION 是什么?谁负责加密的?谁负责解密的?如果文件没有加密是否还能被运行?
实际上加密的 ENCRYPTION 就是我们所说的壳,砸壳就是将 ENCRYPTION 进行解密操作。从上面的截图我们可以看出, ENCRYPTION 的起始偏移地址为文件的 0x4000 位置,而结束位置可以计算出为 0x4000+0x424000 = 0x428000 。这个范围正好对应着 Mach-O 的文本段(不是 1:1 的,起始位置 0x4000 ,而不是 0x0 )。也就是说加密实际上是对 TEXT 段进行加密。 TEXT 内存储的是代码信息,包括函数指令、类名、方法名、字符串信息等。
对 TEXT 进行加密,加密后的 Mach-O 文件无法获取到代码信息,也就是说指令信息我们无法直接获取到了。除了指令外,在 DATA 段中,有些数据存储的是指针信息,指向 TEXT 段的数据,这样的数据也无法解析出来。
加壳之后的应用,在不解密的情况下,无法暴露指令和文本数据,这能很好地保护应用。这个壳是在上传到 App Store 由 App Store 进行加密的,用户下载的应用也是被加壳的应用。存储在手机的文件也是被加密的,只有在应用 运行 时, iOS 才会对文件进行解密,也就是说在用户手机上运行的文件都是解密脱壳后的文件。我们在进行真机调试时,安装到手机上的文件是未加密的,这个时候 Crypt ID 标记为 0 。 iOS 系统在识别 Crypt ID 为 0 时不会进行解密处理。
3) code signature
code signature 包含资源文件的签名信息,如果资源文件被更改替换,那么签名是无法验证通过的。因此下载 XIB 等方式实现 UI 的动态布局是无法实现的。那么这里的 code signature 与 Mach-O 文件里的 signature 是一样的吗?当然是不一样的。这里的签名验证的是资源文件,而 Mach-O 文件中的 code signature 是验证 Mach-O 是否被篡改以及是否是 apple 允许安装的应用。
三、 dumpdecrypted砸壳原理简介
砸壳的技术方案可以分为两种,一种是静态砸壳,一种是动态砸壳。静态砸壳的原理是硬破解 apple 的加密算法,目前是一种使用频率极低的技术方案。动态砸壳是利用 iOS 将文件解密后加载到内存后,将解密数据拷贝到磁盘的方案。动态砸壳目前成熟的方案很多,在这里介绍下 dumpdecrypted 的方式。
dumpdecrypted 是以动态库的方式,将代码注入到目标进程中。那么如何让一个应用程序在运行时加载我们的动态库呢?目前的方案主要有两种:
1 )修改 Mach-O 文件,在 LC 中,添加 LC_LOAD_DYLIB 信息,然后重签名运行。
这需要开发者对 Mach-O 文件有足够的了解,否则很容易损毁文件。不过已经有相应的工具: https://github.com/Tyilo/insert_dylib 。有兴趣的可以试验下。
2 )通过在手机的终端输入 DYLD_INSERT_LIBRARIES=" 动态库 " APP 路径 命令(这就要求手机必须是越狱的),指定应用加载动态库, dumpdecrypted 采用的就是这种方式。
DYLD_INSERT_LIBRARIES 是系统的环境变量。通过在终端输入 man dyld 可以查看环境变量及其解释。 DYLD_INSERT_LIBRARIES 的解释如下:
除了 DYLD_INSERT_LIBRARIES 变量外,我们可以打印看到还有许多环境变量,
这些变量的解释和用处都在终端中有说明,在此不再一一解释。额外提一句,我们可以在应用中通过 getenv 函数检测是否存在环境变量,这可以作为安全监测数据。
在动态库被加载后,标记为 __attribute__((constructor)) 的函数会被执行。启动函数执行后,核心步骤只做 3 件事
1 )在加密的原文件中复制从起始位置开始的未加密的数据。
2 )从内存中的文件复制解密的数据。
3 )在加密原文件中跳过加密部分,拷贝剩余未加密数据。
这 3 件事做完后,应用程序脱壳就完成了。在阅读代码时,我有两个问题:
1 )函数为什么指定成
void dumptofile(int argc, const char **argv, const char **envp, const char **apple, struct ProgramVars *pvars) 类型?
后来发现实际上这是 __attribute__((constructor)) 固定的函数类型, 5 个参数分别代表了 ( 参数个数,参数列表,环境变量,可执行程序路径,文件信息 ) 。
2 )如何获取应用在磁盘的路径?
argv[0] ,也就是参数列表的第一个,代表的是可执行文件的路径。这与 main 函数类似。通过 apple 也可以获取到文件路径, dumpdecrypted 使用的是 argv[0] 。
四、重签名
在脱壳后,只能保证 Mach-O 文件变成可读的,即函数指令和字符信息能暴露出来,但是此时的文件并不能运行。这是由于 apple 除了做代码可读化的加密外,还做了签名验证,从而保证在 iOS 系统中成功运行的程序都是被苹果校验过的,被篡改的或其他的渠道程序不能被加载。因此需要对砸壳后的文件进行重签名。
1) 签名的作用
在应用 ipa 内,存在多处签名,不同的签名有不同的作用。但是这些签名整体目的只有一个:所有安装和运行的 APP 必须是苹果允许的。也就是说,在安装时 iOS 会验证一些文件的签名,在启动时 iOS 系统也会验证一部分文件的签名。
2) 签名文件
从 App Store 下载的应用验证最简单,只要 iOS 系统用公钥验证 APP 在 App Store 后台用私钥生成的签名即可。但是我们开发过程中的真机调试是如何进行签名验证呢?首先来看下面这个流程图(图片摘自 http://blog.cnbang.net/tech/3386/ )
签名的秘钥一共有两对,针对这些步骤我们来一步步解释这些步骤在什么时候操作的,如何操作的以及形式是什么。
首先,两对秘钥中, App Store 的私钥和 iOS 系统内部的公钥我们接触不到,因此不做解释。但是 Mac 中的公钥和私钥我们确实使用过。
MAC 公钥 :公钥即是我们在钥匙串中申请的 .certSigningRequest 文件。
MAC 私钥 :在申请 certSigningRequest 文件文件时生成的配对的私钥,保存在本地电脑中。
证书生成 :证书生成对应图中步骤 3 ,我们将 MAC 的公钥上传到苹果后台通过苹果的私钥进行签名,签名后生成的文件即是开发者证书。
描述文件 :由于苹果要限制安装的设备、安装的 APP 以及所具备的权限(如推送),苹果将这些信息连同证书合并再签名得到的文件就是描述文件。描述文件在开发阶段存放在 APP 包内,文件名为 embedded.mobileprovision 。至此,我们可以知道已经存在两处签名了, 1 是苹果对本地公钥的签名, 2 是对证书描述文件的签名,这两处签名都是 App Store 的私钥进行签名的。
在通过 Xcode 打包时, Xcode 会通过本地私钥对 APP 进行签名,这个签名上图中表现出一部分,实际上签名有两处:一处是对资源进行签名,也就是说 ipa 内所有的资源文件包括 xib 、 png 等都需要进行签名,签名存放在 code signature 中。另一处签名是针对代码的签名(这个签名不是加密壳), ipa 内的 Mach-O 文件的 code signature 存放着打包时的签名信息。
3 、验证流程
有了这么多的签名,那么这些签名是在什么时候进行验证的呢?验证分两个步骤进行,分别是安装时验证和启动时验证。
1 )安装时验证
在安装时, iOS 系统会取出 code signature 验证各个资源文件的签名。如果资源文件都验证通过,那么取出 embedded.mobileprovision ,验证设备 ID ,如果该设备在设备列表中并且相符,那么安装成功。但是 INHOUSE 版本和 App Store 版的 APP 不需要验证 embedded.mobileprovision 。(因为不存在这个文件,这是由于发布市场不需要放开验证权限,与你的 Mac 和 iPhone 无关,所以也就不需要你的公钥)
2 )启动时验证
验证 bundle id 与 embedded.mobileprovision 中的 APPID 是否一致,验证 entitlements 与 embedded.mobileprovision 的 entitlements 是否一致。如果一致则尝试将执行可执行程序。在 iOS 内核执行 execve 函数调用 Mach-O 可执行文件之前,会先获取 Mach-O 的 code signature 。那么 code signature 里到底存的啥?可以通过 codesign -dvvvvv 查看 Mach-O 的 code signature ,里面存的都是签名信息。
五、 iOS 应用包扫描
在我们 ipa 包提交到苹果审核后,苹果会通过代码扫描我们应用程序所使用到的 API 。那么苹果根据我们提交的应用包,能扫描到什么内容呢?
1 、示例
符号信息在打包时存储在两个 Mach-O 文件中: 1 、可执行程序。 2 、 DSYM 文件。可执行程序中存在类相关信息及动态链接相关符号。 DSYM 是在打包时从可执行文件中剥离出来的 Mach-O 文件,包含静态链接相关符号、代码路径等完备信息。如果打包时不选用苹果自带的崩溃统计工具, DSYM 只上传给 buggly 使用。苹果所能扫描的只有资源文件以及可执行程序。但是除了可执行程序除了符号信息外,还包含其他信息。
1 )扫描类信息
类关键信息包括类名、方法名、方法描述(参数、返回值类型等)、类是否被使用、方法是否被使用。
从上图中我们可以看出 APP 中有个 KFYGoodDetailsViewController 这个类。
我们还能知道代码中包含 changeStarForegrandViewWithPoint: 方法。
我们还能拿到所有函数的描述
可以知道函数的返回值类型是什么,参数类型是什么,参数有多少,但是参数的命名获取不到( NSString* ) name ,这个 name 获取不到。
还能知道有哪些类被使用过,包括系统的类已经自己的声明的类。但是通过 XIB 绑定的类不会被加入到 classref 。字符串动态调用的类也不被加入。
2 )扫描动态链接符号
动态链接符号包括动态库的函数、变量、私有函数。
扫描符号可以通过 nm 命令快速扫描输出到文件
U 代表是未定义符号(动态库中的函数),而 T 表示的是符号定义在 Text 段(自己写的函数)。
3 )扫描字符串
字符串包括: OC 字符串和 C 字符串
使用到的 @"%.2f" , @“backgroundStar” 等
六、总结
Mach-O 文件的作用其实跟打孔纸带的作用是一样的,只不过 Mach-O 文件描述的内容更加丰富。除了代码和数据外, Mach-O 还包含了加密、验证这样的机制,使得代码更加安全。
参考: http://blog.cnbang.net/tech/3386/
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Getting Real
Jason Fried、Heinemeier David Hansson、Matthew Linderman / 37signals / 2009-11-18 / USD 24.99
Getting Real details the business, design, programming, and marketing principles of 37signals. The book is packed with keep-it-simple insights, contrarian points of view, and unconventional approaches......一起来看看 《Getting Real》 这本书的介绍吧!