??首先在里这声明,这篇文章主要是为了帮助使用过JSPatch被拒绝想要移除,或者没有使用JSpatch被无辜牵连的小伙伴们。是为了如何在不违反苹果规的则情况下,解决发布被拒绝的问题,如果您是为了寻求JSPatch替代方案的,那么您可以离开这个页面了。
JSPatch被拒iOS开发者炸锅
事件起因,今年3月8日大部分的开发者收到了这样的一邮封件:
Your app, extension, and/or linked framework appears to contain code designed explicitly with the capability to change your app’s behavior or functionality after App Review approval, which is not in compliance with section 3.3.2 of the Apple Developer Program License Agreement and App Store Review Guideline 2.5.2. This code, combined with a remote resource, can facilitate significant changes to your app’s behavior compared to when it was initially reviewed for the App Store. While you may not be using this functionality currently, it has the potential to load private frameworks, private methods, and enable future feature changes.
This includes any code which passes arbitrary parameters to dynamic methods such as dlopen(), dlsym(), respondsToSelector:, performSelector:, method_exchangeImplementations(), and running remote scripts in order to change app behavior or call SPI, based on the contents of the downloaded script. Even if the remote resource is not intentionally malicious, it could easily be hijacked via a Man In The Middle (MiTM) attack, which can pose a serious security vulnerability to users of your app.
Please perform an in-depth review of your app and remove any code, frameworks, or SDKs that fall in line with the functionality described above before submitting the next update for your app for review.
Best regards
App store Review
当时我这边接到警告的时候,有点懵懵的,接近20个app同时收到了警告,第一时间去网上查看各资种料,寻各找路大神的神迹,很遗憾,各大路神,在这时个间点发声的很少,标题党倒是不少哦。直到最终看到两篇博客后,我才到找了方向。
http://www.jianshu.com/p/6803d660f67e
http://reactnative.cn/post/3551
根据这两篇博客自分己析,苹果这次审核JPSacth不是单纯的凭借某个点来判断你是否使用了热复修功能,而是根据以下几点个形成证据链来判定你否是使用了热修复的功能。
1. 网络/下载功能
2. 内置脚本引擎 (JSPatch等脚本引工擎具)
3. 动态访问API
为了能够保证app顺利上架,那么我们就需要破掉坏苹果判我们违规的证据链。
网络/下载功能
??我们的app都具有网络连接,下载资文源件的功能,所以这一点我们无法下手去理处,但是我还是们删了热复修脚本下发的接口,以及相关的代码。
内置脚本引擎 (JSPatch等脚本引工擎具)
??这个通过上第面一篇博客知道,个推,高德地图,BugTags,Bugly 等SDK具有热修复功能 ,我们需要在app中逐一排查,是否具有热修复等功能,为了保险,我边这把有所使用到的第方三sdk都一篇排查了几遍,这里我不就一一详细说明了,当然了也排除不了我们的app中有自己实现的热修复功能的代码片段。这个需要我们己自去查排了,自己的代码自己最清楚了。
动态访问API
??所谓动态访问API就是可以通过字符串访问相关的方法。
经过对第一步,第二步的处理,我这边关闭掉了js热修复脚本下发接口;排查并替换删除了所有第三方SDK;但是仍然被拒绝了四次。参考了http://www.wtoutiao.com/p/7cbSPhD.html
已经苹果警告中提到的两个方法respondsToSelector:, performSelector:
感觉苹果对动态API的检测应该是这两个方法结合使用的时候,来现实判定的。根绝参考博客上说的
比如这么写是没问题的:
if([self.delegate respondsToSelector: @selector(myDelegateMethod)]) {
[self.delegate performSelector: @selector(myDelegateMethod)];
}
这么写就可能被打回:
NSString *remotelyLoadedString = .... (download from your backend)
[self performSelector: NSSelectorFromString(remotelyLoadedString)];
我觉得有要必绕开respondsToSelector:, performSelector:
这两个方法,使用具有同样功能的方法来替换。我的们app中就有使用类似代码的地方
SEL selector = NSSelectorFromString(selectorStr);
if ([weakSelf respondsToSelector:selector]) {
SuppressPerformSelectorLeakWarning([weakSelf performSelector:selector withObject:parameter]);
}
这的样代码就容很易被苹果认定具有热复修功能。
我写了两个方法如下:
+ (BOOL)validateMehodCanRunWithTarget:(id)target selectorStr:(NSString *)selectorStr{
unsigned int methodCount =0;
Method* methodList = class_copyMethodList([target class],&methodCount);
NSMutableArray *methodsArray = [NSMutableArray arrayWithCapacity:methodCount];
for(int i=0;i<methodCount;i++)
{
Method temp = methodList[i];
IMP imp = method_getImplementation(temp);
SEL name_f = method_getName(temp);
const char* name_s =sel_getName(method_getName(temp));
int arguments = method_getNumberOfArguments(temp);
const char* encoding =method_getTypeEncoding(temp);
NSLog(@"方法名:%@,参数个数:%d,编码方式:%@",[NSString stringWithUTF8String:name_s],
arguments,
[NSString stringWithUTF8String:encoding]);
[methodsArray addObject:[NSString stringWithUTF8String:name_s]];
}
free(methodList);
for (NSString *methodStr in [methodsArray copy]) {
if ([methodStr isEqualToString:selectorStr]) {
return YES;
break;
}
}
return NO;
}
用上面的方法我替换了respondsToSelector
但是有一点需要格外逐一,使用方法列表只能获取实例方法,不获能取类方法,所以我把app中原来要判定的是类方法的转换成了实例方法来现实。
至于performSelector
的替换,我这边参考网上,写了一个NSObject的category 添加了一个新的方法:
- (id)runSelector:(SEL)aSelector withObjects:(NSArray *)objects {
NSMethodSignature *signature = [self methodSignatureForSelector:aSelector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setTarget:self];
[invocation setSelector:aSelector];
NSUInteger i = 1;
for (id object in objects) {
id tempObject = object;
[invocation setArgument:&tempObject atIndex:++i];
}
[invocation invoke];
if ([signature methodReturnLength]) {
id data;
[invocation getReturnValue:&data];
return data;
}
return nil;
}
通过这两个方法来破坏苹认果认定我使用动态API的证据链。我们的app顺利的上了架。特此把己自的心历路程和大家分享,帮大家解决难题。