先放两张效果图,是在同一个workspace中代码修改前和修改后的app网络请求的结果图。可以很明显看到通过修改自己的代码,改变了友盟分享SDK的网络请求协议。
图1.修改前(不支持HTTPS)
图2.修改后(支持HTTPS)
说明:项目ATS Settings设置了Allow Arbitrary Loads = YES,即允许HTTP请求。
本过程使用XCode 8.2.1运行app在iPhone5s上,iPhone5s上设置代理到本机8888端口,Charles监听本机8888端口,可以拦截app的HTTP/HTTPS网络请求。通过Charles拦截app的网络请求,可以观察到app的所有HTTP/HTTPS请求,是否符合Apple的ATS要求。以下两张图聚焦友盟分享SDK的网络请求。
最初,可以很轻松地观察拦截到的友盟SDK请求为HTTP协议(如图1),但是使用HTTPS协议也能访问,比如 https://api.share.mob.com 和 http://api.share.mob.com 都可以访问。
由此看来,友盟分享的网站是支持HTTPS的。这是改变友盟分享SDK的网络协议的基础。
然后,寻找切入点。查看友盟SDK的头文件,发现MOBFoundation.framework的MOBFApplication.h文件中有检测是否启用ATS的功能。可以从这点出发对它进行改造。
MOBFApplication.h声明
替换方法很简单,使用runtime知识即可,这里使用Method Swizzling对原始方法进行替换。因为不需要原始方法的逻辑,只用自己的方法替换掉原始方法就好,不用管原始方法的逻辑。实现起来很简单:
-(void)exchangeMethodMobEnabledATS{
Method originATS_enable = class_getClassMethod([MOBFApplication class], @selector(enabledATS));
Method changeATS_enable = class_getClassMethod([HomeLifeAppDelegate class], @selector(app_enableATS));
method_exchangeImplementations(originATS_enable, changeATS_enable);
}
+ (BOOL)app_enableATS{
return true;
}
在application:didFinishLaunchingWithOptions:方法中调用
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self exchangeMethodMobEnabledATS];
}
运行工程,Charles中拦截的友盟SDK网络请求如下:
Method Swizzling以后
效果非常明显,除了一个HTTP请求以外,其它请求都改用HTTPS了。这说明之前的思路是正确的。
剩下的这个HTTP请求估计是时机的问题,猜测很可能是exchangeMethodMobEnabledATS方法调用之前就已经确定好enableATS的值了。我们可以将exchangeMethodMobEnabledATS方法的执行时机提前,看是否有什么不同。
App能否在main()函数之前执行代码呢?它的入口在哪里呢?
我们一般考虑App的生命期是从main()函数开始的,main()函数执行以后会进入AppDelegate中的application:willFinishLaunchingWithOptions:等方法,这样看来,application:willFinishLaunchingWithOptions:方法已经是最早可以执行代码的时机了。
事实上,有某种方法可以让函数在main()函数之前或者之后执行。如下所示:
+ (BOOL)app_enableATS{
return true;
}
static void __attribute__((constructor)) before_main_exchange_method() {
Method originATS_enable = class_getClassMethod([MOBFApplication class], @selector(enabledATS));
Method changeATS_enable = class_getClassMethod([HomeLifeAppDelegate class], @selector(app_enableATS));
method_exchangeImplementations(originATS_enable, changeATS_enable);
}
可以放在工程的任何地方,都会在main()函数执行前被执行(某些SDK和库不用开发者调用都能自动执行,原来The Point在这里→_→)。
也就是之后执行enabledATS方法时都会调用自定义的app_enableATS方法,保证友盟SDK在判断环境的时候都会认为当前是ATS可用状态。
这样做了以后,友盟SDK已经完全支持HTTPS了,无论工程设置了是否允许HTTP。其网络请求如下:
screenshot5.png
PS:
在main()函数或exit()函数之后执行代码的能力:
staticvoid__attribute__((destructor)) after_main_exchange_method() {
}
可以用它干些有意思的事了(o)/











网友评论