Android 代码混淆实战
Posted 一口仨馍
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 代码混淆实战相关的知识,希望对你有一定的参考价值。
本文已授权微信公众号《鸿洋》原创首发,转载请务必注明出处。
什么是代码混淆?
Java 是一种跨平台的、解释型语言,Java 源代码编译成中间”字节码”存储于 class 文件中。由于跨平台的需要,Java 字节码中包括了很多源代码信息,如变量名、方法名,并且通过这些名称来访问变量和方法,这些符号带有许多语义信息,很容易被反编译成 Java 源代码。为了防止这种现象,我们可以使用 Java 混淆器对 Java 字节码进行混淆。
代码混淆的作用
- 减小APK的体积
- 增加反编译后的阅读困难度(注:代码混淆并不能防止反编译)。
实现代码混淆
Eclipse用户
修改项目下project.properties文件,将下面代码前面的注释(#)去掉。
proguard.config=$sdk.dir/tools/proguard/proguard-android.txt:proguard-project.txt
Android studio用户
修改Module下build.gradle,将minifyEnabled
设置为true
。
用Demo说话
public class MainActivity extends Activity
private String mName;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setName();
private void setName()
mName = "董永康";
private String getName()
return mName;
这里以ES为例。打开proguard.project.txt。原文如下。
# To enable ProGuard in your project, edit project.properties
# to define the proguard.config property as described in that file.
#
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in $sdk.dir/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the ProGuard
# include property in project.properties.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the javascript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview
# public *;
#
大概阅读下。注意下面这行说明:
# By default, the flags in this file are appended to flags specified in $sdk.dir/tools/proguard/proguard-android.txt
这里说明我们的proguard.project.txt文件会被追加到proguard-android.txt文件下。也就是说真正的混淆文件其实是proguard-android.txt,我们再配置的文件只是添加在proguard-android.txt文件后面。proguard-android.txt文件只是基础配置没多少内容,有兴趣的可以去看下。
现在我们先不对混淆做任何配置,只使用默认的混淆看看有什么结果。反编译apk查看代码步骤如下:
- 生成TestProguard.apk文件
- 修改后缀apk为zip,解压
- 复制解压后的classes.dex文件到dex2jar目录下(点我下载dex2jar和jd-gui)
- 进入dex2jar解压目录,使用dex2jar命令反编译
- 使用jd-gui查看源代码
dex2jar反编译命令为:d2j-dex2jar classes.dex
使用jd-gui打开后的代码如下:
public class MainActivity extends Activity
private String a;
private void a()
this.a = "董永康";
protected void onCreate(Bundle paramBundle)
super.onCreate(paramBundle);
setContentView(2130903064);
a();
可以看到属性mName变成了a,getName()
方法由于没有调用,自动被取出掉了。这就是代码混淆的好处。你想想,如果你反编译一套代码后看见变量名甚至类名都变成了a、b、c,你还有兴趣接着看下去吗?
代码混淆生成apk之后,项目下面会多出来一个proguard文件夹,下面分别解释proguard文件夹中四个文件的作用。
- dump.txt : 描述了apk中所有类 文件中内部的结构体。( Describes the internal structure of all the class files in the .apk file )
- mapping.txt : 列出了原始的类、方法和名称与混淆代码间的映射。( Lists the mapping between the original and obfuscated class, method, and field names. )
- seeds.txt : 列出了没有混淆的类和方法。( Lists the classes and members that are not obfuscated )
- usage.txt : 列出congapk中删除的代码。( Lists the code that was stripped from the .apk )
着重注意下mapping.txt这个文件,下文会涉及到。
实现自己的代码混淆
上面只是基础的混淆,远远不能满足实际需求。下面添加一个我们自己的混淆规则。在proguard-project.txt中添加如下代码:
-keep public class com.example.testproguard.MainActivity
void setName();
乍一看不知道什么意思,对比下混淆后的代码
public class MainActivity extends Activity
private String a;
private void setName()
this.a = "董永康";
protected void onCreate(Bundle paramBundle)
super.onCreate(paramBundle);
setContentView(2130903064);
setName();
对比可以发现,setName()
方法没有被混淆。这就是-keep
关键字的作用。保持某部分代码不会混淆。有读者可能会觉得本来就是混淆,这会怎么又不混淆了。默认的混淆规则是:除了声明不被混淆的代码,其余的都会被混淆!比如我们在WebView
中和JS交互的时候,需要提供一个接口给JS调用,如果混淆了方法名,JS就会找不到这个方法。
上面只是个示例,更多参见下文:
-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; # 保持自己定义的类不被混淆
查看混淆后的日志
代码几乎都被我们混淆了,我们怎么再去查看错误日志?对着a.b.c(),鬼知道这是什么方法!骚年,别急。Google当然会为我们考虑到这种情况。下面讲解怎么查看混淆后的代码。
- cmd进入sdk/tools/proguard/bin目录。
- 将混淆后的日志和上文提到的mapping文件放入此目录中。
- 执行命令:retrace.bat mapping.txt XXX.txt
执行命令前
执行命令后
可以看到我们发生错误的方法为initViews()
,而不是a()
;感谢耐心阅读到最后!
google:http://developer.android.com/tools/help/proguard.html
以上是关于Android 代码混淆实战的主要内容,如果未能解决你的问题,请参考以下文章
Android项目实战(二十五):Android studio 混淆+打包+验证是否成功
Android项目实战(二十五):Android studio 混淆+打包+验证是否成功