七 LLDB简单使用

nx_001.jpeg

在上篇文章中,我们讲了Hook的原理分析,fishHook的简单使用,在使用的过程中,我们也详细的了解了一下MachO文件的构成,fishHook的执行流程、使用方法等。

在我们玩逆向的时候在大多数时候其实是拿不到源码的。所以了解一些LLDB来辅助我对别人APP的学(破)习(坏),是非常有必要的。

本篇文章,我们来了解下LLDB相关的内容吧。。

今天的DEMO也比较简单,可以在点击这里下载到:Demo7_LLDB

接下来本文会从以下几点进行阐述:

  • LLDB简介
  • LLDB断点设置
  • LLDB执行代码
  • LLDB查看堆栈信息
  • LLDB内存断点
  • LLDB其它指令
  • target-stop-hook
  • 关于image和其它常用指令

LLDB 简介

LLDB(Low Lever Debug) 默认内置于Xcode中的动态调试工具。标准的 LLDB 提供了一组广泛的命令,旨在与老版本的 GDB 命令兼容。 除了使用标准配置外,还可以很容易地自定义 LLDB 以满足实际需要。

默认内置于Xcode中的动态调试工具。标准的 LLDB 提供了一组广泛的命令,旨在与老版本的 GDB 命令兼容。 除了使用标准配置外,还可以很容易地自定义 LLDB 以满足实际需要。

命令格式如下:

 [ [...]] +  + [-options [option-value]] + [argument [argument...]]

:我们想在前面的命令序列的上下文中执行的一些操作。
:行为修改器(action modifiers)。通常带有一些值。

  • []表示命令是可选的,可以有也可以没有。
  • (命令)(子命令):LLDB调试命令的名称。命令和子命令按层级结构来排列:一个命令对象为跟随其的子命令对象创建一个上下文,子命令又为其子命令创建一个上下文,依此类推。
  • 我们想在前面的命令序列的上下文中执行的一些操作。
  • 行为修改器(action modifiers)。通常带有一些值。
  • 根据使用的命令的上下文来表示各种不同的东西。

The full lldb command names are often long, but any unique short form can be used. Instead of "breakpoint set", "br se" is also acceptable.
一般lldb的命令会很长,但是只要能够想出足够断,并且又能代表唯一性的缩写,那么缩写命令也是同一生效的如:breakpoint set == br se

LLDB的所有命令在LLVM官网或者Apple官网 都可以查询到。笔者会在这篇文章中列举一些比较常用的命令。

1. LLDB断点设置

首先,我们先看一下官网关于LLDB的命令,下面我们提供关于部分添加断点的命令,也是我们本段主要的内容:

1 breakpoint1.png

1.1 LLDB 命令设置断点
1 breakpoint2.png
breakpoint set -n test1(函数名称)

注意: 我们项目运行起来可以点击下面按钮进入LLDB模式

1 breakpoint_into.png

1.2 LLDB 设置多个断点
1 breakpoint3.png
4.使用LLDB下载多个断点
breakpoint set -n "[ViewController save:]" -n "[ViewController pauseGame:]" -n "[ViewController continueGame:]"
1.3 LLDB 设置断点失效、启用
1 breakpoint4.png
1 breakpoint5.png
// 使某个断点失效
breakpoint disable 1 
//启用某个断点
breakpoint enable 1

注意:
breakpoint disable 1 使当前的这一组断点全部失效。
breakpoint disable 1.1 使某一个断点失效
使某一个断点失效:

1 breakpoint6.png

1.4 LLDB 删除断点
1 breakpoint8.png
1 breakpoint9.png
// delete 使用
breakpoint delete 1.1 
breakpoint delete 1

注意:
breakpoint delete 1.1 是使某个断点失效。
breakpoint delete 1 是删除第一组的数据。删除只能删除指定的组。
breakpoint delete 删除所有的断点。

