APK安装时的过滤方式:包名白名单、证书认证

小编 2026-06-14 阅读:1036 评论:0
有些项目不允许所有APK都拥有安装权限,例如apk只能通过应用商城来安装或者升级,只允许某些特定的apk自升级,不允许pm install等。这就需要添加安装权限白名单来控制。 先介绍android中常用...

有些项目不允许所有APK都拥有安装权限,例如apk只能通过应用商城来安装或者升级,只允许某些特定的apk自升级,不允许pm install等。这就需要添加安装权限白名单来控制。

先介绍android中常用的几种安装方式,好针对这几种进行修改
1、 直接调用安装接口。

    Uri mPackageURI = Uri.fromFile(new File(Environment.getExternalStorageDirectory() + apkName));
     
    int installFlags = 0;
    PackageManager pm = getPackageManager();
    try{
        PackageInfo pi = pm.getPackageInfo(packageName,
        PackageManager.GET_UNINSTALLED_PACKAGES);
        if(pi != null) {
            installFlags |= PackageManager.REPLACE_EXISTING_PACKAGE;
        }
    }
    catch (NameNotFoundException e){}
    PackageInstallObserver observer = new PackageInstallObserver();
    pm.installPackage(mPackageURI, observer, installFlags);

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

这种修改需要直接修改packageManagerService。对应下面的第一种方法。

