lldb调试和chisel、DerekSelander、cycript的简单应用

0x01 LLDB

Xcode 5发布之后,LLDB调试器已经取代了GDB,成为了Xcode工程中默认的调试器。它与LLVM编译器一起,带给我们更丰富的流程控制和数据检测的调试功能。LLDB为Xcode提供了底层调试环境,其中包括内嵌在Xcode IDE中的位于调试区域的控制面板,在这里我们可以直接调用LLDB命令。

基础语法

<command> [<subcommand> [<subcommand>...]] <action> [-options [option-value]] [argument [argument...]]
  • commandsubcommand:LLDB调试命令的名称。
  • action:执行命令的操作。
  • options:命令的选项。
  • arguement:命令的参数。
  • []:表示命令是可选的。可有可无。

例:

breakpoint set -n test

command: breakpoint 表示断点命令
action: set 表示设置断点
option: -n 表示根据方法name设置断点
arguement: test 表示方法名为test

基础命令

  • 通过help命令查看相关lldb指令描述
(lldb) help
Debugger commands:
  apropos           -- List debugger commands related to a word or subject.
  breakpoint        -- Commands for operating on breakpoints (see 'help b' for
                       shorthand.)
  bugreport         -- Commands for creating domain-specific bug reports.
  command           -- Commands for managing custom LLDB commands.
  disassemble       -- Disassemble specified instructions in the current
                       target.  Defaults to the current function for the
                       current thread and stack frame.
  expression        -- Evaluate an expression on the current thread.  Displays
                       any returned value with LLDB's default formatting.
  frame             -- Commands for selecting and examing the current thread's
                       stack frames.
  • 通过apropos命令获取具体命令的合法参数及含义
(lldb) apropos bt
The following commands may relate to 'bt':
  _regexp-bt -- Show the current thread's call stack.  Any numeric argument
                displays at most that many frames.  The argument 'all' displays
                all threads.
  bt         -- Show the current thread's call stack.  Any numeric argument
                displays at most that many frames.  The argument 'all' displays
                all threads.
  • 一些小指令
    c/continue: 继续执行
    po:expression命令选项:-O 表示调用对象的discraption方法
    up: 往上走。
    n:单步往下走。将子函数当做整体一步执行。
    s: 单步运行,遇到子函数会进去。
    ni: 单步执行会跳转到指令内部,汇编级别。
    finish:返回上层调用栈。
    bt: 查看当前调用的堆栈信息。
    thread return:回滚。会改变代码执行流程。
    frame select 3:查看某个堆栈信息。
    image list:查看某块列表。
    register read:读取寄存器。
    register write:写入寄存器。
    Memory read:读取内存值。
    frame variable:可以打印当前方法的所有参数变量。frame表示帧。
(lldb) frame select 3
frame #3: 0x000000010af838e8 UIKitCore`forwardTouchMethod + 353
UIKitCore`forwardTouchMethod:
    0x10af838e8 <+353>: movq   -0x68(%rbp), %rdi
    0x10af838ec <+357>: movq   0x865bc5(%rip), %rbx      ; (void *)0x00000001076a0010: objc_release
    0x10af838f3 <+364>: callq  *%rbx
    0x10af838f5 <+366>: movq   -0x70(%rbp), %rdi

断点相关

  • 通过函数名设置断点
(lldb) breakpoint set -name action1
Breakpoint 1: no locations (pending).
WARNING:  Unable to resolve breakpoint to any actual locations.
(lldb) br list
Current breakpoints:
1: name = 'action1', locations = 0 (pending)
  • 设置OC方法断点。
breakpoint set -n "[ViewController action1:]"

可同时设置多个。就可以同时启用或者禁用某一组。

(lldb)breakpoint set -n "[ViewController action1:]" -n "[ViewController action2:]" -n "[ViewController action3:]
(lldb) br list
 Current breakpoints:
 1: names = {'[ViewController action1]', '[ViewController action1]', '[ViewController action2]', '[ViewController action2]', '[ViewController action3]', '[ViewController action3]'}, locations = 3, resolved = 3, hit count = 0
 1.1: where = LLDB`-[ViewController action1] + 12 at ViewController.m:42, address = 0x000000010a48f60c, resolved, hit count = 0
 1.2: where = LLDB`-[ViewController action2] + 12 at ViewController.m:45, address = 0x000000010a48f61c, resolved, hit count = 0
 1.3: where = LLDB`-[ViewController action3] + 12 at ViewController.m:48, address = 0x000000010a48f62c, resolved, hit count = 0
  • 禁用或者启用某一组。(也可以禁用1.1)
