对了 LLDB 有了一些认识之后,也能够更好的使用 LLDB 了,但是对于常用的一些指令使用起来还是过于麻烦,为了更简单的使用 LLDB 就需要一些插件帮助了,下面就介绍一些 LLDB 插件的安装和使用。
1、Chisel 安装
这个是 FaceBook 提供给开发者的 LLDB 的插件,打开终端使用 brew install chisel 安装。
安装完成后,在家目录下找 .lldbinit 文件,如果没有就 touch .lldbinit 新建一个,然后 vi .lldbinit 添加 fblld.py 的路径(我的路径为 /usr/local/Cellar/chisel/1.8.1/libexec/fblldb.py)。
添加 command script import /usr/local/Cellar/chisel/1.8.1/libexec/fblldb.py ,最后 ESC ,输入 :wq 保存并退出。
安装完成后,如果已经打开了 Xcode ,可以在 Xcode 的 LLDB 上输入 command source ~/.lldbinit 重新加载,也可以重新打开 Xcode。
Xcode 新建工程,随意一个断点, 然后 lldb输入 pviews self.view 指令,回车后打印了 self.view 的详细信息,就说明安装成功了。
Xcode 11 注意:
因为 Xcode 11 版本不支持 python 2 ,所以如果控制台出现如下报错:
error: module importing failed: Missing parentheses in call to 'print'. Did you mean print('Whoops! You are missing the <' + arg.argName + '> argument.')? (fblldb.py, line 98)
请点此处下载最新的 Chisel,终端执行下方指令(我当前的是 Chisel 1.8.1) ,进入 1.8.1 ,用新下载的文件替换 1.8.1 里面相同名称的文件即可解决。
cd /usr/local/Cellar/chisel
open .
2、Chisel 使用
启动一个项目,随意进入一个页面,然后暂停项目。
1、pviews 指令
pviews :可以查看图层,能清楚的看到图层关系,是以字符串输出的,成树状结构,很清晰。
Xcode 下方 LLDB 输入 pviews 就能查看所有图层关系。

2、pvc 指令
pvc :可以查看控制器的层级。

3、pactions 指令
paction:可以打印一个按钮的所在的页面和它的点击事件方法。
当前页面上有一个 设置 按钮 。

打开 ViewDebug ,这个不管在正向和逆向中都可以看到按钮的指针地址,所以获取到 设置 按钮的指针地址为:0x7fce735209e0 。

LLDB 上输入:pactions 0x7fce735209e0 ,就可以打印 设置 按钮的所在的页面和它的点击事件方法。

4、presponder 指令
presponder :获取按钮的响应链。
LLDB 输入:presponder 0x7fce735209e0 当前响应链比较少,所以就直接打印了自己和系统的响应链了。

4、pclass 指令
pclass :可以查看一个类的继承关系。
当前类为 RootNavigationController 、地址为 0x7fce7386c600 、LLDB:pclass 0x7fce7386c600 回车,输出了继承树。

5、pmethods 指令
pmethods :查看一个类的所有方法。

6、pinternals 指令
pinternals :查看一个类的成员变量以及在当前内存中的值。

7、fvc、fv 指令
fvc:使用一个 Controller 类名获取 Controller 类的在内存中的地址(不一定100%能获取到)。
LLDB 输入:fvc RootNavigationController ,显示如下:

fv:使用一个 View 类名获取 View 类的在内存中的地址(不一定100%能获取到)。
8、taplog 指令
taplog :断点一次可处理响应事件的 View。
暂停程序,然后 Xcode 下方 LLDB 输入 :taplog ,发现断点过掉了,随意点一个 UIButton 然后进会进入断点。

8、flicker 指令
flicker :会让当前地址的视图隐藏并显示(闪烁)一次。
比如上方获取到的 Button, flicker 0x7ffc03438290 ,这个 Button 就会闪烁一次。

