IOS逆向学习-动态调试原理LLDB

Posted GY-93

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了IOS逆向学习-动态调试原理LLDB相关的知识,希望对你有一定的参考价值。

1. 动态调试Xcode

  • 什么叫动态调试?
    • 将程序运行起来,通过下面断点,打印等方式,查看参数、返回值、函数调用流程等

1.1 Xcode的动态调用原理

  • 关于GCC、LLVM、GDB、LLDB

    • Xcode的编译器发展:GCC -> LLVM
    • Xcode的调试器发展:GDB -> LLDB
  • debugserver一开始存放在Mac的Xcode里面

    • /Applications/Xcode12.3.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/9.1/DeveloperDiskImage.dmg/usr/bin/debugserver
      在这里插入图片描述
  • 当Xcode识别手机设备时,Xcode会自动将debugserver安装到iPhone上,如果是从来没有真机测试的iPhone上是没有debugserver的:在这里插入图片描述

  • Xcode调试的局限性:一般情况下,只能通过Xcode安装的APP

  • Xcode调试APP的原理图:
    在这里插入图片描述

  • 当你真机运行的时候,Xcode会在手机上安装debugserver的,debugserver监听着LLDB发送的一些指令,首先LLDB将这些指令传送给debugserver, 然后debugserver接收到这个指令时,在执行到app上,app执行指令后把结果反馈给debugserver,然后再由debugserver把结果返回给LLDB,在由LLDB把信息打印在Xcode上,这也是Xcode可以动态调试app的原因:
    在这里插入图片描述

  • p: 直接地址

  • po:打印对象

  • LLDB 是通过数据线来传递数据的

1.2 动态调试任何APP

1.2.1 原理图和条件

动态调试任何App示意图:
在这里插入图片描述

  • 首先手机上必须要有debugserver,其次我们可以通过终端 输入lldb进入lldb的调试环境:在这里插入图片描述

1.2.2 debugserver的权限问题

  • 默认情况下,/Developer/usr/bin/debugserver缺少一定的权限,只能通过Xcode安装的APP,无法连接其他APP(比如来自App Store的App)
  • 如果希望调试其他APP,需要对debuserver重新签名,签上两个调试相关的权限
    • get-task-allow Boolean 类型 值为YES
    • task_for_pid-allow Boolean 类型 值为YES
  • 如何给debugserver签上权限
    • iPhone上的/Developer目录是只读,无法直接对/Developer/usr/bin/debugserver文件直接签名,需要先把debugserver赋值到Mac上
    • 通过ldid导出以前文件以前的签名权限:ldid -e debugserver > debugserver.entitlements
    • debugserver.entitlements文件加上get-task-allow、task_for_pid-allow权限:在这里插入图片描述
    • 在通过ldid命令重新签名:did -Sdebugserver.entitlements debugserver
    • 将已经签好权限的debugserver方法到手机的/usr/bin目录,便于找到debugserver指令
    • 关于权限也可以使用codesign
# 查看权限信息
codesign -d --entitlements - debugserver

# 签名权限
codesign -f -s - --entitlements debugserver.entitlements debugserver
# 或则缩写
codesign -fs- --entitlements debugserver.entitlements debugserver

1.2.3 让debugserver附加到某个APP进程

  • 我们可以再终端查看debugserver的指令:在这里插入图片描述

  • debugserver *:端口号 -a 进程

    • *:端口号:使用iphone的某个端口启动debugserver服务(只要不是保留端口号就行,就和我们使用SSH登录把iphone的22端口映射到本机的10010一样)
    • 进程:输入APP的进程信息(进程ID和进程名称)

1.2.4 通过debugserver启动App

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

1.2.5 在Mac上启动LLDB,远程连接iPhone上的debugserver服务

  • 启动LLDB:lldb

  • 连接debugserver服务:process connect connect://手机IP地址:debugserver服务端端口号

  • 使用LLDB的c命令让程序先继续运行:c

  • 手机端:在这里插入图片描述

  • 电脑端:在这里插入图片描述
    刚开始是卡主的,我们输入c指令App才可以正常运行

1.2.5 可能遇到的问题(手机环境:ios 12.5.1)

  • failed to attach to process named: “”: 找不到该进程的名称,确认进程名称重新输入

  • Failed to get connection from a remote gdb process在这里插入图片描述

  • 解决方法:

    • 使用的端口可能被占用, 换一个端口试试
    • debugserver的权限文件中包含以下权限(删除该权限即可):在这里插入图片描述
  • rejecting incoming connection from ::ffff:127.0.0.1在这里插入图片描述

出现这种情况指令:
手机端:debugserver *:10012 -a neteasemusic
电脑端:process connect connect://localhost:10012或则process connect connect://127.0.0.1:10012

解决方案:
手机端:debugserver 127.0.0.1:10012 -a neteasemusic
电脑端:process connect connect://127.0.0.1:10012

暂时还没有找到其它的解决方案

2. LLDB

2.1 LLDB命令格式

<command> [<subcommand> [<subcommand>...]] <action> [-options [option-Value]] [argument [arguments...]]
  • <command>:命令
  • <subcommand>:子命令
  • <action>:命令操作
  • <options>:命令选项
  • <argument>: 命令参数
  • 比如给test函数设置断点:breakpoint set -n test:在这里插入图片描述
    • breakpoint<command>
    • set<action>
    • -n<options>
    • test<agrument>

通过上图,当我们过掉touchesBegan:withEvent:方法中断点时,会进入到test函数中的断点(通过LLDB指令添加的断点)

