源码网商城,靠谱的源码在线交易网站 我的订单 购物车 帮助

源码网商城

iOS开发中实现hook消息机制的方法探究

  • 时间:2021-11-25 11:44 编辑: 来源: 阅读:
  • 扫一扫,手机访问
摘要:iOS开发中实现hook消息机制的方法探究
[b]Method Swizzling 原理[/b] 在Objective-C中调用一个方法,其实是向一个对象发送消息,查找消息的唯一依据是selector的名字。利用Objective-C的动态特性,可以实现在运行时偷换selector对应的方法实现,达到给方法挂钩的目的。 每个类都有一个方法列表,存放着selector的名字和方法实现的映射关系。IMP有点类似函数指针,指向具体的Method实现。 [img]http://files.jb51.net/file_images/article/201510/2015101393512380.png?201591393529[/img] 我们可以利用 method_exchangeImplementations 来交换2个方法中的IMP, 我们可以利用 class_replaceMethod 来修改类, 我们可以利用 method_setImplementation 来直接设置某个方法的IMP, …… 归根结底,都是偷换了selector的IMP,如下图所示: [img]http://files.jb51.net/file_images/article/201510/2015101393719583.png?201591393734[/img] [b]Method Swizzling 实践[/b] 举个例子好了,我想钩一下NSArray的lastObject 方法,只需两个步骤。 第一步:给NSArray加一个我自己的lastObject
[u]复制代码[/u] 代码如下:
#import "NSArray+Swizzle.h"      @implementation NSArray (Swizzle)      - (id)myLastObject  {      id ret = [self myLastObject];      NSLog(@"**********  myLastObject *********** ");      return ret;  }  @end 
乍一看,这不递归了么?别忘记这是我们准备调换IMP的selector,[self myLastObject] 将会执行真的 [self lastObject] 。 第二步:调换IMP
[u]复制代码[/u] 代码如下:
#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;      }  } 
控制台输出Log:
[u]复制代码[/u] 代码如下:
2013-07-18 16:26:12.585 Hook[1740:c07] **********  myLastObject ***********   2013-07-18 16:26:12.589 Hook[1740:c07] TEST RESULT : 3 
结果很让人欣喜,是不是忍不住想给UIWebView的loadRequest: 加 TODO 了呢? [b]示例[/b] 有了这个原理,接下来让我们来看一个实例: 下面先直接上源码:  
[u]复制代码[/u] 代码如下:
// //  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对应的method     Method sendEvent = class_getInstanceMethod([UIWindow class], @selector(sendEvent:));     Method sendEventMySelf = class_getInstanceMethod([self class], @selector(sendEventHooked:));         // 将目标函数的原实现绑定到sendEventOriginalImplemention方法上     IMP sendEventImp = method_getImplementation(sendEvent);     class_addMethod([UIWindow class], @selector(sendEventOriginal:), sendEventImp, method_getTypeEncoding(sendEvent));         // 然后用我们自己的函数的实现,替换目标函数对应的实现     IMP sendEventMySelfImp = method_getImplementation(sendEventMySelf);     class_replaceMethod([UIWindow class], @selector(sendEvent:), sendEventMySelfImp, method_getTypeEncoding(sendEvent)); } /*  * 截获到window的sendEvent  * 我们可以先处理完以后,再继续调用正常处理流程  */ - (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
  下面我们来逐行分析一下上面的代码:     首先我们来看19行,这一行主要目的是获取到UIWindow原生的sendEvent的Method(一个结构体,用来对方法进行描述),接着第20行是获取到我们自己定义的类中的sendEvent的Method(这两个方法的签名必须一样,否则运行时报错)。第23行我们通过UIWindow原生的sendEvent的Method获取到对应的IMP(一个函数指针),第24行使用运行时API Class_addMethod给UIWindow类添加了一个叫sendEventOriginal的方法,该方法使用UIWindow原生的sendEvent的实现,并且有着相同的方法签名(必须相同,否则运行时报错)。27行是获取我们自定义类中的sendEventMySelf的IMP,28行是关键的一行,这一行的主要目的是为UIWindow原生的sendEvent指定一个新的实现,我们看到我们将该实现指定到了我们自己定义的sendEventMySelf上。到了这儿我们就完成了偷梁换柱,大功告成。     执行上面这些行以后,我们就成功的将UIWindow的sendEvent重定向到了我们自己的写的sendEventMySelf的实现,然后将其原本的实现重定向到了我们给它新添加的方法sendEventOriginal中。而sendEventMySelf中,我们首先可以对这个消息进行我们想要的处理,然后再通过41行调用sendEventOriginal方法转到正常的执行流程。     这块儿你可能有个困惑 “我们自定义类中明明是没有sendEventOriginal方法的啊?”     为什么执行起来不报错,而且还会正常执行?因为sendEventMySelf是UIWindow的sendEvent重定向过来的,所以在运行时该方法中的self代表的就是UIWindow的实例,而不再是TestHookObject的实例了。加上sendEventOriginal是我们通过运行时添加到UIWindow的实例方法,所以可以正常调用。当然如果直接通过下面这种方式调用也是可以的,只不过编译器会提示警告(编译器没那么智能),因此我们采用了performSelector的调用方式。  
[u]复制代码[/u] 代码如下:
[self sendEventOriginal:event];
  以上就是Hook的实现,使用时我们只需要让TestHookObject类执行一次初始话操作就可以了,执行完以后。UIWindow的sendEvent消息就会会hook到我们的sendEventMySelf中了。     下面是调用代码:      
[u]复制代码[/u] 代码如下:
- (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; }
代码中我们还专门添加了一个button来验证,hook完以后消息是否正常传递。经验证消息流转完全正常。
  • 全部评论(0)
联系客服
客服电话:
400-000-3129
微信版

扫一扫进微信版
返回顶部