小白笔记("动态"hook某class的delegate方法")

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 测试吧!

其实你做的工作说白了就是把

%hook XXX
- (void)method1;
- (void)method2;
- (void)method3;
- (void)method4;
%end

给简化成了

DynamicHook(xxx, method, replacement, ...)

而并没有hook所有实现了delegate的类,是吧?

hook所有的!例如某个app中有classA, classB都实现了UIWebViewDelegate的方法,传统写需要分别写:
%hook classA

-(void)methodA;
-(void)methodB;

%end

%hook classA

-(void)methodA;
-(void)methodB;

%end

这种写法,不需要逆向出app中的classA和classB

但是你是怎么知道setDelegate:被谁调用的呢?

hook原理是,事先在tweak的xxx.m中准备"delegate方法模板(替换成的方法实现eg. new_methodA)"方法用于替换原来的delegate方法, tweak实际上直接hook的 UIWebView的setDelegate方法,然后在setDelegate方法中动态交换new,old方法的实现!
webview.delegate=obj的时候触发setDelegate, “函数指针的交换”(这里做了两件事,1,向[webview.delegate class]类中添加新方法new_methodA, 2.交换[webview.delegate class]的新旧方法old_methodA的实现为new_methodA的实现)

[self.delegate class]

我意思是

%hook SomeClass
- (void)setDelegate:(id)delegate
{
    ...
}
%end

你怎么知道这个SomeClass是什么呢?

1 个赞

例如:classA中有个UIWebView, 在classA中设置webView.delegate=classB_Ojb;
在UIWebView中的setDelegate中[self.delegate class]== classB_Ojb

然后接下来是向classB_Ojb中添加一个方法new_Method;
然后交换classB_Ojb中的new_Method, old_Method的实现

tweak中不需要直接获得这个SomeClass, 因为这种写法不是直接用%hook … %end完事!, 灵感来源于念茜的那个帖子里面的技巧!

我明白了,我们说的不是一回事。你相当于是已知setDelegate:的调用者,然后去根据调用者hook所有实现delegate的类;
我的意思是,我现在想hook所有的

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

没有任何已知条件。你的帖子不是以解决这种问题为出发点的,是吧?

1 个赞

例如:UIWebView有一个Property,delegate, 然后已知setter是setDelegate;
tweak中
%hook UIWebView

  • (void)setDelegate:(id)delegate
    {
    //1.[delegate class]中添加new_webViewDidStartLoad:
    // 2.交互webViewDidStartLoad跟new_webViewDidStartLoad的实现
    // 这两步是封装在tweak的某个class的方法中
    }
    %end

还有这种简化主要hook系统Delegate 的api,或者基类中的某个delegate
要确定的是从哪class中抛出了委托!这里就达到了只需要知道"委托人",不需要确定"被委托人",一个"委托人"可能有多个"拜托的人"

是的,你的方法需要已知委托人,而我之前以为连委托人都不需要

没那么强大~

不知道还有没有什么更好的办法实现hook delegate方法呢?

不大清楚,好像没有通用的方法