HTTPDNS iOS 植入

转载地址:https://blog.csdn.net/icefishlily/article/details/78863305

HTTPDNS SDK手册链接 https://help.aliyun.com/document_detail/30141.html?spm=5176.doc30113.6.577.e8LhPM

https://help.aliyun.com/product/30100.html?spm=a2c4g.11186623.6.540.54136c1ejgXrdm

https://www.jianshu.com/p/cef92a5aea01

首先说明一下,要解决的问题:DNS劫持。

对,就是要解决DNS劫持这个问题。不太懂网络的同学们可能不太懂什么是DNS,什么又是DNS劫持,这里简单介绍一下。

DNS就是域名解析系统,就是把我们平时用的网址域名(如www.baidu.com www.sina.com.cn)解析成相对应的服务器IP,只有解析成IP之后,网络请求才能找到服务器。

DNS劫持是啥呢?更简单,就是有人把你的域名解析成了其他的IP,进而达到给你返回他想要返回的内容,比如广告,钓鱼等等.........

那么,iOS APP里里会遇到DNS劫持的问题?当然是我们的UIWebView和WKWebView。当我们的webView加载网页时,很容易被DNS劫持,从而产生很多小广告之类的东西。为了防止这些现象,我们引入了HTTPDNS

OK!说明白了要解决的问题,那接着看HTTPDNS是怎么工作的。

其实很简单,就是阿里云自己提供了一套可靠的DNS系统,我们每次要做域名解析时,直接去阿里云上取IP,而不用通常的DNS方案,所以除了阿里云可以进行DNS劫持,其他人都没戏了(我们是相信阿里云的)。

HTTPDNS植入iOS APP方案:

我们需要把所有的APP产生的请求(或者我们想做HTTPDNS的请求)都通过HTTPDNS来进行域名解析,通过NSURLProtocol类即可实现。

。NSURLProtocol可以接管应用程序所发出的所有请求.NSURLProtocol是抽象类,需要创建一个子类才能使用在子类中重写必要的方法,来实现响应的接管功能:

(1)

第一步:判断哪些请求需要拦截,需要返回是;不需要返回NO
+(BOOL)canInitWithRequest:(NSURLRequest *)请求

(2)

第二步:对拦截的请求进行处理,修改主机,添加cookie等
+(NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)请求
(3)

第三步:建立NSURLSesion对象,并发起请求
- (void)startLoading
(4)

第四步:停止请求
- (void)stopLoading

(5)
第五步:NSURLSessionDelegate中,完成
请求返回数据的回吐,即将请求返回的数据回送给客户端(本来请求的发起者,如UIWebView)
https证书的校验

拦截流程图:

拦截之后的处理流程图:


//
//  CustomURLProtocol.h
//  iphone-pay
//
//  Created by HLYUE on 2017/11/7.
//  Copyright © 2017年 RHJX Inc. All rights reserved.
//
 
#import
 
@interface CustomURLProtocol : NSURLProtocol
 
@end
 
 
//
//  CustomURLProtocol.m
//  iphone-pay
//
//  Created by HLYUE on 2017/11/7.
//  Copyright © 2017年 RHJX Inc. All rights reserved.
//
 
#import "CustomURLProtocol.h"
#import
 
static NSString * const URLProtocolHandledKey = @"URLProtocolHandledKey";
 
@interface CustomURLProtocol ()
@property (nonatomic, strong) NSURLSessionDataTask *dnstask;
@end
 
@implementation CustomURLProtocol
 
//判断哪些request需要拦截,需要返回YES;不需要返回NO
+ (BOOL)canInitWithRequest:(NSURLRequest *)request {
    //只处理http和https请求
    NSString *scheme = [[request URL] scheme];
    if ( ([scheme caseInsensitiveCompare:@"http"] == NSOrderedSame || [scheme caseInsensitiveCompare:@"https"] == NSOrderedSame)) {
        //看看是否已经处理过了,防止无限循环
        if ([NSURLProtocol propertyForKey:URLProtocolHandledKey inRequest:request]) {
            return NO;
        }
        
        //post请求不拦截
        if ([request.HTTPMethod isEqualToString:@"POST"]) {
            return NO;
        }
        
        NSString *hostStr = [request.URL host];
        HttpDnsService *httpdns = [HttpDnsService sharedInstance];
        NSString *httpDnsIP = [httpdns getIpByHostAsync:hostStr];
        if (httpDnsIP) {
            return YES;
        }
    }
 
    return NO;
}
 
