美文网首页
iOS逆向工程(八):动态调试

iOS逆向工程(八):动态调试

作者: 冰风v落叶 | 来源:发表于2020-03-18 11:41 被阅读0次

    动态调试

    一、什么是动态调试
      1. 动态调试就是将程序运行起来,通过打断点、打印等方式,查看参数、返回值、函数调用流程等信息
      1. 之前我们说的静态分析,就是程序不运行的时候,对程序的可执行文件进行分析,分析头文件、伪代码之类的信息
      1. 学会动态调试之后,我们就可以分析某个程序的整体调用流程了,例如:分析微信抢红包的时候,就可以知道微信调用了哪些方法去抢红包,以便我们hook
    二、Xcode动态调试的原理
      1. 其实Xcode自带的LLDB工具,就是动态调试的工具,我们平常开发的时候也一直在用,在Xcode打断点,程序就会暂停,输入lldb指令,就会出现结果,那么Xcode动态调试的原理是怎样的呢?
      1. Xcode动态调试的原理是这样的:我们用Xcode启动程序的时候,就会在自动在手机上安装一个debugserver程序,Xcode的LLDB工具会跟debugserver程序进行交互,将lldb指令传输给debugserver程序,debugserver程序又会将lldb指令传输给App,App执行完之后,会将返回值传输给debugserver程序,debugserver程序又发给LLDB,在控制台显示出结果,如下图所示:
        Xcode动态调试的原理.png
      1. debugserver程序一开始是存放在Mac中的Xcode里面的,手机里是没有的,只有当Xcode识别到手机设备时,才会自动把debugserver程序安装到手机上。debugserver的具体路径,如下所示
    debugserver在Mac上的路径是:
    /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/13.2/DeveloperDiskImage.dmg/usr/bin/debugserver
    
    debugserver安装到手机的路径是:
    /Developer/usr/bin/debugserver
    
      1. 需要注意的是,Xcode动态调试,只能调试通过Xcode安装的App,也就是说我们逆向别人App的时候,是无法用Xcode动态调试来调试的,那么如何才能调试任意App呢?
    三、动态调试任意App的原理
      1. 想要调试任意App,我们就需要用终端取代Xcode,终端里的LLDB工具与手机的debugserver程序交互,来传输lldb命令,如下所示:
        动态调试任意App原理.png
      1. 我们知道Xcode安装的debugserver只能与Xcode安装的App进行交互,因为沙盒机制的原因,导致debugserver没有权限与其他App产生交互,所以我们还需要给debugserver赋予相应的权限,才能与任意App进行交互
      1. 动态调试任意App只需要三步:将有权限的debugser安装到手机、让debugserver与App建立交互、让debugserver与LLDB产生交互
    四、动态调试任意App的第一步:将有权限的debugserver安装到手机

    这一步是一劳永逸的,安装完后,以后只需要第二步和第三步,就可以启动动态调试了

      1. 在Mac的中找到与手机系统版本对应的debugserver程序,Mac中的路径如下:
        /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/13.2/DeveloperDiskImage.dmg/usr/bin/debugserver
      1. 使用ldid签名工具debugserver新增两个权限:get-task-allow、 task_for_pid-allow,这两个都是Bool值,设置为YES,新增权限流程如下:
      • (1). 通过ldid命令,导出debugserver现有的权限,命令是:
        $ ldid -e debugserver > debugserver.entitlements

      • (2). 编辑debugserver.entitlements文件,新增get-task-allow、 task_for_pid-allow,值设置为YES,如下图所示:

        编辑debugserver.entitlements文件.png
      • (3). 通过ldid命令,对debugserver进行重新签名,命令是:
        $ ldid -Sdebugserver.entitlements debugserver,注意S是大写,并且与debugserver紧挨着。

      1. 将已经签好权限的debugserver放到手机的/usr/bin里面,便于手机找到debugserver命令
    五、动态调试任意App的第二步:让debugserver与App建立交互
      1. 远程登录到手机后 ,让debugserver附加到某个App进程,命令如下:
    打开命令行:sh usb.sh
    
    新建命令行:sh login.sh,登录到手机后
    
    让debugserver附加到某个App进程:$ debugserver 127.0.0.1:端口号 -a 进程名
    
    例如让debugserver附加到微信的进程:$ debugserver 127.0.0.1:10011 -a WeChat
    
      1. 重点命令就这一句话:$ debugserver 127.0.0.1:端口号 -a 进程名,其中127.0.0.1:端口号代表使用iPhone的某个端口启动debugserver服务,只要不是保留端口号就行
    六、动态调试任意App的第三步:让debugserver与LLDB建立交互
      1. 在Mac上启动LLDB服务,并且远程连接到iPhone的debugserver服务,命令如下:
    启动LLDB
    ` $ lldb    `
    
    连接到iPhone的debugserver服务
    ` (lldb) process connect connect://手机IP地址:debugserver服务的端口号  `
    
    以微信为例,连接到iPhone的debugserver服务:
    ` (lldb) process connect connect://10.88.211.170:10011  `
    
      1. 重点命令就这一句话process connect connect://手机IP地址:debugserver服务的端口号,如果输入完这句命令后,出现以下提示,就说明已经动态调试已经就绪,可以执行LLDB指令来调试了(注意此时会处在线程卡死状态,需要执行(lldb) c命令,让程序先运行起来)
    (lldb) process connect connect://localhost:10011
    Process 1194 stopped
    * thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGSTOP
        frame #0: 0x000000018da64634 libsystem_kernel.dylib`mach_msg_trap + 8
    libsystem_kernel.dylib`mach_msg_trap:
    ->  0x18da64634 <+8>: ret
    
    libsystem_kernel.dylib`mach_msg_overwrite_trap:
        0x18da64638 <+0>: mov    x16, #-0x20
        0x18da6463c <+4>: svc    #0x80
        0x18da64640 <+8>: ret
    Target 0: (WeChat) stopped.
    
    七、使用USB方式动态调试任意App
      1. 上面说的流程是通过用Wifi的方式传输LLDB命令的,传输速度较慢,所以推荐使用USB的方式建立交互,也就是:将Mac的10011端口iPhone的10011端口建立映射,然后debugserver服务使用iPhone的10011端口,此时Mac的LLDB工具Mac的10011端口传输数据就相当于与iPhone的10011端口传输数据,如下所示:
    在Mac上输入命令,让10011端口与10011端口映射、10010与22端口映射
    `python /Users/songpeng/Documents/python-client/tcprelay.py -t 22:10010 10011:10011`
    
    在iPhone上执行下面的命令,启动debugserver服务,端口号为10011
    `$ debugserver 127.0.0.1:10011 -a WeChat`
    
    在Mac上执行以下命令,让LLDB与本地的10011传输数据,由于映射已经建立,就相当于给iPhone的debugserver服务发送数据
    `$ lldb  `
    `(lldb) process connect connect://localhost:10011 `
    
      1. 可以将以前写的usb.sh文件的内容改成python /Users/songpeng/Documents/python-client/tcprelay.py -t 22:10010 10011:10011,这样即可以用USB的方式SSH登录到手机,也可以以USB的方式让LLDB与debugserver建立交互。以后使用起来就很方便了

    - 3. 我们总结一下,使用USB方式动态调试任意App的全部流程,如下所示,以后忘记原理没关系,只需要按顺序输入以下命令,就可以顺利开启动态调试

    在Mac上打开命令行窗口,让10011端口与10011端口映射、10010与22端口映射
    `$ sh usb.sh`
    
    在Mac上新建命令行窗口,然后SSH登陆到手机
    `$ sh login.sh`
    
    登录到手机后,启动手机的debugserver服务,让其与App建立交互
    `iPhone7ceshiji:~ root# debugserver 127.0.0.1:10011 -a WeChat`
    
    在Mac上新建命令行窗口,进入lldb工具,并且让LLDB与debugserver建立交互
    `$ lldb`
    `(lldb) process connect connect://localhost:10011`
    
    使用LLDB命令c,先让程序继续运行
    `(lldb) c`
    
      1. 动态调试建立好之后,我们就可以使用LLDB指令,来正式开始调试了
    八、LLDB指令
      1. LLDB指令的格式是:
        <command> [<subcommand> [<subcommand>...]] <action> [-options [option- value]] [argument [argument...]],其中,command代表命令,subcommand代表子命令,action代表命令的动作,- options 代表命令的选项,argument代表命令的参数,[]中的可以省略。
      1. help命令,用于查看指令的用法,例如:help breakpoint、help breakpoint set
      1. expression命令,执行一个表达式,例如:expression self.view.backgroundColor = [UIColor redColor],expression与print、p、call的效果一样
      1. thread backtrace命令,打印线程的堆栈信息,与bt命令效果一样
      1. thread return []命令,让函数直接返回某个值,不会执行断点后面的代码了,例如:thread return 3,返回了3
      1. frame variable []命令,打印当前栈帧的变量
      1. thread continue、continue、c命令,让程序继续运行
      1. thread step-over、next、n命令,单步执行,把子函数当做一个整体,不会进入子函数
      1. thread step-in、step、s命令,单步执行,遇到子函数会进入子函数内部
      1. breakpoint set命令,设置断点,参数主要有以下几种:
      • breakpoint set -a 函数地址
      • breakpoint set -n 函数名
      • breakpoint set -r 正则表达式
      • breakpoint set -s 动态库 -n 函数名
      1. breakpoint list命令,列出所有的断点,每个断点都有自己的编号
      1. breakpoint delete 断点编号命令,删除某个断点
      1. breakpoint command add 断点编号命令,给断点预先设置需要执行的命令,到触发断点时,就会按顺序执行
      1. breakpoint command list 断点编号命令,查看某个断点的预设命令
      1. watchpoint内存断点,就是当内存数据改变时,触发此断点,以便确认是谁修改了内存,子命令主要有以下几种:
      • watchpoint set variable 变量,例如:watchpoint set variable self->_age,当age变量改变时,断点就会触发,以便找到修改age内存的代码

      • watchpoint set expression 地址,例如:watchpoint set expression &self->_age

      • watchpoint list,列出所有的内存断点

      • watchpoint delete 断点编号,删除此内存断点

      • watchpoint command add 断点编号,给此内存断点,增加预设命令

      1. image lookup,寻找模块信息,如果你想找某个类型、某个方法、某个地址在模块中的什么位置,就可以用这个命令,主要参数如下:
      • image lookup -t 类型,查找某个类型的信息,例如image lookup -t NSInterger
      • image lookup -a 地址,看看某个内存地址在模块中的位置
      • image lookup -n 符号或者函数名,查找某个符号或者函数的位置
      1. image list,列出所加载的模块信息
      1. 一些小技巧:敲Enter会自动执行上次的命令、绝大部分命令可以使用缩写

    相关文章

      网友评论

          本文标题:iOS逆向工程(八):动态调试

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