FSA全栈行动 FSA全栈行动
首页
  • 移动端文章

    • Android
    • iOS
    • Flutter
  • 学习笔记

    • 《Kotlin快速入门进阶》笔记
    • 《Flutter从入门到实战》笔记
    • 《Flutter复习》笔记
前端
后端
  • 学习笔记

    • 《深入浅出设计模式Java版》笔记
  • 逆向
  • 分类
  • 标签
  • 归档
  • LinXunFeng
  • GitLqr

公众号:FSA全栈行动

记录学习过程中的知识
首页
  • 移动端文章

    • Android
    • iOS
    • Flutter
  • 学习笔记

    • 《Kotlin快速入门进阶》笔记
    • 《Flutter从入门到实战》笔记
    • 《Flutter复习》笔记
前端
后端
  • 学习笔记

    • 《深入浅出设计模式Java版》笔记
  • 逆向
  • 分类
  • 标签
  • 归档
  • LinXunFeng
  • GitLqr
  • AndroidUI

  • Android第三方SDK

  • Android混淆

  • Android仓库

  • Android新闻

  • Android系统开发

  • Android源码

  • Android注解AOP

  • Android脚本

  • AndroidTv开发

  • AndroidNDK

  • Android音视频

  • Android热修复

  • Android性能优化

  • Android云游戏

  • Android插件化

  • iOSUI

  • iOS工具

  • iOS底层原理与应用

  • iOS组件化

  • iOS音视频

  • iOS疑难杂症

  • iOS之Swift

  • iOS之RxSwift

  • iOS开源项目

  • iOS逆向

    • Mac远程登录到iOS设备
    • Mach-O文件结构分析
    • iOS逆向 - 应用脱壳
    • iOS逆向 - 运行时分析(一)class-dump
    • iOS逆向 - 运行时分析(二)Cycript
      • 一、Cycript
      • 二、安装与简单使用
      • 三、实战
      • 四、高级用法
        • 1、choose - 查找对象
        • 2、分类拓展
        • 3、封装函数
    • iOS逆向 - 运行时分析(三)Frida
  • Flutter开发

  • 移动端
  • iOS逆向
LinXunFeng
2022-03-19
目录

iOS逆向 - 运行时分析(二)Cycript

欢迎关注微信公众号:[FSA全栈行动 👋]

# 一、Cycript

Cycript 是一款混合了 OC 和 JS 语法解释器的脚本语言(即可以使用 OC 和 JS 的语法来写脚本),其主要用来注入运行时程序进行调试,在程序重启后,所有通过 Cycript 进行的程序修改内容都会失效,对原生程序和代码毫无副作用。

官网地址:http://www.cycript.org (opens new window)

# 二、安装与简单使用

在 Cydia 中搜索 Cycript,找到安装即可。

在 iOS 设备上输入 cycript,会显示 cy# 提示符

lxf-iPad:~ root# cycript
cy#

这是一个 JS 控制台,输入的内容都由 JS 内核运行。

cy# var name = 'lxf'
"lxf"
cy# print(name)
lxf

常用快捷键

快捷键 作用
ctrl + c 取消当前输入或中断执行中的命令
ctrl + d 退出 Cycript
ctrl + l clear 清屏

使用 -p 参数注入指定程序,前提是你的程序得在运行中。

下面展示一下注入 Twitter 后,查看其沙盒路径

lxf-iPad:~ root# cycript -p Twitter
cy# NSHomeDirectory()
@"/var/mobile/Containers/Data/Application/2FA6975C-9DB0-4B08-9FE9-365473E86748"
cy#

# 三、实战

目标:如上图所示,我们来将 linxunfeng.top 改成 fullstackaction.com

要修改它就得先找到它,可以使用 recursiveDescription 函数可以递归打印任意视图的层次结构

[[UIApp keyWindow] recursiveDescription].toString()

如果我们不清楚完整的函数名时,可以使用敲击 tab 键列出所有匹配项

# cy# [[UIApp keyWindow] recursive【敲击tab键】

cy# [[UIApp keyWindow] recursive
recursiveDescription             recursivelyForceDisplayIfNeeded

看着上面图中打印出来的内容,任谁看了都觉得很烦恼,这里可以使用简化版的方法 _autolayoutTrace

[[UIApp keyWindow] _autolayoutTrace].toString()

相比上面的 recursiveDescription 来对,会清晰很多

往下翻,可以找到该段落

