preface

DNS hijackingInterception of domain name resolution requests within a hijacked network, analysis of the requested domain name, and clearance of requests beyond the scope of review, otherwise returning a false IP address or doing nothing to render the request unresponsive.



DNS hijackingThe main performance is to watch videos, after clicking inexplicably jump to some advertising sites. Normally, when we click on a link, we’re directed to a link calledThe DNS server“, turning the link into something that the machine can recognizeipAddress, the process is as follows:



Domain name – >ipThe process of addressing is calledThe DNS. In this process, becauseDNSThe request message is in plaintext state, which may be detected during the request process and then disguised by an attackerDNSThe server sends a false message to the hostipAddress in response to a message that allows the host to access a bogus server.

NSURLProtocol

NSURLProtocol is one of apple’s dark arts for developers. It can intercept and tamper with most web requests to alter the loading behavior of urls. This allows us to change the details of the request without having to change the business code of the network request. As an abstract class, we must inherit from NSURLProtocol to implement intermediate attacks.

  • Whether to process the corresponding request. Simply returning YES can create a large number of NSURLProtocol objects due to the possibility of dynamic links on web pages, so we need to ensure that YES can be returned only once per request

    + (BOOL)canInitWithRequest: (NSURLRequest *)request;
    + (BOOL)canInitWithTask: (NSURLSessionTask *)task;
    Copy the code
  • Whether to redirect the request or modify key information such as the request header and domain name. Return a new NSURLRequest object to customize the business

    + (NSURLRequest *)canonicalRequestForRequest: (NSURLRequest *)request;
    Copy the code
  • If processing the request returns YES, then the following two callbacks correspond to the start and end phases of the request. This is where you can mark that the request object has been processed

    - (void)startLoading;
    - (void)stopLoading;
    Copy the code

When making a network request, the system will ask the registered NSURLProtocol to determine if the request needs to be processed and modified, registering your subclass with the following code

[NSURLProtocol registerClass: [CustomURLProtocol class]];
Copy the code

The DNS

In general, DNS hijacking is considered mostly when webView is used. Compared to using a web page, a normal web request, even if hijacked, is nothing more than returning the wrong data, or simply 404, and there are other options for dealing with hijacking, so this article discusses how to handle the hijacking of web page loads.

LocalDNS

LocalDNS is a common anti-hijack scheme. In simple terms, you get the requested domain name when the web page initiates a request, resolve it locally to get the IP, and return a direct request to the web page’S IP address. The struct hostent is used to represent address information:

struct hostent { char *h_name; // official name of host char **h_aliases; // alias list int h_addrtype; / / host address type - AF_INET | | AF_INET6 int h_length; // length of address char **h_addr_list; // list of addresses };Copy the code

The C function gethostbyname uses a recursive query to convert the incoming domain name into a struct hostent structure, but this function has a defect: due to the recursive query of the domain name, it often times out. But gethostbyName itself does not support timeout processing, so this function is called in an operation queue, with a semaphore waiting 1.5 seconds for the query:

+ (struct hostent *)getHostByName: (const char *)hostName { __block struct hostent * phost = NULL; dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); NSOperationQueue * queue = [NSOperationQueue new]; [queue addOperationWithBlock: ^{ phost = gethostbyname(hostName); dispatch_semaphore_signal(semaphore); }]; Dispatch_semaphore_wait (dispatch_time(DISPATCH_TIME_NOW, 1.5 * NSEC_PER_SEC)) [queue cancelAllOperations]; return phost; }Copy the code

Then the address information in the structure is symbolized by the function inet_ntop to obtain the ADDRESS information of the C string type. Provides the getIpAddressFromHostName method to hide processing details for ipv4 and ipv6 addresses:

+ (NSString *)getIpv4AddressFromHost: (NSString *)host { const char * hostName = host.UTF8String; struct hostent * phost = [self getHostByName: hostName]; if ( phost == NULL ) { return nil; } struct in_addr ip_addr; memcpy(&ip_addr, phost->h_addr_list[0], 4); char ip[20] = { 0 }; inet_ntop(AF_INET, &ip_addr, ip, sizeof(ip)); return [NSString stringWithUTF8String: ip]; } + (NSString *)getIpv6AddressFromHost: (NSString *)host { const char * hostName = host.UTF8String; struct hostent * phost = [self getHostByName: hostName]; if ( phost == NULL ) { return nil; } char ip[32] = { 0 }; char ** aliases; switch (phost->h_addrtype) { case AF_INET: case AF_INET6: { for (aliases = phost->h_addr_list; *aliases ! = NULL; aliases++) { NSString * ipAddress = [NSString stringWithUTF8String: inet_ntop(phost->h_addrtype, *aliases, ip, sizeof(ip))]; if (ipAddress) { return ipAddress; } } } break; default: break; } return nil; } + (NSString *)getIpAddressFromHostName: (NSString *)host { NSString * ipAddress = [self getIpv4AddressFromHost: host]; if (ipAddress == nil) { ipAddress = [self getIpv6AddressFromHost: host]; } return ipAddress; }Copy the code

extension

The obtained IP address directly resolved by localDNS may not be optimal. Another method is that the application delivers the DNS resolution list from the server after each startup to obtain the IP address from the list. This approach is undoubtedly more efficient than recursive queries, but you need to pay attention to how to avoid the parsed list being tampered with by the middle man during the request delivery process.

Because the requested address may not be valid, theipmappinghostTo ensure that an invalid address can be accessed using the original domain name. In addition to determineipAn invalid address table should be maintained to determine whether to continue using the address access after domain name resolution. The entire domain name resolution process is as follows

WebKit

WKWebView is an alternative to UIWebView, but the former is not good enough to be used by many people. In addition, when NSURLProtocol is used to implement the anti-DNS hijacking function, there is no context after setting canInitWithRequest:. Through looking up information found that want to achieve WebKit request interception need to call some private methods, let WKWebView support NSURLProtocol article has done a good deal, on the basis of the article, the author of the registration protocol process added a layer of processing (after all, Apple dad to trap us will never be easy) :

static inline NSString * lxd_scheme_selector_suffix() {
    return @"SchemeForCustomProtocol:";
}

static inline SEL lxd_register_scheme_selector() {
    const NSString * const registerPrefix = @"register";
    return NSSelectorFromString([registerPrefix stringByAppendingString: lxd_scheme_selector_suffix()]);
}

static inline SEL lxd_unregister_scheme_selector() {
    const NSString * const unregisterPrefix = @"unregister";
    return NSSelectorFromString([unregisterPrefix stringByAppendingString: lxd_scheme_selector_suffix()]);
}
Copy the code

Demo: LXDAppMonitor

The resources

NSURLProtocol iOS network request optimized DNS mapping iOS applications support IPV6, that’s what WKWebView supports NSURLProtocol