美文网首页
常用lldb

常用lldb

作者: blueshadow | 来源:发表于2017-06-15 16:08 被阅读608次

Chapter 1 Gettting Started 概览

常用寄存器(x86_64)
• FirstArgument:RDI
• SecondArgument:RSI
• ThirdArgument:RDX
• FourthArgument:RCX
• FifthArgument:R8
• SixthArgument:R9

  • 返回值RAX

常用寄存器 (arm64)

  • x1

真机

[Tab 1]
$ iproxy 5000 22
[Tab 2]
$ iproxy 1234 1234
[Tab 3]
$ ssh root@localhost -p 5000
root# ps -e | grep Preferences
root# debugserver *:1234 -attach=40776
[Tab 4]
lldb
(lldb) process connect connect://localhost:1234
[Terminal 1]
$ tty
/dev/ttys003

[Terminal 2]
(lldb) file /Applications/Xcode.app/Contents/MacOS/Xcode
(lldb) process launch -e /dev/ttys003
(Ctrl + C)
(lldb) breakpoint set -n "-[NSView hitTest:]"
Breakpoint 1: where = AppKit`-[NSView hitTest:], address =
0x000000010338277b
(lldb) continue
Process 9532 resuming

(make sure the breakpoint is triggered)
Process 9532 resuming
Process 9532 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x00007fff817becfb AppKit`-[NSView hitTest:]
AppKit`-[NSView hitTest:]:
->  0x7fff817becfb <+0>: pushq  %rbp
    0x7fff817becfc <+1>: movq   %rsp, %rbp
    0x7fff817becff <+4>: pushq  %r15
    0x7fff817bed01 <+6>: pushq  %r14

# po is usually more useful as it gives the NSObject’s description or debugDescription, if available.
(lldb) po $rdi
<_NSThemeCloseWidget: 0x11ca41f80>

# 给断点添加条件
(lldb) breakpoint modify 1 -c "(BOOL)[$rdi isKindOfClass:[NSTextView class]]"
(触发断点后)
(lldb) po [$rdi superclass]

# 用lldb查看私有API r-使用正则表达式;n-通过名字寻找函数或符号
(lldb) image lookup -rn 'DVTSourceTextView\ '

# 通过block注入来swizzle
(lldb) po @import Foundation
(lldb) po
Enter expressions, then terminate with an empty line to evaluate:
@import Cocoa;
id $class = [NSObject class];
SEL $sel = @selector(init);
void *$method = (void *)class_getInstanceMethod($class, $sel);
IMP $oldImp = (IMP)method_getImplementation($method);
id (^$block)(id) = ^id(id object) {
  if ((BOOL)[object isKindOfClass:[NSView class]]) {
    fprintf(stderr, "%s\n", (char *)[[[object class] description]
UTF8String]);
  }
  return object;
};
IMP $newImp = (IMP)imp_implementationWithBlock($block);
method_setImplementation($method, $newImp);

# 善于用help和apropos查看文档
(lldb) help breakpoint name
# will do a case-insensitive search for any word or string against the LLDB documentation and return any matching results.
(lldb) apropos swift

Chapter 3: Attaching with LLDB

LLDB attaching实际是个误导人的词。debugserver才是真正负责attaching到进程的。

# attach到已存在进程
$ lldb -n Xcode
$ lldb -p `pgrep -x Xcode`

# attach到未存在进程
$ lldb -n Finder -w  # LLDB会等待直到一个叫Finder的进程下次启动

# attach到自己想要的进程。这种方式有个弊端是进程的stderr output (i.e. NSLog & company)回自动发送到Terminal里面去。
$ lldb -f /System/Library/CoreServices/Finder.app/Contents/MacOS/Finder
(lldb) process launch

# Options while launching
$ lldb -f /bin/ls
(lldb) process launch -w /Applications   # 改变当前工作目录,等于cd再ls
(lldb) process launch -- /Applications  # 给ls传参,等于 ls /Applications
(lldb) process launch -- ~/Desktop  # ls会报错,这个目录不存在
(lldb) process launch -X true -- ~/Desktop  # 用-X true来展开任何shell符号
(lldb) run ~/Desktop  # run 等价于 process launch -X true --
(lldb) process launch -o /tmp/ls_output.txt -- /Applications

(lldb) target delete
(lldb) target create /usr/bin/wc
$ echo "hello world" > /tmp/wc_input.txt
(lldb) process launch -i /tmp/wc_input.txt  # 等价于 wc < /tmp/wc_input.txt

(lldb) run  # 不想要stdin,程序会一直在那等着,输入内容,Ctrl+D结束输入

(lldb) process launch -n  # -n告诉LLDB不创建stdin,于是wc会立即结束突出

Chapter 4: Stopping in Code

# 检查一个函数是否存在
(lldb) image lookup -n "-[UIViewController viewDidLoad]"
1 match found in /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk//System/Library/Frameworks/UIKit.framework/UIKit:
        Address: UIKit[0x00000000001ca654] (UIKit.__TEXT.__text + 1867588)
        Summary: UIKit`-[UIViewController viewDidLoad]

