Cycript是由Cydia創(chuàng)始人Saurik推出的一款腳本語言,Cycript混合了OC、JavaScript語法的解釋器,這意味著我們能夠在一個(gè)命令中使用OC或者JavaScript,甚至兩者并用。它能夠掛鉤正在運(yùn)行的進(jìn)程,能夠在運(yùn)行時(shí)修改很多東西。
安裝
- 下載
Cycript,官方文檔 - 下載完成之后,為了方便使用,可以將其拷貝至/opt目錄下。
cp ./cycript_0.9.594 /opt/cycript_0.9.594
- 配置環(huán)境變量,在
~/.bash_profile或者~/.zshrc文件中添加以下配置
#Cycript配置
export CYCRIPT_PATH=/opt/cycript_0.9.594
使用
附加進(jìn)程
Monkey中集成了Cycript,使用MonkeyDev重簽名應(yīng)用,會自動注入libcycript.dylib相關(guān)文件。
當(dāng)設(shè)備啟動注入了Cycript的目標(biāo)應(yīng)用,應(yīng)用進(jìn)程會調(diào)用Cycript的方法,打開端口供第三方監(jiān)聽,如下:
CYListenServer(6666);
當(dāng)設(shè)備啟用監(jiān)聽時(shí),第三方可以通過端口附加進(jìn)程,進(jìn)入cy環(huán)境,從而查看當(dāng)前進(jìn)程中的內(nèi)存數(shù)據(jù)。假設(shè)端口IP為:172.20.10.14,則終端附加方式如下:
cycript -r 172.20.10.14:6666
cy#
注意:進(jìn)程運(yùn)行的設(shè)備和附加進(jìn)程的終端必須在同一網(wǎng)絡(luò)環(huán)境。
常用指令
- 獲取
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#
注意:當(dāng)程序的進(jìn)程結(jié)束了,定義的所有變量也會釋放掉。
- 獲取
對象
//通過(#對象地址)獲取對象
cy# #0x14f11f800
#"<MMUINavigationController: 0x14f11f800> ChildViewControllers:(\n \"<WCAccountLoginFirstViewController: 0x14f0fa600>\"\n)"
//通過(*定義的變量)獲取對象
cy# *keyWd
{isa:iConsoleWindow,_responderFlags:@error,_constraintsExceptingSubviewAutoresizingConstraints:null...
查看當(dāng)前視圖結(jié)構(gòu)
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>>
查詢當(dāng)前進(jìn)程中該類型的對象
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>>"]
-
修改內(nèi)存中的數(shù)據(jù)
修改當(dāng)前應(yīng)用圖標(biāo)的通知?dú)馀輸?shù)目為999,假設(shè)當(dāng)前使用Monkey安裝并運(yùn)行微信(WeChat8.0.2.ipa)。
cy# [UIApp setApplicationBadgeString:@"999"]
修改結(jié)果如下:
修改控件屬性
假設(shè)當(dāng)前處于微信的登陸界面,打印keyWindow下所有視圖
UIWindow.keyWindow().recursiveDescription().toString()
從當(dāng)前界面中可以看到,有個(gè)+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"
擴(kuò)展指令
MonkeyDev對一些常用方法進(jìn)行了封裝,提供給開發(fā)者使用,封裝方法的實(shí)現(xiàn)在Mokey項(xiàng)目的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等價(jià)如下指令:
cy# pviews
function (){return UIApp.keyWindow.recursiveDescription().toString()}
獲取當(dāng)前控制器
cy# pvcs()
"<MMUINavigationController 0x116068000>, state: appeared, view: <UILayoutContainerView 0x115876ee0>\n | <WCAccountLoginFirstViewController 0x1160c2a00>, state: appeared, view: <UIView 0x1158941a0>"
pvcs等價(jià)如下指令
cy# pvcs
function (){return UIWindow.keyWindow().rootViewController._printHierarchy().toString()}
cy文件
Cycript是一門腳本語言,它可以加載封裝好的\*.cy文件,因此,我們可以將常用的Cycript功能封裝至\*.cy文件,在調(diào)試的時(shí)候可以直接使用\*.cy文件中封裝的指令。
在以上的擴(kuò)展示例中,MonkeyDev也對常用的指令進(jìn)行了封裝。接下來我們來嘗試封裝自己的\*.cy文件。
示例1
- 創(chuàng)建
test.cy文件 - 將
test.cy文件添加至MonkeyDemo項(xiàng)目中。 -
test.cy文件中先添加簡單的方法
sum = function(a,b){
return a + b;
}
- 在
MokeyDemo項(xiàng)目中,使用Copy Files添加test.cy,注意:test.cy是腳本文件,不是MachO,因此不需要勾選簽名。
image.png - 運(yùn)行
MokeyDemo項(xiàng)目 - 在終端對
MokeyDemo項(xiàng)目進(jìn)行附加
cycript -r 172.20.10.14:6666
- 導(dǎo)入
test.cy腳本
@import test
- 調(diào)用sum方法
sum(10,20)
30
示例2
- 創(chuàng)建
test.cy文件 - 將
test.cy文件添加至MonkeyDemo項(xiàng)目中。 -
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項(xiàng)目中,使用Copy Files添加test.cy,注意:test.cy是腳本文件,不是MachO,因此不需要勾選簽名。
image.png - 運(yùn)行
MokeyDemo項(xiàng)目 - 在終端對
MokeyDemo項(xiàng)目進(jìn)行附加
cycript -r 172.20.10.14:6666
- 導(dǎo)入
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"
- 調(diào)用
currentVC方法
currentVC()
#"<WCAccountLoginFirstViewController: 0x14b0b4200>"
總結(jié)
Cycipt是一種腳本語言,混合了多種語法(混合多種語法的解釋器),所以可以兼容。Cycipt可以附加到進(jìn)程,用來動態(tài)調(diào)試。可以將常用的功能封裝成
\*.cy文件,當(dāng)附加進(jìn)程后,導(dǎo)致\*.cy文件,即可使用封裝的功能。
