JSPatch源码解析
2021-10-13 本文已影响0人
不拘小节123456
原理简单说就是:1,解析js文件(通过JSContext).2,通过runtimeApi动态创建类.3,可以hook原生Api进行拦截(该hook方案不存在继承关系间hook可能失败问题)
1,简单的js调原生
js业务代码
defineClass('JPViewController', {
handleBtn: function(sender) {
console.log('------------handleBtn -- JPTableViewController');
var tableViewCtrl = JPTableViewController.alloc().init()
self.navigationController().pushViewController_animated(tableViewCtrl, YES)
}
})
经过正则过滤之后
defineClass('JPViewController', {
handleBtn: function(sender) {
console.__c("log")('------------handleBtn -- JPTableViewController');
var tableViewCtrl = JPTableViewController.__c("alloc")().__c("init")()
self.__c("navigationController")().__c("pushViewController_animated")(tableViewCtrl, YES)
}
})
在JSPatch.js解析js文件中代码
var _methodFunc = function(instance, clsName, methodName, args, isSuper, isPerformSelector) {
var selectorName = methodName
if (!isPerformSelector) {
methodName = methodName.replace(/__/g, "-")
selectorName = methodName.replace(/_/g, ":").replace(/-/g, "_")
var marchArr = selectorName.match(/:/g)
var numOfArgs = marchArr ? marchArr.length : 0
if (args.length > numOfArgs) {
selectorName += ":"
}
}
var ret = instance ? _OC_callI(instance, selectorName, args, isSuper):
_OC_callC(clsName, selectorName, args)
return _formatOCToJS(ret)
}
var _customMethods = {
__c: function(methodName) {
var slf = this
console.log('------------customMethods')
console.log(slf.__obj)
console.log(slf.__clsName)
console.log(methodName)
if (slf instanceof Boolean) {
return function() {
return false
}
}
if (slf[methodName]) {
return slf[methodName].bind(slf);
}
if (!slf.__obj && !slf.__clsName) {
throw new Error(slf + '.' + methodName + ' is undefined')
}
if (slf.__isSuper && slf.__clsName) {
slf.__clsName = _OC_superClsName(slf.__obj.__realClsName ? slf.__obj.__realClsName: slf.__clsName);
}
var clsName = slf.__clsName
if (clsName && _ocCls[clsName]) {
var methodType = slf.__obj ? 'instMethods': 'clsMethods'
if (_ocCls[clsName][methodType][methodName]) {
slf.__isSuper = 0;
return _ocCls[clsName][methodType][methodName].bind(slf)
}
}
return function(){
var args = Array.prototype.slice.call(arguments)
return _methodFunc(slf.__obj, slf.__clsName, methodName, args, slf.__isSuper)
}
},
super: function() {
var slf = this
if (slf.__obj) {
slf.__obj.__realClsName = slf.__realClsName;
}
console.log('-----------super')
console.log(slf.__clsName)
return {__obj: slf.__obj, __clsName: slf.__clsName, __isSuper: 1}
},
performSelectorInOC: function() {
var slf = this
var args = Array.prototype.slice.call(arguments)
return {__isPerformInOC:1, obj:slf.__obj, clsName:slf.__clsName, sel: args[0], args: args[1], cb: args[2]}
},
performSelector: function() {
var slf = this
var args = Array.prototype.slice.call(arguments)
return _methodFunc(slf.__obj, slf.__clsName, args[0], args.splice(1), slf.__isSuper, true)
}
}
for (var method in _customMethods) {
if (_customMethods.hasOwnProperty(method)) {
Object.defineProperty(Object.prototype, method, {value: _customMethods[method], configurable:false, enumerable: false})
}
}
//原生部分代码
context[@"_OC_callI"] = ^id(JSValue *obj, NSString *selectorName, JSValue *arguments, BOOL isSuper) {
return callSelector(nil, selectorName, arguments, obj, isSuper);
};
context[@"_OC_callC"] = ^id(NSString *className, NSString *selectorName, JSValue *arguments) {
return callSelector(className, selectorName, arguments, nil, NO);
};
static id formatJSToOC(JSValue *jsval)
{
id obj = [jsval toObject];
if (!obj || [obj isKindOfClass:[NSNull class]]) return _nilObj;
if ([obj isKindOfClass:[JPBoxing class]]) return [obj unbox];
if ([obj isKindOfClass:[NSArray class]]) {
NSMutableArray *newArr = [[NSMutableArray alloc] init];
for (int i = 0; i < [(NSArray*)obj count]; i ++) {
[newArr addObject:formatJSToOC(jsval[i])];
}
return newArr;
}
if ([obj isKindOfClass:[NSDictionary class]]) {
if (obj[@"__obj"]) {
id ocObj = [obj objectForKey:@"__obj"];
if ([ocObj isKindOfClass:[JPBoxing class]]) return [ocObj unbox];
return ocObj;
} else if (obj[@"__clsName"]) {
return NSClassFromString(obj[@"__clsName"]);
}
if (obj[@"__isBlock"]) {
Class JPBlockClass = NSClassFromString(@"JPBlock");
if (JPBlockClass && ![jsval[@"blockObj"] isUndefined]) {
return [JPBlockClass performSelector:@selector(blockWithBlockObj:) withObject:[jsval[@"blockObj"] toObject]];
} else {
return genCallbackBlock(jsval);
}
}
NSMutableDictionary *newDict = [[NSMutableDictionary alloc] init];
for (NSString *key in [obj allKeys]) {
[newDict setObject:formatJSToOC(jsval[key]) forKey:key];
}
return newDict;
}
return obj;
}
总结:业务js代码经过正则后编译成js解析器可以解析到js代码,通过JSPatch.js文件根据原生注册的方法进行交互
2,部分runtimeApi
//创建对象,并进行注册
cls = objc_allocateClassPair(superCls, className.UTF8String, 0);
objc_registerClassPair(cls);
//创建协议,并进行添加
Protocol *protocol = objc_getProtocol([trim(protocolName) cStringUsingEncoding:NSUTF8StringEncoding]);
class_addProtocol (cls, protocol);
//添加方法
class_addMethod(cls, @selector(ORIGforwardInvocation:), originalForwardImp, "v@:@");
//添加属性:通过关联对象技术来添加属性
class_addMethod(cls, @selector(getProp:), (IMP)getPropIMP, "@@:@");
class_addMethod(cls, @selector(setProp:forKey:), (IMP)setPropIMP, "v@:@@");
static id getPropIMP(id slf, SEL selector, NSString *propName) {
return objc_getAssociatedObject(slf, propKey(propName));
}
static void setPropIMP(id slf, SEL selector, id val, NSString *propName) {
objc_setAssociatedObject(slf, propKey(propName), val, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
3,hook原有Api.
JSPatch的hook之所以可以不存在继承链hook的问题,是因为给原有的Method指向了一个新的SEL缓存了,在消息转发拦截的时候在重新调用这个,他由于没有交换,所以不存在这个问题
//获取forwardImp,可以直接进入到消息转发流程,进行统一拦截
IMP msgForwardIMP = _objc_msgForward;
#if !defined(__arm64__)
if (typeDescription[0] == '{') {
//In some cases that returns struct, we should use the '_stret' API:
//http://sealiesoftware.com/blog/archive/2008/10/30/objc_explain_objc_msgSend_stret.html
//NSMethodSignature knows the detail but has no API to return, we can only get the info from debugDescription.
NSMethodSignature *methodSignature = [NSMethodSignature signatureWithObjCTypes:typeDescription];
if ([methodSignature.debugDescription rangeOfString:@"is special struct return? YES"].location != NSNotFound) {
msgForwardIMP = (IMP)_objc_msgForward_stret;
}
}
#endif
//将原来的函数Imp指向自定义名字的SEL(拼接ORIG)
if (class_respondsToSelector(cls, selector)) {
NSString *originalSelectorName = [NSString stringWithFormat:@"ORIG%@", selectorName];
SEL originalSelector = NSSelectorFromString(originalSelectorName);
if(!class_respondsToSelector(cls, originalSelector)) {
class_addMethod(cls, originalSelector, originalImp, typeDescription);
}
}
//将原有SEL指向forwardImp进行统一拦截
class_replaceMethod(cls, selector, msgForwardIMP, typeDescription);
//常用hook方法,如果继承关系进行hook会有问题,如果有三层继承关系 Base -> Parenet -> Child ,Base有函数(-(void)text;),Parenet(-(void)text_parent;),Child(-(void)text_child),如果先hookChild的(text ->text_child),在hookparent(text->text_parent),会出现text_parent无法调用.
void swizzleMethod(Class class, SEL originalSelector, SEL swizzledSelector)
{
// the method might not exist in the class, but in its superclass
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
// 如果当前类没有改方法,添加该方法,大部分情况是子类没有改方法,父类有,
BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
// the method doesn’t exist and we just added one
if (didAddMethod) {
//将swizzledSelector 指向原来的Method
class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
}
else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}