LLDB汇编调试

LLDB

  • 官方文档

  • 资料

  • 简介:LLDB是个开源的内置于XCode的调试工具,给我们平时开发调试带来很大的便利,同时它对我们逆向分析别人的APP同样有很大的帮助

  • 查看指令的用法

    $help [cmd]
    
  • 内存查看

    $memory read 0x地址(缩写是:x) //可以查看寄存器内存 比如str 是把寄存器值写到内存中,可以在前后下断点用memory查看
    $memory write 0xaddress 0x65 //写入
    
  • 寄存器操作

    $register write r0 1
    $register read  //打印寄存器list
    $p/x $lr // 查看LR funcA调用funcB,funcB结束后,会回到funcA。如果能知道LR的值就知道funcB被谁调用
    $p/x $x0 //打印寄存器地址  可以执行运算 比如 p/x 0x0064000 + 0xff 
    $x/10 $sp //打印从栈底开始连续的10个字 一般情况下,oc方法在栈中参数不会超过10个,这个命令就足够了,挨个打印 然后 po address
    $dis -a 内存地址。 通过内存看函数 拿到内存后可以通过此命令查看汇编代码
    $po $x1
    $pblock 0xaaaaa   //打印函数的Block参数
    $methods 0xffffff //查看对像 及属性方法
    $search UItextfiled //搜索UItextfiled的对象和实例
    

    p/x 表示输出16进制,还有 p/t(10进制) p/o(8进制)

  • 查看代码段

    $image lookup -a 0x102110226 //汇编的地址
    $image lookup -t Person //快速查看一个类
    $image list //系统加载的各种文件信息
    $image list -o -f //模块偏移地址
    
  • X命令是直接输出内存内容 格式为$x/nfu target

    $x/16xb charArray  
    $x/16xb self
    $x/c $x1 //打印X0掉用函数
    $x/s $1 
    

参数解释:
n,表示要显示的内存单元的个数
f,表示显示方式, 可取如下值:
参数 | 含义
---|---
x | 按十六进制格式显示变量
d | 按十进制格式显示变量
u | 按十进制格式显示无符号整型
o | 按八进制格式显示变量
t | 按二进制格式显示变量
a | 按十六进制格式显示变量
i | 指令地址格式
c | 按字符格式显示变量
s | 按字符串格式显示变量
f | 按浮点数格式显示变量
u,表示一个地址单元的长度:
参数 | 含义
---|---
b | 按单字节分段输出
h | 按双字节分段输出
w | 按四字节分段输出
g | 按八字节分段输出
target,表示内存地址,可以是变量名,也可以是内存地址。

  • 断点操作
    $bt //查看堆栈列表
    $up //断下后才可以使用 往函数上一层
    $down //断下后才可以使用 往函数下一层
    $b set -n "C函数名"
    $br s -a 0x1029c28d0  //给内存地址下断点 常用
    $b -[class method] //给类的方法下断点 常用
    $b set -n "-[OC类 方法名:]"
    $b set -r 方法名: //遍历整个项目中,为所有同名的所有方法下断点 
    $br delete //删除全部断点 也可以指定index 
    $n next //遇到子函数也一并执行
    $s //单步运行,遇到子函数会进去
    $ni 和n一样 只是针对汇编 上两个是针对源代码
    $si 和s一样, 只是针对汇编
    $continue //继续执行
    $c
    $b list //查看断点列表
    $breakpoint disable //断点禁用 
    $breakpoint enable //断点启用  
    $watchpoint set variable p1->name 
    $watchpoint set expression 0x312315(一个内存地址)
    $stop-hook - 让你在每次stop的时候去执行一些命令,只对breadpoint,watchpoint
    $target stop-hook list
    $target stop-hook add -o "frame variable"
    $target stop-hook delete
    $frame select index //定位到第index层的代码位置
    $frame variable 查看所有参数,可以通过p进行修改
    $thread return 代码回滚到上一层,并退出
    

传统下断点的方式

  1. LLDB链接到程序
  2. 调动命令
im li -o -f Aweme #查看二进制模块偏移

3.在反编译工具中查看要断点位置的基地址
4.计算偏移后的地址

p/x 基地址 + 偏移地址

br -s -a 地址

但以上存在两个问题
1.需要工具支持IDA Hopper
2.不能对已经通过运行时替换函数地址的函数

