LLDB常用命令--飘云整理

这里整理一下飘哥常用的,我会慢慢添加并总结一些经验。

lldb命令支持简写,自己慢慢研究吧

优雅人生飘云原创整理,转载请注明出处!
https://www.dllhook.com
https://www.chinapyg.com

官方文档:http://lldb.llvm.org/

本地调试(macOS)

(lldb) target create "/Users/piaoyun/Desktop/xx.app/Contents/MacOS/xxxx"

带参数运行

(lldb) target create "/bin/ls"
Current executable set to '/bin/ls' (x86_64).
(lldb) set set target.run-args  $(python \-c 'print "a"*200')
(lldb) run
Process 40752 launched: '/bin/ls' (x86_64)
ls: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: No such file or directory
Process 40752 exited with status = 1 (0x00000001)
(lldb)

远程调试

iOS设备端

  • 附加进程:

    $ ./debugserver *:1234 -a "YourAPPName"
    
  • 直接启动进程:

    $ debugserver -x backboard *:1234 /path/to/app/executable
    
  • 例:

  • $ debugserver -x backboard *:1234 /Applications/MobileNotes.app/MobileNotes
    

此命令会启动记事本,并断在dyld的第一条指令上

macOS端

在macOS终端运行LLDB命令后,输入以下2条命令:

$ platform select remote-ios
$ process connect connect://你的设备IP地址:1234

用USB连接

  • 老版本usbmuxd

1.wget下载源代码

$ wget http://cgit.sukimashita.com/usbmuxd.git/snapshot/usbmuxd-1.0.8.tar.bz2
$ tar xjfv usbmuxd-1.0.8.tar.bz2
$ cd usbmuxd-1.0.8/python-client/
$ python tcprelay.py -t 1234:1234

2.在macOS终端运行LLDB命令后,输入以下2条命令:

(lldb) platform select remote-ios
(lldb) process connect connect://localhost:1234
  • 新版本usbmuxd

1.用brew安装

$ brew install usbmuxd

2.端口映射

$ iproxy 2222 22 [设备UUID]

3.另外开启一个终端用2222端口进行连接

$ ssh –p 2222 root@127.0.0.1

expression(执行表达式)

简写:expr

  • 实例OC代码
-(void)loginWithUserName:(NSString *)username password:(NSString *)password
{
    NSLog(@"login.... username:%@   password:%@", username, password);  // 假设我们在此下断点
}

例子:

(lldb) expression $r6 = 1   // 设置r6寄存器的值
(lldb) expression $r6       // 查看r6寄存器的值
(lldb) expression username(源代码中变量) = @"www.dllhook.com"
(lldb) expression [self btnTest]     // 调用某类某方法

po(打印参数)

OC代码同上一节

例子:

(lldb) po $r6
(lldb) po username
(lldb) po [[NSString alloc] initWithData:$r2 encoding:4]   // 打印$r2寄存器的明文数据
(lldb) po [$r5 base64EncodedStringWithOptions:0];          // 打印$r5寄存器(NSData)类型的base64明文数据

print(打印参数)

print (type)

例子:

(lldb) print (int)$r6
(lldb) print username

backtrace(调用栈信息)

简写:bt

例子:

(lldb) bt
// 返回如下:
* thread #1: tid = 0x1ee09, 0x00035e80 debug`-[ViewController loginWithUserName:password:](self=0x15d7be60, _cmd=0x00036441, username=0x15db0120, password=0x0003768c) + 168 at ViewController.m:34, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1

* frame #0: 0x00035e80 debug`-[ViewController loginWithUserName:password:](self=0x15d7be60, _cmd=0x00036441, username=0x15db0120, password=0x0003768c) + 168 at ViewController.m:34

frame(栈帧信息)

查看当前栈帧信息

(lldb) frame info
frame #0: 0x0000000197c60c30 libsystem_asl.dylib` asl_open

选择栈帧

语法:frame select 序号

