lldb的一些调试用法

843 阅读5分钟

前言

LLDB是我们平常在开发过程中的默认调试器

image.png

如上图所示:

  • lldb还有一个lldb-driver(驱动)
  • 外层的API我们可以通过C++或者Python来使用
  • 支持的文件格式不仅有MachO还有ELF
  • 符号表支持DWARF格式
  • 汇编方面由LLVM进行支撑
  • 处理是由GDB-Remote来完成

image.png

如图所示:

  • 其实我们使用lldb来调试的时候,lldb放在本地端(一般就是电脑端)而lldb-server是放在远程端(我们调试使用的手机)或者还是在本地端(使用模拟器调试)
  • 用户使用命令行XcodePython来使用
  • lldblldb-server则是使用TCP协议来通信,其中还有就是遵循GDB的一个基于字节流的协议

接下来可以来验证lldblldb-server通过一个字节流的协议来通信这个说法:

  • 首先进入lldb环境
  • file 可执行文件
  • log enable gdb-remote packets
  • 之后可以看到不断的在发送和接收数据

image.png

lldb的流程调试

在编译好的llvm项目中找到本次需要使用的target lldb,在 Driver 文件中找到main函数,这里就是lldb的入口,首先可以先看一下这里传入的参数

image.png

参数分析:

  • argc代表传入了一个参数
  • parray 1 argv 表示从数组起argv始地址开始展示1个元素,可以看到这里就是只有一个参数就是lldb

再往下看可以看到这里会有一处判断输入的命令中是否会有help关键字,如果有的话会进行打印

image.png

那么这里如何在不改变代码的同时想看到打印呢?

bool hasArg(OptSpecifiers ...Ids) const {

    return getLastArg(Ids...) != nullptr;

  }

思路:

  • 首先看到是调用了函数hasArg
  • 可以看到这里会有一次比较,那么如果在函数返回之前改变了结果就可以实现不更改代码而改变返回值的效果

使用命令image lookup -n "hasArg"在可执行文件的镜像中寻找hasArg的信息 image.png

di -s 0x0000000100004774在刚找到的地址进行汇编代码展示,目的是找到在函数返回前修改返回值

image.png

在汇编代码中可以看出在函数返回之前最终的结果是存放于寄存器w0

br set -a 0x1000047a8在这个地址的位置加入断点,可以看到断点是断在了函数hasArg

image.png

那么在这里进行给寄存器w0重新赋值即可

register write w0 1,之后接着运行控制台打印出了与我们直接在终端使用命令lldb --help的效果是一样的

image.png

image.png

命令总结:

  1. image lookup -n "hasArg"

  2. di -s 0x0000000100004774

  3. br set -a 0x1000047a8

  4. register write w0 1

lldb 在实际开发的应用

现在设置一个场景:在一个封装的定位器中有一个属性标识了定位是否成功,那么在每次调试的时候为了方便需要这个标识是成功的,但是不好给它写死,因为这样会修改代码调试好了后又要删掉这里,会很不方便。那么这时就可以使用lldb来调试

利用行号下断点

image.png

可以在52行下断点然后直接更改isLocationSucess的值为true

进入lldb环境后使用命令br set -l 52 -f ViewController.m -C "e -- _manager.isLocationSucess = YES" -G true

命令解析:

  • -l 是指定52行
  • -f 是指定文件 ViewController.m
  • -C 是指触发此断点后执行后面的命令,即修改isLocationSucessTRUE
  • -G 是指此断点在执行后面的命令后不会有触感

执行命令后显示修改成功 image.png

但是这里用行号去打断点会显得十分不灵活,因为谁都无法保证这个文件不会去改动,一旦改动后用行号打断点的命令就会失效

利用源码下断点

lldb环境下使用命令br set -p "if \(_manager.isLocationSucess\) " -f ViewController.m -C "e -- _manager.isLocationSucess = YES" -G true

命令解析:

  • -p 对符合正则表达式的源码处下断点
  • -f 指定文件
  • -C 是指触发此断点后执行后面的命令
  • -G 是指此断点在执行后面的命令后不会有触感

image.png

但是这个方法也不能保证此处代码不被更改

init方法下断点

init方法下断点的同时去修改实例对象managerisLocationSucess的属性

image lookup -vrn "\[LGLocationManager init\]"找到init方法的范围

image.png

di -s 0x0000000104191770 -c 36init方法初始地址开始展示36行的汇编代码

image.png

在函数返回之前设置断点,x0寄存器此时存储的就是manager,这时对isLocationSucess进行修改

br set -a 0x1041917fc在返回处设置断点

br command add在前一个断点出添加命令

Enter your debugger command(s).  Type 'DONE' to end.

> e -- [(LGLocationManager *)$x0 setIsLocationSucess:YES]

> continue

> DONE

image.png

对sp中的地址下断点

br set -S "-[KKLocationManager init]" -K falseinit符号设置断点

di -f显示断点处的汇编指令,也以便能拿到spx0寄存器

br command add对上一个断点添加指令

> br s -a `*(unsigned long *)$sp`

> e -- [(KKLocationManager *)$x0 setIsLocationSucess:YES]**

> continue

> DONE

添加指令的作用:

  • sp寄存器中的地址下断点
  • 在栈顶指针中有值的时候此时x0中的值已经设置完毕了
  • 修改x0中内容

image.png

可以看到已经修改成功

更改返回的字符串

thread return

thread return是可以更改当前栈返回的默认值

比如下图中,点击后返回的应当是Dog

image.png

e -- NSString *$name = @"Cat"lldb中创建一个变量

br set -r thread_return -C "thread return `$name`" -G true

使用thread return把返回值更改为之前创建的新变量

但是这个会有新的问题,会显示BAD_ACCESS

修改内存

image.png

先来看看str到底是什么

image.png

str内存偏移16个字节存储的东西就是@"Cat"字符串,下面可以来验证

image.png

image lookup -a 0x0000000100cdbdd7可以根据地址来查找看看这个字符串到底是在哪里的

image.png

可以清楚的看到是在MachOTEXT段的cstring中存放,那么同理@"Dog"这个字符串应该也是存放在这个位置,既然是这样就可以更改内存来达到目的

re read x0这个函数的返回值是存放在x0寄存器中的

me read -s 8 -f x -c 4 0x0000000100cdc088的意思:

  • -s 8 8个字节为一组
  • -f x 16进制的格式
  • -c 4 总共读4组

image.png

e -f x -- (unsigned long *)($x0 + 16)偏移16个字节的指针中存放的就是@"Dog"

image.png

me write -s 8 `(unsigned long *)($x0 + 16)` 0x0000000100cdbdd7

将原来存放@"Dog"的指针更改其内容为@"Cat"

image.png

更改成功

lldb中的记录和重播的功能

image.png

从文档中可以看到这里是告诉调试器去捕获一个复制器

image.png


lldb --capture

file next

file test

b main

br list

r

di -f -m

reproducer status

reproducer generate

lldb --replay /var/folders/nb/s5qd3zpx4ksg7ck7xj7mf24c0000gn/T/reproducer-e1bafe

--replay就是告诉调试器通过后面路径的文件来重播复制器,可以看到上面执行的命令会再次执行一遍

image.png