在上篇文章中,我们讲了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...]]
-
[]
表示命令是可选的,可以有也可以没有。 -
和(命令)
: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.1 LLDB 命令设置断点
breakpoint set -n test1(函数名称)
注意: 我们项目运行起来可以点击下面按钮进入LLDB模式
1.2 LLDB 设置多个断点
4.使用LLDB下载多个断点
breakpoint set -n "[ViewController save:]" -n "[ViewController pauseGame:]" -n "[ViewController continueGame:]"
1.3 LLDB 设置断点失效、启用
// 使某个断点失效
breakpoint disable 1
//启用某个断点
breakpoint enable 1
注意:
breakpoint disable 1
使当前的这一组断点全部失效。
breakpoint disable 1.1
使某一个断点失效
使某一个断点失效:
1.4 LLDB 删除断点
// delete 使用
breakpoint delete 1.1
breakpoint delete 1
注意:
breakpoint delete 1.1
是使某个断点失效。
breakpoint delete 1
是删除第一组的数据。删除只能删除指定的组。
breakpoint delete
删除所有的断点。
1.5 LLDB 给某一个函数设置断点(批量设置断点)
// 给某一个函数设置断点(批量设置断点)
breakpoint set --select touchesBegan:withEvent:
1.6 LLDB 根据某个字符串设置断点
// 遍历文件/系统中含有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.2 使用LLDB命令给数据设值
// 获取数据(拿到最后一个数据)
p (Person *)self.models.lastObject
// 设值(上一步会返回一个标记,我们使用这个标记就好了,类似于动态对象,如下图)
2.3 使用LLDB命令查看数据值
1.先获取数据
2.读取数据
2.4 使用LLDB执行代码
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堆栈
LLDB 选择堆栈,查看参数
LLDB 修改堆栈函数的参数
注意:
- 当我们使用frame查看指定堆栈,修改函数的参数的时候
frame 修改参数
frame select 1 // 切换堆栈
frame variable // 修改查看参数
p str = @"123" // 设置新参数
frame variable // 查看新设置的参数
如果后面有输出语句,参数依然是输出之前的参数,这是因为我们代码已经走到这里了,函数调用关系已经确定。
- 回滚指令 thread return
1.回滚指令之后函数不会再往下执行。
2.函数直接返回,不会再执行断点之后的代码
3.up指令不会改变其执行的流程
- 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)内存断点
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
如下图:
命令名称 | 命令例子 |
---|---|
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
以下是部分命令
命令名称 | 命令例子 |
---|---|
增加一个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调试器使用简介