Android 代码混淆Proguard

news2024/11/29 0:29:58

混淆概念

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 //是否debug
            jniDebuggable 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 = 8000


def 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:
                break
        else:
            continue
    result = '\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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/85344.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【POJ No. 1743】音乐主题 Musical Theme

【POJ No. 1743】音乐主题 Musical Theme 北大OJ 题目地址 【题意】音乐旋律被表示为N &#xff08;1≤N ≤20000&#xff09;个音符的序列&#xff0c;它们是[1, 88]内的整数&#xff0c;每个音符都代表钢琴上的一个键。许多作曲家都围绕一个重复的主题谱写音乐&#xff0c;该…

建木HA部署

背景 在建木v2.6.1之前&#xff0c;建木Server仅支持单机部署&#xff0c;如果出现单机故障&#xff0c;难以应用于在线场景&#xff0c;并且单机压力过大时&#xff0c;会影响系统延展性。 什么是HA HA&#xff08;High Availability Cluster&#xff09;是高可用集群系统的…

【软件测试】开发人员不鸟自己?看看资深测试如何做的......

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 测试人员经常抱怨开…

jsp+servlet+mysql实现的新闻发布管理系统源码+运行教程+文档

今天给大家演示一下一款由jsp servlet mysql实现的新闻发布管理系统&#xff0c;主要实现了前台游客浏览新闻、评论新闻&#xff0c;后台管理员管理新闻等功能&#xff0c;新闻有热点新闻、最新更新等方式在首页展示&#xff0c;还有幻灯片展示重大新闻等功能&#xff0c;满足了…

一招解决开发环境问题——远程容器开发指南

前言 使用C作为主要开发语言的程序猿们应该会认同搭建开发环境是一件烦人的事情。为了编译一个程序不仅需要下载各种依赖包&#xff0c;还可能面临本地系统不兼容、编译器版本不一致、包版本冲突等各种问题。笔者在运营iLogtail开源社区的过程中发现开发和调试环境问题也是成员…

web网页设计期末课程大作业——HTML+CSS+JavaScript美食餐饮文化主题网站设计与实现

&#x1f468;‍&#x1f393;静态网站的编写主要是用HTML DIVCSS JS等来完成页面的排版设计&#x1f469;‍&#x1f393;,常用的网页设计软件有Dreamweaver、EditPlus、HBuilderX、VScode 、Webstorm、Animate等等&#xff0c;用的最多的还是DW&#xff0c;当然不同软件写出的…

为云原生插上翅膀,天翼云弹性存储CStor-CSI助力容器腾飞

云原生是一种新型技术体系&#xff0c;已成为云计算未来的发展方向&#xff0c;越来越多的数字化项目与云原生紧密相连。作为云原生的基座&#xff0c;容器是必不可少的核心技术。然而&#xff0c;以Docker为代表的容器引擎&#xff0c;并不能满足大批量的容器业务需求&#xf…

推荐一套yyds的Java学习资料(非常经典)

Java 是全球最受欢迎的编程语言之一&#xff0c;在世界编程语言排行榜 TIOBE 中&#xff0c;Java 一直霸占着前三名&#xff0c;有好多年甚至都是第一名。 最近几年&#xff0c;全球约有 1/3 的专业程序员将 Java 作为主要编程语言&#xff0c;这一比例在我国更是高达 1/2&…

CTFSHOW web入门 java反序列化篇 web855

web855 得到源码后看到readObject里面有两条路可以走 1、写文件&#xff0c;文件名固定&#xff0c;文件内容开头固定后面内容可以通过write写入 2、执行命令&#xff0c;但是shellcodoe是不可控的&#xff08;static&#xff09; 如果两条路分开来看都没啥可利用的价值&…

ai绘画新功能上线,说一句话就能生成好看的AI画作

ai绘画可以将自己的图片生成二次元&#xff0c;还可以通过关键词描述生成好看的画作&#xff0c;这些我们都早已尝试过了&#xff0c;并且也玩得不亦乐乎&#xff0c;但AI绘画还能进行语音创作&#xff0c;只需要同AI说一句话&#xff0c;它就能创造出相关的画作&#xff0c;所…