breakpoint disable 1
breakpoint enable 1
  • 删除某一组。(只能以组为单位的删除)
breakpoint delete 1

下面这样只能达到禁用效果

breakpoint delete 1.1
  • 给项目中所有某个方法设置断点,适合自定义方法。
breakpoint set --selector viewDidLoad
  • 给某个文件或者某个方法设置断点
br set --file ViewController.m --selector action1:
  • 给系统所有带有某个字符串的方法设置断点。
br set -r xxx
  • 删除断点
breakpoint delete
br delete
  • 内存断点。类似于KVO,可以监听某块内存区域值的变化。
//监听p1对象中的name字段的内存区域。
watchpoint set variable p1->_name

也可以直接表达为具体的地址。

p &p1->_name
watchpoint set expression 0x0000600002d21c68

进阶用法

  • command指令。通过指令给断点处添加额外指令的实现。
(lldb) breakpoint command add 1
Enter your debugger command(s).  Type 'DONE' to end.
> frame variable
> DONE

当截住断点时,便会打印相关的指令。


  • hook指令。同上,只是针对每个断点生效。每次截住断点,打印配置好的指令。这里指令一般是通用命令。
target stop-hook add -o "frame variable"

补充:

lldb有一个初始化文件.lldbinit,通过在文件中配置相关指令,使用lldb截住的断点都可以执行相关指令。

操作步骤:

1、vim ~/.lldbinit,进入配置文件。
2、进入编辑模式,将target stop-hook add -o "frame variable"命令输入其中。保存即可生效。
3、在Xcode代码中设置断点,截住时便会打印frame variable的方法参数信息。

0x02 chisel

chiselfacebook利用llbd官方的api,用脚本实现了一些特定的功能。

安装

通过Homebrew进行安装

brew update
brew install chisel

vim ~/.lldbinit修改lldb初始化文件,将chisel的脚本文件的替身地址加入其中

command script import /usr/local/Cellar/chisel/1.8.0/libexec/fblldb.py

重启Xcode,即可使用。

常用命令概览

  • 打印视图层级
(lldb) pviews 0x170391b90
<UIView: 0x170391b90; frame = (0 20; 320 633); autoresize = W; layer = <CALayer: 0x170a23960>>
   | <UIImageView: 0x1701ffd00; frame = (0 -20; 320 568); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x170a235e0>>
  • 打印视图上级
 pviews -u 0x17438fe50
  • 打印整个界面的层级关系
pviews
  • 查看响应者链
presponder 0x150867780
  • 查看按钮绑定的事件
(lldb) pactions 0x150867780
<WCAccountLoginControlLogic: 0x174a915d0>: onFirstViewRegester
  • 查看按钮的继承关系
(lldb) pclass 0x150867780
FixTitleColorButton
   | UIButton
   |    | UIControl
   |    |    | UIView
   |    |    |    | UIResponder
   |    |    |    |    | NSObject
  • 列出某个对象的方法列表
(lldb) pmethods 0x14f787f20
Class Methods:
No methods were found
  • 打印对象的所有属性及属性值。
 pinternals 0x136a11200
  • 根据控制器的名称找到这个名称当前所有的地址。
(lldb) fvc WCAccountMainLoginViewController
0x136a11200 WCAccountMainLoginViewController
//根据地址显示控制器信息。
(lldb) fvc -v 0x136a11200
Found the owning view controller.
<WCAccountMainLoginViewController: 0x136a11200>
  • 快速定位到某个组件。

taplogcontinue,点击页面的某个控件,便会自动打印控件的相关信息。

(lldb) taplog 
Process 2388 resuming
<WCUITextField: 0x136685ee0; baseClass = UITextField; frame = (10 0; 217 44); text = ''; clipsToBounds = YES; opaque = NO; gestureRecognizers = <NSArray: 0x170a5fdd0>; layer = <CALayer: 0x1708320e0>>
  • 闪烁一次控件,方便开发者定位控件。
flicker 0x1378b2880
  • 动态查看控件相关信息.

enter之后,输入相关的指令可查看对应的一些控件信息,并且会给控件更改backgroundColor来标注控件。

(lldb) vs 0x1378b2880 
Use the following and (q) to quit.
(w) move to superview   找到父控件。
(s) move to first subview   找到第一个subview
(a) move to previous sibling  向前移动
(d) move to next sibling  向后移动。
(p) print the hierarchy  打印层级