改进

作为一个逆向开发新手,特分享一个以小技巧,可以免除对反汇编软件的依赖,而且拦截成功率更高。

LLDB连接到程序

--[模型对象 _shortMethodDescription]

找到需要下断点的类,如MMServiceCenter,然后在LLDB命令行输入po [MMServiceCenter _shortMethodDescription]。以微信的[MMServiceCenter getService:]断点为例,操作如下:
image

然后在命令行输入br -s -a 0x100bd04f0 即可下断点

--[模型对象 _ivarDescription]
使用方式如上

断点设置

如果有不会的可以查看LLDB官方文档
- 设置地址
br s -a addr  //eg  br s -a 0x0000193c  或者 br s -a  0x0000193b + 0x00013bc
或者
b  addr  // b 0x0000193c

- 设置方法名断点

breakpoint/b set -n xxx  //xxx:方法名

- 在名为main的所有函数上设置断点。
br s -n main

-在文件的某一行下断点
br s -f test.c -l 12 
#或者
b test.c:12

-通过函数名称下断点
(lldb) breakpoint set --name "-[NSString stringWithFormat:]"
(lldb) br s -n "-[NSString stringWithFormat:]"
(lldb) b -[NSString stringWithFormat:]

expression命令

打印变量

print 简写p 是 expression -- 别名,打印基本数据类型。

po 是 expr -o -- 的别名。

(lldb) expr -o -- [SomeClass returnAnObject]
or using the po alias:
(lldb) po [SomeClass returnAnObject]

以特定格式打印变量
下面分别以16进制(x),字符(c),二进制(t)打印变量

(lldb) p/x 2
(int) $0 = 0x00000002
lldb) p/c (char)97
(char) $2 = 'a'
(lldb) p/t 2
(int) $4 = 0b00000000000000000000000000000010

调试信息

  • frame info 可以查看当前调试的行数和源码信息
(lldb) frame info
frame #0: 0x0000000104cc6d1c TestPAD`-[ViewController testParam:b:c:d:](self=0x0000000149d0aaa0, _cmd="testParam:b:c:d:", a=10, b=20, c=30, d=40) at ViewController.m:31

  • thread info 可以查看当前调试线程、行数、和源码信息
(lldb) thread info 
thread #1: tid = 0xfb0ab5, 0x0000000104cc6d1c TestPAD`-[ViewController testParam:b:c:d:](self=0x0000000149d0aaa0, _cmd="testParam:b:c:d:", a=10, b=20, c=30, d=40) at ViewController.m:31, queue = 'com.apple.main-thread', stop reason = breakpoint 9.1

  • thread list 可以查看当前所有线程的调试状态
lldb) thread list
Process 29252 stopped
* thread #1: tid = 0xfb0ab5, 0x0000000104cc6d1c TestPAD`-[ViewController testParam:b:c:d:](self=0x0000000149d0aaa0, _cmd="testParam:b:c:d:", a=10, b=20, c=30, d=40) at ViewController.m:31, queue = 'com.apple.main-thread', stop reason = breakpoint 9.1
  thread #3: tid = 0xfb0ad8, 0x000000018608fdf4 libsystem_dnssd.dylib`ConvertHeaderBytes, queue = 'com.skyeye.analytics.network.queue'
  thread #4: tid = 0xfb0ad9, 0x00000001860f5dbc libsystem_kernel.dylib`__workq_kernreturn + 8
  thread #5: tid = 0xfb0ada, 0x0000000186206c1c libsystem_pthread.dylib`start_wqthread
  thread #6: tid = 0xfb0adb, 0x00000001860d4bc4 libsystem_kernel.dylib`mach_msg_trap + 8, queue = 'com.SkyEye.905541C85D654B539C85DEECF2689651.0x1c0462b00.network'
  thread #7: tid = 0xfb0adc, 0x00000001860d4bc4 libsystem_kernel.dylib`mach_msg_trap + 8, name = 'com.apple.uikit.eventfetch-thread'
  thread #8: tid = 0xfb0ade, 0x00000001860f5c1c libsystem_kernel.dylib`__ulock_wait + 8, queue = 'com.skyeye.analytics.interface.queue'
  thread #9: tid = 0xfb0ae0, 0x00000001860d4bc4 libsystem_kernel.dylib`mach_msg_trap + 8, name = 'com.apple.NSURLConnectionLoader'
  • frame variable(简写 fr v)当前调试堆栈的所有参数和临时变量