2、通过Intent机制,调用packageInstaller进行安装。

    String fileName = Environment.getExternalStorageDirectory() + apkName;
    Uri uri = Uri.fromFile(new File(fileName));
    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setDataAndType(Uri, application/vnd.android.package-archive\");
    startActivity(intent);

    1
    2
    3
    4
    5

因为应用是通过packageInstaller进行安装的,相当于隔了一层代理,所以在packageManagerService并无法判断正在调用安装的是哪个app,只能在packageInstaller中进行修改,参考下面的第二中方法。

3、通过命令进行安装 pm install,参考第三种方法修改。
1、packageManagerService修改

packageManagerService的修改,我们在其中添加接口及代码来控制apk安装。

1)增加以下函数:

         private boolean isWhiteListApp(String pkgName){
            final File systemDir;
            final File whitelistFile;
            final ArrayList<String> whiteListApps = new ArrayList<String>();
            systemDir = new File(\"/system/\", \"etc\");
            whitelistFile = new File(systemDir, \"whitelistapps\");
            if (!whitelistFile.exists()) {
                return false;
            }
            try {
                whiteListApps.clear();
                BufferedReader br = new BufferedReader(new FileReader(whitelistFile));
                String line = br.readLine();
                while (line != null) {
                    //Log.d(TAG, \"whitelistapps readLine:\" + line);
                    whiteListApps.add(line);
                    line = br.readLine();
                }
                br.close();
            } catch (IOException e) {
                Log.e(TAG, \"IO Exception happened while reading whitelistapps\");
                e.printStackTrace();
                return false;
            }
            Iterator<String> it = whiteListApps.iterator();
            while (it.hasNext()) {
                String whitelisItem = it.next();
                if (pkgName.equals(whitelisItem)) {
                    return true;
                }
            }
            return false;
        }

    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进行匹配,判断是否在白名单中,如果不在的话则提示错误。

        private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
     

            ......
     

            try {
                pp.collectCertificates(pkg, parseFlags);
                pp.collectManifestDigest(pkg);
            } catch (PackageParserException e) {
                res.setError(\"Failed collect during installPackageLI\", e);
                return;
            }
     
            // longroey++ start
            if(!isWhiteListApp(pkg.packageName)) {
                res.setError(PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
                        \"app is not in the whitelist. packageName:\" + pkg.packageName);
                return;
            }
            // longroey++ end
     
            /* If the installer passed in a manifest digest, compare it now. */
            if (args.manifestDigest != null) {
                if (DEBUG_INSTALL) {
                    final String parsedManifest = pkg.manifestDigest == null ? \"null\"
                            : pkg.manifestDigest.toString();
                    Slog.d(TAG, \"Comparing manifests: \" + args.manifestDigest.toString() + \" vs. \"
                            + parsedManifest);
                }
                if (!args.manifestDigest.equals(pkg.manifestDigest)) {
                    res.setError(INSTALL_FAILED_PACKAGE_CHANGED, \"Manifest digest changed\");
                    return;
                }
            } else if (DEBUG_INSTALL) {
                final String parsedManifest = pkg.manifestDigest == null
                        ? \"null\" : pkg.manifestDigest.toString();
                Slog.d(TAG, \"manifestDigest was not present, but parser got: \" + parsedManifest);
            }
     

            ......
     
        }

    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,会直接提示不允许安装后退出。

        protected void onCreate(Bundle icicle) {
            super.onCreate(icicle);
     
            // get intent information
            final Intent intent = getIntent();
            mPackageURI = intent.getData();
            mPm = getPackageManager();
            final int uid = getOriginatingUid(intent);
            String callingApp = mPm.getNameForUid(uid);
            final File sourceFile = new File(mPackageURI.getPath());
            PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile);
            mPkgInfo = PackageParser.generatePackageInfo(parsed, null,
                PackageManager.GET_PERMISSIONS, 0, 0, null,
                new PackageUserState());
     
            // add for installer enable/disable
            if (!isWhiteListApp(callingApp)) {
                Toast.makeText(this, R.string.install_not_allow, Toast.LENGTH_LONG).show();
                this.finish();
            }
            mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
     ...省略

    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再去判断参数。

        public static void main(String[] args) {
            new Pm().run(args);
        }
     
    public void run(String[] args) {
     ...省略
            if (\"install\".equals(op)) {
                runInstall();
                return;
            }
     ...省略

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11

那我们要添加的话,先获取app名,再和packageManagerService一样,增加isWhiteListApp去判断是不是要调用runInstall()就OK了。

    String callingApp = \"\";
    try {
        callingApp = mPm.getNameForUid(Binder.getCallingUid());
     } catch(RemoteException re) {
        Log.e(\"Pm\", Log.getStackTraceString(new Throwable()));

}

    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文件的位置。
---------------------
作者:long375577908
来源:CSDN
原文:https://blog.csdn.net/long375577908/article/details/78721010
版权声明:本文为博主原创文章,转载请附上博文链接!

版权声明

本文仅代表作者观点,不代表百度立场。
本文系作者授权百度百家发表,未经许可,不得转载。

上一篇:jquery练习5 下一篇:win7中配置iis
热门文章
  • 机房智能化温湿度解决方式之POE供电以太网温湿度传感器

    机房智能化温湿度解决方式之POE供电以太网温湿度传感器
    机房智能化温湿度解决方式之POE供电以太网温湿度传感器 北京盈创力和电子科技有限公司 智能型TCP网口温湿度记录仪 北京IP网络温湿度记录仪厂家,北京盈创力和 北京智能型TCP网口温湿度记录仪IP网络温湿度记录仪是一种新型的基于TCP/IP协议双绞线以太网标准温湿度采集模块,利用它可以实现现场温度值、相对湿度值的采集,同时利用其自身的RJ45通信接口可以方便地和机房监控主机或交换机集线器进行联网。 工作于-40℃~85℃工业级带...
  • Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering

    Sequential Monte Carlo Methods (SMC) 序列蒙特卡洛/粒子滤波/Bootstrap Filtering
    Problem Statement 我们考虑一个具有马尔可夫性质、非线性、非高斯的状态空间模型(State Space Model):对于一个时间序列上的观测结果{yt,t∈N}\\{ y_t , t \\in N \\}{yt​,t∈N},我们认为每个观测结果yty_tyt​的生成依赖于一个无法直接观察的隐变量xt∈{xt,t∈N}x_t \\in \\{x_t , t \\in N \\}xt​∈{xt​,t∈N},即:p(...
  • HTTP状态保持的原理

    HTTP状态保持的原理
    a)在用户登录之后,浏览器返回响应的时候会在响应中添加上cookieb)浏览器接收到cookie之后会自动保存c)当用户再次请求同一服务器中的其他网页的时候,浏览器会自动带上之前保存的cookied)服务接收到请求之后可以请 request 对象中取到cookie 判断当前用户是否登录  Http是无状态的,就是连接时数据互通,关闭后...
  • Hive 系统函数及示例

    Hive 系统函数及示例
    查看所有系统函数 show functions; 函数分类 内置函数【系统函数】 数学函数: floor、round、ceil、cos、log2等 字符串函数: length、reverse、trim、lower、get_json_object、repeat等 收集函数: size 转换函数: cast 日期函数: year、month、datediff、date、date_add等 条件函数: coalesce、case…w...
  • CSRF的原理和防范措施

    CSRF的原理和防范措施
    a)攻击原理:i.用户C访问正常网站A时进行登录,浏览器保存A的cookieii.用户C再访问攻击网站B,网站B上有某个隐藏的链接或者图片标签会自动请求网站A的URL地址,例如表单提交,传指定的参数iii.而攻击网站B在访问网站A的时候,浏览器会自动带上网站A的cookieiv.所以网站A在接收到请求之后可判断当前用户是登录状态,所以...
标签列表