讓別人的app變成自己的app系列 -- 攔截網絡請求後進行網址或ip替換

讓別人的app變成自己的app系列 -- 攔截網絡請求後進行網址或ip替換

注: 本文僅用於學習研究,請勿用於非法用途

破解APP的方法很多,如砸殼破解逆行等,或者修改Macho文件,但由於很多網站由於加了密,這些辦法都不行.今天我們在這裏講一個終極辦法攔截網絡請求或者替換IP的方法.

1:應用場景

  • 1:應用場景
    比如某些搶紅包應用有設備鎖或者時間鎖,按設備或者按年付費,部分用戶不想付費的話,我們可以通過攔截網絡,mock data 來破解此類應用;

  • 2:實現原理
    一些人買了一套系統, app端訪問的接口是寫死在打包好的二進制文件,想部署多套應用無法部署.這個時候可以通過攔截網絡請求後進行服務器地址替換,完成多套部署.

2:實現原理

要想攔截,我們需要先弄懂IOS的網絡請求原理,請看下圖

 

# 網絡 <--> NSURLProtocol <--> 網絡庫

大部分的網絡請求都要通過一個叫NSURLProtocol 的抽象類,既然都要通過這個抽象類,那我們是不是通過重載NSURLProtocol的方式進行網絡請求的攔截與過濾呢,答案當然是肯定的.但世界上沒有銀彈,NSURLProtocol不能解決所有的問題,爲什麼呢.因爲NSURLProtocol可以攔截的網絡請求包括NSURLSession,NSURLConnection。現在主流的iOS網絡庫,例如AFNetworking,Alamofire等網絡庫都是基於NSURLSession或NSURLConnection的,所以這些網絡庫的網絡請求都可以被NSURLProtocol所攔截。

PS:基於CFNetwork的網絡請求,以及WKWebView的請求是無法攔截的。例如ASIHTTPRequest,MKNetwokit等網路庫都是基於CFNetwork的,所以這些網絡庫的網絡請求無法被NSURLProtocol攔截。

3.實現步驟

1、創建NSURLProtocol子類

由於NSURLProtocol是一個抽象類,要使用它的時候需要創建它的一個子類。.m文件如下:

 

#import "ReplaceURLProtocol.h"
// 爲了避免canInitWithRequest和canonicalRequestForRequest的死循環
static NSString *const URLProtocolHandledKey = @"URLProtocolHandledKey";

// 老url網址
static NSString *const  old_url = @"baidu.com";
// 新url網址
static NSString *const  new_url = @"google.com";
@interface ReplaceURLProtocol()<NSURLSessionDelegate>
@property(nonatomic,strong)NSURLSession * session;
@end

@implementation ReplaceURLProtocol
+(BOOL)canInitWithRequest:(NSURLRequest *)request
{
  return YES;
}
//改變請求request
+(NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
{
 // 業務邏輯寫這裏
  return request;
}

//開始請求
-(void)startLoading
{
 //業務邏輯寫這裏
}

//停止請求
-(void)stopLoading
{
}

#pragma mark ---- NSURLSessionDelegate
/*
  NSURLSessionDelegate接到數據後,通過URLProtocol傳出去
 */
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
  if (error)
  {
    [self.client URLProtocol:self didFailWithError:error];
  }
  else
  {
    [self.client URLProtocolDidFinishLoading:self];
  }
}

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {
  
  [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
  completionHandler(NSURLSessionResponseAllow);
  
}

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
  [self.client URLProtocol:self didLoadData:data];
}

@end

2、註冊protocol

基於NSURLConnection或[NSURLSession sharedSession]創建的網絡請求,在AppDelegate的didFinishLaunchingWithOptions方法中調用registerClass方法即可。

 

//註冊protocol 
[NSURLProtocol registerClass:[ReplaceURLProtocol class]];returnYES;}

3、攔截用戶的網絡請求
首先,在攔截到網絡請求後會先調用+(BOOL)canInitWithRequest:(NSURLRequest *)request方法。我們可以在該方法裏進行是否處理這個攔截的邏輯。如設置只對攔截到的http或https請求進行處理。

 

