1、Cycript
Cycript 是Objective-C++、ES6(JavaScript)、Java等语法的混合物(可以认为是一门编程语言)。它可以用来探索、修改、调试正在运行的Mac/iOS App,它有详细的 官方文档。
要在iPhone上调试运行中的App需要先通过Cydia安装Cycript:
2、Cycript的开启和关闭
2.1、开启Cycript
先SSH登录到iPhone,进入Cycript环境输入命令:$ cycript
调试某个程序命令:
$ cycript -p 进程ID
$ cycript -p 进程名称
2.2、ps命令
要使用ps指令需要先通过Cydia安装adv-cmds:
ps命令是process status的缩写,使用ps命令可以列出系统当前进程,列出所有的进程:
$ ps -A
在列出所有的进程中找到喜马拉雅App的进程:
...
8447 ?? 0:00.00 /Applications/Spotlight.app/Spotlight
8815 ?? 0:00.00 /Applications/ScreenshotServicesService.app/ScreenshotServicesService
8816 ?? 0:00.00 /var/containers/Bundle/Application/C6BB0254-C0D5-47F3-BAE5-93708FE7A570/ting.app/ting
8819 ?? 0:00.00 /System/Library/Frameworks/WebKit.framework/XPCServices/com.apple.WebKit.Networking.xpc/com.apple.WebKit.Networking
8820 ?? 0:00.00 /System/Library/PrivateFrameworks/AccessibilityUI.framework/XPCServices/com.apple.accessibility.AccessibilityUIServer.x
8821 ?? 0:00.00 sshd: root@ttys i
...
它的进程ID是8816,进程名称是ting,命令行输入 $ cycript -p ting 就可以开调试喜马拉雅App了。
也可以通过关键词进行搜索:
$ ps -A | grep 关键词
例如筛选出所有正在运行的App,可以输入命令:$ ps -A | grep Application
8447 ?? 0:00.00 /Applications/Spotlight.app/Spotlight
8815 ?? 0:00.00 /Applications/ScreenshotServicesService.app/ScreenshotServicesService
8816 ?? 0:00.00 /var/containers/Bundle/Application/C6BB0254-C0D5-47F3-BAE5-93708FE7A570/ting.app/ting
9200 ?? 0:00.00 /Applications/Preferences.app/Preferences
9219 ?? 0:00.00 /var/containers/Bundle/Application/95A14D66-57E1-4235-99CE-4CF3011AE89A/com_kwai_gif.app/com_kwai_gif
9225 ?? 0:00.00 /var/containers/Bundle/Application/718B7570-37AB-413D-88F9-0E6568258B03/News.app/News
9241 ?? 0:00.00 /Applications/AppStore.app/AppStore
9246 ttys000 0:00.00 grep Application
2.3、关闭Cycript
3、常用语法
3.1、获取对象
例如获取UIApplication的单利:cy# [UIApplication sharedApplication] 或 cy# UIApp
#"<XMUIApplication: 0x15fd09090>"
获取根控制器:cy# UIApp.keyWindow.rootViewController
#"<XMMainTabBarController: 0x16117b000>"
3.2、定义变量
使用关键字var定义变量,cy# var 变量名 = 变量值,例如:
cy# var window = UIApp.keyWindow
#"<UIWindow: 0x160911fc0; frame = (0 0; 375 667); autoresize = W+H; gestureRecognizers = <NSArray: 0x282b32820>; layer = <UIWindowLayer: 0x28251ba20>>"
3.3、用内存地址获取对象
通过 井号 + 内存地址 也可以获取对象,比如上一步获取的keyWindow的地址是0x160911fc0,命令行输入:
cy# #0x160911fc0.rootViewController
#"<XMMainTabBarController: 0x16117b000>"
3.4、查看已加载的所有OC类
3.5、查看对象的所有成员变量
通过 星号 + 对象 可以查看所有成员变量,例如:
cy# *UIApp
{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>",
...
3.6、递归打印View的所有子控件
使用recursiveDescription方法打印所有子控件
例如打印根视图控制器的view的所有子控件:
cy# UIApp.keyWindow.rootViewController.view.recursiveDescription().toString()
3.7、筛选出某种类型的对象
使用choose筛选某种类型的对象,例如在上一步的输出结果中筛选UITableViewCell类型的控件:
cy# choose(UITableViewCell)
[#"<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文件并添加加法和减法两个函数:
(function(exports){
exports.sum = function(a, b) {
return a + b;
};
exports.minus = function(a, b) {
return a - b;
};
})(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
{sum:function (t,e){return t+e},minus:function (t,e){return t-e}}
调用函数:cy# test.sum(10, 20)
30
除了添简单的函数,还可以使用iOS的API获取更多的信息:
(function(exports){
exports.bundleId = [NSBundle mainBundle].bundleIdentifier;
exports.rootVC = function() {
return UIApp.keyWindow.rootViewController;
};
// 不使用exports代表是全局函数,调用时不需要使用文件名
XLDocPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
})(exports);
在cycript环境下获取bundleId:cy# test.bundleId
@"com.gemd.iting"
获取根视图控制器:cy# test.rootVC()
#"<XMMainTabBarController: 0x13206ba00>"
获取沙盒路径:cy# XLDocPath
@"/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、练习
找到“关于喜马拉雅”这个界面,然后更改一下它的版本号的内容和颜色:
1、利用choose方法筛选一下UILabel
#cy choose(UILabel)
2、搜索9.0.31,找到版本号对应的UILabel
#"<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”
4、修改文字颜色
#cy #0x120eb0d90.textColor = [UIColor redColor]
这里都是在内存中修改,当界面退出重新进入就会恢复成原来的样子