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);
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)