首先表明態(tài)度,私有API僅供研究,不能在AppStore上架,蘋果在review guide里也明確禁止使用私有API。當(dāng)然,如果你是企業(yè)版,覺得無所謂,可以拿來使用,暫時(shí)不知道企業(yè)版發(fā)布的app,被蘋果檢測(cè)到使用了私有API會(huì)有什么后果。
下面介紹幾個(gè)在探索私有API時(shí)常用的方法
1.加載/卸載framework
很多私有API不在public的framework里,這時(shí)候我們就要通過path加載出framework,加載完成之后,才能通過類名創(chuàng)建對(duì)象,注意使用動(dòng)態(tài)加載framework的方法,需要手動(dòng)卸載,使用該framework中的類,需要在卸載操作之前。
#import <dlfcn.h>
//加載framework
void *FTServicesHandle = dlopen("/System/Library/PrivateFrameworks/FTServices.framework/FTServices", RTLD_LAZY);
//卸載framework
dlclose(FTServicesHandle);
2.獲取類的所有方法
當(dāng)我們知道這個(gè)類的時(shí)候,需要知道所有的方法,才能取根據(jù)方法名猜測(cè)改方法的功能,下面就是在runtime中獲取一個(gè)類的所有方法的代碼。
#import <objc/runtime.h>
unsigned int count;
Method *methods = class_copyMethodList([self class], &count);
for (int i = 0; i < count; i++) {
Method method = methods[i];
SEL selector = method_getName(method);
NSString *name = NSStringFromSelector(selector);
NSLog(@"->%@",name);
}
3.調(diào)用私有API的方法
調(diào)用私有API可以直接通過performSelector,但我們都知道performSelector的限制,沒有返回值,這樣我們?nèi)绻肽玫揭粋€(gè)類的屬性,就比較麻煩。下面介紹一種我的使用方法,現(xiàn)在不知道會(huì)出現(xiàn)什么問題,還請(qǐng)大佬幫忙review,大概邏輯就是使用protocol定義相應(yīng)的屬性和方法,已獲取手機(jī)上已安裝的app為例(僅在iOS11以前生效),介紹這個(gè)方法。
相關(guān)的類有:LSApplicationWorkspace和LSApplicationProxy,定義LSApplicationProxy_JTAppProtocol和LSApplicationWorkspace_JTAppProtocol兩個(gè)協(xié)議,里面的方法和屬性分別和上面兩個(gè)類的方法和屬性對(duì)應(yīng),名字和返回值都一致。
@protocol LSApplicationProxy_JTAppProtocol <NSObject>
+ (instancetype)applicationProxyForIdentifier:(NSString*)identifier;
@property (nonatomic, readonly) NSString *localizedShortName;
@property (nonatomic, readonly) NSString *localizedName;
@property (nonatomic, readonly) NSString *bundleId;
@property (nonatomic, readonly) NSArray *appTags;
@end
@protocol LSApplicationWorkspace_JTAppProtocol <NSObject>
- (NSArray *)allInstalledApplications;
@end
使用的方法也很簡單,用id<LSApplicationProxy_JTAppProtocol>對(duì)象接收LSApplicationProxy對(duì)象,用id<LSApplicationWorkspace_JTAppProtocol>對(duì)象接收LSApplicationWorkspace對(duì)象,這樣我們認(rèn)為該對(duì)象是服從自己定義的protocol的,就可以直接調(diào)用方法和屬性,也能保證有返回值,使用如下。
@interface JTAppService ()
@property (nonatomic, strong) id<LSApplicationWorkspace_JTAppProtocol> applicationWorkspace;
@end
@implementation JTAppService
+ (instancetype)sharedInstance {
static dispatch_once_t once;
static id sharedInstance;
dispatch_once(&once, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
- (instancetype)init {
if(self = [super init]) {
self.applicationWorkspace = [[NSClassFromString(@"LSApplicationWorkspace") alloc] init];
}
return self;
}
- (NSArray *)readInstalledApplicationNames {
NSArray *allInstalledApplications = [self.applicationWorkspace allInstalledApplications];
NSMutableArray *allApplicationNames = [NSMutableArray arrayWithCapacity:allInstalledApplications.count];
for(id<LSApplicationProxy_JTAppProtocol> proxy in allInstalledApplications) {
if([[proxy appTags] indexOfObject:@"hidden"] != NSNotFound) {
[allApplicationNames addObject:proxy.localizedName ?: proxy.localizedShortName];
}
}
return allApplicationNames;
}
@end
4.代碼混淆
代碼混淆說起來不是私有API研究中的一項(xiàng),但很多人看到私有API的功能之后,想用上,又不想被AppStore的review reject,那要么用熱更新動(dòng)態(tài)下發(fā)代碼,要么就走代碼混淆方案,混淆也很簡單,方法就是用ASCII碼的char型數(shù)組表示想混淆的字符串,當(dāng)然混淆之后也有風(fēng)險(xiǎn),慎用?。。?/p>
#define LSApplicationWorkspaceChar (char[]){0x4c,0x53,0x41,0x70,0x70,0x6c,0x69,0x63,0x61,0x74,0x69,0x6f,0x6e,0x57,0x6f,0x72,0x6b,0x73,0x70,0x61,0x63,0x65,'\0'}
self.applicationWorkspace = [[NSClassFromString([NSString stringWithUTF8String:LSApplicationWorkspaceChar]) alloc] init];
總結(jié)
私有API的研究方法很簡單,難點(diǎn)在于如何找到相關(guān)功能的framework和類名,知道了這些,就可以取出方法名和屬性,然后就是一個(gè)一個(gè)猜測(cè),然后試,需要兼容所有機(jī)型和系統(tǒng)版本,是一個(gè)比較大的工作量,建議只做研究,不上線。