美文网首页Objective-C RunTime
OCRuntime方法hook踩坑,hook具体类的方法

OCRuntime方法hook踩坑,hook具体类的方法

作者: 闭家锁 | 来源:发表于2018-03-29 01:32 被阅读68次

    前提是想hook NSString类的某些实例方法,随便测试了两个方法,一个是原类中的方法:isEqual,另外一个是分类方法:isEqualToString,测试hook是否成功也很简单,分别调用一个字符串的这两个方法,看是否能打印出hook方法中的Log。

    最初的hook代码如下:

      @implementation NSString(isequal)
      + (void)load {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            Class class = [self class];
            SEL originalSelector = @selector(isEqualToString:);
            SEL swizzledSelector = @selector(zx_isEqualToString:);
        
            Method originalMethod = class_getInstanceMethod(class, originalSelector);
            Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
        
            BOOL success = class_addMethod(class,
                                       originalSelector,
                                       method_getImplementation(swizzledMethod),
                                       method_getTypeEncoding(swizzledMethod));
            if (success) {
                class_replaceMethod(class,
                                swizzledSelector,
                                method_getImplementation(originalMethod),
                                method_getTypeEncoding(originalMethod));
           } else {
                method_exchangeImplementations(originalMethod, swizzledMethod);
           }
        
           originalSelector = @selector(isEqual:);
           swizzledSelector = @selector(zx_isEqual:);
        
           originalMethod = class_getInstanceMethod(class, originalSelector);
           swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
        
           success = class_addMethod(class,
                                  originalSelector,
                                  method_getImplementation(swizzledMethod),
                                  method_getTypeEncoding(swizzledMethod));
           if (success) {
               class_replaceMethod(class,
                                swizzledSelector,
                                method_getImplementation(originalMethod),
                                method_getTypeEncoding(originalMethod));
           } else {
               method_exchangeImplementations(originalMethod, swizzledMethod);
           }
         });
       }
    
       - (BOOL)zx_isEqualToString:(NSString*)aString {
             NSLog(@"调用了zx_isEqualToString");
             return [self zx_isEqualToString:aString];
       }
    
       - (BOOL)zx_isEqual:(id)object{
            NSLog(@"调用了zx_isEqual");
            return [self zx_isEqual:object];
       }
    
       @end
    

    测试代码:

    NSString* strA = @"aaa";
    NSString* strB = @"aaa";
    if ([strA isEqualToString:strB]) {
        NSLog(@"strOrg isEqualToString strCop!");
    }
    if ([strA isEqual:strB]) {
        NSLog(@"strOrg isEqual strCop!");
    }
    

    这样看貌似一切都没有什么问题,run起来看打印结果,当调用[strOrg isEqualToString:strB]时,应当会调用hook方法,并打印“调用了zx_isEqualToString”这句话,当执行[strOrg isEqual:strB]时,应当打印hook方法中的“调用了zx_isEqual”这句话,但结果如何,实际run一下便知

    但实际当执行[strA isEqualToString:strB]时,控制台并没有打印任何内容,执行[strOrg isEqual:strB]时,控制台也没有打印任何内容,这就奇怪了,难道hook代码有问题?方法并没有实现交换?

    同样的hook代码,去hook UIView中的方法:

    @implementation UIView(hook)
    + (void)load {
      static dispatch_once_t onceToken;
      dispatch_once(&onceToken, ^{
          Class class = [self class];
          SEL originalSelector = @selector(initWithFrame:);
          SEL swizzledSelector = @selector(zx_initWithFrame:);
        
          Method originalMethod = class_getInstanceMethod(class, originalSelector);
          Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
        
          BOOL success = class_addMethod(class,
                                       originalSelector,
                                       method_getImplementation(swizzledMethod),
                                       method_getTypeEncoding(swizzledMethod));
          if (success) {
             class_replaceMethod(class,
                                swizzledSelector,
                                method_getImplementation(originalMethod),
                                method_getTypeEncoding(originalMethod));
          } else {
             method_exchangeImplementations(originalMethod, swizzledMethod);
          }
        
          originalSelector = @selector(removeFromSuperview);
          swizzledSelector = @selector(zx_removeFromSuperview);
        
          originalMethod = class_getInstanceMethod(class, originalSelector);
          swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
        
          success = class_addMethod(class,
                                  originalSelector,
                                  method_getImplementation(swizzledMethod),
                                  method_getTypeEncoding(swizzledMethod));
          if (success) {
              class_replaceMethod(class,
                                swizzledSelector,
                                method_getImplementation(originalMethod),
                                method_getTypeEncoding(originalMethod));
          } else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
          }
        });
       }
    
    - (instancetype)zx_initWithFrame:(CGRect)frame {
        //hook原类方法
        NSLog(@"调用了hook 原类实例方法zx_initWithFrame");
        return [self zx_initWithFrame:frame];
    }
    
    - (NSUInteger)zx_removeFromSuperview {
        //hook分类方法
        NSLog(@"调用了hook 分类实例方法zx_removeFromSuperview");
        return [self zx_removeFromSuperview];
    }
    @end
    

    测试代码很简单,分别执行initWithFrame和removeFromSuperview,最后查看打印结果:

    UIView* view = [[UIView alloc] initWithFrame:CGRectZero];
    [view removeFromSuperview];
    
    实际run起来,查看控制台打印,和预期一致,分别调用了hook后的方法实现: fangfahook.png

    到此就更加奇怪了,同样的代码,为什么用在UIView的方法中,hook一点问题都没有,但是在NSString的方法中就不生效呢?问题到底在哪?

    最终打断点,观察字符串有什么不同,在类型信息中显示
    strA __NSCFConstantString * @"aaa" 0x000000010d4893a8
    strB __NSCFConstantString * @"aaa" 0x000000010d4893c8

    最关键的就是__NSCFConstantString,这个类型,说明strA,strB已经不是NSString,而是一个具体的String类型__NSCFConstantString,最终调整hook代码的Class class = [self class];这句,改成Class class = NSClassFromString(@"__NSCFConstantString");其他代码不变,只是把获取的类指向这个具体的类型。

    再次打断点调试,所有hook方法都被正确调用,控制台如预期输出打印,结论就是当需要hook类似NSString这种类型的方法时要确定被调用的实例类型是准确的具体类型,否则hook的可能不是你所希望的类的实例方法。

    类似的情况还有NSArray已及NSDictionary,他们对应的hook类型分别是__NSNSArrayI和__NSDictionaryI。

    具体原因分析可能是NSString根据不同的实例化方法,会返回具体不同类型的String,例如__NSCFConstantString这种具体的String类型,类似于工厂,而hook需要针对具体类型进行hook才能达到效果。

    相关文章

      网友评论

        本文标题:OCRuntime方法hook踩坑,hook具体类的方法

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