Cycript
是由Cydia创始人Saurik推出的一款脚本语言,Cycript混合了OC、JavaScript
语法的解释器,这意味着我们能够在一个命令中使用OC或者JavaScript,甚至两者并用。它能够挂钩正在运行的进程,能够在运行时修改很多东西。
安装
- 下载
Cycript
,官方文档 - 下载完成之后,为了方便使用,可以将其拷贝至/opt目录下。
cp ./cycript_0.9.594 /opt/cycript_0.9.594
- 配置环境变量,在
~/.bash_profile
或者~/.zshrc
文件中添加以下配置
#Cycript配置
export CYCRIPT_PATH=/opt/cycript_0.9.594
使用
附加进程
Monkey
中集成了Cycript
,使用MonkeyDev
重签名应用,会自动注入libcycript.dylib
相关文件。
当设备启动注入了Cycript
的目标应用,应用进程会调用Cycript
的方法,打开端口供第三方监听,如下:
CYListenServer(6666);
当设备启用监听时,第三方可以通过端口附加进程,进入cy环境,从而查看当前进程中的内存数据。假设端口IP为:172.20.10.14,则终端
附加方式如下:
cycript -r 172.20.10.14:6666
cy#
注意:进程运行的设备
和附加进程的终端
必须在同一网络环境
。
常用指令
- 获取
keyWindow
cy# UIWindow.keyWindow()
#"<iConsoleWindow: 0x14ea6fee0; baseClass = UIWindow; frame = (0 0; 375 812); gestureRecognizers = <NSArray: 0x282279260>; layer = <UIWindowLayer: 0x282db4080>>"
- 获取
UIApplication
cy# UIApp
#"<UIApplication: 0x14ea28e00>"
- 定义
变量并赋值
cy# var keyWd = UIWindow.keyWindow()
#"<iConsoleWindow: 0x14ea6fee0; baseClass = UIWindow; frame = (0 0; 375 812); gestureRecognizers = <NSArray: 0x282279260>; layer = <UIWindowLayer: 0x282db4080>>"
cy# keyWd.rootViewController
#"<MMUINavigationController: 0x14f11f800> ChildViewControllers:(\n \"<WCAccountLoginFirstViewController: 0x14f0fa600>\"\n)"
cy#
注意:当程序的进程结束了,定义的所有变量也会释放掉。
- 获取
对象
//通过(#对象地址)获取对象
cy# #0x14f11f800
#"<MMUINavigationController: 0x14f11f800> ChildViewControllers:(\n \"<WCAccountLoginFirstViewController: 0x14f0fa600>\"\n)"
//通过(*定义的变量)获取对象
cy# *keyWd
{isa:iConsoleWindow,_responderFlags:@error,_constraintsExceptingSubviewAutoresizingConstraints:null...
查看当前视图结构
cy# keyWd.recursiveDescription()
@"<iConsoleWindow: 0x14ea6fee0; baseClass = UIWindow; frame = (0 0; 375 812); gestureRecognizers = <NSArray: 0x282279260>; layer = <UIWindowLayer: 0x282db4080>>\n | <UITransitionView: 0x14ea65650; frame = (0 0; 375 812);...
cy# keyWd.recursiveDescription().toString()
`<iConsoleWindow: 0x14ea6fee0; baseClass = UIWindow; frame = (0 0; 375 812); gestureRecognizers = <NSArray: 0x282279260>; layer = <UIWindowLayer: 0x282db4080>>
| <UITransitionView: 0x14ea65650; frame = (0 0; 375 812); autoresize = W+H; layer = <CALayer: 0x282db5460>>
| | <UIDropShadowView: 0x14ea74650; frame = (0 0; 375 812); autoresize = W+H; layer = <CALayer: 0x282db5580>>
| | | <UILayoutContainerView: 0x14ea2e280; frame = (0 0; 375 812); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x28227a040>; layer = <CALayer: 0x282db47c0>>
| | | | <UINavigationTransitionView: 0x14ea724f0; frame = (0 0; 375 812); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x282db4800>>
查询当前进程中该类型的对象
cy# choose(UIButton)
[#"<FixTitleColorButton: 0x14ef05d40; baseClass = UIButton; frame = (20 18; 157.5 47); clipsToBounds = YES; opaque = NO; autoresize = RM; layer = <CALayer: 0x282d3d200>>",#"<FixTitleColorButton: 0x14ea2cfb0; baseClass = UIButton; frame = (197.5 18; 157.5 47); clipsToBounds = YES; opaque = NO; autoresize = LM; layer = <CALayer: 0x282dead20>>"]
-
修改内存中的数据
修改当前应用图标的通知气泡数目为999,假设当前使用Monkey安装并运行微信(WeChat8.0.2.ipa)
。
cy# [UIApp setApplicationBadgeString:@"999"]
修改结果如下:修改控件属性
假设当前处于微信的登陆界面,打印keyWindow
下所有视图
UIWindow.keyWindow().recursiveDescription().toString()
从当前界面中可以看到,有个+86
的view,因此可以直接在打印的所有视图中查找+86
所在的控件
<WCUITextField: 0x116387c00; baseClass = UITextField; frame = (20 0; 73 44); text = '+86'; opaque = NO; autoresize = W+H; tintColor = UIExtendedSRGBColorSpace 0.027451 0.756863 0.376471 1; gestureRecognizers = <NSArray: 0x2817946f0>; borderStyle = None; background = <_UITextFieldNoBackgroundProvider: 0x2819310a0: textfield=<WCUITextField 0x116387c00>>; layer = <CALayer: 0x281b76700>>
修改WCUITextField文本框的text属性,即:修改该控件的显示,将+86
修改成+95
#0x116387c00.text = @"+95"
扩展指令
MonkeyDev
对一些常用方法进行了封装,提供给开发者使用,封装方法的实现在Mokey
项目的Config
目录下,找到MDConfig.plist
文件
- 查看所有视图
cy# pviews()
`<iConsoleWindow: 0x115c4f020; baseClass = UIWindow; frame = (0 0; 375 812); gestureRecognizers = <NSArray: 0x283ab8720>; layer = <UIWindowLayer: 0x283517b80>>
| <UITransitionView: 0x115866000; frame = (0 0; 375 812); autoresize = W+H; layer = <CALayer: 0x283517da0>>
| | <UIDropShadowView: 0x115851d10; frame = (0 0; 375 812); autoresize = W+H; layer = <CALayer: 0x283517900>>
pviews
等价如下指令:
cy# pviews
function (){return UIApp.keyWindow.recursiveDescription().toString()}
获取当前控制器
cy# pvcs()
"<MMUINavigationController 0x116068000>, state: appeared, view: <UILayoutContainerView 0x115876ee0>\n | <WCAccountLoginFirstViewController 0x1160c2a00>, state: appeared, view: <UIView 0x1158941a0>"
pvcs
等价如下指令
cy# pvcs
function (){return UIWindow.keyWindow().rootViewController._printHierarchy().toString()}
cy文件
Cycript
是一门脚本语言,它可以加载封装好的\*.cy
文件,因此,我们可以将常用的Cycript
功能封装至\*.cy
文件,在调试的时候可以直接使用\*.cy
文件中封装的指令。
在以上的扩展示例中,MonkeyDev也对常用的指令进行了封装。接下来我们来尝试封装自己的\*.cy文件
。
示例1
- 创建
test.cy
文件 - 将
test.cy
文件添加至MonkeyDemo
项目中。 -
test.cy
文件中先添加简单的方法
sum = function(a,b){
return a + b;
}
- 在
MokeyDemo
项目中,使用Copy Files
添加test.cy
,注意:test.cy
是脚本文件,不是MachO,因此不需要勾选签名。
- 运行
MokeyDemo
项目 - 在终端对
MokeyDemo
项目进行附加
cycript -r 172.20.10.14:6666
- 导入
test.cy
脚本
@import test
- 调用sum方法
sum(10,20)
30
示例2
- 创建
test.cy
文件 - 将
test.cy
文件添加至MonkeyDemo
项目中。 -
test.cy
文件中先添加简单的方法
(function(exports){
APPID = [NSBundle mainBundle].bundleIdentifier,
APPPATH = [NSBundle mainBundle].bundlePath,
APPHOME = NSHomeDirectory(),
rootVC = function(){
return UIApp.keyWindow.rootViewController;
};
keyWindow = function(){
return UIApp.keyWindow;
};
getCurrentVC = function(rootVC){
var currentVC;
if([rootVC presentedViewController]){
rootVC = [rootVC presentedViewController];
}
if([rootVC isKindOfClass:[UITabBarController class]]){
currentVC = getCurrentVC(rootVC.selectedViewController);
}
else if([rootVC isKindOfClass:[UINavigationController class]]){
currentVC = getCurrentVC(rootVC.visibleViewController);
}
else{
currentVC = rootVC;
}
return currentVC;
};
currentVC = function(){
return getCurrentVC(rootVC());
};
})(exports);
- 在
MokeyDemo
项目中,使用Copy Files
添加test.cy
,注意:test.cy
是脚本文件,不是MachO,因此不需要勾选签名。
- 运行
MokeyDemo
项目 - 在终端对
MokeyDemo
项目进行附加
cycript -r 172.20.10.14:6666
- 导入
test.cy
脚本
@import test
- 获取
APPID
APPID
@"com.hq.MokeyDemo"
- 获取
APPPATH
APPPATH
@"/private/var/containers/Bundle/Application/D620C178-5030-48E4-9276-981150FF7299/MokeyDemo.app"
- 获取
APPHOME
APPHOME
@"/var/mobile/Containers/Data/Application/C2ED1E99-47C4-4C29-8AE6-9C5C136CEE04"
- 调用
currentVC
方法
currentVC()
#"<WCAccountLoginFirstViewController: 0x14b0b4200>"
总结
Cycipt
是一种脚本语言,混合了多种语法(混合多种语法的解释器),所以可以兼容。Cycipt
可以附加到进程,用来动态调试。可以将常用的功能封装成
\*.cy文件
,当附加进程后,导致\*.cy文件
,即可使用封装的功能。