[Android Security] APK自我保护 - DEX/APK校验
cp : https://segmentfault.com/a/1190000005105973
DEX校验
classes.dex 是 Android 虚拟机的可执行文件,我们所写的 java 代码其实都在这里面,所有很多对应用程序的篡改都是针对 classes.dex 文件的。可以找一个 APK 解压就可以看到 classes.dex 文件。APK 其实就是一个压缩包,通过将后缀名改为zip
就可以直接解压,或者用 unzip
命令。
编写代码
代码比较简单,这里是通过建计算好的 crc 保存在 string.xml 文件里,当然我们事先可以随便拿一个值代替,等开发完成后替换掉。
校验代码:
/** * 校验Dex CRC值 */ private void verifyDex(){ //获取String.xml中的value Long dexCrc = Long.parseLong(this.getString(R.string.crc_value)); String apkPath = this.getPackageCodePath(); try { ZipFile zipFile = new ZipFile(apkPath); ZipEntry dexEntry = zipFile.getEntry("classes.dex"); //计算classes.dex的 crc long dexEntryCrc = dexEntry.getCrc(); Log.d("DEX", dexEntryCrc + ""); //对比 if(dexCrc == dexEntryCrc){ Log.d("DEX", "dex hasn't been modified"); }else{ Log.d("DEX", "dex has been modified"); } } catch (IOException e) { e.printStackTrace(); } }
计算 CRC 值
这一步必须在应用开发完成的时候去计算,如果改动了代码就必须重新计算。
利用 unzip 解压
解压命令:
unzip -d output app-debug.apk
output 文件夹:
total 2200 drwxr-xr-x 8 xiangqing staff 272B 4 20 22:18 . drwxr-xr-x 6 xiangqing staff 204B 4 20 22:18 .. -rw-r--r-- 1 xiangqing staff 3.1K 9 15 2015 AndroidManifest.xml drwxr-xr-x 7 xiangqing staff 238B 4 20 22:18 META-INF -rw-r--r-- 1 xiangqing staff 1.1M 9 15 2015 classes.dex drwxr-xr-x 3 xiangqing staff 102B 4 20 22:18 org drwxr-xr-x 10 xiangqing staff 340B 4 20 22:18 res -rw-r--r-- 1 xiangqing staff 4.4K 9 15 2015 resources.arsc
利用 crc32 命令获取 crc 值
mac系统自带 crc32 命令:
crc32 classes.dex
这里生成的 crc 值是16进制数我们可以转换成十进制。
保存到 string.xml 文件
因为 string.xml 文件是资源文件不会影响到 classes.dex 所以修改是没有关系的。
APK校验
与 DEX 校验不同 APK 检验必须把把计算好的 Hash 值放在网络服务端,因为对 APK 的任何改动都会影响到最后的 Hash 值。
校验代码:
/** * 校验APK MD5值 */ private void verifyApk(){ //获取data/app/****/base.apk 路径 String apkPath = getPackageResourcePath(); Log.d("APK", apkPath); MessageDigest msgDigest; try { //获取apk并计算MD5值 msgDigest = MessageDigest.getInstance("MD5"); byte[] bytes = new byte[4096]; int count; FileInputStream fis; fis = new FileInputStream(new File(apkPath)); while((count = fis.read(bytes)) >0){ msgDigest.update(bytes, 0, count); } //计算出MD5值 BigInteger bInt = new BigInteger(1, msgDigest.digest()); String md5 = bInt.toString(16); fis.close(); Log.d("APK", md5); /** * 获取服务端的 MD5值进行对比 */ } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
计算 MD5 值
这一步必须在应用开发完成并且打包 release 版本的时候进行计算。
mac 系统为例:
md5 release.apk
总结
当然上述的保护方式容易被暴力破解, 完整性检查最终还是通过返回 true/false 来控制 后续代码逻辑的走向,如果攻击者直接修改代码逻辑,完整性检查始终返回 true,那这种方 法就无效了,所以类似文件完整性校验需要配合一些其他方法,或者有其他更为巧妙的方式 实现?