目录

  • MDCycript/MS.cy
  • MDCycript/md.cy
  • mjcript/mjcript.cy
  • libcycript.cy
  • cycript-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 的 BundleIDAPPPATH:     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 的 BundleIDMJAppId = [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 @666MJIsNumber = 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 时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除。默认为 falseenumerable: false,          // 当且仅当该属性的 enumerable 键的值为 true 时,该属性才会出现在对象的枚举属性中。默认为 falsewritable: true,             // 当且仅当该属性的 writable 键的值为 true 时,属性的值,也就是下面的 value,才能被赋值运算符改变。默认为 falsevalue: 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 :/// 这不适用于符号链接,但我不想修复 statreturn 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 所标识的目录不存在,则直接返回 nullif (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 内核 <sys/sysctl.h> 中定义的支持的组件的最大长度CTL_MAXNAME: 12,// Mach 内核 <mach/machine.h> 中定义的 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 被设置为 NULLdladdr(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;&0cy# 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] +[<NSNumber: 0x10032d0c4> 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]  = 0x7fff754fc0700x7fff754fc070*/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.5000000x22*/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")0x100331590cy# 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)0xbff8000000000000cy# 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)falsecy# utils.isMemoryReadable(0x1337)falsecy# utils.isMemoryReadable(NSObject)truecy# 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)0cy# utils.getPointer(1234)1234cy# utils.getPointer(NSObject)140735254495472cy# 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)140735254495472cy# utils.pointerCompare(ptr, NSObject)truecy# 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)falsecy# utils.isClass(NSObject)truecy# 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)falsecy# utils.isObject(NSObject)truecy# utils.isObject(object_getClass(NSObject))truecy# utils.isObject([new NSObject init])truecy# var a = malloc(100); utils.isObject(a)falsecy# *@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.htmlvar 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])truecy# 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().length421cy# utils.find_subviews(utils.is_not_standard_class).length48cy# utils.find_subviews(x => true, choose(UINavigationItemView)[0]).length2*/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().length9*/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])[#"<UIWindow: ...>",#"<UILayoutContainerView: ...>",#"<UINavigationTransitionView: ...>"]*/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)truecy# 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)[#"<UIView: ...>",1,2]cy# utils.view_relation(subview1, subview2)[#"<UIView: ...>",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# ?expandexpand == truecy# utils.get_dyld_info()"0x0000000100000000: /Users/Tyilo/bin/nsrunlooper0x00007fff89ff4000: /usr/lib/libobjc.A.dylib0x00007fff9325f000: /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation0x00007fff9324f000: /usr/lib/libSystem.B.dylib0x00007fff8cec6000: /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 = 0x12345678305419896cy# 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 = 0x12345678305419896cy# ?expandexpand == truecy# 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# ?expandexpand == truecy# var method = class_getInstanceMethod(NSNumber, @selector(intValue));...cy# var imp = method_getImplementation(method);...cy# utils.disasm(imp, 10)"0x7fff83363b8c   1                       55  push rbp0x7fff83363b8d   3                   4889e5  mov rbp, rsp0x7fff83363b90   2                     4157  push r150x7fff83363b92   2                     4156  push r140x7fff83363b94   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]@10cy# [n intValue]10cy# var method = class_getInstanceMethod([n class], @selector(longLongValue));...cy# var imp = method_getImplementation(method);...cy# utils.asm(imp, 'mov eax, 42; ret;')6cy# [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);

Cycript(四):常用脚本相关推荐

  1. android adb 分析,android adb shell常用脚本分析课件.ppt

    android adb shell常用脚本分析课件 ADB概要 Android 调试系统是一个面对客户服务系统,包括三个组成部分: 电脑上运行的客户端. 在你用于开发的机器上作为后台进程运行的服务器. ...

  2. 2022年最新运维常用脚本学习

    以下是常用脚本,由简单到复杂,赶紧收藏起来. 目录 一.日志备份 二.监控内存和磁盘容量,小于给定值时报警 三.检测当前用户权限 四.自动创建相应的账户及配置密码 五.输入三个数并进行升序排序 六.石 ...

  3. postman初步及常用脚本

    对接接口参数这块简单省事的设计. Postman简单介绍 Postman是一个 Chrome 扩展,能提供强大的 Web API & HTTP 请求调试功能. Postman能够发送任何类型的 ...

  4. JS常用脚本+html代码大全+对联广告代码效果大全

    JS常用脚本 1. on_contextmenu="window.event.returnValue=false" 将彻底屏蔽鼠标右键 <table border on_co ...

  5. opencv 常用脚本合集

    opencv 常用脚本合集 一.批量调整图片大小(c++) 二.OpenCV常用图像拼接方法 :直接拼接(硬拼) 三.opencv切割图像 四.图片从中间竖着切开 一.批量调整图片大小(c++) 在处 ...

  6. ORM(四)应用.脚本管理工具

    ORM(四)应用.脚本管理工具 数据脚本的维护,不知道各位有什么好的工具推荐没有,由于以前一直是用手工来进行脚本的维护操作,很麻烦,而且容易出错.大多数时候,都在原来的基础上进行直接修改.今天有点时间 ...

  7. mysql员工脚本_mySQL常用脚本汇总

    @ mysql的常用脚本有哪些呢?我们来做个总结 建表语句 sql语句最后指定主键 DROP TABLE IF EXISTS `user_info1`; CREATE TABLE `user_info ...

  8. oracle 运营维护_Oracle数据库日常运维常用脚本

    大 中 小 Oracle数据库日常运维常用脚本 1 查看所有数据文件 select file_name from dba_data_files union select file_name from ...

  9. Perl,Python,Ruby,Javascript 四种脚本语言比较

    Perl 为了选择一个合适的脚本语言学习,今天查了不少有关Perl,Python,Ruby,Javascript的东西,可是发现各大阵营的人都在吹捧自己喜欢的语言,不过最没有争议的应该是Javascr ...

  10. SQL Performance Analyzer SPA常用脚本汇总

    SPA常用脚本汇总 附件为 一个SPA报告 spa_buffergets_summary SQL 性能分析器 SQL Performance Analyzer SPA Oracle Database ...

最新文章

  1. Deepmind最新研究:从图表示学习看算法推理
  2. java弱引用弱点_终于有人把Java强、软、弱、虚四种引用知识点整理出来了
  3. 无缓冲channel
  4. Python中的常见面试题
  5. 关于TCP/IP协议及网络通信相关问题
  6. Leetcode--150. 逆波兰表达式求值
  7. python获取网络信息_利用psutil获取网络信息
  8. 为什么高斯分布概率密度函数的积分等于1
  9. 《数据库设计入门经典》读书笔记——第二章:工作场所中的数据库建模
  10. c++list遍历_小白学PyTorch | 6 模型的构建访问遍历存储(附代码)
  11. for XML path 转义
  12. 「轻阅读」如何设计全链路99.99%高可用架构?
  13. Vue项目中完成谷歌统计和百度统计功能
  14. 制作 macOS High Sierra 正式版 USB 启动盘的方法 (亲测可用)
  15. 游戏引擎设计 - 物理(Crapell Game Engine Design - physic)
  16. 通过Cookie跳过登录验证码【限cookie不失效有用】
  17. 三分钟了解坚果J10的不足,多方位分析,让你学会挑选投影仪
  18. 这5个显示器选购技巧,把显示器讲明白了
  19. 计算机快速结束进程,结束进程快捷键,详细教您电脑结束进程快捷键怎么操作...
  20. Python 爬取周杰伦歌曲信息,爬取豆瓣top250的电影,并保存至excel中

热门文章

  1. ZYNQ7000学习 (五):PS与PL协同设计实现GPIO
  2. vue基础(主要为vue3)
  3. 苹果CMS10播放地址及图片批量替换
  4. m87靶机实验实战演练
  5. beh部署-datanode节点添加
  6. html中滚动条的代码是什么?如何设置html滚动条?
  7. 解决:操作无法完成(错误0x00000709)。再次检查打印机名称,并确保打印机已连接到...
  8. 实体类转换成Xml格式实例
  9. scala函数参数的传递:传值调用和传名调用
  10. Rocketmq持久化