1.5 LLDB 给某一个函数设置断点(批量设置断点)
1 breakpoint10.png
// 给某一个函数设置断点(批量设置断点)
breakpoint set --select touchesBegan:withEvent:
1.6 LLDB 根据某个字符串设置断点
1 breakpoint11.png
// 遍历文件/系统中含有Game的函数设置断点
breakpoint set -r Game: 
1.7 LLDB 根据某个字符串设置断点
// 给某一个文件中的某一方法设置断点
breakpoint set --file ViewController.m -select touchesBegan:withEvent:
1.8 设置断点命令总结
命令名称 命令参样例
使用名称设置断点 breakpoint set --name test1(函数名)
使用内存地址设置断点 breakpoint -a 0xXXXXXXXX 删除断点 breakpoint delete 1
使断点失效/生效 breakpoint disable/enable 2
查看所有断点 breakpoint list
OC中所有命名中包含为Test4的方法设置断点 breakpoint set -r Test4
下载多个断点 breakpoint set -n "[ViewController save:]" -n "[ViewController pauseGame:]" -n "[ViewController continueGame:]"
使某个断点启用/失效 breakpoint enable 1 breakpoint disable 1注意:breakpoint disable 1 会使当前的这一组断点全部失效,使用breakpoint disable 1.1 使某一个断点失效
删除断点 breakpoint delete 1.1 breakpoint delete 1注意:breakpoint delete 1.1 是使某个断点失效,而breakpoint delete 1 是删除第一组的数据。删除只能删除指定的组
查看帮助命令 help breakpoint
给所有同名的函数设置断点 breakpoint set --select touchesBegan:withEvent:
清空所有断点 breakpoint delete
给某一个文件中的某一方法设置断点 breakpoint set --file ViewController.m -select touchesBegan:withEvent:
遍历文件/系统中含有Game breakpoint set -r Game:

2. LLDB执行代码

上一节中我们知道了LLDB怎么去设置断点,那么这一小节我们来看一下LLDB怎么执行代码吧。
准备工作:

  • 1.创建Person类,添加name、age属性
  • 2.在viewDidLoad中将数据添加到数组中。
// 大致代码如下:
#import "Person.h"

@interface ViewController ()
@property (nonatomic,strong) NSMutableArray *models;
@end

@implementation ViewController
- (NSMutableArray *)models{
    if (!_models) {
        _models = [NSMutableArray array];
    }
    return _models;
}

void test1(){
    NSLog(@"test1: %d",3);
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    Person *p1 = [[Person alloc] init];
    p1.name = @"p1";
    p1.age = 11;
    
    Person *p2 = [[Person alloc] init];
    p2.name = @"p2";
    p2.age = 12;
    
    Person *p3 = [[Person alloc] init];
    p3.name = @"p3";
    p3.age = 13;
    
    [self.models addObject:p1];
    [self.models addObject:p2];
    [self.models addObject:p3];
}
2.1 使用LLDB命令向models中添加一个数据
// 添加数据
p [self.models addObject:[[Person alloc] init]]

// 查看数据
po self.models
2 LLDB插入数据.png
2.2 使用LLDB命令给数据设值
// 获取数据(拿到最后一个数据)
p (Person *)self.models.lastObject

// 设值(上一步会返回一个标记,我们使用这个标记就好了,类似于动态对象,如下图)
2 LLDB动态设置数据.png
2.3 使用LLDB命令查看数据值
2 LLDB动态读取数据.png
1.先获取数据
2.读取数据
2.4 使用LLDB执行代码
2 LLDB执行代码段.png
1.打开LLDB
2.mac 快捷键 option+回车,在面板中写代码
3.p Person *p4 = [[Person alloc] init];
  p4.name = @"p4";
  p4.age = 14;
  [self.models addObject:p4]
2.1 LLDB动态运行代码总结
命令名称 命令例子
插入数据 如上 2.1
给数据设值 如上 2.2
查看数据 如上 2.3
LLDB执行代码 如上 2.4

3. LLDB查看堆栈信息

命令名称 命令例子
查看当前所有堆栈 bt
返回上一步堆栈 up
执行下一步堆栈 down
查看某一条堆栈 frame select 1
查看当前堆栈的参数 frame variable
堆栈回滚到上一条 thread return
程序继续执行 c
单步下一步 n
进入下一个函数(方法) s
汇编级别的单步下一步 ni
汇编级别的进入下一个函数(方法) si

下面出示例图:
LLDB_bt堆栈

3 LLDB_bt堆栈.png

LLDB 选择堆栈,查看参数

3 LLDB_select_variable.png

LLDB 修改堆栈函数的参数

3 LLDB frame修改参数.png

注意:

  1. 当我们使用frame查看指定堆栈,修改函数的参数的时候
