简介
Cycript是由Cydia创始人Saurik推出的一款脚本语言,Cydia的主要目的是为越狱的iOS用户提供的一个装包工具。据说Cydia之父Saurik,曾经也是给iPhone写程序的,后来他写了一个视频拍摄软件而苹果没允许上架,他一气之下就开了Cydia软件商店,专门收留不被苹果通过的软件。
Cycript混合了OC和JavaScript语法的解释器,这意味着我们能够在一个命令中使用OC或者JavaScript,甚至两者并用。它能够Hook(挂钩)正在运行的进程,能够在运行时修改很多东西。
功能特点
- 能够Hook正在运行的进程,并且找出正被使用的类信息,例如view controllers和第三方库,甚至程序的delegate(委托)的名称。
- 对于一个特定的类,能够得到所有被使用的方法名称。例如View Controller, App delegate或者任何其他的类。
- 能够得到所有实例变量的名称和在程序运行的任意时刻实例变量的值。
- 能够在运行时修改实例变量的值。
- 能够执行Method Swizzling(方法混合)例如替换一个特定方法的实现。
- 可以在运行时调用任意方法,即使这个方法目前并不在应用的实际代码当中。
下载安装
使用越狱的设备打开Cydia然后搜索Cycript安装即可。本文使用的是iPhone6/iOS11.4.1

Cycript调试
注入进程
- 一般都是使用 cycript来进行一些代码的测试,所以需要先把cycript注入到目标应用的进程中,通过如下方式注入:
- 首先远程SSH连接设备,然后搜索需要注入进程的进程号。本文以微信作为案例,首先打开微信,然后搜索微信进程id。
| 12
 3
 
 | klygteki-iPhone:~ root# ps aux | grep "WeChat"root            9166   7.8  0.2  1595184   1720 s000  S+   10:46AM   0:00.03 grep WeChat
 mobile          8328   1.2  3.7  1963744  37232   ??  Ss   Wed07PM   0:13.33 /var/containers/Bundle/Application/D24DBE0F-73A9-4F6E-B763-37114DE691B0/WeChat.app/WeChat
 
 | 
- 根据进程id来注入到微信,注入成功之后会看到命令界面出现cy#表示注入成功,接下来可以输入cycript命令。
| 12
 
 | klygteki-iPhone:~ root# cycript -p 8328cy#
 
 | 
