App在升级过程中,遭到劫持。根据用户反映更新后是一个未知的App,针对此类情况,特做了app防劫持安装防护。
稍作分析只要在本地对apk文件进行包名、应用名和签名校验,如果包名和签名不一致,那么就是伪装程序,这个漏洞显而易见!废话不多说直接上代码。
public class ReadApkMessageUtils { private static String appName; private static String packageName; public static String apkInfoPackageName(String absPath, Context context) { PackageManager pm = context.getPackageManager(); PackageInfo pkgInfo = pm.getPackageArchiveInfo(absPath, PackageManager.GET_ACTIVITIES); if (pkgInfo != null) { ApplicationInfo appInfo = pkgInfo.applicationInfo; /* 必须加这两句,不然下面icon获取是default icon而不是应用包的icon */ appInfo.sourceDir = absPath; appInfo.publicSourceDir = absPath; // 得到包名 packageName = appInfo.packageName; } return packageName; } //获取程序自身的签名 public static String getSign(Context context) { PackageManager pm = context.getPackageManager(); List<PackageInfo> apps = pm.getInstalledPackages(PackageManager.GET_SIGNATURES); Iterator<PackageInfo> iter = apps.iterator(); while (iter.hasNext()) { PackageInfo info = iter.next(); String packageName = info.packageName; //按包名读取签名 if (packageName.equals(SystemToolUtils.getAppPackageName(context))) { //根据你自己的包名替换 return getPublicKey(info.signatures[0].toByteArray()); } } return null; } //获取新下载还未安装APK的签名信息 @SuppressWarnings({"unchecked", "rawtypes"}) public static String getPackageArchiveInfo(String apkFile, int flags) { //这个是与显示有关的, 里面涉及到一些像素显示等等, 我们使用默认的情况 DisplayMetrics metrics = new DisplayMetrics(); metrics.setToDefaults(); Object pkgParserPkg = null; Class[] typeArgs = null; Object[] valueArgs = null; try { Class<?> packageParserClass = Class.forName("android.content.pm.PackageParser"); Constructor<?> packageParserConstructor = null; Object packageParser = null; //由于SDK版本问题,这里需做适配,来生成不同的构造函数 if (Build.VERSION.SDK_INT > 20) { //无参数 constructor packageParserConstructor = packageParserClass.getDeclaredConstructor(); packageParser = packageParserConstructor.newInstance(); packageParserConstructor.setAccessible(true);//允许访问 typeArgs = new Class[2]; typeArgs[0] = File.class; typeArgs[1] = int.class; Method pkgParser_parsePackageMtd = packageParserClass.getDeclaredMethod("parsePackage", typeArgs); pkgParser_parsePackageMtd.setAccessible(true); valueArgs = new Object[2]; valueArgs[0] = new File(apkFile); valueArgs[1] = PackageManager.GET_SIGNATURES; pkgParserPkg = pkgParser_parsePackageMtd.invoke(packageParser, valueArgs); } else { //低版本有参数 constructor packageParserConstructor = packageParserClass.getDeclaredConstructor(String.class); Object[] fileArgs = {apkFile}; packageParser = packageParserConstructor.newInstance(fileArgs); packageParserConstructor.setAccessible(true);//允许访问 typeArgs = new Class[4]; typeArgs[0] = File.class; typeArgs[1] = String.class; typeArgs[2] = DisplayMetrics.class; typeArgs[3] = int.class; Method pkgParser_parsePackageMtd = packageParserClass.getDeclaredMethod("parsePackage", typeArgs); pkgParser_parsePackageMtd.setAccessible(true); valueArgs = new Object[4]; valueArgs[0] = new File(apkFile); valueArgs[1] = apkFile; valueArgs[2] = metrics; valueArgs[3] = PackageManager.GET_SIGNATURES; pkgParserPkg = pkgParser_parsePackageMtd.invoke(packageParser, valueArgs); } typeArgs = new Class[2]; typeArgs[0] = pkgParserPkg.getClass(); typeArgs[1] = int.class; Method pkgParser_collectCertificatesMtd = packageParserClass.getDeclaredMethod("collectCertificates", typeArgs); valueArgs = new Object[2]; valueArgs[0] = pkgParserPkg; valueArgs[1] = PackageManager.GET_SIGNATURES; pkgParser_collectCertificatesMtd.invoke(packageParser, valueArgs); // 应用程序信息包, 这个公开的, 不过有些函数变量没公开 Field packageInfoFld = pkgParserPkg.getClass().getDeclaredField("mSignatures"); Signature[] info = (Signature[]) packageInfoFld.get(pkgParserPkg); return getPublicKey(info[0].toByteArray()); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } return null; } private static String getPublicKey(byte[] signature) { try { /** * Created by zeng * 想要了解更多关于X509,请自行百度 * 创建X509证书工厂类 */ CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); //创建证书对象 X509Certificate cert = (X509Certificate) certFactory.generateCertificate(new ByteArrayInputStream(signature)); //得到证书公钥 return cert.getPublicKey().toString(); } catch (CertificateException e) { e.printStackTrace(); } return null; } } 以上是防劫持相应做本地apk校验的一种方法 针对App防劫持有以下方法: 1.升级api加入https 这个肯定不用再过多介绍,看了我介绍的retrofit 的https就明白了。 2.下载Api也需要加入https,也不用再做介绍 ,这里强调的是需要对服务器返回的文件进行Hash值校验,防止文件被篡改,通过对文件的Hash值,还要对服务端返回的自定义key的值进行校验,防止不是自己服务器返回错误的文件。 3.安装过程也必须对Apk文件进行包名和签名验证,防止Apk被恶意植入木马,或替换。 在此感谢:http://www.jianshu.com/p/2767a66968fe