(lldb) image lookup -rn test  # 正则查找在任何包含有test(大小写匹配)的函数或框架

# 然后添加断点
(lldb) b -[UIViewController viewDidLoad]
(lldb) rb '\-\[UIViewController\ '  # rb = breakpoint set -r %1
(lldb) breakpoint delete  # 删除所有断点
(lldb) rb '\-\[UIViewController(\(\w+\))?\ '  # 找出包括category方法的有所UIVC的实例方法
(lldb) rb . -f DetailViewController.swift  # -f域限制,这个为所有的属性getter/setter,block,category,方法或函数添加断点
(lldb) rb .  # 这个是非常疯狂的,为所有一切添加断点
(lldb) rb . -s UIKit  # 限制在某个library,为这个库的所有东西添加断点
(lldb) rb . -s UIKit -o  # 断点只会被触发一次然后被删除

修改或删除断点

(lldb) b main
  Breakpoint 1: 20 locations.
(lldb) breakpoint list
(lldb) breakpoint list 1
(lldb) breakpoint list 1 -b
(lldb) breakpoint list 1 3
(lldb) breakpoint list 1-3  # 以上也适用delete
(lldb) breakpoint delete 1
(lldb) breakpoint delete 1,1

Chapter 5: Expression

关于expression命令的介绍,用它你可以在debugger中执行任意代码

Formatting p & po

'p' = 'expression --'
'po' = 'expression -O --' # -O用来打印object的description

(lldb) p self
(Signals.MasterViewController) $R3 = 0x00007fc08860e960 {
  UIKit.UITableViewController = {
    baseUIViewController@0 = <extracting data from value failed>

    _tableViewStyle = 0
    _keyboardSupport = nil
    _staticDataSource = nil
    _filteredDataSource = 0x0000608000245e20
    _filteredDataType = 0
  }
  detailViewController = nil
}
(lldb) type summary add Signals.MasterViewController --summary-string "Wahoo!"
(lldb) p self
(Signals.MasterViewController) $R5 = 0x00007fc08860e960 Wahoo!
(lldb) type summary clear

Swift vs Objective-C debugging contexts

调试oc代码时,lldb便是oc语法;调试swift代码时,lldb便是swift语法。
有个例外时非断点停止而是主动点击暂停按钮(pause the application out of the blue),进入的context一定是OC,不管app是不是swift

# 在swift环境强制使用oc语法
(lldb) expression -l objc -O -- [UIApplication sharedApplication]

User defined variables

一定要带上美元符号
oc环境创建的变量,最好还是不要用于swift环境,行为是未定义的,Apple的开发者正在开发这个语境

(lldb) po id $test = [NSObject new]
(lldb) po $test
<NSObject: 0x60000001d190>
(lldb) expression -l swift -O -- $test
<NSObject: 0x60000001d190>
(lldb) exppression -l swift -O -- $test.description
error: <EXPR>:3:1: error: use of unresolved identifier '$test'
$test.description
^~~~~

