❗️iOS高级强化

LLDB调试(二)

2021-03-04  本文已影响0人  浅墨入画

前言:

lldb断点类型:

  1. 软件断点:正在调试的二进制文件中的断点,在调试器应使用一种迫使 CPU发出软件中断(又称为trap)的操作码来 停止我们的程序的位置替 换现有的操作码,击中断点并发送中断后,调试器将接收中断信号,将操 作码替换为原始操作码,等待下一个命令。
  2. 硬件断点:使用专用硬件,例如断点的x86调试寄存器,观察CPU和停止执 行程序的当前状态。
  3. 非符号断点:如果标记了一行代码以供调试器停止程序执行,代码中的位 置会转换为调试器实际在其中放置断点的执行地址。
  4. 符号断点:直接绑定到符号。

一. lldb复制器

lldb Reproducer :为了解决复现在使用lldb过程中遇到的bug,会在 lldb运行过程中捕获所有必要的信息,方便以后在调试调试器时重播调试会话。
lldb Reproducer 有两个功能:

  1. 捕捉
  2. 重播
    lldb Reproducer 在捕捉时,会将调试过程中的所有信息包括调试的对象 信息,例如使用的符号、Mach-o的地址,调试信息路径,进程信息,内存信息保存下来,以便下次重播。
// 保存这次调试的过程,使用捕获capture
$ lldb --capture
(lldb) file test
Current executable set to '/Users/wangning/Documents/资料/3:1/第十二节课、LLDB深入学习(中)/上课代码/01-Reproducer/test' (x86_64).
// 添加符号断点 b main,所有符号断点都被lldb转换成地址0x0000000100003f70
(lldb) b main
Breakpoint 1: where = test`main, address = 0x0000000100003f70
(lldb) r
Process 15930 launched: '/Users/wangning/Documents/资料/3:1/第十二节课、LLDB深入学习(中)/上课代码/01-Reproducer/test' (x86_64)
Process 15930 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000100003f70 test`main
test`main:
->  0x100003f70 <+0>: pushq  %rbp
    0x100003f71 <+1>: movq   %rsp, %rbp
    0x100003f74 <+4>: subq   $0x10, %rsp
    0x100003f78 <+8>: movl   $0x0, -0x4(%rbp)
Target 0: (test) stopped.
// 显示当前汇编
(lldb) di -f
test`main:
->  0x100003f70 <+0>:  pushq  %rbp
    0x100003f71 <+1>:  movq   %rsp, %rbp
    0x100003f74 <+4>:  subq   $0x10, %rsp
    0x100003f78 <+8>:  movl   $0x0, -0x4(%rbp)
// 同时显示当前汇编 以及 源码
(lldb) di -f -m
// 同时显示当前汇编 源码 操作码
(lldb) di -f -m -b
// 查看当前reproducer状态,当前处于capture mode,并且把捕捉信息存放到路径 /var/folders/d7/5qn4fnqn0p197t4lkw1bw1p40000gn/T/reproducer-37d05b 下
(lldb) reproducer status
Reproducer is in capture mode.
Path: /var/folders/d7/5qn4fnqn0p197t4lkw1bw1p40000gn/T/reproducer-37d05b
// 结束捕捉模式,并且把所有调试信息存放到路径/var/folders/d7/5qn4fnqn0p197t4lkw1bw1p40000gn/T/reproducer-37d05b下
(lldb) reproducer generate
Reproducer written to '/var/folders/d7/5qn4fnqn0p197t4lkw1bw1p40000gn/T/reproducer-37d05b'
Please have a look at the directory to assess if you're willing to share the contained information.
// 恢复上面所有操作命令,上面所有操作命令都会在终端展示出来
$ lldb --replay /var/folders/d7/5qn4fnqn0p197t4lkw1bw1p40000gn/T/reproducer-37d05b

二. lldb插件入口

lldb调试进程信号量类型

提前加载plugin两种方式:

  1. 系统定义的Plugin:LLDB.framework/Resources/PlugIns
  2. 用户自定义Plugin:~/Library/Application Support/LLDB/ PlugIns

操作:用户自定义方式加载插件

// 进入自定义目录,注意空格转义
$ cd ~/Library/Application\ Support
如果Application Support目录下没有LLDB文件夹就手动创建,并且在LLDB文件夹下创建PlugIns文件夹,并把插件libfooplugin.dylib放入,如下图所示 image.png
// 创建空工程,ViewController.m方法viewDidLoad中打断点,进入lldb
// 可以使用plugin load方式把插件加入到 这一次调试周期内,也可以把插件放入系统指定的路径下,插件对全局都能生效
(lldb) plugin load

三. lldb源码与API学习

LLDB API可以通过脚本桥接接口作为Python函数使用或者C++函数使用:

  1. SBDebugger:调试器对象,lldb.SBDebugger对象拥有在调试会话的命令
    解释器和所有目标。
  2. SBTarget:当前选定的目标,lldb.SBTarget管理一个正在运行的进程, 以及该进程的使用的可执行文件和调试文件。
  3. SBProcess:当前选定的target所在的process,lldb.SBProcess对象 管理线程并控制访问存储的过程。
  4. SBThread:当前选定的线程,lldb.SBThread对象管理该线程的堆栈帧。
  5. SBFrame:当前选定的堆栈frame,该lldb.SBFrame对象管理当前堆栈信息 和该堆栈使用到的寄存器组。

