混淆概念

Android代码混淆,又称Android混淆,是伴随着Android系统的流行而产生的一种APP保护技术,用于保护APP不被破解和逆向分析。
在Android的具体表现就是打包时,将项目里的包名、类名、变量名根据混淆规则进行更改,使反编译工具反编译出来的代码人难以阅读,从而达到防止被逆向破解的目的。

在Android里面,由于AndroidStudio集成了ProGuard,因此我们最常用,最简单的混淆是ProGuard混淆。
ProGuard混淆主要包括有四个功能:

  1. 压缩(Shrink):用于检测和删除没有使用的类、字段、方法和属性。
  2. 优化(Optimize):对于字节码进行优化,并且移除无用指令。
  3. 混淆(Obfuscate):使用a,b,c等名称对类,字段和方法进行重命名。
  4. 预检(Preverify):主要是在Java平台上对处理后的代码进行预检。

综上我们可以总结出混淆的两大作用:

  1. 安全,提高了反编译&逆向破解的难度。
  2. 压缩代码,删除无用的代码,简单的代码重命名,都可以减少Apk体积

其实混淆还有另一个妙用,也是今天我们重点要说的:
不同的混淆规则可以编译完全不同的代码,降低代码重复率,用于制作马甲包。

开启混淆

在主模块的build.gradle中添加如下代码,即可开启混淆。

android{//...buildTypes {release {debuggable false //是否debugjniDebuggable false // 是否打开jniDebuggable开关minifyEnabled true  //代码压缩,混淆shrinkResources true //资源压缩zipAlignEnabled true //压缩优化proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'signingConfig signingConfigs.release //签名}debug {signingConfig signingConfigs.release //签名}} //...
}

配置混淆开关的是minifyEnabled。
proguardFiles用于指定混淆规则,自动使用默认的混淆规则,而我们可以在proguard-rules.pro中自定义自己的混淆规则。

混淆规则

常见的混淆规则应该主要包含如下四个部分:

  1. 基本配置,设定混淆的规则等,基本配置是每个混淆文件必须存在的。
  2. 基本的keep项,多数Android工程都需要非混淆的内容,包括有四大组件,AndroidX等内容。
  3. 第三方库sdk的混淆白名单,根据对应文档添加即可(新版本构建工具可以在sdk内部添加,因此大部分sdk新版本都无须再添加)。
  4. 其他需要不混淆的内容,包括:实体类,json解析类,WebView及js的调用模块,与反射相关的类和方法。

混淆的详细规则可以参考:ProGuard的官方文档
混淆指令的详细解释可以参考:混淆必知必会

混淆白名单

指定一些包名、类名、变量等不可以被混淆。假设没指定白名单就进行混淆打包,而某某类的类名被混淆了(假设变成了a),那么可能其他引用或使用该类的类就找不到该类,说不定应用就会因此崩溃或是导致相应的功能无法使用。
比如我们通过反射调用了一个类,如果该类打包时被混淆,那么运行时必定会找不到该类i,进而导致App异常。

打包效果

proguard-rules.pro中配置完常用混淆后,我们可以打一个release包。(每次混淆修改后,打包前注意clean)
打包成功后在AndroidStudio中双击打开apk,点击classes.dex就可以看到混淆后的代码,包名,类名,方法都变成了近似于“乱码”。

打印混淆信息

混淆后虽然增强了App的安全性,上线后同时也会导致一些“副作用”:

  1. 日志打印信息不准确
  2. 报错信息无法定位具体位置

我们可以在proguard-rules.pro中添加如下代码,混淆后,就会在指定文件中输入混淆信息。
这对于我们排查线上问题有很大帮助,诸如bugly等异常上报平台,都可以上传mapping.txt帮助在错误信息中定位代码。

-printseeds proguardbuild/print_seeds.txt #未混淆的类和成员
-printusage proguardbuild/print_unused.txt #列出从 apk 中删除的代码
-printmapping proguardbuild/print_mapping.txt #混淆前后的映射,生成映射文件

混淆出不同的代码

实际上基本混淆配置完只要不去修改,相同代码每次打包得到的代码都是一样的。
对于单包常规开发来说,这是没有问题的。
但是实际上绝大多数App都需要马甲包去增加搜索关键字,进而达到引流效果。
然而马甲包上架有一个始终无法回避问题:代码相似度,在审核严格的平台,代码相似度过高甚至会导致主包下架,造成无法挽回的损失。

降低代码相似度,常规来说有两个方案:

  1. 成本高&低效率的一点点改代码。
  2. 暴力的生成垃圾代码,增大分母,降低百分比。

如何通过混淆来解决这个问题呢?核心混淆规则如下:

# 指定一个文本文件,其中所有有效字词都用作混淆字段和方法名称。
# 默认情况下,诸如“a”,“b”等短名称用作混淆名称。
# 使用模糊字典,您可以指定保留关键字的列表,或具有外来字符的标识符,
# 例如: 忽略空格,标点符号,重复字和#符号后的注释。
# 注意,模糊字典几乎不改善混淆。 有些编译器可以自动替换它们,并且通过使用更简单的名称再次混淆,可以很简单地撤消该效果。
# 最有用的是指定类文件中通常已经存在的字符串(例如'Code'),从而减少类文件的大小。 仅适用于混淆处理。
-obfuscationdictionary proguardbuild/pro_package.txt# 指定一个文本文件,其中所有有效词都用作混淆类名。 与-obfuscationdictionary类似。 仅适用于混淆处理。
-classobfuscationdictionary proguardbuild/pro_class.txt# 指定一个文本文件,其中所有有效词都用作混淆包名称。与-obfuscationdictionary类似。 仅适用于混淆处理。
-packageobfuscationdictionary proguardbuild/pro_func.txt

简单来说我们可以指定一个txt文件作为混淆字典,混淆过程中修改的包名,类,方法都取自于该字典。只要我们每个马甲包的字典不一致,我们自然能得到代码相似度低的马甲包。

你可以简单粗暴的Copy中的别人的混淆字典:丧心病狂的Android混淆文件生成器

打包出来的效果如下:

Tips:相同的混淆字典打包出来的代码仍是一致的,因此你每个马甲包都应该使用不同的混淆字典。

脚本生成

如果你有几百个马甲包,Copy别人的迟早“山穷水尽”。
作为程序员,这些都不应该难到我们。Python脚本随机生成就是啦~


import random
import string
import os
'''
脚本生成混淆字典
'''//生产8000个不重复的字符串
totalNum = 8000def main():dir = "../app/proguardbuild"createProRules(dir+'/pro_package.txt')createProRules(dir+'/pro_class.txt')createProRules(dir+'/pro_func.txt')def createProRules(fileName):dirName = os.path.dirname(fileName)if not os.path.exists(dirName):os.mkdir(dirName)'''生成totalNum个随机不重复的字符串:return:'''new_list = []while 1:value = ''.join(random.sample(string.ascii_letters +string.digits, random.randint(1, 8)))  //生成1~8位数随机字符串if value not in new_list:new_list.append(value)if len(new_list) == totalNum:breakelse:continueresult = '\n'.join(new_list)file = open(fileName, 'w', encoding='UTF-8')file.write(result)file.close()if __name__ == '__main__':main()

使用Python脚本生成混淆字典后,打包效果如下:

至此,你再也不用效率低下的去改代码,也不用生成一堆垃圾代码让“强迫症”难受。

混淆模板

我的混淆模板,仅供参考~