(lldb) frame variable
(ViewController *) self = 0x0000000149d0aaa0
(SEL) _cmd = "testParam:b:c:d:"
(int) a = 10
(int) b = 20
(int) c = 30
(int) d = 40
(int) ii = 10000

  • register read 返回当前线程通用寄存器的值(对64为对应x0-x31)
lldb) register read
General Purpose Registers:
        x0 = 0x0000000149d0aaa0
        x1 = 0x0000000104d9dd46  "testParam:b:c:d:"
        x2 = 0x000000000000000a
        x3 = 0x0000000000000014
        x4 = 0x000000000000001e
        x5 = 0x0000000000000028
        x6 = 0x0000000000000000
        x7 = 0x000000016b156808
        x8 = 0x0000000104dd9330  "testParam:b:c:d:"
        x9 = 0x0000000000000000
       x10 = 0x0086860100868680
       x11 = 0x0000000000868601
       x12 = 0x0000000000868500
       x13 = 0x0000000000000001
       x14 = 0x0000000000000000
       x15 = 0x00868601008686c0
       x16 = 0x0000000000000000
       x17 = 0x0000000104cc6cf4  TestPAD`-[ViewController testParam:b:c:d:] at ViewController.m:29
       x18 = 0x0000000000000000
       x19 = 0x00000001b70ab8c0  UIKit`_UIApplicationLinkedOnVersion
       x20 = 0x0000000149d0aaa0
       x21 = 0x0000000000000018
       x22 = 0x0000000190799d6a  "count"
       x23 = 0x0000000000000000
       x24 = 0x0000000000000000
       x25 = 0x000000014a017c00
       x26 = 0x0000000000000408
       x27 = 0x00000001c0099410
       x28 = 0x0000000000000000
        fp = 0x000000016b157f60
        lr = 0x0000000104cc6bbc  TestPAD`-[ViewController viewDidLoad] + 164 at ViewController.m:24
        sp = 0x000000016b157f30
        pc = 0x0000000104cc6d1c  TestPAD`-[ViewController testParam:b:c:d:] + 40 at ViewController.m:31
      cpsr = 0x20000000


  • memory read (简写x)以给定格式读取给定内存地址数据

(1)比如以字符串读取x1寄存器的值

(lldb) memory read -f s $x1
或者
(lldb) x -f s $x1
或者
(lldb) x/s $x1
输出为:
0x104d9dd46: "testParam:b:c:d:"

(2)读取栈中所有值,即sp和fp连续内存区域值。

(lldb) x -f A $sp $fp

(3)每多少字节读取某个地址

memory read --size 8 --format x 0x17425ff20
#或者
x/8 0x17425ff20

#node:函数指针占8个字节,int占4个字节
几个实用的命令
  • 查看层级
pviews
  • 查看某个控件的响应链
presponder 0x1133d32
  • 查看按钮Action事件
pactions 0x1133d32
  • 查看block参数
pblock 0x134a32d
  • 查看某个对象所有的方法
methods 0x13d42
  • 搜索UITextField的实例对象及地址
search UITextField
  • 对某个类所有的方法下断点并跟踪打印调用参数,
    bclass
//例如
blcass LoginViewController
br command  add 1
Enter your debugger command(s). Type 'DONE' to end

> po $0  //打印第一个参数(调用类)
> x/s $1 //打印方法
> c     //下一步
> DONE
  • 查看某个地址所在模块的信息
  • image lookup
//例如
(lldb) b [LoginRegiseterViewController snapchatterButtonClicked]
Brekpoint 1: where = Snapchat -[LoginRegiseterViewController snapchatterButtonClicked],
address = 0x0000012da2

(lldb) c
 Process 18474 resuming
 
 (lldb) image lookup -a 0x0000012da2
 Adress: ---对应的偏移地址-----
 Summary: ---对应的函数信息---

对模块中进行模糊查询有关的符号信息

  • image lookup -rn
#查看snapchat中所有带有login字符串的符号信息

(lldb)image lookup -rn login snapchat

 Adress: ---对应的偏移地址-----
 Summary: ---对应的函数信息---
 等等