iOS逆向之LLDB+Debugserver动态调试

一、前言

动态调试就是在我们的程序运行之时,通过下断点、打印等一系列方式查看参数、返回值、函数调用流程等等。不仅是在iOS开放中需要动态调试,在任何语言的开发过程中都需要用到动态调试。

学会动态调试之后,我们就可以分析某个程序的整体调用流程了,例如:分析微信抢红包的时候,就可以知道微信调用了哪些方法去抢红包,以便我们hook。

一、Xcode动态调试的原理

1、GCC、LLVM、GDB、LLDB

我们在开发iOS程序的时候常常会用到调试跟踪,如何正确的使用调试器来debug十分重要。xcode里有内置的Debugger,老版使用的是GDB,xcode自4.3之后默认使用的就是LLDB了。

GDB:UNIX及UNIX-like下的调试工具。
LLDB:LLDB是个开源的内置于XCode的具有REPL(read-eval-print-loop)特征的Debugger,其可以安装C++或者Python插件。
GCC:(GNU Compiler Collection,GNU编译器套装),是一套由 GNU 开发的编程语言编译器。
LLVM:(low level virtual machine)是一个开源编译器框架,这个库提供了与编译器相关的支持,能够进行程序语言的编译期优化、链接优化、在线编译优化、代码生成。简而言之,可以作为多种语言编译器的后台来使用。

Xcode的编译器发展历程:GCC → LLVM
Xcode的调试器发展历程:GDB → LLDB

2、debugserver

debugserver、lldb是协同工作的,debugserver依附在APP上,时刻监听APP的运行状态,并有控制APP执行的能力;lldb是在APP外部的,可以和debugserver建立连接,通过debugserver获取APP运行状态,并且能通知debugserver对APP做一些事情。在真机调试的时候,Xcode将debugserver加入到APP中,通过lldb来调试APP,那么同样也可以在iterm上对越狱手机上的任意APP进行调试。

iOS逆向之LLDB+Debugserver动态调试_第1张图片
图1:Xcode调试原理.png

debugserver一开始存放在Mac的Xcode里面,当Xcode识别到手机设备时,Xcode会自动将debugserver安装到iPhone上。Xcode调试有个局限性,就是一般情况下,只能调试通过Xcode安装的APP。

debugserver的Mac存放路径:/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/De viceSupport/9.1/DeveloperDiskImage.dmg/usr/bin/debugserver
debugserver的iPhone存放路径:/Developer/usr/bin/debugserver

图2:debugserver存放路径.png

二、动态调试任意App

iOS逆向之LLDB+Debugserver动态调试_第2张图片
图3:动态调任意APP.png

1、debugserver的权限问题

默认情况下,/Developer/usr/bin/debugserver缺少一定的权限,只能调试通过Xcode安装的APP,无法调试调试APP,比如来自AppStore的APP。

如果希望调试其他APP,需要对debugserver重新签名,签上2个调试相关的权限。

  • get-task-allow
  • task_for_pid-allow

2、给debugserver签上权限

  • iPhone上的/Developer目录是只读的,无法直接对/Developer/usr/bin/debugserver文件签名,需要先把debugserver复制到Mac.

  • 通过ldid命令导出文件以前的签名权限

$ ldid -e debugserver > debugserver.entitlements
  • debugserver.entitlements文件加上get-task-allowtask_for_pid-allow权限。

ps:如果Xcode12 打不开debugserver.entitlements文件的话,可以使用Xcode11试试

图4:给debugserver新增权限.png
  • 通过ldid命令重新签名
ldid -Sdebugserver.entitlements debugserver
  • 由于/Developer/usr/bin/目录是只读的,所以我们将重新签名过的debugserver放在/usr/bin/下,然后对debugserver增加运行权限:chmod +x /usr/bin/debugserver,就可以在终端使用debugserver
图5:通过ldid命令重新签名.png
  • 关于权限的签名,也可以使用codesign
