[转]Android 代码混淆和加固 so库 简单教你一行代码实现

混淆

因为开启混淆会使编译时间变长,所以debug模式下不开启。我们需要做的是:
1.将release下minifyEnabled的值改为true,打开混淆;
2.buildConfigField 不显示log日志

为什么要混淆:

  • 优化java的字节码
  • 减小apk文件的大小,在混淆过程中会删除未使用过的类和成员
  • 代码安全,使类、函数、变量名随机变成无意义的代号形如:a,b,c...之类。防止app被反编译之后能够很容易的看懂代码

APP需要保留的公共部分(通用)

  1. 四大组件以及子类;
  2. 自定义Application;
    1. native方法
    2. R下面的资源
    3. 序列化(Parcelable,Serializable)
    4. support下面的继承子类
    5. Activity中参数是view的方法
    6. 枚举
    7. 自定义View
    8. 带有回调函数(On*Listener,OnEvent)
    9. WebView

1. 判断程序是否运行在模拟器上

boolean isRunningInEmualtor() {
        boolean qemuKernel = false;
        Process process = null;
        DataOutputStream os = null;
        try{  
            process = Runtime.getRuntime().exec("getprop ro.kernel.qemu");  
            os = new DataOutputStream(process.getOutputStream());
            BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream(),"GBK"));
            os.writeBytes("exit\n");  
            os.flush();
            process.waitFor();
            // getprop ro.kernel.qemu == 1  在模拟器
            // getprop ro.product.model == "sdk"  在模拟器
            // getprop ro.build.tags == "test-keys"  在模拟器
            qemuKernel = (Integer.valueOf(in.readLine()) == 1);
            Log.d("com.droider.checkqemu", "检测到模拟器:" + qemuKernel);             
        } catch (Exception e){  
            qemuKernel = false;
            Log.d("com.droider.checkqemu", "run failed" + e.getMessage()); 
        } finally {
            try{  
                if (os != null) {  
                    os.close();  
                }  
                process.destroy();  
            } catch (Exception e) {
                
            }  
            Log.d("com.droider.checkqemu", "run finally"); 
        }
        return qemuKernel;
    }

2. 检测keystore签名,再与之前得做比较

public int getSignature(String packageName) {      
        PackageManager pm = this.getPackageManager();
        PackageInfo pi = null;
        int sig = 0;
        try {
            pi = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
            Signature[] s = pi.signatures;
            sig = s[0].hashCode();  
        } catch (Exception e1) {
            sig = 0;
            e1.printStackTrace();
        }
        return sig;
    }
 

3. 检测包名,版本名和版本号,然后做判断:

private String getAppInfo() {
         try {
             String pkName = this.getPackageName();
             String versionName = this.getPackageManager().getPackageInfo(
                     pkName, 0).versionName;
             int versionCode = this.getPackageManager()
                     .getPackageInfo(pkName, 0).versionCode;
             return pkName + "   " + versionName + "  " + versionCode;
         } catch (Exception e) {
         }
         return null;
     }

 4.完整性校验

  • 对签名文件中classes.dex哈希值的校验

Android工程代码经编译打包生成apk包后,开发者需要对其签名才能在安卓市场上发布供用户下载和安装。对apk包签名后,会在原apk包结构基础上加入META-INF文件目录。

META-INF文件目录下含有三个文件:MANIFEST.MF文件、ANDROIDD.SF文件、ANDROIDD.RSA文件,META_INF目录文件结构如下图所示:

其中,MANIFEST.MF文件描述了在签名时,签名工具对apk包中各个文件摘要计算后的哈希值,并对哈希值做了Base64编码。MANIFEST.MF文件中描述的classes.dex文件的SHA-1哈希值如下图所示:

一旦攻击者对APK中反编译并篡改代码,经二次打包签名后的classes.dex文件的SHA-1必定改变,因此,我们可以将该文件中的classes.dex文件的SHA-1哈希值保存起来作为校验对比值,应用程序启动时读取apk安装包中的MANIFEST.MF文件,解析出classes.dex的SHA-1哈希值,然后与原SHA-1哈希值进行比较,判断此APK包代码文件是否被篡改。
通过检查签名文件classes.dex文件的哈希值来判断代码文件是否被篡改的java实现代码如下所示:

通过检查签名文件classes.dex文件的哈希值来判断代码文件是否被篡改
@param orginalSHA 原始Apk包的SHA-1值

 
public static void apkVerifyWithSHA(Context context, String baseSHA) {  
      String apkPath = context.getPackageCodePath(); // 获取Apk包存储路径  
      try {  
          MessageDigest dexDigest = MessageDigest.getInstance("SHA-1");  
           byte[] bytes = new byte[1024];  
           int byteCount;  
           FileInputStream fis = new FileInputStream(new File(apkPath)); // 读取apk文件  
           while ((byteCount = fis.read(bytes)) != -1) {  
               dexDigest.update(bytes, 0, byteCount);  
           }  
           BigInteger bigInteger = new BigInteger(1, dexDigest.digest()); // 计算apk文件的哈希值  
           String sha = bigInteger.toString(16);  
           fis.close();  
           if (!sha.equals(baseSHA)) { // 将得到的哈希值与原始的哈希值进行比较校验  
               Process.killProcess(Process.myPid()); // 验证失败则退出程序  
           }  
       } catch (NoSuchAlgorithmException e) {  
           e.printStackTrace();  
       } catch (FileNotFoundException e) {  
           e.printStackTrace();  
       } catch (IOException e) {  
           e.printStackTrace();  
       }  
   } 

5.Android逆向-.so文件动态调试步骤

so库加密的方法

1.是在有源码的基础上进行对特定的section进行加密

2.基于二进制级别的特定函数的加密

 

参考博客:

https://blog.csdn.net/nicolelili1/article/details/79243744

https://blog.csdn.net/feibabeibei_beibei?t=1


---------------------
作者:深南大盗
来源:CSDN
原文:https://blog.csdn.net/WHB20081815/article/details/88960114
版权声明:本文为作者原创文章,转载请附上博文链接!
内容解析By:CSDN,CNBLOG博客文章一键转载插件

发布了113 篇原创文章 · 获赞 69 · 访问量 30万+

猜你喜欢

转载自blog.csdn.net/admans/article/details/103416355