Androd安全——混淆技术完全解析
0.前言
在上一篇Androd安全——反编译技术完全解析中介绍了反编译方面的知识,因此我们认识到为了安全我们需要对代码进行混淆。
混淆代码并不是让代码无法被反编译,而是将代码中的类、方法、变量等信息进行重命名,把它们改成一些毫无意义的名字。因为对于我们而言可能Cellphone类的call()方法意味着很多信息,而A类的b()方法则没有任何意义,但是对于计算机而言,它们都是平等的。
所以说混淆代码可以在不影响程序正常运行的前提下让破解者很头疼,从而大大提升了程序的安全性。
1. 混淆的使用
在Android Studio中借助SDK中自带的Proguard工具,如下所示,只需要修改build.gradle中minifyEnabled的值为true即可,这样Build->Generate Signed APK打出来的APK包就是混淆过的,当然,Debug版的APK是不会混淆的。
release {
minifyEnabled true //设置是否启用混淆
//用于选定混淆配置文件
proguardFiles getDefaultProguardFile('proguard-android.txt'),'proguard-rules.pro'
}
2. 默认的混淆规则
2.1 普通工具类
下面是一个非常普通的工具类,没有任何继承关系。Utils中有两个方法内部逻辑一样,唯一的据别是稍后methodNormal()方法会被调用,而methodUnused()方法不会被调用。
public class Utils {
public void methodNormal() {
String logMessage = "this is normal method";
logMessage = logMessage.toLowerCase();
System.out.println(logMessage);
}
public void methodUnused() {
String logMessage = "this is unused method";
logMessage = logMessage.toLowerCase();
System.out.println(logMessage);
}
}
混淆结果如下:
总结:像Utils这样的普通类,不管是类名、方法名还是变量都会混淆。除了混淆之外反编译之后就只剩一个方法了,因为另外一个方法没有被调用,所以认为是多余的代码,在打包的时候就给移除掉了(没有被调用的资源同样也会被移除掉),因此minifyEnabled除了混淆代码之外,还起到压缩APK包的作用。
2.2 NativeUtils类
下面这个类中同样有两个方法,一个是native方法,一个是非native方法。
public class NativeUtils {
public static native void methodNative();
public static void methodNotNative() {
String logMessage = "this is not native method";
logMessage = logMessage.toLowerCase();
System.out.println(logMessage);
}
}
混淆结果如下:
总结:NativeUtils的类名没有被混淆,这是由于它有一个声明成native的方法。只要一个类中有存在native方法,它的类名就不会被混淆(因为C++代码要通过包名+类名+方法名来进行交互),其中声明成native的方法也没有被混淆。但是非native方法的方法名和局部变量都被混淆了。
2.3 MyFragment类
下面这个类继承自Fragment,并且有一个全局变量。onCreateView()方法是Fragment的生命周期函数,在onCreateView()方法中又调用了methodWithGlobalVariable()和methodWithLocalVariable()方法,这两个方法的内部分别引用了一个全局变量和一个局部变量。
public class MyFragment extends Fragment {
private String toastTip = "toast in MyFragment";
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_layout, container, false);
methodWithGlobalVariable();
methodWithLocalVariable();
return view;
}
public void methodWithGlobalVariable() {
Toast.makeText(getActivity(), toastTip, Toast.LENGTH_SHORT).show();
}
public void methodWithLocalVariable() {
String logMessage = "log in MyFragment";
logMessage = logMessage.toLowerCase();
System.out.println(logMessage);
}
}
混淆结果如下:
总结:这个类也是混淆的比较彻底的,基本没有任何保留。所有的方法名、全局变量、局部变量都被混淆了。 其中,onCreateView()这样的生命周期方法会不会被混淆和我们使用Fragment的方式有关,比如在本项目中使用的是android.support.v4.app.Fragment,support-v4包下的,就连Fragment的源码都一起混淆了,因此生命周期方法当然也不例外了。但如果你使用的是android.app.Fragment,这就是调用手机系统中预编译好的代码,混淆无法影响到系统内置的代码,因此这种情况下onCreateView()方法名就不会被混淆,但其它的方法以及变量仍然会被混淆。
2.4 MainActivity类
MainActivity和MyFragment类似,也是定义了methodWithGlobalVariable()和methodWithLocalVariable()这两个方法,然后MainActivity对MyFragment进行了添加,并在Button的点击事件里面调用了自身的、Utils的、以及NativeUtils中的方法。
public class MainActivity extends AppCompatActivity {
private String toastTip = "toast in MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportFragmentManager().beginTransaction().add(R.id.fragment, new MyFragment()).commit();
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
methodWithGlobalVariable();
methodWithLocalVariable();
Utils utils = new Utils();
utils.methodNormal();
NativeUtils.methodNative();
NativeUtils.methodNotNative();
}
});
}
public void methodWithGlobalVariable() {
Toast.makeText(MainActivity.this, toastTip, Toast.LENGTH_SHORT).show();
}
public void methodWithLocalVariable() {
String logMessage = "log in MainActivity";
logMessage = logMessage.toLowerCase();
System.out.println(logMessage);
}
}
混淆结果如下:
总结:MainActivity的类名、onCreate()这种声明周期方法是没有混淆的(凡是需要在AndroidManifest.xml中去注册的所有类的类名以及从父类重写的方法名都自动不会被混淆,这份规则同样也适用于Service、BroadcastReceiver和ContentProvider),但是我们定义的方法、全局变量、局部变量都被混淆了。
最后,第三方的Jar包(包名、类名以及方法名)都是会被混淆的。
3. 认识混淆规则
这些混淆规则是在哪里定义的呢?其实就是刚才在build.gradle的release闭包下配置的proguard-android.txt文件,这个文件存放于<Android SDK>/tools/proguard目录下,我们打开来看一下,每句话的意义已经在注释里标明了:
-dontusemixedcaseclassnames //表示混淆时不使用大小写混合类名
-dontskipnonpubliclibraryclasses //表示不跳过library中的非public的类
-verbose //表示打印混淆的详细信息 # Optimization is turned off by default. Dex does not like code run
# through the ProGuard optimize and preverify steps (and performs some
# of these optimizations on its own).
-dontoptimize//不进行优化,建议使用此选项,因为优化可能不保证在所有版本的Dalvik上都正常运行。
-dontpreverify//不进行预校验,预校验是作用在Java平台上的,Android平台上去掉可加快混淆速度
# Note that if you want to enable optimization, you cannot just
# include optimization flags in your own project configuration file;
# instead you will need to point to the
# "proguard-android-optimize.txt" file instead of this one from your
# project.properties file. -keepattributes *Annotation* //表示对注解中的参数进行保留
//表示不混淆上面声明的两个类,这两个类基本用不上,用于接入Google原生的一些服务
-keep public class com.google.vending.licensing.ILicensingService
-keep public class com.android.vending.licensing.ILicensingService # For native methods, see http://proguard.sourceforge.net/manual/examples.html#native
//不混淆任何包含native方法的类的类名以及native方法名,我们已验证过了
-keepclasseswithmembernames class * {
native <methods>;
} # keep setters in Views so that animations can still work.
# see http://proguard.sourceforge.net/manual/examples.html#beans
//不混淆任何一个View中的setXxx()和getXxx()方法
//属性动画需要有相应的setter和getter的方法实现,混淆了就无法工作了
-keepclassmembers public class * extends android.view.View {
void set*(***);
*** get*();
} # We want to keep methods in Activity that could be used in the XML attribute onClick
//表示不混淆Activity中参数是View的方法
//如android:onClick=”click”,用户点击按钮会调用Activity中的click(View view)方法
//如果这个方法被混淆的话就找不到了
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
} # For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations
//不混淆枚举中的values()和valueOf()方法
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
//不混淆Parcelable实现类中的CREATOR字段
// CREATOR字段是绝对不能改变的(包括大小写),否则整个Parcelable工作机制都会失败
-keepclassmembers class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator CREATOR;
}
//表示不混淆R文件中的所有静态字段
// R文件是通过字段来记录每个资源的id的,字段名若被混淆了,id就找不到了
-keepclassmembers class **.R$* {
public static <fields>;
} # The support library contains references to newer platform versions.
# Dont warn about those in case this app is linking against an older
# platform version. We know about them, and they are safe.
//版本比较低在打包时就会给予警告
//不过support包中所有的代码都在版本兼容性上做足了判断
//因此不用担心代码会出问题,所以直接忽略警告就可以了
-dontwarn android.support.**
4. 自定义混淆规则
我们可以修改proguard-android.txt中的规则,但是直接在proguard-android.txt中修改会影响对本机上所有项目的混淆规则,app模块目录下都有一个proguard-rules.pro文件,这个文件就是用于让我们编写只适用于当前项目的混淆规则的,下面我们对混淆规则做一次修改。
4.1 对MyFragment类进行完全保留,不混淆其类名、方法名、以及变量名
//keep后声明完整的类名,然后保留类中的所有内容可以使用*通配符实现
-keep class com.example.guolin.androidtest.MyFragment {
*;
}
4.2 对Utils类中的未调用方法进行保留,防止其被移除掉
//使用keepclassmembers关键字,后跟Utils完整类名,然后在内部声明未调用的方法
-keepclassmembers class com.example.guolin.androidtest.Utils {
public void methodUnused();
}
4.3 对第三方库进行保留,不混淆android-support库
//引入第三方库,一种是通过本地jar包引入的,一种是通过remote引入
//这两种方式没什么区别,要保留代码都可以使用**这种通配符来实现,支持extends关键字
-keep class <第三方包名>.<类名> {
*;
} -keep class android.support.** {
*;
}
5. 拓展资料
虽说上面表格已经解释的很详细了,但是很多人对于keep和keepclasseswithmembers这两个关键字的区别还是搞不懂。确实,它们之间用法有点太像了,唯一的区别就在于类中声明的成员存不存在,我们还是通过一个例子来直接地看一下。
//保留所有含有native方法的类的类名和native方法名
//而如果某个类中没有含有native方法,那就还是会被混淆
-keepclasseswithmember class * {
native <methods>;
}
//所有类的类名都不会被混淆了
//因为keep看到class *就认为应将所有类名进行保留,不关心该类是否含有native方法
//当然这样写只会保证类名不会被混淆,类中的成员还是会被混淆的
-keep class * {
native <methods>;
}
本文转载整理自郭大侠博客:http://blog.csdn.net/guolin_blog/article/details/50451259
Androd安全——混淆技术完全解析的更多相关文章
- Android安全攻防战,反编译与混淆技术完全解析(下)
在上一篇文章当中,我们学习了Android程序反编译方面的知识,包括反编译代码.反编译资源.以及重新打包等内容.通过这些内容我们也能看出来,其实我们的程序并没有那么的安全.可能资源被反编译影响还不是很 ...
- Android安全攻防战,反编译与混淆技术完全解析(上)
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/49738023 之前一直有犹豫过要不要写这篇文章,毕竟去反编译人家的程序并不是什么值 ...
- Android安全攻防战,反编译与混淆技术全然解析(下)
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/50451259 在上一篇文章其中,我们学习了Android程序反编译方面的知识,包括 ...
- Androd安全——反编译技术完全解析
)第二步成功后我们会发现在当前目录下多了一个<APKName>文件夹,这个文件夹中存放的就是反编译的结果了.我们可以打开AndroidManifest.xml.res/layout即可查看 ...
- 字符串混淆技术应用 设计一个字符串混淆程序 可混淆.NET程序集中的字符串
关于字符串的研究,目前已经有两篇. 原理篇:字符串混淆技术在.NET程序保护中的应用及如何解密被混淆的字符串 实践篇:字符串反混淆实战 Dotfuscator 4.9 字符串加密技术应对策略 今天来 ...
- 秋色园QBlog技术原理解析:性能优化篇:缓存总有失效时,构造持续的缓存方案(十四)
转载自:http://www.cyqdata.com/qblog/article-detail-38993 文章回顾: 1: 秋色园QBlog技术原理解析:开篇:整体认识(一) --介绍整体文件夹和文 ...
- 字符串混淆技术在.NET程序保护中的应用及如何解密被混淆的字符串
Visual Studio提供的Dotfuscator保护程序,可以对用户代码中包含的字符串进行加密.比如下面的例子,为了找到这个程序的注册算法,用.NET Reflector加载程序集后,发现代码中 ...
- ProGuard代码混淆技术详解
前言 受<APP研发录>启发,里面讲到一名Android程序员,在工作一段时间后,会感觉到迷茫,想进阶的话接下去是看Android系统源码呢,还是每天继续做应用,毕竟每天都是画UI ...
- 最快下载速度100Mbps!4G LTE技术全解析
1导读,关于4G的几个关键概念 [PConline资讯]100Mbps下载速度是什么概念?比3G网速快50倍又是什么概念?比3G通信方式更灵活.通信频谱更宽绰.通信质量更高效.通信费用更便宜是怎样一个 ...
随机推荐
- Ienumerable和Ienumerator的使用
using UnityEngine; using System.Collections; public class TestCoroutine : MonoBehaviour { void Start ...
- u-boot分析(六)----时钟初始化
u-boot分析(六) 上篇博文我们按照210的启动流程,分析到了关闭看门狗,今天我们继续按照u-boot的启动流程进行分析,今天我们会主要分析时钟的初始化. 今天我们会用到的文档: 1. ...
- mysql:用cmd启动mysql服务被拒绝原因
原因是命令行的权限不够,需要以管理员模式运行,然后输入net start mysql 即可启动mysql服务
- 中兴ZXR10 6905核心交换机配置案例
Connecting to 192.168.0.254:23...Connection established.To escape to local shell, press 'Ctrl+Alt+]' ...
- 微软提供的 Web 版 Raspberry Pi 模拟器
https://docs.microsoft.com/en-gb/azure/iot-hub/iot-hub-raspberry-pi-web-simulator-get-started#overvi ...
- python绘图 matplotlib教程
mark一个很好的python绘图教程 https://liam0205.me/2014/09/11/matplotlib-tutorial-zh-cn/
- jmeter参数化读取数据进行多次运行
jmeter参数化数据,可以使用csv,还可以使用数据库的方式 1.使用csv读取数据 在线程组中,配置原件中,选择csv data set config 1.本地创建了16个数据,存为test.tx ...
- 1.5配置NetBackup数据库备份策略(nbu策略catalog)
1.5配置NetBackup数据库备份策略 建议定期备份NetBackup的索引数据库Catalog,以确保故障时的有效恢复.从Javaconsole可以进入备份NetBackup内部数据库配置窗口, ...
- 0x40二分法
二分模板一共有两个,分别适用于不同情况.算法思路:假设目标值在闭区间[l, r]中, 每次将区间长度缩小一半,当l = r时,我们就找到了目标值. 版本1 在单调递增序列a中查找>=x的数中最小 ...
- python3中使用HTMLTestRunner.py报ImportError: No module named 'StringIO'的解决办法
.原因是官网的是python2语法写的,看官手动把官网的HTMLTestRunner.py改成python3的语法: 参考:http://bbs.chinaunix.net/thread-415474 ...