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?
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.
Related
Context
I have an instance of class called Solution and I have a function name as a string functionName that I want to call on the Solution instance solutionInstance. I have the parameters for the function in an array and I'd like to pass those as well.
I am using the Swift compiler to compile all of my .swift files together (swiftc with a files enumerated and then -o and the output file name) then I run the final output.
Python Example
Here is how I do this in Python:
method = getattr(solutionInstance, functionName) # get method off of instance for function
programOutput = method(*testInputsParsed) # pass the list of parameters & call the method
Purpose
This is server-side code that runs in a container to run a user's code. This code lives in a "Driver" main.swift file that calls the methods and orchestrates testing.
Problem
Swift is statically typed and I've been searching around and most sources say there is limited reflection support in Swift (and suggest to "reach into Objective-C" to get the functionality desired).
Swift is not my native language (TypeScript/JavaScript, Java, Python strongest, then C# and C++ mild, then just implementing Swift code for this feature now) so I'm not sure what that means and I haven't been able to find a definitive answer.
Question
How can I call a function by its name on a Solution class instance (it implements no protocols, at least by me) and pass an array of parameters in Swift (using reflection)? How does my setup need to change to make this happen (importing libraries, etc.)
Thank you!
Referenced Posts
Calling Method using reflection
Does Swift support reflection?
Call a method from a String in Swift
How to invoke a class method using performSelector() on AnyClass in Swift?
Dynamically call a function in Swift
First of all, as you noted Swift doesn't have full reflection capabilities and rely on the coexisting ObjC to provide these features.
So even if you can write pure Swift code, you will need Solution to be a subclass of NSObject (or implement NSObjectProtocol).
Playground sample:
class Solution: NSObject {
#objc func functionName(greeting: String, name: String) {
print(greeting, name)
}
}
let solutionInstance = Solution() as NSObject
let selector = #selector(Solution.functionName)
if solutionInstance.responds(to: selector) {
solutionInstance.perform(selector, with: "Hello", with: "solution")
}
There are other points of concern here:
Swift's perform is limited to 2 parameters
you need to have the exact signature of the method (#selector here)
If you can stick an array in the first parameters, and alway have the same signature then you're done.
But if you really need to go further you have no choice than to go with ObjC, which doesn't work in Playground.
You could create a Driver.m file of the like:
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
id call (NSObject *callOn, NSString *callMethod, NSArray <NSObject *>*callParameters)
{
void *result = NULL;
unsigned int index, count;
Method *methods = class_copyMethodList(callOn.class, &count);
for (index = 0; index < count; ++index)
{
Method method = methods[index];
struct objc_method_description *description = method_getDescription(method);
NSString *name = [NSString stringWithUTF8String:sel_getName(description->name)];
if ([name isEqualToString:callMethod])
{
NSMethodSignature *signature = [NSMethodSignature signatureWithObjCTypes:description->types];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
NSObject *parameters[callParameters.count];
for (int p = 0; p < callParameters.count; ++p) {
parameters[p] = [callParameters objectAtIndex:p];
[invocation setArgument:¶meters[p] atIndex:p + 2]; // 0 is self 1 is SEL
}
[invocation setTarget:callOn];
[invocation setSelector:description->name];
[invocation invoke];
[invocation getReturnValue:&result];
break;
}
}
free(methods);
return (__bridge id)result;
}
Add it to a bridging-header (for Swift to know about what is in ObjC):
// YourProjectName-Bridging-Header.h
id call (NSObject *callOn, NSString *callMethod, NSArray *callParameters);
And call it with a Solution.swift like this:
import Foundation
class Solution: NSObject {
override init() {
super.init()
// this should go in Driver.swift
let result = call(self, "functionNameWithGreeting:name:", ["Hello", "solution"])
print(result as Any)
}
#objc
func functionName(greeting: String, name: String) -> String {
print(greeting, name)
return "return"
}
}
output:
Hello solution
Optional(return)
Edit: compilation
To compile both ObjC and Swift on the command line you can first compile ObjC to an object file:
$ cc -O -c YouObjCFile.m
Then compile your Swift project with the bridging header and the object file:
$ swiftc -import-objc-header ../Your-Bridging-Header.h YouObjCFile.o AllYourSwiftFiles.swift -o program
working sample
Say, I have defined a private variable isStarted in MyService class & this class is a singleton:
#implementation MyService {
BOOL isStarted;
}
...
#end
My question:
Without creating property & accessors, is there a way to access this private variable from another class after I get a instance of this class [MyService sharedInstance]. If there is a way, could you please show me how? Thanks in advance.
(Please don't talk about "it is not recommended to do so" etc. I understand that. I am just curious about the possibility of accessing private field in objective-c in general. )
Hi again=) There is a way - you can get your all class variables on the runtime and there search needed one. Still that is hard way and not recommended. Why do you not want to use properties?
- (void*)getValueWithName:(const char*)varName forClass:(NSString*)className {
unsigned int varCount;
Class theClass = NSClassFromString(className);
Ivar *vars = class_copyIvarList(theClass, &varCount);
for (int i = 0; i < varCount; i++) {
Ivar var = vars[i];
const char* name = ivar_getName(var);
if (strcmp(name, varName) == 0) {
ptrdiff_t offset = ivar_getOffset(var);
unsigned char* bytes = (unsigned char *)(__bridge void*)self;
free(vars);
return (bytes+offset);
}
}
free(vars);
return NULL;
}
And casting value:
NSInteger t = *(NSInteger*)[self getValueWithName:"p" forClass:#"ViewController"];
BOOL checkBool = *(BOOL*)[self getValueWithName:"boolean" forClass:#"ViewController"];
NSString *checkString = *((__unsafe_unretained NSString **)([self getValueWithName:"string" forClass:#"ViewController"]));
I'm not sure if being a singleton makes this different, but here is a similar question.
According to this post, you can.
Accessing private variables from an external class - iOS
It's kind of old, but I'd give it a shot.
Specifically, this line
[object valueForKey:#"variable_name"];
I have a very specific issue where, if I have a swift class which inherits from an objective-c class, and it has a dynamic property.
Now, on the +initialize, I am injecting getter and setters into the swift(:NSObject) class, and these work no problem, except for a slight issue when setting values from an init overload.
So, my swift class looks like this:
class TestClass: BaseClass {
dynamic var testStringProperty: String?
init(initialValue: String) {
super.init()
self.testStringProperty = initialValue;
// does not trigger the get/set methods
// a po self.testStringProperty will output 'nil'
let xyz = self.testStringProperty;
// xyz is actually set to the value of initialValue, but it does not trigger the getter.
}
}
And the objective-c class that bases the swift is as follows:
static id storedVar;
#implementation BaseClass
+ (void)initialize {
// we are going to exchange getter/setter methods for a property on a SwiftClass, crude but demonstrates the point
if(![[self description] isEqualToString:#"BaseClass"]) {
IMP reader = (IMP)getPropertyIMP;
IMP writer = (IMP)setPropertyIMP;
const char* type = "#";
NSString* propertyName = #"testStringProperty";
IMP oldMethod = class_replaceMethod([self class], NSSelectorFromString(propertyName), reader, [[NSString stringWithFormat:#"%s#:", type] UTF8String]);
NSString* setMethod = [NSString stringWithFormat:#"set%#%#:", [[propertyName substringToIndex:1] uppercaseString], [propertyName substringFromIndex:1]];
oldMethod = class_replaceMethod([self class], NSSelectorFromString(setMethod), writer, [[NSString stringWithFormat:#"v#:%s",type] UTF8String]);
}
}
static id getPropertyIMP(id self, SEL _cmd) {
return storedVar;
}
static void setPropertyIMP(id self, SEL _cmd, id aValue) {
storedVar = aValue;
}
#end
Long story short, whilst in the call to init(initialValue: String), the getters and setters are not triggered, but immediately after the call to init has completed they work.
This is despite the call to initialize completing successfully and the methods being replaced.
But outside of the init function, the get/set behave as expected.
Here is where it get's called.
let test = TestClass(initialValue: "hello");
test.testStringProperty = "hello"
A po test.testStringProperty after the creation of the object, will output nil. But the subsequent assignment, triggers all the correct methods.
It only fails when assigning within the init. Everywhere else it works like a charm.
I would like to get this to work within the initializer if possible, i'm not sure if there is another way to work around it.
Here is a link to the sample app that replicates the issue:
https://www.dropbox.com/s/5jymj581yps799d/swiftTest.zip?dl=0
I'm currently trying to learn how to use private frameworks, just to gain some deeper understanding of these kinds of things.
So, while experimenting with ToneLibrary.framework (See classdump), I noticed that I am able to work with existing instances, but am unable to instantiate a class object from a private framework like I normally would myself.
For example, from the framework mentioned above, I have imported and TLToneManager.h and TLITunesTone.h.
I can do:
NSArray *tones = [[TLToneManager sharedRingtoneManager] installedTones];
for (id object in tones) {
TLITunesTone *tone = (TLITunesTone *)object;
NSLog(#"Tone: %#", [tone name]);
NSLog(#"Tone: %#", [tone filePath]);
}
But I can't do:
TLITunesTone *newTone = [[TLITunesTone alloc] init];
[newTone setName:#"TestTone"];
as it will result in "the symbol for TLITunesTone cannot be found for architecture arm7v".
If I add Class TLITunesTone = NSClassFromString(#"TLITunesTone"); before that code, it will complain that newTone is an undeclared identifier. I tried forward declaring the class, with the same result.
This however will work:
Class TLITunesTone = NSClassFromString(#"TLITunesTone");
id newTone = [[TLITunesTone alloc] init];
[newTone setName:#"TestTone"];
And I can now continue use newTone normally.
So my question is: Why can't I use a static type for an instance of a class which is part of a private framework?
Following the explanation here:
https://github.com/nst/iOS-Runtime-Headers
I am trying to obtain the class of the TUPhoneLogger in the bundle TelephonyUtilities.framework. However, the debugger always show "error: unknown class".
I've 2 different methods:
First method:
NSBundle* b = [NSBundle bundleWithPath:#"/System/Library/PrivateFrameworks/TelephonyUtilities.framework"];
BOOL success = [b load];
NSLog(#"%#", [b definedClasses_dd]);
Note: I've created a #interface NSBundle (DDAdditions)
extension:
- (NSArray *)definedClasses_dd {
NSMutableArray *array = [NSMutableArray array];
int numberOfClasses = objc_getClassList(NULL, 0);
Class *classes = calloc(sizeof(Class), numberOfClasses);
numberOfClasses = objc_getClassList(classes, numberOfClasses);
for (int i = 0; i < numberOfClasses; ++i) {
Class c = classes[i];
if ([NSBundle bundleForClass:c] == self) {
[array addObject:c];
const char* nameOfClass = class_getName(c);
NSString* classString = [NSString stringWithUTF8String:nameOfClass];
if([classString isEqualToString:#"TUPhoneLogger"]) {
NSLog(#"Found it! TUPhoneLogger");
id test= [[c alloc] init];
NSLog(#"test: %#", test);
}
}
}
free(classes);
Second method:
NSBundle* b = [NSBundle bundleWithPath:#"/System/Library/PrivateFrameworks/TelephonyUtilities.framework"];
Class telephonyClass = [b classNamed:#"TUPhoneLogger"];
id test= [[telephonyClass alloc] init];
NSLog(#"%#", test);
In the debugger:
+1 to Victor, as I think it's simpler to just include the Framework as a Build Phase library in your project. Private frameworks are found under the PrivateFrameworks SDK subdirectory, but otherwise, it works similarly as with Public frameworks (with differences described in Victor's answer).
I will just offer one other technique that works, if you do want dynamic loading:
#include <dlfcn.h>
#import <objc/runtime.h>
and then
void* handle = dlopen("/System/Library/PrivateFrameworks/TelephonyUtilities.framework/TelephonyUtilities", RTLD_NOW);
Class c = NSClassFromString(#"TUPhoneLogger");
id instance = [[c alloc] init];
dlclose(handle);
I suppose a benefit of dlopen() is that you don't have to remember to call load, which got you in your second example. A downside is that you should call dlclose() afterwards.
Note: the slight difference in path for dlopen() vs NSBundle bundleWithPath: (file vs. dir)
Note++: this code won't work in the simulator, as the simulator probably is missing that framework (no real phone functionality)
Update
In iOS 9.3, Apple removed Private Frameworks from the SDK. So, since then, it's actually typically the case that you'll need to use this dynamic technique, if the Framework is not one of the public iOS frameworks. See this answer for more
There is a third method (which I prefer)
a) You link to this framework statically (meaning, you add it to your target)
b) You define necessary class (TUPhoneLogger ) in .h class. You can get it by using class-dump(-z)
c) You include this .h file
d) You just use private class the same way as you use public class.
Small additional explanation
There is no "magic" about private frameworks and private API. The only different that they aren't documented and included in .h files.
Step b) and c) creates .h classes with them and as result they can be used exactly the same way as usual public API.