Cycript(四):常用脚本

Cycript(四):常用脚本,第1张

目录 MDCycript/MS.cyMDCycript/md.cymjcript/mjcript.cylibcycript.cycycript-utils/utils.cy

MDCycript/MS.cy

git 地址:https://github.com/AloneMonkey/MDCycript/blob/master/MS.cy

(function(ms) {
	// 获取存储 libcycript.dylib 的目录的路径
    let GetLibraryPath = function() {
        let handle = dlopen(NULL, RTLD_NOLOAD);
        if (handle == null)
            return null;

        try {
            let CYListenServer = dlsym(handle, "CYListenServer");
            if (CYListenServer == null)
                return null;

            let info = new Dl_info;
            if (dladdr(CYListenServer, info) == 0)
                return null;

            let path = info->dli_fname;
            let slash = path.lastIndexOf('/');
            if (slash == -1)
                return null;

            path = path.substr(0, slash);

            GetLibraryPath = function() {
                return path;
            };

            return GetLibraryPath();
        } finally {
            dlclose(handle);
        }
    };
    
	// 安全性判断: libcycript.dylib 必须存在
    var libcycript = dlopen(GetLibraryPath() + "/libcycript.dylib", RTLD_NOLOAD);
    if (libcycript == null) {
        return;
    }
    
	// 安全性判断: libsubstrate.dylib 必须存在
    var libsubstrate = dlopen(GetLibraryPath() + "/libsubstrate.dylib", RTLD_GLOBAL | RTLD_LAZY);
    if (libsubstrate == null) {
        return;
    }
    
	// 导入 libsubstrate.dylib 中的 C 函数
    extern "C" void* MSGetImageByName(const char *);
    extern "C" void* MSFindSymbol(void *, const char *);
    extern "C" void MSHookFunction(void *, void *, void **);
    extern "C" void MSHookMessageEx(Class, SEL, void *, void **);

    var slice = Array.prototype.slice;
    
	// 用于封装 Cydia Substrate 的 MSHookFunction 函数
	// @param.func 原始函数
	// @param.hook 替换函数
	// @param.old  用于保存原始实现
    ms.HookFunction = function(func, hook, old) {
        var type = typeid(func);

        var pointer;
        if (old == null || typeof old === "undefined")
            pointer = null;
        else {
            pointer = new (typedef void **);
            *old = function() { return type(*pointer).apply(null, arguments); };
        }

        MSHookFunction(func.valueOf(), type(hook), pointer);
    };
    
	// 用于封装 Cydia Substrate 的 MSHookMessageEx 函数
	// @param.isa 要 hook 的 OC 类
	// @param.sel 要 hook 的方法的名称
	// @param.imp 替换实现
	// @param.old 用于保存原始实现
    ms.HookMessage = function(isa, sel, imp, old) {
        var type = sel.type(isa);

        var pointer;
        if (old == null || typeof old === "undefined")
            pointer = null;
        else {
            pointer = new (typedef void **);
            *old = function() { return type(*pointer).apply(null, [this, sel].concat(slice.call(arguments))); };
        }

        MSHookMessageEx(isa, sel, type(function(self, sel) { return imp.apply(self, slice.call(arguments, 2)); }), pointer);
    };
    
	// 将当前脚本的 ms 中定义的函数暴露给 Cycript 的全局作用域
    for (var k in ms) {
        if(ms.hasOwnProperty(k)) {
            var f = ms[k];
            if(typeof f === 'function') {
                Cycript.all[k] = f;
            }
        }
    }

})(exports);
MDCycript/md.cy

git 地址:https://github.com/AloneMonkey/MDCycript/blob/master/md.cy

(function(utils) {

    utils.constants = {
        APPID:  	 NSBundle.mainBundle.bundleIdentifier,	// 当前 App 的 BundleID
        APPPATH:     NSBundle.mainBundle.bundlePath,		// 当前 App 的工程主目录
        APPHOME:	 NSHomeDirectory(),						// 当前 App 的沙盒主目录
        APPDOC:      NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0],	// 当前 App 的沙盒目录(Documents)
        APPLIBRARY:  NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES)[0],		// 当前 App 的沙盒目录(Library)
        APPCACHE:    NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]		// 当前 App 的沙盒目录(Caches)
    };
    
	// 打印当前 App 主窗口(keyWindow)的视图层级的递归描述
    utils.pviews = function() {
        return UIApp.keyWindow.recursiveDescription().toString();
    };
    
	// 打印当前 App 主窗口(keyWindow)的控制器层级的递归描述
    utils.pvcs = function() {
        return UIWindow.keyWindow().rootViewController._printHierarchy().toString();
    };
    
	// 打印从指定的响应者(target)开始的响应者链条
	// @param.target 因为 nextResponder 方法位于 UIResponder 中
	//				 所以 target 必须是继承自 UIResponder 的类
	//				 即,target 可以是 UIApplication、UIViewController、UIWindow、UIView、等等
    utils.rp = function(target) {
        var result = "" + target.toString();
        while(target.nextResponder){
            result += "\n" + target.nextResponder.toString();
            target = target.nextResponder;
        }
        return result;
    };
    
	// 打印指定的控件(target)的所有 Target 和 Action
	// @param.target 因为 allTargets 方法位于 UIControl 中
	//				 所以 target 必须是继承自 UIControl 的类
	//				 即,target 可以是 UIButton、UITextField、等等
    utils.pactions = function(target) {
		var result = '';
		var objs = target.allTargets.allObjects();
		for(var i = 0; i < objs.length; i++){
			var actions = [target actionsForTarget:objs[i] forControlEvent:0];
			result += objs[i] + " " + [actions componentsJoinedByString:@","];
		}
		return result;
	}
	
	// 将当前脚本的 utils 中定义的常量暴露给 Cycript 的全局作用域
    for (var k in utils.constants) {
        Cycript.all[k] = utils.constants[k];
    }
    
	// 将当前脚本的 utils 中定义的函数暴露给 Cycript 的全局作用域
    for (var k in utils) {
        if(utils.hasOwnProperty(k)) {
            var f = utils[k];
            if(typeof f === 'function') {
                Cycript.all[k] = f;
            }
        }
    }
    
})(exports);
mjcript/mjcript.cy