# 随便在一个类的实例方法里面设置一个断点(最好是ViewController的,这样易于观察实验结果),run并命中断点
(lldb) p self  # lldb会赋予一个变量 $R0 引用它,请恢复执行,并手动停止
(lldb) po $R0.title  # 记住上面的内容,主动停止会进入oc环境
error: use of undeclared identifier '$R0'
(lldb) expression -l swift -- $R0.title
(String?) $R1 = "Quarterback"  # 会打印出ViewController的title!
(lldb) expression -l swift -- $R0.title = "💩💩💩💩💩 "  # ⌘ + ⌃ + space然后输入poop可以找到这个emoji
# 恢复执行你就可以看到ViewController的title变成五坨翔啦。这个东西特别好用,当你需要观察给一个方法传一些特殊的值观察对应行为的时候

# 假设上面你下的断点在viewDidAppear函数里,暂停执行
(lldb) expression -l swift -O -- $R0.viewDidLoad()  
# 你会发现断点并没有命中!这是因为lldb默认忽视任何断点。你可以通过-i来打开
(lldb) expression -l swift -O -i 0 -- $R0.viewDidLoad()
# 现在断点可以正常的被触发了。这个技巧是一个很好的用来测试函数的方法。比如,你可以实现test-driven debugging,通过给一个函数传递不同的参数来观察它是如何处理不同的输入

Type formatting类型格式化

LLDB有一个很不错的功能是格式化基本类型数据,这使得LLDB是一个很好的

# 主动停止app
(lldb) expression -G x -- 10  # G代表GDB
(int) $0 = 0x0000000a
(lldb) p/x 10  # /x 指定用十六进制格式
(int) $1 = 0x0000000a
(lldb) p/t 10  # /t 指定用二进制格式
(int) $2 = 0b00000000000000000000000000001010
(lldb) p/t -10
(int) $3 = 0b11111111111111111111111111110110
(lldb) p/t 10.0
(double) $4 = 0b0100000000100100000000000000000000000000000000000000000000000000
(lldb) p/d 'D'  # /d 指定用十进制格式
(char) $5 = 68
(lldb) p/c 1430672467  # /c 指定用char格式
(int) $6 = STFU  # 将这个整型转换成二进制,并分成4个byte,把每个byte转换成ASCII码

完整的输出格式化列表如下:
• x: hexadecimal
• d: decimal
• u: unsigned decimal
• o: octal
• t: binary
• a: address
• c: character constant
• f: float
• s: string
如果这些格式化还不能够满足你,你可以使用lldb额外的formatters,但此时你不可以使用GDB的格式化语法了:

(lldb) expression -f Y -- 1430672467
(int) $0 = 53 54 46 55             STFU

Chapter 6: Thread, Frame & Stepping Around

一个神奇的方法:Control+Shift+StepOver/StepIn
可以控制其他线程保持停止状态,这个在排查棘手的同步问题时特别有用

(lldb) thread backtrace
(lldb) bt  # 这两个其实是不一样的,用help验证
(lldb) frame info  # 打印出当前frame
frame #0: 0x000000010f5ce87a Signals`MasterViewController.viewWillAppear(animated=false, self=0x00007fcb3651be10) -> () at MasterViewController.swift:55

