17 - Cycript

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é)果如下:
IMG_0104.jpg
  • 修改控件屬性

假設(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文件

image.png

  • 查看所有視圖
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文件,即可使用封裝的功能。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容