1.单步调试
单步调试通常分为两大类,一类为源码级别(source level),一类为指令级别(instrution level)。一行源代码一般需要多行汇编才可以实现,所以当我们越狱开发调试汇编指令单步调试需要用到(instrution level)指令级别。而每一大类又分为step-in和step-over,step-in会进入函数调用,而step-over会跳过函数调用。
1.1源码级别(source level)
1.1.1 step-in
(lldb) thread step-in
(lldb) step
(lldb) s
以上三条命令是等同的。假如我们在ViewController的第20行下了断点,每执行一次上述命令断点会跳转到下一行源代码位置,如果下一行是一个函数调用,会进入函数调用内部。比如当执行到23行时,输入s命令,会进入testMethod函数内部,跳转到39行。
1.1.2 step-over
(lldb) thread step-over
(lldb) next
(lldb) n
以上三条命令是等同的。假如我们在ViewController的第20行下了断点,每执行一次上述命令断点会跳转到下一行源代码位置,如果下一行是一个函数调用,则不会进入函数调用内部。比如当执行到23行时,输入n命令,却不会跳转到39行,而是45行(之所以会跳转到45行,因为在45行设置了断点)。
- s和n都是跳转到断点的下一行源代码位置,区别为,如果下一行源代码有函数调用,s会进入函数内部,n则会跳过函数执行。如果没有函数调用则两组命令没有任何区别。
1.2指令级别(instrution level)
理解了s和n命令的区别。si和ni同理。假如汇编指令有一个bl跳转指令,si会单步进入bl指令的子函数内部,而ni就不会。
1.2.1 step-in
(lldb) thread step-inst
(lldb) si
1.1.2 step-over
(lldb) thread step-inst-over
(lldb) ni
1.3 step-out
(lldb) thread step-out
(lldb) finish
- setp out 从一个函数跳出。
- 如果没有执行s或者si,却执行了finish,其实会跳转到汇编指令bl的下一条位置(step out默认是从一个函数跳出,对系统函数调用一定是通过bl执行了函数调用,下一个位置必定为bl的下一个位置)
- 要从嵌套的step out中退出,执行c命令即可跳转到下一个断点。
对于单步调试总结下:对于逆向开发si和ni命令会使用较多,正向App开发,s和n命令较多
1.4 s(si)、n(ni)和xcode调试工具对应关系
1.3.1xcode第二个图标
- 点击第二个图标,同continue命令,即c是等同的,跳转到下一个断点。
1.3.2xcode第三个图标
- 点击第三个图标,同thread step-over(next、n)。
- 按住Control键,同时点击第三个图标,同thread step-inst-over(ni)命令。
- 同时按住Control键+Shift键,并同时点击第三个图标,同thread step-over(next、n)。
1.3.3xcode第四个图标
- 点击第四个图标,同thread step-in(step、s)。
- 按住Control键,同时点击第四个图标,同thread step-inst(si)命令。我们可以通过xcode调试时,按住Control键,会发现需要点击多次断点才会移动到下一行(原因是一行源代码一般需要多行汇编才可以实现)
- 同时按住Control键+Shift键,并同时点击第四个图标,同同thread step-in(step、s)。
1.3.4xcode第五个图标
- 点击第五个图标,同finish(即f命令,thread step-out)
2.断点命令
2.1设置断点
- 通过函数名字设置断点
(lldb) breakpoint set --name "-[NSString stringWithFormat:]"
(lldb) br s -n "-[NSString stringWithFormat:]"
(lldb) b -[NSString stringWithFormat:]
- 通过地址设置断点
(lldb) breakpoint set --address 0x00000001c44441d0
(lldb) br s -a 0x00000001c44441d0
2.2列举所有断点
(lldb) breakpoint list
(lldb) br l
2.3删除断点
(lldb) breakpoint delete 1
(lldb) br del 1
3. expression命令
3.1打印变量
- 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
3.2申明变量
(lldb) e NSString* abc = @"abc"
(lldb) po abc
abc
3.3修改变量
在如下函数设置断点
如图所示,ii初始值为0,但是可以通过expression命令修改其初始化值为 10000
lldb) p ii
(int) $0 = 0
(lldb) e ii = 10000
(int) $1 = 10000
(lldb) p ii
(int) $2 = 10000
4.调试信息
- 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'
thread #10: tid = 0xfb0ae2, 0x0000000186206c1c libsystem_pthread.dylib`start_wqthread
- 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
- register read --all(简写 re r -a) 返回当前线程所有寄存器的值(对64位架构,包含x0-x31、s0-s31 d0-d31 v0-v31 w0-w31)
- thread backtrace(简写 bt)当前线程堆栈回溯信息
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 9.1
* 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
frame #1: 0x0000000104cc6bbc TestPAD`-[ViewController viewDidLoad](self=0x0000000149d0aaa0, _cmd="viewDidLoad") at ViewController.m:24
frame #2: 0x000000018fa14efc UIKit`-[UIViewController loadViewIfRequired] + 1040
frame #3: 0x000000018fabc5ec UIKit`-[UINavigationController _updateScrollViewFromViewController:toViewController:] + 76
frame #4: 0x000000018fabba8c UIKit`-[UINavigationController _startTransition:fromViewController:toViewController:] + 196
frame #5: 0x000000018fabb490 UIKit`-[UINavigationController _startDeferredTransitionIfNeeded:] + 1168
frame #6: 0x000000018fabaf0c UIKit`-[UINavigationController __viewWillLayoutSubviews] + 164
frame #7: 0x000000018fabae0c UIKit`-[UILayoutContainerView layoutSubviews] + 188
frame #8: 0x000000018fa122f8 UIKit`-[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1276
frame #9: 0x000000018a5cbec8 QuartzCore`-[CALayer layoutSublayers] + 184
frame #10: 0x000000018a5cffa8 QuartzCore`CA::Layer::layout_if_needed(CA::Transaction*) + 332
frame #11: 0x000000018a53ea98 QuartzCore`CA::Context::commit_transaction(CA::Transaction*) + 336
frame #12: 0x000000018a564eb4 QuartzCore`CA::Transaction::commit() + 540
frame #13: 0x000000018fc90174 UIKit`__34-[UIApplication _firstCommitBlock]_block_invoke_2 + 140
frame #14: 0x00000001865860fc CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 20
frame #15: 0x00000001865859cc CoreFoundation`__CFRunLoopDoBlocks + 288
frame #16: 0x00000001865836dc CoreFoundation`__CFRunLoopRun + 1068
frame #17: 0x00000001864a3fb8 CoreFoundation`CFRunLoopRunSpecific + 436
frame #18: 0x000000018833bf84 GraphicsServices`GSEventRunModal + 100
frame #19: 0x000000018fa782e8 UIKit`UIApplicationMain + 208
frame #20: 0x0000000104cd5d14 TestPAD`main(argc=1, argv=0x000000016b15b9c8) at main.m:14
frame #21: 0x0000000185fc656c libdyld.dylib`start + 4
- thread backtrace all(简写bt all)所有线程堆栈回溯信息
- 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) 读取目标内存指令,比如x/10xg sp 。10后面的x代表用16进制来显示结果,g代表giant word(8字节)大小。所以x/10xgsp就是用16进制显示栈区10个64位元素内容。常见的大小格式为"b-byte"(1字节) "h-half word"(2字节) "w-word"(4字节) "g-giant word"(8字节)
- disassemble --frame(简写di -f)将当前frame的当前函数转为汇编代码
- disassemble --name "函数名字"(简写di -n )将当前frame的指定的函数转为汇编代码
(lldb) di -n "-[ViewController testParam:b:c:d:]"
TestPAD`-[ViewController testParam:b:c:d:]:
0x104cc6cf4 <+0>: sub sp, sp, #0x40 ; =0x40
0x104cc6cf8 <+4>: stp x29, x30, [sp, #0x30]
0x104cc6cfc <+8>: add x29, sp, #0x30 ; =0x30
0x104cc6d00 <+12>: stur x0, [x29, #-0x8]
0x104cc6d04 <+16>: stur x1, [x29, #-0x10]
0x104cc6d08 <+20>: stur w2, [x29, #-0x14]
0x104cc6d0c <+24>: str w3, [sp, #0x18]
0x104cc6d10 <+28>: str w4, [sp, #0x14]
0x104cc6d14 <+32>: str w5, [sp, #0x10]
0x104cc6d18 <+36>: str wzr, [sp, #0xc]
-> 0x104cc6d1c <+40>: ldur w2, [x29, #-0x14]
0x104cc6d20 <+44>: ldr w3, [sp, #0xc]
0x104cc6d24 <+48>: add w2, w3, w2
0x104cc6d28 <+52>: str w2, [sp, #0xc]
0x104cc6d2c <+56>: ldr w2, [sp, #0x18]
0x104cc6d30 <+60>: ldr w3, [sp, #0xc]
0x104cc6d34 <+64>: add w2, w3, w2
0x104cc6d38 <+68>: str w2, [sp, #0xc]
0x104cc6d3c <+72>: ldr w2, [sp, #0x14]
0x104cc6d40 <+76>: ldr w3, [sp, #0xc]
0x104cc6d44 <+80>: add w2, w3, w2
0x104cc6d48 <+84>: str w2, [sp, #0xc]
0x104cc6d4c <+88>: ldr w2, [sp, #0x10]
0x104cc6d50 <+92>: ldr w3, [sp, #0xc]
0x104cc6d54 <+96>: add w2, w3, w2
0x104cc6d58 <+100>: str w2, [sp, #0xc]
0x104cc6d5c <+104>: ldr w2, [sp, #0xc]
0x104cc6d60 <+108>: mov x0, x2
0x104cc6d64 <+112>: mov x1, sp
0x104cc6d68 <+116>: str x0, [x1]
0x104cc6d6c <+120>: adrp x0, 249
0x104cc6d70 <+124>: add x0, x0, #0xc68 ; =0xc68
0x104cc6d74 <+128>: bl 0x104d9a9b4 ; symbol stub for: NSLog
0x104cc6d78 <+132>: ldp x29, x30, [sp, #0x30]
0x104cc6d7c <+136>: add sp, sp, #0x40 ; =0x40
0x104cc6d80 <+140>: ret
4.Match-O可执行文件及Shared库查询命令
- image list(简写im li) 列举所有可执行文件和系统库信息
(lldb) image list -o -f "TestPAD"
[ 0] 0x0000000004ca4000 /Users/shiguiling063/Library/Developer/Xcode/DerivedData/TestPAD-bpwpvzedhypusjgfujyksktehppj/Build/Products/Debug-iphoneos/TestPAD.app/TestPAD
- image lookup -r -n <FUNC_REGEX> 从debug符号标中正则匹配函数
(lldb) image lookup -r -n "testParam"
或者
(lldb) im loo -r -n "testParam"
2 matches found in /Users/shiguiling063/Library/Developer/Xcode/DerivedData/TestPAD-bpwpvzedhypusjgfujyksktehppj/Build/Products/Debug-iphoneos/TestPAD.app/TestPAD:
Address: TestPAD[0x0000000100022cf4] (TestPAD.__TEXT.__text + 118184)
Summary: TestPAD`-[ViewController testParam:b:c:d:] at ViewController.m:29 Address: TestPAD[0x0000000100022cf4] (TestPAD.__TEXT.__text + 118184)
Summary: TestPAD`-[ViewController testParam:b:c:d:] at ViewController.m:29
- image lookup --type "定义名称" 查找定义
(lldb) image lookup --type ViewController
或者
(lldb) im loo -t ViewController
Best match found in /Users/shiguiling063/Library/Developer/Xcode/DerivedData/TestPAD-bpwpvzedhypusjgfujyksktehppj/Build/Products/Debug-iphoneos/TestPAD.app/TestPAD:
id = {0xb00000042}, name = "ViewController", byte-size = 16, decl = ViewController.h:11, compiler_type = "@interface ViewController : UITableViewController{
NSArray * _dataArr;
}
@property ( getter = dataArr,setter = setDataArr:,readwrite,copy,nonatomic ) NSArray * dataArr;
@end"
- image dump symtab -m "模块名" Dump出给定模块的所有符号
比如需要Dump出测试工程TestPAD所有的符号(如果不指定模块名,会Dump出所有符号,包扩系统库的,会比较耗时)
(lldb) image dump symtab -m TestPAD
Symtab, file = /Users/shiguiling063/Library/Developer/Xcode/DerivedData/TestPAD-bpwpvzedhypusjgfujyksktehppj/Build/Products/Debug-iphoneos/TestPAD.app/TestPAD, num_symbols = 4121:
Debug symbol
|Synthetic symbol
||Externally Visible
|||
Index UserID DSX Type File Address/Value Load Address Size Flags Name
------- ------ --- --------------- ------------------ ------------------ ------------------ ---------- ----------------------------------
[ 0] 0 D SourceFile 0x0000000000000000 Sibling -> [ 19] 0x00640000 /Users/shiguiling063/Downloads/越狱开发相关/TestPADSource/TestPAD/PAD/PADSource/TalkingData/SkyEyeData/Codeless/SEObjectSerializer.m
[ 1] 2 D ObjectFile 0x000000005b55a875 0x0000000000000000 0x00660001 /Users/shiguiling063/Library/Developer/Xcode/DerivedData/TestPAD-bpwpvzedhypusjgfujyksktehppj/Build/Intermediates.noindex/TestPAD.build/Debug-iphoneos/TestPAD.build/Objects-normal/arm64/SEObjectSerializer.o
[ 2] 4 D Code 0x0000000100006028 0x0000000000000140 0x000e0000 -[SEObjectSerializer initWithConfiguration:objectIdentityProvider:]
[ 3] 8 D Code 0x0000000100006168 0x000000000000035c 0x000e0000 -[SEObjectSerializer serializedObjectsWithRootObject:]
[ 4] 12 D Code 0x00000001000064c4 0x0000000000000c00 0x000e0000 -[SEObjectSerializer visitObject:withContext:]
[ 5] 16 D Code 0x00000001000070c4 0x0000000000000120 0x000e0000 -[SEObjectSerializer classHierarchyArrayForObject:]
[ 6] 20 D Code 0x00000001000071e4 0x000000000000027c 0x000e0000 -[SEObjectSerializer allValuesForType:]
[ 7] 24 D Code 0x0000000100007460 0x000000000000053c 0x000e0000 -[SEObjectSerializer parameterVariationsForPropertySelector:]
[ 8] 28 D Code 0x000000010000799c 0x0000000000000968 0x000e0000 -[SEObjectSerializer instanceVariableValueForObject:propertyDescription:]
[ 9] 32 D Code 0x0000000100008304 0x000000000000040c 0x000e0000 -[SEObjectSerializer invocationForObject:withSelectorDescription:]
[ 10] 36 D Code 0x0000000100008710 0x0000000000000578 0x000e0000 -[SEObjectSerializer propertyValue:propertyDescription:context:]
[ 11] 40 D Code 0x0000000100008c88 0x000000000000099c 0x000e0000 -[SEObjectSerializer propertyValueForObject:withPropertyDescription:context:]
[ 12] 44 D Code 0x0000000100009624 0x000000000000009c 0x000e0000 -[SEObjectSerializer isNestedObjectType:]
[ 13] 48 D Code 0x00000001000096c0 0x0000000000000264 0x000e0000 -[SEObjectSerializer classDescriptionForObject:]
[ 14] 52 D Code 0x0000000100009924 0x0000000000000070 0x000e0000 -[SEObjectSerializer .cxx_destruct]
[ 15] 55 D X ObjCIVar 0x00000001001372f0 0x0000000000000004 0x001e0000
5.Script & Chisel
LLDB 有内建的,完整的 Python支持。在LLDB中输入 script,会打开一个 Python REPL。你也可以输入一行 python 语句作为 script 命令的参数,这可以运行 python 语句而不进入REPL
(lldb) script print 'Hello World'
Hello World
Facebook开源的Chisel就是基于此实现
Facebook通过python脚本扩展和丰富了lldb命令,具体以Chisel文档为准
6. watchpoint命令
- Set a watchpoint on a variable when it is written to
(lldb) watchpoint set variable global_var
或者
(lldb) wa s v global_var
- 根据内存地址设置watchpoint
(lldb) watchpoint set expression -- my_ptr
或者
(lldb) wa s e -- my_ptr
- 满足条件触发watchpoint
(lldb) watch set var global
(lldb) watchpoint modify -c '(global==5)'
(lldb) c
- 列举所有watchpoint
(lldb) watchpoint list
或者
(lldb) watch l
- 删除watchpoint
(lldb) watchpoint delete 1
或者
(lldb) watch del 1
7.其他命令补充
- register write 将值写回寄存器
将当前pc寄存器后移8字节
(lldb) register write pc `$pc+8`
- target stop-hook 仅在触发watchpoint和断点的情况下才会触发stop-hook
以下命令添加一个stop-hook命令,触发了watchpoint或者断点后,通过frame variable命令输出当前frame所有变量和临时变量
(lldb) target stop-hook add --one-liner "frame variable"
8.更多
更多LLDB命令请参照LLDB官网公布资料:http://lldb.llvm.org/lldb-gdb.html