前言
本篇文章给大家演示一下,如何在越狱机
上进行第三方App
的调试。期间会利用一些很常用的动态调试的工具,例如Reveal
、Cycript
、lldb
等。
一、Reveal
首先给大家介绍Reveal
,它是一款UI调试
神器,对iOS逆向
开发非常有帮助。这里使用Version 4(8796)
版本。
Reveal官网直接下载安装,可以用试用版。
1.1安装
1.1.1 Mac端
下载完成后,打开,输入邮箱,接收试用key
去到邮箱,接收并输入试用key
主界面
1.1.2 手机端
打开Cydia
,安装Reveal Loader
插件
1.2 使用
导入dylib
文件
- 在手机上,进入/Library,创建RHRevealLoader目录
mkdir RHRevealLoader
- 在Mac电脑上,打开Reveal
找到RevealServer
路径
- 打开终端,将
RevealServer
拷贝到手机的/Library/RHRevealLoader
目录下,重命名为libReveal.dylib
scp -P 12345 ./RevealServer root@localhost:/Library/RHRevealLoader/libReveal.dylib
开启允许调试的应用
- 打开设置,找到
Reveal
选项
- 开启允许调试的应用,例如
WeChat
使用Reveal进行UI调式
在Mac电脑上,打开Reveal软件。手机上重新启动
WeChat
。在电脑的Reveal中,出现两个WeChat,分别是WiFi连接和USB连接。
- 点击USB连接的WeChat,可进行UI调式,并且不会阻塞WeChat的进程
至此,我们就能在上面很直接的看到微信的UI层级了,使用起来XCode
的Debug View
相似度很高。
二、debugserver
2.1 lldb附加
接下来,我们看看在越狱环境
中,是如何使用Xcode
利用lldb
进行进程附加
的。
- 打开
Xcode
,随意打开一个项目,空工程
也可以 - 选择
真机
调试,在Debug
菜单中,选择Attach to Process
,选择WeChat
进程
显示Running
,表示附加成功
- 使用
lldb
将应用暂停
- 使用
Debug View
进行UI调试
2.2 lldb原理
上面之所以能通过lldb
进行进程附加
,调试手机中的应用,是因为手机中的debugserver
开启了相关服务。
-
Xcode
中有lldb
给手机中的debugserver
发送指令。 - 手机中的
debugserver
会附加App
,读App
中的内容做一系列操作。 -
debugserver
将读取到的结果给到lldb
显示。
在越狱环境中,我们只需要开启debugserver
服务,就可以利用lldb
远程调试三方应用了。
2.3 探索debugserver
Mac端
- 找到Mac电脑中的
debugserver
,进入以下目录
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport
- 可以找到不同iOS系统版本,所对应的镜像文件
- 进入设备对应的系统目录,找到
dmg
文件(我的设备是12.1.2
版本)
- 打开
dmg
文件,进入usr/bin
目录可以看到debugserver
。这就是Xcode
安装到真机
中的文件
手机端
在手机系统中,也存在一个debugserver
。当Xcode
第一次连接手机,就会将对应版本的debugserver
安装到手机系统中。
进入手机的/Developer/usr/bin
目录下
2.4 copy debugserver
- 将
手机
中的debugserver
拷贝到Mac电脑
中
scp -P 12345 root@localhost:/Developer/usr/bin/debugserver ./
- 将拷贝后的
debugserver
生成md5值
md5 debugserver
- 找到Mac电脑中的
debugserver
cd /Volumes/DeveloperDiskImage/usr/bin
- 将Mac电脑中的
debugserver
生成md5值
md5 debugserver
上图可见,手机端和Mac端的debugserver
文件的Hash一致,说明手机中的debugserver
就是Mac电脑中指定系统目录下的debugserver
。
2.5 USB启动debugserver
2.5.1 iPhone中开启debugserver服务
Mac
电脑中的lldb
连接手机上的debugserver
,需要配置IP和端口号
。
在手机中,查看debugserver
命令
./debugserver
-------------------------
debugserver-@(#)PROGRAM:LLDB PROJECT:lldb-900.3.87
for arm64.
Usage:
debugserver host:port [program-name program-arg1 program-arg2 ...]
debugserver /path/file [program-name program-arg1 program-arg2 ...]
debugserver host:port --attach=
debugserver /path/file --attach=
debugserver host:port --attach=
debugserver /path/file --attach=
debugserver 主机地址:端口号 –a 应用进程
- 由于主机地址是当前手机,可以使用
localhost
代替 - 端口号 启动
server
服务开放端口,让远程的lldb
通过sever
调试进程
2.5.2 附加WeChat应用
接下来,我们来使用手机上的debugserver
,进行附加WeChat
应用。
- 找到
WeChat
进程
- 使用
debugserver
附加WeChat
应用
遇到错误
Failed to get connection from a remote gdb process.
解决方法 使用
ldid
对debugserver
配置权限
- 进入手机中
debugserver
拷贝到Mac电脑的目录(上面执行过) - 导出
debugserver
的权限
ldid -e debugserver > debugserver.entitlements
我导出来的
entitlements
中,有2个plist,因为在iOS 12
之后debugserver
包含两个架构arm64
和arm64e
。
我们可以拆分
架构生成重签对应架构的debugserver(当然不拆也没有问题,不拆plist中两份配置都要改,我选择的不拆)
lipo -thin arm64 debugserver -output debugserver_arm64
- 删除
三项
权限- seatbelt-profiles
- com.apple.security.network.server
- com.apple.security.network.client
- 添加
四项
权限
task_for_pid-allow
get-task-allow
platform-application
run-unsigned-code
那么,修改后的debugserver.entitlements
文件
com.apple.springboard.debugapplications
com.apple.backboardd.launchapplications
com.apple.backboardd.debugapplications
com.apple.frontboard.launchapplications
com.apple.frontboard.debugapplications
com.apple.diagnosticd.diagnostic
com.apple.private.memorystatus
com.apple.private.cs.debugger
platform-application
get-task-allow
task_for_pid-allow
run-unsigned-code
com.apple.system-task-ports
com.apple.springboard.debugapplications
com.apple.backboardd.launchapplications
com.apple.backboardd.debugapplications
com.apple.frontboard.launchapplications
com.apple.frontboard.debugapplications
com.apple.diagnosticd.diagnostic
com.apple.private.memorystatus
com.apple.private.cs.debugger
platform-application
get-task-allow
task_for_pid-allow
run-unsigned-code
com.apple.system-task-ports
- 导入权限文件到
debugserver
ldid -Sdebugserver.entitlements debugserver
回到之前的附加流程
- 接下来就是将
debugserver
拷贝回手机
⚠️注意:手机中的
/Developer/usr/bin
目录,有权限问题
,不能直接拷贝。
那么,就拷贝到手机的/usr/bin
目录,拷贝后可全局使用
scp -P 12345 ./debugserver root@localhost:/usr/bin/debugserver
- 接着,在手机端上找到
WeChat
进程
ps -A | grep WeChat
- 最后,使用
debugserver
,再次附加WeChat
应用
debugserver localhost:12346 -a 11656
-------------------------
debugserver-@(#)PROGRAM:LLDB PROJECT:lldb-900.3.87
for arm64.
Attaching to process 11656...
Listening to port 12346 for a connection from localhost...
- 使用
lldb
连接debugserver
- 在Mac电脑上,进入lldb环境
lldb
- 连接
debugserver
process connect connect://10.165.45.19:12346
错误 error: Failed to connect port
我们使用USB
端口映射,修改usbConnect.sh
脚本,增加12346
的端口映射
python /Users/aronm1/python-client/tcprelay.py -t 22:12345 12346:12346
使用USB连接
- 手机上使用
debugserver
,附加WeChat
应用
./debugserver localhost:12346 -a 11733
再一次,Mac电脑上,进入lldb
环境
lldb
使用lldb连接debugserver
process connect connect://localhost:12346
-------------------------
Process 11733 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
frame #0: 0x00000001a3e740f4 libsystem_kernel.dylib`mach_msg_trap + 8
libsystem_kernel.dylib`mach_msg_trap:
-> 0x1a3e740f4 <+8>: ret
libsystem_kernel.dylib`mach_msg_overwrite_trap:
0x1a3e740f8 <+0>: mov x16, #-0x20
0x1a3e740fc <+4>: svc #0x80
0x1a3e74100 <+8>: ret
Target 0: (WeChat) stopped.
连接成功,输入c
,继续运行
c
-------------------------
Process 11733 resuming
输入process interrupt,暂停
process interrupt
使用command + w
,停止
WeChat附加,但不杀掉应用
。
三、class-dump
class-dump
之前文章就使用过了,它是一个命令行工具,最高版本为class-dump 3.5 (64 bit)
,已经停止更新
。
查看class-dump
的路径
which class-dump
上图可见,来自MonkeyDev
框架。
3.1 class-dump的使用
在MonkeyDev中,class-dump如何使用?
- 搭建MonkeyDev项目
- 在
Build Settings
中,将MONKEYDEV_CLASS_DUMP
默认的NO
修改为YES
- 编译项目,主工程下生成
Headers
目录,自动导出头文件(以微信
为例)
⚠️注意:工程目录下不要包含
中文
,否则Headers
目录以及头文件无法生成。
四、命令行工具
接下来,给大家演示一下 搭建自定义的命令行工具。
- 创建App项目,命名
FuncDemo
- 打开
main.m
文件,写入以下代码
#import
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
for (int intIndex=0; intIndex
- 编译项目,将
MachO
文件拷贝到手机上
需要将
MachO
文件copy
出来
scp -P 12345 ./FuncDemo root@localhost:~/
- USB连接手机设备
usb-iphone8.sh
- 使用自定义命令行工具
./FuncDemo -v
-------------------------
参数0:./FuncDemo
参数1:-v
上图可见,参数0
为默认,显示当前MachO
。
五、lldb手动砸壳
砸壳
之前讲过,有很多方式可以进行。逆向分析一个应用,第一步就是对应用砸壳
。
例如,查看WeChat
的crypt
信息
otool -l WeChat | grep crypt
-------------------------
cryptoff 16384
cryptsize 187236352
cryptid 1
-
cryptid
为0
表示应用已砸壳 -
cryptoff
表示开始加密的偏移位置 -
cryptsize
表示加密长度
将应用
砸壳
后,才能使用class-dump
导出头文件。
5.1 查看手机中的MachO
-
USB
连接手机设备,找到WeChat
的沙盒路径
ps -A | grep WeChat
-------------------------
11783 ?? 0:52.19 /var/containers/Bundle/Application/B9046860-DDDA-44B4-AFF5-AF20FFA6FC9D/WeChat.app/WeChat
- 将
WeChat
拷贝到Mac电脑
scp -P 12345 root@localhost:/var/containers/Bundle/Application/B9046860-DDDA-44B4-AFF5-AF20FFA6FC9D/WeChat.app/WeChat ./
- 查看
MachO
文件中的crypt
信息
otool -l WeChat | grep crypt
5.2 方式一:修改cryptid砸壳
使用
MachOView
打开WeChat
在
Load Commands
中,找到LC_ENCRYPTION_INFO_64
,修改Crypt ID
为0
- 使用
class-dump
导出头文件
class-dump -H WeChat -o ./header
⚠️注意:建议千万别试,会不停的循环输出
乱码
!
所以,仅修改cryptid,无法导出头文件。因此砸壳的关键,并不是cryptid
,而是将加密的代码段
进行解密
。
5.3 方式二:lldb手动砸壳
接下来,我们尝试lldb
手动砸壳,我们知道
砸壳的逻辑,是从内存中读取
cryptoff位置
到cryptsize长度
的数据,然后将其覆盖
原始MachO文件。
- 使用
Xcode
打开工程,选择真机设备
,附加WeChat进程
-
image list
获取MachO的首地址
(lldb) image list
[ 0] EB606691-98E6-384F-BABB-F46E7BC265F9 0x0000000102378000 /var/containers/Bundle/Application/B9046860-DDDA-44B4-AFF5-AF20FFA6FC9D/WeChat.app/WeChat (0x0000000102378000)
首地址是 0x0000000102378000
- 从内存中,将加密部分的代码段,导出到
WeChat.bin
文件。因为已读取到内存中,相当于已解密。
memory read --force --outfile ~/Downloads/WeChat.bin --binary --count 187236352 0x0000000102378000+16384
代码段加密的开始位置
MachO首地址 + 加密偏移地址
- 将
WeChat.bin
文件,复制到WeChat
MachO文件相同的目录,然后将其写入到MachO
文件中相同位置,相当于用解密后的数据,覆盖原始的加密数据
dd seek=16384 bs=1 conv=notrunc if=./WeChat.bin of=WeChat
-
seek
从输出文件开头跳过x
个块后再开始复制 -
bs
同时设置读入/输出
的块大小为x
个字节 -
conv=notrunc
不截断输出文件 -
if
输入
文件名,默认为标准输入。即指定源文件
-
o
f输出
文件名,默认为标准输出。即指定目的文件
耗时比较久,共计500多秒。接着对WeChat
MachO文件的cryptid
修改为0,可以成功导出头文件
六、Tweak屏蔽Badge红点气泡
接下来,我们创建Tweak插件,来屏蔽应用的红点气泡
实现该功能的前提是需要附加系统的桌面程序
SpringBoard
。
- USB连接手机,找到
SpringBoard
进程
- 将
SpringBoard
拷贝到Mac电脑
scp -P 12345 [email protected]:/System/Library/CoreServices/SpringBoard.app/SpringBoard ./
- 查看
SpringBoard
的MachO
文件中的crypt
信息
上图可见,MachO
中没有加密信息,说明SpringBoard
原本就没有加壳
。
- 既然没有加壳,我们尝试使用
class-dump
导出头文件
class-dump -H SpringBoard -o ./header
果然,可以dump出头文件,所以SpringBoard
是没有加壳
的。
- 动态调试,定位找出红点气泡相关的类。
现在我们已知的动态调试有3种方式
-
Reveal
SpringBoard
无法显示在Mac端的Reveal
客户端中,不可用! -
Cycript
采用附加SpringBoard
进程的方式,然后通过cy指令
查找定位红点UI,需要手动搜索,很不直观,不建议使用! -
lldb
使用lldb附加SpringBoard
进程,然后通过Debug View
找到红点对象,很直接,建议使用!
我们定位到红点对象的类 SBIconParallaxBadgeView
。
- 验证是否为
SBIconParallaxBadgeView
- usb连接手机,进入cy环境
- 导入自定义
cy
脚本
- 打印当前vc视图层级
cy# currentVC()
#""
cy# #0x10329c780.view.recursiveDescription().toString()
- 在结果中搜索
SBIconParallaxBadgeView
结果只有1处,因为当前页面只有一处红点气泡。
=; =; }; layer = >\n
- 将其设置为隐藏
#0x112192f20.hidden=YES
然后手机上的红点气泡没了
- 接下来我们通过
Hook
的方式,实现隐藏气泡
。
- 在导出的头文件中,找到
SBIconParallaxBadgeView.h
文件
- 对
SBIconParallaxBadgeView
的init
方法进行HOOK
,破坏它,即可隐藏红点气泡
七、Tweak插件实现隐藏气泡
要实现对SBIconParallaxBadgeView
的init
方法进行HOOK
,需要搭建Tweak
插件。
- 使用
nic.pl→15
,创建Tweak插件
- 在
Makefile
文件中,增加IP和端口
小技巧:配置到环境变量,一劳永逸。
vim ~/.zshrc
export THEOS_DEVICE_IP=localhost
export THEOS_DEVICE_PORT=12345
source ~/.zshrc
- 打开
Tweak.x
文件,写入以下代码
%hook SBIconParallaxBadgeView
- (id)init {
return nil;
}
%end
- 编译、打包、安装插件
cd badgedemo
make
make package;make install
⚠️注意:安装这一步需要USB连接手机设备!
安装完成后,手机会重启桌面,再次进入桌面时,气泡全部消失。同时,在Cydia
中会显示已安装的BadgeDemo
插件
八、MonkeyDev搭建Tweak插件
最后,我们使用MonkeyDev
插件,来创建Tweak插件
。
- 新建
Logos Tweak
项目
项目名称 BadgeMonkeyDemo
- 项目结构
-
BadgeMonkeyDemo.xm
代码 -
control
配置信息,版本号、作者名称等 -
BadgeMonkeyDemo.plist
附加应用的包名称等信息
- 在
Build Settings
中,搜索Monkey
,找到Tweak
的设置
有以下自定义的配置项
-
MonkeyDevBuildPackageOnAnyBuild
每次编译时打包 -
MonkeyDevClearUiCacheOnInstall
安装时清除缓存 -
MonkeyDevCopyOnBuild
编译时拷贝包到目录 -
MonkeyDevDeviceIP
设备IP
-
MonkeyDevDevicePassword
设备密码 -
MonkeyDevDevicePort
设备端口 -
MonkeyDevInstallOnAnyBuild
每次编译时安装 -
MonkeyDevkillProcessOnInstall
安装成功后杀掉的进程
- 和写Tweak插件时一样,设置IP和端口,同样将这两项配置在环境变量中
export MonkeyDevDeviceIP=localhost
export MonkeyDevDevicePort=12345
- 同样,打开BadgeTweakDemo.xm文件,写入以下代码
#import
%hook SBIconParallaxBadgeView
- (id)init {
return nil;
}
%end
- 在
Build Settings
中,搜索signing
设置签名
Code Signing Identity
设置为iOS Developer
- 编译项目
如果报错
解决 按目录找到CydiaSubstrate.tbd
文件,删除里面的i386和x86_64
再次run项目,成功安装Tweak
插件,红点气泡全部隐藏。
总结
-
Reveal
-
iOS
安装插件 -
Mac
安装客户端App - 将动态库导入
iPhone
-
-
USB
启动debugserver
终端附加
◦ 手机,使用debugserver 主机名称:端口 -a 进程id
◦Mac
电脑,启动lldb,使用process connect connect://主机名称:端口
◦USB
端口映射Xcode
附加
◦ 打开工程
◦ 选择设备
◦ 附加进程(菜单栏Debug->Attach to process ->选择进程
)
-
debugserver
权限问题- 导出权限文件,查看文件
◦ldid -e debugserver > debugserver.entitlements
- 删除权限
◦seatbelt-profiles
◦com.apple.security.network.server
◦com.apple.security.network.client
- 添加权限
◦task_for_pid-allow
设置为YES
◦get-task-allow
设置为YES
◦platform-application
设置为YES
◦run-unsigned-code
设置为YES - 导入权限文件
◦ldid -Sdebugserver.entitlements debugserver
- 导出权限文件,查看文件
-
class-dump
class-dump -H MachO文件路径 -o 头文件路径
-
MonkeyDev
中,可以快速使用class-dump
-
命令行工具
-
argc
:参数个数 -
argv
:参数数组
-
-
lldb手动砸壳
- memory read命令
◦ 通过--outfile
参数,导出文件
◦ 通过--count
参数,指定导出的大小 -
dd
命令
◦ 写入源文件
◦seek
指定偏移,也就是跳过多少开始写入
◦conv
保留没有替换的部分
- memory read命令
-
Tweak修改系统行为
-
Reveal
无法使用,在手机设置页的Reveal
选项中,没有SpringBoard
应用 -
Cycript
可以使用,但定位UI
不直观 -
lldb
可以使用,最简单的方式
-
-
MonkeyDev
搭建Tweak
插件- 在
Build Settings
中,配置参数 - 设置签名
- 编译项目并安装插件
- 在