git 地址:https://github.com/CoderMJLee/mjcript/blob/master/mjcript.cy

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

	// 当前 App 的 BundleID
	MJAppId = [NSBundle mainBundle].bundleIdentifier;

	// 当前 App 的工程主目录
	MJAppPath = [NSBundle mainBundle].bundlePath;

	// 当前 App 的沙盒目录(Documents)
	MJDocPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];

	// 当前 App 的沙盒目录(Caches)
	MJCachesPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0]; 

	// 加载系统动态库
	// @param.name 动态库的名称,例如: UIKit、Foundation、等等
	MJLoadFramework = 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;
	};

	// 当前 App 的主窗口
	MJKeyWin = function() {
		return UIApp.keyWindow;
	};

	// 当前 App 的主窗口的根控制器
	MJRootVc = function() {
		return UIApp.keyWindow.rootViewController;
	};

	var _MJFrontVc = function(vc) {
		if (vc.presentedViewController) {
        	return _MJFrontVc(vc.presentedViewController);
	    } else if ([vc isKindOfClass:[UITabBarController class]]) {
	        return _MJFrontVc(vc.selectedViewController);
	    } else if ([vc isKindOfClass:[UINavigationController class]]) {
	        return _MJFrontVc(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 = _MJFrontVc(childVc);
    				break;
    			}
    		}
	        return vc;
    	}
	};

	// 找到当前 App 的主窗口中显示在最前面的控制器
	MJFrontVc = function() {
		return _MJFrontVc(UIApp.keyWindow.rootViewController);
	};

	// 递归打印指定的控制器 vc 的视图的层级结构
	MJVcSubviews = function(vc) { 
		if (![vc isKindOfClass:[UIViewController class]]) throw new Error(invalidParamStr);
		return vc.view.recursiveDescription().toString(); 
	};

	// 递归打印当前 App 的主窗口中显示在最前面的控制器的视图的层级结构
	MJFrontVcSubViews = function() {
		return MJVcSubviews(_MJFrontVc(UIApp.keyWindow.rootViewController));
	};

	// 获取指定的按钮 btn 绑定的所有 TouchUpInside 事件的方法名
	MJBtnTouchUpEvent = 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;
	};

	// 封装 CGPointMake 函数
	MJPointMake = function(x, y) { 
		return {0 : x, 1 : y}; 
	};

	// 封装 CGSizeMake 函数
	MJSizeMake = function(w, h) { 
		return {0 : w, 1 : h}; 
	};

	// 封装 CGRectMake 函数
	MJRectMake = function(x, y, w, h) { 
		return {0 : MJPointMake(x, y), 1 : MJSizeMake(w, h)}; 
	};

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

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

	// 判断指定的参数 str 是否为字符串 "str" @"str"
	MJIsString = function(str) {
		return typeof str == 'string' || str instanceof String;
	};

	// 判断指定的参数 arr 是否为数组 []、@[]
	MJIsArray = function(arr) {
		return arr instanceof Array;
	};

	// 判断指定的参数 num 是否为数字 666 @666
	MJIsNumber = function(num) {
		return typeof num == 'number' || num instanceof Number;
	};

	var _MJClass = function(className) {
		if (!className) throw new Error(missingParamStr);
		if (MJIsString(className)) {
			// 字符串
			return NSClassFromString(className);
		} 
		if (!className) throw new Error(invalidParamStr);
		// 实例对象或者类对象
		return className.class();
	};

	// 打印所有的子类
	// 打印参数 className 所标识的类的所有的子类
	// @param.className  可以是字符串、实例对象、类对象
	// @param.reg 		 只有类名与该正则表达式匹配的子类才会被打印
	MJSubclasses = function(className, reg) {
		className = _MJClass(className);

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

	// 打印所有方法的: (方法选择器、方法类型编码) + (方法名称)
	// @param.className 用于标识方法所属的类,可以是字符串、实例对象、类对象
	// @param.reg		用于过滤方法名称的正则表达式
	// @param.clazz		标识位,为 true 则打印类方法;为 false 则打印对象方法
	var _MJGetMethods = function(className, reg, clazz) {
		className = _MJClass(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];
	};

	// 打印所有方法的: (方法选择器、方法类型编码)
	// @param.className 用于标识方法所属的类,可以是字符串、实例对象、类对象
	// @param.reg		用于过滤方法名称的正则表达式
	// @param.clazz		标识位,为 true 则打印类方法;为 false 则打印对象方法
	var _MJMethods = function(className, reg, clazz) {
		return _MJGetMethods(className, reg, clazz)[0];
	};

	// 打印所有方法的: (方法名称)
	// @param.className 用于标识方法所属的类,可以是字符串、实例对象、类对象
	// @param.reg		用于过滤方法名称的正则表达式
	// @param.clazz		标识位,为 true 则打印类方法;为 false 则打印对象方法
	var _MJMethodNames = function(className, reg, clazz) {
		return _MJGetMethods(className, reg, clazz)[1];
	};

	// 打印所有对象方法的: (方法选择器、方法类型编码)
	// @param.className 用于标识方法所属的类,可以是字符串、实例对象、类对象
	// @param.reg		用于过滤方法名称的正则表达式
	MJInstanceMethods = function(className, reg) {
		return _MJMethods(className, reg);
	};

	// 打印所有对象方法的: (方法名称)
	// @param.className 用于标识方法所属的类,可以是字符串、实例对象、类对象
	// @param.reg		用于过滤方法名称的正则表达式
	MJInstanceMethodNames = function(className, reg) {
		return _MJMethodNames(className, reg);
	};

	// 打印所有类方法的: (方法选择器、方法类型编码)
	// @param.className 用于标识方法所属的类,可以是字符串、实例对象、类对象
	// @param.reg		用于过滤方法名称的正则表达式
	MJClassMethods = function(className, reg) {
		return _MJMethods(className, reg, true);
	};
	
	// 打印所有类方法的: (方法名称)
	// @param.className 用于标识方法所属的类,可以是字符串、实例对象、类对象
	// @param.reg		用于过滤方法名称的正则表达式
	MJClassMethodNames = function(className, reg) {
		return _MJMethodNames(className, reg, true);
	};

	// 打印所有成员变量的: (名称、值)
	// @param.obj 成员变量所属的实例对象
	// @param.reg 用于过滤成员变量名称或成员变量值的正则表达式
	MJIvars = 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; 
	};

	// 打印所有成员变量的: (名称)
	// @param.obj 成员变量所属的实例对象
	// @param.reg 用于过滤成员变量名称的正则表达式
	MJIvarNames = 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);
libcycript.cy
// 由 Jay Freeman (saurik) 开发

