Cycript(五):安装与使用

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

    1. 禁用系统完整性保护(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
      
    2. 创建符号链接,将对 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
      
    3. 启用系统完整性保护(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
    1

  • ② 将 /opt/cycript_0.9.594/Cycript.ios/Cycript.framework 集成到 CycriptDemo 工程中
    2.1
    因为 Cycript.framework 不包含 Bitcode
    所以需要将 Target - Build Settings - Build Options - Enable Bitcode 设置为 NO
    2.2

  • ③ 因为 Cycript.framework 依赖:JavaScriptCore.frameworklibsqlite3.tbdlibc++.tbd
    所以需要在 Target - Build Phases - Link Binary With Libraries 中添加对这三个库的引用
    3

  • ④ 因为 Cycript.framework 在启动时,会去主 Bundle 中读取它自己的数据库 libcycript.db
    所以需要在 Target - Build Phases - Copy Bundle Resources 中添加对 /opt/cycript_0.9.594/Cycript.lib/libcycript.db 的引用
    4

  • ⑤ 在 ViewController.m 中编写如下代码
    5

  • ⑥ 将 MacBook 和 iPhone 连接到同一 WiFi 下,并查看 iPhone 的 IP 地址
    6

  • ⑦ 将工程 CycriptDemo 运行到真机
    7

  • ⑧ 在 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"
    

    8

  • 如果终端界面一直停留在执行连接命令 cycript -r 192.168.1.229:8899 这一步,没有进入到 Cycript 的 REPL 环境,则有以下三种可能:

    1. MacBook 和 iPhone 没有连接到同一 WiFi 下(请将 MacBook 和 iPhone 连接到同一 WiFi 下)
    2. MacBook 和 iPhone 连接的 WiFi 有问题(换一个 WiFi 试试;或直接使用 iPhone 开个热点,然后 MacBook 连接到 iPhone 发出的热点)
    3. 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
    1

  • ② 将 MacBook 和 iPhone 连接到同一 WiFi 下,并查看 iPhone 的 IP 地址
    2

  • ③ 将工程运行到真机
    3

  • ④ 在 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"
    

    4

  • 如果终端界面一直停留在执行连接命令 cycript -r 192.168.1.229:6666 这一步,没有进入到 Cycript 的 REPL 环境,则有以下三种可能:

    1. MacBook 和 iPhone 没有连接到同一 WiFi 下(请将 MacBook 和 iPhone 连接到同一 WiFi 下)
    2. MacBook 和 iPhone 连接的 WiFi 有问题(换一个 WiFi 试试;或直接使用 iPhone 开个热点,然后 MacBook 连接到 iPhone 发出的热点)
    3. 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 中创建对这些外部脚本的引用:
    1
    ② 当需要使用外部 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 脚本,比如通过如下的配置:
    1
    LoadAtLaunch:表示是否在启动的时候默认加载脚本,默认加载的脚本就不用再 @import xxx 导入,可以直接使用。如果不是默认加载的脚本就需要 @import xxx 导入,xxx 就是图中的 key,比如 @import nslog@import ms@import hook@import md

    priority:表示加载的优先级,数字越小优先级越高,比如某些脚本需要依赖其它脚本就需要调整优先级,让被依赖的脚本先加载

    contenturl:脚本可以直接写到 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 中:
    2
    当需要使用外部 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 脚本的引用删除
    3

  • 在越狱环境中导入外部的 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 编码

    如下所示的界面:
    1
    通过 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 的两种使用方式

    1. 在非越狱环境中,通过将 Cycript.framework 集成到特定的 App,以在该 App 中使用 Cycript
    2. 在越狱环境中,通过 MobileSubstrate 加载并注入所有的 App 中,以在所有 App 中使用 Cycript
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值