# 查看权限信息
$ codesign -d --entitlements - debugserver

# 签名权限
$ codesign -f -s - --entitlements debugserver.entitlements debugserver 
# 或着简写为
$ codesign -fs- --entitlements debugserver.entitlements debugserver

3、让debugserver附加到某个APP进程

远程登录到手机后 ,让debugserver附加到某个App进程,
命令如下:debugserver *:端口号 -a 进程

  • *:端口号:表示使用iPhone上的某个端口启动debugserver服务(注意:不能使用保留端口号)
  • -a 进程:输入APP的进程信息(指定进程id或者进程名称)

例如让debugserver附加到微信的进程:debugserver 127.0.0.1:10011 -a WeChat

4、在Mac上启动LLDB,远程连接iPhone上的debugserver

  • 启动LLDB:
$ lldb
(lldb)
  • 连接debugserver服务
(lldb) process connect connect://手机IP地址:debugserver服务端口号
  • 使用LLDB的c命令让程序先继续运行
(lldb) c
  • 接下来就可以使用LLDB命令调试APP了

5、使用debugserver启动App

$ debugserver -x auto *:端口号 APP的可执行文件路径

6、使用USB方式动态调试任意App

上面的步骤不好理解,没有关系,让我们再重新详细来一遍吧。

1)使用python建立本地映射

编辑脚本文件uConnect.sh, 让10010端口22端口映射、10011端口10011端口映射。本地映射逻辑可参考: Mac远程登录iPhone

python ~/Documents/usbmuxd/python-client/tcprelay.py -t 22:10010 10011:10011

编辑脚本文件uLogin.sh,让Mac连接上手机

ssh root@localhost -p 10010

然后mac各自执行脚本

iOS逆向之LLDB+Debugserver动态调试_第3张图片
图6:使用python建立本地映射.png
2)iPhone启动debugserver服务

以抖音为例,手机上开启app,然后通过指令搜索抖音进程。之后通过进程id或者进程名启动debugserver服务

ZhangCong-iPhone5s:~ root# ps -A | grep Aweme
10602 ??         0:42.86 /var/mobile/Containers/Bundle/Application/BFA3B9CA-42BA-41B2-9C29-259EBF3EF97C/Aweme.app/Aweme
10610 ttys000    0:00.01 grep Aweme
ZhangCong-iPhone5s:~ root# debugserver 127.0.0.1:10011 -a 10602
debugserver-@(#)PROGRAM:debugserver  PROJECT:debugserver-340.3.51.1
 for arm64.
Attaching to process 10602...
Listening to port 10011 for a connection from 127.0.0.1...
3)LLDB连接debugserver服务

上步启动debugserver服务后,可以看到debugserver处于Listening监听状态,等待被连接。这时,我们需要在Mac上开启LLDB,去连接debugserver。

在Mac上执行以下命令,让LLDB与本地的10011传输数据,由于映射已经建立,就相当于给iPhone的debugserver服务发送数据。连接成功后,需要执行命令c,好让程序继续运行。

$ LLDB
(lldb) process connect connect://localhost:10011
图7:LLDB连接debugserver服务.png

三、LLDB常用命令

LLDB常用命令比较多,在这篇文章有详细介绍,可以转阅这里: iOS开发之LLDB常用命令

四、ASLR介绍

以前我们用Xcode的LLDB指令打断点时,可以用方法名打断点,例如breakpoint set -n "[UIViewController touchBegin:]",但是我们想动态调试别人的App,就不能用方法名称了,需要用到方法的内存地址才能打,例如breakpoint set --address 0x123123123。而想知道方法的内存地址就需要学习ASLR。

1、什么是ASLR

ASLR(Address Space Layout Randomization),地址空间布局随机化,是一种针对缓冲区溢出的安全保护技术,通过对堆、栈、共享库映射等线性区布局的随机化,通过增加攻击者预测目的地址的难度,防止攻击者直接定位攻击代码位置,达到阻止溢出攻击的目的的一种技术。iOS4.3开始引入了ASLR技术

