SoFunction
Updated on 2025-04-07

Detailed explanation of how IOS prevents packet capture

Packet capture principle

In fact, the principle is very simple: generally, packet capture is used to impersonate your server through proxy services. What the client really interacts with is this fake proxy service. This fake service interacts with our real service. This proxy is an intermediary, and all our data will pass through this intermediary, so our data will be crawled. HTTPS will also be falsified by this middleman to obtain our encrypted data.

Prevent bag catching

In order to make the data safer, how can we prevent packets from being caught?

The first idea is: if we can judge whether there is an agent, and there is an agent, then there is a risk.

The second idea: for HTTPS requests. We judge the legality of the certificate.

The first method is implemented:

1. Before initiating a request, determine whether there is a proxy. If there is a proxy, it will return directly, and the request fails.

CFDictionaryRef dicRef = CFNetworkCopySystemProxySettings();
CFStringRef proxyStr = CFDictionaryGetValue(dicRef, kCFNetworkProxiesHTTPProxy);
NSString *proxy = (__bridge NSString *)(proxyStr);
+ (BOOL)getProxyStatus {
    NSDictionary *proxySettings = NSMakeCollectable([(NSDictionary *)CFNetworkCopySystemProxySettings() autorelease]);
    NSArray *proxies = NSMakeCollectable([(NSArray *)CFNetworkCopyProxiesForURL((CFURLRef)[NSURL URLWithString:@""], (CFDictionaryRef)proxySettings) autorelease]);
    NSDictionary *settings = [proxies objectAtIndex:0];
    
    NSLog(@"host=%@", [settings objectForKey:(NSString *)kCFProxyHostNameKey]);
    NSLog(@"port=%@", [settings objectForKey:(NSString *)kCFProxyPortNumberKey]);
    NSLog(@"type=%@", [settings objectForKey:(NSString *)kCFProxyTypeKey]);
    
    if ([[settings objectForKey:(NSString *)kCFProxyTypeKey] isEqualToString:@"kCFProxyTypeNone"])
    {
        //No proxy is set        return NO;
    }
    else
    {
        //Set the proxy        return YES;
    }
}

2. We can clear the proxy in the request configuration so that the request does not leave the proxy.

We hook to the sessionWithConfiguration: method. Then clear the agent

+ (void)load{
  Method method1 = class_getClassMethod([NSURLSession class],@selector(sessionWithConfiguration:));
  Method method2 = class_getClassMethod([NSURLSession class],@selector(px_sessionWithConfiguration:));
  method_exchangeImplementations(method1, method2);

  Method method3 = class_getClassMethod([NSURLSession class],@selector(sessionWithConfiguration:delegate:delegateQueue:));
  Method method4 = class_getClassMethod([NSURLSession class],@selector(px_sessionWithConfiguration:delegate:delegateQueue:));
  method_exchangeImplementations(method3, method4);
}

+ (NSURLSession*)px_sessionWithConfiguration:(NSURLSessionConfiguration*)configuration delegate:(nullable id)delegate delegateQueue:(nullable NSOperationQueue*)queue
{
      if(configuration)  = @{};

  return [self px_sessionWithConfiguration:configuration delegate:delegate delegateQueue:queue];
}

+ (NSURLSession*)px_sessionWithConfiguration:(NSURLSessionConfiguration*)configuration
{

      if(configuration)  = @{};

  return [self px_sessionWithConfiguration:configuration];
}

​ Implementation of the second idea:

It mainly uses HTTPS requests to verify the certificate.

Get the content of the server certificate through SecTrustRef

static NSArray * AFCertificateTrustChainForServerTrust(SecTrustRef serverTrust) {
   CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust);
   NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount];

   for (CFIndex i = 0; i < certificateCount; i++) {
       SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i);
       [trustChain addObject:(__bridge_transfer NSData *)SecCertificateCopyData(certificate)];
   }

   return [NSArray arrayWithArray:trustChain];
}

Then read the contents of the local certificate for comparison

NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"certificate" ofType:@"cer"];//Certificate pathNSData *certData = [NSData dataWithContentsOfFile:cerPath];
SSet&lt;NSData*&gt; * set = [[NSSet alloc]initWithObjects:certData  , nil];  // Local certificate content// Server certificate contentNSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
    if ([set containsObject:trustChainCertificate]) {
        // Certificate verification is passed    }
}

SSL Pinning (AFN+SSL Pinning) Recommended

SSL Pinning, that is, SSL certificate binding. Verify the server identity through SSL certificate binding to prevent the application from being caught.

1. Obtain the certificate

The client requires a certificate (Certification file), a file in .cer format. You can request it from the server.

If they give a .pem file, use the command line to convert:

openssl x509 -inform PEM -in -outform DER -out

If you are given a .crt file, please convert it like this:

openssl x509 -in -out -outform der

If you don't give you anything, you can do it yourself:

openssl s_client -connect :443 </dev/null 2>/dev/null | openssl x509 -outform DER > **

2. Add the certificate to the project

Drag the generated .cer certificate file directly to the relevant folder of your project, remember to check Copy items if needed and Add to targets.

3. Parameter name meaning

AFSecurityPolicy

SSLPinningMode

AFSecurityPolicy is a network communication security policy module in AFNetworking. It offers three SSL Pinning Modes

/**

 ## SSL Pinning Modes

 The following constants are provided by `AFSSLPinningMode` as possible SSL pinning modes.

 enum {

 AFSSLPinningModeNone,

 AFSSLPinningModePublicKey,

 AFSSLPinningModeCertificate,

 }

 `AFSSLPinningModeNone`

 Do not used pinned certificates to validate servers.

 `AFSSLPinningModePublicKey`

 Validate host certificates against public keys of pinned certificates.

 `AFSSLPinningModeCertificate`

 Validate host certificates against pinned certificates.

*/

AFSSLPinningModeNone: Full trust server certificate;

AFSSLPinningModePublicKey: Only compare whether the Public Key of the server certificate and the local certificate are consistent. If it is consistent, trust the server certificate;

AFSSLPinningModeCertificate: Comparing all contents of server certificates and local certificates, if the server certificate is completely consistent, trust the server certificate;

Which mode should I choose?

AFSSLPinningModeCertificate: The safest comparison mode. But it is also more troublesome, because the certificate is packaged in the APP. If the server certificate changes or expires and the old version cannot be used, we need the user to update the APP to use the latest certificate.

AFSSLPinningModePublicKey: Only compare the Public Key for certificates. As long as the Public Key does not change, other changes to the certificate will not affect the use.
If you can't guarantee that your users always use the latest version of your app, so we use AFSSLPinningModePublicKey.

allowInvalidCertificates

/**
 Whether or not to trust servers with an invalid or expired SSL certificates. Defaults to `NO`.
 */
@property (nonatomic, assign) BOOL allowInvalidCertificates;

Whether to trust illegal certificates, the default is NO.

validatesDomainName

/**
 Whether or not to validate the domain name in the certificate's CN field. Defaults to `YES`.
 */
@property (nonatomic, assign) BOOL validatesDomainName;

Whether to check the DomainName field in the verification certificate, it may be IP, domain name such as *., default to YES, strictly guaranteeing security.

4. Set up SLL Pinning using AFSecurityPolicy

+ (AFHTTPSessionManager *)manager
{
    static AFHTTPSessionManager *manager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
    
        NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
        manager =  [[AFHTTPSessionManager alloc] initWithSessionConfiguration:config];

        AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModePublicKey withPinnedCertificates:[AFSecurityPolicy certificatesInBundle:[NSBundle mainBundle]]];
         = securityPolicy;
    });
    return manager;
}

Extended

Android prevents packet capture

1. Single interface access without proxy

URL url = new URL(urlStr);  
urlConnection = (HttpURLConnection) (Proxy.NO_PROXY); 

2. OkHttp framework

OkHttpClient client = new OkHttpClient().newBuilder().proxy(Proxy.NO_PROXY).build(); 

The above is a detailed explanation of how IOS prevents packet capture. For more information about how IOS prevents packet capture, please pay attention to my other related articles!