9、vs 指令
vs :可以查看图层的当前层级,下一个层级,上一个层级。
LLDB 上输入 :vs 0x7ffc03438290 就会发现,界面上按钮变为红色,LLDB控制台打印了一些的指令信息。

-
(q) to quit.:输入q退出。 -
(w) move to superview:输入w移动到父View。 -
(s) move to first subview:输入s移动到我当前子控件的第一个(subviews.firstObject)。 -
(a) move to previous sibling:输入a移动到当前视图平级关系的前一个视图。 -
(d) move to next sibling:输入d移动到当前视图平级关系的后一个视图。 -
(p) print the hierarchy:输入p打印当前控件的层级关系。
3、lldb_commands 的安装
这也是一个功能比较强大的 LLDB 插件。点此处下载
安装方式如下:
To Install, copy/clone the lldb_commands folder to a dir of your choosingOpen up (or create) ~/.lldbinitAdd the following command to your ~/.lldbinit file: command script import /path/to/lldb_commands/dslldb.py
配置方式和 Chisel 一样,不过就是这个需要自己下载,个人建议可以把 lldb_commands 和 Chisel 放在一个路径下 /usr/local/Cellar/ 下。
终端输入 vi ~/.lldbinit ,按 i,再添加一条 command script import /usr/local/Cellar/lldb_commands/dslldb.py ,然后按 ESC ,在输入 :wq 保存退出。
Xcode 输入 command source ~/.lldbinit 导入一下。
4、lldb_commands 的使用
1、search 指令
search :如果上方的 fvc、fv 指令查找不到的话,可以使用 search 指令,比如:search UIView:

2、sbt 指令
sbt:逆向中是没有符号表的,所以给一个内存地址下断点后,使用并 bt 不能显示详细信息。但是 lldb_commands 就可以帮助恢复一些函数名称,但是不一定能恢复全部。
当前给 下一步 按钮添加断点,然后打印调用队栈。

