IOS开发-在广告机项目实现屏保功能时使用到的Hook函数,分享些关于hook函数的资料

废话不多说。直接上代码。

#import <Foundation/Foundation.h>


@interface HookObject :NSObject


//单位分钟

@property (nonatomic)NSInteger timeout;


@end



#import "HookObject.h"

#import <objc/objc.h>

#import <objc/runtime.h>


@interface HookObject ()


@property (nonatomic, strong)NSTimer *timer;


@end


@implementation HookObject


#define kCaptureTouch    @"kCaptureTouch"

#define kDefaultTimeOut  3 //单位分钟


- (id)init

{

    self = [super init];

    

    if (self)

    {

        // 获取到UIWindowsendEvent对应的method

        /**

         *  先获取UIWindowsendEvent:(事件分发)的方法

         *  再获取我们要覆盖sendEvent:方法的方法在selfsendEventMySelf

         **/

        Method sendEvent = class_getInstanceMethod([UIWindow class], @selector(sendEvent:));

        Method sendEventMySelf = class_getInstanceMethod([self class], @selector(sendEventHooked:));

        

        // 将目标函数的原实现绑定到sendEventOriginal方法上

        /**

         *  实现的步骤是:先获取sendEvent的方法实现

         *  [UIWindow class]添加sendEventOriginal:方法,该方法的实现和

         *  sendEventImp是一样的,并且获取sendEvend的参数和返回类型并添加到

         *  sendEventOriginal:中去

         **/

        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));

        

        //注册捕获到触摸的通知

        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(captureTouchNotification) name:kCaptureTouch object:nil];

        

        _timeout = kDefaultTimeOut;

    }

    

    return self;

}


//捕获到触摸的通知回调方法

- (void)captureTouchNotification

{

    NSLog(@"捕获到触摸");

    

    //固定套路,发消息通知取消播放屏保,然后重新设置定时器。外层做判断,如果本身没在播放,则不处理此消息即可。

    [[NSNotificationCenter defaultCenter] postNotificationName:kTimeOutStopVideo object:nil];

    

    [self.timer invalidate];

    self.timer = nil;

    self.timer = [NSTimer scheduledTimerWithTimeInterval:self.timeout*60 target:self selector:@selector(touchTimeOutAction) userInfo:nil repeats:NO];

}


//触摸超时回调方法

- (void)touchTimeOutAction

{

    NSLog(@"触摸超时,开始屏保");

    

    //固定套路,发消息通知开始播放屏保

    [[NSNotificationCenter defaultCenter] postNotificationName:kTimeOutPlayVideo object:nil];

}


//重写设置器,重新设置定时器

- (void)setTimeout:(NSInteger)timeout

{

    NSLog(@"重新设置超时时间");

    _timeout = timeout;

    

    [self.timer invalidate];

    self.timer = nil;

    self.timer = [NSTimer scheduledTimerWithTimeInterval:self.timeout*60 target:self selector:@selector(touchTimeOutAction) userInfo:nil repeats:NO];

}


/*

 * 截获到windowsendEvent

 * 我们可以先处理完以后,再继续调用正常处理流程

 */

- (void)sendEventHooked:(UIEvent *)event

{

    //捕获到触摸事件则开始计时,这里不能用self去访问 HookObject 当前这个类的类变量,原因如下说明,而应该发通知去通知这个类

    if (event.type == UIEventTypeTouches)

    {

        //至少有开始和结束2中状态,判断一种就行,否则会发送多个通知

        if ([[[event allTouches] anyObject] phase] == UITouchPhaseBegan)

        {

            [[NSNotificationCenter defaultCenter] postNotificationName:kCaptureTouch object:nil];

        }

    }

    

    //这个是UIWindow类的方法指定过来的,所以这里的self就不是HookObject的实例了,而是执行的UIWindow实例

    [self performSelector:@selector(sendEventOriginal:) withObject:event];

}


//移除观察者

- (void)dealloc

{

    [[NSNotificationCenter defaultCenter] removeObserver:self];

}


@end




然后黏贴些技术资料助于理解---资料来源http://blog.csdn.net/hursing/article/details/8688860



利用Objective-C运行时hook函数的三种方法

方法一,hook已有公开头文件的类:

首先写一个Utility函数:

[cpp]  view plain copy
  1. #import <objc/runtime.h>  
  2. inline void exchangeMethod(Class aClass, SEL oldSEL, SEL newSEL)  
  3. {  
  4.     Method oldMethod = class_getInstanceMethod(aClass, oldSEL);  
  5.     assert(oldMethod);  
  6.     Method newMethod = class_getInstanceMethod(aClass, newSEL);  
  7.     assert(newMethod);  
  8.     method_exchangeImplementations(oldMethod, newMethod);  
  9. }  

现在,目标是hook UIWebView没公开的函数

[cpp]  view plain copy
  1. - (void)webView:(id)arg1 didFinishLoadForFrame:(id)arg2;  

因为已知类的声明,所以可以使用category:

[cpp]  view plain copy
  1. @interface UIWebView (Hook)  
  2. + (void)hook;  
  3. - (void)hook_webView:(id)arg1 didFinishLoadForFrame:(id)arg2;  
  4. @end  
  5. @implementation UIWebView (Hook)  
  6. + (void)hook  
  7. {  
  8.     // hook UIWebView中表示一个HTML的frame加载完毕的函数  
  9.     exchangeMethod([UIWebViewclass],  
  10.                    @selector(webView:didFinishLoadForFrame:),  
  11.                    @selector(hook_webView:didFinishLoadForFrame:));  
  12. }  
  13. - (void)hook_webView:(id)arg1 didFinishLoadForFrame:(id)arg2  
  14. {  
  15.     // 因为交换了selector和implementation的映射,原样调一下本函数实际会调用被hook的函数。  
  16.     [self hook_webView:arg1 didFinishLoadForFrame:arg2];  
  17.     NSLog(@"webView:didFinishLoadForFrame:");  
  18. }  
在程序启动的时候调用一下 [UIWebView hook] 即可。使用一个UIWebView打开一个网页,即会打印NSLog。


方法二,hook没有公开头文件的类,需要另建一个类作为新函数载体,然后先为被hook的类增加函数,再替换:
UIWebView体系中有一个类叫UIWebBrowserView,它是真正显示网页的UIView,并有部分函数是作为WebCore向外发送回调信息的接收者。
现我们去hook UIWebBrowserView的这个函数:

[cpp]  view plain copy
  1. - (void)webView:(id)arg1 didFinishLoadForFrame:(id)arg2;  
嗯,是的,这个函数和UIWebView的一样,实际上就是UIWebBrowserView会再调用通知到UIWebView的同名函数。
创建一个类,不要与被hook的类同名,例如加了个Hook后缀:
[cpp]  view plain copy
  1. @interface UIWebBrowserViewHook : NSObject  
  2. + (void)hook;  
  3. - (void)hook_webView:(id)arg1 didFinishLoadForFrame:(id)arg2;  
  4. @end  
 
其中以hook_为前缀的新增函数的实现与方法一中相同,差别在类函数中: 
[cpp]  view plain copy
  1. @implementation UIWebBrowserViewHook  
  2. + (void)hook  
  3. {  
  4.     Class aClass = objc_getClass("UIWebBrowserView");  
  5.     SEL sel = @selector(hook_webView:didFinishLoadForFrame:);  
  6.     // 为UIWebBrowserView增加函数  
  7.     class_addMethod(aClass, sel, class_getMethodImplementation([self class], sel), "v@:@@");  
  8.     // 交换实现  
  9.     exchangeMethod(aClass, @selector(webView:didFinishLoadForFrame:), sel);  
  10. }  
在程序启动的时候调用一下 [UIWebBrowserViewHook hook] 即可。使用一个UIWebView打开一个网页,即会打印NSLog。


方法三,hook没有公开头文件的类,另建一个类作为新函数载体,用新函数替换旧函数,并把旧函数保存到静态变量里:

继续以UIWebBrowserView为例子。注意新函数可以与被hook的函数同名

[cpp]  view plain copy
  1. @interface UIWebBrowserViewHook : NSObject  
  2. + (void)hook;  
  3. - (void)webView:(id)arg1 didFinishLoadForFrame:(id)arg2;  
  4. @end  
需要用到另一个Utility函数:
[cpp]  view plain copy
  1. inline void replaceImplementation(Class newClass, Class hookedClass, SEL sel, IMP& oldImp)  
  2. {  
  3.     Method old = class_getInstanceMethod(hookedClass, sel);  
  4.     IMP newImp = class_getMethodImplementation(newClass, sel);  
  5.     oldImp = method_setImplementation(old, newImp);  
  6. }  

当两个selector不同名时,以上函数再增加一个参数即可。

下面是实现:

[cpp]  view plain copy
  1. @implementation UIWebBrowserViewHook  
  2. static IMP webView_didFinishLoadForFrame = NULL;  
  3. + (void)hook  
  4. {  
  5.     Class hookedClass = objc_getClass("UIWebBrowserView");  
  6.     SEL sel = @selector(webView:didFinishLoadForFrame:);  
  7.     replaceImplementation([self class], hookedClass, sel, webView_didFinishLoadForFrame);  
  8. }  
  9.   
  10. - (void)webView:(id)arg1 didFinishLoadForFrame:(id)arg2  
  11. {  
  12.     // 需要这样来调用被替换掉的原实现  
  13.     webView_didFinishLoadForFrame(self, @selector(webView:didFinishLoadForFrame:), arg1, arg2);  
  14.     NSLog(@"webView:didFinishLoadForFrame:");  
  15. }  
  16. @end  
在程序启动的时候调用一下 [UIWebBrowserViewHook hook] 即可。使用一个UIWebView打开一个网页,即会打印NSLog。


三种方法的比较:

最方便的当然是第一种,但需要是hook有公开头文件的类。

方法二和方法三都新建了一个类,方法二需要描述selector的types,这个比较麻烦,如例子中的"v@:@@"表示返回值为void,第一和第二个参数都是id。方法三不用types,但要增加全局变量。


Objective-C的runtime参考资料:
http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Introduction/Introduction.html#


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值