记制作lldb插件

546 阅读5分钟

搭建模板

在上文lldb的流程&环境的配置一文中已经分析过,lldb-plugin被调用到的关键点在于会在插件动态库的Mach-O中寻找lldb::PluginInitialize符号并以此为入口来进行调用。

因此,创建lldb-plugin的三要素如下:

  • 在插件中实现lldb::PluginInitialize
  • 自定义AnalyzeAddressCommand 继承自lldb::SBCommandPluginInterface
  • 重写DoExecute方法

创建工程的步骤:

  • 创建一个动态库
  • 引入lldb头文件
  • HEADER_SEARCH_PATHS
  • LD_RUNPATH_SEARCH_PATHS = /Applications/Xcode.app/Contents/SharedFrameworks
  • OTHER_LDFLAGS = $(inherited) -F "/Applications/Xcode.app/Contents/SharedFrameworks" -framework "LLDB"

image.png

根据上述步骤和所需元素来进行搭建工程即可,至此lldb-plugin模板搭建完成

制作插件

填充模板

现在就可以开始做一个插件了,功能是通过插件来判断输入的地址属于哪里,根据之前的模板来填充相关信息,如下图所示

image.png

提示:

  • AddMultiwordCommand用于多个关键字的命令,例如此处HD address 0x000001
  • 如果是p或者po命令属于单个关键字命令,则使用AddCommand即可

创建测试工程

在对插件进行调试的时候一种方法是将动态库每次打包然后进入lldb环境进行调试,显然这种方法并灵活,而且无法进行实时的调试,那么这里新建一个单元测试来对插件进行调试。

image.png

创建好单元测试工程后需要与主工程进行关联

image.png

image.png

因为需要对插件进行测试,所以需要将一些API暴露在头文件,所以将之前的部分内容重新写入头文件中,然后在测试文件中进行引入,同时对于lldb动态库的引入也需要像之前一样在测试工程中引入

image.png

一切准备就绪后cmd+U开始测试发现并无报错即可

测试代码

在之前的文章中对lldb的流程进行了分析,所以现在如果对插件进行测试的话,应当如以下步骤:

  • 初始化SBDebugger
  • 设置环境为同步环境
  • 创建一个Target
  • 加载一个可执行文件并添加一个断点
  • 验证插件

使用的可执行文件如下图,并且使用命令对其进行编译clang -g test.c

image.png

image.png

运行后发现正确命中打入断点的位置,测试工程搭建完毕。

对插件进行测试

在测试工程中对插件进行测试有两种方法,一种是手动来调用一种是使用plugin load命令

手动调用

image.png

手动调用很简单就是自己来添加命令并直接调用,运行后可以看到调用成功

image.png

plugin load

手动去加载一下插件即可

image.png

运行后可以看到插件加载成功且成功调用

image.png

判断地址插件的核心

判断地址无非是分为三个位置来判断:

  • MachO
  • 栈上
  • 堆上

image.png

例如现在加载的文件中可以很清楚的判断到:

  • m处于MachO
  • p处于堆上
  • b处于栈上

是否在MachO中

那么现在工程中在创建了Target后将断点打在此文件的17行处,即可得到所有的变量,便可以使用命令HD address m的地址来查看其地址处于什么位置

image.png

判断传入的地址是否在MachO中的关键是判断这个地址是否有Section的信息,如果判断成立的话则进入专门判断MachO的方法中

image.png

其中是对MachO中信息的一些收集并整体返回出来,结果如下图

image.png

同样可以根据命令objdump --macho -d 可执行文件来查看到func1位于__TEXT,__text

image.png

是否在栈上

判断是否在栈上就更为简单了,只需要得到相应线程中的栈帧信息,从中便可以取得栈顶和栈底,那么只需要判断当前地址是否处于栈顶和栈底之间,即可判断出此地址是否在栈空间上

image.png

在测试代码中来验证b的地址是否位于栈上

image.png

image.png

是否位于堆空间

是否位于堆空间可以使用函数malloc_zone_from_ptr来判断,同时可以使用malloc_size来看到分配的内存空间有多大

image.png

打印结果为0x600000f145e0 heap pointer, (0x10 bytes), zone: 0x1d641c000,可以得知obj对象分配的内存空间位于堆空间,并且大小为16字节,那么通过objc的方式判断堆空间的代码已经有了,同样可以通过c++还有Swift来实现这段代码

c++

image.png

这段代码就是利用上面说过的函数来判断地址是否在堆空间,但是这里居然显示不在堆空间上,因为通过命令HD address 0x00001,这个地址应当在插件所在的进程中进行判断,而此时的判断则是发生在lldb所在的进程也就是插件执行的环境,而这个地址则需要在执行插件所在的调试环境来判断,这两个环境是分别有自己的环境,所以需要将这部分代码放在执行插件所在的调试环境。

image.png

image.png

swift

同理,在swift中的判断原理也是一样的,只不过代码不同

image.png

image.png

总结

以上就是制作以及调试lldb插件的步骤和流程,本次是使用c++来编写插件,使用单元测试来调试插件。