Reputation: 499
I have a project where I used both, Objective C and Swift. From security reasons, for some classes and methods written in Objective C I used obfuscation.
This is my code:
#ifndef AccessManager_h
#define AccessManager ySbIXoscpewLLSwVacDpZvPvQeNDtG
#define loginWithUsername TEWhYyXaCdGrCViPbZZHWXoBiUPvPn
#endif
@interface AccessManager : AFHTTPSessionManager
-(void)loginWithUsername:(NSString*)username password:(NSString*)password success:(WSSuccessBlock)success failure:(WSFailureBlock)failure;
@end
And this is my swift code that tried to access the obfuscated class
AccessManager.instance().login(withUsername: "my_user", password: "password", success: { (response) in
//TODO
}) { (error) in
//TODO
}
And this is the error that I get when I build the project
Undefined symbols for architecture armv7:
"_OBJC_CLASS_$_AccessManager", referenced from:
objc-class-ref in MySwiftClass.o
ld: symbol(s) not found for architecture armv7
clang: error: linker command failed with exit code 1 (use -v to see invocation)
How can access an Objective C class that is obfuscated from Swift?
Upvotes: 1
Views: 997
Reputation: 61
If you want to use preprocessor like -
#define ABC DEF
where DEF is a Class name. You need to import this file in your bridging header and then define it as below:
typedef DEF* ABC;
You can refer to this answer
Swift is not able to understand the type of the defined preprocessor which is why you are not able to use it.
Upvotes: 1
Reputation: 16256
Below is very basic example, but gives an idea of what you can do:
Provide an interface to your class and method's runtime representations, by means of global C-functions that are callable from Swift code.
Unlike Objective-C methods, which use dynamic dispatch and whose names thus survive into the binary as plain strings (selectors), C function calls are compiled into jump instructions to the appropriate memory address; the function name is not present in the binary for hackers to decompile/inspect.
AccessManager.h:
#import <Foundation/Foundation.h>
#ifndef AccessManager_h
#define AccessManager ySbIXoscpewLLSwVacDpZvPvQeNDtG
#define loginWithUsername TEWhYyXaCdGrCViPbZZHWXoBiUPvPn
#endif
/// Provides the class object at runtime
Class accessManagerClass();
/// Provides the method name at runtime
SEL loginMethodName();
@interface AccessManager : NSObject
- (instancetype) init;
- (void) loginWithUsername:(NSString*) userName;
@end
AccessManager.m:
#import "AccessManager.h"
Class accessManagerClass() {
return [AccessManager self];
}
SEL loginMethodName() {
return @selector(loginWithUsername:);
}
@implementation AccessManager
- (void) loginWithUsername:(NSString*) userName {
// Dummy implementation, for demonstration purposes:
NSLog(@"%s", __func__);
NSLog(userName);
}
@end
Bridging Header:
#import "AccessManager.h"
ViewController.swift (most of the class omitted):
override func viewDidLoad() {
super.viewDidLoad()
if let managerClass: AnyClass = accessManagerClass(), let methodName = loginMethodName() {
// Instantiate the manager from the class:
let manager = (managerClass as! NSObject.Type).init()
// (I'm still a bit fuzzy about metatypes on Swift,
// perhaps the line above can be improved)
// Call the obfuscated method:
manager.perform(methodName, with: "AAAA")
}
}
When run, it prints:
% -[ySbIXoscpewLLSwVacDpZvPvQeNDtG TEWhYyXaCdGrCViPbZZHWXoBiUPvPn:]
% AAAA
...which proves that:
I put a sample project with all the code above on GitHub.
Upvotes: 0
Reputation: 9484
By using Macros you have changed the name of the class and the method name. This is an easy way to perform obfuscation.
But, that would also break the Swift-Objective-C interoperability if you try to use original class name and its methods.
You can of-course still use your obj-c class by the redefined names in a Swift class(changed the block params to simple BOOLs for simplicity):
let accessManager = ySbIXoscpewLLSwVacDpZvPvQeNDtG()
accessManager.teWhYyXaCdGrCViPbZZHWXoBiUPvPn("", password: "", success: true, failure: true)
Upvotes: 0