\u2022T1ProfileHeaderViewContro...:0x1164bac50
   UIView:0x1164bae30
   |   T1ProfileUserInfoViewCont...:0x117748050
   |   ProfileHeaderBio:0x116498070
   |   T1TranslateButton:0x116474cc0
   |   ProfileHeaderTranslatedBi...:0x11773ed70
   |   ProfileTranslationActivit...:0x11773f170
   |   UIImageView:0x11773f3f0
   |   ProfileHeaderLocation:0x11641f090
   |   UIButtonLabel:0x11641f3b0
   |   ProfileHeaderWebSite:0x113526840
   |   |   UIImageView:0x1164e3b60
   |   |   UIButtonLabel:0x11355bbe0'linxunfeng.top'
   |   ProfileHeaderBirthday:0x11355bee0
   |   |   UIButtonLabel:0x11355c200
   |   ProfileHeaderCreatedDate:0x11355c500
   |   |   UIImageView:0x1164e4100
   |   |   UIButtonLabel:0x11355c820'2017\u5e748\u6708 \u52a0\u5165'
   |   TFNAttributedTextView:0x11641f6b0
   |   |   T1AccessibilityProxyView:0x11641af00
   T1ProfileFriendsFollowing...:0x1164dc870
   |   TFNSolidColorView:0x1164dca50
   |   T1FlexibleLayoutView:0x1135df410
   |   |   TFNAttributedTextView:0x1135df810
   |   |   |   T1AccessibilityProxyView:0x1164e06a0
   |   |   T1UserFacepileView:0x1164df730
   |   |   TFNAttributedTextView:0x1164dfb50
   |   T1ProfileFriendsFollowingHighlightView:0x1164dff50

这里说明一下,Cycript 对中文的显示不太友好

可以将上面的输出内容拷贝到一些 Unicode转中文 的工具里,直接转换输出

•T1ProfileHeaderViewContro...:0x1164bac50
   UIView:0x1164bae30
   |   T1ProfileUserInfoViewCont...:0x117748050
   |   ProfileHeaderBio:0x116498070
   |   T1TranslateButton:0x116474cc0
   |   ProfileHeaderTranslatedBi...:0x11773ed70
   |   ProfileTranslationActivit...:0x11773f170
   |   UIImageView:0x11773f3f0
   |   ProfileHeaderLocation:0x11641f090
   |   UIButtonLabel:0x11641f3b0
   |   ProfileHeaderWebSite:0x113526840
   |   |   UIImageView:0x1164e3b60
   |   |   UIButtonLabel:0x11355bbe0'linxunfeng.top'
   |   ProfileHeaderBirthday:0x11355bee0
   |   |   UIButtonLabel:0x11355c200
   |   ProfileHeaderCreatedDate:0x11355c500
   |   |   UIImageView:0x1164e4100
   |   |   UIButtonLabel:0x11355c820'2017年8月 加入'
   |   TFNAttributedTextView:0x11641f6b0
   |   |   T1AccessibilityProxyView:0x11641af00
   T1ProfileFriendsFollowing...:0x1164dc870
   |   TFNSolidColorView:0x1164dca50
   |   T1FlexibleLayoutView:0x1135df410
   |   |   TFNAttributedTextView:0x1135df810
   |   |   |   T1AccessibilityProxyView:0x1164e06a0
   |   |   T1UserFacepileView:0x1164df730
   |   |   TFNAttributedTextView:0x1164dfb50
   |   T1ProfileFriendsFollowingHighlightView:0x1164dff50

如果内容较少,如

UIButtonLabel:0x11355c820'2017\u5e748\u6708 \u52a0\u5165'

可以使用 echo -e 进行转换输出

echo -e '2017\u5e748\u6708 \u52a0\u5165'
2017年8月 加入

回到正题,我们已经找到了目的控件,接下来就是修改它

UIButtonLabel:0x11355bbe0'linxunfeng.top'

在 Cycript 中,如果我们知道一个对象的内存地址,就可以通过 # 操作符来获取该对象。

不过它的类型是:UIButtonLabel,我们可以看看它的父控件

   |   ProfileHeaderWebSite:0x113526840
   |   |   UIImageView:0x1164e3b60
   |   |   UIButtonLabel:0x11355bbe0'linxunfeng.top'

打印 ProfileHeaderWebSite:0x113526840 这个对象

cy# #0x113526840
#"<UIButton: 0x113526840; frame = (20 12.186; 123 18); clipsToBounds = YES; opaque = NO; gestureRecognizers = <NSArray: 0x282f56df0>; layer = <CALayer: 0x2822821c0>>"

可以看到是个 UIButton,那怎么改标题不是很明白了吗?

[#0x113526840 setTitle:@"fullstackaction.com" forState:UIControlStateNormal]

但是并没有任何效果~,没事,那就换设置富文本试试

var siteAttr = [[NSAttributedString alloc] initWithString:@"fullstackaction.com" attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:10],NSForegroundColorAttributeName:[UIColor blueColor]}];

