【iOS】检查类函数是否被覆盖

2023-02-22  本文已影响0人  24c41111e47b

检查类函数是否被覆盖,一般包含分类覆盖和hook覆盖,这里给出代码快速检测是否被覆盖


1.检测分类函数列表

在要检测的类中引入头文件

#import <objc/runtime.h>
#import <objc/message.h>

在要检测的类中实现如下函数, 并调用进行打印类函数列表(原类 + 分类)

- (void)runTests

{
    unsigned int count;
    Method *methods = class_copyMethodList([self class], &count);
    for (int i = 0; i < count; i++)
    {
        Method method = methods[i];
        SEL selector = method_getName(method);
        NSString *name = NSStringFromSelector(selector);
//        if ([name hasPrefix:@"test"])

        NSLog(@"方法 名字 ==== %@",name);
        if (name)
        {
            //avoid arc warning by using c runtime

//            objc_msgSend(self, selector);
        }

        NSLog(@"Test '%@' completed successfuly", [name substringFromIndex:4]);

    }
}

2.检测是否被Hook

方法Hook一般是在Load函数中实现的,所以可以在AppDelegate.m中进行检测
实现如下函数

BOOL isSiwwzledOrOverridden(Class cls, SEL selector) {
    
    NSMutableDictionary *offsetDict = @{}.mutableCopy;
    unsigned classCount = 0;
    Class *allClasses = objc_copyClassList(&classCount);
    for (unsigned classIndex = 0; classIndex < classCount; ++classIndex) {
        @autoreleasepool {
            Class cls = allClasses[classIndex];
            //只管UI NS开头的类
            if ([NSStringFromClass(cls) hasPrefix:@"UI"] || [NSStringFromClass(cls) hasPrefix:@"NS"]) {
                unsigned methodCount = 0;
                Method *methods = class_copyMethodList(cls, &methodCount);
                for (unsigned methodIndex = 0; methodIndex < methodCount; ++methodIndex) {
                    Method mtd = methods[methodIndex];
                    NSString *mtdString = [NSString stringWithUTF8String:sel_getName(method_getName(mtd))];
                    //_开头的内部方法忽略
                    if ([mtdString hasPrefix:@"_"]){
                        continue;
                    }

                    IMP imp = method_getImplementation(mtd);
                    int offset = (long) cls - (long) imp;
                    offsetDict[[NSString stringWithFormat:@"[%@ %s]", NSStringFromClass(cls), sel_getName(method_getName(mtd))]] = @(offset);
                }
            }
        }
    }
    
    //省略部分代码。。。
    if (offsetDict){
        NSNumber *num = offsetDict[[NSString stringWithFormat:@"[%@ %s]", NSStringFromClass(cls), sel_getName(selector)]];
        if (num == nil){
            NSLog(@"Could not find selector!");
            return NO;
        }
        IMP imp = [cls instanceMethodForSelector:selector];
        int offset = (long) cls - (long) imp;

        if (offset != [num integerValue])
            return YES;
    }
    return NO;
}

然后调用如下函数检查,这里以检查 UIViewControllerviewDidLoad方法为例

BOOL ret = isSiwwzledOrOverridden([UIViewController class], @selector(viewDidLoad));
// ret YES 表示被Hook或者被覆盖
// ret NO 表示没有被Hook
上一篇 下一篇

猜你喜欢

热点阅读