0x03 DerekSelander

安装

点击DerekSelander下载脚本文件。通过在.lldbinit中完成配置即可使用。

常见命令

  • 查找UIImageView的对象。
search UIImageView   
<_UINavigationBarBackIndicatorView: 0x1703e0d00; frame = (8 11.5; 13 21); alpha = 0; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x170837c00>>
<UIImageView: 0x1703e0900; frame = (0 0; 130 47); clipsToBounds = YES; hidden = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x17023e420>>
<UIImageView: 0x1743ebf00; frame = (0 501.5; 320 2.5); alpha = 0; opaque = NO; autoresize = TM; userInteractionEnabled = NO; layer = <CALayer: 0x17522a3a0>>
<UIImageView: 0x1701fe500; frame = (0 64; 320 0); hidden = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x170635ba0>>
  • 列出某个类所有的方法和属性。(方法后面的地址为imp的地址。)
(lldb) methods 0x174897200
<WCAccountLoginControlLogic: 0x174897200>:
in WCAccountLoginControlLogic:
    Properties:
        @property (readonly) unsigned long hash;
        @property (readonly) Class superclass;
        @property (readonly, copy) NSString* description;
        @property (readonly, copy) NSString* debugDescription;
    Instance Methods:
        - (void) startLogic; (0x102565bdc)
        - (void) stopLogic; (0x102566144)
        - (void) onOneClickLoginSwitchAccount; (0x1025669d4)
        - (void) onOneClickLoginProblem; (0x102566a08)
        - (void) onOneClickLoginGoToSecurityCenter; (0x102566a40)
        - (void) onOneClickLoginGoToHelpCenter; (0x102566a78)
  • 通过内存地址给方法下断点。
(lldb) b -a 0x1025662c0
Breakpoint 1: where = WeChat`___lldb_unnamed_symbol142407$$WeChat, address = 0x00000001025662c0
  • 通过sbt查看函数调用栈、并得到去符号后的信息。
(lldb)sbt
frame #0 : 0x1025662c0 WeChat`-[WCAccountLoginControlLogic onFirstViewLogin] 
frame #1 : 0x187970d34 UIKit`-[UIApplication sendAction:to:from:forEvent:] + 96
frame #2 : 0x1030de4e0 WeChat`___lldb_unnamed_symbol189001$$WeChat ... unresolved womp womp + 460
frame #3 : 0x187959e48 UIKit`-[UIControl _sendActionsForEvents:withEvent:] + 612

0x04 cycript

cycript是一个允许开发者使用OCJS结合语法查看及修改运行时App内存信息的工具,通过它我们可以注入程序、查看程序界面、调用程序函数验证自己的想法等。同lldb不同、当开始使用cycript时它会常驻内存,所以也更方便我们去动态调试。

配置安装

1、在~/.zshrc文件中链接bash_profile.

source .bash_profile

2、在官网中下载SDK文件。在bash_profile中配置dycript

3、安装完成,通过cycript命令即可进入cycript界面了。

界面调试

通过MonkeyDev运行重签应用,通过查看自动生成的动态库文件,我们可得到对应的端口号。

查看手机IP,通过cycript -r 192.168.0.105:6666连接手机。当出现如下界面,便可以自由调试了。在这里你可以编写OCJS语言、达到对应用程序的调试。

下面展示一些简单的调试命令:

  • 打印当前UIApplication的信息。
cy# [UIApplication sharedApplication]
#"<UIApplication: 0x10ba038e0>"

cy# UIApp
#"<UIApplication: 0x10ba038e0>"
  • 定义一个变量keywindow来保存当前的window,接着便可以通过变量keywindow直接打印有关window的属性。
cy# var keyWindow = UIWindow.keyWindow()
#"<iConsoleWindow: 0x10b856e70; baseClass = UIWindow; frame = (0 0; 375 667); gestureRecognizers = <NSArray: 0x282910540>; layer = <UIWindowLayer: 0x282656340>>"

cy# keyWindow.rootViewController
#"<MMUINavigationController: 0x10c165e00>"
  • 通过#,查看内存地址的详细信息。
cy# #0x10c165e00
#"<MMUINavigationController: 0x10c165e00>"
  • 拿到某个对象的成员变量的key和value
cy# *#0x10c165e00

只获取key

