国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 系統(tǒng) > iOS > 正文

iOS開(kāi)發(fā)中實(shí)現(xiàn)hook消息機(jī)制的方法探究

2019-10-21 18:57:11
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友
這篇文章主要介紹了iOS開(kāi)發(fā)中實(shí)現(xiàn)hook消息機(jī)制的方法探究,這里用到了一個(gè)Method Swizzling原理,需要的朋友可以參考下
 

Method Swizzling 原理

在Objective-C中調(diào)用一個(gè)方法,其實(shí)是向一個(gè)對(duì)象發(fā)送消息,查找消息的唯一依據(jù)是selector的名字。利用Objective-C的動(dòng)態(tài)特性,可以實(shí)現(xiàn)在運(yùn)行時(shí)偷換selector對(duì)應(yīng)的方法實(shí)現(xiàn),達(dá)到給方法掛鉤的目的。
每個(gè)類都有一個(gè)方法列表,存放著selector的名字和方法實(shí)現(xiàn)的映射關(guān)系。IMP有點(diǎn)類似函數(shù)指針,指向具體的Method實(shí)現(xiàn)。
iOS開(kāi)發(fā)中實(shí)現(xiàn)hook消息機(jī)制的方法探究
我們可以利用 method_exchangeImplementations 來(lái)交換2個(gè)方法中的IMP,
我們可以利用 class_replaceMethod 來(lái)修改類,
我們可以利用 method_setImplementation 來(lái)直接設(shè)置某個(gè)方法的IMP,
……
歸根結(jié)底,都是偷換了selector的IMP,如下圖所示:
iOS開(kāi)發(fā)中實(shí)現(xiàn)hook消息機(jī)制的方法探究
Method Swizzling 實(shí)踐


舉個(gè)例子好了,我想鉤一下NSArray的lastObject 方法,只需兩個(gè)步驟。
第一步:給NSArray加一個(gè)我自己的lastObject

 

復(fù)制代碼代碼如下:

#import "NSArray+Swizzle.h"  
  
  
@implementation NSArray (Swizzle)  
  
  
- (id)myLastObject  
{  
    id ret = [self myLastObject];  
    NSLog(@"**********  myLastObject *********** ");  
    return ret;  
}  
@end 

 

乍一看,這不遞歸了么?別忘記這是我們準(zhǔn)備調(diào)換IMP的selector,[self myLastObject] 將會(huì)執(zhí)行真的 [self lastObject] 。

 

第二步:調(diào)換IMP

 

復(fù)制代碼代碼如下:

