在进入逆向正式实战以前 , 动态调试和静态分析都是咱们必不可少的能力 .面试
LLDB
是无论在正向开发仍是逆向开发中 , 都是帮助咱们调试必不可少的手段 . 而在逆向开发中不能像正向开发同样页面断点 , 可视化数据展现 , 源代码调试等方式的状况下 , LLDB
的做用就会尤为重要 .express
考虑到并非全部同窗都会亲自建一个工程来实战演练一下 LLDB
, 本篇文章我会结合实战来介绍 LLDB
经常使用指令的实际效果 , 以此来更好地理解和记忆 LLDB
( 毕竟面试仍是常常会问到的 ) , 熟悉的同窗能够自行跳过 .vim
另外笔者写指令时会尽可能写全 , 全称指令有助于理解 , 而且之后再换为简写也很简单 , 反过来就不行了 . 所以大佬勿喷 . bash
默认内置于
Xcode
中的动态调试工具。标准的LLDB
提供了一组普遍的命令,旨在与老版本的GDB
命令兼容。 除了使用标准配置外,还能够很容易地自定义LLDB
以知足实际须要。函数
help
help
能够查看某一条指令的说明 : breakpoint help
.
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSLog(@"touch screen");
testFunc();
}
void testFunc(){
printf("testFunc");
}
- (IBAction)btnAction1:(id)sender {
}
- (IBAction)btnAction2:(id)sender {
}
- (IBAction)btnAction3:(id)sender {
}
@end
复制代码
continue
, 简写为 c
.step
, 简写为 s
, 遇到嵌套子函数会进去.next
, 简写为 n
, 遇到嵌套子函数会当作总体一步执行 .指令 : breakpoint list
, 简写为 b l
工具
( 咱们在 Xcode
可视化加的断点也能够被查看到 , 由于自己都在寄存器中 ) .ui
指令 : breakpoint set --name testFunc
atom
结果 : spa
说明 :线程
该指令直接经过方法名称下断点 . --name
可简写为 -n
查看断点 :
结果验证 : continue 过掉断点 , 点击屏幕 , 来到咱们下的断点 .
指令 :
breakpoint set --name "-[ViewController btnAction1:]" --name "-[ViewController btnAction2:]" --name "-[ViewController btnAction3:]"
复制代码
结果 :
说明 :
该指令直接经过一次添加多个方法名称下断点 . --name
可简写为 -n
, 添加结果是一组断点 , 可是为多个位置 .
查看断点 :
指令 : breakpoint disable 5
/ breakpoint enable 5
结果 :
说明 :
经过 breakpoint list
先查看断点 ID
, 而后禁用或者启用某一个或者某一组断点.
查看断点 :
指令 : breakpoint set --selector touchesBegan:withEvent:
结果 :
说明 :
经过 selector
添加断点 , 不局限于某个类 , 当再添加 --file
指令时 , 会具体到该类中寻找这个 sel
下断点 .
例: breakpoint set --file ViewController.m --selector touchesBegan:withEvent:
查看断点 :
指令 : breakpoint set --func-regex btnActi
结果 :
说明 :
selector
添加断点 , 可是并不是是模糊搜索的模式 , 用的正则来处理的 , 所以输入的名称不能错 , 只能是未写完 .--file
指令来针对某个类处理 查看断点 :
指令 : breakpoint delete 10
结果 :
说明 :
不传断点 ID
则为删除全部断点 , 会提示确认操做.
查看断点 :
LLDB
指令对断点作的操做与Xcode
可视化页面中对断点所作的添加 / 删除 / 禁用 / 启用 操做都是同步的.当删除断点时 , 不能删除某一组中的一个 , 删除会变成
disable
, 只能删除组
breakpoint set
可直接简写为b
breakpoint list
可简写为break l
,breakpoint disable
可简写为break dis
等等以此类推 , 能够简写到只要系统能够区分你到底敲的是哪一条命令均可以
指令 : expression self.view.subviews
, 简写 p
结果 :
说明 :
p
为 expression
的简写 , 并不是不少同窗理解的 print
哦 , po
是 expression -O
( --object-description
NSObject
的 description
方法 ) 的简写.
使用举例 :
p self.view.backgroundColor = [UIColor orangeColor];
c
一样的 , p
能够写多句代码 , 使用 ;
便可 . 该方法经常使用与逆向中 Cycript
检查是不是咱们要找的 view
.
thread backtrace
, 简写 bt
,bt 3
up / down
方法能够跳转到 前一个
/ 后一个
方法中 frame select 5
也能够直接经过编号跳转对应方法frame variable
能够查看方法参数
thread return
能够线程回滚 , 并且能够修改参数 , 可是执行完该函数会直接 return
.还有一些其余的指令这里就不一一列举了 , 经过 help
指令能够查看自行练习.
image list
image lookup -t LBPerson
刚刚咱们看了不少断点调试指令 , 那么逆向过程当中呢 ? 咱们并无源码 , 结果就是 : 不少经过方法名称下断点等方式都不能用 😶 .
为何 ?
release
模式的状况下 , 符号是会被编译器自动去掉的 .怎么办 ?
#import "ViewController.h"
@interface LBPerson : NSObject
@property(nonatomic , assign) int age;
@property(nonatomic , copy) NSString * name;
@end
@implementation LBPerson
@end
@interface ViewController ()
@property(nonatomic, strong) LBPerson * person;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.person = [[LBPerson alloc] init];
self.person.age = 20;
self.person.name = @"lb";
NSLog(@"haha");
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
self.person.name = @"test";
}
复制代码
在 NSLog
处加个断点 , 运行 .
来到断点后输入指令 : watchpoint set variable self->_person->_name
过掉断点 . 点击屏幕 , 修改了 person
的 name
, 监控到内存断点 , 并打印以下 :
Watchpoint 1 hit:
old value: 0x0000000107f72078
new value: 0x0000000107f720b8
复制代码
查看内容 .
set
方法时调用的 .
逆向中咱们每每只有内存地址 , 甚至不必定拿获得变量或者属性 . 那么就须要经过内存地址下断点 .
一样刚刚的代码 , 从新运行来到 NSLog
断点.
p
查看 name
内存地址 , p &self->_person->_name
watchpoint set expression 0x0000600000db2f70
同上述 breakpoint
, 很少赘述.
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
NSLog(@"touch screen");
[self test];
}
- (void)test{
NSLog(@"Add Command test");
}
复制代码
testFunc
添加断点指令 : b testFunc
指令 : breakpoint command add 1
输入 :
>po self
>p self.view.backgroundColor = [UIColor orangeColor];
>DONE
复制代码
过掉断点 , 点击屏幕.
再过掉断点 , 查看页面 .
这种方法适用场景较多 , 例如某个断点一断住就查看形参 等等 , 自由发挥 .
breakpoint command list 1
breakpoint command list 1
指令 : target stop-hook add --one "frame variable"
说明 :
--one
表明添加一条指令 , 可简写为-o
- 可视化页面中
Pause paogram execution
以及Debug view
不属于stop
范畴.
结果: 在 TouchBegan
添加一个断点 , 点击屏幕 , 打印以下 :
相较于上一个 特定断点添加指令 , 显然是更通用 .
那么一样 , 查看断点指令列表 : target stop-hook list
target stop-hook delete
, 使用
undisplay 1
, 也是同样的 .
禁用
/
启用
同理 .
以上咱们说的这些 , 创建在工程运行起来以后 , 进入 lldb
模式 , 添加指令等操做的前提下 . 那么咱们思考一个问题 , 每次运行工程都要从新添加 , 能否自动处理呢 ?
答案是确定的 .
打开 终端
/ iTerm2
. 来到家目录下 , ls -a
查看文件.
.lldbinit
文件 , 直接 vim
添加 target stop-hook add -o "frame variable"
.target stop-hook add -o "frame variable"
.来到工程中 , 从新 run
一下 , 点击屏幕方法添加一个断点 , 点击屏幕 :
其缘由是
lldb
在启动时会加载这个文件 , 对每一个工程都有效 .
最后简单说一下逆向过程当中如何下一个内存断点
MachOView
/ Hopper
, 找到方法地址 , 减去 PageZero
的虚拟内存 (也就是最前面的那个 1
, / 64 位下是 4 个 G, ) 获得方法基于 Mach-O
首地址的真实偏移地址.lldb
查看 Mach-O
地址 .这个是因为 ASLR 的缘由 . 说白了就是地址空间配置随机加载 , 所以咱们须要在加载完后再获取 Mach-O
的地址便可.
物理地址 = ALSR + 虚拟地址