(lldb) frame select 0
frame #0: 0x0000000197c60c30 libsystem_asl.dylib` asl_open
libsystem_asl.dylib`asl_open:
->  0x197c60c30 <+0>:  stp    x20, x19, [sp, #-32]!
    0x197c60c34 <+4>:  stp    x29, x30, [sp, #16]
    0x197c60c38 <+8>:  add    x29, sp, #16              ; =16
    0x197c60c3c <+12>: mov    x20, x2
    0x197c60c40 <+16>: bl     0x197c64798               ; asl_client_open
    0x197c60c44 <+20>: mov    x19, x0
    0x197c60c48 <+24>: cbz    x19, 0x197c60c70          ; <+64>
    0x197c60c4c <+28>: movz   w0, #0

breakpoint(打印断点列表)

breakpoint可以简写为br

打印断点列表

  • breakpoint list // 打印断点列表

例子:

(lldb) br l
// 返回如下:
Current breakpoints:
1: file = '/Users/piao/Desktop/debug/debug/main.m', line = 16, locations = 1, resolved = 1, hit count = 1
  1.1: where = debug`main + 54 at main.m:16, address = 0x00036232, resolved, hit count = 1 
2: file = '/Users/piao/Desktop/debug/debug/ViewController.m', line = 34, locations = 1, resolved = 1, hit count = 1
  2.1: where = debug`-[ViewController loginWithUserName:password:] + 168 at ViewController.m:34, address = 0x00035e80, resolved, hit count = 1 

函数地址下断

(lldb) breakpoint set -a 函数地址   // 常规断点

匹配特征字下断

飘云提示:这个非常有用!虽然官方文档一直有,但是没重视。

(lldb) breakpoint set --func-regex 函数/方法关键字

例子:

(lldb) breakpoint set --func-regex ptrace
Breakpoint 1: where = libsystem_kernel.dylib`__ptrace, address = 0x0000000197d6b244
(lldb) breakpoint set --func-regex open
Breakpoint 2: 1130 locations.

这样下断的优势:

比如在某动态库中有 testA函数,那么常规做法是先 image list -o -f 查看模块基址 然后 image lookup -r -n 函数关键字找到偏移 然后再 br s -a 基址+偏移!

用上面这个命令下端就简洁方便了!!!lldb会自动帮你下断所有匹配特征字的断点,可以模糊匹配哦

对动态库函数下断

(lldb) breakpoint set --shlib foo.dylib --name foo

对某个OC方法下断

(lldb) breakpoint set --method/--name "-[TSRegistWinCtrl showRegister]"

对某个OC类的所有方法下断

(lldb) breakpoint set -r '\[RegisterWindowController .*\]'

// 或者
(lldb) breakpoint set --func-regex '\[RegisterViewController .*\]'

在某个OC所有同名方法设置断点

下面例子对每个viewDidLoad方法都设置断点:

(lldb) breakpoint set --selector viewDidLoad

断点命令处理

这个非常有用,可以进行断点过程中的一些自动化处理:

(lldb) breakpoint command add 断点序号

对导出函数下断

这个非常有用,对导出函数下断非常好 / 貌似是模糊匹配:

(lldb) breakpoint set -F isTest
// 可以简写为: b isTest

run(运行程序)

简写:r

process continue(继续执行)

简写:c

step(源码级步入)

简写:s

源码级别单步执行,遇到子函数则进入。

stepi(指令级步入)

简写:si

单步执行,遇到子函数则进入。

next(源码级步过)

简写:n

源码级别单步执行,遇到子函数不进入,直接步过。

nexti(指令级步过)

简写:ni

单步执行,遇到子函数不进入,直接步过。

finish(完成并退出子函数)

完成并退出子函数

return(退出子函数)

直接退出子函数

thread(线程操作)

(lldb) thread list // 打印线程列表

image (镜像操作)

列出所有依赖库

