什么是代码混淆

代码混淆就是将代码中的各种元素,如变量,方法,类和包的名字改写成无意义的名字,增加项目反编译后被读懂的难度。

Android代码混淆使用ProGuard工具,ProGuard是一个压缩、优化和混淆Java字节码文件的免费的工具,它可以删除无用的类、字段、方法和属性。

以下是官网对ProGuard的说明:

ProGuard是一个对Java类文件进行压缩,优化,混淆和校验的工具。

压缩过程查找并删除没有使用到的类,字段,方法和属性。优化过程对方法的字节码进行分析和优化。

混淆过程把剩余的元素名字该写成简短且无意义的名字。这些过程会使程序体积更小,运行更高效,更难被反编译。

最后的校验过程为类增加校验信息,但这个过程依赖J2ME和JDK6或以上的编译环境。

rom编译

Android.mk文件中,用LOCAL_PROGUARD_ENABLED来配置混淆的模式;LOCAL_PROGUARD_FLAG_FILES用来指定配置文件。LOCAL_PROGUARD_ENABLED的取值如下:

full:使用编译系统默认的配置:压缩但不混淆和优化,默认的混淆配置文件是build/core/proguard.flags

custom:和full一样,但不包括aapt生成的resource相关的混淆配置。

nosystem:不使用系统的默认配置,但使用aapt生成的resource相关的混淆配置,其他混淆由模块自己负责。

disabled:关闭混淆

obfuscation:和full一样,并且开启混淆

optimization:和full一样,并且开启优化

不设置时,如果是app,默认为full,如果是library,则默认为disabled。

编译userdebug版本时,编译脚本会把app的obfuscation改成full,即不混淆;所以userdebug版本的app是不混淆的。想了解更多信息,可以自行阅读project_src/build/core/下的java.mk,package_internel.mk,java_library.mk,proguard.flags,proguard_base_keeps.flags等文件。

Android Studio

项目目录下的build.gradle文件中minifyEnabled设置为true为开启,false为关闭;proguardFiles用来指定混淆配置文件。使用Build菜单下的Generate Signed APK进行打包即可。记得在Build Type:选项下选择release,否则只打包不会混淆。

Eclipse

项目目录下的project.properties文件中添加配置即可开启混淆:proguard.config=xxx,xxx为混淆配置文件路径,多个配置文件用:分隔。 然后Export APK就可以了,注意直接运行程序生成的安装包是没有经过混淆的。

如何使用混淆

理想的目标是将所有元素都加入混淆,但混淆会另反射无法工作。因此反射以及反射延伸出来的功能使用到的元素都不能混淆。

因为Android开发中有些内容每次都要配置,所以sdk中提供了一份默认配置文件,我们新建项目时可以复制或引用sdk下的默认配置,在此基础上再增加自己的需求。默认配置文件在android_sdk/tools/proguard/proguard-android.txt。

下面介绍一些常用配置以及Android开发中哪些元素不应该混淆。常用配置:

-keep

keep用来指定哪些元素不进行混淆,它有很多变种,比如:

-keep 保留指定的包,类和类成员不被混淆。

-keepclassmembers 保留指定的类成员不被混淆,但包名类名会被混淆。

-keepclasseswithmembers 保留指定的类成员及其类不被混淆。

当未配置-dontshrink(该配置是关闭压缩功能,也就是不会删除未使用的元素,未配置时,也即是开启压缩功能)时,以上3个配置指定的元素即使未使用过,也不会被删除。 以下3个命令与以上3个命令对应,区别是在上述情况中,指定的元素未使用过就会被删除。

-keepnames 也可以写成-keep,allowshrinking

-keepclassmembernames 也可以写成-keepclassmembers,allowshrinking

-keepclasseswithmembernames 也可以写成-keepclasseswithmembers,allowshrinking

示例:

保留Util类名,但内部成员会被混淆

-keep public class com.test.proguard.util.Util

保留Util类名及其内部成员

-keep public class com.test.proguard.util.Util {;}

保留util包及其下级包的类和内部成员

-keep public class com.test.proguard.util.* {;}

保留第三方lib库及继承自第三方的类:

======= Sina Weibo SDK =========

-dontwarn com.sina.*

-keep class com.sina.{;}

-keep interface com.sina.{;}

-keep public class * extends com.sina.**

