有些项目不允许所有APK都拥有安装权限,例如apk只能通过应用商城来安装或者升级,只允许某些特定的apk自升级,不允许pm install等。这就需要添加安装权限白名单来控制。
先介绍android中常用的几种安装方式,好针对这几种进行修改
1、 直接调用安装接口。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
这种修改需要直接修改packageManagerService。对应下面的第一种方法。
2、通过Intent机制,调用packageInstaller进行安装。
- 1
- 2
- 3
- 4
- 5
因为应用是通过packageInstaller进行安装的,相当于隔了一层代理,所以在packageManagerService并无法判断正在调用安装的是哪个app,只能在packageInstaller中进行修改,参考下面的第二中方法。
3、通过命令进行安装 pm install,参考第三种方法修改。
1、packageManagerService修改
packageManagerService的修改,我们在其中添加接口及代码来控制apk安装。
1)增加以下函数:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
isWhiteListApp函数会去读取白名单文件/system/etc/whitelistapps,然后和我们传进来的包名进行匹配,在白名单中返回true,其他情况均返回false。
2)获取调用的包名判断是否在白名单中
接下来要在installPackageLI函数对调用安装的apk进行匹配,判断是否在白名单中,如果不在的话则提示错误。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
3) 增加白名单
/system/etc/whitelistapps内容如下,在编译时可以在mk中修改拷贝到etc目录下,例如下面就是允许这三个包名有安装权限。
com.xxx.xxx1
com.xxx.xxx2
com.xxx.xxx3
2、packageInstaller的修改
还是参考packageManagerService的修改,增加isWhiteListApp函数,去读取白名单文件/system/etc/whitelistapps,然后进行包名匹配,在白名单中返回true,其他情况均返回false。
我们在packageInstaller的PackageInstallerActivity.java中增加以下修改// add for installer enable/disable ,不在白名单中的app,会直接提示不允许安装后退出。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
3、pm install的修改
禁止pm install,因为有些APK安装竟然是调用pm install命令去安装的。
修改要在pm.java修改,修改方法和上面基本一致。
可以看到,pm install其实调用的是run再去判断参数。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
那我们要添加的话,先获取app名,再和packageManagerService一样,增加isWhiteListApp去判断是不是要调用runInstall()就OK了。
- 1
- 2
- 3
- 4
- 5
- 6
APK安装时的过滤方式:包名白名单、证书认证
1.定义一些全局变量,文件位置:
Build.java (frameworks\base\core\java\android\os)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
/**
* 包管理方式名称<br>
* whitelist: 白名单方式
* certificate: 证书认证方式
* none: 不进行管理
*/
public
static
String packageManage =
"none"
;
/**
* 允许 Launch 显示的 APP 及 APP 白名单
*/
public
static
String[] packageAllow =
new
String[]{
"com.baidu.searchbox"
,
"com.thinta.product.thintazlib"
,
"com.thinta.product.x4usertool"
};
/**
* 允许 Launch 显示的 APP的 证书存放路径
*/
public
static
String certificatePath =
"/system/etc/security/media.zip"
;
|
2.修改安装APK过程,在安装过程添加验证
修改文件的位置:
PackageManagerService.java (frameworks\base\services\core\java\com\android\server\pm)
首先添加一个函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
private
static
HashSet<X509Certificate> getTrustedCerts(File keystore)
throws
IOException, GeneralSecurityException {
HashSet<X509Certificate> trusted =
new
HashSet<X509Certificate>();
if
(keystore ==
null
) {
return
trusted;
}
ZipFile zip =
new
ZipFile(keystore);
try
{
CertificateFactory cf = CertificateFactory.getInstance(
"X.509"
);
Enumeration<?
extends
ZipEntry> entries = zip.entries();
while
(entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
InputStream is = zip.getInputStream(entry);
try
{
trusted.add((X509Certificate) cf.generateCertificate(is));
}
finally
{
is.close();
}
}
}
finally
{
zip.close();
}
return
trusted;
}
|
修改的函数:private void installPackageLI(InstallArgs args, PackageInstalledInfo res)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
第一处修改:
if
(Build.ThintaCust.packageManage.equals(
"certificate"
))
tmp_flags = PackageManager.GET_SIGNATURES;
final
int
parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
| (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK :
0
)
| (onSd ? PackageParser.PARSE_ON_SDCARD :
0
) | tmp_flags;
第二处修改:
if
(Build.ThintaCust.packageManage.equals(
"none"
)){
Log.d(
"XYP_DEBUG"
,
"packageManage = none \n"
);
}
else
if
(Build.ThintaCust.packageManage.equals(
"whitelist"
)){
Log.d(
"XYP_DEBUG"
,
"packageManage = whitelist \n"
);
List<String> list = Arrays.asList(Build.ThintaCust.packageAllow);
if
(list.contains(pkg.packageName)){
Log.d(
"XYP_DEBUG"
,
"can install \n"
);
}
else
{
Log.d(
"XYP_DEBUG"
,
"forbid install \n"
);
res.setError(PackageManager.INSTALL_FAILED_USER_RESTRICTED,
"installPackageLI, forbid install"
);
return
;
}
}
else
if
(Build.ThintaCust.packageManage.equals(
"certificate"
)){
int
verify_pass =
0
;
try
{
File file =
new
File(Build.ThintaCust.certificatePath);
HashSet<X509Certificate> trusted = getTrustedCerts(file);
CertificateFactory cf = CertificateFactory.getInstance(
"X.509"
);
for
(X509Certificate c : trusted) {
String tmp_public_key = c.getPublicKey().toString();
for
(Signature sig : pkg.mSignatures)
{
X509Certificate cert = (X509Certificate)cf.generateCertificate(
new
ByteArrayInputStream(sig.toByteArray()));
String tmp_key = cert.getPublicKey().toString();
if
(tmp_public_key.equals(tmp_key)){
verify_pass =
1
;
break
;
}
}
if
(verify_pass ==
1
)
break
;
}
if
(verify_pass !=
1
){
Log.d(
"XYP_DEBUG"
,
"forbid install \n"
);
res.setError(PackageManager.INSTALL_FAILED_USER_RESTRICTED,
"installPackageLI, forbid install"
);
return
;
}
}
catch
(FileNotFoundException e){
Log.d(
"XYP_DEBUG"
, e.toString());
}
catch
(CertificateException e){
Log.d(
"XYP_DEBUG"
, e.toString());
}
catch
(IOException e){
Log.d(
"XYP_DEBUG"
, e.toString());
}
catch
(GeneralSecurityException e){
Log.d(
"XYP_DEBUG"
, e.toString());
}
}
|
3.证书的压缩方式:
zip -r media.zip media.x509.pem
直接用命令把*.x509.pem 打包成zip文件,然后放到目标板的合适位置;
用第一步中的certificatePath指向存放该zip文件的位置。