调试一款应用,使用重签名方案,很容易被第三方察觉。在越狱环境中,我们可以在不污染App的情况下,对第三方程序进行动态调试。

Reveal

Reveal是一款UI调试神器,对iOS逆向开发非常有帮助。这里使用Version 4(8796)版本

Mac电脑中,安装Reveal软件
iOS逆向实战--031:越狱调试 - 图1

在手机中,安装Reveal插件

打开Cydia,安装Reveal Loader插件
iOS逆向实战--031:越狱调试 - 图2

导入dylib文件

在手机上,进入/Library,创建RHRevealLoader目录

  1. mkdir RHRevealLoader

Mac电脑上,打开Reveal,找到iOS Library选项
iOS逆向实战--031:越狱调试 - 图3

找到RevealServer路径
iOS逆向实战--031:越狱调试 - 图4

打开终端,将RevealServer拷贝到手机的/Library/RHRevealLoader目录下,重命名为libReveal.dylib

  1. scp -P 12345 ./RevealServer root@localhost:/Library/RHRevealLoader/libReveal.dylib
  2. -------------------------
  3. RevealServer 100% 9100KB 35.5MB/s 00:00

开启允许调试的应用

打开设置,找到Reveal选项
iOS逆向实战--031:越狱调试 - 图5

开启允许调试的应用,例如WeChat
iOS逆向实战--031:越狱调试 - 图6

使用Reveal进行UI调式

Mac电脑上,打开Reveal软件。手机上,重新启动WeChat

在电脑的Reveal中,出现两个WeChat,分别是WiFi连接和USB连接
iOS逆向实战--031:越狱调试 - 图7

点击USB连接的WeChat,可进行UI调式,并且不会阻塞WeChat的进程
iOS逆向实战--031:越狱调试 - 图8

debugserver

在越狱环境中,使用Xcode进行lldb附加

打开Xcode,随意打开一个项目,空工程也可以

选择真机,在Debug菜单中,选择Attach to Process,选择WeChat进程
iOS逆向实战--031:越狱调试 - 图9

显示Running,表示附加成功
iOS逆向实战--031:越狱调试 - 图10

使用lldb将应用暂停
iOS逆向实战--031:越狱调试 - 图11

使用Debug View进行UI调试
iOS逆向实战--031:越狱调试 - 图12

lldb原理

Xcode中的lldb可以调试手机中的应用,是因为手机中的debugserver开启了相关服务
iOS逆向实战--031:越狱调试 - 图13

所以在越狱环境中,我们只需要开启debugserver服务,就可以利用lldb远程调试三方应用了

探索debugserver

找到Mac电脑中的debugserver,进入以下目录:

  1. /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport

可以找到不同iOS系统版本,所对应的镜像文件
iOS逆向实战--031:越狱调试 - 图14

进入设备对应的系统目录,找到dmg文件
iOS逆向实战--031:越狱调试 - 图15

打开dmg文件,进入usr/bin目录可以看到debugserver。这就是Xcode安装到真机中的文件
iOS逆向实战--031:越狱调试 - 图16

手机设备中的debugserver

在手机系统中,已经存在一个debugserver。当Xcode第一次连接手机,就会将对应版本的debugserver安装到手机系统中

进入手机的/Developer/usr/bin目录下
iOS逆向实战--031:越狱调试 - 图17

将手机中的debugserver拷贝到Mac电脑中

  1. scp -P 12345 root@localhost:/Developer/usr/bin/debugserver ./
  2. -------------------------
  3. debugserver 100% 9505KB 33.8MB/s 00:00

将拷贝后的debugserver生成md5

  1. md5 debugserver
  2. -------------------------
  3. MD5 (debugserver) = b771aad8917de2ff41feb5acfe4a9b15

找到Mac电脑中的debugserver

  1. cd /Volumes/DeveloperDiskImage/usr/bin

Mac电脑中的debugserver生成md5

  1. md5 debugserver
  2. -------------------------
  3. MD5 (debugserver) = b771aad8917de2ff41feb5acfe4a9b15

两个文件的Hash一致,说明手机中的debugserver,就是Mac电脑中指定系统目录下的debugserver

USB启动debugserver

iPhone中开启debugserver服务

Mac电脑中的lldb连接手机上的debugserver,需要配置IP和端口号

