热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

用ProGuard混淆Android代码

一、什么是ProGuard为了保证代码的可读性,编写的函数或者变量一般都使用有意义的名字,一看到函数名就知道这个函数是干什么的,一看到变量名就知道这个变量存放的值代表什么含义。这样,对于

一、什么是ProGuard

为了保证代码的可读性,编写的函数或者变量一般都使用有意义的名字,一看到函数名就知道这个函数是干什么的,一看到变量名就知道这个变量存放的值代表什么含义。这样,对于以后代码的维护会有很多的好处,将代码交接给别人维护也会非常的方便。

但是Android应用是用Java代码写的,最后这些代码会被编译成一个包含Dalvik指令的dex文件,并且所有的函数名、变量名都会原封不动的保留在dex文件中。对于某些居心叵测的人,可以使用dex2jarjd-gui试着反推出原始的Java代码或者程序的大致逻辑,然后用apktool修改代码并重新打包。这时候,清晰的函数名和变量名将会给这些黑客以很大的帮助。

拿我写的一个应用来说,在没有经过任何处理的情况下,用dex2jarapk文件转换成jar文件后,再用jd-gui将其打开:


可以看到,其实现的逻辑几乎一览无疑,想要了解背后的实现原理,根本没有任何难度。

那么有没有什么办法既能保证原始代码的可读性,又可以防止这些有用的信息被包含在编译后的代码中,从而泄露给其他的人呢?Google为此提供了一个ProGuard的工具。

ProGuard是在Android SDK中提供的一个免费的开源工具,它能够使用语义上隐晦的名称来重命名代码中的类、变量和函数等,达到混淆代码的功能。

二、如何使用ProGuard

当在 Eclipse 中创建一个新的 Android 工程时,在工程目录的根路径下,会有一个 project.properties 文件,将其打开,可以看到下面的两行:

#To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
#proguard.cOnfig=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
所以,默认情况下, ProGuard 是被关闭的,如果想打开的话,将 proguard.config 前面的井号(“ # ”)去掉就行了。

可以看到,在这个配置项中指定了两个文件。第一个位于Android SDK中,它是Android为所有项目定义的一个通用的配置文件;而第二个位于本工程的根目录下(项目创建时,会在根目录下自动创建一个proguard-project.txt配置文件),其里面的内容是空的,如果第一个通用配置文件无法满足你的需要,还需要专门为你自己的项目添加配置项的话,可以加在这个文件中。一般情况下,通用配置文件已经够用了,并不需要对项目进行特殊的定制。

到此,我们已经通过简单的配置,在Eclipse工程中打开了ProGuard工具混淆Android代码的功能。当你下次在Eclipse中导出你的Android应用时,ProGuard会自动混淆你的代码:


ProGuard只在release模式下编译应用程序才会起作用。而如果在debug模式下编译的话,并不会触发ProGuard对代码进行混淆。

下面我们来看看混淆的效果,还是同样的工程,当开启ProGuard混淆功能后,经过dex2jar处理,在jd-gui中看到的结果如下:


可以看出来,相对于前面来说,混淆过后的代码,其函数和变量名都被替换成了没有任何含义的abcd等字母,单纯看函数名和变量名已经没法猜出其具体作用了。

当你通过Eclipse导出了一个通过ProGuard混淆过的apk后,会在项目的根目录下创建一个叫做proguard的目录,自动生成四个文件:

1)dump.txt:描述apk内所有类文件的内部结构。

2)mapping.txt:列出了原始的类、方法和变量名与混淆后代码间的映射关系。这个文件非常重要,如果你的代码产生bug的话,那么在log中显示的是混淆后的代码。如果希望定位到原始的源代码的话,可以根据记录在mapping.txt文件中的映射关系进行反推。

3)seeds.txt列出了没有被混淆的类和成员函数及变量。

4) usage.txt 列出了被删除的无用代码。

注意,对于下列几种情况,ProGuard不会对代码进行混淆:

1) 反射用到的类;

2) AndroidManifest.xml文件中引用到的类;

3) 需要使用JNI调用的类。

三、如何配置ProGuard

其实 ProGuard 的配置文件主要用来告诉 ProGuard ,项目中的哪些代码是不能被混淆的。要想保证代码的某些部分不被 ProGuard 混淆,必须要在配置文件中添加一些以 -keep 打头的配置项,一共有 6 种关键字,它们的区别和含义如下:


ProGuard在对代码混淆的同时,还会剔除一些无用的代码,也就是从任何调用路径都无法被执行的代码,这样做的好处是可以有效减小应用程序的大小。第一列的关键词可以保证指定的代码不被改名也不会被剔除,即使没有用;第二列的关键词只保证指定代码不会被改名,但如果ProGuard判断这些代码没有用的话,任然会将其删除掉。

6个关键字后面都要包含一个类规格(Class Specification),说明作用的范围,如:

-keep class_specification
这个所谓的类规格是一个非常复杂的概念,其大致定义如下:
[[!]public|final|abstract ...] [!]interface|class|enum classname
[extends|implements classname]
[{
[[!]public|private|protected|static|volatile|transient ...] |
(fieldtype fieldname);
[[!]public|private|protected|static|synchronized|native|abstract ...] |
(argumenttype,...) | (returntype methodname(argumenttype,...));
[[!]public|private|protected|static ... ] *;
...
}]
是不是觉得非常复杂?下面我们来解释一下。表达式中的方括号(“ [ ] ”)表示里面的内容是可选的,可以有也可以没有;省略号(“ ”)表示前面的内容均可以以任何次序和组合同时使用;竖线(“ | ”)表示这些选项只能出现一个。

表达式中的class关键字表示任何接口类、抽象类和普通类;interface关键字表示只能是接口类;enum关键字表示只能是枚举类。如果在interfaceenum关键字前面加上感叹号(“!”)分别表示不是接口类的类和不是枚举类的类。class关键字前面是不能加感叹号的。

对于类名(classname)来说,可以是类全名,或者可以包含以下一些特殊字符的正则表达式:

1:问好代表一个任意字符,但不能是句号(“.”,因为句号是包名分隔符);

2*:单个星号代表任意个任意字符,但不能代表句号;

3 ** :两个星号代表任意个任意字符,且能代表句号。
对于单个星号来说,如果类名部分只有一个星号,不包含其它任何字符,为了保证兼容性,其代表任何类,就跟两个星号的作用一样了。

extendsimplements表示限定类一定要扩展自一个指定类或者实现了一个指定接口类,这时候通常类名部分是一个星号。

对于类中的成员变量(Fields)来说,可以通过变量类型fieldtype和变量名fieldname来精确指定,也可以通过表示类中的任何成员变量。

对于类中的成员函数(Methods)来说,可以通过返回类型returntype、方法名methodname和参数类型argumenttype来唯一限定,也可以通过来表示类中的任何成员函数。

对于类的构造函数来说,可以用加上构造函数的参数来指定。

星号(“*”)可以匹配类中的任何成员变量和函数。

对于类中的成员函数名methodname和成员变量名fieldname来说也可以使用通配符来匹配,同样问号(“”)可以匹配一个任意字符,而星号(“*”)可以匹配任意多个任意字符。

对于类中的成员变量的类型、成员函数的返回类型和参数类型,以及构造函数的参数类型来说,可以使用下面这些通配符来匹配:

1) %:匹配任何原始类型,如booleanint等,但不包括void

2) :匹配一个任意字符,不包括句号;

3) *:匹配任意个任意字符,不包括句号;

4) **:匹配任意个任意字符,包括句号;

5) ***:匹配任意类型,包括原始类型和非原始类型,数组类型和非数组类型;

6) :匹配任何数目个任何类型的参数。

在类名前、类中成员变量和成员函数名前,可以加上访问限定符(如publicprivateprotected等,修饰类、成员变量和成员函数的访问限定符各不相同)。如果加上了访问限定符后,就表示要匹配的类、成员变量或成员函数的定义中必须包含这些限定符。如果在限定符前面加上感叹号“!”,则刚好相反,定义中必须不包含这些限定符。

事实上还可以加上对特定类型注释(Annotation)的限定条件,但一般用不到,这里就不再多说了。

作为例子,我们可以看看Android SDK中已经为我们预定义的一些ProGuard规则。这些规则保证一般的Android应用不需要任何配置就可以使用ProGuard进行混淆,并且混淆后的代码还可以无错误的运行,具体解释请看注释:

#两个特定的类不能被混淆和删除
-keep public class com.google.vending.licensing.ILicensingService
-keep public class com.android.vending.licensing.ILicensingService
#所有包含JNI调用的类以及其内部的成员都不能被混淆
-keepclasseswithmembernames class * {
native ;
}
#扩展自android.view.View类的任何public类的setter和getter方法都不能被混淆和删除
-keepclassmembers public class * extends android.view.View {
void set*(***);
*** get*();
}
#扩展自android.app.Activity类的任何类中访问属性是public,返回值是void,参数是android.view.View类型的所有函数都不能被混淆或删除
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}
#任何枚举类中的values和valueOf静态方法都不能被混淆和删除
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
#实现了android.os.Parcelable接口类的任何类,以及其内部定义的Creator内部类类型的public final静态成员变量,都不能被混淆和删除
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
#所有自动生成的R类中的public静态成员变量
-keepclassmembers class **.R$* {
public static ;
}
最后总结一下,ProGuard是一个非常方便的代码混淆工具。只需要在你的项目开发过程中,花一点点时间将其集成进去,并稍微多做一下测试,就可以起到很好的效果,可以极大的增加破解的难度,建议大家多多使用。
推荐阅读
  • 动态壁纸 LiveWallPaper:让您的桌面栩栩如生(第二篇)
    在本文中,我们将继续探讨如何开发动态壁纸 LiveWallPaper,使您的桌面更加生动有趣。作为 2010 年 Google 暑期大学生博客分享大赛 Android 篇的一部分,我们将详细介绍 Ed Burnette 的《Hello, Android》第三版中的相关内容,并分享一些实用的开发技巧和经验。通过本篇文章,您将了解到如何利用 Android SDK 创建引人入胜的动态壁纸,提升用户体验。 ... [详细]
  • 在分析Android的Audio系统时,我们对mpAudioPolicy->get_input进行了详细探讨,发现其背后涉及的机制相当复杂。本文将详细介绍这一过程及其背后的实现细节。 ... [详细]
  • CentOS 7 中 iptables 过滤表实例与 NAT 表应用详解
    在 CentOS 7 系统中,iptables 的过滤表和 NAT 表具有重要的应用价值。本文通过具体实例详细介绍了如何配置 iptables 的过滤表,包括编写脚本文件 `/usr/local/sbin/iptables.sh`,并使用 `iptables -F` 清空现有规则。此外,还深入探讨了 NAT 表的配置方法,帮助读者更好地理解和应用这些网络防火墙技术。 ... [详细]
  • 在Linux系统中避免安装MySQL的简易指南
    在Linux系统中避免安装MySQL的简易指南 ... [详细]
  • 在使用SSH框架进行项目开发时,经常会遇到一些常见的问题。例如,在Spring配置文件中配置AOP事务声明后,进行单元测试时可能会出现“No Hibernate Session bound to thread”的错误。本文将详细探讨这一问题的原因,并提供有效的解决方案,帮助开发者顺利解决此类问题。 ... [详细]
  • 本文探讨了资源访问的学习路径与方法,旨在帮助学习者更高效地获取和利用各类资源。通过分析不同资源的特点和应用场景,提出了多种实用的学习策略和技术手段,为学习者提供了系统的指导和建议。 ... [详细]
  • 本文探讨了Android系统中支持的图像格式及其在不同版本中的兼容性问题,重点涵盖了存储、HTTP传输、相机功能以及SparseArray的应用。文章详细分析了从Android 10 (API 29) 到Android 11 的存储规范变化,并讨论了这些变化对图像处理的影响。此外,还介绍了如何通过系统升级和代码优化来解决版本兼容性问题,以确保应用程序在不同Android版本中稳定运行。 ... [详细]
  • 本文详细介绍了在 CentOS 7 系统中配置 fstab 文件以实现开机自动挂载 NFS 共享目录的方法,并解决了常见的配置失败问题。 ... [详细]
  • 解决Only fullscreen opaque activities can request orientation错误的方法
    本文介绍了在使用PictureSelectorLight第三方框架时遇到的Only fullscreen opaque activities can request orientation错误,并提供了一种有效的解决方案。 ... [详细]
  • 本文详细介绍了如何解决DNS服务器配置转发无法解析的问题,包括编辑主配置文件和重启域名服务的具体步骤。 ... [详细]
  • 本文对SQL Server系统进行了基本概述,并深入解析了其核心功能。SQL Server不仅提供了强大的数据存储和管理能力,还支持复杂的查询操作和事务处理。通过MyEclipse、SQL Server和Tomcat的集成开发环境,可以高效地构建银行转账系统。在实现过程中,需要确保表单参数与后台代码中的属性值一致,同时在Servlet中处理用户登录验证,以确保系统的安全性和可靠性。 ... [详细]
  • 您的数据库配置是否安全?DBSAT工具助您一臂之力!
    本文探讨了Oracle提供的免费工具DBSAT,该工具能够有效协助用户检测和优化数据库配置的安全性。通过全面的分析和报告,DBSAT帮助用户识别潜在的安全漏洞,并提供针对性的改进建议,确保数据库系统的稳定性和安全性。 ... [详细]
  • 在 Android 开发中,`android:exported` 属性用于控制组件(如 Activity、Service、BroadcastReceiver 和 ContentProvider)是否可以被其他应用组件访问或与其交互。若将此属性设为 `true`,则允许外部应用调用或与之交互;反之,若设为 `false`,则仅限于同一应用内的组件进行访问。这一属性对于确保应用的安全性和隐私保护至关重要。 ... [详细]
  • 本文介绍了一种自定义的Android圆形进度条视图,支持在进度条上显示数字,并在圆心位置展示文字内容。通过自定义绘图和组件组合的方式实现,详细展示了自定义View的开发流程和关键技术点。示例代码和效果展示将在文章末尾提供。 ... [详细]
  • 卓盟科技:动态资源加载技术的兼容性优化与升级 | Android 开发者案例分享
    随着游戏内容日益复杂,资源加载过程已不仅仅是简单的进度显示,而是连接玩家与开发者的桥梁。玩家对快速加载的需求越来越高,这意味着开发者需要不断优化和提升动态资源加载技术的兼容性和性能。卓盟科技通过一系列的技术创新,不仅提高了加载速度,还确保了不同设备和系统的兼容性,为用户提供更加流畅的游戏体验。 ... [详细]
author-avatar
hgo6786689
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有