保留util包下的所有类成员不被混淆,但包名类名会被混淆

-keepclassmembers public class com.test.proguard.util.** {*;}

保留所有名为showText并且是public void的方法不被混淆

-keepclassmembers class * {

public void showText(...);

}

保留Serializable的所有子孙类中所有的private String的属性。

-keepclassmembers class * extends java.io.Serializable {

private java.lang.String *;

}

保留Serializable的所有子孙类中所有的private String的属性以及该类名。

-keepclasseswithmembers class * extends java.io.Serializable {

private java.lang.String *;

}

-dontwarn

dontwarn和keep可以说是形影不离,尤其是处理引入的lib库时.引入的lib库可能存在一些无法找到的引用和其他问题,在build时可能会发出警告,如果我们不进行处理,通常会导致build中止.因此为了保证build继续,我们需要使用dontwarn忽略这些我们无法解决的lib库的警告.

示例:

忽略com.google.zxing包相关的警告

-dontwarn com.google.zxing.**

其他配置

-dontshrink 不压缩,作用于全局

-dontoptimize 不优化,作用于全局

-dontobfuscate 不混淆,作用于全局

-dontwarn 忽略所有警告,使混淆不会因为警告而停止运行,但会打印警告信息

-useuniqueclassmembernames 类和成员都使用唯一的名字,如果没有这个选项,会有很多变量或方法或类名都叫‘a’,‘b’

-dontusemixedcaseclassnames 不使用大小写混合类名

-verbose 混淆过程中打印更多信息,如果因为异常停止混淆,则会输出stack trace,而不仅仅是异常信息

-keepattributes [attribute_filter] Class文件中包含一些与运行无关的信息,比如SourceFile(从哪个源文件编译而来),SourceDir(源文件的文件目录),LineNumberTable(代码行),Exceptions,InnerClasses,Signature,Deprecated,Annotation等等,混淆过程会默认移除掉这些信息,但可以用keepattributes来指定保留那类信息,比如-keepattributes SourceFile,LineNumberTable可以保留代码行和源文件信息。

-include 引入其他的配置文件

不应该混淆的元素

需要反射的元素

由于反射是通过元素名字来查找的,因此当名字改写后,无法找到目标,会导致出现ClassNotFoundException,NoSuchFiledException,NoSuchMethodException等异常。

例如如下代码会抛出ClassNotFoundException:

try {

String str = "com.test.proguard.util.Util";

Class clazz = Class.forName(str);

Object object = clazz.newInstance();

} catch (Exception e) {

e.printStackTrace();

}

有趣的是,上面这段代码如果改写成下面这样,则会顺利找到Util类:

try {

Class clazz = Class.forName("com.test.proguard.util.Util");

Object object = clazz.newInstance();

} catch (Exception e) {

e.printStackTrace();

}

这两段代码的区别在于forName传入的参数是常量还是变量,传入常量的调用方式被ProGuard混淆处理了,所以可以正常运行。

ProGuard还对其他一些反射用法进行了处理。例如:

Class.forName("SomeClass")

SomeClass.class

SomeClass.class.getField("someField")

SomeClass.class.getDeclaredField("someField")

SomeClass.class.getMethod("someMethod", new Class[] {})

AtomicIntegerFieldUpdater.newUpdater(SomeClass.class, "someField")

枚举:Enum.valueOf(String)用到反射,不能混淆

四大组件:四大组件必须在manifest中注册,混淆后类名被改写将无法被找到,会抛出异常。

aidl:aidl

GSON:GSON是一个利用反射进行序列化的第三方lib库。

实现Parcelable接口的可序列化类:进程间通信的话,要保证两端类名相同,进程内传递时反序列化时需要反射CREATOR对象。

注解:很多场景下注解被用作在运行时反射来确定一些元素的特征。

自定义View

native方法

jni调用的java方法

js调用的java方法

如何恢复被混淆的trace

Proguard进行混淆时会生成一个映射表,文件名是mapping.txt,通过sdk下的retrace.sh脚本和mapping.txt就可以把混淆的trace恢复到原来的样子

示例:

trace.txt文件:

java.lang.Exception

at com.test.proguard.a.b.a(Util.java:39)

at com.test.proguard.a.a.a(TestStart.java:14)

at com.test.proguard.MainActivity.a(MainActivity.java:32)

at com.test.proguard.MainActivity.a(MainActivity.java:31)