(function() {

// __defineGetter__ 方法可以将一个函数绑定在当前对象的指定属性上,当指定属性的值被读取时,你所绑定的函数就会被调用
// @param.prop 一个字符串,表示指定的属性名
// @param.func 一个函数,当 prop 属性的值被读取时自动被调用
Number.prototype.__defineGetter__('$cyt', function() {
    if (this.$cyt_)
        return this.$cyt_;
    if ((this|0) == this)
        return int;
});

// typeid 函数用于获取指定的对象 object 的运行时类型
// 实际上就是调用指定对象 object 上 prototype 的 $cyt
this.typeid = function(object) {
    return object.$cyt;
};

// 在指定的对象 object 上定义一个新属性 properties
// 该函数主要用于在各个 Type 类型的 prototype 中添加一个 toCYON 属性
let $cy_set = function(object, properties) {
    for (const name in properties)
        if ("defineProperty" in Object)
        // Object.defineProperty() 函数会直接在指定的对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象
        // @param.obj			要定义属性的对象
        // @param.prop			要定义或修改的属性的名称或符号(Symbol)
        // @param.descriptor	要定义或修改的属性描述符
        // @return				被传递给本函数的对象
        Object.defineProperty(object, name, {
            configurable: true,			// 当且仅当该属性的 configurable 键的值为 true 时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认为 false
            enumerable: false,			// 当且仅当该属性的 enumerable 键的值为 true 时,该属性才会出现在对象的枚举属性中。默认为 false
            writable: true,				// 当且仅当该属性的 writable 键的值为 true 时,属性的值,也就是下面的 value,才能被赋值运算符改变。默认为 false
            value: properties[name],	// 该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数,等)。默认为 undefined
        });
        else object[name] = properties[name];
};

// 在 Boolean.prototype 上添加 toCYON 属性
$cy_set(Boolean.prototype, {
    toCYON: function() {
        return `new Boolean(${this.toString()})`;
    },
});

// 在 Date.prototype 上添加 toCYON 属性
$cy_set(Date.prototype, {
    toCYON: function() {
        return `new ${this.constructor.name}(${this.toUTCString().toCYON()})`;
    },
});

// 在 Error.prototype 上添加 toCYON 属性
$cy_set(Error.prototype, {
    toCYON: function() {
        let stack = this.stack;
        if (typeof stack == 'undefined')
            stack = '';
        else {
            stack = stack.split('\n');
            if (stack.slice(-1)[0] == "global code")
                stack = stack.slice(0, -1);
            for (let i = 0; i != stack.length; ++i)
                stack[i] = '\n    ' + stack[i];
            if (stack.length == 0)
                stack = '';
            else {
                stack = stack.join('');
                stack = ` /*${stack} */`;
            }
        }
        return `new ${this.constructor.name}(${this.message.toCYON()})${stack}`;
    },
});

// 在 Number.prototype 上添加 toCYON 属性
$cy_set(Number.prototype, {
    toCYON: function() {
        if ("$cyt" in this)
            //return `${this.$cyt.toCYON()}(${this.toString()})`;
            return this.toString();
        return `new Number(${this.toString()})`;
    },
});

// 在 RegExp.prototype 上添加 toCYON 属性
$cy_set(RegExp.prototype, {
    toCYON: function() {
        return this.toString();
    },
});

// 当 Cycript 中有内置 ObjectiveC 对象时:
// 1.在 NSArray.prototype 上添加 toCYON 属性
// 2.在 NSDictionary.prototype 上添加 toCYON 属性
if ("ObjectiveC" in Cycript) {
	// 1.在 NSArray.prototype 上添加 toCYON 属性
    $cy_set(NSArray.prototype, {
        $cyg: function(key) {
            return objc_msgSend(this, "objectAtIndex:", key);
        },

        $cys: function(key, value) {
            return objc_msgSend(this, "setObject:atIndex:", value, key);
        },
    });
	// 2.在 NSDictionary.prototype 上添加 toCYON 属性
    $cy_set(NSDictionary.prototype, {
        $cyg: function(key) {
            return objc_msgSend(this, "objectForKey:", key);
        },

        $cys: function(key, value) {
            return objc_msgSend(this, "setObject:forKey:", value, key);
        },
    });
}

// 判断指定的路径 path 是否为文件。如果是文件则返回 true,否则返回 false
// @param.path 路径
let IsFile = function(path) {
    // XXX: this doesn't work on symlinks, but I don't want to fix stat :/
    // 这不适用于符号链接,但我不想修复 stat
    return access(path, F_OK) == 0 && access(path + '/', F_OK) == -1;
};

// 判断指定的字符串 lhs 是否以 rhs 开头
let StartsWith = function(lhs, rhs) {
    return lhs.substring(0, rhs.length) == rhs;
};

// 解析指定的文件
// @param.exact 是否进行精确解析
//				如果为 true,则先进行精确解析: 如果文件路径 name 所标识的文件存在,则直接返回该文件路径。否则,进行模糊解析
//				如果为 false,则直接进行模糊解析: 如果能匹配到文件路径 name 所标识 .js 或 .json 文件,则返回该 .js 或 .json 文件。否则,返回 null
// @param.name	文件路径
let ResolveFile = function(exact, name) {
    if (exact && IsFile(name))
        return name;
    for (let suffix of ['.js', '.json'])
        if (IsFile(name + suffix))
            return name + suffix;
    return null;
};

// 获取当前 App 沙盒的 Documents 目录的路径
let GetDocumentPath = function() {
    return NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, 1, true)[0];
};

// 获取存储 libcycript.dylib 的目录的路径
let GetLibraryPath = function() {
    let handle = dlopen(NULL, RTLD_NOLOAD);
    if (handle == null)
        return null;

    try {
        let CYListenServer = dlsym(handle, "CYListenServer");
        if (CYListenServer == null)
            return null;

        let info = new Dl_info;
        if (dladdr(CYListenServer, info) == 0)
            return null;

        let path = info->dli_fname;
        let slash = path.lastIndexOf('/');
        if (slash == -1)
            return null;

        path = path.substr(0, slash);

        GetLibraryPath = function() {
            return path;
        };

        return GetLibraryPath();
    } finally {
        dlclose(handle);
    }
};

// 解析指定的目录
// @param.name 目录路径
let ResolveFolder = function(name) {
	// 如果参数 name 所标识的目录不存在,则直接返回 null
    if (access(name + '/', F_OK) == -1)
        return null;
	// 如果参数 name 所标识的目录存在 package.json 文件
    if (IsFile(name + "/package.json")) {
    	// 加载该 package.json 文件
        let package = require(name + "/package.json");
        let path = ResolveFile(true, name + "/" + package.main);
        if (path != null)
            return path;
    }
	// 如果参数 name 所标识的目录不存在 package.json 文件
    return ResolveFile(false, name + "/index");
};

// 解析指定的文件或目录: 
// 1.先将参数 name 当做文件,调用 ResolveFile 进行解析
// 2.如果解析失败,再将参数 name 当做目录,调用 ResolveFolder 进行解析
let ResolveEither = function(name) {
    let path = null;
    if (path == null)
        path = ResolveFile(true, name);
    if (path == null)
        path = ResolveFolder(name);
    return path;
};

// 查询参数 name 所标识的模块的带有完整绝对路径的文件名
require.resolve = function(name) {
	// 1.如果参数 name 所标识的是一个绝对路径,则在该绝对路径下查找模块
    if (StartsWith(name, '/')) {
        let path = ResolveEither(name);
        if (path != null)
            return path;
	// 2.如果参数 name 所标识的是一个相对路径
    } else {
    	// 获取进程当前的工作目录,并将该目录的各部分分割成字符串数组
        let cwd = *new (typedef char[1024]);
        cwd = getcwd(cwd, cwd.length).toString();
        cwd = cwd.split('/');
		// 2.1如果参数 name 所标识的相对路径以 ./ 或 ../ 开头(这意味着相对于上一级目录,或上上级目录)
		// 则在进程当前工作目录的上一级或上上级中查找模块
        if (StartsWith(name, './') || StartsWith(name, '../')) {
            let path = ResolveEither(cwd + '/' + name);
            if (path != null)
                return path;
        // 2.2如果参数 name 所标识的相对路径不以 ./ 或 ../ 开头
        } else {
			// 2.2.1如果参数 name 所标识的相对路径的各级中存在 node_modules 目录,则在该 node_modules 目录下查找模块
			//		注意搜索的优先级: 倒序遍历参数 name 所标识的相对路径
            for (let i = cwd.length; i != 0; --i) {
                let modules = cwd.slice(0, i).concat("node_modules").join('/');
                let path = ResolveEither(modules + "/" + name);
                if (path != null)
                    return path;
            }
			// 2.2.2在存储 libcycript.dylib 的目录下查找模块
            let library = GetLibraryPath();
            let path = ResolveFile(true, library + "/" + name + ".cy");
            if (path != null)
                return path;
			// 2.2.3在当前 App 沙盒的 Documents 目录下查找模块
            let document = GetDocumentPath();
            path = ResolveFile(true, document + "/cycript/" + name + ".cy");
            if (path != null)
                return path;
        }
    }
	// 3.如果都不满足,则抛出模块找不到的异常
    throw new Error("Cannot find module '" + name + "'");
};

// process.binding(fs) 的辅助函数
var _syscall = function(value) {
    if (value == -1)
        throw new Error(strerror(errno));
};

var info = *new (struct stat);
if (false) {
} else if ("st_atim" in info) {
    var st_atime = "st_atim";
    var st_mtime = "st_mtim";
    var st_ctime = "st_ctim";
} else if ("st_atimespec" in info) {
    var st_atime = "st_atimespec";
    var st_mtime = "st_mtimespec";
    var st_ctime = "st_ctimespec";
} else {
    var st_atime = "st_atime";
    var st_mtime = "st_mtime";
    var st_ctime = "st_ctime";
}

var toDate = function(timespec) {
    return new Date(timespec.tv_sec * 1000 + timespec.tv_nsec / 1000);
};

// 定义模块的绑定关系,并将结果保存在 bindings 中
var bindings = {};

process.binding = function(name) {
    let binding = bindings[name];
    if (typeof binding != 'undefined')
        return binding;

    switch (name) {
        case 'buffer': binding = {
            setupBufferJS() {
            },
        }; break;

        case 'cares_wrap': binding = {
        }; break;

        case 'constants': binding = {
        }; break;

        case 'fs': binding = {
            FSInitialize() {
            },

            lstat(path) {
                var info = new (struct stat);
                _syscall(lstat(path, info));

                return {
                    dev: info->st_dev,
                    mode: info->st_mode,
                    nlink: info->st_nlink,
                    uid: info->st_uid,
                    gid: info->st_gid,
                    rdev: info->st_rdev,
                    blksize: info->st_blksize,
                    ino: info->st_ino,
                    size: info->st_size,
                    blocks: info->st_blocks,

                    atime: toDate(info->[st_atime]),
                    mtime: toDate(info->[st_mtime]),
                    ctime: toDate(info->[st_ctime]),

                    isSymbolicLink() {
                        return S_ISLNK(this.mode);
                    },
                };
            },
        }; break;

        case 'pipe_wrap': binding = {
        }; break;

        case 'smalloc': binding = {
            alloc() {
            },
        }; break;

        case 'stream_wrap': binding = {
        }; break;

        case 'tcp_wrap': binding = {
        }; break;

        case 'timer_wrap': binding = {
            kOnTimeout: 0,
            Timer: {
            },
        }; break;

        case 'tty_wrap': binding = {
        }; break;

        case 'uv': binding = {
        }; break;

        default:
            throw new Error('No such module: ' + name);
    }

    bindings[name] = binding;
    return binding;
};

// 获取当前进程的环境变量,并将结果保存在 process.env 中
process.env = {};

let environ = *(typedef char ***)(dlsym(RTLD_DEFAULT, "environ"));
for (let i = 0; environ[i] != null; ++i) {
    let assign = environ[i];
    let equal = assign.indexOf('=');
    let name = assign.substr(0, equal);
    let value = assign.substr(equal + 1);
    process.env[name.toString()] = value;
}

// 当前工作目录的绝对路径
process.cwd = function() {
    let cwd = new (typedef char[1024]);
    return getcwd(cwd, cwd.length).toString();
};

// 当前进程的 ID
process.pid = getpid();

})();
cycript-utils/utils.cy

