1、Cycript

Cycript 是Objective-C++、ES6(JavaScript)、Java等语法的混合物(可以认为是一门编程语言)。它可以用来探索、修改、调试正在运行的Mac/iOS App,它有详细的 官方文档
要在iPhone上调试运行中的App需要先通过Cydia安装Cycript:
image.png

2、Cycript的开启和关闭

2.1、开启Cycript

先SSH登录到iPhone,进入Cycript环境输入命令:$ cycript
调试某个程序命令:
$ cycript -p 进程ID
$ cycript -p 进程名称

2.2、ps命令

要使用ps指令需要先通过Cydia安装adv-cmds:
image.png
ps命令是process status的缩写,使用ps命令可以列出系统当前进程,列出所有的进程:
$ ps -A
在列出所有的进程中找到喜马拉雅App的进程:

  1. ...
  2. 8447 ?? 0:00.00 /Applications/Spotlight.app/Spotlight
  3. 8815 ?? 0:00.00 /Applications/ScreenshotServicesService.app/ScreenshotServicesService
  4. 8816 ?? 0:00.00 /var/containers/Bundle/Application/C6BB0254-C0D5-47F3-BAE5-93708FE7A570/ting.app/ting
  5. 8819 ?? 0:00.00 /System/Library/Frameworks/WebKit.framework/XPCServices/com.apple.WebKit.Networking.xpc/com.apple.WebKit.Networking
  6. 8820 ?? 0:00.00 /System/Library/PrivateFrameworks/AccessibilityUI.framework/XPCServices/com.apple.accessibility.AccessibilityUIServer.x
  7. 8821 ?? 0:00.00 sshd: root@ttys i
  8. ...

它的进程ID是8816,进程名称是ting,命令行输入 $ cycript -p ting 就可以开调试喜马拉雅App了。
也可以通过关键词进行搜索:
$ ps -A | grep 关键词
例如筛选出所有正在运行的App,可以输入命令:$ ps -A | grep Application

  1. 8447 ?? 0:00.00 /Applications/Spotlight.app/Spotlight
  2. 8815 ?? 0:00.00 /Applications/ScreenshotServicesService.app/ScreenshotServicesService
  3. 8816 ?? 0:00.00 /var/containers/Bundle/Application/C6BB0254-C0D5-47F3-BAE5-93708FE7A570/ting.app/ting
  4. 9200 ?? 0:00.00 /Applications/Preferences.app/Preferences
  5. 9219 ?? 0:00.00 /var/containers/Bundle/Application/95A14D66-57E1-4235-99CE-4CF3011AE89A/com_kwai_gif.app/com_kwai_gif
  6. 9225 ?? 0:00.00 /var/containers/Bundle/Application/718B7570-37AB-413D-88F9-0E6568258B03/News.app/News
  7. 9241 ?? 0:00.00 /Applications/AppStore.app/AppStore
  8. 9246 ttys000 0:00.00 grep Application

2.3、关闭Cycript

快捷键 Control + D,退出Cycript环境。

3、常用语法

3.1、获取对象

例如获取UIApplication的单利:cy# [UIApplication sharedApplication]cy# UIApp

  1. #"<XMUIApplication: 0x15fd09090>"

获取根控制器:cy# UIApp.keyWindow.rootViewController

  1. #"<XMMainTabBarController: 0x16117b000>"

3.2、定义变量

使用关键字var定义变量,cy# var 变量名 = 变量值,例如:
cy# var window = UIApp.keyWindow

  1. #"<UIWindow: 0x160911fc0; frame = (0 0; 375 667); autoresize = W+H; gestureRecognizers = <NSArray: 0x282b32820>; layer = <UIWindowLayer: 0x28251ba20>>"

3.3、用内存地址获取对象

通过 井号 + 内存地址 也可以获取对象,比如上一步获取的keyWindow的地址是0x160911fc0,命令行输入:
cy# #0x160911fc0.rootViewController

  1. #"<XMMainTabBarController: 0x16117b000>"

3.4、查看已加载的所有OC类

cy# ObjectiveC.classes

3.5、查看对象的所有成员变量

