zoukankan      html  css  js  c++  java
  • 利用Objective-C运行时hook函数的三种方法

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

    首先写一个Utility函数:

    1. #import <objc/runtime.h>  
    2. 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没公开的函数

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

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

     

    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([UIWebView class],  
    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的这个函数:

     

    1. - (void)webView:(id)arg1 didFinishLoadForFrame:(id)arg2;  
    嗯,是的,这个函数和UIWebView的一样,实际上就是UIWebBrowserView会再调用通知到UIWebView的同名函数。
    创建一个类,不要与被hook的类同名,例如加了个Hook前缀:
    1. @interface UIWebBrowserViewHook : NSObject  
    2. + (void)hook;  
    3. - (void)hook_webView:(id)arg1 didFinishLoadForFrame:(id)arg2;  
    4. @end  
    
    其中以hook_为前缀的新增函数的实现与方法一中相同,差别在类函数中:
    
    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的函数同名

    1. @interface UIWebBrowserViewHook : NSObject  
    2. + (void)hook;  
    3. - (void)webView:(id)arg1 didFinishLoadForFrame:(id)arg2;  
    4. @end  
    需要用到另一个Utility函数:
    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不同名时,以上函数再增加一个参数即可。

    下面是实现:

    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#//apple_ref/doc/uid/TP40008048

     
     
  • 相关阅读:
    SQL大语句——实现分页,select top后面跟变量出错
    jQuery异步请求(如getJSON)跨域解决方案
    Debug常用命令
    清华操作系统实验--80x86汇编基础
    在Windows10中运行debug程序
    恢复Windows10应用商店
    最少硬币问题
    嵌套矩形问题
    清华大学操作系统实验准备--挖坑
    动态规划入门-数字三角形
  • 原文地址:https://www.cnblogs.com/feng9exe/p/6015959.html
Copyright © 2011-2022 走看看