//对拦截的request进行处理,修改host,添加cookie
+ (NSURLRequest *) canonicalRequestForRequest:(NSURLRequest *)request {
    NSMutableURLRequest *mutableReqeust = [request mutableCopy];
    mutableReqeust = [self redirectHostInRequset:mutableReqeust];
    return [mutableReqeust copy];
}
 
+(NSMutableURLRequest*)redirectHostInRequset:(NSMutableURLRequest*)request {
    if ([request.URL host].length == 0) {
        return request;
    }
    
    //从URL中获取域名
    NSString *originUrlString = [request.URL absoluteString];
    NSString *originHostString = [request.URL host];
    
    NSRange hostRange = [originUrlString rangeOfString:originHostString];
    if (hostRange.location == NSNotFound) {
        return request;
    }
    
    HttpDnsService *httpdns = [HttpDnsService sharedInstance];
    NSString *httpDnsIP = [httpdns getIpByHostAsync:originHostString];
    
    if (httpDnsIP) {
        //替换包头中的url开头的域名
        NSString *urlString = [originUrlString stringByReplacingCharactersInRange:hostRange withString:httpDnsIP];
        NSURL *url = [NSURL URLWithString:urlString];
        request.URL = url;
        
        //注意:若包头的host(key-value中的host)本身就是一个IP,则需要将这个IP替换成域名(该域名需要从referer中获取)
        if ([self isValidIP:originHostString]) {
            //从referer中获取域名
            NSString *referStr = [request valueForHTTPHeaderField:@"Referer"];
            NSArray *firstArray = [referStr componentsSeparatedByString:@"://"];
            NSString *secondStr;
            if (firstArray.count >= 2) {
                secondStr = firstArray[1];
            } else if (firstArray.count == 1) {
                secondStr = firstArray[0];
            }
            if (secondStr) {
                NSRange range = [secondStr rangeOfString:@"/"];
                if (range.length > 0) {
                    originHostString = [[secondStr componentsSeparatedByString:@"/"] firstObject];
                    originUrlString = [originUrlString stringByReplacingOccurrencesOfString:httpDnsIP withString:originHostString];
                }
            }
        }
        //将从referer中取出的域名,放到请求包头的Host中
        [request setValue:originHostString forHTTPHeaderField:@"Host"];
        
        //设置http的header的cookie
        NSArray *cookiesArray = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:[NSURL URLWithString:originUrlString]];
        NSDictionary *cookieDict = [NSHTTPCookie requestHeaderFieldsWithCookies:cookiesArray];
        NSString *cookie = [cookieDict objectForKey:@"Cookie"];
        [request setValue:cookie forHTTPHeaderField:@"Cookie"];
    }
    return request;
}
 
/**
 *判断一个字符串是否是一个IP地址
 **/
+ (BOOL)isValidIP:(NSString *)ipStr {
    if (nil == ipStr) {
        return NO;
    }
    
    NSArray *ipArray = [ipStr componentsSeparatedByString:@"."];
    if (ipArray.count == 4) {
        for (NSString *ipnumberStr in ipArray) {
            int ipnumber = [ipnumberStr intValue];
            if (!(ipnumber>=0 && ipnumber<=255)) {
                return NO;
            }
        }
        return YES;
    }
    return NO;
}
 
- (void)startLoading {
    NSMutableURLRequest *mutableReqeust = [[self request] mutableCopy];
    //标识该request已经处理过了,防止无限循环
    [NSURLProtocol setProperty:@YES forKey:URLProtocolHandledKey inRequest:mutableReqeust];
    
    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
    configuration.protocolClasses = @[[CustomURLProtocol class]];
    NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
    self.dnstask = [session dataTaskWithRequest:mutableReqeust];
    NSString *referStr = [mutableReqeust valueForHTTPHeaderField:@"Referer"];
    NSLog(@"start loading httpDNS********************\nstart loading httpDNS \n *****url :%@\n *****host:%@ \n *****referer:%@\n", mutableReqeust.URL, [mutableReqeust valueForHTTPHeaderField:@"Host"], referStr);
    [self.dnstask resume];
}
 
- (void)stopLoading {
    [self.dnstask cancel];
}
 
