目录
Cycript 的安装
-
① 下载与安装
从 Cycript 官网 下载 Cycript SDK,并将其放到
/opt
目录下。Cycript SDK 的目录结构如下所示:/opt/cycript_0.9.594 > tree . ├── Cycript.ios │ └── Cycript.framework │ ├── Cycript │ └── Headers │ └── Cycript.h ├── Cycript.lib │ ├── cycript-a32 │ ├── cycript-apl │ ├── cycript-pie │ ├── cycript0.9 │ │ ├── com │ │ │ └── saurik │ │ │ └── substrate │ │ │ └── MS.cy │ │ └── org │ │ └── cycript │ │ └── NSLog.cy │ ├── cynject │ ├── l │ │ └── linux │ ├── libJavaScriptCore.so │ ├── libcycript-sim.dylib │ ├── libcycript-sys.dylib -> libcycript.dylib │ ├── libcycript.cy │ ├── libcycript.db │ ├── libcycript.dylib │ ├── libcycript.jar │ ├── libcycript.so │ ├── libsubstrate.dylib │ └── u │ └── unknown ├── Cycript.osx │ └── Cycript.framework │ ├── Cycript │ └── Headers │ └── Cycript.h └── cycript 15 directories, 22 files
-
② 解决 Ruby 版本不匹配而导致的报错
打开终端,进入到 Cycript 的存放目录
/opt/cycript_0.9.594
,运行./cycript
命令。不出意外的话,这时候会有如下报错:/opt/cycript_0.9.594 > ./cycript dyld: Library not loaded: /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/libruby.2.0.0.dylib Referenced from: /opt/cycript_0.9.594/./Cycript.lib/cycript-apl Reason: image not found [1] 46992 abort ./cycript
这是因为本地的 Ruby 版本与 Cycript 要求的 Ruby 版本不一样导致的。先查看一下本地的 Ruby 版本:
~ > ruby -v ruby 2.6.3p62 (2019-04-16 revision 67580) [universal.x86_64-darwin19] ~ > cd /System/Library/Frameworks/Ruby.framework/Versions /System/Library/Frameworks/Ruby.framework/Versions > ls 2.6 Current
因为本地的 Ruby 的版本(
Ruby 2.6
)与 Cycript 要求的 Ruby 的版本(Ruby 2.0
),同属于Ruby 2.x
这个大版本内,并且一般情况下库的升级都会考虑向后兼容性。所以这两个版本的 Ruby 所提供的接口与支持,应该是差不多的。没必要为了配置 Cycript,降级本地的 Ruby 版本,或者再单独下载一个Ruby 2.0
。这里为了省事,直接为Ruby 2.6
创建一个符号链接,以后对Ruby 2.0
的调用,都会被重定向到Ruby 2.6
:-
禁用系统完整性保护(SIP,System Integrity Protection)
# 重启 macOS 并按住 option 键,直到出现磁盘图标后松开 # 然后按住 command + R,直到出现苹果图标后松开 # 之后等待片刻,进入 macOS 恢复模式 # 在进入恢复模式后,在顶部菜单栏中选择: 实用工具-终端,在终端输入命令 csrutil disable # 如果返回以下提示,则说明 SIP 禁用成功 Successfully disabled System Integrity Protection.Please restart the machine for the changes to take effect. # 重启 macOS reboot
-
创建符号链接,将对
Ruby 2.0
的调用重定向到对Ruby 2.6
的调用# 重新以可读可写的方式挂载文件系统 ~ > sudo mount -uw / # 创建符号链接,将对 Ruby 2.0 的调用重定向到对 Ruby 2.6 的调用 ~ > sudo mkdir -p /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ ~ > sudo ln -s /System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib/libruby.2.6.dylib /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/libruby.2.0.0.dylib
-
启用系统完整性保护(SIP,System Integrity Protection)
# 重启 macOS 并按住 option 键,直到出现磁盘图标后松开 # 然后按住 command + R,直到出现苹果图标后松开 # 之后等待片刻,进入 macOS 恢复模式 # 在进入恢复模式后,在顶部菜单栏中选择: 实用工具-终端,在终端输入命令 csrutil enable # 如果返回以下提示,则说明 SIP 启用成功 Successfully enabled System Integrity Protection.Please restart the machine for the changes to take effect. # 重启 macOS reboot
-
-
③ 配置环境变量 PATH,以方便地启动 Cycript 控制台
虽然安装进行到这里,Cycript 就可以正常运行了。但是实际使用起来不是很方便:因为每次都需要进入到
/opt/cycript_0.9.594
目录,然后运行./cycript
命令,才能调起 Cycript 的控制台为了能够在任意目录下方便地启动 Cycript 控制台,我们需要将存放 Cycript 的目录
/opt/cycript_0.9.594/
添加到环境变量PATH
中。在~/.zshrc
的底部添加如下配置:# 将 Cycript 的存放目录添加到环境变量 PATH 中 export PATH=/opt/cycript_0.9.594/:$PATH
之后,就可以在任意目录下启动 Cycript 的控制台了:
~ > cycript cy# a = 99 + 1 100
在非越狱环境中使用 Cycript(通过手动配置)
-
① 新建一个名为
CycriptDemo
的 iOS - App 工程
并将Project
-Info
-Deployment Target
-iOS Deployment Target
设置为10.0
-
② 将
/opt/cycript_0.9.594/Cycript.ios/Cycript.framework
集成到CycriptDemo
工程中
因为Cycript.framework
不包含 Bitcode
所以需要将Target
-Build Settings
-Build Options
-Enable Bitcode
设置为NO
-
③ 因为
Cycript.framework
依赖:JavaScriptCore.framework
、libsqlite3.tbd
、libc++.tbd
所以需要在Target
-Build Phases
-Link Binary With Libraries
中添加对这三个库的引用
-
④ 因为
Cycript.framework
在启动时,会去主 Bundle 中读取它自己的数据库libcycript.db
所以需要在Target
-Build Phases
-Copy Bundle Resources
中添加对/opt/cycript_0.9.594/Cycript.lib/libcycript.db
的引用
-
⑤ 在
ViewController.m
中编写如下代码
-
⑥ 将 MacBook 和 iPhone 连接到同一 WiFi 下,并查看 iPhone 的 IP 地址
-
⑦ 将工程
CycriptDemo
运行到真机
-
⑧ 在 MacBook 端启动 Cycript 控制台,然后连接到 iPhone 上 Cycript 的监听端口,并将 App 的背景颜色修改为橙色
~ > cycript -r 192.168.1.229:8899 cy# UIApp.keyWindow.rootViewController.view.backgroundColor = UIColor.orangeColor #"UIExtendedSRGBColorSpace 1 0.5 0 1"
-
如果终端界面一直停留在执行连接命令
cycript -r 192.168.1.229:8899
这一步,没有进入到 Cycript 的 REPL 环境,则有以下三种可能:- MacBook 和 iPhone 没有连接到同一 WiFi 下(请将 MacBook 和 iPhone 连接到同一 WiFi 下)
- MacBook 和 iPhone 连接的 WiFi 有问题(换一个 WiFi 试试;或直接使用 iPhone 开个热点,然后 MacBook 连接到 iPhone 发出的热点)
- Cycript 在 iPhone 端监听的端口号被占用(修改 iPhone 监听的端口号,重新将工程运行到真机,并使用新的端口号进行连接)
在非越狱环境中使用 Cycript(通过 MonkeyDev 自动配置)
虽然在非越狱环境中,我们可以通过手动配置的方式使用 Cycript
但是当我们每次要使用 Cycript 时,都需要:手动添加 Cycript.framework
及其依赖库、手动禁用 Bitcode
、手动开启 Cycript 的监听端口,这是个无聊且没有技术含量的过程
而在 MonkeyDev 中,已经为我们:自动集成了 Cycript.framework
及其依赖库、默认禁用了 Bitcode
、默认开启了 Cycript 的监听端口 6666
。只需要简单的几步,就可以使用 Cycript:
-
① 新建一个名为
CycriptDemo
的 MonkeyApp 工程
并将Project
-Info
-Deployment Target
-iOS Deployment Target
设置为10.0
-
② 将 MacBook 和 iPhone 连接到同一 WiFi 下,并查看 iPhone 的 IP 地址
-
③ 将工程运行到真机
-
④ 在 MacBook 端启动 Cycript 控制台,然后连接到 iPhone 上 Cycript 的监听端口,并将 App 的背景颜色修改为橙色
~ > cycript -r 192.168.1.229:6666 cy# UIApp.keyWindow.rootViewController.view.backgroundColor = UIColor.orangeColor #"UIExtendedSRGBColorSpace 1 0.5 0 1"
-
如果终端界面一直停留在执行连接命令
cycript -r 192.168.1.229:6666
这一步,没有进入到 Cycript 的 REPL 环境,则有以下三种可能:- MacBook 和 iPhone 没有连接到同一 WiFi 下(请将 MacBook 和 iPhone 连接到同一 WiFi 下)
- MacBook 和 iPhone 连接的 WiFi 有问题(换一个 WiFi 试试;或直接使用 iPhone 开个热点,然后 MacBook 连接到 iPhone 发出的热点)
- Cycript 在 iPhone 端监听的端口号被占用(修改 iPhone 监听的端口号,重新将工程运行到真机,并使用新的端口号进行连接)
在越狱环境中使用 Cycript
-
① 在已越狱的 iOS 设备上安装 Cycript
从 Cydia 自带的源
Cydia/Telesphoreo
下载:直接打开已越狱的 iOS 设备上的 Cydia,然后搜索 Cycript 后安装即可 -
② 使用 SSH 连接到已越狱的 iOS 设备
ssh root@192.168.1.229
-
③ 使用 ps 命令查看要修改的应用的进程 ID 或进程名称
ps ax | grep WeChat
-
④ 将 Cycript 附加到指定 ID 或指定名称所标识的进程
cycript -p 1354
-
⑤ 补充
因为已越狱的 iOS 设备上可以获取 root 权限
所以可以直接抓取要附加的进程,并在该进程上创建一个挂起的线程,然后在这个挂起的线程里申请一片用于加载Cycript.framework
动态库的内存,最后恢复线程,Cycript.framework
动态库就被注入了
导入外部的 Cycript 脚本
-
在非越狱环境中导入外部的 Cycript 脚本(在没有使用 MonkeyDev 的情况下)
① 将外部 Cycript 脚本添加到主工程中,并在主工程的
Target
-Build Phases
-Copy Bundle Resources
中创建对这些外部脚本的引用:
② 当需要使用外部 Cycript 脚本所提供的功能时,请在 Cycript 的 REPL 中使用@import
命令导入这些外部脚本(其中,模块名称即文件名称):~ > cycript -r 192.168.1.229:8899 cy# @import libcycript cy# @import md cy# @import mjcript cy# @import MS cy# @import utils
-
在非越狱环境中导入外部的 Cycript 脚本(在使用 MonkeyDev 的情况下)
① 在 MonkeyDev 中,通过配置
MDConfig.plist
可以导入从网络下载的外部 Cycript 脚本,比如通过如下的配置:
LoadAtLaunch
:表示是否在启动的时候默认加载脚本,默认加载的脚本就不用再@import xxx
导入,可以直接使用。如果不是默认加载的脚本就需要@import xxx
导入,xxx
就是图中的 key,比如@import nslog
、@import ms
、@import hook
、@import md
priority
:表示加载的优先级,数字越小优先级越高,比如某些脚本需要依赖其它脚本就需要调整优先级,让被依赖的脚本先加载content
和url
:脚本可以直接写到content
里面,也可以是网络的url
,会自动下载下来:➜ cycript_0.9.594 ./cycript -r 192.168.1.229:6666 cy# APPID @"com.alonemonkey.TestCycript" cy# pviews() <UIWindow: 0x105313b60; frame = (0 0; 375 667); autoresize = W+H; gestureRecognizers = <NSArray: 0x1c425bae0>; layer = <UIWindowLayer: 0x1c40394e0>> | <UIView: 0x1053205d0; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x1c403bd80>> | | <UILabel: 0x10531e7d0; frame = (102.5 45; 170 40); text = 'AloneMonkey'; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x1c408c0d0>> | | <UILabel: 0x1053207b0; frame = (97.5 110; 180 40); text = 'You are the best!!!'; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x1c408e150>> | | <UITextView: 0x105836000; frame = (26 230; 343 427); text = '/opt/MonkeyDev/bin/md ....'; clipsToBounds = YES; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x1c0058fc0>; layer = <CALayer: 0x1c002f1a0>; contentOffset: {0, 0}; contentSize: {343, 317}; adjustedContentInset: {0, 0, 0, 0}> | | | <<_UITextContainerView: 0x105318060; frame = (0 0; 343 317); layer = <__UITextTiledLayer: 0x1c40c42f0>> minSize = {0, 0}, maxSize = {1.7976931348623157e+308, 1.7976931348623157e+308}, textContainer = <NSTextContainer: 0x1c0105fa0 size = (343.000000,inf); widthTracksTextView = YES; heightTracksTextView = NO>; exclusionPaths = 0x1c40025e0; lineBreakMode = 0> | | | | <__UITileLayer: 0x1c0240780> (layer) | | | | <__UITileLayer: 0x1c0240720> (layer) | | | | <__UITileLayer: 0x1c0240a20> (layer) | | | <UIImageView: 0x105322260; frame = (3 421.5; 337 2.5); alpha = 0; opaque = NO; autoresize = TM; userInteractionEnabled = NO; layer = <CALayer: 0x1c403c620>> | | | <UIImageView: 0x105322490; frame = (337.5 380; 2.5 44); alpha = 0; opaque = NO; autoresize = LM; userInteractionEnabled = NO; layer = <CALayer: 0x1c403c6e0>> | | <UIButton: 0x1053163b0; frame = (127.5 175; 120 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x1c4039aa0>> | | | <UIButtonLabel: 0x10540fcf0; frame = (2 6; 116.5 18); text = 'ShowChangeLog'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x1c0087b70>> | | <_UILayoutGuide: 0x105320fa0; frame = (0 0; 0 20); hidden = YES; layer = <CALayer: 0x1c403bfe0>> | | <_UILayoutGuide: 0x1053213a0; frame = (0 667; 0 0); hidden = YES; layer = <CALayer: 0x1c403bee0>> cy# pvcs() "<CustomViewController 0x1053133f0>, state: appeared, view: <UIView 0x1053205d0>" 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# ?exit
虽然理想很丰满,但是现实很骨感。因为 GitHub 的服务器位于国外,所以在通过网络下载 Cycript 脚本的时候,通常会失败。此时,MonkeyDev 工程的控制台会输出如下报错信息:
...... Download cycript(https://cydia.saurik.com/api/latest/3) then run: ./cycript -r 192.168.1.229:6666 ...... # 提示下载 https://raw.githubusercontent.com/AloneMonkey/MDCycript/master/MS.cy 失败 2022-02-20 23:35:01.773264+0800 TargetApp[24710:5320995] [] tcp_input [C1.1:3] flags=[R] seq=228439538, ack=2355836980, win=1031 state=ESTABLISHED rcv_nxt=228439538, snd_una=2355836463 2022-02-20 23:35:01.774120+0800 TargetApp[24710:5320995] Connection 1: received failure notification 2022-02-20 23:35:01.774150+0800 TargetApp[24710:5320995] Connection 1: received ECONNRESET with incomplete TLS handshake - generating errSSLClosedNoNotify 2022-02-20 23:35:01.774176+0800 TargetApp[24710:5320995] Connection 1: failed to connect 3:-9816, reason -1 2022-02-20 23:35:01.774188+0800 TargetApp[24710:5320995] Connection 1: encountered error(3:-9816) 2022-02-20 23:35:01.775097+0800 TargetApp[24710:5320995] Task <FE90F26F-FBA4-436F-A767-4A2DC298A95D>.<1> HTTP load failed, 0/0 bytes (error code: -1200 [3:-9816]) 2022-02-20 23:35:01.782158+0800 TargetApp[24710:5320994] Task <FE90F26F-FBA4-436F-A767-4A2DC298A95D>.<1> finished with error [-1200] Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={_kCFStreamErrorCodeKey=-9816, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, NSUnderlyingError=0x2838648a0 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, _kCFNetworkCFStreamSSLErrorOriginalValue=-9816, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9816}}, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://raw.githubusercontent.com/AloneMonkey/MDCycript/master/MS.cy, NSErrorFailingURLStringKey=https://raw.githubusercontent.com/AloneMonkey/MDCycript/master/MS.cy, _kCFStreamErrorDomainKey=3} 2022-02-20 23:35:01.782232+0800 TargetApp[24710:5320994] [Cycript] Failed download script [ms]: An SSL error has occurred and a secure connection to the server cannot be made. # 提示下载 https://raw.githubusercontent.com/AloneMonkey/MDCycript/master/md.cy 失败 2022-02-20 23:35:01.953544+0800 TargetApp[24710:5320996] [] tcp_input [C2.1:3] flags=[R] seq=1015217985, ack=2664387600, win=1031 state=ESTABLISHED rcv_nxt=1015217985, snd_una=2664387083 2022-02-20 23:35:01.954294+0800 TargetApp[24710:5320996] Connection 2: received failure notification 2022-02-20 23:35:01.954342+0800 TargetApp[24710:5320996] Connection 2: received ECONNRESET with incomplete TLS handshake - generating errSSLClosedNoNotify 2022-02-20 23:35:01.954358+0800 TargetApp[24710:5320996] Connection 2: failed to connect 3:-9816, reason -1 2022-02-20 23:35:01.954372+0800 TargetApp[24710:5320996] Connection 2: encountered error(3:-9816) 2022-02-20 23:35:01.955309+0800 TargetApp[24710:5320996] Task <DCA9F824-77FA-42E2-9CE5-30930D8F652B>.<2> HTTP load failed, 0/0 bytes (error code: -1200 [3:-9816]) 2022-02-20 23:35:01.956396+0800 TargetApp[24710:5320997] Task <DCA9F824-77FA-42E2-9CE5-30930D8F652B>.<2> finished with error [-1200] Error Domain=NSURLErrorDomain Code=-1200 "An SSL error has occurred and a secure connection to the server cannot be made." UserInfo={_kCFStreamErrorCodeKey=-9816, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, NSUnderlyingError=0x283884b40 {Error Domain=kCFErrorDomainCFNetwork Code=-1200 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, _kCFNetworkCFStreamSSLErrorOriginalValue=-9816, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9816}}, NSLocalizedDescription=An SSL error has occurred and a secure connection to the server cannot be made., NSErrorFailingURLKey=https://raw.githubusercontent.com/AloneMonkey/MDCycript/master/md.cy, NSErrorFailingURLStringKey=https://raw.githubusercontent.com/AloneMonkey/MDCycript/master/md.cy, _kCFStreamErrorDomainKey=3} 2022-02-20 23:35:01.956501+0800 TargetApp[24710:5320997] [Cycript] Failed download script [md]: An SSL error has occurred and a secure connection to the server cannot be made. # 不管通过网络下载 Cycript 脚本是成功还是失败,都会调用 -[MDCycriptManager finishDownload] 方法,输出如下信息: 2022-02-20 23:35:01.956551+0800 TargetApp[24710:5320997] [Cycript] Finish download all script! ......
② 幸运的是,在 MonkeyDev 中也可以手动导入外部 Cycript 脚本。不过,导入的外部 Cycript 脚本需要放置在主工程的
Target
-Build Phases
-Copy Files
-Frameworks
中:
当需要使用外部 Cycript 脚本所提供的功能时,请在 Cycript 的 REPL 中使用@import
命令导入这些外部脚本(其中,模块名称即文件名称):~ > cycript -r 192.168.1.229:6666 cy# @import libcycript cy# @import md cy# @import mjcript cy# @import MS cy# @import utils
注意:
在将外部 Cycript 脚本添加到 Xcode 工程中时,Xcode 默认会在主工程的Target
-Build Phases
-Copy Bundle Resources
中创建对这些外部 Cycript 脚本的引用。但是这与 MonkeyDev 所约定的保存外部 Cycript 脚本的位置(Target
-Build Phases
-Copy Files
-Frameworks
)不一样。为了避免将外部 Cycript 脚本重复添加到主工程的 Bundle 中,最好将 Xcode 默认创建的这些对外部 Cycript 脚本的引用删除
-
在越狱环境中导入外部的 Cycript 脚本
在已越狱的 iOS 设备中,提供了一个名为
/usr/lib/cycript0.9/
的目录,用于存放外部 Cycript 脚本,此目录相当于在越狱环境中 Cycript@import
命令的根目录。例如:saurik 将其编写的 Cycript 脚本
MS.cy
存放在了已越狱 iOS 设备中的/usr/lib/cycript0.9/com/saurik/substrate
目录下,则在 Cycript 的 REPL 中可以通过以下命令导入该脚本:cy# @import com.saurik.substrate.MS
Tyilo 将其编写的 Cycript 脚本
utils.cy
存放在了已越狱 iOS 设备中的/usr/lib/cycript0.9/com/tyilo
目录下,则在 Cycript 的 REPL 中可以通过以下命令导入该脚本:cy# @import com.tyilo.utils;
补充:Cycript 和 LLDB 中经常用到的 Objective-C 私有方法
-
① -[NSObject _ivarDescription]
用于列出指定实例对象的所有成员变量的类型和值,包括继承自(自定义超类和系统超类)的成员变量
cy# [#0x00000002837e1800 _ivarDescription].toString() `<HcgStudent: 0x2837e1800>: in HcgStudent: \t_chineseScore (float): 80 \t_englishScore (float): 90 \t_mathematicalScore (float): 100 in HcgPerson: \t_age (int): 20 \t_height (float): 170 \t_name (NSString*): @"hcg" \t_addr (NSString*): @"XiaMen" in NSObject: \tisa (Class): HcgStudent (isa, 0x21a104ba2035)`
-
② -[NSObject _shortMethodDescription]
用于列出指定实例对象的所有对象方法和类方法,包括继承自(自定义超类)的方法
cy# [#0x00000002837e1800 _shortMethodDescription].toString() `<HcgStudent: 0x2837e1800>: in HcgStudent: \tClass Methods: \t\t+ (id) studentWithChineseScore:(float)arg1 englishScore:(float)arg2 mathematicalScore:(float)arg3; (0x104a94080) \tProperties: \t\t@property (nonatomic) float chineseScore; (@synthesize chineseScore = _chineseScore;) \t\t@property (nonatomic) float englishScore; (@synthesize englishScore = _englishScore;) \t\t@property (nonatomic) float mathematicalScore; (@synthesize mathematicalScore = _mathematicalScore;) \tInstance Methods: \t\t- (void) setChineseScore:(float)arg1; (0x104a9416c) \t\t- (void) setEnglishScore:(float)arg1; (0x104a941c4) \t\t- (void) setMathematicalScore:(float)arg1; (0x104a9421c) \t\t- (id) initWithChineseScore:(float)arg1 englishScore:(float)arg2 mathematicalScore:(float)arg3; (0x104a94000) \t\t- (float) chineseScore; (0x104a94144) \t\t- (float) englishScore; (0x104a9419c) \t\t- (float) mathematicalScore; (0x104a941f4) in HcgPerson: \tClass Methods: \t\t+ (id) personWithName:(id)arg1 age:(int)arg2 height:(float)arg3 addr:(id)arg4; (0x104a94338) \t\t+ (id) breathe; (0x104a94574) \tProperties: \t\t@property (retain, nonatomic) NSString* name; (@synthesize name = _name;) \t\t@property (nonatomic) int age; (@synthesize age = _age;) \t\t@property (nonatomic) float height; (@synthesize height = _height;) \t\t@property (retain, nonatomic) NSString* addr; (@synthesize addr = _addr;) \tInstance Methods: \t\t- (void) setAddr:(id)arg1; (0x104a9467c) \t\t- (id) addr; (0x104a94660) \t\t- (id) initWithName:(id)arg1 age:(int)arg2 height:(float)arg3 addr:(id)arg4; (0x104a9424c) \t\t- (id) sayHello; (0x104a9447c) \t\t- (id) name; (0x104a94590) \t\t- (void) .cxx_destruct; (0x104a946b0) \t\t- (void) setName:(id)arg1; (0x104a945ac) \t\t- (float) height; (0x104a94620) \t\t- (void) setHeight:(float)arg1; (0x104a9463c) \t\t- (int) age; (0x104a945e0) \t\t- (void) setAge:(int)arg1; (0x104a945fc) (NSObject ...)`
-
③ -[NSObject _methodDescription]
用于列出指定实例对象的所有对象方法和类方法,包括继承自(自定义超类和系统超类)的方法
cy# [#0x00000002837e1800 _methodDescription].toString() `<HcgStudent: 0x2837e1800>: in HcgStudent: \tClass Methods: \t\t+ (id) studentWithChineseScore:(float)arg1 englishScore:(float)arg2 mathematicalScore:(float)arg3; (0x104a94080) \tProperties: \t\t@property (nonatomic) float chineseScore; (@synthesize chineseScore = _chineseScore;) \t\t@property (nonatomic) float englishScore; (@synthesize englishScore = _englishScore;) \t\t@property (nonatomic) float mathematicalScore; (@synthesize mathematicalScore = _mathematicalScore;) \tInstance Methods: \t\t- (void) setChineseScore:(float)arg1; (0x104a9416c) \t\t- (void) setEnglishScore:(float)arg1; (0x104a941c4) \t\t- (void) setMathematicalScore:(float)arg1; (0x104a9421c) \t\t- (id) initWithChineseScore:(float)arg1 englishScore:(float)arg2 mathematicalScore:(float)arg3; (0x104a94000) \t\t- (float) chineseScore; (0x104a94144) \t\t- (float) englishScore; (0x104a9419c) \t\t- (float) mathematicalScore; (0x104a941f4) in HcgPerson: \tClass Methods: \t\t+ (id) personWithName:(id)arg1 age:(int)arg2 height:(float)arg3 addr:(id)arg4; (0x104a94338) \t\t+ (id) breathe; (0x104a94574) \tProperties: \t\t@property (retain, nonatomic) NSString* name; (@synthesize name = _name;) \t\t@property (nonatomic) int age; (@synthesize age = _age;) \t\t@property (nonatomic) float height; (@synthesize height = _height;) \t\t@property (retain, nonatomic) NSString* addr; (@synthesize addr = _addr;) \tInstance Methods: \t\t- (void) setAddr:(id)arg1; (0x104a9467c) \t\t- (id) addr; (0x104a94660) \t\t- (id) initWithName:(id)arg1 age:(int)arg2 height:(float)arg3 addr:(id)arg4; (0x104a9424c) \t\t- (id) sayHello; (0x104a9447c) \t\t- (id) name; (0x104a94590) \t\t- (void) .cxx_destruct; (0x104a946b0) \t\t- (void) setName:(id)arg1; (0x104a945ac) \t\t- (float) height; (0x104a94620) \t\t- (void) setHeight:(float)arg1; (0x104a9463c) \t\t- (int) age; (0x104a945e0) \t\t- (void) setAge:(int)arg1; (0x104a945fc) in NSObject: \tClass Methods: ... \tProperties: ... \tInstance Methods: ...`
-
④ -[UIView _autolayoutTrace]
用于打印包含指定视图的窗口中的整个视图的层级结构(简要)
cy# [#0x137f26d40 _autolayoutTrace].toString() ` \u2022UIWindow:0x137d06b30 | \u2022UIView:0x137f26d40 | | *<UILayoutGuide: 0x283d315e0 - "UIViewLayoutMarginsGuide", layoutFrame = {{16, 44}, {343, 734}}, owningView = <UIView: 0x137f26d40; frame = (0 0; 375 812); autoresize = W+H; layer = <CALayer: 0x2804097a0>>> | | *UILabel:0x137f264d0'Airths LionHeart' | | *UILabel:0x137f26eb0'You are the best!!!' | | *UITextView:0x138868800'Current Version:/opt/Mon...' | | | _UITextLayoutView:0x137f20e10 | | | _UITextContainerView:0x137f1fea0 | | | | _UITextViewCanvasView:0x137f202c0 | | | _UIScrollViewScrollIndicator:0x139d0bd50 | | | | UIView:0x139d0bef0 | | | _UIScrollViewScrollIndicator:0x139d0c060 | | | | UIView:0x139d0c200 | | *UIButton:0x137f1ca00'ShowChangeLog' | | | UIButtonLabel:0x139d0c870'ShowChangeLog' | | *_UILayoutGuide:0x137f27130 | | *_UILayoutGuide:0x137f274c0 Legend: \t* - is laid out with auto layout \t+ - is laid out manually, but is represented in the layout engine because translatesAutoresizingMaskIntoConstraints = YES \t\u2022 - layout engine host`
-
⑤ -[UIView recursiveDescription]
用于打印包含指定视图的窗口中的整个视图的层级结构(详细)
cy# [#0x137f26d40 recursiveDescription].toString() `<UIView: 0x137f26d40; frame = (0 0; 375 812); autoresize = W+H; layer = <CALayer: 0x2804097a0>> | <UILabel: 0x137f264d0; frame = (102.5 69; 170 40); text = 'Airths LionHeart'; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x28271f4d0>> | <UILabel: 0x137f26eb0; frame = (97.5 134; 180 40); text = 'You are the best!!!'; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x28271ce10>> | <UITextView: 0x138868800; frame = (26 254; 343 514); text = 'Current Version:/opt/Mon...'; clipsToBounds = YES; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x280a592f0>; layer = <CALayer: 0x28040d6a0>; contentOffset: {0, 0}; contentSize: {343, 50}; adjustedContentInset: {0, 0, 0, 0}> | | <_UITextLayoutView: 0x137f20e10; frame = (0 0; 0 0); layer = <CALayer: 0x28040f440>> | | <<_UITextContainerView: 0x137f1fea0; frame = (0 0; 343 50); layer = <CALayer: 0x28040f2e0>> minSize = {0, 0}, maxSize = {1.7976931348623157e+308, 1.7976931348623157e+308}, textContainer = <NSTextContainer: 0x283530dc0 size = (343.000000,inf); widthTracksTextView = YES; heightTracksTextView = NO>; exclusionPaths = 0x1cdc13c98; lineBreakMode = 0> | | | <_UITextViewCanvasView: 0x137f202c0; frame = (0 0; 343 50); userInteractionEnabled = NO; layer = <_UITextTiledLayer: 0x2836387e0>> | | <_UIScrollViewScrollIndicator: 0x139d0bd50; frame = (3 508; 337 3); alpha = 0; autoresize = TM; layer = <CALayer: 0x2804653a0>> | | | <UIView: 0x139d0bef0; frame = (0 0; 337 3); layer = <CALayer: 0x2804653e0>> | | <_UIScrollViewScrollIndicator: 0x139d0c060; frame = (337 383; 3 41); alpha = 0; autoresize = LM; layer = <CALayer: 0x280465420>> | | | <UIView: 0x139d0c200; frame = (0 0; 3 41); layer = <CALayer: 0x280465440>> | <UIButton: 0x137f1ca00; frame = (127.5 199; 120 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x28040d5a0>> | | <UIButtonLabel: 0x139d0c870; frame = (1.5 6; 117 18); text = 'ShowChangeLog'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x2827050e0>> | <_UILayoutGuide: 0x137f27130; frame = (0 0; 0 44); hidden = YES; layer = <CALayer: 0x280409860>> | <_UILayoutGuide: 0x137f274c0; frame = (0 778; 0 34); hidden = YES; layer = <CALayer: 0x280409940>>`
-
⑥ -[UIViewController _printHierarchy]
用于打印包含指定控制器的窗口中的整个控制器的层级结构
cy# [#0x10510fcf0 _printHierarchy].toString() "<CustomViewController 0x10510fcf0>, state: appeared, view: <UIView 0x10521e860>"
补充:其他细节
-
Cycript 在打印控制器或者视图时,会将汉字转换为 Unicode 编码
如下所示的界面:
通过 Cycript 打印视图的层级结构:~ > cycript -r 192.168.1.229:8899 cy# UIApp.keyWindow.recursiveDescription().toString() `<UIWindow: 0x102e0c270; frame = (0 0; 414 896); gestureRecognizers = <NSArray: 0x281ba4b10>; layer = <UIWindowLayer: 0x2815fde60>> | <UITransitionView: 0x102e12ab0; frame = (0 0; 414 896); autoresize = W+H; layer = <CALayer: 0x2815e5de0>> | | <UIDropShadowView: 0x102f08600; frame = (0 0; 414 896); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x2815e0d20>> | | | <UIView: 0x102e10d10; frame = (0 0; 414 896); autoresize = W+H; layer = <CALayer: 0x2815e6900>> | | | | <UIButton: 0x102e11080; frame = (115 106; 184 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x2815e6a40>> | | | | | <UIButtonLabel: 0x102f1e6f0; frame = (0 6; 184 18); text = '\u8fd9\u662f\u4e00\u4e2a\u53ea\u5305\u542b\u4e2d\u6587\u7684\u6309\u94ae'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x2836eb2a0>> | | | | | | <_UILabelContentLayer: 0x2815fbf00> (layer) | | | | <_UILayoutGuide: 0x102e11b30; frame = (0 0; 0 44); hidden = YES; layer = <CALayer: 0x2815e67a0>> | | | | <_UILayoutGuide: 0x102e120c0; frame = (0 862; 0 34); hidden = YES; layer = <CALayer: 0x2815e66a0>>`
Cycript 在打印视图的层级结构时,会将遇到的所有中文转换成对应的 Unicode 编码
如果需要通过界面上控件中的文本快速地定位指定的控件
则需要将文本中的中文转换成对应的 Unicode 编码后,再在输出结果中进行搜索,例如中文(
这是一个只包含中文的按钮
): Unicode 编码 (\u8fd9\u662f\u4e00\u4e2a\u53ea\u5305\u542b\u4e2d\u6587\u7684\u6309\u94ae
) -
Cycript 的两种使用方式
- 在非越狱环境中,通过将
Cycript.framework
集成到特定的 App,以在该 App 中使用 Cycript - 在越狱环境中,通过
MobileSubstrate
加载并注入所有的 App 中,以在所有 App 中使用 Cycript
- 在非越狱环境中,通过将