LLDB高级调试+Cycript
对了 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 choosing
Open up (or create) ~/.lldbinit
Add 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 常用指令整理,请点击这里。