prevent reflection (objc/runtime) on iOS

2019-06-17 08:13发布

问题:

I am working on a static library that handles sensitive data. It is imperative that the developer using the library can not use reflection on the library.

On Android, we solve the problem by developing an aar file with services and run the service into separate process;(When the service is running into another process then the developer can not use reflection) but I am wondering if something similar exists in iOS ?

Can we execute a static library into a separate process? if not, how we can avoid reflection on our static libraries?

For example:

        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 is a class from my library. In the first line, I initialize an object from this class.

In the next line, I read the variables, methods and property list of the class and log it. Here is the result:

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

wannaTestIt is a private method and testValue is a private variable. So I expect that the developer that uses the library can not access them. However, because the user of the library could get the name, the user can ultimately call the method to read the value of the iVar.

How can I prevent this?

回答1:

If you want to completely "prevent" reflection then, well, you have to use a different language. Reflection is a key thing in Objective C and it's not possible to "block" or "disable" it.

However, you can make this run-time information much less useful for the researcher by obfuscating it. For example, take a look at this tool: https://github.com/Polidea/ios-class-guard. This is just an example. I'm not related to this particular project and you can freely chose a different obfuscator or write your own.

If what you need is to limit reflection to public API only and even disclosing a number of private methods and ivars (without their actual names) is not okay for you then you have no other choice than writing you sensitive code in a different language. You can use Pimpl design pattern to achieve what you want. This way, your classes would only have public methods and a single private ivar _impl (or something like that). Where _impl is an instance of the implementation class that is written in C++ (or Objective C++ if you need access to ObjC APIs) and all public methods act like a proxy. Something like this:

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

This way all your private data and methods would be encapsulated in the MyClassImpl class. If you keep declaration and implementation of such class in private (i.e. do not distribute MyClassImpl.h file with your library) and use language like C++ for implementing it, then you will achieve what you want.

Also note that if you chose Objective C++ then MyClassImpl should be a C++ class (declared with class keyword) and not Objective C class (declared with @interface/@end block and implemented inside @implementation/@end block). Otherwise all you private data will be available for reflection anyway, but would require a couple of additional steps from the researcher.