2、Mach-O文件的内部结构

1)Mach-O基本结构
iOS逆向之LLDB+Debugserver动态调试_第4张图片
图8:Mach-O基本结构.png
2)Data基本结构
图9:Data基本结构.png
3)字段介绍
图10:字段介绍.png

3、未使用ASLR时,Mach-O文件是如何载入内存的?

  • _PAGEZERO在Mach-O文件中的大小为0x0,加载进内存后大小为0x100000000(arm64架构下)
  • Header和Load commands都是描述信息,存储在_TEXT中,所以内存空间从_PAGEZERO开始分配
  • 码编译完成后,_TEXT中方法的位置和_DATA中变量的位置就是固定的,这样很容易进行动态调试,为了增加安全性,从iOS4.3开始就引入了ASLR技术
iOS逆向之LLDB+Debugserver动态调试_第5张图片
图11:使用ASLR前的内存布局.png

4、使用ASLR时,Mach-O文件是如何载入内存的?

  • ASLR是Address Space Layout Randomization(地址空间布局随机化)的缩写
  • 引入ASLR技术后,当Mach-O文件加载进内存时,会随机生成一个内存地址,然后从该地址开始进行内存空间的分配
  • 虽然方法和变量的位置是固定的,但是内存地址就变得随机了,这样就会增加动态调试的难度,从而提高了安全性
iOS逆向之LLDB+Debugserver动态调试_第6张图片
图12:使用ASLR后的内存布局.png

五、如何给别人的App打断点,进行调试

1、无ASLR虚拟内存地址

用Hopper等工具静态分析出某个函数在虚拟内存中的函数地址,这里的函数地址是静待分析出来的,也就是未经过ASLR偏移的地址,并不能直接用于打断点,如下图所示,-[AWEAwemePlayVideoViewController play] 函数的未ASLR偏移的函数地址是:0x1006de960。如下图所示:

图13:查找方法地址.png

2、ASLR随机内存地址(Mach-O文件加载进内存的起始地址)

使用LLDB指令image list -o -f | grep Mach-O文件名称,获取ASLR偏移量,如下图所示,本次载入内存的偏移量是0x7c000

(lldb) image list -o -f | grep Aweme
[  0] 0x000000000007c000 /var/mobile/Containers/Bundle/Application/BFA3B9CA-42BA-41B2-9C29-259EBF3EF97C/Aweme.app/Aweme(0x000000010007c000)

3、真实内存地址(调试其他APP时,需要用真实内存地址来设置断点,用方法名是无效的)

使用LLDB指令breakpoint set -a 函数地址,给某个函数打断点,这里的函数地址指的是虚拟内存中的真实函数地址,也就是说这里的函数地址,是ASLR偏移量+静待分析的函数地址,也就是0x1006de960+0x7c000,完整的LLDB指令就是 breakpoint set -a 0x1006de960+0x7c000,如下图所示:

图14:方法下断点.png

4、调试程序

打完断点之后,当App触发此断点时,就会卡住,以便我们输入LLDB调试,例如,我这里对微信的发送消息的方法[AWEAwemePlayVideoViewController play] 打了断点,之后每次发消息时都会卡主,以便我们继续输入LLDB指令调试。如果不想要断点了,可以用breakpoint delete 断点编号删除断点;可以通过breakpoint list命令,列出所有的断点编号。

  • po $x0:打印方法调用者
  • x/s $x1:打印方法名
  • po $x2:打印参数(以此类推,x3、x4也可能是参数)
  • 如果是非arm64,寄存器就是r0、r1、r2

更多指令请转阅: iOS开发之LLDB常用命令



参考链接:
https://www.jianshu.com/p/fa2f080fb4bb
https://www.jianshu.com/p/94f67bb84c93
https://www.jianshu.com/p/220b9be14be7

你可能感兴趣的:(iOS逆向之LLDB+Debugserver动态调试)