【Linux开发笔记】VSCode+WSL——Windows搭建最轻量便捷的Ubuntu/Linux开发环境

1.概述 我们一般搭建Ubuntu开发环境都是采用VMware或者VirtualBox的虚拟机安装Ubuntu的方案&#xff0c;但是这样的方案会有几个弊端&#xff1a; 安装、启动慢&#xff1b;使用图形桌面时卡顿、鼠标不跟手、打字有延迟&#xff1b;磁盘空间占用比较大&#xff1b;内存资源占用…

安卓开发Android studio学习笔记21:ViewPager两种方式实现引导页

实现引导页一、ViewPager实现引导页第一步&#xff1a;创建三个xml1.page1.xml2.page2.xml3.page3.xml第二步&#xff1a;创建适配器GuideAdapter第三步&#xff1a;创建引导页原点1.activity_guide.xml2.GuideActivity.java二、 ViewPager&#xff08;2&#xff09;实现引导页…

[附源码]Node.js计算机毕业设计电影售票管理系统Express

项目运行 环境配置&#xff1a; Node.js最新版 Vscode Mysql5.7 HBuilderXNavicat11Vue。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等等。 环境需要 1.运行环境&#xff1a;最好是Nodejs最新版&#xff0c;我…

Docker中的bridge模式,可以这么设置

最近有几个已经就业的小伙伴&#xff0c;过来问千锋健哥关于Docker网络配置的问题&#xff0c;他们在实际开发中还是有些疑问。关于Docker网络这一块的内容确实很多&#xff0c;为了让大家搞清楚这个问题&#xff0c;健哥准备搞几篇系列文章&#xff0c;来为各位小伙伴解惑。这…

小游戏开发者流量变现指南

2018年微信在其6.6.1版本中宣布支持小游戏&#xff0c;之后的几年&#xff0c;但凡能掀起各大社交平台上病毒式传播的&#xff0c;几乎都是小游戏。 小游戏玩法简单&#xff0c;传播机制简单&#xff0c;套路简单&#xff0c;连赚钱的本质也简单。就拿近期火爆的《羊了个羊》小…

程序员也可以很浪漫,精选10个圣诞节特效及源码

最近离圣诞节不远了、整理了一些关于圣诞相关的前端特效网页设计和小游戏的代码送大家、直接上效果吧。 代码过长的 可预览获取 圣诞节快乐 - 文字渐入动画 <body><svg viewport"0 0 300 300"><text class"Merry" x"150" y&qu…

货淋室及货通道维护要点有哪些

货淋室及货淋通道维护要点&#xff0c;货淋室是货物进入洁净室所必需的通道&#xff0c;它可以减少货物进出洁净室所带来的污染问题。 货淋室及货淋通道维护要点&#xff1a; 1、定期使用仪器测定设备的各项技术指标&#xff0c;如不符合技术参数要求应及时予以处理。 2、根…

高低jdk版本中jndi注入(下)

目录 0x01 绕过高版本JDK&#xff08;8u191&#xff09;限制 如下两种绕过方式&#xff1a; 0x02 利用本地恶意Class作为Reference Factory 2.1 攻击利用 1. 服务端 2. 服务端 2.2 几种变体的表达式 调试分析 小结 0x03 利用LDAP返回序列化数据&#xff0c;触发本地Gadg…

nacos--基础--5.1--集成--SpringCloud--配置管理、服务发现、服务注册

nacos–基础–5.1–集成–SpringCloud–配置管理、服务发现、服务注册 代码位置 https://gitee.com/DanShenGuiZu/learnDemo/tree/master/nacos-learn/nacos-spring-cloud1、介绍 主要面向 Spring 的使用者通过2个实例&#xff0c;来介绍nacos和Spring的集成 配置管理服务注册…

运行时发现文件路径输出404

运行时发现文件路径输出404 tomcat不能显示中文原因主要是编码的问题&#xff0c; 因为Tomcat5的http Connector所用的URI解码默认用的是 ISO-8859-1&#xff0c; 而一般浏览器默认用的发送编码为UTF-8&#xff0c;这样问题就出现了&#xff0c; 初步的解决方法如下&#xff1a…