APK安装时的过滤方式(packageManagerService):包名白名单或者黑名单---功能验证过,可以参考

参考地址:https://blog.csdn.net/long375577908/article/details/78721010

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

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

  1. Uri mPackageURI = Uri.fromFile( new File(Environment.getExternalStorageDirectory() + apkName));
  2. int installFlags = 0;
  3. PackageManager pm = getPackageManager();
  4. try{
  5. PackageInfo pi = pm.getPackageInfo(packageName,
  6. PackageManager.GET_UNINSTALLED_PACKAGES);
  7. if(pi != null) {
  8. installFlags |= PackageManager.REPLACE_EXISTING_PACKAGE;
  9. }
  10. }
  11. catch (NameNotFoundException e){}
  12. PackageInstallObserver observer = new PackageInstallObserver();
  13. pm.installPackage(mPackageURI, observer, installFlags);

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

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

  1. String fileName = Environment .getExternalStorageDirectory() + apkName;
  2. Uri uri = Uri .fromFile( new File(fileName));
  3. Intent intent = new Intent(Intent .ACTION_VIEW);
  4. intent .setDataAndType(Uri, application/vnd .android .package -archive “);
  5. startActivity(intent);

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

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

1、packageManagerService修改

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

1)增加以下函数:

  1.     private boolean isWhiteListApp( String pkgName){
  2.         final File systemDir;
  3.         final File whitelistFile;
  4.         final ArrayList< String> whiteListApps = new ArrayList< String>();
  5.         systemDir = new File( “/system/”, “etc”);
  6.         whitelistFile = new File(systemDir, “whitelistapps”);
  7.         if (!whitelistFile.exists()) {
  8.             return false;
  9.         }
  10.         try {
  11.             whiteListApps.clear();
  12.             BufferedReader br = new BufferedReader( new FileReader(whitelistFile));
  13.             String line = br.readLine();
  14.             while (line != null) {
  15.                 //Log.d(TAG, “whitelistapps readLine:” + line);
  16.                 whiteListApps.add(line);
  17.                 line = br.readLine();
  18.             }
  19.             br.close();
  20.         } catch (IOException e) {
  21.             Log.e(TAG, “IO Exception happened while reading whitelistapps”);
  22.             e.printStackTrace();
  23.             return false;
  24.         }
  25.         Iterator< String> it = whiteListApps.iterator();
  26.         while (it.hasNext()) {
  27.             String whitelisItem = it.next();
  28.             if (pkgName.equals(whitelisItem)) {
  29.                 return true;
  30.             }
  31.         }
  32.         return false;
  33.     }

isWhiteListApp函数会去读取白名单文件/system/etc/whitelistapps,然后和我们传进来的包名进行匹配,在白名单中返回true,其他情况均返回false。

2)获取调用的包名判断是否在白名单中

接下来要在installPackageLI函数对调用安装的apk进行匹配,判断是否在白名单中,如果不在的话则提示错误。

  1.     private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
  1.         ……
  1.         try {
  2.             pp.collectCertificates(pkg, parseFlags);
  3.             pp.collectManifestDigest(pkg);
  4.         } catch (PackageParserException e) {
  5.             res.setError( “Failed collect during installPackageLI”, e);
  6.             return;
  7.         }
  8.         // longroey++ start
  9.         if(!isWhiteListApp(pkg.packageName)) {
  10.             res.setError(PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE,
  11.                     “app is not in the whitelist. packageName:” + pkg.packageName);
  12.             return;
  13.         }
  14.         // longroey++ end
  15.         /* If the installer passed in a manifest digest, compare it now. */
  16.         if (args.manifestDigest != null) {
  17.             if (DEBUG_INSTALL) {
  18.                 final String parsedManifest = pkg.manifestDigest == null ? “null”
  19.                         : pkg.manifestDigest.toString();
  20.                 Slog.d(TAG, “Comparing manifests: “ + args.manifestDigest.toString() + ” vs. “
  21.                         + parsedManifest);
  22.             }
  23.             if (!args.manifestDigest.equals(pkg.manifestDigest)) {
  24.                 res.setError(INSTALL_FAILED_PACKAGE_CHANGED, “Manifest digest changed”);
  25.                 return;
  26.             }
  27.         } else if (DEBUG_INSTALL) {
  28.             final String parsedManifest = pkg.manifestDigest == null
  29.                     ? “null” : pkg.manifestDigest.toString();
  30.             Slog.d(TAG, “manifestDigest was not present, but parser got: “ + parsedManifest);
  31.         }

  1.         ……
  2.     }

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. protected void onCreate (Bundle icicle) {
  2. super.onCreate(icicle);
  3. // get intent information
  4. final Intent intent = getIntent();
  5. mPackageURI = intent.getData();
  6. mPm = getPackageManager();
  7. final int uid = getOriginatingUid(intent);
  8. String callingApp = mPm.getNameForUid(uid);
  9. final File sourceFile = new File(mPackageURI.getPath());
  10. PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile);
  11. mPkgInfo = PackageParser.generatePackageInfo(parsed, null,
  12. PackageManager.GET_PERMISSIONS, 0, 0, null,
  13. new PackageUserState());
  14. // add for installer enable/disable
  15. if (!isWhiteListApp(callingApp)) {
  16. Toast.makeText( this, R.string.install_not_allow, Toast.LENGTH_LONG).show();
  17. this.finish();
  18. }
  19. mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
  20. …省略

3、pm install的修改

禁止pm install,因为有些APK安装竟然是调用pm install命令去安装的。 
修改要在pm.java修改,修改方法和上面基本一致。 
可以看到,pm install其实调用的是run再去判断参数。

  1. public static void main(String[] args) {
  2. new Pm().run(args);
  3. }
  4. public void run(String[] args) {
  5. 省略
  6. if ( “install”. equals(op)) {
  7. runInstall();
  8. return;
  9. }
  10. 省略

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

String callingApp = “”;

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

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 3 第一处修改:       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文件的位置。

    原文作者:fmc088
    原文地址: https://blog.csdn.net/fmc088/article/details/80949812
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