上一篇说了如何反编译APK文件获取Android源代码,但是如果所有的APK文件都可以随随便便被反编译,岂不是安全性也忒低了~所以这次说一下如何通过代码混淆的方式,提升APK文件的安全性,保护Android源码不那么容易被获取。
Java 是一种跨平台的、解释型语言,Java 源代码编译成中间”字节码”存储于 class 文件中。由于跨平台的需要,Java 字节码中包括了很多源代码信息,如变量名、方法名,并且通过这些名称来访问变量和方法,这些符号带有许多语义信息,很容易被反编译成 Java 源代码。为了防止这种现象,我们可以使用 Java 混淆器对 Java 字节码进行混淆。
混淆就是对发布出去的程序进行重新组织和处理,使得处理后的代码与处理前代码完成相同的功能,而混淆后的代码很难被反编译,即使反编译成功也很难得出程序的真正语义。被混淆过的程序代码,仍然遵照原来的档案格式和指令集,执行结果也与混淆前一样,只是混淆器将代码中的所有变量、函数、类的名称变为简短的英文字母代号,在缺乏相应的函数名和程序注释的况下,即使被反编译,也将难以阅读。同时混淆是不可逆的,在混淆的过程中一些不影响正常运行的信息将永久丢失,这些信息的丢失使程序变得更加难以理解。
混淆器的作用不仅仅是保护代码,它也有精简编译后程序大小的作用。由于以上介绍的缩短变量和函数名以及丢失部分信息的原因, 编译后 jar 文件体积大约能减少25% ,这对当前费用较贵的无线网络传输是有一定意义的。
下面用一个demo来演示代码混淆的步骤:
首先我们新建一个Android工程,项目结构如下图所示:
我们可以看到,在项目结构图中存在两个文件:proguard-project.txt和project.properties,这两个文件就是我们代码混淆需要用到的文件,我们打开project-.properties文件,在里面添加一行代码:proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt,如下图所示:
下面为了测试代码混淆的效果,我们在MainActivity中添加几个方法,主要代码如下所示:
package cn.yubo.test; import android.os.Bundle; import android.app.Activity; import android.util.Log; import android.view.View; import android.widget.Toast; public class MainActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void btnclick(View view){ Toast.makeText(this, "hello, world", Toast.LENGTH_SHORT).show(); fun3(); } private void fun1(){ Log.d("yubo", "hello"); } private void fun2(){ fun1(); } private void fun3(){ fun2(); } }这里就是随便写了几个方法,然后在按钮的点击事件方法中调用,下面我们就可以导出apk包了,导出apk包的方法,是在项目上右键---->export,如下图
一直next下去,到需要使用keystore的一步时,如果没有keystore,我们可以自己创建一个keystore,如果有,可以直接使用,创建keystore的方法,就是指定keystore的路径和密码,这里比较简单,然后再next下去,到最后一步指定apk的导出路径,finish之后就成功导出apk文件了
到这里,我们导出的apk文件就成功做了代码混淆的处理了,为了验证代码混淆的效果,我们使用之前反编译的方法,看看反编译获取的代码是什么样子,这里我放上一张截图如下:
从上面的图中我们可以看到,之前写在MainActivity中的方法,经过反编译后,全部被替换成了方法名为a,b,c的方法,很明显代码混淆的效果出来了,当然,这里测试的只是代码混淆的一部分,代码混淆可以混淆很多类和方法,这里就不再赘述了,基本方法就是上面所说的~
下面给出从其他博客中拷贝过来的代码混淆参数,博客出处为:http://www.cnblogs.com/sunzn/archive/2013/03/06/2946952.html
-optimizationpasses 5 # 指定代码的压缩级别 -dontusemixedcaseclassnames # 是否使用大小写混合 -dontskipnonpubliclibraryclasses # 是否混淆第三方jar -dontpreverify # 混淆时是否做预校验 -verbose # 混淆时是否记录日志 -optimizations !code/simplification/arithmetic,!field/*,!class/merging/* # 混淆时所采用的算法 -keep public class * extends android.app.Activity # 保持哪些类不被混淆 -keep public class * extends android.app.Application # 保持哪些类不被混淆 -keep public class * extends android.app.Service # 保持哪些类不被混淆 -keep public class * extends android.content.BroadcastReceiver # 保持哪些类不被混淆 -keep public class * extends android.content.ContentProvider # 保持哪些类不被混淆 -keep public class * extends android.app.backup.BackupAgentHelper # 保持哪些类不被混淆 -keep public class * extends android.preference.Preference # 保持哪些类不被混淆 -keep public class com.android.vending.licensing.ILicensingService # 保持哪些类不被混淆 -keepclasseswithmembernames class * { # 保持 native 方法不被混淆 native <methods>; } -keepclasseswithmembers class * { # 保持自定义控件类不被混淆 public <init>(android.content.Context, android.util.AttributeSet); } -keepclasseswithmembers class * { public <init>(android.content.Context, android.util.AttributeSet, int); # 保持自定义控件类不被混淆 } -keepclassmembers class * extends android.app.Activity { # 保持自定义控件类不被混淆 public void *(android.view.View); } -keepclassmembers enum * { # 保持枚举 enum 类不被混淆 public static **[] values(); public static ** valueOf(java.lang.String); } -keep class * implements android.os.Parcelable { # 保持 Parcelable 不被混淆 public static final android.os.Parcelable$Creator *; } -keep class MyClass; # 保持自己定义的类不被混淆