frame 修改参数
frame select 1 // 切换堆栈
frame variable // 修改查看参数
p str = @"123" // 设置新参数
frame variable // 查看新设置的参数

如果后面有输出语句,参数依然是输出之前的参数,这是因为我们代码已经走到这里了,函数调用关系已经确定。

  1. 回滚指令 thread return
1.回滚指令之后函数不会再往下执行。
2.函数直接返回,不会再执行断点之后的代码
3.up指令不会改变其执行的流程
  1. si、ni 可以使用 control + 鼠标点击指令按钮查看

4. LLDB内存断点

某个属性地址只要有改变,就触发断点。相当于对某个属性设置了KVO。

命令名称 命令例子
直接观察一个变量 watchpoint set variable global_var
直接观察一个变量的地址 watchpoint set expression -- 0xxxxxx
删除断点 watchpoint delete 1
使断点失效/生效 watchpoint disable/enable 2
查看所有内存断点 watchpoint list
// 例子
1.touchesBegan 添加代码
Person *p1 = self.models.firstObject;
p1.name = @"hello";

2.再将数据添加到模型的时候设置断点,当p1赋值完成后LLDB执行以下代码。
watchpoint set variable p1->_name

3. 断点执行下一步,查看(查看所有内存断点 watchpoint list)内存断点
4 内存断点.png

5. LLDB command命令

LLDB 添加 command 指令

1.添加2个断点,当程序走到断点时,我们执行一些其它的命令,第一组屏幕点击,第二组给某个方法设置指令
2.breakpoint 执行command命令
 breakpoint command add 2(表示第几组)
 Enter your debugger command(s).  Type 'DONE' to end.
 > po self
 > p self.view.subViews
 > DONE
 
 // 也可以添加、执行删除命令
 breakpoint command delete 2

如下图:


5 LLDB command命令.png
命令名称 命令例子
breakpoint 添加指令 breakpoint command add 2(表示第几组)
breakpoint 删除指令 breakpoint command delete 2(表示第几组)

6. target stop-hook 和 .lldbinit的配置

target stop-hook含义:给所有的断点添加参数,执行某些命令。

// 不管你断住了哪一个方法,我都会把这个方法的参数打印出来 -o 表示添加一条指令
target stop-hook add -o "frame variable"

// 删除
target stop-hook delete

// 查看列表
target stop-hook list
6 target stop-hook.png

以下是部分命令

命令名称 命令例子
增加一个HOOK target stop-hook add -o "frame variable"
查看所有HOOK target stop-hook list
删除HOOK target stop-hook disable 1
使HOOK失效/生效 target stop-hook disable/enable 2

由于每次使用的断点比较多,并且都是执行相同的代码,这时我们就可以把这部分代码封装起来,写成一个文本文件夹,每次执行默认加载

.lldbinit的配置

1. .lldbinit 是在当前用户的家目录下,为隐藏文件。
2. .lldbinit 是隐藏文件,默认看不见,需要使用 command+shift+. 组合键显示文件。
3. 如果之前没有使用过,则没有.lldbinit文件,我们需要使用vim .lldbinit 创建文件。
4. 在 文件中 添加以下命令:target stop-hook add -o "frame variable",保存退出。
5. 使用 cat .lldbinit 查看 .lldbinit文件的内容。
6. 执行Xcode代码,会发现有断点的地方都执行了一下 “target stop-hook add -o "frame variable""这个命令

7. 关于image和其它常用指令

命令名称 命令例子
查看工程中使用的库(包括MachO自己) image list
查找可执行文件或共享库的原始地址 image lookup --address 0x0000000100000de0
输出NSURL的成员变量及属性信息。 image lookup --type NSURL
导出可执行文件和共享库的所有符号表 image dump symtab

8. 总结&Demo

本篇文章,我们讲了LLDB的一些常用的语法,和一些命令,
这片文章的内容其实非常简单,首先介绍了一下LLDB的一下基本用法,有.lldbinit文件可以帮我们自动加载脚本,所有就有了一个简单的LLDB脚本案例,

参考代码:LLDBDemo

参考:

作者:一缕清风扬万里
原文地址:https://www.jianshu.com/p/fd10bb8b89d6
LLDB调试器使用简介

你可能感兴趣的:(七 LLDB简单使用)