[翻译]AppStore App二进制砸壳流程揭秘


#1

原文地址 https://ubrigens.com/posts/demystifying_app_cracking.html
感觉很多新入逆向坑的同学缺乏一些底层的知识,随手翻到这玩意儿正好翻译一哈。晚点应该我再会写一篇手把手教新手如何自己写代码解密的文章。

iOS的app是作为后缀为.ipa的包裹进行分发的. 这些包裹只不过是修改了后缀名的zip压缩包, 并且可以轻易的被合适的工具解包. 解压出的文件目录结构对我们来说用处不大并且已经在其他地方得到了很详细的解释 详见附录[1,2]

对于一个■■者来说这其中最有价值的文件就是可执行文件, 可执行文件的路径可以通过查看Info.plist获得, 更准确地说,是键值CFBundleExecutable下的文本. 今天,绝大多数这些可执行文件都包含多个架构的代码,这种可执行文件被称之为胖二进制(Fat Binary),得名于它们包含诸如ARMv7, ARMv7s以及ARM64 (又被称之为ARMv8)等多个架构的代码。 Mac上使用同样的概念,区别是包含的架构一般是X86和X86_64

在运行时,动态链接器 (几乎永远是 dyld)[译者注:一个特定的LoadCommand可以指定动态链接器的路径,但这并不在本文的范围内] 会检查二进制并找出最适合运行的架构(译注:下文每个架构又被称之为一片,Slice), 处理所有的加载指令并最终开始执行可执行文件. 更多关于可执行文件格式 - MachO - 以及不同类型加载指令的信息可以在Apple官网上找到,参见附录3 [3]. 以下是这个过程对于iPhone5图形化之后的结果:

blackbox_decryption
大体上,OS内核可以被当作是一个自动解密可执行文件在当前硬件上运行效果最好的部分的黑箱。在这个例子中我们使用posix_spawn来运行可执行文件,但实际上任何其他的类似的函数都可以. 为了简化这张图表,启动后内存中只有解密的部分这一事实被忽略了。

在iOS上所有的第三方程序都必须由一个合法的Developer ID签名.代码签名也是通过一个加载指令来指出的,就在MachO头部(译注:此处指单个架构的MachO而非一开始提到的Fat Binary)之后。所以每个架构都有自己的代码签名,而不是整个胖二进制共享一个代码签名。 代码签名是由内核进行验证的,并且没有合法代码签名的进程将会收到一个来自内核的信号量使得他们完全无法被运。.在非越狱设备上,二进制(译注:指代码签名)的完整性是由内核负责确保的,而在越狱设备上一个app的大多数内容都是被允许改变的,因为至关重要的安全机制已经被越狱关闭了。 即便如此,可执行文件仍然需要一个代码签名才能正常运行,由一个名为ldid的工具生成的假签名就足够了

常见的解密工具,例如Clutch [4] ,使用一种并不优美的方式来解密尽可能多的架构。假设一个App包含上面提到的所有三个架构,Clutch就会给二进制的头部打三次补丁来分别强制让操作系统运行这三片各一次。 所以很显然只有当设备的硬件支持这种架构时这一方法才有效,这也意味着一台iPhone 6可以被用来给上面提到的所有三种架构解密,而iPhone 4S只能解密ARMv7部分。

下图是这一过程图形化的表示,再一次的,示例用的设备是iPhone 5.
clutch

在这个例子中,目标二进制包含全部三种架构,因为我们使用的iPhone5有一颗ARMv7s兼容的CPU,正常的情况下只有对应的ARMV7S片会被执行。Clutch 滥用了ARM处理器大体上都向前兼容这一事实,也就是说能运行ARMv7S的设备也能运行ARMV7. 总的来说,Clutch运行了这个App两次,即每个硬件支持的架构一次. 为了强制操作系统运行对应的片,Clutch将其他片的标记都修改成了Intel处理器。

每一片的解密过程的第一步都是用posix_spawn来创建一个新的维持在暂停状态的进程。这是通过给posix_spawn传递Apple独有的标记POSIX_SPAWN_START_SUSPENDED来实现的. 来自App的任何代码都不会被执行, 但是我们所要解密的app的代码已经自动的被操作系统解密完成。接下来,在通过使用task_for_pid获取新的进程的mach_port(译者注:原作者手癌了应该,这里应该是mach_task,在Mach的结构下获取了一个进程的mach_task就相当于获取了这个进程的所有权限,可以任意读写目标进程的内存,task_for_pid需要有get-task-allow等一系列特别的Entitlements), 并将目标进程的内存复制到磁盘,最后Patch可执行文件的一些地方(比如说LC_ENCRYPTION_COMMAND这个加载指令中标记二进制是否被加密的标记位需要被更新) 后就开始处理下一片。如果你对细节感兴趣,可以阅读Clutch源码 [附录4].

最后,所有解密的片被合并在一起。 因为iPhone 5不支持ARM64,所以输出的加密二进制只包含两篇。即使这样,由于ARM64向前兼容的原因,解密的二进制依旧可以正常在iPhone 6上运行 - 虽然可能会稍微慢一点点。

下面这几行跟主题关系不大我懒得翻了
App Thinning results in binaries that only contain one architecture, forcing crackers to use multiple devices to crack each individual slice and then manually combine them to create a version that runs on as many devices as possible. Bitcode on the other hand could allow Apple to create personalized versions of apps, allowing them to trace accounts that distribute cracked versions of an app (fittingly called traitor tracing in the literature). If used, both technologies will hopefully reduce the impact of application cracking on the revenue of iOS developers.

Changelog:

June 17: Fixed date, changed title to better reflect the contents of this article.
September 10: Added disclaimer, added section on impact of App Thinning and Bitcode in iOS 9


#2

另外之前几贴面向新手的LLVM的文章狗神微博推广了下似乎反响还不错。如果有人感兴趣的话我可以接着写下一篇。就是不太清楚读者是更想看Hikari的代码解析这种,还是纯粹的萌新向介绍。


#3

萌新向介绍吧:joy::joy::joy::joy::joy:


#4

目前阶段,这种会更受欢迎


#5

先有张总后有天。期待张总LLVM萌新向文章。


#6

主要我感觉不太适合当老师,这几篇都是自己想一个例子然后再解决这个例子的,感觉比较干


#7

想一个例子再解决它,这个思路挺好的呀!没有人天生就会干嘛的,一边做一边学一边悟,慢慢就会了


#8

:kissing_heart:厉害了张总!