apk完整性校验-防反编译

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

为了防止apk被恶意反编译、篡改、二次开发,我们在安装apk程序的时候,系统需要对apk做完整性校验,目前主流的完整性校验包括客户端打包校验、crc32文件校验和apk自身完整性校验。

提示:以下是本篇文章正文内容

一、客户端打包校验

这种校验方式,只能校验打包的key值一致性,如果反编译篡改用相同密钥进行打包签名则无法校验,所以存在局限性.

步骤一、
事先拿到apk签名key值的SHA1(证书指纹),通过输入命令:keytool -v -list -keystore 文件路径.jks,这里的文件路径就是指向你的key的一个文件路径.
步骤二、
在代码里获取apk签名key值的SHA1(证书指纹)

public static String sHA1(Context context) {
    try {
        PackageInfo info = context.getPackageManager().getPackageInfo(
                context.getPackageName(), PackageManager.GET_SIGNATURES);
        byte[] cert = info.signatures[0].toByteArray();
        MessageDigest md = MessageDigest.getInstance("SHA1");
        byte[] publicKey = md.digest(cert);
        StringBuffer hexString = new StringBuffer();
        for (int i = 0; i < publicKey.length; i++) {
            String appendString = Integer.toHexString(0xFF & publicKey[i])
                    .toUpperCase(Locale.US);
            if (appendString.length() == 1)
                hexString.append("0");
            hexString.append(appendString);
            hexString.append(":");
        }
        String result = hexString.toString();
        return result.substring(0, result.length()-1);
    } catch (PackageManager.NameNotFoundException e) {
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    }
    return null;
}

步骤三、
校验两个key之的SHA1(签名指纹)

/**
     * 和key的签名文件对比
     * @param s
     */
    private void compare(String s){
        if (!s.equals("你获取的SHA1")){
            killAppProcess();
        }
    }
    /**
     * 杀死进程
     */
    public void killAppProcess()
    {
        //注意:不能先杀掉主进程,否则逻辑代码无法继续执行,需先杀掉相关进程最后杀掉主进程
        ActivityManager mActivityManager = (ActivityManager)LoadingActivity.this.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningAppProcessInfo> mList = mActivityManager.getRunningAppProcesses();
        for (ActivityManager.RunningAppProcessInfo runningAppProcessInfo : mList)
        {
            if (runningAppProcessInfo.pid != android.os.Process.myPid())
            {
                android.os.Process.killProcess(runningAppProcessInfo.pid);
            }
        }
        android.os.Process.killProcess(android.os.Process.myPid());
        System.exit(0);
    }

二、crc32文件校验

apk主要的逻辑功能都是通过classes.dex文件实现的,所以对classes.dex文件进行完整性校验,可以防止apk被逻辑篡改.
步骤一、运行以下程序先打印出完整的.dex文件的crc32值.
步骤二、将正确的crc32值储存在资源文件字符串里classesdex_crc,或者服务器存储crc32值.
步骤三、再次运行以下程序,判断apk的crc32值是否正确.正确则打印log:Dex hasn't beenmodified! ,否则log:Dex has beenmodified!

public class MainActivity extendsActivity {
 
@Override
 
protected void onCreate(BundlesavedInstanceState) {
 
     super .onCreate(savedInstanceState);
 
     setContentView(R.layout.activity_main);
 
     String apkPath = getPackageCodePath();
 
     Long dexCrc = Long.parseLong(getString(R.string.classesdex_crc));
 
     try
 
     {
 
         ZipFile zipfile = new ZipFile(apkPath);
 
         ZipEntry dexentry = zipfile.getEntry( "classes.dex" );
 
         Log.i( "verification" , "classes.dexcrc=" +dexentry.getCrc());
 
         if (dexentry.getCrc() != dexCrc){
 
         Log.i( "verification" , "Dexhas been modified!" );
 
         } else {
 
         Log.i( "verification" , "Dex hasn't been modified!" );
 
         }
 
     } catch (IOException e) {
 
      // TODO Auto-generated catch block
 
      e.printStackTrace();
 
     }
 
    }
 
}

三、apk自身完整性校验

对整个apk进行完整性校验,所以对apk生成的哈希值SHA1不能储存在资源文件中,需要储存在服务器或其它地方.
步骤一、先在linux下计算出正确的apk_hash值,用sha1 sum命令,sha1sum verification.apk
步骤二、将步骤一生成的apk_hash值储存在服务器上,之后每次运行程序都用该apk_hash值和服务器取出的正确的hash值比较.

程序生成和比较hash值代码:

public class MainActivity extendsActivity {
 
@Override
 
protectedvoid onCreate(Bundle savedInstanceState) {
 
      super .onCreate(savedInstanceState);
 
      setContentView(R.layout.activity_main);
 
      String apkPath = getPackageCodePath();
 
      MessageDigest msgDigest = null ;
 
      try {
 
         msgDigest = MessageDigest.getInstance( "SHA-1" );
 
         byte [] bytes = new byte [ 1024 ];
 
         int byteCount;
 
         FileInputStream fis = new FileInputStream( new File(apkPath));
 
         while ((byteCount = fis.read(bytes)) > 0 )
 
         {
 
             msgDigest.update(bytes, 0 , byteCount);
 
         }
 
         BigInteger bi = new BigInteger( 1 , msgDigest.digest());
 
         String sha = bi.toString( 16 );
 
         fis.close();
 
         //这里添加从服务器中获取哈希值而后进行对比校验
 
         } catch (Exception e) {
 
             e.printStackTrace();
 
         }
 
     }
 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值