(lldb) frame select 1
frame #1: 0x000000010f5cee51 Signals`@objc MasterViewController.viewWillAppear(Bool) -> () at MasterViewController.swift:0
   1    /**
   2     * MasterViewController.swift
   3     *
   4     * Copyright (c) 2017 Razeware LLC
   5     *
   6     * Permission is hereby granted, free of charge, to any person obtaining a copy
   7     * of this software and associated documentation files (the "Software"), to deal
(lldb) run  # 重新启动app,Xcode不需要重编译
(lldb) next  # step over
(lldb) step  # step into Xcode默认next如果没有debug symbols
(lldb) settings show target.process.thread.step-in-avoid-nodebug
target.process.thread.step-in-avoid-nodebug (boolean) = true
(lldb) step -a0  # 忽视设置,不管有无debug symbols都进入
(lldb) finish  # step out 回车lldb会执行上一条指令
(lldb) next/step --run-mode/-m  # 控制其余线程是否停止

查看栈上数据

一个非常有用的frame子命令是frame variable。这个命令会用在可执行文件头部中(或dSYM如果你的app被stripped了)的debug symbol信息来dump出关于那个栈帧的信息。有了这个debug信息,这个命令只要带上恰当的参数,你很容易就知道函数中所有变量(包括程序中的全局变量)的作用域

(lldb) frame variable  # 查看当前栈帧的变量
(Bool) animated = false
(Signals.MasterViewController) self = 0x00007f94de713760 {
  UIKit.UITableViewController = {
    baseUIViewController@0 = <extracting data from value failed>

    _tableViewStyle = 0
    _keyboardSupport = 0x000060800002aa00
    _staticDataSource = nil
    _filteredDataSource = 0x000060800005bcc0
    _filteredDataType = 0
  }
  detailViewController = nil
}
(CGFloat) bottomInset = 4.9406564584124654E-324
(lldb) frame info


  (lldb) frame variable -F self  # 查看所有私有变量,F是flat的意思

Chapter 7: Image

block所在函数的参数是如何传给block的呢?
编译器能够分析出哪些参数是block需要的,然后创建一个以这些参数为参数的函数。但block被调用时,实际上是这个函数被调用了。

(lldb) image list
(lldb) image list Foundation
(lldb) image dump symtab UIKit -s address  # dump all the symbol table informaton available for UIKit
(lldb) image lookup -n "-[UIViewController viewDidLoad]"
1 match found in /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk//System/Library/Frameworks/UIKit.framework/UIKit:
        Address: UIKit[0x00000000001ca9a4] (UIKit.__TEXT.__text + 1868340)
        Summary: UIKit`-[UIViewController viewDidLoad]
(lldb) image lookup -rn UIViewController
(lldb) image lookup -rn '\[UIViewController\ '
(lldb) image lookup -rn '\[UIViewController\(\w+\)\ '  # 包括category的方法
(lldb) image lookup -rn _block_invoke
(lldb) image lookup -rn _block_invoke ImageName

# 给一个block下断点,观察block的frame variable
(lldb) frame variable
(__block_literal_5 *)  = 0x0000600000269c00
(int) sig = 23
(siginfo_t *) siginfo = 0x00007fff55a67588
(UnixSignalHandler *) self = 0x0000608000262c80
(UnixSignal *) unixSignal = 0x000000010eb3cd08
(__NSCFConstantString *) kSignalHandlerCountUpdatedNotification = 0x000000010a25e488 @"com.razeware.breakpoints.contentupdated"
(lldb) image dump symfile ImageName
# 很多内容出来,文本查找__block_literal_5
0x7fb975802760:   Type{0x100000ce6} , name = "__block_literal_5", size = 52, decl = UnixSignalHandler.m:123, compiler_type = 0x00007fb979082860 struct __block_literal_5 {
        void *__isa;
        int __flags;
        int __reserved;
        void (*__FuncPtr)();
        __block_descriptor_withcopydispose *__descriptor;
        UnixSignalHandler *const self;
        siginfo_t *siginfo;
        int sig;
    }
这个就是定义了block的东东啦
(lldb) po (__block_literal_5 *)0x0000600000269c00
<__NSMallocBlock__: 0x600000269c00>
(lldb) p/x ((__block_literal_5 *)0x0000600000269c00)->__FuncPtr
(void (*)()) $2 = 0x000000010a259810 (Commons`__38-[UnixSignalHandler appendSignal:sig:]_block_invoke_2 at UnixSignalHandler.m:123)
(lldb) image lookup -a 0x000000010a259810
      Address: Commons[0x0000000000002810] (Commons.__TEXT.__text + 2352)
      Summary: Commons`__38-[UnixSignalHandler appendSignal:sig:]_block_invoke_2 at UnixSignalHandler.m:123
(lldb) po ((__block_literal_5 *)0x0000600000269c00)->sig
23

使用image dump symfile ImageName命令来探究一个未知的数据类型是如何工作的,这是一个很好的手段。这个命令也可以让我们了解编译器如何生成目标代码。除此之外,你也可以通过它来检查block是怎么引用block外部的指针来调试内存循环引用问题。

(lldb) image lookup -rn __NSMallocBlock__  # 没有任何输出,说明它没有重写父类的任何方法
(lldb) po [__NSMallocBlock__ superclass]
__NSMallocBlock
(lldb) image lookup -rn __NSMallocBlock  # 输出的方法看起来都和内存管理有关
(lldb) po [__NSMallocBlock superclass]
NSBlock
(lldb) image lookup -rn 'NSBlock\ '
6 matches found in /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk//System/Library/Frameworks/CoreFoundation.framework/CoreFoundation:
        Address: CoreFoundation[0x00000000000400c0] (CoreFoundation.__TEXT.__text + 257696)
        Summary: CoreFoundation`-[NSBlock copy]        Address: CoreFoundation[0x0000000000078330] (CoreFoundation.__TEXT.__text + 487696)
        Summary: CoreFoundation`-[NSBlock copyWithZone:]        Address: CoreFoundation[0x00000000001892b0] (CoreFoundation.__TEXT.__text + 1605776)
        Summary: CoreFoundation`+[NSBlock allocWithZone:]        Address: CoreFoundation[0x00000000001892d0] (CoreFoundation.__TEXT.__text + 1605808)
        Summary: CoreFoundation`+[NSBlock alloc]        Address: CoreFoundation[0x00000000001892f0] (CoreFoundation.__TEXT.__text + 1605840)
        Summary: CoreFoundation`-[NSBlock invoke]        Address: CoreFoundation[0x0000000000189300] (CoreFoundation.__TEXT.__text + 1605856)
        Summary: CoreFoundation`-[NSBlock performAfterDelay:]
# 接下来我们想探究下invoke这个方法,但是我们调用invoke之后不希望这个block被内存管理给释放掉。可以这样处理
(lldb) po id $block = (id)0x0000600000269c00
(lldb) po [$block retain]
<__NSMallocBlock__: 0x600000269c00>
(lldb) po [$block invoke]
2017-07-11 10:38:05.075 Signals[507:5687492] Appending new signal: SIGIO

(lldb) image lookup -rn (?i)\ _\w+description\]  # (?i) means case insensitive
(lldb) image lookup -rn NSObject\(IvarDescription\)
这3个方法非常吸引人:
_ivarDescription
_propertyDescription 
_methodDescription
(lldb) po [[UIApplication sharedApplication] _ivarDescription]  # 可以发现一个叫UIStatusBar的私有类
(lldb) image lookup -rn '\[UIStatusBar\ set'
(lldb) po [[UIApplication sharedApplication] statusBar]
<UIStatusBar: 0x7fcb42809e00; frame = (0 0; 414 20); opaque = NO; autoresize = W+BM; layer = <CALayer: 0x608000032400>>
(lldb) po [0x7fcb42809e00 setBackgroundColor:[UIColor purpleColor]]  # statusBar就会变成紫色的啦

Chapter 8: Persisting & Customizing Commands 持久化和自定义命令

如何持久化

LLDB启动时会在几个目录里面寻找初始化文件,如果找到则会被加载到LLDB中,加载时机是在LLDB启动后attach到process之前。
LLDB将会在以下几个地方寻找初始化文件:
1.~/.lldbinit-[context]。其中context为Xcode或者lldb。即如果只想生效于Xcode中的lldb,用~/.lldbinit-Xcode,如果只想生效与命令行中的lldb,用~/.lldbinit-lldb

  1. 接下来LLDB就会搜索在~/.lldbinit-[context]中找到的内容。这个是最理想的文件,大部分情形我们还是希望两种场景下都使用
  2. LLDB将会搜索被启动时的目录。不幸的是,Xcode启动LLDB在/目录

创建.lldbinit文件

# 注意这里的lldb context应该是Swift,所以才强制为oc语法
(lldb) command alias -- Yay_Autolayout expression -l objc -O -- [[[[[UIApplication sharedApplication] keyWindow] rootViewController] view] recursiveDescription]
(lldb) command alias cpo expression -l objc -O --

Chapter 9: Regex Commands

alias只能方便我们处理静态的命令,如果命令有接受输入的话alias就显得很捉急了。

# 对新命令后面的内容进行正则替换。下面这个命令alias是无法完成的
(lldb) command regex rlook 's/(.+)/image lookup -rn %1/'  # %1指的是被匹配的内容
(lldb) rlook F00  # 等同于 image lookup -rn FOO, 正则表达式是从rlook之后开始匹配
(lldb) rl viewDidLoad  # 等同于 rlook viewDidLoad
(lldb) rl viewDidLoad Signals
# 来点高级点的
(lldb) command regex -- tv 's/(.+)/expression -l objc -O -- @import QuartzCore; [%1 setHidden:!(BOOL)[%1 isHidden]]; (void)[CATransaction flush];/'
(lldb) tv [[[UIApp keyWindow] rootViewController] view]  # 重复这个命令,手机就会黑屏、恢复黑屏一直循环

(lldb) command regex getcls 's/(([0-9]|\$|\@|\[).*)/cpo [%1 class]/'
(lldb) getcls @"hello world"
__NSCFString
(lldb) getcls @[@"hello world"] 
__NSSingleObjectArrayI
(lldb) getcls [UIDevice currentDevice] 
UIDevice
(lldb) cpo [UIDevice currentDevice] 
<UIDevice: 0x60800002b520>
(lldb) getcls 0x60800002b520
UIDevice
(lldb) command regex getcls 's/(([0-9]|\$|\@|\[).*)/cpo [%1 class]/' 's/(.+)/expression -l swift -O -- type(of: %1)/'
(lldb) getcls self
Signals.MasterViewController

相关文章

  • IOS逆向_动态调试:LLDB

    1. LLDB简介; 2. 常用的LLDB语法; 1. LLDB简介: 1.1 概念: LLDB是Xcod...

  • 知识点-LLDB调试命令

    lldb是我们平时在打断点时候,打印面板出现的。 lldb是Xcode自带的调试工具,下面是常用的lldb调试命令...

  • 常用lldb

    Chapter 1 Gettting Started 概览 常用寄存器(x86_64)• FirstArgumen...

  • LLDB 学习

    常用LLDB 指令 指令格式 [ [ ...] ] [- options [option-value]] [...

  • Chisel 和 lldb_commands 常用指令

    Chisel 和 lldb_commands 常用指令是 LLDB 上的指令 。 更详细的说明点击这里。 Chi...

  • Swift 常用快捷键

    iOS开发之Xcode常用调试技巧总结 Xcode 常用快捷键 lldb技巧:

  • iOS之LLDB常用调试命令

    iOS之LLDB常用调试命令熟练使用 LLDB,让你调试事半功倍使用facebook开源的Chisel调试Home...

  • 各种调试技巧

    断点调试并配合LLDB常用调试命令LLDB命令详见:http://www.jianshu.com/p/d14a25...

  • LLDB调试命令初探

    LLDB调试命令初探 初识LLDB 你可能从未使用过LLDB,那让我们先来热热身。 在调试器中最常用到的命令是p(...

  • iOS开发之LLDB常用调试技巧

    原文地址iOS开发之LLDB常用调试技巧用好了LLDB,让调试变得轻松愉快,本文会写出并示例讲解一些常用的指令,以...

网友评论

      本文标题:常用lldb

      本文链接:https://www.haomeiwen.com/subject/tghhqxtx.html