# ------------------------------基本指令区---------------------------------
-optimizationpasses 5 #指定压缩级别
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/* #混淆时采用的算法
-verbose #打印混淆的详细信息
-dontoptimize #关闭优化
-keepattributes *Annotation* #保留注解中的参数
-keepattributes *Annotation*,InnerClasses # 保持注解
-keepattributes Signature  # 避免混淆泛型, 这在JSON实体映射时非常重要
-ignorewarnings # 屏蔽警告
-keepattributes SourceFile,LineNumberTable # 抛出异常时保留代码行号
#混淆时不使用大小写混合,混淆后的类名为小写(大小写混淆容易导致class文件相互覆盖)
-dontusemixedcaseclassnames#未混淆的类和成员
-printseeds proguardbuild/print_seeds.txt
#列出从 apk 中删除的代码
-printusage proguardbuild/print_unused.txt
#混淆前后的映射,生成映射文件
-printmapping proguardbuild/print_mapping.txt# 指定一个文本文件,其中所有有效字词都用作混淆字段和方法名称。
# 默认情况下,诸如“a”,“b”等短名称用作混淆名称。
# 使用模糊字典,您可以指定保留关键字的列表,或具有外来字符的标识符,
# 例如: 忽略空格,标点符号,重复字和#符号后的注释。
# 注意,模糊字典几乎不改善混淆。 有些编译器可以自动替换它们,并且通过使用更简单的名称再次混淆,可以很简单地撤消该效果。
# 最有用的是指定类文件中通常已经存在的字符串(例如'Code'),从而减少类文件的大小。 仅适用于混淆处理。
-obfuscationdictionary proguardbuild/pro_package.txt# 指定一个文本文件,其中所有有效词都用作混淆类名。 与-obfuscationdictionary类似。 仅适用于混淆处理。
-classobfuscationdictionary proguardbuild/pro_class.txt# 指定一个文本文件,其中所有有效词都用作混淆包名称。与-obfuscationdictionary类似。 仅适用于混淆处理。
-packageobfuscationdictionary proguardbuild/pro_func.txt# -------------------------------基本指令区--------------------------------#---------------------------------默认保留区---------------------------------
#继承activity,application,service,broadcastReceiver,contentprovider....不进行混淆
-keep public class * extends android.app.Activity
-keep public class * extends androidx.fragment.app.Fragment
-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.view.View
-keep class android.support.** {*;}# androidx 混淆
-keep class com.google.android.material.** {*;}
-keep class androidx.** {*;}
-keep public class * extends androidx.**
-keep interface androidx.** {*;}
-keep class * implements androidx.** {*;
}
-dontwarn com.google.android.material.**
-dontnote com.google.android.material.**
-dontwarn androidx.**
-printconfiguration
-keep,allowobfuscation interface androidx.annotation.Keep
-keep @androidx.annotation.Keep class *
-keepclassmembers class * {@androidx.annotation.Keep *;
}#不混淆View中的set***() 和 get***()方法 以保证属性动画正常工作  某个类中的某个方法不混淆
#自定义View的set get方法 和 构造方法不混淆
-keep public class * extends android.view.View{*** get*();void set*(***);public <init>(android.content.Context);public <init>(android.content.Context, android.util.AttributeSet);public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclasseswithmembers class * {public <init>(android.content.Context, android.util.AttributeSet);public <init>(android.content.Context, android.util.AttributeSet, int);
}
#这个主要是在layout 中写的onclick方法android:onclick="onClick",不进行混淆
-keepclassmembers class * extends android.app.Activity {public void *(android.view.View);
}#保持 Serializable 不被混淆
-keepnames class * implements java.io.Serializable
#实现Serializable接口的类重写父类方法保留
-keepclassmembers class * implements java.io.Serializable {static final long serialVersionUID;private static final java.io.ObjectStreamField[] serialPersistentFields;private void writeObject(java.io.ObjectOutputStream);private void readObject(java.io.ObjectInputStream);java.lang.Object writeReplace();java.lang.Object readResolve();
}
# 保留R文件中所有静态字段,以保证正确找到每个资源的ID
-keepclassmembers class **.R$* {public static <fields>;
}-keepclassmembers class * {void *(*Event);
}#保留枚举类中的values和valueOf方法
-keepclassmembers enum * {public static **[] values();public static ** valueOf(java.lang.String);
}
#保留Parcelable实现类中的Creator字段,以保证Parcelable机制正常工作
-keep class * implements android.os.Parcelable {public static final android.os.Parcelable$Creator *;
}#保持 Parcelable 不被混淆
-keep class * implements android.os.Parcelable {public static final android.os.Parcelable$Creator *;
}#不混淆包含native方法的类的类名以及native方法名
-keepclasseswithmembernames class * {native<methods>;
}#避免log打印输出
-assumenosideeffects class android.util.Log {public static *** v(...);public static *** d(...);public static *** i(...);public static *** w(...);
}
#对含有反射类的处理
#--------------------------------默认保留区--------------------------------------------#----------------------------- WebView(项目中没有可以忽略) -----------------------------
#webView需要进行特殊处理
# WebView
-dontwarn android.webkit.WebView
-dontwarn android.net.http.SslError
-dontwarn android.webkit.WebViewClient
-keep public class android.webkit.WebView
-keep public class android.net.http.SslError
-keep public class android.webkit.WebViewClient
#在app中与HTML5的JavaScript的交互进行特殊处理
#我们需要确保这些js要调用的原生方法不能够被混淆,于是我们需要做如下处理:
-keepclassmembers class com.deepocean.tplh5.helper.JsInterfaceHelper {<methods>;
}
#----------------------------- WebView(项目中没有可以忽略) -----------------------------#----------------------------- 实体类不可混淆 ------------------------------------------
#添加实体类混淆规则
# Application classes that will be serialized/deserialized over Gson
-keep class **.entity.** { *; }
-keep class **.bean.** { *; }
#----------------------------- 实体类不可混淆 ------------------------------------------#----------------------------- 第三方类库 ------------------------------------------
#添加第三方类库的混淆规则
#Adjust sdk
-keep class com.adjust.sdk.**{ *; }
-keep class com.google.android.gms.common.ConnectionResult {int SUCCESS;
}
-keep class com.google.android.gms.ads.identifier.AdvertisingIdClient {com.google.android.gms.ads.identifier.AdvertisingIdClient$Info getAdvertisingIdInfo(android.content.Context);
}
-keep class com.google.android.gms.ads.identifier.AdvertisingIdClient$Info {java.lang.String getId();boolean isLimitAdTrackingEnabled();
}
-keep public class com.android.installreferrer.**{ *; }
# OkHttp3 去掉缺失类警告
-dontwarn org.bouncycastle.**
-dontwarn org.conscrypt.**
-dontwarn org.openjsse.javax.net.ssl.**
-dontwarn org.openjsse.net.ssl.**
#----------------------------- 第三方类库 ------------------------------------------

参考

ProGuard的官方文档
混淆必知必会
Android代码混淆配置:ProGuard

Android 代码混淆Proguard相关推荐

  1. android加密墙,Android代码混淆加密配置(Proguard文件解析)

    Android代码混淆加密配置(Proguard文件解析) Android代码混淆加密配置(Proguard文件解析) 为了防止自己的APP被轻易反编译,我们需要对APK进行混淆,或者特殊加密处理.可 ...

  2. 使用ProGuard进行Android代码混淆(保姆级教程)

    使用ProGuard进行Android代码混淆(保姆级教程) 一.ProGuard的功能 ProGuard是一个混淆代码的开源项目,主要是用来对java代码进行混淆,当然也就可以对java代码进行混淆 ...

  3. android aidl混淆代码,Android代码混淆

    什么是代码混淆 代码混淆就是将代码中的各种元素,如变量,方法,类和包的名字改写成无意义的名字,增加项目反编译后被读懂的难度. Android代码混淆使用ProGuard工具,ProGuard是一个压缩 ...

  4. android代码混淆个人总结及踩坑

    android代码混淆个人总结及踩坑 前言 公司项目使用组件化开发的形式,需要对自己负责的模块进行一些混淆配置,关于混淆相信做android开发的都或多或少有过一些接触,通过对混淆文件的配置从而将代码 ...

  5. android 混淆后的机制,Android 代码混淆机制

    Android 代码混淆机制 由于Android项目是基于java语言的,而java属于高层抽象语言,易于反编译,其编译后的程序包包含了大量的源代码变量.函数名.数据结构等信息,根据其编译打包后的AP ...

  6. android混淆语法(android代码混淆工具)

    android 代码混淆算法有哪些 根据SDK的版本不同有2中不同的代码混淆方式,以上的proguard.cfg参数详解中所涉及到的信息是在较低版本SDK下的混淆脚本,事实上在高版本的SDK下混淆的原 ...

  7. Android 代码混淆语法讲解及常用模板(转)

    转载请注明原博客地址  Android 代码混淆语法讲解及常用模板 前言 混淆对于每一个开发者来说都不陌生,对于大多数 APP 而言,在上线之前,通常会进行代码混淆,加固,防止自己的 APP 被别人轻 ...

  8. Android代码混淆配置说明

    1.为什么需要代码混淆 Proguard混淆用于保护APP不被破解和逆向分析,Proguard通过移除没有用到的代码以及通过特定规则重命名类.变量.方法来压缩.优化.混淆你的代码.这样可以让你APK更 ...

  9. Android代码混淆官方实现方法

    首先查看一下 "project.properties" 这个文件: # This file is automatically generated by Android Tools. ...

最新文章

  1. 文件夹浏览(SHBrowseForFolder)
  2. WebAssembly的Qt
  3. [省选联考 2020 A/B 卷] 信号传递(状压dp + 卡空间)
  4. java怎样访问servlet_如何访问URL并从java servlet获取响应?
  5. center os php,Center OS 7 Apache安装配置
  6. C#界面控件DotNetBar使用详解
  7. creo扫描选择多条链作为轨迹_跟阿铭学Creo产品设计,乌比莫斯之环框架模型设计你掌握了吗?...
  8. 写一个函数,输入一个年份,判断是不是闰年
  9. 算法珠玑-变位词的四种解法
  10. 计算机管理磁盘管理无法删除卷,如何解决对磁盘重新分区磁盘卷无法删除的问题...
  11. 转【JMeter】--JMeter下载及使用
  12. 去哪儿网2023正式秋招啦,来这里可以内推
  13. 新媒体运营教程:策划一场成功漂亮的活动策划
  14. HTML中padding作用
  15. setting 文件 阿里云
  16. Python中strip用法
  17. 电脑内存占用过高 解决方法
  18. 微信小程序宠物论坛6
  19. 凯捷与亚马逊云科技开启科技共创之旅,打造领跑行业数智化体系架构
  20. 安培龙冲刺创业板上市:收入依赖美的,邬若军、黎莉夫妇为实控人

热门文章

  1. 陈宗年:建智慧城市可以先从小镇入手
  2. 【观察】中建材信息再转型背后,驶入数字化时代新蓝海
  3. 做程序员明明那么赚钱,为什么会忧虑自己的未来?
  4. 撰写毕业论文中word公式编辑器的学习使用(一)——录制宏
  5. Oracle Secure Backup管理员指南 第7章 管理磁带设备
  6. github label有什么用?
  7. 阿里动态规划笔试题事后总结
  8. 一场火锅,看清了马云!
  9. u盘变成RAW格式怎么修复?u盘数据拯救
  10. java线程运行的时间片长度_CPU核心数,线程数,时间片轮转机制解读