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

相关文章

使用DeepSeek API 结合VSCode提升开发效率

《使用DeepSeekAPI结合VSCode提升开发效率》:本文主要介绍DeepSeekAPI与VisualStudioCode(VSCode)结合使用,以提升软件开发效率,具有一定的参考价值... 目录引言准备工作安装必要的 VSCode 扩展配置 DeepSeek API1. 创建 API 请求文件2.

使用TomCat,service输出台出现乱码的解决

《使用TomCat,service输出台出现乱码的解决》本文介绍了解决Tomcat服务输出台中文乱码问题的两种方法,第一种方法是修改`logging.properties`文件中的`prefix`和`... 目录使用TomCat,service输出台出现乱码问题1解决方案问题2解决方案总结使用TomCat,

解决IDEA使用springBoot创建项目,lombok标注实体类后编译无报错,但是运行时报错问题

《解决IDEA使用springBoot创建项目,lombok标注实体类后编译无报错,但是运行时报错问题》文章详细描述了在使用lombok的@Data注解标注实体类时遇到编译无误但运行时报错的问题,分析... 目录问题分析问题解决方案步骤一步骤二步骤三总结问题使用lombok注解@Data标注实体类,编译时

Java中使用Java Mail实现邮件服务功能示例

《Java中使用JavaMail实现邮件服务功能示例》:本文主要介绍Java中使用JavaMail实现邮件服务功能的相关资料,文章还提供了一个发送邮件的示例代码,包括创建参数类、邮件类和执行结... 目录前言一、历史背景二编程、pom依赖三、API说明(一)Session (会话)(二)Message编程客

C++中使用vector存储并遍历数据的基本步骤

《C++中使用vector存储并遍历数据的基本步骤》C++标准模板库(STL)提供了多种容器类型,包括顺序容器、关联容器、无序关联容器和容器适配器,每种容器都有其特定的用途和特性,:本文主要介绍C... 目录(1)容器及简要描述‌php顺序容器‌‌关联容器‌‌无序关联容器‌(基于哈希表):‌容器适配器‌:(

使用Python实现高效的端口扫描器

《使用Python实现高效的端口扫描器》在网络安全领域,端口扫描是一项基本而重要的技能,通过端口扫描,可以发现目标主机上开放的服务和端口,这对于安全评估、渗透测试等有着不可忽视的作用,本文将介绍如何使... 目录1. 端口扫描的基本原理2. 使用python实现端口扫描2.1 安装必要的库2.2 编写端口扫

使用Python实现操作mongodb详解

《使用Python实现操作mongodb详解》这篇文章主要为大家详细介绍了使用Python实现操作mongodb的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、示例二、常用指令三、遇到的问题一、示例from pymongo import MongoClientf

SQL Server使用SELECT INTO实现表备份的代码示例

《SQLServer使用SELECTINTO实现表备份的代码示例》在数据库管理过程中,有时我们需要对表进行备份,以防数据丢失或修改错误,在SQLServer中,可以使用SELECTINT... 在数据库管理过程中,有时我们需要对表进行备份,以防数据丢失或修改错误。在 SQL Server 中,可以使用 SE

使用Python合并 Excel单元格指定行列或单元格范围

《使用Python合并Excel单元格指定行列或单元格范围》合并Excel单元格是Excel数据处理和表格设计中的一项常用操作,本文将介绍如何通过Python合并Excel中的指定行列或单... 目录python Excel库安装Python合并Excel 中的指定行Python合并Excel 中的指定列P

浅析Rust多线程中如何安全的使用变量

《浅析Rust多线程中如何安全的使用变量》这篇文章主要为大家详细介绍了Rust如何在线程的闭包中安全的使用变量,包括共享变量和修改变量,文中的示例代码讲解详细,有需要的小伙伴可以参考下... 目录1. 向线程传递变量2. 多线程共享变量引用3. 多线程中修改变量4. 总结在Rust语言中,一个既引人入胜又可