+(BOOL)canInitWithRequest:(NSURLRequest *)request
{
   // 不是網絡請求,不處理
    if (![request.URL.scheme isEqualToString:@"http"] &&
        ![request.URL.scheme isEqualToString:@"https"]) {
        return NO;
    }
    
    //     指定攔截網絡請求,如:www.baidu.com
    if ([request.URL.absoluteString containsString:old_url]) {
        return YES;
    }else {
        return NO;
    }
}

接着,會調用+(NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request在該方法中,我們可以對request進行處理。例如修改頭部信息等。最後返回一個處理後的request實例。也可以在該方法裏面將用戶的請求域名替換成別的域名:

 

/**
 設置我們自己的自定義請求
 可以在這裏統一加上頭之類的
 @param request 應用的此次請求
 @return 我們自定義的請求
 */
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request {
    NSMutableURLRequest *mutableReqeust = [request mutableCopy];
    // 設置已處理標誌
    [NSURLProtocol setProperty:@(YES)
                        forKey:kProtocolHandledKey
                     inRequest:mutableReqeust];
    NSLog(@"************ 原始請求的url1 %@",request.URL);
    if ([request.URL host].length == 0)
    {
        return request;
    }
    
    NSString * originUrlStr = [request.URL absoluteString];
    NSString * originHostStr = [request.URL host];
    NSRange hostRange = [originUrlStr rangeOfString:originHostStr];
    
    if (hostRange.location == NSNotFound)
    {
        return request;
    }
    
    //指定攔截網絡請求,如:www.baidu.com
    if ([request.URL.absoluteString containsString:old_url]) {
        //定向到百度搜索
        NSString * ip = new_url;
        NSString * urlStr = [originUrlStr stringByReplacingCharactersInRange:hostRange withString:ip];
        NSURL * url = [NSURL URLWithString:urlStr];
        mutableReqeust.URL = url;
        
        NSLog(@"************ 替換後的url 1 %@",mutableReqeust.URL);
        
        return [mutableReqeust copy];
    }
    else{
        return request;
    }

4、轉發

-(void)startLoading將處理過的request重新發送出去。發送的形式,可以是基於NSURLConnection,NSURLSession甚至CFNetwork。我們也可以在該方法裏面設置網絡代理,如下我們設置一個代理後,重新創建一個NSURLSession將網絡請求發送出去:

 

// 重新父類的開始加載方法
- (void)startLoading {
    
    NSMutableURLRequest * mutableRequest = [[self request] mutableCopy];
    NSLog(@"************ 開始請求 %@",mutableRequest.URL);
    NSURLSessionConfiguration * configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];//創建一個臨時會話配置
    //網絡請求
    self.session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:[[NSOperationQueue alloc] init]];
// 注 這裏也可以添加代理 捕獲用戶請求數據
    NSURLSessionTask * task = [self.session dataTaskWithRequest:self.request];
    [task  resume];//開始任務

}

5、回調

上面使用的是NSURLSession請求,所以我們通過NSURLSessionDelegate來接收網絡請求的數據(成功或失敗等信息):

 

#pragma mark ---- NSURLSessionDelegate
/*
  NSURLSessionDelegate接到數據後,通過URLProtocol傳出去
 */
//失敗
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
  if (error)
  {
    [self.client URLProtocol:self didFailWithError:error]; //請求錯誤
  }
  else
  {
    [self.client URLProtocolDidFinishLoading:self]; //完成加載
  }
}
//接收到響應
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler {
  
  [self.client URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; //創建一個響應(緩存策略:不緩存)
  completionHandler(NSURLSessionResponseAllow);
  
}
//接收到數據
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
{
  [self.client URLProtocol:self didLoadData:data]; //接收到數據
}

6、結束

在-(void)stopLoading完成網絡請求的操作

 

//結束請求
-(void)stopLoading
{
  [self.session invalidateAndCancel];
  self.session = nil;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章