Cycript脚本语言使用与实战

一、Cycript的介绍与安装

Cycript是由Cydia创始人Saurik推出的一款脚本语言,Cycript 混合了Objective-C与javascript语法的解释器,这意味着我们能够在一个命令中用Objective-C或者javascript,甚至两者兼用。
它能够挂钩正在运行的进程,能够在运行时修改应用的很多东西。

Cycript的安装简单,可在越狱设备上从Cydia自带源Cydia/Telesphoreo下载,直接打开设备上的Cydia然后搜索Cycript后安装即可。

二、Cycript脚本命令的简单使用

1.new Instance根据地址获取对象,和直接使用#号获取对象

cy# var view=[[UIView alloc] init]
#"<UIView: 0x11fa8aec0; frame = (0 0; 0 0); layer = <CALayer: 0x280436980>>"
cy# view.tag = 5
5
cy# [#0x11fa8aec0 tag]
5
cy# var p = new Instance(0x11fa8aec0)
#"<UIView: 0x11fa8aec0; frame = (0 0; 0 0); tag = 5; layer = <CALayer: 0x280436980>>"
cy# p.tag
5

2.choose传递一个类,可以在内存中找出属于这个类的对象

cy# choose(XLFloatBtn)
[#"<XLFloatBtn: 0x110da03b0; baseClass = UIButton; frame = (374 428; 40 40); opaque = NO; layer = <CALayer: 0x280301c60>>"]

3.打印视图层次

UIApp.keyWindow.recursiveDescription().toString()
cy# UIApp.keyWindow.recursiveDescription().toString()
`<UIWindow: 0x10530f7d0; frame = (0 0; 414 896); opaque = NO; autoresize = W+H; gestureRecognizers = <NSArray: 0x280c20540>; layer = <UIWindowLayer: 0x280222d20>>
   | <UIImageView: 0x1053179a0; frame = (0 0; 414 896); autoresize = W+H; userInteractionEnabled = NO; layer = <CALayer: 0x280219540>>
   | <UITransitionView: 0x10530f190; frame = (0 0; 414 896); autoresize = W+H; layer = <CALayer: 0x28020cc20>>
   |    | <UIDropShadowView: 0x105318500; frame = (0 0; 414 896); autoresize = W+H; layer = <CALayer: 0x28020cb60>>
   |    |    | <CTStandaloneView: 0x105323c40; frame = (0 0; 414 896); autoresize = W+H; layer = <CALayer: 0x28020c500>>
   |    |    |    | <CTStageView: 0x105324360; frame = (0 0; 414 896); autoresize = W+H; layer = <CAEAGLLayer: 0x28020c680>>
   |    |    |    | <WKWebView: 0x105815000; frame = (9.5 -1000; 300 250); layer = <CALayer: 0x280230f40>>
   |    |    |    |    | <WKScrollView: 0x106116800; baseClass = UIScrollView; frame = (0 0; 300 250); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x280c7dc80>; layer = <CALayer: 0x280318c20>; contentOffset: {0, -48}; contentSize: {300, 202}; adjustedContentInset: {48, 0, 0, 0}>
   |    |    |    |    |    | <WKContentView: 0x10581a400; frame = (0 0; 300 202.041); transform = [0.30612245202064514, 0, 0, 0.30612245202064514, 0, 0]; anchorPoint = (0, 0); gestureRecognizers = <NSArray: 0x280c7eb50>; layer = <CALayer: 0x280231c00>>
   |    |    |    |    |    |    | <UIView: 0x10531ea20; frame = (0 0; 980 660); anchorPoint = (0, 0); clipsToBounds = YES; layer = <CALayer: 0x280318b60>>
   |    |    |    |    |    |    |    | <UIView: 0x10511b010; frame = (0 0; 980 660); autoresize = W+H; layer = <CALayer: 0x28031d260>>
   |    |    |    |    |    |    |    |    | <WKCompositingView: 0x110dd52f0; frame = (0 0; 980 660); layer = <CALayer: 0x280303920>> layerID = 1 "drawing area root"
   |    |    |    |    |    |    |    |    |    | <WKCompositingView: 0x110db7d20; frame = (0 0; 980 660); anchorPoint = (0, 0); clipsToBounds = YES; layer = <CALayer: 0x2803021e0>> layerID = 2 "content root"
   |    |    |    |    |    |    |    |    |    |    | <WKCompositingView: 0x110db8700; frame = (0 0; 980 660); layer = <CALayer: 0x2803038a0>> layerID = 5 "RenderView 0x104e85070"
   |    |    |    |    |    |    |    |    |    |    |    | <WKCompositingView: 0x110da27a0; frame = (0 0; 0 0); transform = [3.2666666507720947, 0, 0, 3.2666666507720947, 0, 0]; layer = <CALayer: 0x280303800>> layerID = 6 "TileGrid container"
   |    |    |    |    |    |    |    |    |    |    |    |    | <WKPlainRemoteLayer: 0x2803039c0> layerID = 8 "tile at 0,0" (layer)
   |    |    |    |    |    |    |    |    |    |    |    | <WKCompositingView: 0x110da39f0; frame = (0 0; 0 0); layer = <CALayer: 0x280303a60>> layerID = 7 "Page TiledBacking containment"
   |    |    |    |    |    |    |    |    |    |    | <WKCompositingView: 0x105314640; frame = (0 0; 0 0); layer = <CALayer: 0x280302060>> layerID = 3 "Document overlay Container"
   |    |    |    |    |    |    | <_UILayerHostView: 0x118cb7900; frame = (0 0; 0 0); layer = <CALayerHost: 0x28031bfa0>>
   |    |    |    |    |    | <UIView: 0x110da2d30; frame = (0 0; 0 0); anchorPoint = (0, 0); opaque = NO; layer = <CALayer: 0x2803196c0>>
   |    |    |    |    |    |    | <UITextSelectionView: 0x10511ef10; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <CALayer: 0x28031e820>>
   |    |    |    |    |    |    |    | <UIView: 0x10511f130; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <CALayer: 0x28031e840>>
   |    |    |    |    |    | <_UIScrollViewScrollIndicator: 0x110da4ed0; frame = (294 3; 3 196); alpha = 0; autoresize = LM; layer = <CALayer: 0x28031a520>>
   |    |    |    |    |    |    | <UIView: 0x110da5060; frame = (0 0; 3 196); layer = <CALayer: 0x28031a540>>
   |    |    |    |    |    | <_UIScrollViewScrollIndicator: 0x110da4bd0; frame = (3 196; 294 3); alpha = 0; autoresize = TM; layer = <CALayer: 0x28031a4c0>>
   |    |    |    |    |    |    | <UIView: 0x110da4d60; frame = (0 0; 294 3); layer = <CALayer: 0x28031a4e0>>
   | <XLFloatBtn: 0x110da03b0; baseClass = UIButton; frame = (374 428; 40 40); opaque = NO; layer = <CALayer: 0x280301c60>>
   |    | <UIButtonLabel: 0x110db8470; frame = (6.5 12; 27 16); text = '\u5b58\u6863'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x282123200>>
   | <UIView: 0x121cc7d70; frame = (-2000 -2000; 0 0); clipsToBounds = YES; layer = <CALayer: 0x2803b2780>>`

4. 加载库

function loadFramework(fw) { 
   var h="/System/Library/",t="Frameworks/"+fw+".framework";
   [[NSBundle bundleWithPath:h+t]||
   [NSBundle bundleWithPath:h+"Private"+t] load];
}
5.使用NSLog
NSLog_ = dlsym(RTLD_DEFAULT, "NSLog")
NSLog = function() { 
   var types = 'v', args = [], count = arguments.length;
   for (var i = 0; i != count; ++i) {
      types += '@';
      args.push(arguments[i]);
  } 
  new Functor(NSLog_, types).apply(null, args);
}

6.Using CGGeometry functions

function CGPointMake(x, y) { return {x:x, y:y}; }
function CGSizeMake(w, h) { return {width:w, height:h}; }
function CGRectMake(x, y, w, h) { return {origin:CGPointMake(x,y), size:CGSizeMake(w, h)}; }

7.输出对象的属性

方法1: 简单基本获取方法。
*controller(直接在对象前面加个*)
方法2:方法一无法获取,就使用方法2
[i for (i in *UIApp)]
方法3:建议方法三,方法三能获取到更多
function tryPrintIvars(a){ 
  var x={}; 
  for(i in *a){ 
    try{ 
      x[i] = (*a)[i]; 
    } 
    catch(e){} 
  }
  return x; 
}

8.根据类获取方法

function printMethods(className, isa) { 
    var count = new new Type("I");
    var classObj = (isa != undefined) ? objc_getClass(className)->isa :     
    objc_getClass(className); 
    var methods = class_copyMethodList(classObj, count); 
    var methodsArray = [];
    for(var i = 0; i < *count; i++) { 
        var method = methods[i]; 
        methodsArray.push({selector:method_getName(method),     
        implementation:method_getImplementation(method)});
    }
        free(methods); 
        return methodsArray;
}

9.获取当前控制器

function currentVC() {
    var app = [UIApplication sharedApplication]  
    var keyWindow = app.keyWindow  
    var rootController = keyWindow.rootViewController  
    var visibleController = rootController.visibleViewController  
    if (!visibleController){
       return rootController
    }
    return visibleController.childViewControllers[0]
}
var vc = currentVC()

10.增加分类

cy# @implementation NSObject(MyCategory) 
    - description { return "hello"; } 
    - (double) f:(int)v {return v * 0.5;}
    @end
cy# o = [new NSObject init]
#"hello"
cy# [o f:3]
1.5

11.创建Block

cy# block = ^ int (int value) {return value + 5;}
^int(int){}
cy# block(10)
15

四、简单实用

想要修改按钮,我们可以有很多方法,现在整理下:
第一种:打印当前视图层次结构找出按钮
第二种:通过dump Class 找出相应的类和头文件,然后再找出按钮。
第三种:通过choose UIButton类,找出按钮。
等等,其实有很多方法都可以找到,大家可以自己研究。

五、归纳

1.UIWindow.keyWindow() 获取keyWindow
2.UIApp 获取Application单例对象
3.recursiveDescription() 循环打印子视图
4.toString() 格式化打印(遇到\n换行)
5.# 对象地址。拿到该对象,可用于调用方法
6.* 对象地址。可以取出对象的成员变量
7.Choose(类名) 查询当前进程中该类型的对象。

pviews()
pvc()
pactions(#0xxxxxxxxx)
rp(#0xxxxxxxxx)
appid 当前bundleid

自定义脚本

(function(exports) {
	var invalidParamStr = 'Invalid parameter';
	var missingParamStr = 'Missing parameter';

	// app id
	CJAppId = [NSBundle mainBundle].bundleIdentifier;

	// mainBundlePath
	CJAppPath = [NSBundle mainBundle].bundlePath;

	// document path
	CJDocPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];

	// caches path
	CJCachesPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];

	// 加载系统动态库
	CJLoadFramework = function(name) {
		var head = "/System/Library/";
		var foot = "Frameworks/" + name + ".framework";
		var bundle = [NSBundle bundleWithPath:head + foot] || [NSBundle bundleWithPath:head + "Private" + foot];
  		[bundle load];
  		return bundle;
	};

	// keyWindow
	CJKeyWin = function() {
		return UIApp.keyWindow;
	};

	// 根控制器
	CJRootVc =  function() {
		return UIApp.keyWindow.rootViewController;
	};

	// 找到显示在最前面的控制器
	var _CJFrontVc = function(vc) {
		if (vc.presentedViewController) {
        	return _CJFrontVc(vc.presentedViewController);
	    }else if ([vc isKindOfClass:[UITabBarController class]]) {
	        return _CJFrontVc(vc.selectedViewController);
	    } else if ([vc isKindOfClass:[UINavigationController class]]) {
	        return _CJFrontVc(vc.visibleViewController);
	    } else {
	    	var count = vc.childViewControllers.count;
    		for (var i = count - 1; i >= 0; i--) {
    			var childVc = vc.childViewControllers[i];
    			if (childVc && childVc.view.window) {
    				vc = _CJFrontVc(childVc);
    				break;
    			}
    		}
	        return vc;
    	}
	};

	CJFrontVc = function() {
		return _CJFrontVc(UIApp.keyWindow.rootViewController);
	};

	// 递归打印UIViewController view的层级结构
	CJVcSubviews = function(vc) {
		if (![vc isKindOfClass:[UIViewController class]]) throw new Error(invalidParamStr);
		return vc.view.recursiveDescription().toString(); 
	};

	// 递归打印最上层UIViewController view的层级结构
	CJFrontVcSubViews = function() {
		return CJVcSubviews(_CJFrontVc(UIApp.keyWindow.rootViewController));
	};

	// 获取按钮绑定的所有TouchUpInside事件的方法名
	CJBtnTouchUpEvent = function(btn) {
		var events = [];
		var allTargets = btn.allTargets().allObjects()
		var count = allTargets.count;
    	for (var i = count - 1; i >= 0; i--) { 
    		if (btn != allTargets[i]) {
    			var e = [btn actionsForTarget:allTargets[i] forControlEvent:UIControlEventTouchUpInside];
    			events.push(e);
    		}
    	}
	   return events;
	};

	// CG函数
	CJPointMake = function(x, y) {
		return {0 : x, 1 : y}; 
	};

	CJSizeMake = function(w, h) {
		return {0 : w, 1 : h}; 
	};

	CJRectMake = function(x, y, w, h) {
		return {0 : CJPointMake(x, y), 1 : CJSizeMake(w, h)};
	};

	// 递归打印controller的层级结构
	CJChildVcs = function(vc) {
		if (![vc isKindOfClass:[UIViewController class]]) throw new Error(invalidParamStr);
		return [vc _printHierarchy].toString();
	};

	// 递归打印view的层级结构
	CJSubviews = function(view) {
		if (![view isKindOfClass:[UIView class]]) throw new Error(invalidParamStr);
		return view.recursiveDescription().toString(); 
	};

	// 判断是否为字符串 "str" @"str"
	CJIsString = function(str) {
		return typeof str == 'string' || str instanceof String;
	};

	// 判断是否为数组 []、@[]
	CJIsArray = function(arr) {
		return arr instanceof Array;
	};

	// 判断num是否为数字
	CJIsNumber = function(num) {
		return typeof num == 'number' || num instanceof Number;
	};

	var _CJClass = function(className) {
		if (!className) throw new Error(missingParamStr);
		if (CJIsString(className)) {
			return NSClassFromString(className);
		} 
		if (!className) throw new Error(invalidParamStr);
		// 对象或者类
		return className.class();
	};

	// 打印所有的子类
	CJSubclasses = function(className, reg) {
		className = _CJClass(className);

		return [c for each (c in ObjectiveC.classes) 
		if (c != className 
			&& class_getSuperclass(c) 
			&& [c isSubclassOfClass:className] 
			&& (!reg || reg.test(c)))
			];
	};

	// 打印所有的方法
	var _CJGetMethods = function(className, reg, clazz) {
		className = _CJClass(className);

		var count = new new Type('I');
		var classObj = clazz ? className.constructor : className;
		var methodList = class_copyMethodList(classObj, count);
		var methodsArray = [];
		var methodNamesArray = [];
		for(var i = 0; i < *count; i++) {
			var method = methodList[i];
			var selector = method_getName(method);
			var name = sel_getName(selector);
			if (reg && !reg.test(name)) continue;
			methodsArray.push({
				selector : selector, 
				type : method_getTypeEncoding(method)
			});
			methodNamesArray.push(name);
		}
		free(methodList);
		return [methodsArray, methodNamesArray];
	};

	var _CJMethods = function(className, reg, clazz) {
		return _CJGetMethods(className, reg, clazz)[0];
	};

	// 打印所有的方法名字
	var _CJMethodNames = function(className, reg, clazz) {
		return _CJGetMethods(className, reg, clazz)[1];
	};

	// 打印所有的对象方法
	CJInstanceMethods = function(className, reg) {
		return _CJMethods(className, reg);
	};

	// 打印所有的对象方法名字
	CJInstanceMethodNames = function(className, reg) {
		return _CJMethodNames(className, reg);
	};

	// 打印所有的类方法
	CJClassMethods = function(className, reg) {
		return _CJMethods(className, reg, true);
	};

	// 打印所有的类方法名字
	CJClassMethodNames = function(className, reg) {
		return _CJMethodNames(className, reg, true);
	};

	// 打印所有的成员变量
	CJIvars = function(obj, reg){
		if (!obj) throw new Error(missingParamStr);
		var x = {}; 
		for(var i in *obj) { 
			try { 
				var value = (*obj)[i];
				if (reg && !reg.test(i) && !reg.test(value)) continue;
				x[i] = value; 
			} catch(e){} 
		} 
		return x; 
	};

	// 打印所有的成员变量名字
	CJIvarNames = function(obj, reg) {
		if (!obj) throw new Error(missingParamStr);
		var array = [];
		for(var name in *obj) { 
			if (reg && !reg.test(name)) continue;
			array.push(name);
		}
		return array;
	};
})(exports);

关于Zeno Chen

本人涉及的领域较多,杂而不精 程序设计语言: Perl, Java, PHP, Python; 数据库系统: MySQL,Oracle; 偶尔做做电路板的开发,主攻STM32单片机
此条目发表在Objective-C分类目录。将固定链接加入收藏夹。