75、iOS逆向(四):Cycript/FLEXible使用、运行时分析

好,咱们继续聊iOS逆向。前面几章我们把工具链搭起来了,也看了Mach-O和Objective-C的底层结构。今天要聊的,是真正让我觉得「逆向有意思」的部分——运行时分析

说白了,就是App跑起来之后,我们怎么动态地看它、改它、甚至玩它。我个人觉得,动态分析比静态分析爽多了。静态分析就像看一张地图,动态分析就像亲自开车上路。你想想看,哪个更刺激?

Cycript:一个让你和App「对话」的工具

Cycript,这名字你可能听过。它本质上是JavaScript + Objective-C的混合体。你可以把它理解成一个REPL(交互式解释器),挂到正在运行的App进程上,然后直接敲代码。

我记得第一次用Cycript的时候,感觉就像开了上帝视角。你敲一句 UIApp.keyWindow.recursiveDescription(),整个界面层级就全出来了。这在调试UI布局时简直是神器。

安装与注入

Cycript的安装其实很简单。你从官网下载或者用Homebrew装都行。但关键一步是注入到目标进程。

# 找到进程PID
ps aux | grep YourApp

# 注入
cycript -p PID

嗯,这里要注意:越狱设备上才能直接注入。非越狱环境需要配合Frida或者自己写dylib注入。我在项目中遇到过非越狱机调试的情况,那会儿折腾了好几天,最后还是用Frida的spawn方式搞定的。

常用操作速查

一旦进入Cycript的交互环境,你就可以开始玩了。我列几个最常用的操作:

操作 代码示例 说明
查看当前控制器 UIApp.keyWindow.rootViewController 拿到最顶层的VC
递归打印视图 UIApp.keyWindow.recursiveDescription() 所有子视图层级
查找某个类的实例 choose(NSClassFromString("YourClass")) 返回所有存活实例
调用任意方法 [instance someMethod] 直接OC语法调用
修改属性值 instance.property = newValue 运行时改值

我个人最常用的是 choose() 函数。为什么?因为很多时候你根本不知道某个对象存在哪里,但你知道它的类名。比如你想找一个叫 LoginManager 的单例,直接 choose(LoginManager) 就出来了。省去了你翻遍整个Mach-O找引用的时间。

小技巧: 如果你不确定类名是否准确,可以用 ObjectiveC.classes 列出所有已加载的类。然后配合 grep 过滤。比如 Object.keys(ObjectiveC.classes).filter(function(k) { return k.indexOf('Login') != -1 })

FLEXible:可视化调试的瑞士军刀

Cycript虽然强大,但毕竟是个命令行。有时候你更想「看」而不是「敲」。这时候FLEXible就登场了。

FLEXible是Flipboard开源的一个工具,后来被社区改造成了FLEX。它本质上是一个浮窗,点开之后能看到App运行时的所有信息:视图层级、网络请求、UserDefaults、文件系统、甚至Core Data数据库。

集成方式

在越狱设备上,你可以直接用Cydia装FLEXible。非越狱环境,我建议用CocoaPods集成到Debug配置里:

pod 'FLEX', :configurations => ['Debug']

然后在App启动时调用:

// AppDelegate.m
#ifdef DEBUG
#import <FLEX/FLEXManager.h>
#endif

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
#ifdef DEBUG
    [[FLEXManager sharedManager] showExplorer];
#endif
    return YES;
}

嗯,这里有个坑。我曾经在Release包忘了去掉这个宏,结果App上线后用户能看到调试浮窗……那场面,你懂的。所以一定要确认只在Debug模式下启用。

FLEXible能做什么?

点开FLEX浮窗,你会看到一个菜单。我挑几个最实用的功能说说:

  • View Hierarchy:3D视图层级。你可以旋转、缩放,看每个View的frame、约束、甚至背景色。比Cycript的recursiveDescription直观一百倍。
  • Network History:所有网络请求的列表。点进去能看到URL、Header、Body、响应时间。我在分析一个加密API时,就是靠这个功能抓到了未加密的请求参数。
  • User Defaults:直接编辑NSUserDefaults。改个开关值、调个服务器地址,不用重启App。
  • File Browser:浏览App沙盒里的所有文件。Documents、Library、tmp,随便看。
  • Heap Objects:列出所有存活的对象实例。和Cycript的 choose() 类似,但带内存地址和引用计数。
核心思路: Cycript适合「精准打击」——你知道要找什么,直接敲代码。FLEXible适合「地毯式搜索」——你不知道问题在哪,先翻一遍看看。

运行时分析:从「看」到「改」

工具只是手段。真正的核心是运行时分析的思路。我总结了一个三步法:

  1. 观察:用FLEXible看界面层级、网络请求、UserDefaults。找到可疑的类或方法。
  2. 验证:用Cycript注入,调用那个方法、查看返回值、修改属性。确认你的猜测。
  3. 利用:写一个Tweak或者Hook脚本,在App启动时自动修改行为。

举个例子。我曾经分析一个金融App的登录逻辑。先用FLEXible看到登录按钮的响应方法是 loginButtonTapped:。然后用Cycript注入:

// 找到当前控制器
var vc = UIApp.keyWindow.rootViewController;
// 调用登录方法,传入一个假的事件对象
[vc loginButtonTapped:nil];

结果发现它直接跳过了输入验证,弹出了登录请求。这说明验证逻辑不在这个方法里,而是在更早的输入框代理方法中。顺着这个思路,我找到了真正的验证代码。

为什么会这样?因为很多开发者习惯把UI事件和业务逻辑混在一起。你从UI入手,往往能发现他们「偷懒」留下的痕迹。

避坑指南

最后,说几个我踩过的坑:

警告:
  • Cycript注入后App崩溃:最常见的原因是调用了不存在的selector。记得先用 respondsToSelector: 检查一下。
  • FLEXible浮窗不显示:检查是否在非主线程调用了 showExplorer。UI操作必须在主线程。
  • 修改属性后界面不刷新:有些View不会自动重绘。手动调用 setNeedsLayoutsetNeedsDisplay 试试。
  • 非越狱设备无法注入:用Frida的 frida-trace 或者自己写dylib通过 DYLD_INSERT_LIBRARIES 注入。

我曾经在分析一个直播App时,用Cycript改了某个View的alpha值,结果画面直接卡死了。后来发现那个View的drawRect方法里做了大量计算,每次修改都会触发重绘。嗯,所以改之前最好先看看那个类的实现复杂度。

知识体系总览

这一章的内容,我画了一张图帮你理清思路:

运行时分析知识体系 Cycript FLEXible 运行时分析 choose() 方法调用 属性修改 类枚举 视图层级 网络请求 UserDefaults 文件浏览 观察 验证 利用 Hook 核心思路:从UI入手 → 找到可疑类/方法 → 动态验证 → 编写自动化脚本 Cycript适合精准打击,FLEXible适合地毯式搜索,两者互补 第75章 · iOS逆向(四)

这张图把三个工具的关系和各自的能力范围画清楚了。Cycript和FLEXible是「观察」和「验证」阶段的主力,而运行时分析是贯穿始终的方法论。

好了,这一章就到这里。工具只是手段,关键是思路。下次你拿到一个App,别急着反编译,先跑起来,用Cycript和FLEXible「摸一摸」它的脾气。你会发现,很多秘密就藏在运行时里。

一句话总结: 静态分析告诉你App「长什么样」,动态分析告诉你App「怎么动」。两者结合,才是完整的逆向视角。
公众号:蓝海资料掘金营,微信deep3321