at com.test.proguard.b.onClick(MainActivity.java:26)

at android.view.View.performClick(View.java:5217)

at android.view.View$PerformClick.run(View.java:21278)

at android.os.Handler.handleCallback(Handler.java:739)

at android.os.Handler.dispatchMessage(Handler.java:95)

at android.os.Looper.loop(Looper.java:148)

at android.app.ActivityThread.main(ActivityThread.java:5547)

at java.lang.reflect.Method.invoke(Native Method)

at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:935)

at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:726)

运行命令:

./retrace.sh ~/mapping.txt ~/trace.txt

输出:

java.lang.Exception

at com.test.proguard.util.Util.showText(Util.java:39)

at com.test.proguard.util.TestStart.start(TestStart.java:14)

at com.test.proguard.MainActivity.test(MainActivity.java:32)

at com.test.proguard.MainActivity.access$0(MainActivity.java:31)

at com.test.proguard.MainActivity$1.onClick(MainActivity.java:26)

at android.view.View.performClick(View.java:5217)

at android.view.View$PerformClick.run(View.java:21278)

at android.os.Handler.handleCallback(Handler.java:739)

at android.os.Handler.dispatchMessage(Handler.java:95)

at android.os.Looper.loop(Looper.java:148)

at android.app.ActivityThread.main(ActivityThread.java:5547)

at java.lang.reflect.Method.invoke(Method.java)

at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:935)

at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:726)

常见问题

1.反射导致找不到类、方法、属性

当反射时抛出:ClassNotFoundException,NoSuchMethodException,NoSuchFieldException时请检查反射目标是否被混淆了。

2.进程间通信传递Parcelable序列化类时报异常

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.test.parcel/com.test.parcel.MainActivity}:

android.os.BadParcelableException: ClassNotFoundException when unmarshalling: com.test.model.Student

at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2514)

at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2575)

at android.app.ActivityThread.access$900(ActivityThread.java:160)

at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1415)

原因:序列化类被混淆后,与另一端的序列化类名称匹配不上,导致抛出ClassNotFoundException异常。

解决:序列化类不应该被混淆。

注意,在Android7.0上Parcelable类的keep需要跟之前的不一样,如下的做法很常见(android本身在proguard_basic_keeps.flags中也是这样写的):

// Parcelable CREATORs must be kept for Parcelable functionality

-keep class * implements android.os.Parcelable {

public static final ** CREATOR;

}

但是这样的写法在Android7上不管用。需要如下写法:

-keepclasseswithmembers class * implements android.os.Parcelable {*;}

或者:

-keepclassmembers class * implements android.os.Parcelable {

public static ;

}

3.Intent传递Parcelable序列化类时报异常

java.lang.RuntimeException: Unable to start service com.smartisan.feedbackhelper.upload.ReliableUploader@431b6290

with Intent { cmp=com.smartisan.gamestore/com.smartisan.feedbackhelper.upload.ReliableUploader (has extras) }:

android.os.BadParcelableException: Parcelable protocol requires a Parcelable.Creator object called CREATOR

on class com.smartisan.feedbackhelper.utils.e

原因:序列化类被混淆后,CREATOR对象变量名被改写,无法被找到,导致抛出异常。

解决:序列化类不应该被混淆。

4.aidl相关类不应该混淆

Parcel : **** enforceInterface() expected 'com.xy.bizport.service.aidl.IXyRemoteCallable' but read 'com.xy.bizport.a.a.a'

原因:混淆后两端类名无法匹配,导致异常。

5.js和java不能互相调用,提示找不到方法

原因:混淆后方法名无法匹配。

解决:增加如下配置:

-keepattributes Annotation, JavascriptInterface

-keepattributes 建议只写一行,因为在odin上配置-keepattributes时,前面的会被后面的覆盖。

