Android开发笔记(七十三)代码混淆与反破解

来源:互联网 发布:数控车床有编程软件吗 编辑:程序博客网 时间:2024/06/05 15:41

代码混淆

ProGuard是ADT自带的apk混淆器,它的用途有:
1、压缩apk包的大小,能删除无用的代码,并简化部分类名和方法名。
2、加大破解源码的难度,因为部分类名和方法名被重命名,使得程序逻辑变得难以理解。


代码混淆的规则在proguard-project.txt中编写,然后在project.properties补充规则文件的路径,如下所示:
proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt


下面是proguard-project.txt的一个例子:
#指定代码的压缩级别-optimizationpasses 5#是否使用大小写混合-dontusemixedcaseclassnames#优化/不优化输入的类文件-dontoptimize#是否混淆第三方jar包-dontskipnonpubliclibraryclasses#混淆时是否做预校验-dontpreverify#混淆时是否记录日志-verbose#混淆时所采用的算法-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*#保护注解-keepattributes *Annotation*#保持JNI用到的native方法不被混淆-keepclasseswithmembers class * {    native <methods>;}#保持自定义控件的构造函数不被混淆,因为自定义控件很可能直接写在布局文件中-keepclasseswithmembers class * {    public <init>(android.content.Context, android.util.AttributeSet);}#保持自定义控件的构造函数不被混淆-keepclasseswithmembers class * {    public <init>(android.content.Context, android.util.AttributeSet, int);}#保持布局中onClick属性指定的方法不被混淆-keepclassmembers class * extends android.app.Activity {   public void *(android.view.View);}#保持枚举enum类不被混淆-keepclassmembers enum * {    public static **[] values();    public static ** valueOf(java.lang.String);}#保持序列化的Parcelable不被混淆-keep class * implements android.os.Parcelable {  public static final android.os.Parcelable$Creator *;}#指定哪些第三方jar包需要混淆#-libraryjars libs/bcprov-jdk16-1.46.jar#保持哪些系统组件类不被混淆-keep public class * extends android.app.Fragment  -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 * extends android.support.v4.**-keep public class com.android.vending.licensing.ILicensingService#保持哪些第三方jar包不被混淆-keep class org.bouncycastle.**-dontwarn org.bouncycastle.**


下面是进行代码混淆时的注意事项:
1、jni的方法要屏蔽混淆,因为so库要求包名、类名、函数名要完全一致
2、可能会在布局文件中直接引用的类名或方法名,要屏蔽混淆。包括自定义控件、布局中onClick属性指定的方法等等。
3、保持第三方jar包不被混淆,有时需要把“keep class”提到“dontwarn”前面。
4、jar包的文件名中不要有特殊字符,比如说“(”、“)”等字符在混淆时就会报错,文件名最好只包含字母、横线、小数点。
5、使用WebView时,会被js调用的类和方法,要屏蔽混淆。具体做法除了要在proguard-project.txt加上如下说明外,还要在java代码中调用一下js使用的方法,才能保证内部类与方法都不会被混淆。有关WebView中调用js的说明参见《Android开发笔记(六十四)网页加载与JS调用》
-keep class com.example.exmweb.WebActivity$MobileSignal{    public <fields>;    public <methods>;}


防二次打包

前面的《Android开发笔记(七十)反编译初步》提到,apk破解得到smali文件后,可以进行修改并重新打包,从而制造一个山寨的APP。因此为了防止自己辛辛苦苦做的APP被别人山寨,就得在代码中加上防二次打包的处理。具体说来,首先开发者在打包前记下签名证书的MD码,然后在代码中获取app安装后的签名,对比两个签名的MD值是否一致,如果不一致就退出app,这样就能防止被二次打包了。


下面是获取apk签名的代码例子
public static String getSignMD5(Context context) {String signMD5 = "";String packageName = context.getPackageName();PackageManager pkgMgr = context.getPackageManager();PackageInfo info = null;try {info = pkgMgr.getPackageInfo(packageName,PackageManager.GET_SIGNATURES);} catch (PackageManager.NameNotFoundException e) {return signMD5;}if (info == null) {return signMD5;} else {Signature[] signs = info.signatures;if ((signs == null) || (signs.length == 0)) {return signMD5;} else {Signature sign = signs[0];signMD5 = MD5Util.encrypBytes(sign.toByteArray());return signMD5;}}}
以上代码用到了MD5加密,加密算法参见《Android开发笔记(七十二)数据加密算法》。


下面是打包apk时的md5签名值的截图



下面是app运行时获取到的md5签名截图




花指令

代码混淆通过对类名和方法名重命名,只是加大了破解的难度,但并不能完全阻止代码被破解。有个办法就是通过让反编译程序出错,使得代码破解失败,花指令便是这样一种思想。花指令(junk code)意思是程序中加入一些与业务无关的指令,希望在反汇编的时候出错,让破解者无法正确地进行反汇编工作,从而迷失方向。常见的花指令常常是随意跳转,一旦目标位置是另一条指令的中间,反汇编的时候便会出现混乱。下面是花指令的一段示例代码(在jd-gui 0.3.6和1.4.0版本上都测试过,加了花指令的函数就无法正常破解):
//花指令开始BufferedReader br = null;try {br = new BufferedReader(new FileReader("/proc/net/arp"));String line;while ((line = br.readLine()) != null) {String[] splitted = line.split(" +");if (splitted.length >= 0) {break;}}} catch (Exception e) {e.printStackTrace();} finally {try {br.close();} catch (IOException e) {e.printStackTrace();}}//花指令结束


当然,除了上面说的代码混淆、防止二次打包、花指令等等技巧,还有其他的一些技术手段,下面是其他几种代码加密方式:
1、把部分代码写入jni接口,因为so库难以反编译。例如在做签名校验时,原签名的值就可以保存在jni接口中。jni的介绍参见《Android开发笔记(六十九)JNI实战》
2、把核心业务放到后端服务器上运行,app与服务器之前通过http接口通信。
3、使用第三方加密平台给app做加壳处理。



点击下载本文用到的代码反破解的工程代码



点此查看Android开发笔记的完整目录
0 0