Android应用程序的保护机制

大家都知道Android的APK是非常容易被破解的,目前市面上有太多的工具可以去反编译Apk,添加自己的代码,然后重新打包后发布到应用市场上。

因为Android应用市场五花八门,没有统一的监管机制,光靠应用市场去保护原创应用是完全行不通的,这就对我们的应用APK提出了较高的安全性要求。

当然破解和防止破解永远都是相对立的,双方技术都在不断的成长,所以我们不能完全杜绝破解,但是能在一定程度上避免破解,也就达到我们的目的了。

本文主要从以下几个方面简单介绍下Android应用程序的保护机制。

一、防止反编译

 主要是保护DEX、RES、SO库等文件。

目前市面上也有第三方公司提供了Apk加固技术,比如360加固。经过加固后的APK能在一定程度上加大被反编译的难度。

二、防止二次打包

首先,我们需要阻止别的程序员反编译,动态调试我的应用。换句话说,就是需要阻止黑客程序员安装我们的应用到模拟器上。

其次,检测应用程序的完整性,我们可以用jni技术校验应用程序的完整性,也可以利用数字签名的方式来检测应用程序的完整性。
1、校验包名

 判断当前应用的包名是否和官方的应用包名一致。

2、检验签名
我们知道当一个apk文件被反编译破解、修改完代码逻辑之后,要使用jarsigner工具来重新给apk签名,才能运行修改后的apk文件。一个应用程序的签名,
是识别一个开发者的唯一标识,如果一个应用程序被别人反编译,那么这个应用程序的签名肯定会改变。当发现程序的签名改变时,我们就强制退出APP

3、校验classes.dex

4、校验整个apk的完整性

完整代码如下

public class ApkDefender {
    /**
     * 检查APP的合法性
     * @param ctx
     * @return false:APP被修改
     */
    public static boolean checkInvalidate(Context ctx) {
        //一、判断线上版本是否运行在模拟器上
        if (isEmulator()) {
            return false;
        }
        //二、应用完整性校验(建议放在服务器端校验)
        //1、包名校验
        if (!"com.terry.securitydefender".equals(ctx.getPackageName())) {
            return false;
        }
        //2、检测签名(最好放在服务器端校验)
        String signature = getSignature(ctx);
        if("8219089110d0c8c1972cd7e34e96a027".equals(signature)){
            return false;
        }
        //3、校验classes.dex(每次发版,classes.dex的Crc都不一样,因此最好放到服务器端校验)
        long dexCrc = getDexCrc(ctx);
        if(dexCrc>0&&dexCrc!=4156059761L){
            return false;
        }
        /**
         4、校验整个apk是否被修改(每次发版,APK的SHA1值都不一样,因此最好放到服务器端校验)
         */
        String sha1 = getSha1(ctx);
        if(!TextUtils.isEmpty(sha1)&&!"bfa2cb2d9160806efaea6adb505e379ac1057a49".equals(sha1)){
            return false;
        }
        return true;
    }

    /**
     * 判断应用程序是否运行在模拟器上
     * @return
     */
    private static boolean isEmulator() {
        //只要是在模拟器中,不管是什么版本的模拟器,在它的MODEL信息里就会带有关键字参数sdk
        if (Build.MODEL.contains("sdk") || Build.MODEL.contains(Build.VERSION.SDK)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 获得应用程序的数字签名
     * @return
     */
    private static String getSignature(Context ctx) {
        try {
            PackageManager pm = ctx.getPackageManager();
            // 得到当前应用程序的签名
            PackageInfo info = pm.getPackageInfo(ctx.getPackageName(),
                    PackageManager.GET_SIGNATURES);
            String signature = info.signatures[0].toCharsString();//原始md5值,比较长
            signature= getMD5(signature);
            return signature;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 获取classes.dex的Crc值,判断classes.dex是否被修改
     * @param ctx
     * @return
     */
    private static long getDexCrc(Context ctx) {
        try {
            String apkPath = getApkPath(ctx);
            if(TextUtils.isEmpty(apkPath))return 0;
            ZipFile zipfile = new ZipFile(apkPath);
            ZipEntry dexentry = zipfile.getEntry("classes.dex");
            return dexentry.getCrc();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

    /**
     * 获取Apk的SHA1值
     * @return
     */
    private static String getSha1(Context ctx) {
        String apkPath = getApkPath(ctx);
        if(TextUtils.isEmpty(apkPath))return null;
        FileInputStream fis = null;
        try {
            MessageDigest msgDigest = MessageDigest.getInstance("SHA-1");
            byte[] bytes = new byte[1024];
            int byteCount;
            fis = new FileInputStream(new File(apkPath));
            while ((byteCount = fis.read(bytes)) > 0) {
                msgDigest.update(bytes, 0, byteCount);
            }
            byte[] digest = msgDigest.digest();
            String sha = HEX.byteToString(digest);
            return sha;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != fis) try {
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 获取apk的安装路径
     * @param context
     * @return
     */
    private static String getApkPath(Context context) {
        try {
            PackageManager pm = context.getPackageManager();
            PackageInfo pkgInfo = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES);
            ApplicationInfo appInfo = pkgInfo.applicationInfo;
            return appInfo.sourceDir;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
	
	//----------------------------------------------
    public static String getMD5(String str) {
		return getMD5(str.getBytes());
	}

	public static String getMD5(byte[] toencode) {
		try {
			MessageDigest md5 = MessageDigest.getInstance("MD5");
			md5.reset();
			md5.update(toencode);
			return HEX.byteToString(md5.digest());
		} catch (NoSuchAlgorithmException e) {
		}
		return "";
	}
	public static String byteToString(byte[] b) {
		char[] newChar = new char[b.length * 2];
		for (int i = 0; i < b.length; i++) {
			newChar[2 * i] = hex[(b[i] & 0xf0) >> 4];
			newChar[2 * i + 1] = hex[b[i] & 0xf];
		}
		return new String(newChar);
	}
private static final char[] hex = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };	
}

这样能在一定程度上加大二次打包的难度。因为这些都是代码编写的,破解者只要找到对应代码修改后,还是能二次打包。

三、代码安全

可以通过一些自动化检测平台来检测我们的应用是否存在漏洞,然后针对性的加强安全性。

腾讯的金刚审计系统 http://service.security.tencent.com/kingkong
360的捉虫猎手 http://appscan.360.cn/
阿里巴巴的聚安全 http://jaq.alibaba.com/gc/appsec/index.htm
百度的移动云测试中心 http://mtc.baidu.com/
梆梆加固测试平台 http://dev.bangcle.com/apps/index

参考:

http://www.freebuf.com/articles/terminal/102396.html
http://www.droidsec.cn/ 安卓安全中文站
http://blog.nsfocus.net/mobile-app-security-security-test/移动app检测要点
http://zbc.baijia.baidu.com/article/365622 10大移动安全威胁

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值