通过 星号 + 对象 可以查看所有成员变量,例如:
cy# *UIApp

  1. {isa:XMUIApplication,_responderFlags:@error,_delegate:#"<XMAppDelegate: 0x283874700>",_remoteControlEventObservers:1,_topLevelNibObjects:null,_networkResourcesCurrentlyLoadingCount:0,_hideNetworkActivityIndicatorTimer:null,_editAlertController:null,_statusBar:null,_statusBarRequestedStyle:0,_statusBarWindow:null,_observerBlocks:@[],_postCommitActions:@[],_postCommitActionsNeedToSynchronize:false,_mainStoryboardName:null,_idleModeController:null,_displayLayoutMonitor:null,_systemUserInterfaceStyle:0,_eventFetcher:#"<UIEventFetcher: 0x283d72a00>",_eventDispatcher:#"<UIEventDispatcher: 0x280f55380>",
  2. ...

3.6、递归打印View的所有子控件

使用recursiveDescription方法打印所有子控件
例如打印根视图控制器的view的所有子控件:
cy# UIApp.keyWindow.rootViewController.view.recursiveDescription().toString()

3.7、筛选出某种类型的对象

使用choose筛选某种类型的对象,例如在上一步的输出结果中筛选UITableViewCell类型的控件:
cy# choose(UITableViewCell)

  1. [#"<XMRecFlowActivityCell: 0x15698c030; baseClass = UITableViewCell; frame = (0 0; 375 132); autoresize = W; layer = <CALayer: 0x2818e6fc0>>",#"<XMRecFlowNewUserRankCell: 0x15617ca00; baseClass = UITableViewCell; frame = (0 292; 375 331); autoresize = W; layer = <CALayer: 0x281f07d20>>",#"<XMRecFlowSquareCell: 0x15618e600; baseClass = UITableViewCell; frame = (0 132; 375 160); autoresize = W; layer = <CALayer: 0x2818e6080>>"]

筛选UIViewController可以输入:
cy# choose(UIViewController)

4、封装Cycript

我们可以将常用的Cycript代码封装在一个.cy文件中,创建一个test.cy文件并添加加法和减法两个函数:

  1. (function(exports){
  2. exports.sum = function(a, b) {
  3. return a + b;
  4. };
  5. exports.minus = function(a, b) {
  6. return a - b;
  7. };
  8. })(exports);

*exports参数名固定,用于向外提供接口

将test.cy文件复制到iPhone的 /usr/lib/cycript0.9/ 路径下,可以通过iFunBox直接拖拽,或者命令行输入:
$ scp -P 10010 ~/Desktop/test.cy root@localhost:/usr/lib/cycript0.9
在cycript环境下导入文件:
cy# @import test

  1. {sum:function (t,e){return t+e},minus:function (t,e){return t-e}}

调用函数:cy# test.sum(10, 20)

  1. 30

除了添简单的函数,还可以使用iOS的API获取更多的信息:

  1. (function(exports){
  2. exports.bundleId = [NSBundle mainBundle].bundleIdentifier;
  3. exports.rootVC = function() {
  4. return UIApp.keyWindow.rootViewController;
  5. };
  6. // 不使用exports代表是全局函数,调用时不需要使用文件名
  7. XLDocPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
  8. })(exports);

在cycript环境下获取bundleId:cy# test.bundleId

  1. @"com.gemd.iting"

获取根视图控制器:cy# test.rootVC()

  1. #"<XMMainTabBarController: 0x13206ba00>"

获取沙盒路径:cy# XLDocPath

  1. @"/var/mobile/Containers/Data/Application/281F97D1-448D-449E-B283-CDACA2FD3532/Documents"

获取沙盒路径后就可以查看喜马拉雅App存储的文件,可以进一步分析。

5、mjcript

mjcript 基于Cycript实现了一些实用的函数,其中一些常用功能:
获取App唯一标识:cy# MJAppId
获取沙盒路径:cy# MJDocPath
获取缓存路径:cy# MJCachesPath
获取App安装包路径:cy# MJAppPath
获取KeyWindow:cy# MJKeyWin()
获取根控制器:cy# MJRootVc()
找到显示在最前面的控制器:cy# MJFrontVc()
加载系统动态库:cy# MJLoadFramework(‘MapKit’)
递归打印controller的层级结构:cy# MJChildVcs(MJRootVc())
递归打印view的层级结构:cy# MJSubviews(#0x10f8d0c00.view)
打印所有的对象方法名字:cy# MJInstanceMethodNames(#0x10f8d0c00)
打印所有的成员变量名字:cy# MJIvarNames(#0x10f8d0c00)
打印所有子类:cy# MJSubclasses(UIViewController)
正则表达式:cy# MJInstanceMethodNames(#0x10f8d0c00, /click/)

6、练习

找到“关于喜马拉雅”这个界面,然后更改一下它的版本号的内容和颜色:
image.png
1、利用choose方法筛选一下UILabel
#cy choose(UILabel)
2、搜索9.0.31,找到版本号对应的UILabel

  1. #"<UITableViewLabel: 0x120eb0d90; frame = (313.5 15; 45.5 20.5); text = '9.0.31'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x28302a800>>"

3、修改版本号内容
#cy #0x120eb0d90.text = “10000.0.31”
image.png
4、修改文字颜色
#cy #0x120eb0d90.textColor = [UIColor redColor]
image.png

这里都是在内存中修改,当界面退出重新进入就会恢复成原来的样子