android aidl混淆代码,Android代码混淆相关推荐

  1. android aidl工具,【Android】AIDL介绍和实例讲解

    前言 为使应用程序之间能够彼此通信,Android提供了IPC (Inter Process Communication,进程间通信)的一种独特实现: AIDL (Android Interface ...

  2. 猿创征文 | Android AIDL 学习笔记——学以致用

    文章目录 Android AIDL 跨进程通信 AIDL文件 AIDL语法 数据类型 关键字 引用 指定方法ID 总结 实现接口 获取AIDL对象 捕获异常 创建Parcelable对象 带Bundl ...

  3. [转]Android 项目的代码混淆,Android proguard 使用说明

    简介 Java代码是非常容易反编译的.为了很好的保护Java源代码,我们往往会对编译好的class文件进行混淆处理. ProGuard是一个混淆代码的开源项目.它的主要作用就是混淆,当然它还能对字节码 ...

  4. android studio 混淆jar,android studio如何生成混淆代码的jar

    释放双眼,带上耳机,听听看~! 今天,简单讲讲android里如何在生成jar包时,同时对jar包的代码进行混淆,避免别人看到核心代码. 之前,我对领导说我打包生成的SDK,别人使用android s ...

  5. android 第三方加密软件,Android实用图文教程之代码混淆、第三方平台加固加密、渠道分发...

    第一步:代码混淆(注意引入的第三方jar) 在新版本的ADT创建项目时,混码的文件不再是proguard.cfg,而是project.properties和proguard-project.txt. ...

  6. android代码混淆作用,Android分享:代码混淆那些事

    1) 前言 ProGuard是一个开源的Java代码混淆器.它可以混淆Android项目里面的java代码,对的,你没看错,仅仅是java代码.它是无法混淆Native代码,资源文件drawable. ...

  7. Android代码混淆方法,Android 代码混淆零基础入门

    内容提要 本篇文章主要有三个部分,让读者读完后能自己写规则混淆项目 对Android代码怎么开启混淆做一个简单的介绍. 对混淆规则做一个简单介绍: 在混淆过后Crash日志反推代码工具retrace. ...

  8. (转)Android studio 使用心得(五)—代码混淆和破解apk

    这篇文章等是跟大家分享一在Android studio 进行代码混淆配置.之前大家在eclipse上也弄过代码混淆配置,其实一样,大家可以把之前在eclipse上的配置文件直接拿过来用.不管是.cfg ...

  9. android 代码保护 高级混淆

    Android高级混淆和代码保护技术 --------- 有了代码混淆还不够,我们需要更多技巧来保护我们的代码,特别是对于需要做混淆但又需要暴露许多 API 的 SDK 开发者来说.混淆是基础,代码安 ...

最新文章

  1. 动态行和列的表格,展现方式
  2. 前端框架:layui
  3. kali2020提高权限到root
  4. SimpleDateFormat格式化
  5. Spring写第一个程序HelloSpring
  6. 蓝牙设置种常用的Intent
  7. 图像变换--灰度切割、位图切割
  8. 工具的使用 —— 搜狗输入法(二)
  9. CSS详解(二)——CSS连接方式、CSS分裂与CSS分裂检测
  10. 粉丝关注数据库表的设计
  11. matlab 图像特征 代码,数字图像特征提取+matlab源代码
  12. VB6升级到VB2010之一:变量升级~
  13. 全国高级计算机职称考试试题及答案,最新全国计算机职称考试试题及答案.doc...
  14. 北京林业大学本科毕业论文答辩和论文选题PPT模板
  15. 【网站】八大极品桌面壁纸网站,惊艳
  16. java提取图片文字
  17. Unity Error 打AssetBundle时出现 An asset is marked with HideFlags.DontSave but is included in the build
  18. 达人评测 r7 7730u和i5 12500h差距 锐龙r77730u和酷睿i512500h对比
  19. 2012年度最佳分享:仿webQQ界面,详情请下载,不吃亏
  20. uniapp 安卓 跳转到系统浏览器_Android 实现浏览器跳转APP应用,网页也可以跳转APP...

热门文章

  1. HCNP-路由交换:GRE(通用路由封装协议)
  2. Beta阶段敏捷冲刺前准备
  3. 360cdn能挡住cc攻击_关于本站8.11→8.13遭受大型CC攻击和CDN恶意流量攻击,已向公安部报警...
  4. 取消Eclipse中的语法分析警告,去掉虫子 ,去掉红点报错方法
  5. 31_ElasticSearch 修改IK分词器源码来基于mysql热更新词库
  6. Java实现 LeetCode 756 金字塔转换矩阵(DFS)
  7. 隐私保护与生成模型: 差分隐私GAN的梯度脱敏方法
  8. 小红书笔记怎么写提高转化率
  9. 2018技术胖Web前端视频教程全套
  10. C#访问大华网络摄像头