#pragma mark NSURLSessionDelegate
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {
    [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageAllowed];
    
    completionHandler(NSURLSessionResponseAllow);
}
 
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
    [[self client] URLProtocol:self didLoadData:data];
}
 
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
    // 请求完成,成功或者失败的处理
    if (!error) {
        //成功
        [self.client URLProtocolDidFinishLoading:self];
    } else {
        //失败
        [self.client URLProtocol:self didFailWithError:error];
    }
}
 
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain {
    /*
     * 创建证书校验策略
     */
    NSMutableArray *policies = [NSMutableArray array];
    if (domain) {
        [policies addObject:(__bridge_transfer id) SecPolicyCreateSSL(true, (__bridge CFStringRef) domain)];
    } else {
        [policies addObject:(__bridge_transfer id) SecPolicyCreateBasicX509()];
    }
    /*
     * 绑定校验策略到服务端的证书上
     */
    SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef) policies);
    /*
     * 评估当前serverTrust是否可信任,
     * 官方建议在result = kSecTrustResultUnspecified 或 kSecTrustResultProceed
     * 的情况下serverTrust可以被验证通过,https://developer.apple.com/library/ios/technotes/tn2232/_index.html
     * 关于SecTrustResultType的详细信息请参考SecTrust.h
     */
    SecTrustResultType result;
    SecTrustEvaluate(serverTrust, &result);
    if (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed) {
        return YES;
    }
    return NO;
}
 
 
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * __nullable credential))completionHandler {
    if (!challenge) {
        return;
    }
    NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
    NSURLCredential *credential = nil;
 
    
    /*
     * 获取原始域名信息。
     */
    NSString* host = [[self.request allHTTPHeaderFields] objectForKey:@"host"];
    if (!host) {
        host = self.request.URL.host;
    }
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
        if ([self evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:host]) {
            disposition = NSURLSessionAuthChallengeUseCredential;
            credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
        } else {
            disposition = NSURLSessionAuthChallengePerformDefaultHandling;
        }
    } else {
        disposition = NSURLSessionAuthChallengePerformDefaultHandling;
    }
    // 对于其他的challenges直接使用默认的验证方案
    completionHandler(disposition,credential);
}
 
@end
需要对request进行的处理说明:
1.将URL中的域名替换成HTTPDNS解析出来的IP

2.添加包头Host

3.添加自己想要添加的Cookie,这一步视需求而定

HTTPDNS的注册初始化方法,在官网中已经说的很明确了,不做过多解释


HTTPDNS SDK手册链接 https://help.aliyun.com/document_detail/30141.html?spm=5176.doc30113.6.577.e8LhPM


#pragma mark -- HttpDNSDegradationDelegate
- (BOOL)shouldDegradeHTTPDNS:(NSString *)hostName {
    //根据HTTPDNS使用说明,存在网络代理情况下需降级为Local DNS
    if (self.configureProxies) {
        return YES;
    }
    return NO;
}
 
- (void)configHTTPDNS {
    self.configureProxies = [NetworkManager configureProxies];
    //注册CustomURLProtocol(NSURLProtocol子类)
    [NSURLProtocol registerClass:[CustomURLProtocol class]];
    
    // 设置AccoutID,当您开通HTTPDNS服务时,您可以在控制台获取到您对应的Accout ID信息
    HttpDnsService *httpdns = [[HttpDnsService alloc] initWithAccountID:156711];
    httpdns.delegate = self;
    
    //设置预解析域名列表
    NSArray * hosts = [[NSArray alloc] initWithObjects:@“你的域名”,nil]; //这里写上你要通过HTTPDNS解析的
    域名[httpdns setPreResolveHosts:hosts];
    
    //是否允许HTTPDNS返回TTL过期的
    域名[httpdns setExpiredIPEnabled:YES];
    
    //本地日志log开关,测试环境打开
    [httpdns setLogEnabled:NO];
    
    //使用缓存机制
    [httpdns setCachedIPEnabled:YES];
}
configHTTPDNS方法需要在APPdelegate的 - (BOOL)应用程序:(UIApplication *)应用程序didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
 
中调用

对于iOS HTTPDNS的SNI场景说明:HTTPDNS的官网并没有成熟的SNI场景解决方案(针对iOS,安卓是有的)。所以我并没有支持SNI场景。也希望其他朋友们珍重。如有大能使用了,请不吝赐教,谢谢!

你可能感兴趣的:(网络)