iOS 抓包原理
2023-06-29 本文已影响0人
jazzfly
拦截原理:
- NSURLProtocol 是苹果为我们提供的 URL Loading System 的一部分,在每一个 HTTP 请求开始时,URL 加载系统创建一个合适的 NSURLProtocol 对象处理对应的 URL 请求,而我们需要做的就是写一个继承自 NSURLProtocol 的类,并通过 - registerClass: 方法注册我们的协议类,然后 URL 加载系统就会在请求发出时使用我们创建的协议对象对该请求进行处理。
// 1.注册我们的协议类
[NSURLProtocol registerClass:[DLCustomURLProtocol class]];
### 拦截方法的介绍:
// 2.是否能够处理给定的请求
+ (BOOL)canInitWithRequest:(NSURLRequest *)request;
// 3.处理URL转换为IP地址
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request;
// 4.开始网络请求
- (void)startLoading;
// 5.结束网络请求
- (void)stopLoading;
// 6.处理网络请求的code
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler;
// 7.处理网络请求的返回信息
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error;
WKWebView使用私有API的原因:
- WKURLSchemeHandler是iOS11就推出的,用于处理自定义请求的方案,不过并不能处理HTTP、HTTPS等常规scheme。但是现在有另外一个方法,就是去 WKWebview的initWithFrame:方法,然后设置一个WKURLSchemeHandler给WKWebview实例,这样就能截获WKWebview的HTTP和HTTPS请求了。
WebKit 进程是独立于 app 进程之外的,两个进程之间使用消息队列的方式进行进程间通信。比如 app 想使用 WKWebView 加载一个请求,就要把请求的参数打包成一个 Message,然后通过 IPC 把 Message 交给 WebKit 去加载,反过来 WebKit 的请求想传到 app 进程的话(比如 URLProtocol ),也要打包成 Message 走 IPC。出于性能的原因,打包的时候 HTTPBody 和 HTTPBodyStream 这两个字段被丢弃掉了,这个可以参考 WebKit 的源码,这就导致 -[WKWebView loadRequest:] 传出的 HTTPBody 和 NSURLProtocol 传回的 HTTPBody 全都被丢弃掉了。所以如果通过 NSURLProtocol 注册拦截 http scheme,那么由 WebKit 发起的所有 http POST 请求就全都无效了.
#pragma mark - WKWebView的私有API
#pragma mark - 以下方法为iOS的私有方法,建议上线时注释————以下全部代码
+ (void)DL_unregisterScheme {
[self wk_registerScheme:@"http"];
[self wk_registerScheme:@"https"];
}
FOUNDATION_STATIC_INLINE Class ContextControllerClass() {
static Class cls;
if (!cls) {
cls = [[[WKWebView new] valueForKey:@"browsingContextController"] class];
}
return cls;
}
FOUNDATION_STATIC_INLINE SEL RegisterSchemeSelector() {
return NSSelectorFromString(@"registerSchemeForCustomProtocol:");
}
+ (void)wk_registerScheme:(NSString *)scheme {
Class cls = ContextControllerClass();
SEL sel = RegisterSchemeSelector();
if ([(id)cls respondsToSelector:sel]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[(id)cls performSelector:sel withObject:scheme];
#pragma clang diagnostic pop
}
}
抓取第三方AFN请求
- 对于NSURLSession的网络请求,需要替换protocolClasses方法
+ (void)exchangeAFNSessionConfiguration {
Class cls = NSClassFromString(@"__NSCFURLSessionConfiguration") ?: NSClassFromString(@"NSURLSessionConfiguration");
Method originalMethod = class_getInstanceMethod(cls, @selector(protocolClasses));
Method stubMethod = class_getInstanceMethod([DLCustomURLProtocol class], @selector(protocolClasses));
if (!originalMethod || !stubMethod) {
CLog(@"Couldn't load NEURLSessionConfiguration");
return;
}
method_exchangeImplementations(originalMethod, stubMethod);
}