#import   
#import "NSArray+Swizzle.h"  
  
  
int main(int argc, char *argv[])  
{  
    @autoreleasepool {  
          
        Method ori_Method =  class_getInstanceMethod([NSArray class], @selector(lastObject));  
        Method my_Method = class_getInstanceMethod([NSArray class], @selector(myLastObject));  
        method_exchangeImplementations(ori_Method, my_Method);  
          
        NSArray *array = @[@"0",@"1",@"2",@"3"];  
        NSString *string = [array lastObject];  
        NSLog(@"TEST RESULT : %@",string);  
          
        return 0;  
    }  

 


控制臺(tái)輸出Log:

復(fù)制代碼代碼如下:

2013-07-18 16:26:12.585 Hook[1740:c07] **********  myLastObject ***********   
2013-07-18 16:26:12.589 Hook[1740:c07] TEST RESULT : 3 

 


結(jié)果很讓人欣喜,是不是忍不住想給UIWebView的loadRequest: 加 TODO 了呢?

示例

有了這個(gè)原理,接下來(lái)讓我們來(lái)看一個(gè)實(shí)例:
下面先直接上源碼:
 

 

復(fù)制代碼代碼如下:

//
//  TestHookObject.m
//  TestHookMessage
//
//  Created by mapleCao on 13-2-28.
//  Copyright (c) 2013年 mapleCao. All rights reserved.
//

 

#import "TestHookObject.h"
#import <objc/objc.h>
#import <objc/runtime.h>

@implementation TestHookObject

// this method will just excute once
+ (void)initialize
{
    // 獲取到UIWindow中sendEvent對(duì)應(yīng)的method
    Method sendEvent = class_getInstanceMethod([UIWindow class], @selector(sendEvent:));
    Method sendEventMySelf = class_getInstanceMethod([self class], @selector(sendEventHooked:));
    
    // 將目標(biāo)函數(shù)的原實(shí)現(xiàn)綁定到sendEventOriginalImplemention方法上
    IMP sendEventImp = method_getImplementation(sendEvent);
    class_addMethod([UIWindow class], @selector(sendEventOriginal:), sendEventImp, method_getTypeEncoding(sendEvent));
    
    // 然后用我們自己的函數(shù)的實(shí)現(xiàn),替換目標(biāo)函數(shù)對(duì)應(yīng)的實(shí)現(xiàn)
    IMP sendEventMySelfImp = method_getImplementation(sendEventMySelf);
    class_replaceMethod([UIWindow class], @selector(sendEvent:), sendEventMySelfImp, method_getTypeEncoding(sendEvent));
}

/*
 * 截獲到window的sendEvent
 * 我們可以先處理完以后,再繼續(xù)調(diào)用正常處理流程
 */
- (void)sendEventHooked:(UIEvent *)event
{
    // do something what ever you want
    NSLog(@"haha, this is my self sendEventMethod!!!!!!!");
    
    // invoke original implemention
    [self performSelector:@selector(sendEventOriginal:) withObject:event];
}

@end


 


  下面我們來(lái)逐行分析一下上面的代碼:
 
  首先我們來(lái)看19行,這一行主要目的是獲取到UIWindow原生的sendEvent的Method(一個(gè)結(jié)構(gòu)體,用來(lái)對(duì)方法進(jìn)行描述),接著第20行是獲取到我們自己定義的類中的sendEvent的Method(這兩個(gè)方法的簽名必須一樣,否則運(yùn)行時(shí)報(bào)錯(cuò))。第23行我們通過(guò)UIWindow原生的sendEvent的Method獲取到對(duì)應(yīng)的IMP(一個(gè)函數(shù)指針),第24行使用運(yùn)行時(shí)API Class_addMethod給UIWindow類添加了一個(gè)叫sendEventOriginal的方法,該方法使用UIWindow原生的sendEvent的實(shí)現(xiàn),并且有著相同的方法簽名(必須相同,否則運(yùn)行時(shí)報(bào)錯(cuò))。27行是獲取我們自定義類中的sendEventMySelf的IMP,28行是關(guān)鍵的一行,這一行的主要目的是為UIWindow原生的sendEvent指定一個(gè)新的實(shí)現(xiàn),我們看到我們將該實(shí)現(xiàn)指定到了我們自己定義的sendEventMySelf上。到了這兒我們就完成了偷梁換柱,大功告成。
 
  執(zhí)行上面這些行以后,我們就成功的將UIWindow的sendEvent重定向到了我們自己的寫的sendEventMySelf的實(shí)現(xiàn),然后將其原本的實(shí)現(xiàn)重定向到了我們給它新添加的方法sendEventOriginal中。而sendEventMySelf中,我們首先可以對(duì)這個(gè)消息進(jìn)行我們想要的處理,然后再通過(guò)41行調(diào)用sendEventOriginal方法轉(zhuǎn)到正常的執(zhí)行流程。
 
  這塊兒你可能有個(gè)困惑 “我們自定義類中明明是沒(méi)有sendEventOriginal方法的啊?” 
 
  為什么執(zhí)行起來(lái)不報(bào)錯(cuò),而且還會(huì)正常執(zhí)行?因?yàn)閟endEventMySelf是UIWindow的sendEvent重定向過(guò)來(lái)的,所以在運(yùn)行時(shí)該方法中的self代表的就是UIWindow的實(shí)例,而不再是TestHookObject的實(shí)例了。加上sendEventOriginal是我們通過(guò)運(yùn)行時(shí)添加到UIWindow的實(shí)例方法,所以可以正常調(diào)用。當(dāng)然如果直接通過(guò)下面這種方式調(diào)用也是可以的,只不過(guò)編譯器會(huì)提示警告(編譯器沒(méi)那么智能),因此我們采用了performSelector的調(diào)用方式。
 

復(fù)制代碼代碼如下:

[self sendEventOriginal:event];

  以上就是Hook的實(shí)現(xiàn),使用時(shí)我們只需要讓TestHookObject類執(zhí)行一次初始話操作就可以了,執(zhí)行完以后。UIWindow的sendEvent消息就會(huì)會(huì)hook到我們的sendEventMySelf中了。
 
  下面是調(diào)用代碼:
 
 
 

 

 

復(fù)制代碼代碼如下:

 

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
    // Override point for customization after application launch.
    self.viewController = [[[TestHookViewController alloc] initWithNibName:@"TestHookViewController" bundle:nil] autorelease];
    self.window.rootViewController = self.viewController;
    [self.window makeKeyAndVisible];
    
    
    //hook UIWindow‘s SendEvent method
    TestHookObject *hookSendEvent = [[TestHookObject alloc] init];
    [hookSendEvent release];
    
    UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
    btn.center = CGPointMake(160, 240);
    btn.backgroundColor = [UIColor redColor];
    [btn addTarget:self action:@selector(btnAction:) forControlEvents:UIControlEventAllEvents];
    [self.window addSubview:btn];
    [btn release];
    
    return YES;
}


 

代碼中我們還專門添加了一個(gè)button來(lái)驗(yàn)證,hook完以后消息是否正常傳遞。經(jīng)驗(yàn)證消息流轉(zhuǎn)完全正常。



注:相關(guān)教程知識(shí)閱讀請(qǐng)移步到IOS開(kāi)發(fā)頻道。
發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 延寿县| 云龙县| 长岭县| 宁强县| 双城市| 洪洞县| 昌吉市| 重庆市| 南康市| 全州县| 娱乐| 菏泽市| 顺义区| 巴东县| 孝义市| 罗平县| 开鲁县| 吕梁市| 科技| 永平县| 城市| 将乐县| 商都县| 新泰市| 湘潭县| 台东市| 磴口县| 邹平县| 米易县| 司法| 邹城市| 平塘县| 九寨沟县| 鄂托克前旗| 车致| 阿拉善左旗| 湖口县| 隆子县| 维西| 阜新| 舒兰市|