2.2 help

  • help<command> : 查看指令的用法,比如help breakpoint、help breakpoint set
    在这里插入图片描述

2.3 expression

  • expression<cmd-options>--<expr>: 执行一个表达式
    • <cmd-options>:命令选项
    • --:命令选项结束符,表示所有命令选项已经设置完毕,如果没有命令选项,--可以省略
    • <expr>:需要执行的表达式expression self.view.backgroundColor = [UIColor redColor]

有时候我们在通过打断点形式调试程序的时候,特别想动态的让这个程序动态的执行某段代码,以前我们是先把程序退掉,然后把要执行的代码加上,在重新运行程序,那么expression指令帮们我们实现这个目的:
在这里插入图片描述
由上图可知,我们可以直达直接访问view的backgroundColor的会报错,但是我们知道每一个view都有一个CALayer,我们可以直接访问这个属性,所以最终需改背景色的表达式:expression self.view.layer.backgroundColor = [UIColor redColor].CGColor

  • expression、expression --和指令print、p、call的效果一样
  • expression -O --和指令po效果一样

在这里插入图片描述

  • p是直接打印对象的地址,但是po相当于NSLog一样的效果:在这里插入图片描述

2.3 thread xxx

  • thread backtrace: 打印线程的堆栈信息,和指令bt的效果一样,示例:比如我们想知道test函数是谁调用的:

在这里插入图片描述

  • thread return [<expr>];让函数直接返回某个值,不会执行断点后面的代码

在这里插入图片描述

  • frame variable [<variable-name>]:打印当前栈帧的变量,(frame(栈帧)一个函数调用就是一帧)

在这里插入图片描述

  • thread continue、continue、c:程序继续运行
  • thread step-over、next、n:单补运行,把子函数当做整体一步执行
  • thread step-in、step、s:单步运行,遇到子函数会进入函数
  • thread step-out、finish:直接执行完当前函数的所有代码,返回到上一个函数

上述四个指令对应着Xcode工具的四个指令:在这里插入图片描述
注意:在一行代码一行代码的情况下 这两个指令效果相同, 如果是执行函数的时候, setp over 是直接过掉整个函数 而setp into 会进入函数内部一步一步的走;如果进入函数内部了, 执行setup out瞬间执行完函数代码,并且回到函数调用的时候, 相当于函数已经调用完了

  • thread step-inst-over 、nexti 、ni:汇编指令级别的单步运行,把一个汇编函数当做一个整体一步执行
  • thread step-inst 、stepi、si:汇编指令级别的单步运行,遇到子函数会进入子函数,一行汇编指令的执行
  • si、nis、n类似:前者是汇编指令级别,后者是源码级别

2.4 breakpoint

  • breakpoint set:设置断点
    • breakpoint set -a 函数地址
    • breakpoint set -n 函数名
      • breakpoint set -n test
      • breakpoint set -n touchesBegan:withEvent:
      • breakpoint set -n "-[ViewController touchesBegan:withEvent:]"
    • breakpoint set -r 正则表达式
    • breakpoint set -s 动态库 -n 函数名
  • breakpoint list:列出所有的断点(每个断点都有自己的编号)
  • breakpoint disable 断点编号:禁用断点
  • breakpoint enable 断点编号:启用断点
  • breakpoint delete 断点编号:删除断点

在这里插入图片描述

  • breakpoint command add 断点编号:给断点预先设置需要执行的命令,到出发断点时,就回按照顺序执行
  • breakpoint command list 断点编号:查看某个断点设置的命令
  • breakpoint command delete 断点编号:删除某个断点设置的命令

在这里插入图片描述

首先查看所有断点, 然后给第二个断点添加需要执行命令, 在出发第二个断点时,就回按照顺序执行预设置的命令

注意: 在给断点设置完预设置命令时,需要输入DONE来结束输入命令。

2.5 内存断点

  • watchpoint set variable 变量watchpoint set variable self->age
  • watchpoint set expression 地址watchpoint set expression &(self->_age)
  • watchpoint list
  • watchpoint disable 断点编号
  • watchpoint enable 断点编号
  • watchpoint delete 断点编号
  • watchpoint command add 断点编号
  • watchpoint command list 断点编号
  • watchpoint command delete 断点编号

我们给_count打了一个断点,当我们修改_count的值时候,就会进入断点
在这里插入图片描述

2.6 image(模块)

  • image lookup -t 类型:查找某个类型的信息,不用进入头文件,可以快速的查看一些类的信息

在这里插入图片描述

  • image lookup -a 地址:根据内存地址查找在模块中的位置,对于崩溃信息的定位有比较好的作用

在这里插入图片描述

  • image lookup -n 符号或者函数名:查找某个符号或则函数的位置

在这里插入图片描述

  • image list
    • 列出锁加载的模块的信息
    • image list -o -f:打印出模块的偏移地址、全路径

可以运行app当前的一些模块 ,我们自己的app本身也是一个模块,app依赖的动态库也是一个模块
在这里插入图片描述

2.7 小技巧

  • Enter,会自动执行上次的命令
  • 绝大部分指令都可以使用缩写
    在这里插入图片描述

以上是关于IOS逆向学习-动态调试原理LLDB的主要内容,如果未能解决你的问题,请参考以下文章

iOS逆向工程之使用LLDB的USB连接调试第三方App

ios逆向过程中lldb调试技巧

ios逆向过程中lldb调试技巧-po篇

C++学习(二六二)LLDB

逆向APP的思路

iOS 调试进阶-更高效的使用 Xcode 和 LLDB