iOS 逆向 - LLDB

前言

在进入逆向正式实战以前 , 动态调试和静态分析都是咱们必不可少的能力 .面试

LLDB 是无论在正向开发仍是逆向开发中 , 都是帮助咱们调试必不可少的手段 . 而在逆向开发中不能像正向开发同样页面断点 , 可视化数据展现 , 源代码调试等方式的状况下 , LLDB 的做用就会尤为重要 .express

  • 考虑到并非全部同窗都会亲自建一个工程来实战演练一下 LLDB , 本篇文章我会结合实战来介绍 LLDB 经常使用指令的实际效果 , 以此来更好地理解和记忆 LLDB ( 毕竟面试仍是常常会问到的 ) , 熟悉的同窗能够自行跳过 .vim

  • 另外笔者写指令时会尽可能写全 , 全称指令有助于理解 , 而且之后再换为简写也很简单 , 反过来就不行了 . 所以大佬勿喷 . bash

LLDB

概述

默认内置于 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
复制代码

经常使用断点指令

1、过掉这个断点

  • 指令 : continue , 简写为 c .
  • 单步走为 step , 简写为 s , 遇到嵌套子函数会进去.
  • 单步运行 next , 简写为 n , 遇到嵌套子函数会当作总体一步执行 .

2、查看当前断点列表

  • 指令 : breakpoint list , 简写为 b l工具

    ( 咱们在 Xcode 可视化加的断点也能够被查看到 , 由于自己都在寄存器中 ) .ui

3、方法名加断点

  • 指令 : breakpoint set --name testFuncatom

  • 结果 : spa

  • 说明 :线程

    该指令直接经过方法名称下断点 . --name 可简写为 -n

  • 查看断点 :

  • 结果验证 : continue 过掉断点 , 点击屏幕 , 来到咱们下的断点 .

4、一次经过多个方法名加断点

  • 指令 :

    breakpoint set --name "-[ViewController btnAction1:]" --name "-[ViewController btnAction2:]" --name "-[ViewController btnAction3:]"
    复制代码
  • 结果 :

  • 说明 :

    该指令直接经过一次添加多个方法名称下断点 . --name 可简写为 -n , 添加结果是一组断点 , 可是为多个位置 .

  • 查看断点 :

5、禁用 / 启用断点

  • 指令 : breakpoint disable 5 / breakpoint enable 5

  • 结果 :

  • 说明 :

    经过 breakpoint list 先查看断点 ID , 而后禁用或者启用某一个或者某一组断点.

  • 查看断点 :

6、经过 sel 加断点

  • 指令 : breakpoint set --selector touchesBegan:withEvent:

  • 结果 :

  • 说明 :

    • 经过 selector 添加断点 , 不局限于某个类 , 当再添加 --file 指令时 , 会具体到该类中寻找这个 sel 下断点 .

    • 例: breakpoint set --file ViewController.m --selector touchesBegan:withEvent:

  • 查看断点 :

7、不完整 sel 加断点

  • 指令 : breakpoint set --func-regex btnActi

  • 结果 :

  • 说明 :

    • 经过 并无写完整的selector 添加断点 , 可是并不是是模糊搜索的模式 , 用的正则来处理的 , 所以输入的名称不能错 , 只能是未写完 .
    • 也可结合 --file 指令来针对某个类处理
  • 查看断点 :

8、删除断点

  • 指令 : breakpoint delete 10

  • 结果 :

  • 说明 :

    不传断点 ID 则为删除全部断点 , 会提示确认操做.

  • 查看断点 :

提示

  • LLDB 指令对断点作的操做与 Xcode 可视化页面中对断点所作的添加 / 删除 / 禁用 / 启用 操做都是同步的.

  • 当删除断点时 , 不能删除某一组中的一个 , 删除会变成 disable , 只能删除组

  • breakpoint set 可直接简写为 b

  • breakpoint list 可简写为 break l , breakpoint disable 可简写为 break dis 等等以此类推 , 能够简写到只要系统能够区分你到底敲的是哪一条命令均可以

其余经常使用命令

执行代码 expression / p

  • 指令 : expression self.view.subviews , 简写 p

  • 结果 :

  • 说明 :

    pexpression 的简写 , 并不是不少同窗理解的 print 哦 , poexpression -O ( --object-description NSObjectdescription 方法 ) 的简写.

  • 使用举例 :

    • 执行代码 : p self.view.backgroundColor = [UIColor orangeColor];
    • 过掉断点 : c
    • 页面效果 :

一样的 , p 能够写多句代码 , 使用 ; 便可 . 该方法经常使用与逆向中 Cycript 检查是不是咱们要找的 view .

函数调用栈

  • 指令 : thread backtrace , 简写 bt ,
  • 结果 :
  • 说明 :
    • bt查看函数调用流程 , 可加上要查看的数量 , 例如 bt 3
    • 经过 up / down 方法能够跳转到 前一个 / 后一个 方法中
    • 经过 frame select 5 也能够直接经过编号跳转对应方法
    • 经过 frame variable 能够查看方法参数
    • 经过 thread return 能够线程回滚 , 并且能够修改参数 , 可是执行完该函数会直接 return .

还有一些其余的指令这里就不一一列举了 , 经过 help 指令能够查看自行练习.

image 相关

image list
  • 指令 : image list
  • 说明 : 查看当前进程加载了哪些库 .
  • 结果 :
image lookup
  • 指令 : image lookup -t LBPerson
  • 说明 : 查看某个类信息 .
  • 结果 :

逆向经常使用命令

刚刚咱们看了不少断点调试指令 , 那么逆向过程当中呢 ? 咱们并无源码 , 结果就是 : 不少经过方法名称下断点等方式都不能用 😶 .

  • 为何 ?

    • 咱们就一个 Mach-O , 没有符号 , 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

过掉断点 . 点击屏幕 , 修改了 personname , 监控到内存断点 , 并打印以下 :

Watchpoint 1 hit:
old value: 0x0000000107f72078
new value: 0x0000000107f720b8
复制代码

查看内容 .

经过函数调用栈能够看到实际上是在属性的 set 方法时调用的 .

经过内存地址下断点

逆向中咱们每每只有内存地址 , 甚至不必定拿获得变量或者属性 . 那么就须要经过内存地址下断点 .

一样刚刚的代码 , 从新运行来到 NSLog 断点.

  • p 查看 name 内存地址 , p &self->_person->_name
  • watchpoint set expression 0x0000600000db2f70
  • 过掉断点 , 点击屏幕
  • 检测到改变

内存断点的删除 禁用 查看列表

同上述 breakpoint , 很少赘述.

LLDB高级用法

特定断点添加指令

@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");
}
复制代码
1. 给 testFunc 添加断点

指令 : b testFunc

2. 给这个断点添加指令

指令 : breakpoint command add 1

3. 输入要添加的指令

输入 :

>po self
>p self.view.backgroundColor = [UIColor orangeColor];
>DONE
复制代码

过掉断点 , 点击屏幕.

结果 :

再过掉断点 , 查看页面 .

这种方法适用场景较多 , 例如某个断点一断住就查看形参 等等 , 自由发挥 .

一样的 查看断点指令列表

breakpoint command list 1

删除断点指令

breakpoint command list 1

stop-hook

  • 指令 : 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" .
  • 若是你没有这个文件 . 不要紧 直接 vim 建立 而后写上 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 + 虚拟地址

相关文章
相关标签/搜索