美文网首页
hook函数的三种方法

hook函数的三种方法

作者: 00d1ed2b53ae | 来源:发表于2018-10-11 09:53 被阅读10640次

    方法一

    hook已有公开头文件的类:

    首先写一个Utility函数:

    #import <objc/runtime.h>
    void exchangeMethod(Class aClass, SEL oldSEL, SEL newSEL){    
    Method oldMethod = class_getInstanceMethod(aClass, oldSEL);    
    assert(oldMethod);    
    Method newMethod = class_getInstanceMethod(aClass, newSEL);    
    assert(newMethod);    
    method_exchangeImplementations(oldMethod, newMethod);
    }
    
    

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

    - (void)webView:(id)arg1 didFinishLoadForFrame:(id)arg2;
    

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

    @interface UIWebView (Hook)+ (void)hook;
    - (void)hook_webView:(id)arg1 didFinishLoadForFrame:(id)arg2;
    @end
    @implementation 
    UIWebView (Hook)+ (void)hook{   
     // hook UIWebView中表示一个HTML的frame加载完毕的函数    
    exchangeMethod([UIWebView class],                   @selector(webView:didFinishLoadForFrame:),                   @selector(hook_webView:didFinishLoadForFrame:));
    }
    - (void)hook_webView:(id)arg1 didFinishLoadForFrame:(id)arg2{    
    // 因为交换了selector和implementation的映射,原样调一下本函数实际会调用被hook的函数。    
    [self hook_webView:arg1 didFinishLoadForFrame:arg2];    
    NSLog(@"webView:didFinishLoadForFrame:");
    }
    

    在程序启动的时候调用一下 [UIWebView hook] 即可。使用一个UIWebView打开一个网页,即会打印NSLog。

    方法二

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

    - (void)webView:(id)arg1 didFinishLoadForFrame:(id)arg2;
    

    嗯,是的,这个函数和UIWebView的一样,实际上就是UIWebBrowserView会再调用通知到UIWebView的同名函数。
    创建一个类,不要与被hook的类同名,例如加了个Hook前缀:

    @interface UIWebBrowserViewHook : NSObject
    + (void)hook;
    - (void)hook_webView:(id)arg1 didFinishLoadForFrame:(id)arg2;
    @end
    

    其中以hook_为前缀的新增函数的实现与方法一中相同,差别在类函数中:

    @implementation UIWebBrowserViewHook+ (void)hook{    
    Class aClass = objc_getClass("UIWebBrowserView");    
    SEL sel = @selector(hook_webView:didFinishLoadForFrame:);    
    // 为UIWebBrowserView增加函数    
    class_addMethod(aClass, sel, class_getMethodImplementation([self class], sel), "v@:@@");   
     // 交换实现    
    exchangeMethod(aClass, @selector(webView:didFinishLoadForFrame:), sel);
    }
    

    在程序启动的时候调用一下 [UIWebBrowserViewHook hook] 即可。使用一个UIWebView打开一个网页,即会打印NSLog。

    方法三

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

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

    @interface UIWebBrowserViewHook : NSObject
    + (void)hook;
    - (void)webView:(id)arg1 didFinishLoadForFrame:(id)arg2;
    @end
    

    需要用到另一个Utility函数:

    inline void replaceImplementation(Class newClass, Class hookedClass, SEL sel, IMP& oldImp){    
    Method old = class_getInstanceMethod(hookedClass, sel);    
    IMP newImp = class_getMethodImplementation(newClass, sel);  
    oldImp = method_setImplementation(old, newImp);}
    

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

    下面是实现:

    @implementation UIWebBrowserViewHookstatic IMP webView_didFinishLoadForFrame = NULL;+ (void)hook{    
    Class hookedClass = objc_getClass("UIWebBrowserView");    
    SEL sel = @selector(webView:didFinishLoadForFrame:);    
    replaceImplementation([self class], hookedClass, sel, webView_didFinishLoadForFrame);
    } 
    - (void)webView:(id)arg1 didFinishLoadForFrame:(id)arg2{    
    // 需要这样来调用被替换掉的原实现    
    webView_didFinishLoadForFrame(self, @selector(webView:didFinishLoadForFrame:), arg1, arg2);    
    NSLog(@"webView:didFinishLoadForFrame:");
    }
    @end
    

    在程序启动的时候调用一下 [UIWebBrowserViewHook hook] 即可。使用一个UIWebView打开一个网页,即会打印NSLog。三种方法的比较:最方便的当然是第一种,但需要是hook有公开头文件的类。方法二和方法三都新建了一个类,方法二需要描述selector的types,这个比较麻烦,如例子中的"v@:@@"表示返回值为void,第一和第二个参数都是id。方法三不用types,但要增加全局变量。

    相关文章

      网友评论

          本文标题:hook函数的三种方法

          本文链接:https://www.haomeiwen.com/subject/khivaftx.html