iOS如何获取手机上的已安装的所有应用

前言

公司内部有一给自己的 App 发布后台,类似于 FIR 那样的存在, 有完整的 LDAP 账号登录。每天的 daily build 和 历史发布版都会放到那里去。然而每次都要登录后台扫描二维码下载实在是太麻烦了,我们就打算做一个客户端。

于是问题来了:如何知道我手机上安装的礼物说版本号比我当前的要低呢?

方案们

方案一 剪贴板共享

我们知道同一个证书下的软件,是可以共享剪贴板的,所以我们可以把礼物说的版本号丢到剪贴板里存下来。

但问题在于:

  1. 礼物说要运行过

  2. 致命伤:必须同一个账号的证书,内部 APP 肯定会去用企业证书,而礼物说可能是线上证书,可能是企业证书。

所以这个方案被否掉了。

方案二 URLScheme

把礼物说的 URLScheme 写成 GiftTalk233 这样的方案,然后在项目中 CanOpenURL 确认安装。

当问题在于:

iOS 9 之后,CanOpenURL 的白名单有上限,而且要修改编译脚本过于麻烦,这个方案也被否掉了。

于是,常规方案都不可行,那么要祭出大杀器了,私有 API

私有 API

毕竟是内部使用的 APP,所以用用私有 API 也没有什么问题。经过查阅越狱开发的文档,我们发现了这么一个私有库:MobileInstallation.framework

使用起来还是蛮方便的,直接:

CFDictionaryRef dict = MobileInstallationLookup(NULL);
NSLog(@"%@", (NSDictionary*) dict);

然而,这个库正常情况下是不会被 Link 进来的,于是我们该怎么做呢?

链接私有库

经过前几篇文章,我们知道动态链接库都是写在 Mach-O 的头部。然而它最终是经过 unix 的一个系统函数 dlopen 来加载的,这货是可以突破沙盒环境的,不信你可以给 dlopen 下个断点看看。所以我们可以手动调用 dlopen 加载想要的私有库。

void* libHandle = dlopen("/System/Library/PrivateFrameworks/MobileInstallation.framework/MobileInstallation",RTLD_NOW);

if (libHandle) {
  NSLog(@"加载成功!");
}

但是对于 C 语言函数来说,他在编译时就被换成了对应的函数指针,所以我们想调用上述方法并没有那么容易。而如果是 OC 的方法的话,我们直接通过 runtime 就可以拿到对应的结果了。

所以现在我们需要拿个函数指针做个映射:

640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy=1
这样就可以了!

 

然鹅,我拿这个这个东西去给同事得瑟的时候,被打脸了。

我拿了一台公司的 iPhone 4 iOS 7 做的开发,这个工作一切正常。但 iOS 8 以上,func 取不到。吓得我赶紧去 Github 上看头文件。

https://github.com/MP0w/iOS-Headers/blob/master/iOS8.1/PrivateFrameworks/MobileInstallation/MIInstallerClient.h

惊了个呆,iOS 8 居然重写了这个 framework,但仔细观察了一下,貌似有一个方法是我们想要的:

  - (void)fetchInstalledAppsWithOptions:(id)arg1 completion:(CDUnknownBlockType)arg2

好吧,我之前树的 Flag 应验了,那就 iOS 8 以上换一个写法吧。

640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy=1
真机运行得到:

required to have an entitlement named “com.apple.private.mobileinstall.allowedSPI” with an array containing “CopyInstalledAppsForLaunchServices” to call the MobileInstallation SPI

Wut?

640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1
entitlement.plist

若你对 iOS 签名机制了机的话,entitlement 绝对不陌生。不过我相信大多数开发者都很少和他打交道,这里我来简单说一下。

我们都知道签名需要证书的 profile 文件,而 entitlement 是一个授权机制,他里面约定了很多 iOS 的更高级的权限。有点像 Android 写在 manifest 里面的 permission。

比如:

  • 访问 HealthKit 需要添加 com.apple.developer.healthkit

  • 使用 Network Extension 查找 WI-FI 需要添加 com.apple.developer.networking.HotspotHelper

但是,这个是需要苹果授权的,且与 profile 对应的。于是我们想在 entitlement 里面添加 com.apple.private.mobileinstall.allowedSPI 是没问题,但是是无法通过签名,会提示找不到对应的 profile。

于是我们想调用私有的 entitlement 的话,越狱设备可以随便搞,但不越狱的话开发者层面是没有什么可以绕过去的方法 (但做安全的蒸米大大说其实是有解的,若有朋友知道欢迎留言)。

仿佛走入了死胡同

柳暗花明又一村

我们都知道同步推是有企业证书版本的,且能查看我们本地的全部 APP。于是,我们对着他们的代码找一下呗~

经过一番查找,我们定位到了一个叫做 ApplicationManager 的类,这个 loadInstalledApplications 的方法很抢眼

640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy=1
然后在逻辑结构中可以看到:

640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy=1
他很鸡贼的判断了一下系统版本是不是大于 8.0,如果不是,就用左边的 MobileInstallation.framework 来做,否则调用了一个方法,叫做 suoyouyianzhuangdeyingyong。我能吐槽这名字么…

通过下面代码,我们可以发现一个类叫做 LSApplicationWorkspace
640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy=1
在 Github 的私有头文件里面搜了一下,惊了个呆:

https://github.com/nst/iOS-Runtime-Headers/blob/master/Frameworks/MobileCoreServices.framework/LSApplicationWorkspace.h

他居然是 MobileCoreServices 的私有函数!我们连 dlopen 都省了。

于是,照猫画虎,我们有:

640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy=1
任务完成!

()

Class c = NSClassFromString(@"LSApplicationWorkspace");

    id s = [(id)c performSelector:NSSelectorFromString(@"defaultWorkspace")];

    NSArray *arr = [s performSelector:NSSelectorFromString(@"allInstalledApplications")];

    for (id item in arr) {

        NSLog(@"%@",[item performSelector:NSSelectorFromString(@"applicationIdentifier")]);

        NSLog(@"%@",[item performSelector:NSSelectorFromString(@"bundleVersion")]);

        NSLog(@"%@",[item performSelector:NSSelectorFromString(@"shortVersionString")]);

}

()

后记

说了那么一大堆,结果就这么几行代码解决了。是不是觉得有点不爽,那就给你爆一个猛料:

这东个西以可过通 JSPatch 或 字符拼串接 来现实生环产境用使。

 

原文:https://mp.weixin.qq.com/s?__biz=MzIwMTYzMzcwOQ==&mid=2650948457&idx=1&sn=9b1f3e0c405b7621a6b3b7c8419a2695&scene=1&srcid=0724IIVS39VqVG4NfViPhU7s&key=77421cf58af4a653d340bb0deaee291e763e1249dcd3486b70f792ddc3190758d2e6bf3bc85be93b98e673fe7c2730ea&ascene=0&uin=MzM5MTU4Mzk1

转载于:https://my.oschina.net/u/2345393/blog/909680

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值