git 地址:https://github.com/Tyilo/cycript-utils/blob/master/utils.cy

(function(utils) {

	// 开关: 是否将当前脚本的 utils.constants 中定义的 C 常量暴露给 Cycript 的全局作用域
	var shouldExposeConsts = true;
	// 开关: 是否将当前脚本的 utils 中定义的函数暴露给 Cycript 的全局作用域
	var shouldExposeFuncs = true;

	// 定义各种常数
	utils.constants = {
		// Mach 内核  中定义的支持的组件的最大长度
		CTL_MAXNAME: 12,
		// Mach 内核  中定义的 CPU 类型
		// 虽然 Cycript 中也内置了这些 CPU 类型的定义,但是 Cycript 中关于这些 CPU 类型的定义被损坏了。因此,在这里重新定义
		CPU_TYPE_X86: 7,
		CPU_TYPE_ARM: 12,
	};
	
	var c = utils.constants;

	c.CPU_TYPE_X86_64 = c.CPU_TYPE_X86 | c.CPU_ARCH_ABI64;
	c.CPU_TYPE_ARM64 = c.CPU_TYPE_ARM | c.CPU_ARCH_ABI64;

	/*
	// struct dl_info 是 Mach-O 里面的一个数据结构,默认是通过 dladdr 函数进行赋值的
	typedef struct dl_info {
        const char      *dli_fname;     // 共享对象的路径名
        void            *dli_fbase;     // 共享对象的基地址
        const char      *dli_sname;     // 最近符号的名称
        void            *dli_saddr;     // 最近符号的地址
	} Dl_info;

	// 查询 dyld(动态链接器)以获取包含指定地址 addr 的相关镜像的信息。查询到的信息将被保存在参数 info 指定的结构体中返回
	// 如果找不到包含指定地址 addr 的镜像,则返回 0
	// 如果找到包含指定地址 addr 的镜像,则返回非 0
	// 如果找到包含指定地址 addr 的镜像,但没有找到最近的符号,则 info.dli_sname 和 info.dli_saddr 被设置为 NULL
	dladdr(const void* addr, Dl_info* info);
	*/
	$cysDl_info = (struct dl_info);

	// 导入系统的 C 函数
	extern "C" mach_port_t mach_task_self();
	extern "C" size_t malloc_size(const void *);
	extern "C" struct dyld_all_image_infos *_dyld_get_all_image_infos();
	extern "C" int sysctl(int *, u_int, void *, size_t *, void *, size_t);
	extern "C" int sysctlnametomib(const char *, int *, size_t *);

	// 输出日志的快捷方式
	var log = x => NSLog(@"%s", x.toString());

	/*
	向下对齐指针 ptr,对齐 alignment 必须是 2 的幂
	在 utils.mprotect 函数中使用
	
	示例:
	cy# utils.align(0x100044, 0x1000).toString(16)
	"100000"
	*/
	utils.align = function(ptr, alignment) {
		var high = Math.floor(ptr / Math.pow(2, 32));
		var low = ptr | 0;

		low = (low & ~(alignment - 1));

		if(low < 0) {
			low = Math.pow(2, 32) + low;
		}

		return low + high * Math.pow(2, 32);
	};

	/*
	设置从 addr 开始、长度为 len 的内存的保护为 prot
	
	示例:
	cy# var foo = new int;
	&0
	cy# utils.mprotect(foo.valueOf(),
	                   foo.type.size,
					   utils.constants.PROT_READ)
	cy# *a = 1
	*** _assert(CYRecvAll(client, &size, sizeof(size))):../Console.cpp(142):Run
	*/
	utils.mprotect = function(addr, len, prot) {
		addr = utils.getPointer(addr);

		var pagesize = getpagesize();

		var aligned = utils.align(addr, pagesize);

		var mprotect_size = addr - aligned + len;

		if(mprotect(aligned, mprotect_size, prot)) {
			throw "mprotect failed.";
		}
	};

	/*
	返回与指定的对象 obj 关联的所有值的数组
	
	示例:
	cy# utils.getValues({a: 1, b: 2, c: 2})
	[1,2,2]
	*/
	utils.getValues = function(obj) {
		return Object.keys(obj).map(o => obj[o]);
	};


	/*
	运行带参数 args 的外部程序 path,并返回该外部程序的标准输出
	
	示例:
	cy# utils.getOutputFromTask("/bin/date", ["+%s"])
	@"1419918861\n"
	*/
	utils.getOutputFromTask = function(path, args) {
		var task = [new NSTask init];
		task.launchPath = path;
		task.arguments = args;

		var pipe = [NSPipe pipe];
		task.standardOutput = pipe;

		[task launch];
		[task waitUntilExit];

		var data = [[pipe fileHandleForReading] readDataToEndOfFile];
		return [new NSString initWithData:data encoding:c.NSUTF8StringEncoding];
	};

	// 将指针 ptr 格式化为 16 进制的地址
	utils.hex = function(ptr) {
		return '0x' + utils.getPointer(ptr).toString(16);
	};

	/*
	记录发送到指定类 cls 的指定消息 sel,就像 theos 中的 logify.pl
	需要 Cydia Substrate (com.saurik.substrate.MS) 和 NSLog (org.cycript.NSLog) 模块
	返回由 MS.hookMessage 所返回的旧消息(注意:这可能不仅仅是旧消息!)
	FIXME:对于某些参数组合,将导致进程崩溃
	
	示例:
	cy# var oldm = utils.logify(object_getClass(NSNumber), @selector(numberWithDouble:))
	...
	cy# var n = [NSNumber numberWithDouble:1.5]
	2014-07-28 02:26:39.805 cycript[71213:507] +[ numberWithDouble:1.5]
	2014-07-28 02:26:39.806 cycript[71213:507]  = 1.5
	@1.5
	*/
	utils.logify = function(cls, sel) {
		@import com.saurik.substrate.MS;

		var selFormat = sel.toString().replace(/:/g, ":%s ").trim();
		var logFormat = @"%s[<%@: %p> " + selFormat + "]";

		var oldm = {};

		MS.hookMessage(cls, sel, function() {
			var args = [logFormat, class_isMetaClass(cls)? "+": "-", cls, (typedef void *)(this)];
			for (arg of arguments) {
				args.push(arg.toString());
			}

			NSLog.apply(null, args);

			var r = oldm->apply(this, arguments);

			if(r !== undefined) {
				NSLog(@" = %s", r.toString());
			}

			return r;
		}, oldm);

		return oldm;
	};

	/*
	作用类似于 utils.logify,但用于函数而不是方法
	
	示例:
	cy# logifyFunc("fopen", 2);
	...
	cy# apply("fopen", ["/etc/passwd", "r"]);
	2015-01-14 07:01:08.009 cycript[55326:2042054] fopen(0x10040d4cc, 0x10040d55c)
	2015-01-14 07:01:08.010 cycript[55326:2042054]  = 0x7fff754fc070
	0x7fff754fc070
	*/
	utils.logifyFunc = function(nameOrPointer, argCount) {
		@import com.saurik.substrate.MS;

		var name = "" + nameOrPointer;

		var ptr = nameOrPointer;
		if(typeof nameOrPointer === "string") {
			ptr = dlsym(RTLD_DEFAULT, nameOrPointer);
			if(!ptr) {
				throw "Couldn't find function with name using dlsym!";
			}
		}

		var oldf = {};
		var f = function() {
			var logFormat = @"%s(";
			for(var i = 0; i < arguments.length; i++) {
				logFormat += (i > 0? ", ": "") + "%s";
			}
			logFormat += ")";

			var args = [];
			for (var arg of arguments) {
				args.push(utils.hex(arg));
			}

			NSLog.apply(null, [logFormat, name].concat(args));

			var r = (*oldf).apply(null, arguments);

			if(r !== undefined) {
				NSLog(@" = %s", utils.hex(r));
			}

			return r;
		};

		var voidPtr = (typedef void *);
		var argTypes = [];

		for(var i = 0; i < argCount; i++) {
			argTypes.push(voidPtr);
		}

		var fType = voidPtr.functionWith.apply(voidPtr, argTypes);

		MS.hookFunction(fType(ptr), fType(f), oldf);

		return oldf;
	};

	/*
	通过提供函数名称 fun 和调用参数 args 来调用 C 函数
	不支持结构体类型的参数
	
	示例:
	cy# utils.apply("printf", ["%s %.3s, %d -> %c, float: %f\n", "foo", "barrrr", 97, 97, 1.5])
	foo bar, 97 -> a, float: 1.500000
	0x22
	*/
	utils.apply = function(fun, args) {
		if(!(args instanceof Array)) {
			throw "utils.apply: Args needs to be an array!";
		}

		var argc = args.length;
		var voidPtr = (typedef void *);
		var argTypes = [];
		for(var i = 0; i < argc; i++) {
			var argType = voidPtr;

			var arg = args[i];
			if(typeof arg === "string") {
				argType = (typedef char *);
			}
			if(typeof arg === "number" && arg % 1 !== 0) {
				argType = (typedef double);
			}

			argTypes.push(argType);
		}

		var type = voidPtr.functionWith.apply(voidPtr, argTypes);

		if(typeof fun === "string") {
			fun = dlsym(RTLD_DEFAULT, fun);
		}

		if(!fun) {
			throw "utils.apply: Function not found!";
		}

		return type(fun).apply(null, args);
	};

	/*
	将字符串 (char *) 转换为 void 指针 (void *)
	因为不能在 saurik 的 Cycript 中将字符串转换为 void 指针,反之亦然
	所以提供了此函数
	
	示例:
	cy# var voidPtr = utils.str2voidPtr("foobar")
	0x100331590
	cy# utils.voidPtr2str(voidPtr)
	"foobar"
	*/
	utils.str2voidPtr = function(str) {
		return (typedef void *)(strdup(str));
	};

	/*
	将 void 指针 (void *) 转换为字符串 (char *) 
	因为不能在 saurik 的 Cycript 中将 void 指针转换为字符串,反之亦然
	所以提供了此函数
	*/
	utils.voidPtr2str = function(voidPtr) {
		return (typedef char *)(p).toString()
	};

	/*
	将 double 类型的值 n 转换为 void 指针
	这可用于查看浮点数的二进制表示
	
	示例:
	cy# var n = utils.double2voidPtr(-1.5)
	0xbff8000000000000
	cy# utils.voidPtr2double(n)
	-1.5
	*/
	utils.double2voidPtr = function(n) {
		var doublePtr = new double;
		*doublePtr = n;

		var voidPtrPtr = (typedef void **)(doublePtr);

		return *voidPtrPtr;
	};

	/*
	将 void 指针类型的值 voidPtr 转换为 double 
	*/
	utils.voidPtr2double = function(voidPtr) {
		var voidPtrPtr = new (typedef void **);
		*voidPtrPtr = voidPtr;

		var doublePtr = (typedef double *)(voidPtrPtr);

		return *doublePtr;
	};

	/*
	以安全的方式确定内存位置 ptr 是否可读
	
	示例:
	cy# utils.isMemoryReadable(0)
	false
	cy# utils.isMemoryReadable(0x1337)
	false
	cy# utils.isMemoryReadable(NSObject)
	true
	cy# var a = malloc(100); utils.isMemoryReadable(a)
	true
	*/
	utils.isMemoryReadable = function(ptr) {
		if(typeof ptr === "string") {
			return true;
		}

		var fds = new (typedef int[2]);
		pipe(fds);
		var result = write(fds[1], ptr, 1) == 1;

		close(fds[0]);
		close(fds[1]);

		return result;
	};

	/*
	以数字形式返回指向对象 obj 的指针
	如果输入的对象 obj 不是数字或不代表内存位置,则返回 null
	
	示例:
	cy# utils.getPointer(0)
	0
	cy# utils.getPointer(1234)
	1234
	cy# utils.getPointer(NSObject)
	140735254495472
	cy# utils.getPointer([])
	null
	*/
	utils.getPointer = function(obj) {
		if(obj === 0 || obj === null) {
			return 0;
		}

		var p = (typedef void *)(obj);

		if(p === null) {
			return null;
		}

		return p.valueOf();
	};

	/*
	确定两个对象 o1 和 o2 是否具有相同的指针值
	
	示例:
	cy# var ptr = utils.getPointer(NSObject)
	140735254495472
	cy# utils.pointerCompare(ptr, NSObject)
	true
	cy# utils.pointerCompare(ptr, NSString)
	false
	*/
	utils.pointerCompare = function(o1, o2) {
		if(o1 === o2) {
			return true;
		}

		return utils.getPointer(o1) === utils.getPointer(o2);
	};

	/*
	以安全的方式确定内存位置 obj 是否为(已注册的 Objective-C 类,或已注册的 Objective-C 元类)
	
	示例:
	cy# utils.isClass(0x1337)
	false
	cy# utils.isClass(NSObject)
	true
	cy# utils.isClass(object_getClass(NSObject))
	true
	*/
	utils.isClass = function(obj) {
		var ptr = utils.getPointer(obj);

		if(!ptr) {
			return false;
		}

		var classes = utils.getValues(ObjectiveC.classes);

		for(var i = 0; i < classes.length; i++) {
			var c = classes[i];
			if(utils.pointerCompare(ptr, c)) {
				return true;
			}

			var metaclass = object_getClass(c);

			if(utils.pointerCompare(ptr, metaclass)) {
				return true;
			}
		}

		return false;
	};

	/*
	以安全的方式确定内存位置 obj 是否包含 Objective-C 对象
	
	示例:
	cy# utils.isObject(0x1337)
	false
	cy# utils.isObject(NSObject)
	true
	cy# utils.isObject(object_getClass(NSObject))
	true
	cy# utils.isObject([new NSObject init])
	true
	cy# var a = malloc(100); utils.isObject(a)
	false
	cy# *@encode(void **)(a) = NSObject; utils.isObject(a)
	true
	*/
	utils.isObject = function(obj) {
		function safe_objc_isa_ptr(ptr) {
			if(!utils.isMemoryReadable(ptr)) {
				return false;
			}

			var isa = utils.getPointer(*@encode(void **)(ptr));

			// See http://www.sealiesoftware.com/blog/archive/2013/09/24/objc_explain_Non-pointer_isa.html
			var objc_debug_isa_class_mask = 0x00000001fffffffa;
			isa = (isa & 1)? (isa & objc_debug_isa_class_mask): isa;

			if((isa & (@encode(void *).size - 1)) != 0) {
				return null;
			} else {
				return isa;
			}
		}

		var ptr = utils.getPointer(obj);

		if(!ptr) {
			return false;
		}

		if(utils.isClass(ptr)) {
			return true;
		}

		var c = safe_objc_isa_ptr(ptr);
		if(!utils.isClass(c)) {
			return false;
		}

		var msize = malloc_size(ptr);
		var isize = class_getInstanceSize(new Instance(c));

		return msize >= isize;
	};

	/*
	将所有 UIImage/NSImage 的实例都转储到临时文件夹
	可选地采用过滤器函数 filter_fun 来过滤要转储的图像
	
	示例:
	cy# utils.dumpImages()
	"43 images written to /tmp/cycript-images-rdIbcB"
	cy# utils.dumpImages(img => img.size.width == 16)
	"5 images written to /tmp/cycript-images-8oso44"
	*/
	utils.dumpImages = function(filter_fun) {
		var image_class = ObjectiveC.classes["UIImage"] || ObjectiveC.classes["NSImage"];
		var images = choose(image_class);

		if(filter_fun) {
			images = images.filter(filter_fun);
		}

		if(images.length === 0) {
			throw "utils.dumpImages: No images found!"
		}

		var template = utils.str2voidPtr("/tmp/cycript-images-XXXXXX");
		mkdtemp(template);
		var dir = utils.voidPtr2str(template);

		for(var i of images) {
			data = [i TIFFRepresentation];
			[data writeToFile:dir + "/0x" + utils.getPointer(i).toString(16) + ".tiff" atomically:YES];
		}

		return images.length + " images written to " + dir;
	}

	var app_class = ObjectiveC.classes["UIApplication"] || ObjectiveC.classes["NSApplication"];
	var app = app_class && [app_class sharedApplication];

	/*
	使用启发式的方法来确定对象 obj 所属的类是否是 Apple 提供的
	
	示例:
	cy# @implementation TestClass : NSObject {} @end
	#"TestClass"
	cy# utils.is_not_standard_class([new TestClass init])
	true
	cy# utils.is_not_standard_class([new NSObject init])
	false
	*/
	utils.is_not_standard_class = function(obj) {
		var classname = [obj className];
		while(classname[0] == "_") {
			classname = classname.substr(1);
		}
		return !([classname hasPrefix:"UI"] || [classname hasPrefix:"NS"]);
	};

	/*
	供 utils.find_subviews 和 utils.find_subview_controllers 使用的内部函数
	*/
	function find_subviews_internal(view, predicate, transform) {
		var arr = [];
		var o = transform(view);
		if(o && predicate(o)) {
			arr.push(o);
		}

		return arr.concat.apply(arr, view.subviews.map(x => find_subviews_internal(x, predicate, transform)));
	}

	/*
	递归查找视图 view 中满足谓词 predicate 的所有子视图
	默认从应用的 keyWindow 开始查找所有子视图
	
	示例:
	cy# utils.find_subviews().length
	421
	cy# utils.find_subviews(utils.is_not_standard_class).length
	48
	cy# utils.find_subviews(x => true, choose(UINavigationItemView)[0]).length
	2
	*/
	utils.find_subviews = function(predicate, view) {
		predicate = predicate || (x => true);
		view = view || app.keyWindow;

		return find_subviews_internal(view, predicate, x => x);
	};

	/*
	类似于 utils.find_subviews 函数,但用于查找控制器而不是查找视图
	*/
	utils.find_subview_controllers = function(predicate, view) {
		predicate = predicate || (x => true);
		view = view || app.keyWindow;

		return find_subviews_internal(view, predicate, x => x.viewDelegate || x.delegate);
	};

	/*
	在应用的 keyWindow 的子视图中查找所有只有一个实例的类
	还会过滤掉 Apple 提供的类
	
	示例:
	cy# utils.find_interesting_view_classes().length
	9
	*/
	utils.find_interesting_view_classes = function() {
		var views = utils.find_subviews();
		var classes = views.map(x => x.className.toString());

		var interesting_classes = classes.filter(x => classes.indexOf(x) === classes.lastIndexOf(x));

		return interesting_classes;
	};

	/*
	类似于 utils.find_interesting_view_classes 函数,但用于查找控制器而不是查找视图
	*/
	utils.find_interesting_viewcontroller_classes = function() {
		var views = utils.find_subview_controllers();
		var classes = views.map(x => x.className.toString());

		var interesting_classes = classes.filter(x => classes.indexOf(x) === classes.lastIndexOf(x));

		return interesting_classes;
	};

	/*
	递归返回指定的视图 view 的 superview
	
	示例:
	cy# utils.view_hierarchy(UIApp.keyWindow.subviews[0].subviews[0])
	[#"",#"",#""]
	*/
	utils.view_hierarchy = function(view) {
		var arr = [];
		do {
			arr.unshift(view);
		} while(view = view.superview);

		return arr;
	};

	/*
	确定指定的 UIView/NSView 类型的实例对象 view 是否在屏幕上
	
	示例:
	cy# utils.is_on_screen(UIApp.keyWindow)
	true
	cy# utils.is_on_screen([new UIView init])
	false
	*/
	utils.is_on_screen = function(view) {
		var hierarchy = utils.view_hierarchy(view);

		return !![hierarchy[0] isEqual:app.keyWindow];
	};

	/*
	返回两个视图 subview1 和 subview2 的公共父视图,以及两个用于标识 subview1、subview2 和父视图之间的距离的整数
	
	示例:
	cy# rootview = [new UIView init]
	...
	cy# subview1 = [new UIView init]; [rootview addSubview:subview1];
	cy# subview2 = [new UIView init]; [rootview addSubview:subview2];
	cy# subview22 = [new UIView init]; [subview2 addSubview:subview22];
	cy# utils.view_relation(subview1, subview22)
	[#"",1,2]
	cy# utils.view_relation(subview1, subview2)
	[#"",1,1]
	cy# utils.view_relation(rootview, [new UIView init])
	null
	*/
	utils.view_relation = function(view1, view2) {
		var view_hierarchy1 = utils.view_hierarchy(view1);
		var view_hierarchy2 = utils.view_hierarchy(view2);

		var i;
		for(i = 0; [view_hierarchy1[i] isEqual:view_hierarchy2[i]]; i++) {
		}

		if(i === 0) {
			return null;
		}

		return [view_hierarchy1[i - 1], view_hierarchy1.length - i, view_hierarchy2.length - i];
	};

	/*
	返回一个包含两个整数的数组,这两个整数指定了当前正在运行的进程的 CPU_TYPE 和 CPU_SUB_TYPE
	如果可执行文件是胖二进制文件,则返回当前活动的 CPU 架构的值
	
	示例:
	cy# utils.getCpuType() // x86_64
	[16777223,0]
	cy# utils.getCpuType() // i368
	[7,0]
	cy# utils.getCpuType() // arm 32 bit
	[12,0]
	*/
	utils.getCpuType = function() {
		var mibLen = c.CTL_MAXNAME;
		var mib = new (typedef int[mibLen]);
		var mibLenPtr = new uint64_t(mibLen);
		var err = sysctlnametomib("sysctl.proc_cputype", mib, mibLenPtr);

		if(err !== 0) {
			throw "utils.getCpuType: Error calling sysctlnametomib!";
		}

		mibLen = *mibLenPtr;
		(*mib)[mibLen] = getpid();
		mibLen++;

		var current_arch = (typedef struct {cpu_type_t type; cpu_subtype_t subtype;});
		var archType = new current_arch;
		var archTypeSizePtr = new uint64_t(current_arch.size);
		err = sysctl(mib, mibLen, archType, archTypeSizePtr, 0, 0);

		if(err !== 0) {
			throw "utils.getCpuType: Error calling sysctl!";
		}

		return [archType->type, archType->subtype];
	};

	/*
	用 0 填充指定的十六进制数 num,以表示 bytes 个字节
	
	示例:
	cy# utils.hexpad(1, 4)
	"00000001"
	cy# utils.hexpad(0xffffff, 4)
	"00ffffff"
	*/
	utils.hexpad = function(num, bytes) {
		if(typeof num === "string") {
			num = Number(num);
		}

		var hex = num.toString(16);
		var padded = Array(bytes * 2 + 1).join('0') + hex;
		return padded.slice(-Math.max(2 * bytes, hex.length));
	};

	/*
	返回一个字符串,其中包含进程中每个已加载镜像的地址和路径
	
	示例:
	cy# ?expand
	expand == true
	cy# utils.get_dyld_info()
	"
	0x0000000100000000: /Users/Tyilo/bin/nsrunlooper
	0x00007fff89ff4000: /usr/lib/libobjc.A.dylib
	0x00007fff9325f000: /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation
	0x00007fff9324f000: /usr/lib/libSystem.B.dylib
	0x00007fff8cec6000: /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
	..."
	*/
	utils.get_dyld_info = function() {
		var all_image_infos = _dyld_get_all_image_infos();
		var image_count = all_image_infos->infoArrayCount;
		var info_array = all_image_infos->infoArray;

		var log = "";

		for(var i = 0; i < image_count; i++) {
			var info = info_array[i];

			var base = info.imageLoadAddress.valueOf();
			log += "\n0x" + utils.hexpad(base, @encode(void *).size) + ": " + info.imageFilePath;
		}

		return log;
	};

	/*
	返回代表内存的十六进制字符串
	内存范围从 addr 开始,长度为 len
	
	示例:
	cy# var foo = new int;
	cy# *foo = 0x12345678
	305419896
	cy# utils.gethex(foo, 4)
	"78563412"
	*/
	utils.gethex = function(addr, len) {
		addr = utils.getPointer(addr);

		var res = "";

		var p = (typedef uint8_t *)(addr);

		for(var i = 0; i < len; i++) {
			res += utils.hexpad(p[i], 1);
		}

		return res;
	};

	/*
	返回代表内存的十六进制字符串(与 xxd 格式相似的十六进制转储)
	内存范围从 addr 开始,长度为 len
	
	示例:
	cy# var foo = new int;
	cy# *foo = 0x12345678
	305419896
	cy# ?expand
	expand == true
	cy# utils.hexdump(foo, 4)
	"
	01005015a0:	78 56 34 12 	xV4.
	"
	*/
	utils.hexdump = function(addr, len, bytes_per_line) {
		addr = utils.getPointer(addr);

		if(!len) {
			len = 0x100;
		}

		if(!bytes_per_line) {
			bytes_per_line = 0x10;
		}

		function isprint(c) {
			return 0x20 <= c && c <= 0x7E;
		}

		var p = (typedef uint8_t *)(addr);
		var res = "\n";

		var addr_len = Math.ceil((addr + len - 1).toString(16).length / 2);

		for(var i = 0; i < len; i += bytes_per_line) {
			var cols = [utils.hexpad(addr + i, addr_len) + ":", "", ""];

			for(var j = i; j < i + bytes_per_line && j < len; j++) {
				var n = p[j];
				cols[1] += utils.hexpad(n, 1) + " ";
				cols[2] += isprint(n)? String.fromCharCode(n): ".";
			}

			res += cols.join("\t") + "\n";
		}

		return res;
	};

	// 获取 rasm2 的参数,用于 utils.disasm 和 utils.asm 函数
	function getRasm2Args(addr) {
		addr = utils.getPointer(addr);

		var cpuType = utils.getCpuType();

		var bits = "32";
		if(cpuType[0] & CPU_ARCH_ABI64) {
			bits = "64";
		}

		var arch;
		if(cpuType[0] & c.CPU_TYPE_X86) {
			arch = "x86";
		} else if(cpuType[0] & c.CPU_TYPE_ARM) {
			arch = "arm";
		} else {
			throw "Unknown arch for cpu: " + cpuType.join(", ");
		}

		var args = ["-a", arch, "-b", bits, "-o", addr.toString()];
		return args;
	}

	// 获取 rasm2 的输出,用于 utils.disasm 和 utils.asm 函数
	function getRasm2Output(args) {
		var rasm2_path = utils.getOutputFromTask("/bin/bash", ["-c", "which rasm2"]).trim();

		if(!rasm2_path) {
			throw "rasm2 command not found in /bin/bash's $PATH";
		}

		return utils.getOutputFromTask(rasm2_path, args);
	}

	/*
	使用 rasm2 反汇编指定的内存范围
	内存范围从 addr 开始,长度为 len
	
	示例:
	cy# ?expand
	expand == true
	cy# var method = class_getInstanceMethod(NSNumber, @selector(intValue));
	...
	cy# var imp = method_getImplementation(method);
	...
	cy# utils.disasm(imp, 10)
	"
	0x7fff83363b8c   1                       55  push rbp
	0x7fff83363b8d   3                   4889e5  mov rbp, rsp
	0x7fff83363b90   2                     4157  push r15
	0x7fff83363b92   2                     4156  push r14
	0x7fff83363b94   2                     4155  push r13
	"
	*/
	utils.disasm = function(addr, len) {
		addr = utils.getPointer(addr);

		if(!len) {
			len = 0x40;
		}

		var args = getRasm2Args(addr);

		var hex = utils.gethex(addr, len);
		args.push("-D", hex);

		return "\n" + getRasm2Output(args);
	};

	/*
	使用 rasm2 将指定的汇编指令 ins 写入到指定的内存地址 addr
	
	示例:
	cy# var n = [NSNumber numberWithLongLong:10]
	@10
	cy# [n intValue]
	10
	cy# var method = class_getInstanceMethod([n class], @selector(longLongValue));
	...
	cy# var imp = method_getImplementation(method);
	...
	cy# utils.asm(imp, 'mov eax, 42; ret;')
	6
	cy# [n longLongValue]
	42
	*/
	utils.asm = function(addr, ins) {
		addr = utils.getPointer(addr);

		var args = getRasm2Args(addr);

		args.push("--", ins);

		var output = getRasm2Output(args).trim();

		if(!output) {
			throw "Couldn't assemble instructions with rasm2.";
		}

		utils.mprotect(addr, output.length / 2, PROT_READ | PROT_WRITE | PROT_EXEC);

		var p = @encode(uint8_t *)(addr);

		for(var i = 0; i < output.length; i += 2) {
			p[i / 2] = parseInt(output[i] + output[i + 1], 16);
		}

		return output.length / 2;
	};

	// 将当前脚本的 utils.constants 中定义的 C 常量暴露给 Cycript 的全局作用域
	if (shouldExposeConsts) {
		for(var k in utils.constants) {
			Cycript.all[k] = utils.constants[k];
		}
	}

	// 将当前脚本的 utils 中定义的函数暴露给 Cycript 的全局作用域
	if (shouldExposeFuncs) {
		for(var k in utils) {
			if(utils.hasOwnProperty(k)) {
				var f = utils[k];
				if(typeof f === 'function') {
					Cycript.all[k] = f;
				}
			}
		}
	}
})(exports);

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/993551.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存