记录一次失败的“利用私有API开发过程”

作者 Tsui YuenHong 日期 2017-02-07
记录一次失败的“利用私有API开发过程”

因为一次一次地终结 APP 很麻烦(虽然 iOS 不用手动终结,但是看得不舒服),所以想开发一个一键终结所有 APP 的工具(之前越狱用习惯了)。

准备工作

寻找有用的私有 API(github 地址),经过搜索和思考,发现了合适的方法。

BackBoardServices.framework 中的BKSSystemService.h 有这两个方法:

- (void)terminateApplication:(id)arg1 forReason:(int)arg2 andReport:(bool)arg3 withDescription:(id)arg4;
- (void)terminateApplicationGroup:(int)arg1 forReason:(int)arg2 andReport:(bool)arg3 withDescription:(id)arg4;

OK,开始猜想,参数是什么。
显然,除了第一个参数,其余三个参数都能顾名思义地知道什么意思。
那终结一个指定的 APP 肯定需要知道这个 APP 的唯一标识符 bundle id。那就猜想第一个参数是 bundle id 的意思。

开始

利用 runtime 调用私有 API。

Class class_BKSSystemService = objc_getClass("BKSSystemService");
NSObject *bksSS = [[class_BKSSystemService alloc] init];
/*
该私有 API 含有2个以上参数,不可以使用以下方法调用
if ([bksSS respondsToSelector:@selector(terminateApplication: forReason: andReport: withDescription:)]) {
[bksSS performSelector:@selector(terminateApplication: forReason: andReport: withDescription:) withObject:nil];
}
*/

如注释,不能通过 performSelector 来调用私有方法。

那就利用 NSInvocation

// 判断是否存在方法
if ([[bksSS class] instancesRespondToSelector:@selector(terminateApplication: forReason: andReport: withDescription:)]) {
// 获取方法签名
NSMethodSignature *signature = [[bksSS class] instanceMethodSignatureForSelector:@selector(terminateApplication: forReason: andReport: withDescription:)];
// 包装方法
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setTarget:bksSS];
[invocation setSelector:@selector(terminateApplication: forReason: andReport: withDescription:)];
// 设置参数
CFStringRef appBundleID = (__bridge CFStringRef)bundleID;
int reason = 0;
bool report = NO;
id description = NULL;
[invocation setArgument:&appBundleID atIndex:2];
[invocation setArgument:&reason atIndex:3];
[invocation setArgument:&report atIndex:4];
[invocation setArgument:&description atIndex:5];
[invocation retainArguments];
[invocation invoke];
}

验证

上面代码 bundleID 换为本项目的 bundle id,发现无效,google 一下发现还需要权限。
Xcode 新增 plist 文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.multitasking.termination</key>
<true/>
</dict>
</plist>

在 Build Settings -> Code Signing Entitlements 中加入这个 plist 路径。

咦~ 的确可以终结当前 APP 喔。

完善

/**
获取全部 APP 的 bundle id
注意 : 该方法过滤掉含有 apple 的 bundle id
@return bundle ids
*/
- (NSArray *)getAllApplicationBundleIDs{
// 获取全部 app
Class LSApplicationWorkspace_class = objc_getClass("LSApplicationWorkspace");
NSObject *workspace = [LSApplicationWorkspace_class performSelector:@selector(defaultWorkspace)];
NSArray *allApps = [workspace performSelector:@selector(allApplications)];
// 枚举遍历获取 bundle id
NSMutableArray *allBundleIDs = [NSMutableArray array];
[allApps enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSString *bundleID = [obj performSelector:@selector(bundleIdentifier)];
if (![bundleID containsString:@"apple"]) {
[allBundleIDs addObject:bundleID];
}
}];
return allBundleIDs;
}

调用过程:

NSArray *allBundleIDs = [self getAllApplicationBundleIDs];
[allBundleIDs enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[self killAPPWithBundleID:obj];
}];

到目前为止,模拟器都是可以成功使用的。

真机调试

悲催来了

google 发现,未越狱不能获取这个权限。

虽然是失败的尝试,起码也复习下私有 API 的调用。