

iOS逆向(11)-砸壳原理剖析,主动加载所有framework
source link: https://juejin.cn/post/6844904006024691726
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

iOS逆向(11)-砸壳原理剖析,主动加载所有framework
在平时的日常工作过程中,时常会有道友私聊我,问我能不能帮他对xxAPP砸个壳这样的场景。所以如何简单快速的对APP砸壳自然尤为重要,本文将会讲述如何实现一键砸壳,做到无痛无瘙痒。
当然按照本系列的惯例,同样会讲述砸壳原理,砸壳遇到的问题如何解决等等。
先看最终效果:
在阅读这篇文章之前,笔者默认读者已经了解如何进入远程连接手机,如果实在不了解请阅读iOS逆向(10)-越狱!越狱原理!远程连接登录手机
在了解如何砸壳之前,需要知道到底什么是壳,砸壳的原理又是什么。
开始之前同样的放出福利:
主动加载默认不启动的framework,解决砸壳失败的痛点:DecryptApp
一、什么是壳
绝大多数APP开发者都认为iOS系统很安全,都未对APP进行加固,所以通俗意义的加壳就是在上传App的时候,由Apple对App的加密。
1、验证何时加壳
(1) Build后
首先利用xcode新建一个默认工程EncryptApp
,不用写任何代码,或者随意写点东西。Build
这个工程,成功之后进入工程路径,查看此时是否加壳。
显示上图目录中EncryptApp
的包内容
利用终端查看MachO文件EncryptApp
是否已经被加壳
otool -l EncryptApp | grep crypt
复制代码
可以看到其中的cryptid
字段为0
,即代表该文件未被加密。
(2) 运行到手机中
先查找EncryptApp
在手机中的位置。
话不多说,先利用SSH,将电脑连接上iPhone设备(也能用)。
先确保EncryptApp
在设备上已经正常运行,在利用ps
命令查找当前进程。
ps -ax | grep EncryptApp
复制代码
从上图就可以查看到当前APP在设备上的物理路径。之后进入该路径,将其导出到电脑桌面(推荐使用iFunBox,如果使用iFunBox查看不了设备的根目录,则代表设备权限不够,需要在Cydia中下载对应版本的AFC2插件)。
或者直接使用命令拷贝到桌面
scp -r -P 12345 root@localhost:/var/containers/Bundle/Application/AD8F7993-260F-465D-B207-900F67195658/Youkui4Phone.app ~/Desktop/
复制代码
同样利用otool
命令查看其是否被加密
同样cryptid
字段为0
,即代表该文件未被加密。
(3) 本地打包后
常规操作,利用证书将工程打包成ipa包,并且导出到桌面目录。
同样利用otool
命令查看其是否被加密
同样cryptid
字段为0
,即代表该文件未被加密。
这个地方有两个cryptid
字段代表这个MachO是个多架构可执行文件,分别是arm7,arm64,而他们都未被加壳。
(4) AppStore下载的包
从AppStore下载一个崭新的优酷,运行后,同样利用ps
查看其目录地址,再利用otool
查看是否加密。
同样将其导出到桌面,查看其是否加密
此时cryptid
字段为1
,即代表该文件被加密方案为1的方案加密了。
从以上过程即可得出结论,App加壳是在咱们的ipa上传到苹果后台之后被加密的。
二、怎么砸壳
要真正了解什么是砸壳,先要知道系统在启动一个app的时候做了些什么事情。
1、内核加载MachO过程
在我们点击App的从之前,APP是以MachO和各种资源的形式躺在磁盘了,在点击icon之后,内核开始读取MachO,先查看该MachO是否被加密。
如果没有被加密,则直接交给dyld
加载并且运行。
如果已经被加密,则需要内核对其进行解密,得到解密后的MachO文件,再将其交给dyld
加载并运行。
dyld
的加载过程可以查看文章iOS逆向(5)-不知MachO怎敢说自己懂DYLD
从这步可以看出,一个被加密的MachO是不可能全完被加密的,否则内核无法知道该MachO是否被加密,是是什么类型的文件,是什么架构的文件等等信息(具体有哪些信息可以通过查看MachO的Header段得知,具体方法同样可以点击iOS逆向(5)-不知MachO怎敢说自己懂DYLD查看)。
我们在使用otool -l EncryptApp | grep crypt
命令时得出的记过出了字段cryptid
,还有cryptoff
和cryptsize
字段,这两个字段就代表着该MachO从位置16384(10进制)开始后的103153664(10进制)字节都被加密了。
内核加载加壳后的MachO大致的流程图如下:
2、砸壳过程
(1) 两种砸壳原理
砸壳就就相当于一个解密过程。我们人(下文姑且成为小明同学)相当于内核和内存,而未加壳的MachO相当于明文,加壳后的MachO相当于密文。
例如:小明同学在看到aGVsbG8=
的时候并不知道其代表了什么含义,但他知道这是用base64
加密而来的,所以只需要通过base64
解密就可以知道这其实是hello
的意思。
由于每次启动App的时候都是需要对App进行解密的,所以如果利用非对称对App进行加密/解密效率是非常低的,这会严重影响用户体验,所以App的加密肯定是用的对称加密的方式来加密的。
所以就能知道砸壳(解密)原理是有两种的。
静态砸壳:破解解密整个过程,还原所有代码。
动态砸壳:由于内核运行的是内存中已经被解密的MachO,所以只要将该内存中的MachO直接dump到磁盘即可,就相当于小明同学在通过解密算法解密后看到的明文后,我们直接叫小明同学告诉我们明文是什么一样。
静态解密的方式成本极大,需要逆向整个内核,而Apple对内核加固的非常严,并且就算那到了整个加密过程,只要Apple对加密方式稍作修改,则有需要从头再来,所以这种方式不可取。
而各大厂商的内存格式都是统一的,所以从内存读取数据的方式也是统一的,所以动态砸壳的方式是目前比较理想的方式。
(2) 手动动态砸壳
启动手机内部的LLDB
服务
上文提到了动态砸壳的原理是从内存dump出对应的二进制,那么我们日常开发的时候是用什么调试内存中的App呢?
答案自然是我们熟知的LLDB
我们的Mac是自带LLDB
的,比如:
使用终端,敲入命令LLDB
(笔者lldb有python2的脚本,但系统已经默认是python3,所有有相关报错)
或者在使用xcode的时候,挂起App,App即进入LLDB
调试状态。
这也证明在我们的手机里面是是有LLDB
的对应服务的。
在购买一台新的手机的时候,手机上本来是没有这个服务的,当我的连上xcode,第一次联调App的时候,xcode将手机系统对应版本的服务Copy到手机中,而这个服务的名称就叫做debugserver
。
在Mac电脑中,iOS 10.3版本的debugserver
的位置位于:
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/10.3
复制代码
../DeveloperDiskImage/usr/bin/debugserver
复制代码
而在手机上,debugserver
的位置位于:
usr/bin
复制代码
我们可以利用md5验证手机内的debugserver
和电脑内的debugserver
是否是同一个东西。
当然结果也有可能是不一样的,因为手机内的
debugserver
有可能是从别的电脑,别的版本的系统里面拷贝进入的,只不过所有LLDB
的指令集都是统一的罢了
总结一下,debugserver
的作用就是连接Mac
上的LLDB
,达到Mac利用LLDB
控制debugserver
,从而debugserver
控制iPhone
的作用。
既然debugserver
是一个服务,那么服务就必然需要开辟一个端口,让远程客户端通过这个端口来连接这个服务。我们可以在Mac连接上手机之后,通过如下命令开启这个端口:
// debugserver *:端接口 -a APP名称/进程号
debugserver *:12346 -a EncryptApp
// 或者
debugserver *:12346 -a 3657
复制代码
12346
端口号可以随便写,但是要注意的是不要跟别的端口号重复既可。
如果不知道APP名称/进程号,可以通过命令ps -ax
查找。
如果知道APP名称或者部分APP名称可以追加grep
命令进行筛选ps -ax | grep EncryptApp
如果有如下报错
error: failed to attach to process named: "" (os/kern) invalid task Exiting. 复制代码
证明,已经该APP已经被
debugserver
给占用了,尝试检查xcode或者终端是否已经连接了手机。
关闭其他占用该App的debugserver
后,重新开启端口,若出现如下信息则代表成功:
之后再开启电脑端的LLDB
模式,并且吸附该端口就可以了。
相关命令如下
// 开启lldb状态
> lldb
// 吸附对应端口
> process connect connect://localhost:12346
// 查看该端口下对应进程的所有image地址,第一个为该App的ASLR
> image list
复制代码
同xcode一样,LLDB
状态下的app是被挂起,无法被操作的。
使用指令c
可以取消挂起App,使用指令exit
可以退出LLDB
状态。更多指令可查看官方指令集
Dump出已加密的二进制
既然开始实操砸壳,那么就肯定需要一个已经加密的App作为对象,我们还是使用老朋友优酷😝。
部分可分为:
1、启动App,开启端口,开启LLDB
状态,吸附该端口
2、查看该MachO加密的部分的位置和加密部分的大小
3、查看该App在内存中的的ASLR
4、利用memory read -force
指令Dump出解密部分的二进制文件
5、利用dd
指令将Dump出的二进制文件重写如原来的MachO文件中
6、更改MachO文件中的cryptid
为0
> otool -l Youkui4Phone | grep crypt
复制代码
cryptoff 16384 // 文件偏移
cryptsize 103153664 // 加密文件大小
cryptid 1 // 已经加密
复制代码
Step 3、查看该App在内存中的的ASLR
> image list
复制代码
ASLR为:0x00000001000c0000
memory read -force
指令Dump出解密部分的二进制文件
// memory read --force --outfile 输出文件的目录名称 --binary --count 加密文件的大小 ASLR+文件偏移
> memory read --force --outfile ./Desktop/decrypted.bin --binary --count 103153664 0x00000001000c0000+16384
复制代码
此时在桌面可以发现一个decrypted.bin
文件。
dd
指令将Dump出的二进制文件重写如原来的MachO文件中
// dd seek=文件偏移地址 bs=单位为1子节 conv=转换方式(保留未被截取部分的内容) if=输入文件地址 of=输出文件地址
> dd seek=16384 bs=1 conv=notrunc if=./decrypted.bin of=./Youkui4Phone.app/Youkui4Phone
复制代码
Step 6、更改MachO文件中的cryptid
为0
此时该MachO其实已经被解密成功了,但是MachO的cryptid
字段还未被更改,所以cryptid
此时还是唯1的。
直接打开MachO,手动更改cryptid字段为0。
验证是否真的成功
利用class-dump的原理,如果砸壳成功,可以导出头文件,如果失败则不能导出头文件。
(3) frida-ios-dump
在真正日常开发中,当然不可能每次砸壳多来这么一遍,既耗时又有耗心力,已经有张总给我们写出了非常简单易用的砸壳工具,配置好就可实现一条命令完成砸壳。具体可查看frida-ios-dump。
原作者发的配置文档: 一条命令完成砸壳
网友写好的详细文档: Frida-ios-dump一键砸壳菜鸡版
已经有非常优秀的配置文档我这就不再复述。
那么是否只要配置好Frida-ios-dump就能无敌天下,在砸壳路上就一帆风顺呢?
答案当然不是的。
三、特殊的壳
记得上面提到的提到的砸壳原理是从内存中dump已经解密的文件,那如果是否有文件在App运行的不再内存中呢?
肯定是有的,比如iOS中常用的动态库framework
,有部分的动态库只有在使用其功能的时候才会内加载到内存中,在使用frida
砸壳的时候这部分的framework
一定是砸壳失败的,砸壳失败在我们对其重签名的时候必然也会失败,在进行分析的时候也就加大了难度(先不讨论非重签名的调试方式),例如:某宝,肥死book等。
使用MonkeyDev对其重签名的时候会报如下错误:
/Users/xxx/Desktop/xxx/TargetApp/xxx.app/Frameworks/xxx.framework/xxx This file is encrypted! please use github.com/AloneMonkey… to decrypt!
看过dyld
源码的同学肯定知道dyld
在接管App启动的时候的步骤:
1、配置环境变量
2、加载共享缓存库
3、实例化主程序
4、加载动态链接库
5、链接主程序
6、加载Load和特定的C++的构造函数方法
7、寻找APP的main函数并调用
自然我们利用dyld
的一些方法就可以获取到当前App所有的framework
。
具体代码可见libAllFramework
使用方法:
1、下载好工程,Bulid工程
2、将Build成功的framework拷贝到手机的Home目录
> scp -r -P 12345 libAllFramework.framework root@localhost:~/
复制代码
这里需要注意Build的环境应该是真机环境,并且注意framework的权限
3、利用DYLD_INSERT_LIBRARIES
执行libAllFramework
// 查看Facebook目录地址
> ps -ax | grep Facebook
// 插入我们的动态库
> DYLD_INSERT_LIBRARIES=libAllFramework.framework/libAllFramework /var/containers/Bundle/Application/B4217EF2-9F16-453F-B1DE-9EC5D0E69B60/Facebook.app/Facebook
复制代码
可以在看到绝大多数的动态库已经被正确的加载了,只有一个名为FBCardIOSDKWrapperFramework
的动态库加载失败,笔者至今没有找到原因为什么会失败,如果有了解的道友请告知一下,万分感谢。
但这并不影响大局,因为我们只需将FBCardIOSDKWrapperFramework
从源文件中删除即可。再次对其砸壳后,重签名即可成功。
不想下载源码的同学也可以直接下载笔者打包好的framework(arm64架构),直接Copy入手机。
本文讲述了在iOS系统中什么是传统意义上的壳,也就是对App进行对称加密。Apple是在什么时候对其加密的。砸壳原理,实操手动砸壳,利用工具砸壳,以及最后举例了一个砸壳会遇到的一些坑。
希望在日后的工作中砸壳将是一种轻松加愉快的事情😸
Recommend
-
52
-
15
iOS冰与火之歌番外篇 - App Hook答疑以及iOS 9砸壳 蒸米
-
13
JAVA逆向&反混淆-追查Burpsuite的破解原理 lxj616
-
52
背景 这篇文章只是个人记录,是如何一步一步从越狱一部手机到反编译一个APP,故不作为教材文章,仅供参考。 第一步、越狱 关于越狱看
-
31
今天我们将继续讲解逆向开发工程另一个重要内容--Hook原理讲解。Hook,可以中文译为“挂钩”或者“钩子”,逆向开发中改变程序运行的一种技术。按照如下过程进行讲解 Hook概述 Hook技...
-
15
iOS dumpdecrypted 砸壳 https://cydia-app.com/downloader/ https://unc0ver.dev/ https://github.com/stefanesser/dumpdecrypted cd code/GitHub/dumpdecrypted/ scp -P 2222 /Users/zhangruquan/cod...
-
104
砸壳这件破事 Original...
-
7
类加载运行全过程package com.tuling.jvm; public class Math { public static final int initData = 666; public static User user = new User(); public int compute() { //一个方法对应一块栈帧内存区域 int a = 1;...
-
24
📢📢📢📢📢📢 💗 你正在阅读 【梦想橡皮擦】 的博客 👍 阅读完毕,可以点点小手赞一下 🌻 发现错误,直接评论区中指正吧 📆 橡皮擦的第 <font color=red>621</font> 篇原创博客
-
5
IOS逆向-恢复Dyld的内存加载方式 作者:蚁景科技 2023-02-08 08:12:15 通过分析可以发现,代码并不是真正的发生了 "新 "的变化。这段代码一直存在于dyld3中,只不过是现在macOS也决定使用这段代码路径。所以我们知...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK