美文网首页逆向学习iOS逆向破解iOS逆向备忘录
iOS逆向之LLDB+Debugserver动态调试

iOS逆向之LLDB+Debugserver动态调试

作者: 张聪_2048 | 来源:发表于2021-04-02 14:18 被阅读0次

    一、前言

    动态调试就是在我们的程序运行之时,通过下断点、打印等一系列方式查看参数、返回值、函数调用流程等等。不仅是在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的编译器发展历程:GCCLLVM
    Xcode的调试器发展历程:GDBLLDB

    2、debugserver

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

    图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

    图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各自执行脚本

    图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基本结构
    图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技术
    图11:使用ASLR前的内存布局.png

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

    • ASLR是Address Space Layout Randomization(地址空间布局随机化)的缩写
    • 引入ASLR技术后,当Mach-O文件加载进内存时,会随机生成一个内存地址,然后从该地址开始进行内存空间的分配
    • 虽然方法和变量的位置是固定的,但是内存地址就变得随机了,这样就会增加动态调试的难度,从而提高了安全性
    图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动态调试

        本文链接:https://www.haomeiwen.com/subject/nitqkltx.html