objective-c runtime安全措施之二:反注入

反注入:在类函数被调用前做完整性检测(预防应用自定义函数或apple标准库函数被修改或替换)
原理:调用dladdr()函数检查类方法的基本信息是否合法
例子1:检查Foundation框架类中NSArray的description方法的基本信息(注释掉__start____end__间的内容)


#import <dlfcn.h>

#import <objc/runtime.h>


- (void)main

{

    //__start__

    NSArray *array = @[@"obj1"];

    NSLog(@"description1:%@",array.description);

    

    //动态方法的处理在objective-c的动态特性一文中有做介绍

    SEL suppIOSEL = NSSelectorFromString(@"mydescription");

    IMP suppIOIMP = imp_implementationWithBlock(^(id _self) {

        NSLog(@"mydescription:%@",_self);

        return _self;

    });

    ReplaceMethod(NSArray.class, NSSelectorFromString(@"description"), suppIOSEL, suppIOIMP);

    

    NSLog(@"description2:%@",array.description);

    //__end__


    IMP imp = class_getMethodImplementation(objc_getClass("NSArray"),

                                            sel_registerName("description"));

    Dl_info info;

    if (dladdr(imp, &info)) {

        NSLog(@"dli_fname: %s\n", info.dli_fname);

        NSLog(@"dli_sname: %s\n", info.dli_sname);

        NSLog(@"dli_fbase: %d\n", (int)info.dli_fbase);

        NSLog(@"dli_saddr: %d\n", (int)info.dli_saddr);

    } else {

        NSLog(@"error: can't find that symbol.\n");

    }

}


void ReplaceMethod(Class c, SEL orig, SEL newSel, IMP impl) {

    Method method = class_getInstanceMethod(c, orig);

    if (!class_addMethod(c, newSel, impl, method_getTypeEncoding(method))) {

    }else SwizzleMethod(c, orig, newSel);

}


static void SwizzleMethod(Class c, SEL orig, SEL new) {

    Method origMethod = class_getInstanceMethod(c, orig);

    Method newMethod = class_getInstanceMethod(c, new);

    if (class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) {

        class_replaceMethod(c, new, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));

    }else {

        method_exchangeImplementations(origMethod, newMethod);

    }

}


在Mac OS上使用gcc编译
$ gcc -o main main.m -lobjc -framework Foundation

然后运行该程序和观察输出,这些信息(地址空间、文件名、符号名)可以确认该函数来源、是否合法


例子2:使用dladdr函数检查类中的所有方法的通用代码


#import <dlfcn.h>

#import <objc/runtime.h>


static inline BOOL validate_methods(const char *, const char *) __attribute__((always_inline));


BOOL validate_methods(const char *cls, const char *fname) {

    Class aClass = objc_getClass(cls);

    Method *methods;

    unsigned int nMethods;

    Dl_info info;

    IMP imp;

    char buf[128];

    Method m;

    if (!aClass)

        return NO;

    methods = class_copyMethodList(aClass, &nMethods);

    while(nMethods--) {

        m = methods[nMethods];

        NSLog(@"validating [ %s %s ]\n",

               (const char *) class_getName(aClass),

               (const char *) sel_getName(method_getName(m)));

        imp = method_getImplementation(m);

        if (!imp) {

            NSLog(@"error: method_getImplementation(%s) failed\n",

                   (const char *) sel_getName(method_getName(m)));

            free(methods);

            return NO;

        }

        if (!dladdr(imp, &info)) {

            NSLog(@"error: dladdr() failed for %s\n",

                   (const char *) sel_getName(method_getName(m)));

            free(methods);

            return NO;

        }

        /* Validate image path */

        if (strcmp(info.dli_fname, fname))

            goto FAIL;

        /* Validate class name in symbol */

        snprintf(buf, sizeof(buf), "[%s ",

                 (const char *) class_getName(aClass));

        if (strncmp(info.dli_sname+1, buf, strlen(buf)))

        {

            snprintf(buf, sizeof(buf), "[%s(",

                     (const char *) class_getName(aClass));

            if (strncmp(info.dli_sname+1, buf, strlen(buf)))

                goto FAIL;

        }

        /* Validate selector in symbol */

        snprintf(buf, sizeof(buf), " %s]",

                 (const char *) sel_getName(method_getName(m)));

        if (strncmp(info.dli_sname + (strlen(info.dli_sname) - strlen(buf)),

                    buf, strlen(buf)))

        {

            goto FAIL;

        }

    }

    return YES;

FAIL:

    NSLog(@"method %s failed integrity test:\n",

           (const char *) sel_getName(method_getName(m)));

    NSLog(@"dli_fname: %s\n", info.dli_fname);

    NSLog(@"dli_sname: %s\n", info.dli_sname);

    NSLog(@"dli_fbase: %d\n", (int)info.dli_fbase);

    NSLog(@"dli_saddr: %d\n", (int)info.dli_saddr);

    free(methods);

    return NO;

}

- (void)main

{

    NSString *root = @"";

    IF_SIMULATOR(root = [[NSProcessInfo processInfo] environment][@"IPHONE_SIMULATOR_ROOT"];)

    const char* fname = [[root stringByAppendingPathComponent:

                          @"/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation"]

                         fileSystemRepresentation];

    BOOL isValidate = validate_methods("NSArray", fname);

    ....

}

【注意】
1.    重命名检查函数(例如不要用validate_methods这样具有明显意义的名字)
2.    在应用的多处加入检查函数,任何与敏感数据交互的方法在执行前都需要检查
3.    例子中的logging和NSLog语句只是用来调试,代码正式发布前要移除
4.    验证完整性只是提高了攻击的门槛,不能完全防御 黑客 ,需要综合应用多种技巧,例如思路一中的反调试技巧,及马上要介绍的反汇编技巧。

你可能感兴趣的:(Objective-C)