(lldb) image list
[  0] 69F6C476-555D-3987-B162-E62C9A1D718C 0x000000010008c000 /var/mobile/Containers/Bundle/Application/D6B51B8B-00F8-443F-9B8E-EF47C2330D2B/WhatsApp.app/WhatsApp (0x000000010008c000)
[  1] 3134CFB2-F722-310E-A2C7-42AE4DC131AB 0x00000001017ec000 /Library/MobileSubstrate/MobileSubstrate.dylib (0x00000001017ec000)
[  2] 4DB79D94-6764-3180-99C9-5615E29073E7 0x0000000196848000 /Users/piao/Library/Developer/Xcode/iOS DeviceSupport/9.0.2 (13A452)/Symbols/usr/lib/libSystem.B.dylib

查找ASLR偏移

(lldb) image list -f -o WhatsApp
[  0] /var/mobile/Containers/Bundle/Application/D6B51B8B-00F8-443F-9B8E-EF47C2330D2B/WhatsApp.app/WhatsApp 0x000000000008c000(0x000000010008c000)

ASLR偏移:0x000000000008c000

打印加载模块列表

(lldb) image list -f -o // 通常带这两个参数使用
[  0] 40E417A4-F966-3DB4-B028-B0272DC016A7 0x000a0000 /Users/piao/Library/Developer/Xcode/DerivedData/debug-bdkhskdqykkoqmhjedilckzvpuls/Build/Products/Debug-iphoneos/debug.app/debug 
      /Users/piao/Library/Developer/Xcode/DerivedData/debug-bdkhskdqykkoqmhjedilckzvpuls/Build/Products/Debug-iphoneos/debug.app.dSYM/Contents/Resources/DWARF/debug
[  1] 011601C0-F561-3306-846B-94A7C8C841DA 0x2d9e6000 /Users/piao/Library/Developer/Xcode/iOS DeviceSupport/7.1.2 (11D257)/Symbols/System/Library/Frameworks/CoreGraphics.framework/CoreGraphics

查找原始地址信息

在可执行文件或任何共享库中查找原始地址的信息。

  • image lookup -a 表达式
    例子:
(lldb) image lookup -a $pc
Address: debug[0x0000b236] (debug.__TEXT.__text + 1254)
Summary: debug`main + 58 at main.m:16

查找某个函数

  1. 对于有调试符号的这样使用: image lookup -r -n <FUNC_REGEX>

例子:

(lldb) image lookup -r -n ptrace
libsystem_kernel.dylib:
        Address: libsystem_kernel.dylib[0x0000000197693244] (libsystem_kernel.dylib.__TEXT.__text + 108616)
        Summary: libsystem_kernel.dylib`__ptrace        Address: libsystem_kernel.dylib[0x0000000197693244] (libsystem_kernel.dylib.__TEXT.__text + 108616)
        Summary: libsystem_kernel.dylib`__ptrace
[PiaoYunDbg]$
  1. 对于无调试符号的这样使用: (lldb) image lookup -r -s <FUNC_REGEX>

disassemble(反汇编)

  • disassemble -a 地址

例子:

(lldb) dis -a $pc
debug`main at main.m:14:
   0xa71fc:  push   {r7, lr}
   0xa71fe:  mov    r7, sp
   0xa7200:  sub    sp, #0x24
   0xa7202:  movs   r2, #0x0
   0xa7204:  movt   r2, #0x0
   0xa7208:  str    r2, [sp, #0x20]
   0xa720a:  str    r0, [sp, #0x1c]
   0xa720c:  str    r1, [sp, #0x18]
   0xa720e:  blx    0xa7fe0                   ; symbol stub for: 
   ...
(lldb) disassemble -A thumb // 指定thumb

可选:

thumbv4t
thumbv5
thumbv5e
thumbv6
thumbv6m
thumbv7
thumbv7f
thumbv7s
thumbv7k
thumbv7m
thumbv7em

memory (内存操作)

读内存

  • memory read [起始地址 结束地址]/寄存器 -outfile 输出路径(内存操作)

例子:

(lldb) memory read $pc
0x00035ebe: 0e 98 07 99 09 68 08 9a 90 47 0c 99 03 90 08 46  .....h...G.....F
0x00035ece: 03 99 01 f0 80 e8 02 22 c0 f2 00 02 41 f2 52 10  ......."....A.R.
(lldb) memory read 0x35f1c 0x35f46 -outfile /tmp/test.txt  // 将内存区域保存到文件

默认情况下,memory read 只能读取 1024字节数据

例如下面的代码就会报错:

(lldb) memory read 0x1000 0x3000 -outfile /tmp/test.txt
error: Normally, 'memory read' will not read over 1024 bytes of data.

解决方法:加-force参数

(lldb) memory read 0x1000 0x3000 -outfile /tmp/test.txt -force
或者:
(lldb) memory read 0x1000 -outfile /tmp/test.txt -count 0x2000 -force
(lldb) memory read $x0(寄存器) -outfile /tmp/test.txt -count 0x2000 -force

二进制输出到文件

  • memory read xxxx --binary

例子:

(lldb) memory read 0x1000 0x3000 -outfile /tmp/test.bin --binary -force

写内存

  • memory write 寄存器/地址 数据

例子:

(lldb) memory write $rip 0xc3
(lldb) memory write $rip+1 0x90

std::string读取

  • 读取方式:从内存+8的地方开始 64bit自行变通

例子:

(lldb) x $r0+8      
0x02db9248: 20 82 e3 14 71 00 00 00 61 00 00 00 c0 82 d3 14   .惝q...a...喇赢
0x02db9258: 71 00 00 00 2b 00 00 00 20 f9 e6 14 61 00 00 00  q...+... .a...
(lldb) x/s 0x14e38220
0x14e38220: "hello!piaoyun"

register(寄存器操作)

读寄存器

  • register read/格式

例子:

(lldb) register read/x
//返回如下:
General Purpose Registers:
        r0 = 0x1599e028
        r1 = 0x38131621  libobjc.A.dylib`objc_msgSend + 1
        r2 = 0x000a85cc  "class"
        r3 = 0x000a85d4  (void *)0x000a8618: AppDelegate
        r4 = 0x00000000
        r5 = 0x000a71fd  debug`main + 1 at main.m:14
        r6 = 0x00000000
        r7 = 0x27d63c80
        r8 = 0x27d63c98
        r9 = 0x00000002
       r10 = 0x00000000
       r11 = 0x00000000
       r12 = 0x3a3ff1c8  (void *)0x3875cc19: _Unwind_SjLj_Unregister + 1
        sp = 0x27d63c5c
        lr = 0x38136eaf  libobjc.A.dylib`objc_autoreleasePoolPush + 311
        pc = 0x000a7236  debug`main + 58 at main.m:16
      cpsr = 0x20000030

写寄存器

  • register write 寄存器名称 数值

例子:改写r9寄存器例子:

(lldb) register write r9 2

display(查看表达式的值)

使用display命令设置一个表达式后,它将在每次单步后,紧接着输出被设置的表达式的的值

  • display 表达式
  • undisplay 序号

例子:

(lldb) display $R0
(lldb) undisplay 1

watchpoint(内存断点)

  • 内存断点 watchpoint set expression 地址
  • watchpoint set variable 变量名称 // 源码调试用到,略过

设置内存断点

例子:

(lldb) watchpoint set expression 0x1457fa70
// 命中后得到结果:
Watchpoint 3 hit:
old value: 3
new value: 4

内存访问断点

  • watchpoint set expression -w read -- 内存地址

例子:

(lldb) watchpoint set expression -w read -- 0x16b9dd91

内存写入断点

  • watchpoint set expression -w write -- 内存地址

例子:

(lldb) watchpoint set expression -w read -- 0x16b9dd91
例子:

条件断点

  • watchpoint modify -c 表达式

例子:

(lldb) watchpoint modify -c '*(int *)0x1457fa70 == 20'
命中后得到结果:
Watchpoint 3 hit:
old value: 15
new value: 20

找按钮事件

  • po [按钮名称/内存地址 allTargets]

例子:

(lldb) po [[self btnTest] allTargets]
{(
    <ViewController: 0x166af1f0>
)}

(lldb) po [[self btnTest] actionsForTarget:(id)0x166af1f0 forControlEvent:0]
<__NSArrayM 0x165b8950>(
testAction:
)

在机器上实战

(lldb) po [[[UIApplication sharedApplication] keyWindow] recursiveDescription]
<UIWindow:
 0x15e771c0; frame = (0 0; 320 568); gestureRecognizers = <NSArray: 
0x15e96210>; layer = <UIWindowLayer: 0x15e988e0>>
   | <UIView: 0x15eb4180; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x15eb4300>>
  
 |    | <UIButton: 0x15eb32d0; frame = (277 285; 46 30); opaque = NO;
 autoresize = RM+BM; layer = <CALayer: 0x15eb2e30>>
   |    
|    | <UIButtonLabel: 0x15db5220; frame = (0 6; 46 18); text = 
'Button'; opaque = NO; userInteractionEnabled = NO; layer = 
<_UILabelLayer: 0x15db5410>>
   |    | <_UILayoutGuide: 0x15eb4360; frame = (0 0; 0 20); hidden = YES; layer = <CALayer: 0x15eb4540>>
   |    | <_UILayoutGuide: 0x15eb4af0; frame = (0 568; 0 0); hidden = YES; layer = <CALayer: 0x15eb4b70>>

(lldb) po [(UIButton *)0x15eb32d0 allTargets]
{(
    <ViewController: 0x15e93250>
)}

(lldb) po [(UIButton *)0x15eb32d0 allTargets]
{(
    <ViewController: 0x15e93250>
)}

(lldb) po [(UIButton *)0x15eb32d0 actionsForTarget:(id)0x15e93250 forControlEvent:0]
<__NSArrayM 0x15dbfc50>(
testAction:
)
// 调用--
(lldb) po [0x15e93250 testAction:nil]
0x00210c18

对按钮属性操作

(lldb) po [[[UIApplication sharedApplication] keyWindow] recursiveDescription]
<UIWindow: 0x15e771c0; frame = (0 0; 320 568); gestureRecognizers = <NSArray: 0x15e96210>; layer = <UIWindowLayer: 0x15e988e0>>
   | <UIView: 0x15eb4180; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x15eb4300>>
   |    | <UIButton: 0x15eb32d0; frame = (277 285; 46 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x15eb2e30>>
   |    |    | <UIButtonLabel: 0x15db5220; frame = (0 6; 46 18); text = 'Button'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x15db5410>>
   |    | <_UILayoutGuide: 0x15eb4360; frame = (0 0; 0 20); hidden = YES; layer = <CALayer: 0x15eb4540>>
   |    | <_UILayoutGuide: 0x15eb4af0; frame = (0 568; 0 0); hidden = YES; layer = <CALayer: 0x15eb4b70>>

(lldb) expression UIButton *$btn = (UIButton *)0x15eb32d0
(lldb) expression [$btn setHidden:YES]

你可能感兴趣的文章

评论区

已有2位网友发表了看法:

1L Eric_kaylum  2020-01-24 01:18:01 回复
请问如何在lldb下调试时输入值?
例如我们在代码中使用scanf()函数循环读取外部的值,在调试时怎么输入呢?具体命令是什么的
2L 访客  2021-07-12 16:50:40 回复
大佬你好,我正在看《iOS应用逆向与安全之道》,想请教下,在LLDB中我下断所有的fopen函数,但是这时候我怎么监控$x0寄存器的值等于我想要的时候再让程序断下,就类似OD中条件断点,在LLDB中需要如何设置

发表评论

必填

选填

选填

必填

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。