防止 iOS 上的反射 (objc/runtime)

prevent reflection (objc/runtime) on iOS

我正在开发一个处理敏感数据的静态库。使用库的开发者必须不能在库上使用反射。

在 Android 上,我们通过将 service 和 运行 的 service 开发到单独的进程中的 aar 文件来解决问题;(当该服务 运行 进入另一个进程,然后开发人员无法使用反射)但我想知道 iOS 中是否存在类似的东西?

我们可以将静态库执行到一个单独的进程中吗?如果不是,我们如何避免对我们的静态库进行反射?

例如:

        MyTestObject *obj = [[[myTestView alloc] init ];

        //===========================================   

        Class clazz = [obj class];
        u_int count;
        Ivar* ivars = class_copyIvarList(clazz, &count);
        NSMutableArray* ivarArray = [NSMutableArray arrayWithCapacity:count];
        for (int i = 0; i < count ; i++)
        {
            const char* ivarName = ivar_getName(ivars[i]);
            [ivarArray addObject:[NSString  stringWithCString:ivarName encoding:NSUTF8StringEncoding]];
        }
        free(ivars);

        objc_property_t* properties = class_copyPropertyList(clazz, &count);
        NSMutableArray* propertyArray = [NSMutableArray arrayWithCapacity:count];
        for (int i = 0; i < count ; i++)
        {
            const char* propertyName = property_getName(properties[i]);
            [propertyArray addObject:[NSString  stringWithCString:propertyName encoding:NSUTF8StringEncoding]];
        }
        free(properties);

        Method* methods = class_copyMethodList(clazz, &count);
        NSMutableArray* methodArray = [NSMutableArray arrayWithCapacity:count];
        for (int i = 0; i < count ; i++)
        {
            SEL selector = method_getName(methods[i]);
            const char* methodName = sel_getName(selector);
            [methodArray addObject:[NSString  stringWithCString:methodName encoding:NSUTF8StringEncoding]];
        }
        free(methods);

        NSDictionary* classDump = [NSDictionary dictionaryWithObjectsAndKeys:
                                   ivarArray, @"ivars",
                                   propertyArray, @"properties",
                                   methodArray, @"methods",
                                   nil];

        NSLog(@"%@", classDump);

        //======================================================

        int v2 = [[obj valueForKey:@"testValue"] intValue];

        SEL s = NSSelectorFromString(@"wannatTestIt");
        [obj performSelector:s];

MyTestObject 是我图书馆的 class。在第一行中,我从 class.

初始化了一个对象

在下一行中,我读取了 class 的变量、方法和 属性 列表并将其记录下来。这是结果:

    {
    ivars =     (
        testValue
    );
    methods =     (
        printTestValue,
        wannatTestIt,
        "initWithFrame:"
    );
    properties =     (
    );
}

wannaTestIt是私有方法,testValue是私有变量。所以我希望使用该库的开发人员无法访问它们。但是,由于库的使用者可以获取到名称,所以使用者最终可以调用该方法来读取iVar的值。

我怎样才能避免这种情况?

如果你想完全"prevent"反思那么,好吧,你必须使用不同的语言。反射是 Objective C 中的关键,"block" 或 "disable" 不可能。

但是,您可以通过混淆来使这个 运行 时间的信息对研究人员的用处大大降低。例如,看看这个工具:https://github.com/Polidea/ios-class-guard。这只是一个例子。我与这个特定项目无关,您可以自由选择不同的混淆器或编写自己的混淆器。

如果您需要的是将反射限制为仅 public API 甚至公开 一些 私有方法和 ivar(没有它们的实际名称) ) 对你来说不好那么你别无选择,只能用不同的语言编写你的敏感代码。您可以使用 Pimpl 设计模式来实现您想要的。这样,您的 classes 将只有 public 方法和一个私有 ivar _impl(或类似的东西)。其中 _impl 是用 C++ 编写的实现 class 的一个实例(或者 Objective C++ 如果您需要访问 ObjC APIs)和所有 public 方法就像一个代理。像这样:

- (NSInteger)computeSuperSensitiveStuffWithFoo:(Foo *)foo Bar:(Bar *)bar {
    return _impl->computeStuff(foo, bar);
}

这样,您所有的私有数据和方法都将封装在 MyClassImpl class 中。如果您将此类 class 的声明和实现保密(即不要将 MyClassImpl.h 文件与您的库一起分发)并使用像 C++ 这样的语言来实现它,那么您将实现您想要的。

另请注意,如果您选择 Objective C++,则 MyClassImpl 应该是 C++ class(使用 class 关键字声明)而不是 Objective C class(用 @interface/@end 块声明并在 @implementation/@end 块内实现)。否则,无论如何您的所有私人数据都可用于反射,但需要研究人员执行几个额外的步骤。