Android 防止App升级过程被劫持和换包

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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值