当前按钮是在 WCAccountMainLoginViewController 上,Action 为 onNext ,使用 mehthods 查找 onNext 的指针地址,然后下断点。
-
search WCAccountMainLoginViewController:获取WCAccountMainLoginViewController在内存中的地址:0x11307ea00 -
methods 0x11307ea00:获取WCAccountMainLoginViewController的所有方法,查找onNext的指针地址为0x108aab4cc -
b -a 0x108aab4cc:给onNext方法下内存断点
(lldb) search WCAccountMainLoginViewController
<WCAccountMainLoginViewController: 0x11307ea00>
(lldb) methods 0x11307ea00
Instance Methods:
- (void) WCBaseInfoItemBeginEdit:(id)arg1; (0x108aac7bc)
- (void) WCBaseInfoItemEndEdit:(id)arg1; (0x108aac870)
- (void) WCBaseInfoItemPressReturnKey:(id)arg1; (0x108aad3f0)
- (void) WCBaseInfoItemEditChanged:(id)arg1; (0x108aac924)
- (void) initMoreView; (0x108aa9838)
- (void) setupWithData:(id)arg1; (0x108aa6e0c)
- (void) onNext; (0x108aab4cc)
(lldb) b -a 0x108aab4cc
Breakpoint 1: where = WeChat`___lldb_unnamed_symbol273847$$WeChat, address = 0x0000000108aab4cc
(lldb)
这样方法的内存断点就下好了,点击一下进入了一个断点。
LLDB 输入 bt ,因为没有符号文件表的关系,看不到函数名称。

然后使用 lldb_commands 的 sbt 指令,能恢复一些函数名称,看到了刚才的点击方法。

如果发现恢复的不是当前想要的,就只能重新运行,再次尝试恢复。
5、Cycript 的介绍
Cycript 是由 Cydia 创始人 Saurik 推出的一款脚本语言。Cycript 混合了 OC 、JavaScript 语法的解释器,这意味着我们能够在一个命令中使用 OC 或者 JavaScript ,甚至两者并用。它能够挂钩正在运行的进程,能够在运行时修改很多东西。
6、Cycript 的安装
首先点此处下载 Cycript,下载完成后,建议放在 ~/opt 目录下面。打开目录,复制粘贴。
cd ~/opt
open .
接下来需要配置一下环境变量,以便于在任何地方使用。
可以配置在 .bash_profile 下(如果你使用的不是 bash ,那么你可以在你所使用的 Shell 下添加一句 source /Users/XXX(前面改成你的用户名,并删掉括号和括号里的更改描述)/.bash_profile)。
然后 vi ~/.bash_profile ,在 export PATH="/opt/local/bin:/opt/local/sbin:$PATH" 前添加 export CYCRIPT=/opt/cycript_0.9.594/(等号前面的命名可以随意,保证不重复就行,等号后面的就是你的 cycript 的路径) ,然后在 export PATH :$PATH前添加 :$CYCRIPT 名称就可以了。
然后 :wq 保存退出,iTerm 输入 :cycript,回车出现了 cy# ,说明安装成功了。control D 退出。
注意,如果安装过程中出现如下报错:

解决方案:
iTerm 输入:cd /System/Library/Frameworks/Ruby.framework/Versions ,open .

7、MonkeyDev 的安装
因为要使用 Cycript ,在非越狱环境下,不能将 Cycript 注入手机,所以需要借助 MonkeyDev 注入 Framework 库来实现。
环境要求
使用工具前确保如下几点:
- 安装最新的theos
sudo git clone --recursive https://github.com/theos/theos.git /opt/theos
- 安装ldid(如安装theos过程安装了ldid,跳过)
brew install ldid
安装
你可以通过以下命令选择指定的Xcode进行安装:
sudo xcode-select -s /Applications/Xcode-beta.app
默认安装的Xcode为:
xcode-select -p
执行安装命令:
sudo /bin/sh -c "$(curl -fsSL https://raw.githubusercontent.com/AloneMonkey/MonkeyDev/master/bin/md-install)"
卸载
sudo /bin/sh -c "$(curl -fsSL https://raw.githubusercontent.com/AloneMonkey/MonkeyDev/master/bin/md-uninstall)"
更新
如果没有发布特殊说明,使用如下命令更新即可:
sudo /bin/sh -c "$(curl -fsSL https://raw.githubusercontent.com/AloneMonkey/MonkeyDev/master/bin/md-update)"
安装/更新之后重启下Xcode再新建项目。
8、Cycript 的使用
iTerm 输入 :cycript,就开始使用 cycript 了。
1、简单的判断字符串
cy# "STRING" == "string"
false
2、简单的两数相加
cy# a = 100
100
cy# b = a + 10
110
3、Cycript 的准备工作
上面的2个仅仅是为了认识一下 Cycript ,我们使用 Cycript 是因为它的进程附加能力。
在逆向中,LLDB 虽然也非常好用,但是使用 LLDB 必须要把程序断点下来,有时候对程序的执行有一些影响,更改完一些东西需要过掉断点才能看到修改的结果,并不能及时的反馈给我们。
但是,Cycript 是动态调试程序的,不需要把程序断点下来,能及时的反馈修改的结果,这样就非常方便了。
接下来,尝试进程附加。因为上方说过要在非越狱环境下使用 Cycript ,需要借助 MonkeyDev 注入 Framework 库来实现。
所以打开 Xcode 使用 MonkeyDev(在 Xcode 的最下方) 新建工程。
注意:MonkeyDev 工程目录中不能有中文路径,否则有问题,这个问题是 Theos 的问题,Theos 是外国人开发的,没有考虑中文情况。
首先运行一下空工程,然后把砸过壳的 xxx.app 或者 xxx.ipa 放入 TargetApp 目录下。再次运行工程,就能重签名 xxx.app 了。

MonkeyDev 帮我们把 Cycript.framework 注入了,查看 XxxDylib 文件夹下文件 XxxDylib.m 中有一行 CYListenServer(6666); 这个就是注入 Cycript 时的端口号。
打开 iPhone -> 设置 -> 无线局域网 -> 点击当前正在使用的WiFi -> 查看IP 地址。
获取到的 IP 地址为:192.168.50.49。
注意: 电脑使用的 WiFi 和手机使用的 WiFi 必须是同一 WiFi,运行的 App 不要进入后台。
4、Cycript 的使用
接下来继续 iTerm 操作,如果在 cycript 下,先 control D 退出 cycript,然后iTerm 输入:cycript -r 192.168.50.49:6666 回车就进入了 cycript 了。
1、keyWindow 的获取
想要获取当前的 keyWindow:UIWindow.keyWindow()就可以获取。
cy# UIWindow.keyWindow()
#"<iConsoleWindow: 0x10e45a9d0; baseClass = UIWindow; frame = (0 0; 375 667); gestureRecognizers = <NSArray: 0x280f063a0>; layer = <UIWindowLayer: 0x28003ea80>>"
2、UIApplication 的获取
想要获取当前的 UIApplication:[UIApplication sharedApplication] 或者 UIApp 就可以。
cy# [UIApplication sharedApplication]
#"<UIApplication: 0x10e41a1a0>"
cy# UIApp
#"<UIApplication: 0x10e41a1a0>"
3、变量的定义和使用
和 OC 的语法很像。当前需要定义一个变量 keyWindow:var keyWindow = UIWindow.keyWindow() ,然后就可以使用 keyWindow 变量了。比如:获取 rootViewController 输入:keyWindow.rootViewController 。
可以使用 tab 进行语法补全,但是没有提示。
cy# var keyWindow = UIWindow.keyWindow()
#"<iConsoleWindow: 0x10e45a9d0; baseClass = UIWindow; frame = (0 0; 375 667); gestureRecognizers = <NSArray: 0x280f063a0>; layer = <UIWindowLayer: 0x28003ea80>>"
cy# keyWindow
#"<iConsoleWindow: 0x10e45a9d0; baseClass = UIWindow; frame = (0 0; 375 667); gestureRecognizers = <NSArray: 0x280f063a0>; layer = <UIWindowLayer: 0x28003ea80>>"
cy# keyWindow.rootViewController
#"<MMUINavigationController: 0x111921a00>"
4、# 的使用
在 cycript 上可以使用 # 跟上一个地址,相当于 LLDB 中 p 跟上一个地址的 功能,执行一个对象。比如:#0x111921a00 回车打印如下:

5、* 的使用
* 跟上一个定义的变量,比如上方的 var keyWindow = UIWindow.keyWindow(),然后 *keyWindow 就会打印所有的成员变量。

6、recursiveDescription() 的使用
在 cycript 上想查看一个页面的 UI 层级,可以使用 keyWindow.recursiveDescription() 循环遍历层级并打印。如果想要格式化 keyWindow.recursiveDescription().toString()。


7、choose 的使用
在 LLDB 上可以使用 fvc 、fv、search 跟一个类名去查找对象,在 cycript 可以使用 choose 跟一个类名去查找对象,cycript 输入:choose(UIButton) 回车。

8、一些其他说明
在 cycript 下 只要我们的 App 不关闭(即所谓的进程不关闭),那刚才申请的 keyWindow 就会一直存在,退出了 App 就需要重新定义变量了。再次启动就不需要 Xcode 了,因为当前安装的 App 已经注入好了 Framework ,直接使用 cycript 即可。
5、使用脚本启动 Cycript
为了能够快速的使用 Cycript ,避免每次都输入繁琐的命令,就可以使用脚本辅助。
比如连接上方已经注入好 Cycript.framework 的其他 App,把连接指令 cycript -r 192.168.50.49:6666 放入脚本 cylogin.sh 里,下次启动如果在IP 地址没有变化的情况下,直接输入 ./cylogin.sh 就可以了。
为了想能够在任何地方都可以使用这些脚本,就需要配置一下环境变量。
既然需要配置环境变量,那不如新建一个文件夹专为此服务,之后还可以给这个文件夹里添加更多脚本。
在家目录下创建一个文件夹 TCShell(你可以在任何目录创建,叫任何名称,只要配置环境变量时,填写好你放的目录和名称即可),cd TCShell ,vi cylogin.sh 将上方的指令 cycript -r 192.168.50.49:6666 放入,然后保存退出。再 chomd 755 cylogin.sh 给 cylogin.sh 添加执行权限。

在配置环境变量前,验证一下 cylogin.sh 脚本是否正确。sh cylogin.sh 或者 ./cylogin.sh 如果进入了 Cycript 显示 cy# 说明我们配置好了。

接下来需要配置环境变量了(和第6条 Cycript 的安装 同理),vi ~/.bash_profile 打开,export 路径后保存退出。重启一下终端,这样就可以在任何地方使用 sh cylogin.sh 进入 Cycript 了。


6、Cycript 练习
微信登录页面有一个国家和地区选择,点击进入后,阿尔巴尼亚的编号是 +355 ,现在把这个改成 +1008611。
猜测+355 是一个 UILabel,cy# 输入 choose(UILabel) 回车,commadn F 搜索 +355,复制地址 0x110351910,然后 #0x110351910.text = '+1008611'; 回车就发现更改成功了。

tancheng@tanchengdeMacBook-Pro /~ : sh cylogin.sh
cy# choose(UILabel)
//...
cy# #0x110351910.text = '+1008611';
"+1008611"
cy#

注意:在重新进入这个页面发现又变成 +355 了,但是执行 #0x110351910 时,打印还是 '+1008611' 这是因为这个对象还没有完全销毁,所以如果页面变了,之前的地址就不要再次使用了。
9、Monkey Dev 中 CY 文件命令的使用
使用 Monkey Dev 注入的 Cycript 时候有一些指令还是很好用的。
-
APPID: 获取 Bundle ID -
pviews():获取子视图层级 -
pvcs():获取控制器层级
10、封装 CY 文件
如果不是 Monkey Dev 注入的 Cycript 那是没有这些的,这就需要我们自己封装了。
现在借助 Monkey Dev 帮我们重签的 WeChat 的时候,将我们自定义的 CY 文件 Copy 到工程中。
新建 Monkey Dev 工程,运行一下空工程(每次必做的事情),添加 Target App 。
Xcode 在第一个 target 新建文件, iOS -> Others -> Empty File ,命名为 CurrentCY.cy ,在这个 target 的 Bulid Phases 下方 Copy Files 添加 CurrentCY.cy。

然后我们写点东西,运行工程。
sum=function(a,b) {
return a+b;
}
CurrentKeyWindow=function(){
return UIApp.keyWindow;
}
CurrentRootVC=function(){
return UIApp.keyWindow.rootViewController;
}
打开 iTerm 进入 Cycript ,导入 CurrentCY.cy 这是必须要做的事情,输入 @import CurrentCY ,回车出现 {} 代表导入成功了,试试导入的指令。
//...
tancheng@tanchengdeMacBook-Pro ~ : sh cylogin.sh
cy# @import CurrentCY
{}
cy# sum(10,20);
30
cy# CurrentKeyWindow ()
#"<iConsoleWindow: 0x135d4a890; baseClass = UIWindow; frame = (0 0; 375 667); gestureRecognizers = <NSArray: 0x282626e50>; layer = <UIWindowLayer: 0x28296ab40>>"
cy# CurrentRootVC()
#"<MMUINavigationController: 0x136849c00>"
cy#
这样自己封装的 CY 文件就可以使用了,后续还可以继续往里面添加一些东西。
以上就是 LLDB高级调试+Cycript 的全部内容了。
关于 Chisel 和 lldb_commands 常用指令整理,请点击这里。
Cycript 常用指令整理,请点击这里。