- 如果需要退出命令编辑模式按Ctrl+D即可退出。
常用命令
| 功能 | 命令 | 
| 获取应用单例 | UIApp或者[uiapplication sharedApplication] | 
| 创建警告弹窗 | [UIAlertView alloc] | 
| 获取应用程序目录的路径 | NSHomeDirectory() | 
| 字符串格式化 | .toString() | 
| 打印视图层次 | UIApp.keyWindow.recursiveDescription().toString() | 
| 根据地址获取对象 | #内存地址 | 
| 打印当前页面view层级 | UIApp.keyWindow.recursiveDescription().toString() | 
| 获取下一个响应者 | [#内存地址 nextResponder] | 
| 查找指定类型 | choose(UILabel) 如: choose(UIViewController) | 
| 获取指定对象的所有属性 | [#内存地址 _ivarDescription].toString() | 
| 定义变量 | var 变量名 = 变量值  var keyWindow = UIApp.keyWindow | 
| 查看架构层级 | [[UIApp keyWindow] _autolayoutTrace].toString() | 
| 查看对象的所有成员变量 | var keyWindow = UIWindow.keyWindow()   *keyWindow | 
更多命令使用文档:http://www.cycript.org/manual/
命令实践
界面弹窗设置
我们想在程序界面显示弹窗,内容提示Hello word 则可以输入如下OC命令。
| 12
 3
 
 | cy# var alert = [[UIAlertView alloc] initWithTitle:@"hello word!" message:nil delegate:nil cancelButtonTitle:@"ok" otherButtonTitles:nil]#"<UIAlertView: 0x1183f92c0; frame = (0 0; 0 0); layer = <CALayer: 0x1133e1d30>>"
 cy# [alert show]
 
 | 
执行命令后我们在设备上可以看到该弹窗,如下图所示:

获取应用沙盒路径
在iOS脱壳过程中必须要获取到应用沙盒路径才可以拿到应用源文件,使用命令[NSHomeDirectory() stringByAppendingString:@"/Documents"] 即可以获取沙盒路径。
| 12
 3
 
 | klygteki-iPhone:~ root# cycript -p WeChat                                        cy# [NSHomeDirectory() stringByAppendingString:@"/Documents"]
 @"/var/mobile/Containers/Data/Application/AC83D268-2FE9-434E-8D76-0935AACB2AC3/Documents"
 
 | 
设置应用未读消息
如果我们想设置微信的未读消息,将应用切换后台回到桌面,我们可以输入名下命令:
| 1
 | cy# UIApp.applicationIconBadgeNumber=20
 | 
说明:在Cycript中UIApp  和 [UIApplication sharedApplication] 等效。执行命令之后我们可以看到微信图标显示了未读消息数是20

打印视图层次
使用命令UIApp.keyWindow.recursiveDescription().toString()可以打印应用当前页面的UI视图层次。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 
 | cy#   UIApp.keyWindow.recursiveDescription().toString()`<iConsoleWindow: 0x10ddd2a70; baseClass = UIWindow; frame = (0 0; 375 667); gestureRecognizers = <NSArray: 0x10dd87230>; layer = <UIWindowLayer: 0x10ddac910>>
 | <UITransitionView: 0x118856a60; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x113310820>>
 |    | <UILayoutContainerView: 0x11884ae80; frame = (0 0; 375 667); autoresize = W+H; gestureRecognizers = <NSArray: 0x11876f810>; layer = <CALayer: 0x1183e0760>>
 |    |    | <UINavigationTransitionView: 0x11876dd90; frame = (0 0; 375 667); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x1187647d0>>
 |    |    |    | <UIViewControllerWrapperView: 0x118836f00; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x118836ee0>>
 |    |    |    |    | <UIView: 0x11884d760; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x112d1dd50>>
 |    |    |    |    |    | <WCTableView: 0x10e35cc00; baseClass = UITableView; frame = (0 0; 375 667); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x1183e7ec0>; layer = <CALayer: 0x11338cdf0>; contentOffset: {0, -64}; contentSize: {375, 393}; adjustedContentInset: {64, 0, 0, 0}>
 |    |    |    |    |    |    | <UIView: 0x11882d280; frame = (0 234; 375 159); layer = <CALayer: 0x11882d1e0>>
 |    |    |    |    |    |    |    | <UIView: 0x1183facd0; frame = (0 0; 375 159); autoresize = LM+RM; layer = <CALayer: 0x1183eed00>>
 |    |    |    |    |    |    |    |    | <UIButton: 0x1183efd30; frame = (20 30; 181 22); opaque = NO; autoresize = LM; layer = <CALayer: 0x1183faec0>>
 |    |    |    |    |    |    |    |    |    | <UIButtonLabel: 0x1183f0050; frame = (0 1.5; 181 19.5); text = '\u7528\u5fae\u4fe1\u53f7/QQ\u53f7/\u90ae\u7bb1\u767b\u5f55'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x1183fb000>>
 |    |    |    |    |    |    |    |    |    |    | <_UILabelContentLayer: 0x11889df40> (layer)
 |    |    |    |    |    |    |    |    | <FixTitleColorButton: 0x118829e40; baseClass = UIButton; frame = (20 112; 335 47); clipsToBounds = YES; opaque = NO; autoresize = W; layer = <CALayer: 0x1183f03a0>>
 |    |    |    |    |    |    |    |    |    | <UIImageView: 0x11889d700; frame = (0 0; 335 47); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x11889d930>>
 |    |    |    |    |    |    |    |    |    | <UIButtonLabel: 0x11882cb60; frame = (140 13; 55.5 21.5); text = '\u4e0b\u4e00\u6b65'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x112d1aac0>>
 |    |    |    |    |    |    |    |    |    |    | <_UILabelContentLayer: 0x11882d120> (layer)
 |    |    |    |    |    |    | <UIView: 0x118837570; frame = (0 0; 375 234); layer = <CALayer: 0x118837760>>
 |    |    |    |    |    |    |    | <UIView: 0x1188371c0; frame = (0 0; 375 234); autoresize = LM+RM; layer = <CALayer: 0x1188373b0>>
 |    |    |    |    |    |    |    |    | <UIView: 0x118776220; frame = (0 0; 375 234); layer = <CALayer: 0x11324ffa0>>
 |    |    |    |    |    |    |    |    |    | <CTRichTextView: 0x11884f950; baseClass = UILabel; frame = (20 73; 335 33); opaque = NO; layer = <_UILabelLayer: 0x11884ff00>>
 |    |    |    |    |    |    |    |    |    | <UIButton: 0x11877ef40; frame = (0 146; 375 44); opaque = NO; layer = <CALayer: 0x1187754d0>>
 |    |    |    |    |    |    |    |    |    |    | <UIView: 0x11886b880; frame = (0 0; 375 44); userInteractionEnabled = NO; layer = <CALayer: 0x11886ba70>>
 |    |    |    |    |    |    |    |    |    |    |    | <WCUITextField: 0x10e382800; baseClass = UITextField; frame = (20 0; 335 44); text = '\u4e4d\u5f97'; opaque = NO; autoresize = W+H; tintColor = UIExtendedSRGBColorSpace 0.00784314 0.733333 0 1; gestureRecognizers = <NSArray: 0x118788ee0>; layer = <CALayer: 0x11886ba90>>
 |    |    |    |    |    |    |    |    |    |    |    |    | <UIView: 0x112d3a090; frame = (0 0; 103 44); layer = <CALayer: 0x1183cd3c0>>
 |    |    |    |    |    |    |    |    |    |    |    |    |    | <MMUILabel: 0x1183ccf70; baseClass = UILabel; frame = (0 0; 93 44); text = '\u56fd\u5bb6/\u5730\u533a'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x11886db90>>
 内容较多省略部分内容
 
 | 
参考资料