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

0x01 LLDB

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

基础语法

 [ [...]]  [-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
lldb调试和chisel、DerekSelander、cycript的简单应用_第1张图片

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

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

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


lldb调试和chisel、DerekSelander、cycript的简单应用_第2张图片
  • 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
lldb调试和chisel、DerekSelander、cycript的简单应用_第3张图片

重启Xcode,即可使用。

常用命令概览

  • 打印视图层级
(lldb) pviews 0x170391b90
>
   | >
  • 打印视图上级
 pviews -u 0x17438fe50
  • 打印整个界面的层级关系
pviews
  • 查看响应者链
presponder 0x150867780
  • 查看按钮绑定的事件
(lldb) pactions 0x150867780
: 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.

  • 快速定位到某个组件。

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

(lldb) taplog 
Process 2388 resuming
; layer = >
  • 闪烁一次控件,方便开发者定位控件。
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 = >
  • 列出某个类所有的方法和属性。(方法后面的地址为imp的地址。)
(lldb) methods 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

lldb调试和chisel、DerekSelander、cycript的简单应用_第4张图片

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

lldb调试和chisel、DerekSelander、cycript的简单应用_第5张图片

界面调试

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

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

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

  • 打印当前UIApplication的信息。
cy# [UIApplication sharedApplication]
#""

cy# UIApp
#""
  • 定义一个变量keywindow来保存当前的window,接着便可以通过变量keywindow直接打印有关window的属性。
cy# var keyWindow = UIWindow.keyWindow()
#"; layer = >"

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

只获取key

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

格式化输出一波:

#0x157f64480.recursiveDescription().toString()
  • 查看当前界面所有为某个类的对象
cy# choose(UIButton)
[#">",#">",#">"]
  • 动态修改某个控件的值
#0x10b1caa00.text="123123"
"123123"

高级用法:

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

lldb调试和chisel、DerekSelander、cycript的简单应用_第6张图片

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

  • 打印视图层级和控制器层级。
pviews() 
pvcs
  • 打印对象所绑定的方法。
cy# pactions(#0x1053163b0)
" showChangeLog:"
  • 打印响应链
cy# rp(#0x1053163b0)
`>
>

; layer = >

`
自定义cy文件,实现自定义命令

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

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

lldb调试和chisel、DerekSelander、cycript的简单应用_第7张图片

lldb调试和chisel、DerekSelander、cycript的简单应用_第8张图片
更改文件的类型

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文件夹中。

lldb调试和chisel、DerekSelander、cycript的简单应用_第9张图片

lldb调试和chisel、DerekSelander、cycript的简单应用_第10张图片

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()
#""
cy# DyKeyWindow()
#"; layer = >"
cy# DyGetCurrentVCFromRootVc(#0x14dfd9730)
#""
cy# DyCurrentVC()
#""

参考链接

The LLDB Debugger
chisel-github
MonkeyDev
cycript
DerekSelander

你可能感兴趣的:(lldb调试和chisel、DerekSelander、cycript的简单应用)