Android应用程序使用的开发语言是Java语言,由于蚕蛹的是这种解释性语言,所以代码可以被反编译。如果没有经过混淆或是加密,会非常让人容易读懂,换言之就是容易被破解。为了能够使我们的apk更好的保护起来,Android的开发以及安全人员对APK进行了一系列的加固措施。对APK加固从开发者的角度来看,以下是常用的手段和方式:
AndroidStudio默认已经有proguard的支持,在小牧目录安排app下的build.gradle中有配置信息,只需要将false变为true即可。 默认只有release的配置,而工程默认是debug的,所以想要为debug上添加混淆要在release后面添加debug模式。 Proguard的混淆结果会处处到/build/outputs/mapping/release/目录或是/build/outputs/mapping/debug/,一共有四个输出文件:
混淆之后可能出错,出错异常的分析: http://simplyadvanced.net/blog/android-how-to-decode-proguards-obfuscated-code-from-stack-trace/ Proguard 包括四个功能,shrinker(压缩),optimizer(优化),obfuscator(混淆),preverifier(预校验)
源码经过混淆之后,在一个产品级的apk中对apk的保护是有一定帮助的,而对于一些有一定经验的破解者,他们还是会找到比较关键的地方,一般这种方式都是经过反编译之后进行分析的,那么如果我们效仿windows下的保护软件,对smali代码进行混淆,那么某种程度上加大了破解者的分析难度。比较方便的混淆方式就是代码乱序。
我们在分析smali代码时,一般会借助于反编译工具反编译成java代码,代码乱序的主要目的就是想要将smali代码乱序之后,使反编译的效果大打折扣,这样就会挡住很短菜鸟,至少可以减慢分析速度。
在开发过程中字符串的使用不可避免,但是这些字符串极可能是破解的关键点,比如服务器的地址和错误提示这些敏感的字符串信息。如果这些字符串采用硬编码的方式,很容易通过静态分析获取。
在apk中包括代码和资源以及签名文件,一般我们会对可执行文件进行校验,还有证书的签名进行校验,以及apk本身的校验。
classes.dex是android虚拟机的可执行文件,我们所写的java代码骑士都在这里面,所有很多赌赢程序的篡改都是针对classes.dex文件的。
代码比较简单,这里是通过计算好的crc保存在string.xml文件里,当然我们可以随便那一个值代替,等开发完成后替换掉。
private void verifyDex(){ 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"); //计算classex.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 ben modified"); } }catch (IOException e){ e.printStackTrace(); } }与DEX校验不同apk校验必须把计算好的Hash值放在网络服务端,因为对APK的任何改动都会印象到最后的Hash值。 校验代码:
private void verifyApk(){ //获取data/app/**/base.apk 路径 String apkPath = getPackageResourcePath(); 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(){ }每个APK都会经过开发者独有的整数进行签名,如果破解者对APK进行二次打包一般会用自己的签名证书 打包。这时候我们就可以通过校验签名证书的MD5值进行校验。 校验代码:
public void verifySignature(){ String packageName = this.getPackageName(); PackageManager pm = this.getPackageManager(); PackageInfo pi; String md5 = ""; try{ pi = pm.getPackageInfo(packageName,PackageManager.GET_SIGNATURES); Signature[] s=pi.signatures; //计算md5值 //获取服务端签名证书,对比