在手机中,查看debugserver命令

  1. ./debugserver
  2. -------------------------
  3. debugserver-@(#)PROGRAM:LLDB PROJECT:lldb-900.3.87
  4. for arm64.
  5. Usage:
  6. debugserver host:port [program-name program-arg1 program-arg2 ...]
  7. debugserver /path/file [program-name program-arg1 program-arg2 ...]
  8. debugserver host:port --attach=<pid>
  9. debugserver /path/file --attach=<pid>
  10. debugserver host:port --attach=<process_name>
  11. debugserver /path/file --attach=<process_name>
  • debugserver 主机地址:端口号 –a 应用进程
  • 由于主机地址是当前手机,可以使用localhost代替
  • 端口号:启动server服务,开放端口,让远程的lldb通过sever调试进程

使用手机上的debugserver,附加WeChat应用

找到WeChat进程

  1. 9651 ?? 0:08.88 /var/containers/Bundle/Application/454EA887-EB3B-43B3-ABFD-B9B2CA006981/WeChat.app/WeChat

使用debugserver附加WeChat应用

  1. ./debugserver localhost:12346 -a 9651
  2. -------------------------
  3. debugserver-@(#)PROGRAM:LLDB PROJECT:lldb-900.3.87
  4. for arm64.
  5. Attaching to process 9651...
  6. Listening to port 12346 for a connection from localhost...
  7. Failed to get connection from a remote gdb process.
  8. Exiting.

遇到错误:Failed to get connection from a remote gdb process.

解决办法:使用ldiddebugserver配置权限

进入手机中debugserver拷贝到Mac电脑的目录

导出debugserver的权限

  1. ldid -e debugserver > debugserver.entitlements

删除三项权限

  1. seatbelt-profiles
  2. com.apple.security.network.server
  3. com.apple.security.network.client

添加四项权限

  1. <key>task_for_pid-allow</key>
  2. <true/>
  3. <key>get-task-allow</key>
  4. <true/>
  5. <key>platform-application</key>
  6. <true/>
  7. <key>run-unsigned-code</key>
  8. <true/>

修改后的debugserver.entitlements文件
iOS逆向实战--031:越狱调试 - 图18

导入权限文件到debugserver

  1. ldid -Sdebugserver.entitlements debugserver

手机中的/Developer/usr/bin目录,有权限问题,不能直接拷贝

debugserver拷贝到手机的/usr/bin目录,拷贝后可全局使用

  1. scp -P 12345 ./debugserver root@localhost:/usr/bin/debugserver
  2. -------------------------
  3. debugserver 100% 9544KB 35.9MB/s 00:00

找到WeChat进程

  1. ps -A | grep WeChat
  2. -------------------------
  3. 9727 ?? 0:07.27 /var/containers/Bundle/Application/454EA887-EB3B-43B3-ABFD-B9B2CA006981/WeChat.app/WeChat

使用debugserver,附加WeChat应用

  1. debugserver localhost:12346 -a 9727
  2. -------------------------
  3. debugserver-@(#)PROGRAM:LLDB PROJECT:lldb-900.3.87
  4. for arm64.
  5. Attaching to process 9727...
  6. Listening to port 12346 for a connection from localhost...

使用lldb连接debugserver

Mac电脑上,进入lldb环境

  1. lldb

连接debugserver

  1. process connect connect://10.165.45.19:12346

遇到错误:error: Failed to connect port

解决办法:使用USB端口映射

修改usbConnect.sh脚本

  1. python /Users/zang/Zang/Tools/python-client/tcprelay.py -t 22:12345 12346:12346
  • 增加12346的端口映射

使用USB连接

  1. usbConnect.sh
  2. -------------------------
  3. Forwarding local port 12345 to remote port 22
  4. Forwarding local port 12346 to remote port 12346

手机上,使用debugserver,附加WeChat应用

  1. ./debugserver localhost:12346 -a 9727

Mac电脑上,进入lldb环境

  1. lldb

使用lldb连接debugserver

```

process connect connect://localhost:12346

Process 9752 stopped

  • thread #1, queue = ‘com.apple.main-thread’, stop reason = signal SIGSTOP frame #0: 0x00000001a3e740f4 libsystem_kernel.dylibmach_msg_trap + 8 libsystem_kernel.dylibmach_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.

  1. > 连接成功,输入`c`,继续运行
  2. >

c

Process 9752 resuming

  1. > 输入`process interrupt`,暂停
  2. >

process interrupt

  1. > 使用`command + w`,停止`WeChat`附加,但不杀掉应用
  2. #####class-dump
  3. > `class-dump`是一个命令行工具,最高版本为`class-dump 3.5 (64 bit)`,已经停止更新
  4. > 查看`class-dump`的路径
  5. >

which class-dump

/Users/zang/Zang/Tools/MonkeyDev/bin/class-dump

  1. > - 来自`MonkeyDev`框架
  2. > `MonkeyDev`中,`class-dump`的使用
  3. > 搭建`MonkeyDev`项目
  4. > `Build Settings`中,将`MONKEYDEV_CLASS_DUMP`默认为`NO`
  5. > 将其修改为`YES`<br />
  6. ![](https://upload-images.jianshu.io/upload_images/9297953-73e652514976f5c6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240#alt=)
  7. > 编译项目,主工程下生成`Headers`目录,自动导出头文件<br />
  8. ![](https://upload-images.jianshu.io/upload_images/9297953-e0a2ca837356cea9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240#alt=)
  9. > 工程目录下不要包含中文,否则`Headers`目录以及头文件无法生成
  10. #####命令行工具
  11. > 搭建自定义的命令行工具
  12. > 创建`App`项目,命名`FuncDemo`
  13. > 打开`main.m`文件,写入以下代码:
  14. >

import

import “AppDelegate.h”

int main(int argc, char * argv[]) {

for (int intIndex=0; intIndex<argc; intIndex++) { printf(“参数%i:%s\n”,intIndex, argv[intIndex]); }

return 0; }

  1. > - `argc`:参数个数
  2. > - `argv`:参数数组
  3. > 编译项目,将`MachO`文件,拷贝到手机上
  4. >

scp -P 12345 ./FuncDemo root@localhost:~/

FuncDemo 100% 150KB 5.9MB/s 00:00

  1. > `USB`连接手机设备
  2. >

usb-6p.sh

  1. > 使用自定义命令行工具
  2. >

./FuncDemo -v

参数0:./FuncDemo 参数1:-v

  1. > - `参数0`为默认,显示当前`MachO`
  2. #####lldb手动砸壳
  3. > 逆向分析一个应用,第一步就是应用砸壳
  4. > 查看`MachO`文件中的`crypt`信息
  5. >

otool -l WeChat | grep crypt

  1. cryptoff 16384

cryptsize 178356224 cryptid 0

  1. > - `cryptid`:为`0`表示应用已砸壳
  2. > - `cryptoff`:表示开始加密的偏移位置
  3. > - `cryptsize`:表示加密长度
  4. > 将应用砸壳后,才能使用`class-dump`导出头文件
  5. > 查看加壳的`MachO`文件
  6. > `USB`连接手机设备,找到`WeChat`的沙盒路径
  7. >

ps -A | grep WeChat

9806 ?? 0:03.88 /var/containers/Bundle/Application/454EA887-EB3B-43B3-ABFD-B9B2CA006981/WeChat.app/WeChat

  1. > `WeChat`拷贝到`Mac`电脑
  2. >

scp -P 12345 root@localhost:/var/containers/Bundle/Application/454EA887-EB3B-43B3-ABFD-B9B2CA006981/WeChat.app/WeChat ./

WeChat 100% 184MB 39.0MB/s 00:04

  1. > 查看`MachO`文件中的`crypt`信息
  2. >

otool -l WeChat | grep crypt

  1. cryptoff 16384

cryptsize 154255360 cryptid 1

  1. > 尝试不砸壳,只修改`cryptid`,能否使用`class-dump`导出头文件
  2. > 使用`MachOView`打开`WeChat`
  3. > `Load Commands`中,找到`LC_ENCRYPTION_INFO_64`,修改`Crypt ID``0`<br />
  4. ![](https://upload-images.jianshu.io/upload_images/9297953-b42c9c7ff46cd199.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240#alt=)
  5. > 使用`class-dump`导出头文件
  6. >

class-dump -H WeChat -o ./header

2021-05-31 17:59:37.344 class-dump[70836:43071318] Warning: Parsing method types failed, ¨ …

  1. > 导出失败。不砸壳,仅修改`cryptid`,无法导出头文件。所以砸壳的关键,并不是`cryptid`,而是将加密的代码段进行解密
  2. > 使用`lldb`手动砸壳
  3. > 砸壳的逻辑,从内存中,读取`cryptoff`位置到`cryptsize`长度的数据,然后将其覆盖原始`MachO`文件
  4. > 使用`Xcode`打开工程,选择设备,附加`WeChat`进程
  5. > 获取`MachO`的首地址
  6. >

image list

[ 0] 2B07ABCB-9885-3FF1-943C-B88A763C03C5 0x00000001008e0000 /var/containers/Bundle/Application/454EA887-EB3B-43B3-ABFD-B9B2CA006981/WeChat.app/WeChat (0x00000001008e0000) …

  1. > - `MachO`首地址为`0x1008e0000`
  2. > 从内存中,将加密部分的代码段,导出到`WeChat.bin`文件。因为已读取到内存中,相当于已解密
  3. >

memory read —force —outfile ~/Downloads/WeChat.bin —binary —count 154255360 0x00000001008e0000+16384

154255360 bytes written to ‘/Users/zang/Downloads/WeChat.bin’

  1. > - 代码段加密的开始位置:`MachO首地址 + 加密偏移地址`
  2. > `WeChat.bin`文件,写入到`MachO`文件中相同位置,相当于用解密后的数据,覆盖原始的加密数据
  3. >

dd seek=16384 bs=1 conv=notrunc if=./WeChat.bin of=WeChat

154255360+0 records in 154255360+0 records out 154255360 bytes transferred in 768.025931 secs (200847 bytes/sec)

  1. > - `seek`:从输出文件开头跳过`x`个块后再开始复制
  2. > - `bs`:同时设置读入/输出的块大小为`x`个字节
  3. > - `conv=notrunc`:不截断输出文件
  4. > - `if`:输入文件名,默认为标准输入。即指定源文件
  5. > - `of`:输出文件名,默认为标准输出。即指定目的文件
  6. > 文件写入成功,将`MachO`文件的`cryptid`修改为`0`,成功导出头文件<br />
  7. ![](https://upload-images.jianshu.io/upload_images/9297953-89f61252d94f1e98.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240#alt=)
  8. #####Tweak修改系统行为
  9. > 搭建`Tweak`插件,屏蔽应用的红点气泡
  10. > 屏蔽应用的红点气泡,需要附加的应用是系统的桌面程序`SpringBoard`
  11. > `USB`连接手机,找到`SpringBoard`进程
  12. >

ps -A | grep SpringBoard

10262 ?? 0:10.04 /System/Library/CoreServices/SpringBoard.app/SpringBoard

  1. > `SpringBoard`拷贝到`Mac`电脑
  2. >

scp -P 12345 root@127.0.0.1:/System/Library/CoreServices/SpringBoard.app/SpringBoard ./

SpringBoard 100% 7374KB 38.9MB/s 00:00

  1. > 查看`MachO`文件中的`crypt`信息
  2. >

otool -l SpringBoard | grep crypt

  1. > - `MachO`中找不到加密信息,说明`SpringBoard`原本就没有加壳
  2. > 使用`class-dump`导出头文件
  3. >

class-dump -H SpringBoard -o ./header

  1. > 成功导出`SpringBoard`应用的头文件<br />
  2. ![](https://upload-images.jianshu.io/upload_images/9297953-6c9906ab1f0f1609.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240#alt=)
  3. > 动态调试
  4. > 我们可以使用的动态调试工具有三种:
  5. > - `Reveal`
  6. > - `Cycript`
  7. > - `lldb`
  8. > 使用`Reveal`无法动态调试,因为在手机设置页的`Reveal`选项中,并没有`SpringBoard`应用
  9. > 使用`Cycript`,可以成功附加`SpringBoard`进程,但定位红点`UI`,并不直观
  10. > 最简单的方式,使用`lldb`附加`SpringBoard`进程,通过`Debug View`找到红点对象<br />
  11. ![](https://upload-images.jianshu.io/upload_images/9297953-ad553f6f75eb5e9e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240#alt=)
  12. > - 红点对象:`SBIconParallaxBadgeView`
  13. > `cy`环境中验证
  14. > 导入自定义`cy`脚本
  15. >

@import com.zang.cur_vc currentVC()

0x10145e3a0.view.recursiveDescription() .toString ()

  1. > 在结果中搜索`SBIconParallaxBadgeView`<br />
  2. ![](https://upload-images.jianshu.io/upload_images/9297953-a24c0c0cf75c202e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240#alt=)
  3. > `3`个红点,刚好对应`3`个控件<br />
  4. ![](https://upload-images.jianshu.io/upload_images/9297953-33b6008e94f13133.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240#alt=)
  5. > 选择其中一个对象,将其设置为隐藏
  6. >

0x10a623710.hidden=YES

  1. > `App Store`上的红点被成功隐藏<br />
  2. ![](https://upload-images.jianshu.io/upload_images/9297953-6aba2034fe71e4d9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240#alt=)
  3. > 在导出的头文件中,找到`SBIconParallaxBadgeView.h`文件<br />
  4. ![](https://upload-images.jianshu.io/upload_images/9297953-236b778056f7792f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240#alt=)
  5. > - `SBIconParallaxBadgeView`进行`HOOK`,破坏它的`init`方法,即可隐藏红点气泡
  6. > 搭建`Tweak`插件
  7. > 使用`nic.pl``15`,创建`Tweak`插件
  8. > `Makefile`文件中,增加`IP`和端口
  9. > 为了一劳永逸,将这两项配置在环境变量中
  10. >

vim ~/.zshrc export THEOS_DEVICE_IP=localhost export THEOS_DEVICE_PORT=12345

  1. > 打开`Tweak.x`文件,写入以下代码:
  2. >

%hook SBIconParallaxBadgeView

  • (id)init { return nil; }

%end

  1. > 编译、打包、安装插件
  2. >

cd reddemo make make package;make install

  1. > 成功安装`Tweak`插件,红点气泡全部隐藏<br />
  2. ![](https://upload-images.jianshu.io/upload_images/9297953-18936a954a736a96.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240#alt=)
  3. #####MonkeyDev搭建Tweak插件
  4. > 使用`MonkeyDev`创建`Tweak`插件<br />
  5. ![](https://upload-images.jianshu.io/upload_images/9297953-810222eb78a184b9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240#alt=)
  6. > 项目命名:`BadgeTweakDemo`<br />
  7. ![](https://upload-images.jianshu.io/upload_images/9297953-16c58720b17e6dd3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240#alt=)
  8. > - `BadgeTweakDemo.xm`:代码
  9. > - `control`:配置信息,版本号、作者名称等
  10. > - `BadgeTweakDemo.plist`:附加应用的包名称
  11. > `Build Settings`中,搜索`Monkey`,找到`Tweak`的设置<br />
  12. ![](https://upload-images.jianshu.io/upload_images/9297953-4a8a7c0a8adad34a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240#alt=)
  13. > - `MonkeyDevBuildPackageOnAnyBuild`:每次编译时打包
  14. > - `MonkeyDevClearUiCacheOnInstall`:安装时清除缓存
  15. > - `MonkeyDevCopyOnBuild`:编译时拷贝包到目录
  16. > - `MonkeyDevDeviceIP`:设备`IP`
  17. > - `MonkeyDevDevicePassword`:设备密码
  18. > - `MonkeyDevDevicePort`:设备端口
  19. > - `MonkeyDevInstallOnAnyBuild`:每次编译时安装
  20. > - `MonkeyDevkillProcessOnInstall`:安装成功后杀掉的进程
  21. > 设置`IP`和端口,同样将这两项配置在环境变量中
  22. >

vim ~/.zshrc export MonkeyDevDeviceIP=localhost export MonkeyDevDevicePort=12345

  1. > 打开`BadgeTweakDemo.xm`文件,写入以下代码:
  2. >

import

%hook SBIconParallaxBadgeView

  • (id)init { return nil; }

%end ```

Build Settings中,搜索signing,设置签名
iOS逆向实战--031:越狱调试 - 图19

  • Code Signing Identity:设置为iOS Developer

编译项目时,如果遇到CydiaSubstrate.tbdbuilt for iOS Simulatorfor architecture arm64问题,按目录找到CydiaSubstrate.tbd文件,删除里面的i386x86_64
iOS逆向实战--031:越狱调试 - 图20

编译项目,成功安装Tweak插件,红点气泡全部隐藏
iOS逆向实战--031:越狱调试 - 图21

总结

Reveal

  • iOS安装插件
  • Mac安装App
  • 将动态库导入iPhone

USB启动debugserver

  • 终端附加
    ◦ 手机,使用debugserver 主机名称:端口 -a 进程id
    Mac电脑,启动lldb,使用process connect connect://主机名称:端口
    USB端口映射

  • Xcode附加
    ◦ 打开工程
    ◦ 选择设备
    ◦ 附加进程

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参数,导出文件。lldb的环境在Mac
    ◦ 通过--count参数,指定导出的大小

  • dd命令
    ◦ 写入源文件
    seek指定偏移,也就是跳过多少开始写入
    conv保留没有替换的部分

Tweak修改系统行为

  • Reveal无法使用,在手机设置页的Reveal选项中,没有SpringBoard应用
  • Cycript可以使用,但定位UI不直观
  • lldb可以使用,最简单的方式

MonkeyDev搭建Tweak插件

  • Build Settings中,配置参数
  • 设置签名
  • 编译项目并安装插件