[#0x113526840 setAttributedTitle:siteAttr forState:UIControlStateNormal]

大功告成😃

# 四、高级用法

# 1、choose - 查找对象

用法:choose(类名)

有时候上面提到的 recursiveDescription 和 _autolayoutTrace,对我们来说输出内容太多了,且我们知道目标对象的类名,此时就可以通过 choose 这个函数,获取符合条件(当前类及子类)的所有记录,从而快速拿到对象的地址来操作对象

cy# choose(UILabel)
[#"<T1AccessibleBadgesLabel: 0x11773a1d0; baseClass = UILabel; frame = (265.5 0; 100.24 20.2871); text = '\xe2\x81\xa8LinXunFeng\xe2\x81\xa9'; clipsToBounds = YES; alpha = 0; gestureRecognizers = <NSArray: 0x282f254d0>; layer = <_UILabelLayer: 0x2803585a0>>",#"<T1ScrollingHorizontalLabelCellLabel: 0x117740a70; baseClass = UILabel; frame = (4 0; 35 20.5); text = '\xe5\xaa\x92\xe4\xbd\x93'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x28035a8a0>>",#"<T1ScrollingHorizontalLabelCellLabel: 0x117742180; baseClass = UILabel; frame = (4 0; 87 20.5); text = '\xe6\x8e\xa8\xe6\x96\x87\xe5\x92\x8c\xe5\x9b\x9e\xe5\xa4\x8d'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x28035ad50>>",#"<T1ScrollingHorizontalLabelCellLabel: 0x117743890; baseClass = UILabel; frame = (4 0; 35 20.5); text = '\xe6\x8e\xa8\xe6\x96\x87'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x28035b110>>",#"<T1ScrollingHorizontalLabelCellLabel: 0x116438ef0; baseClass = UILabel; frame = (4 0; 35 20.5); text = '\xe5\x96\x9c\xe6\xac\xa2'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x28033e580>>",#"<T1AccessibleBadgesLabel: 0x116484790; baseClass = UILabel; frame = (0 0; 139.113 28.6406); text = '\xe2\x81\xa8LinXunFeng\xe2\x81\xa9'; clipsToBounds = YES; userInteractionEnabled = NO; gestureRecognizers = <NSArray: 0x282f27f60>; layer = <_UILabelLayer: 0x28035d9f0>>"]

# 2、分类拓展

在 Cycript 中也可以使用 OC 的 Category,为类拓展方法,如

cy# @implementation NSString (LXF)
    + website { return "fullstackaction.com"; }
    @end
cy# [NSString website]
@"fullstackaction.com"

# 3、封装函数

在实战过程中,往往会遇到一些层次很深的情况,如果靠我们自己手动去循环的查找,会变成非常麻烦,如查找当前最顶层的控制器,这时,我们可以利用 Cycript 来编写自定义函数解决这个问题

cy# function topVc(vc) {
        if (vc.presentedViewController) {
              return _topVc(vc.presentedViewController);
        } else if ([vc isKindOfClass:[UITabBarController class]]) {
                return _topVc(vc.selectedViewController);
        } else if ([vc isKindOfClass:[UINavigationController class]]) {
                return _topVc(vc.visibleViewController);
        } else {
              var count = vc.childViewControllers.count;
              for (var i = count - 1; i >= 0; i--) {
                    var childVc = vc.childViewControllers[i];
                    if (childVc && childVc.view.window) {
                          vc = _topVc(childVc);
                          break;
                    }
              }
              return vc;
        }
    }

打印最顶层的控制器

cy# topVc(UIApp.keyWindow.rootViewController)
#"<T1TabBarViewController: 0x107864270>"

每次都在终端里写一次,那岂不是也非常麻烦?我们可以将这些代码写到一个 .cy 文件中

我们可以在 /usr/lib/cycript0.9/com/saurik/substrate/ 找到一个名为 MS.cy 的文件

(function(exports) {

exports.getImageByName = MSGetImageByName;
exports.findSymbol = MSFindSymbol;

exports.hookFunction = function(func, hook, old) {
    ...
};

exports.hookMessage = function(isa, sel, imp, old) {
    ...
};

})(exports);

参考它写一个即可。附上自己封装的 cy 文件:https://github.com/LinXunFeng/lxf_cycript (opens new window)

下载下来后,将 lxf.cy 拷贝至 /usr/lib/cycript0.9/com/lxf/ 路径下

然后在 cycript 环境下执行:

cy# @import com.lxf.lxf; 0

注:分号后面的0 是为了隐藏脚本导入后的脚本内容输出

导入后,使用 lxf.函数名() 即可

cy# lxf.topVc()
#"<T1TabBarViewController: 0x107864270>"

如果不太记得函数名字,可以使用 tab键 查看

cy# lxf.【敲击tab键】
__defineGetter__      __lookupSetter__      appPath               classMethods          findVc                isString              keyWindow             methods               rootVc                toLocaleString        valueOf
__defineSetter__      __proto__             cachesPath            constructor           hasOwnProperty        ivarNames             loadFramework         printIvars            subViews              toString
__lookupGetter__      appId                 classMethodNames      documentPath          isPrototypeOf         ivars                 methodNames           propertyIsEnumerable  subViewsSimple        topVc
cy# lxf.
#iOS#逆向
iOS逆向 - 运行时分析(一)class-dump
iOS逆向 - 运行时分析(三)Frida

← iOS逆向 - 运行时分析(一)class-dump iOS逆向 - 运行时分析(三)Frida→

最近更新
01
Flutter - Xcode16 还原编译速度
04-05
02
AI - 免费的 Cursor 平替方案
03-30
03
Android - 2025年安卓真的闭源了吗
03-28
更多文章>
Theme by Vdoing | Copyright © 2020-2025 FSA全栈行动
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×