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

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

首先写一个Utility函数:

#import 

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 UIWebBrowserViewHook

static 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,但要增加全局变量。

Objective-C的runtime参考资料:

http://developer.apple.com/library/iOS/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40008048

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值