加载lldbinit文件两种方式:

  1. 全局生效.lldbinit文件:将.lldbinit文件放置在根目录~/下
  2. 指定项目生效:手动设置的.lldbinit文件
创建.lldbinit文件,内容如下
command script import ./lldbinit.py
$ lldb --local-lldbinit
(lldb) file test
Current executable set to '/Users/wangning/Documents/资料/3:1/第十二节课、LLDB深入学习(中)/上课代码/02-lldb与.lldbinit/test' (x86_64).

// lldb可以预先内置一些命令
(lldb) help lldbinitcmds
error: 'lldbinitcmds' is not a known command.
Try 'help' to see a current list of commands.
Try 'apropos lldbinitcmds' for a list of related commands.
Try 'type lookup lldbinitcmds' for information on types, methods, functions, modules, etc.
(lldb) command script add -f lldbinit.cmd_lldbinitcmds lldbinitcmds
(lldb) help lldbinitcmds
     For more information run 'help lldbinitcmds'  Expects 'raw' input (see 'help
     raw-input'.)
Syntax: lldbinitcmds
Function lldbinit.cmd_lldbinitcmds was not found. Containing module might be missing.
(lldb) lldbinitcmds

// 命令expression -O -- 起别名为Cat
(lldb) command alias Cat expression -O --
(lldb) Cat 123
123
修改.lldbinit文件,内容如下
command script import ./lldbinit.py
command alias Cat expression -O 1%

// 1%表示输入第一个内容, 2%表示第二个
创建lldbinit.text, 内容为command alias Cat expression -O %1

手动在Xcode配置.lldbinit文件如下 image.png
(lldb) help Cat
     Evaluate an expression on the current thread.  Displays any returned value
     with LLDB's default formatting.  Expects 'raw' input (see 'help
     raw-input'.)
Syntax: Cat <cmd-options> -- <expr>
... 

REPL
全称为Read-Eval-Print Loop(“读取-求值-输出”循环), 一个简单 的,交互式的编程环境,用来接收代码的输入并直接输出回应。

LLDB API可以通过脚本桥接接口作为Python函数使用或者C++函数使用:

  1. SBCommandInterpreter:用于处理/解释lldb的命令。
  2. SBBreakpoint:创建和管理断点。

SBCommandInterpreter: 处理解析,添加,执行
CommandObject :DoExecute:处理命令的地方

操作:手动创建包装一个可执行文件,现在通过api给可执行文件打断点
Cmd + Shift + N ,选择如下模版,创建工程 SBAPI学习

image.png
// 显示Xcode内置的python动态库所在位置
$ lldb -P
/Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python3
$ open /Applications/Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/Python3
打开上面Python3文件,可以看到有LLDB动态库,如下图所示 image.png

1. 接下来把lldb所有头文件拷贝到工程 SBAPI学习 目录下
2.为了方便把lldb头文件导入 Header Search Paths,创建Config.xcconfig文件进行配置

// Config.xcconfig文件配置如下
HEADER_SEARCH_PATHS = ${SRCROOT}/include
LD_RUNPATH_SEARCH_PATHS = /Applications/Xcode.app/Contents/SharedFrameworks
image.png
// main.mm文件内容
#import <Foundation/Foundation.h>
#import <lldb/API/LLDB.h>

using lldb::SBDebugger;

int main(int argc, const char * argv[]) {
    //初始化配置
    SBDebugger::Initialize();
    //初始化SBDebugger
    SBDebugger debugger = SBDebugger::Create();
    NSLog(@"%d", debugger.GetNumTargets());
    return 0;
}
// 正常打印 SBAPI学习[8196:430497] 0

// 现在想边写代码边调试
// 替换上面动态库LLDB为liblldb.12.0.0git.dylib(llvm中获取)
// Config.xcconfig文件内容修改如下
HEADER_SEARCH_PATHS = ${SRCROOT}/include
LD_RUNPATH_SEARCH_PATHS = ${SRCROOT}
(lldb) b SBDebugger::Create
Breakpoint 2: 3 locations
// 执行断点,跳入lldb源码
阻止自省和调试尝试的功能是系统完整性保护,也称为无根。该系统限制了程序可以执行的操作(即使它们具有root访问权限),以阻止恶意软件将其自身植入系统内部。

 1、重启`macOS`
 2、不停的按`Command + R`,直到`Apple logo`出现。进入`Recovery Mode`。
 3、选择`Terminal`。输入:
     
     ```bash
     csrutil disable && reboot
     ```
 4、重启,然后打开终端,输入:
     
     ```bash
     csrutil status
     ```
     查看当前`Rootless`是否被关闭。
上一篇 下一篇

猜你喜欢

热点阅读