zoukankan      html  css  js  c++  java
  • Android安全-代码安全3-Dex文件校验

    Android安全-代码安全3-Dex文件校验

    重编译apk其实就是重编译了classes.dex文件,重编译后,生成的classes.dex文件的hash值就改变了,因此我们可以通过检测安装后classes.dex文件的hash值来判断apk是否被重打包过。

      (1)读取应用安装目录下/data/app/xxx.apk中的classes.dex文件并计算其哈希值,将该值与软件发布时的classes.dex哈希值做比较来判断客户端是否被篡改。

      (2)读取应用安装目录下/data/app/xxx.apk中的META-INF目录下的MANIFEST.MF文件,该文件详细记录了apk包中所有文件的哈希值,因此可以读取该文件获取到classes.dex文件对应的哈希值,将该值与软件发布时的classes.dex哈希值做比较就可以判断客户端是否被篡改。

      为了防止被破解,软件发布时的classes.dex哈希值应该存放在服务器端。

      另外由于逆向c/c++代码要比逆向Java代码困难很多,所以关键代码部位应该使用Native C/C++来编写。

      classes.dex 在 Android 系统上基本负责完成所有的逻辑业务,因此很多针对 Android 应用
    程序的篡改都是针对 classes.dex 文件的。在 APK 的自我保护上,也可以考虑对 classes.dex
    文件进行完整性校验,简单的可以通过 CRC 校验完成,也可以检查 Hash 值。由于只是检查
    classes.dex,所以可以将 CRC 值存储在 string 资源文件中,当然也可以放在自己的服务器上,
    通过运行时从服务器获取校验值。基本步骤如下:
     

    • 首先在代码中完成校验值比对的逻辑,此部分代码后续不能再改变,否则 CRC 值
      会发生变化;
    • 从生成的 APK 文件中提取出 classes.dex 文件,计算其 CRC 值,其他 hash 值类似;
    • 将计算出的值放入 strings.xml 文件中。

    核心代码如下:

    代码:
    1. String apkPath = this.getPackageCodePath();
    
    2. Long dexCrc = Long.parseLong(this.getString(R.string.dex_crc));
    
    3. try {
    
    4. ZipFile zipfile = new ZipFile(apkPath);
    
    5. ZipEntry dexentry = zipfile.getEntry("classes.dex");
    
    6. if(dexentry.getCrc() != dexCrc){
    
    7. System.out.println("Dex has been *modified!");
    
    8. }else{
    
    9. System.out.println("Dex hasn't been modified!");
    
    10. }
    
    11. } catch (IOException e) {
    
    12. // TODO Auto-generated catch block
    
    13. e.printStackTrace();
    
    14. }
    

    但是上述的保护方式容易被暴力破解, 完整性检查最终还是通过返回 true/false 来控制
    后续代码逻辑的走向,如果攻击者直接修改代码逻辑,完整性检查始终返回 true,那这种方
    法就无效了,所以类似文件完整性校验需要配合一些其他方法,或者有其他更为巧妙的方式
    实现?

    APK 完整性校验

    虽然 Android 程序的主要逻辑通过 classes.dex 文件执行,但是其他文件也会影响到整个
    程序的逻辑走向,以上述 Dex 文件校验为例,如果程序依赖 strings.xml 文件中的某些值,则
    修改这些值就会影响程序的运行,所以进一步可以整个 APK 文件进行完整性校验。但是如
    果对整个 APK 文件进行完整性校验,由于在开发 Android 应用程序时,无法知道完整 APK 文
    件的 Hash 值,所以这个 Hash 值的存储无法像 Dex 完整性校验那样放在 strings.xml 文件中,
    所以可以考虑将值放在服务器端。核心代码如下:

    代码:
    1. MessageDigest msgDigest = null;
    
    2. try {
    
    3. msgDigest = MessageDigest.getInstance("MD5")
    
    4. byte[] bytes = new byte[8192];
    
    5. int byteCount;
    
    6. FileInputStream fis = null;
    
    7. fis = new FileInputStream(new File(apkPath));
    
    8. while ((byteCount = fis.read(bytes)) > 0)
    
    9. msgDigest.update(bytes, 0, byteCount);
    
    10. BigInteger bi = new BigInteger(1, msgDigest.digest());
    
    11. String md5 = bi.toString(16);
    
    12. fis.close();
    
    13. /*
    
    14. 从服务器获取存储的 Hash 值,并进行比较
    
    15. */
      
    16. } catch (Exception e) {
    
    17. e.printStackTrace();
    
    18. }
    转自:http://bbs.pediy.com/showthread.php?t=183116
  • 相关阅读:
    Windows FFMPEG开发环境配置
    双网卡单IP实现网卡冗余与负载均衡
    Thinking in Java 4th(Java编程思想第四版)文档、源码、习题答案(偶尔回顾)
    IDEA编译时出现 Information:java: javacTask: 源发行版 1.8 需要目标发行版 1.8
    运行java飞行记录器JFR(java flight recorder)
    Java黑科技之源:JVMTI完全解读
    jvisualvm安装visualgc插件
    系统运行缓慢,CPU 100%,以及Full GC次数过多问题的排查思路
    Jenkins 改成中文语言显示
    windows上Jenkins安装及其配置
  • 原文地址:https://www.cnblogs.com/harry335/p/5192184.html
Copyright © 2011-2022 走看看