1.之前hook某classA的delegate方法的时候得先分析哪个classB实现了这个delegate的方法,然后就hook这个classB中实现的delegate方法!
操作起来实在不便!尤其是hook系统api的delegate方法, 有没有一个一劳永逸的方法呢?
2.切入正题:以hook UIWebViewDelegate 的几个方法为例!
(a)假设目标App中XXWebViewController.m中实现了UIWebViewDelegate的方法:
webViewDidStartLoad:
webViewDidFinishLoad:
webView:didFailLoadWithError:
webView:shouldStartLoadWithRequest:navigationType:
(b)传统做法是在tweak中hook这个XXWebViewController中的上述四个方法达到hook UIWebViewDelegate的目的!
(c)在tweak中实现一个全局方法,可以是在下一步的.m中 :灵感来源:http://blog.csdn.net/yiyaaixuexi/article/details/9374411
void exchangeMethod(Class originalClass, SEL original_SEL, Class replacedClass, SEL replaced_SEL)
{
// 1.这里是为了防止App中多次(偶数次)调用setDelegete时,导致后面的IMP替换又回到没替换之前的结果了!(这步是猜测,没来急在sample中测试)
static NSMutableArray * classList =nil;
if (classList == nil) {
classList = [NSMutableArray alloc] init];
}
NSString * className = [NSString stringWithFormat:@"%@__%@", NSStringFromClass(originalClass), NSStringFromSelector(original_SEL)];
for (NSString * item in classList) {
if ([className isEqualToString:item]) {
NSLog(@"tweak : setDelegate 2nd for (%@)==> return!", className);
return;
}
}
NSLog(@"tweak : setDelegate 1st for (%@)==> return!", className);
[classList addObject:className];
// 2.原delegate 方法
Method originalMethod = class_getInstanceMethod(originalClass, original_SEL);
assert(originalMethod);
//IMP originalMethodIMP = method_getImplementation(originalMethod);
// 3.新delegate 方法
Method replacedMethod = class_getInstanceMethod(replacedClass, replaced_SEL);
assert(replacedMethod);
IMP replacedMethodIMP = method_getImplementation(replacedMethod);
// 4.先向实现delegate的classB添加新的方法
if (!class_addMethod(originalClass, replaced_SEL, replacedMethodIMP, method_getTypeEncoding(replacedMethod))) {
NSLog(@"tweak : class_addMethod ====> Error! (replaced_SEL)");
}
else
{
NSLog(@"tweak : class_addMethod ====> OK! (replaced_SEL)");
}
// 5.重新拿到添加被添加的method,这部是关键(注意这里originalClass, 不replacedClass)
Method newMethod = class_getInstanceMethod(originalClass, replaced_SEL);
//IMP newMethodIMP = method_getImplementation(newMethod);
// 6.正式交换这两个函数指针的实现
method_exchangeImplementations(originalMethod, newMethod);
// 7.如果把第6步换成下面的方法,或者去掉5.6两步直接用下面的方法试试,有惊喜~
//method_exchangeImplementations(originalMethod, replacedMethod);
}
(d)新建一个DynamicUIWebViewDelegateHook类
实现4个方法:如
#pragma mark - UIWebViewDelegate
// webView Delegate Method
- (void)replaced_webViewDidStartLoad:(UIWebView *)webView
{
NSLog(@"tweak : Hook [UIWebView replaced_webViewDidStartLoad:]");
//(c)中的5.6步省略的话,调用这步将会死循环!!!
[self replaced_webViewDidStartLoad:webView];
}
- (void)replaced_webViewDidFinishLoad:(UIWebView *)webView
{
NSLog(@"tweak : Hook [UIWebView replaced_webViewDidFinishLoad:]");
[self replaced_webViewDidFinishLoad:webView];
}
// 剩下省略...
+ (void)enableHookUIWebViewDelegateMethod:(Class)aClass
{
exchangeMethod(aClass, @selector(webViewDidStartLoad:), [self class], @selector(replaced_webViewDidStartLoad:));
exchangeMethod(aClass, @selector(webViewDidFinishLoad:), [self class], @selector(replaced_webViewDidFinishLoad:));
exchangeMethod(aClass, @selector(webView:didFailLoadWithError:), [self class], @selector(replaced_webView:didFailLoadWithError:));
exchangeMethod(aClass, @selector(webView:shouldStartLoadWithRequest:navigationType:), [self class], @selector(replaced_webView:shouldStartLoadWithRequest:navigationType:));
}
(e)在hookUIWebView.xm中hook UIWebView类:
#import "DynamicUIWebViewDelegateHook.h"
%hook UIWebView
// WebView Method
- (void)setDelegate:(id)arg
{
NSLog(@"tweak : Hook [UIWebView setDelegate:]");
// 先调用原方法
%orig(arg);
// 动态hook
// webView 设置delegate的时候启用Dynamic hook
Class aClass = [self.delegate class];
[DynamicUIWebViewDelegateHook enableHookUIWebViewDelegateMethod:aClass];
}
%end
3.至此,收工!编译tweak 测试吧!