[i for(i in *#0x157f64480)]
  • 查看某个类的层级关系;
#0x157f64480.recursiveDescription()

格式化输出一波:

#0x157f64480.recursiveDescription().toString()
  • 查看当前界面所有为某个类的对象
cy# choose(UIButton)
[#"<FixTitleColorButton: 0x157f64480; baseClass = UIButton; frame = (20 18; 130 47); clipsToBounds = YES; opaque = NO; autoresize = RM; layer = <CALayer: 0x170a2f100>>",#"<UIButton: 0x157f6b6d0; frame = (234 20; 86 49); opaque = NO; autoresize = LM; layer = <CALayer: 0x1748284e0>>",#"<FixTitleColorButton: 0x1590af6e0; baseClass = UIButton; frame = (170 18; 130 47); clipsToBounds = YES; opaque = NO; autoresize = LM; layer = <CALayer: 0x170e2ae80>>"]
  • 动态修改某个控件的值
#0x10b1caa00.text="123123"
"123123"

高级用法:

因为我们使用的monkeyDev自动链接了两个cy文件,在文件中实现了对某些方法的自定义函数名以方便我们快捷log。


猴神配置的cy文件的链接:
MS.cy
md.cy
下面简单列举以下文件提到的一些命令:

  • 打印视图层级和控制器层级。
pviews() 
pvcs
  • 打印对象所绑定的方法。
cy# pactions(#0x1053163b0)
"<CustomViewController: 0x1053133f0> showChangeLog:"
  • 打印响应链
cy# rp(#0x1053163b0)
`<UIButton: 0x1053163b0; frame = (127.5 175; 120 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x1c4039aa0>>
<UIView: 0x1053205d0; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x1c403bd80>>
<CustomViewController: 0x1053133f0>
<UIWindow: 0x105313b60; frame = (0 0; 375 667); autoresize = W+H; gestureRecognizers = <NSArray: 0x1c425bae0>; layer = <UIWindowLayer: 0x1c40394e0>>
<UIApplication: 0x105406120>
<AppDelegate: 0x1c002b1a0>`
自定义cy文件,实现自定义命令

通过借鉴猴神的自定义命令的实现,我们可以完成一些常规命令的自定义,方便使用。

1、新建名为dyTest.cy文件。编写相关命令。

更改文件的类型

2、定义指令名和方法实现。

//匿名函数自执行表达式
(function(exports){


//这种直接赋值的变量定义适用于不会发生值改变的情况。因为这样在程序加载的时候变量的值就固定了。
APPID = [NSBundle mainBundle].bundleIdentifier,
APPPATH = [NSBundle mainBundle].bundlePath,

//如果值有变化,可借鉴于函数实现的方式。
DyRootvc = function(){
return UIApp.keyWindow.rootViewController;
};


DyKeyWindow = function(){
return UIApp.keyWindow;
};

DyGetCurrentVCFromRootVc = function(rootVC){
var currentVC;
if([rootVC presentedViewController]){
rootVC = [rootVC presentedViewController];
}

if([rootVC isKindOfClass:[UITabBarController class]]){
currentVC = DyGetCurrentVCFromRootVc(rootVC.selectedViewController);
}else if([rootVC isKindOfClass:[UINavigationController class]]){
currentVC = DyGetCurrentVCFromRootVc(rootVC.visibleViewController);
}else{
currentVC = rootVC;
}

return currentVC;
};


DyCurrentVC = function(){
return DyGetCurrentVCFromRootVc(DyRootvc());
};

})(exports);
//匿名函数自执行表达式

3、copy file 文件,保证其运行时能在Frameworks文件夹中。


4、实践自定义命令。

cy# @import dyTest
{}
cy# APPID
@"com.WeChat.signXcode.dyTest"
cy# APPPATH
@"/private/var/mobile/Containers/Bundle/Application/D4AA77EB-FC58-4189-84CF-114A9EA2BB1E/dyTest.app"
cy# DyRootvc()
#"<MMUINavigationController: 0x14dfd9730>"
cy# DyKeyWindow()
#"<iConsoleWindow: 0x14f005a80; baseClass = UIWindow; frame = (0 0; 320 568); gestureRecognizers = <NSArray: 0x170849f30>; layer = <UIWindowLayer: 0x174420aa0>>"
cy# DyGetCurrentVCFromRootVc(#0x14dfd9730)
#"<WCAccountLoginFirstViewController: 0x14ded9e00>"
cy# DyCurrentVC()
#"<WCAccountLoginFirstViewController: 0x14ded9e00>"

参考链接

The LLDB Debugger
chisel-github
MonkeyDev
cycript
DerekSelander

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容