服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - IOS - iOS开发中实现hook消息机制的方法探究

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

2020-12-25 15:44lmmort IOS

这篇文章主要介绍了iOS开发中实现hook消息机制的方法探究,这里用到了一个Method Swizzling原理,需要的朋友可以参考下

method swizzling 原理

在objective-c中调用一个方法,其实是向一个对象发送消息,查找消息的唯一依据是selector的名字。利用objective-c的动态特性,可以实现在运行时偷换selector对应的方法实现,达到给方法挂钩的目的。
每个类都有一个方法列表,存放着selector的名字和方法实现的映射关系。imp有点类似函数指针,指向具体的method实现。
iOS开发中实现hook消息机制的方法探究
我们可以利用 method_exchangeimplementations 来交换2个方法中的imp,
我们可以利用 class_replacemethod 来修改类,
我们可以利用 method_setimplementation 来直接设置某个方法的imp,
……
归根结底,都是偷换了selector的imp,如下图所示:
iOS开发中实现hook消息机制的方法探究
method swizzling 实践


举个例子好了,我想钩一下nsarray的lastobject 方法,只需两个步骤。
第一步:给nsarray加一个我自己的lastobject

 

复制代码 代码如下:

#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

 

复制代码 代码如下:

#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:

复制代码 代码如下:

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 了呢?

示例

有了这个原理,接下来让我们来看一个实例:
下面先直接上源码:
 

 

复制代码 代码如下:


//
//  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的调用方式。
 

复制代码 代码如下:

[self sendeventoriginal:event];


  以上就是hook的实现,使用时我们只需要让testhookobject类执行一次初始话操作就可以了,执行完以后。uiwindow的sendevent消息就会会hook到我们的sendeventmyself中了。
 
  下面是调用代码:
 
 
 

 

 

复制代码 代码如下:

 

 

- (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完以后消息是否正常传递。经验证消息流转完全正常。

延伸 · 阅读

精彩推荐
  • IOSiOS开发之视图切换

    iOS开发之视图切换

    在iOS开发中视图的切换是很频繁的,独立的视图应用在实际开发过程中并不常见,除非你的应用足够简单。在iOS开发中常用的视图切换有三种,今天我们将...

    执着丶执念5272021-01-16
  • IOSiOS中滑动控制屏幕亮度和系统音量(附加AVAudioPlayer基本用法和Masonry简单使用)

    iOS中滑动控制屏幕亮度和系统音量(附加AVAudioPlayer基本用法和

    这篇文章主要介绍了iOS中滑动控制屏幕亮度和系统音量(附加AVAudioPlayer基本用法和Masonry简单使用)的相关资料,需要的朋友可以参考下...

    CodingFire13652021-02-26
  • IOSiOS中MD5加密算法的介绍和使用

    iOS中MD5加密算法的介绍和使用

    MD5加密是最常用的加密方法之一,是从一段字符串中通过相应特征生成一段32位的数字字母混合码。对输入信息生成唯一的128位散列值(32个字符)。这篇文...

    LYSNote5432021-02-04
  • IOSiOS实现控制屏幕常亮不变暗的方法示例

    iOS实现控制屏幕常亮不变暗的方法示例

    最近在工作中遇到了要将iOS屏幕保持常亮的需求,所以下面这篇文章主要给大家介绍了关于利用iOS如何实现控制屏幕常亮不变暗的方法,文中给出了详细的...

    随风13332021-04-02
  • IOSiOS自定义UICollectionViewFlowLayout实现图片浏览效果

    iOS自定义UICollectionViewFlowLayout实现图片浏览效果

    这篇文章主要介绍了iOS自定义UICollectionViewFlowLayout实现图片浏览效果的相关资料,需要的朋友可以参考下...

    jiangamh8882021-01-11
  • IOSiOS开发技巧之状态栏字体颜色的设置方法

    iOS开发技巧之状态栏字体颜色的设置方法

    有时候我们需要根据不同的背景修改状态栏字体的颜色,下面这篇文章主要给大家介绍了关于iOS开发技巧之状态栏字体颜色的设置方法,文中通过示例代码...

    梦想家-mxj8922021-05-10
  • IOSiOS中UILabel实现长按复制功能实例代码

    iOS中UILabel实现长按复制功能实例代码

    在iOS开发过程中,有时候会用到UILabel展示的内容,那么就设计到点击UILabel复制它上面展示的内容的功能,也就是Label长按复制功能,下面这篇文章主要给大...

    devilx12792021-04-02
  • IOS详解iOS中多个网络请求的同步问题总结

    详解iOS中多个网络请求的同步问题总结

    这篇文章主要介绍了详解iOS中多个网络请求的同步问题总结,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    liang199111302021-03-15