前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android开发笔记(七十三)代码混淆与反破解

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

作者头像
aqi00
发布2019-01-18 13:01:37
1.7K0
发布2019-01-18 13:01:37
举报
文章被收录于专栏:老欧说安卓老欧说安卓

代码混淆

ProGuard是ADT自带的apk混淆器,它的用途有: 1、压缩apk包的大小,能删除无用的代码,并简化部分类名和方法名。 2、加大破解源码的难度,因为部分类名和方法名被重命名,使得程序逻辑变得难以理解。 代码混淆的规则在proguard-project.txt中编写,然后在project.properties补充规则文件的路径,如下所示:

代码语言:javascript
复制
proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt

下面是proguard-project.txt的一个例子:

代码语言:javascript
复制
#指定代码的压缩级别
-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调用

代码语言:javascript
复制
-keep class com.example.exmweb.WebActivity$MobileSignal{
    public <fields>;
    public <methods>;
}

防二次打包

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

代码语言:javascript
复制
	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版本上都测试过,加了花指令的函数就无法正常破解):

代码语言:javascript
复制
		//花指令开始
		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开发笔记的完整目录

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016年03月01日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 代码混淆
  • 防二次打包
  • 花指令
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档