Cycript(五):安装与使用

2024-02-28 20:10
文章标签 安装 使用 cycript

本文主要是介绍Cycript(五):安装与使用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

    • Cycript 的安装
    • 在非越狱环境中使用 Cycript(通过手动配置)
    • 在非越狱环境中使用 Cycript(通过 MonkeyDev 自动配置)
    • 在越狱环境中使用 Cycript
    • 导入外部的 Cycript 脚本
    • 补充:Cycript 和 LLDB 中经常用到的 Objective-C 私有方法
    • 补充:其他细节

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
    └── cycript15 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.dylibReferenced from: /opt/cycript_0.9.594/./Cycript.lib/cycript-aplReason: 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:0x137f274c0Legend:
    \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

这篇关于Cycript(五):安装与使用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/756494

相关文章

Python 安装和配置flask, flask_cors的图文教程

《Python安装和配置flask,flask_cors的图文教程》:本文主要介绍Python安装和配置flask,flask_cors的图文教程,本文通过图文并茂的形式给大家介绍的非常详细,... 目录一.python安装:二,配置环境变量,三:检查Python安装和环境变量,四:安装flask和flas

使用Python自建轻量级的HTTP调试工具

《使用Python自建轻量级的HTTP调试工具》这篇文章主要为大家详细介绍了如何使用Python自建一个轻量级的HTTP调试工具,文中的示例代码讲解详细,感兴趣的小伙伴可以参考一下... 目录一、为什么需要自建工具二、核心功能设计三、技术选型四、分步实现五、进阶优化技巧六、使用示例七、性能对比八、扩展方向建

使用Python实现一键隐藏屏幕并锁定输入

《使用Python实现一键隐藏屏幕并锁定输入》本文主要介绍了使用Python编写一个一键隐藏屏幕并锁定输入的黑科技程序,能够在指定热键触发后立即遮挡屏幕,并禁止一切键盘鼠标输入,这样就再也不用担心自己... 目录1. 概述2. 功能亮点3.代码实现4.使用方法5. 展示效果6. 代码优化与拓展7. 总结1.

使用Python开发一个简单的本地图片服务器

《使用Python开发一个简单的本地图片服务器》本文介绍了如何结合wxPython构建的图形用户界面GUI和Python内建的Web服务器功能,在本地网络中搭建一个私人的,即开即用的网页相册,文中的示... 目录项目目标核心技术栈代码深度解析完整代码工作流程主要功能与优势潜在改进与思考运行结果总结你是否曾经

Linux中的计划任务(crontab)使用方式

《Linux中的计划任务(crontab)使用方式》:本文主要介绍Linux中的计划任务(crontab)使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、前言1、linux的起源与发展2、什么是计划任务(crontab)二、crontab基础1、cro

kotlin中const 和val的区别及使用场景分析

《kotlin中const和val的区别及使用场景分析》在Kotlin中,const和val都是用来声明常量的,但它们的使用场景和功能有所不同,下面给大家介绍kotlin中const和val的区别,... 目录kotlin中const 和val的区别1. val:2. const:二 代码示例1 Java

C++变换迭代器使用方法小结

《C++变换迭代器使用方法小结》本文主要介绍了C++变换迭代器使用方法小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1、源码2、代码解析代码解析:transform_iterator1. transform_iterat

Win11安装PostgreSQL数据库的两种方式详细步骤

《Win11安装PostgreSQL数据库的两种方式详细步骤》PostgreSQL是备受业界青睐的关系型数据库,尤其是在地理空间和移动领域,:本文主要介绍Win11安装PostgreSQL数据库的... 目录一、exe文件安装 (推荐)下载安装包1. 选择操作系统2. 跳转到EDB(PostgreSQL 的

C++中std::distance使用方法示例

《C++中std::distance使用方法示例》std::distance是C++标准库中的一个函数,用于计算两个迭代器之间的距离,本文主要介绍了C++中std::distance使用方法示例,具... 目录语法使用方式解释示例输出:其他说明:总结std::distance&n编程bsp;是 C++ 标准

vue使用docxtemplater导出word

《vue使用docxtemplater导出word》docxtemplater是一种邮件合并工具,以编程方式使用并处理条件、循环,并且可以扩展以插入任何内容,下面我们来看看如何使用docxtempl... 目录docxtemplatervue使用docxtemplater导出word安装常用语法 封装导出方