手把手教你写混淆小助手-Obfuscator

本文介绍了一种细粒度代码混淆方案,通过自定义Gradle插件和Jitpack发布的混淆注解,实现了代码级的混淆配置。该方案不仅减少了APK大小,还提升了反编译难度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

涉及的技术:

1.自定义Gradle plugin
2.Jitpack发布开源库
3.Proguard代码混淆
4.Android Gradle编译

Github地址:

Obfucator源码
obfuscator-plugin源码

一.为什么要做混淆

Android是java开发的, 所以很容易被反编译.为了提升app的安全等级, 需要对app进行混淆. 常用有3种处理方式:

  1. proguard代码混淆
  2. dexguard
  3. apk 加壳

现在市面上有很多加壳平台,可以防止app被反编译.
但是这不是万能的,因为脱壳技术也日益更新,如果app脱壳成功,代码就可以全被看到了,所以很有必要对代码进行proguard混淆. 这可以
1).可以减少apk的大小;
2)加大反编译后读取代码逻辑的难度.

二.不能混淆的几种case

app中有些代码是不能混淆的,所以我们要编写keep配置,让他保持不混淆
例如,保持com.xiaomi包下的类不被混淆

-keep class com.xiaomi.**{*;}
常见不能混淆的2种case:
  1. 反射调用到的类或者变量
  1. 用于解释Json数据的bean

比较经典的是ObjectAnimator.
像下面例子, ObjectAnimator会通过反射调用setNumber. 如果setNumber被混淆了ObjectAnimator就不起作用了.

ObjectAnimator.ofObject(this, "number", new TypeEvaluator<Long>() {
    @Override
    public Long evaluate(float fraction, Long startValue, Long endValue) {
        ...
    }
}, number);

//这里的setNumber是不能混淆的.
//如果混淆了,ObjectAnimator就不起作用了
public void setNumber(Long number) {
}

像上面示例,我们要保持setNumber方法不被混淆会比较麻烦. 一般为了图方便, 直接就让整个类都不被混淆.

三. 痛点

入上面ObjectAnimator示例,现在配置混淆有2个痛点:

  1. 配置粒度较细的混淆很困难
  2. 因为写混淆配置麻烦, 所以混淆一般都是写完一部分代码后, 再写混淆配置. 这造成写配置的时候都不记得哪些需要加上混淆配置了.

解决方案

一.混淆助手Obfuscator

Obfucator源码

混淆小助手Obfuscator可以:

  1. 控制粒度很细的混淆
  1. 写代码的时候,可以很方便的随手写上混淆.

实现原理:

实现思路是利用"注解/接口"控制混淆, 注解的优势是写代码可以随手写上; 接口的优势是类可以实现多个接口.

场景: 保持某个 "方法/变量" 不被混淆

回到上面ObjectAnimator的例子:
一共用3步实现方法不被混淆

1.先声明了一个注解 ObfuscateKeepThisField

@Inherited
public @interface ObfuscateKeepThisField {
}

2.在proguard-rules.pro中添加配置

#保留带注释的成员,适用于类和内部类
#对应声明ObfuscateKeepThisField
-keepclassmembers class * {
@com.helen.obfuscator.ObfuscateKeepThisField * ;
}

3.在setNumber上加上此注解

@ObfuscateKeepThisField
public void setNumber(Long number) {
}

这里步骤1和2 在整个App中做一次, 后续整个App中的所有类都可以用ObfuscateKeepThisField进行处理了.

按照这个套路丰富了一下注解声明,就有了混淆小助手

4048192-1a5fd7c8ad77e0d7.png
screenshot.png

Obfuscator现在支持:

  1. 类中所有public成员不混淆(不包括内部类)
  1. 类中所有成员不混淆(不包括内部类)(内部类如果不需要混淆也要加入此注释)
  2. 保留所有实现IObfuscateKeepAll接口的类,(注意接口有传递性,他的子类也会被keep)(内部类如果没有继承此接口会被混淆)
  3. 保留带注释的成员,适用于类和内部类
  4. 保留类中set/get/is函数

二.发布到Jitpack

把前面声明的注解发布到Jitpack上, 这样所有项目都可以复用. 过程很简单,几分钟完事.
Jitpack发布开源库

三.编写 Gradle plugin

obfuscator-plugin源码

上面的混淆需要手动把混淆配置拷贝到 proguard-rules.pro, 这个手动的步骤也略麻烦. Gradle plugin功能这么强大, 我们来利用 编译插件来自动完成 混淆配置.

自定义Plugin方法见文章自定义Gradle plugin

对于obfuscator-plugin,需要解决3个问题:

  1. 在"Android App&libaray"中才生效
  2. 把Jitpack中obfuscator的依赖自动加进来
  3. 生成混淆配置,并且让其生效

他的结构如下


4048192-afd5e511003c2dea.png
screenshot.png

下面分析一下实现:

1.判断是否是App或者Library

我们的App或者Library,都需要在build.gradle中声明
比如App的是

apply plugin: 'com.android.application'

如果是Library,我们会声明

apply plugin: 'com.android.library'

所以判断当前module类型就很简单了,通过判断project的plugin里有没有AppPlugin或者LibraryPlugin来实现.

Project project;
/**
 * 是否含有 Android application plugin
 * @return true:有
 */
boolean hasApp() {
    return project.plugins.withType(AppPlugin);
}

/**
 * 是否含有 Android lib plugin
 * @return true:有
 */
boolean hasLib() {
    return project.plugins.withType(LibraryPlugin);
}

2. 加入obfuscator相关依赖

Gradle plugin 做这个事很简单, 他会传给Plugin一个参数project. 用它就可以加入各种依赖.

@Override
void apply(Project project) {

    //添加仓库
    project.repositories{
        
        //obfuscator发布在 jitpack上.
        //所以需要依赖jitpack的maven仓库
        maven { url 'https://jitpack.io' }
    }
    
    //加入项目依赖
    project.dependencies {
       //引入obfuscator的注解声明
       compile 'com.github.helen-x:obfuscator:1.0'
    }

}

3.加入混淆配置

我们编译时系统会要求声明BuildType, 比如release, debug.
BuildType是Gradle的一个编译类. 从他的代码可以看到proguard配置文件是一个List.我们可以通过 proguardFile() 函数加入新的Proguard配置.

//BuildType 类
public class BuildType extends DefaultBuildType implements CoreBuildType, Serializable {

  @NonNull
  public BuildType proguardFile(@NonNull Object proguardFile) {
        getProguardFiles().add(project.file(proguardFile));
        return this;
    }
}

那我们需要做的是:

  1. 在buildType中加入Proguard
  2. 在合适的情况下生成proguard文件
在buildType中加入Proguard
project.android.buildTypes.all { buildType ->

    //获取自定义配置文件路径
    String obfuscatorConfigFile = getObfuscatorFilePath(project);
    //把我们的配置加入到 Proguard文件列表中
    buildType.proguardFile(obfuscatorConfigFile);
  }

在合适的情况下生成proguard文件

新建一个Task,用来生成Proguard文件

variants.all { variant ->

    //新建一个Task,用来生成Proguard文件
    def Task processResourcesTask = variant.outputs[0].processResources
    def addObfuscatorTask = project.task("addObfuscator${variant.buildType.name}") {
        //生成Proguard文件
        createObfuscatorConfigFile(project);
        dependsOn processResourcesTask
    }
    //加入依赖,保证我们的task在javaCompile之前运行
    javaCompile.dependsOn addObfuscatorTask
} 

生成混淆文件

//生成混淆文件
private static String createObfuscatorConfigFile(Project project) {

  final String obfuscator_path = getObfuscatorFilePath(project);
  try {
        File file = new File(obfuscator_path);
        if (file.exists()) {
            file.delete();
        }
        file.createNewFile();
        file.write("""${getObfuscatorConfigure(project)}""", Charsets.UTF_8.toString());
    } catch (e) {
        return null;
    }
  return obfuscator_path;
}

获取Proguard的具体配置内容

//获取Proguard的具体配置内容 
private final static String OBFUSCATOR_PKG_NAME = "com.helen.obfuscator";
private static String getObfuscatorConfigure(Project project) {

    LogUtil.info(project, "get obfuscator configure");
    String configs = "\n" +
            "#[Start----]obfuscate.helper 自定义混淆声明  \n" +
            "\n" +
            "-keep class " + OBFUSCATOR_PKG_NAME + ".* { *; }\n" +
            "\n" +
            "#类中所有成员不混淆(不包括内部类)\n" +
            "#对应声明 ObfuscateKeepAll\n" +
            "-keep @" + OBFUSCATOR_PKG_NAME + ".ObfuscateKeepAll class * { *; }\n" +
            "\n" +
            "#类中所有public成员不混淆(不包括内部类)\n" +
            "#对应声明ObfuscateKeepPublic\n" +
            "-keep @" + OBFUSCATOR_PKG_NAME + ".ObfuscateKeepPublic class * {   \n" +
            "  public <fields>;\n" +
            "  public <methods>;\n" +
            "}\n" +
            "\n" +
            "#保留带注释的成员,适用于类和内部类\n" +
            "#对应声明ObfuscateKeepThisField\n" +
            "-keepclassmembers class * {\n" +
            "@" + OBFUSCATOR_PKG_NAME + ".ObfuscateKeepThisField * ;\n" +
            "}\n" +
            "\n" +
            "#保留类中set/get/is函数\n" +
            "#对应声明ObfuscateKeepSetterGetter\n" +
            "-keepclassmembers @" + OBFUSCATOR_PKG_NAME + ".ObfuscateKeepSetterGetter class * {\n" +
            "    void set*(***);\n" +
            "    boolean is*(); \n" +
            "    *** get*();\n" +
            "}\n" +
            "#保留所有实现IObfuscateKeepAll接口的类,(注意接口有传递性,他的子类也会被keep)(内部类如果没有继承此接口会被混淆)\n" +
            "#对应接口 IObfuscateKeepAll\n" +
            "-keep class * implements " + OBFUSCATOR_PKG_NAME + ".IObfuscateKeepAll {\n" +
            "    *;\n" +
            "}\n" +
            " #[-----end]obfuscate.helper 自定义混淆声明   ";
    return configs;
};

至此, Obfuscator-plugin编写完毕.

混淆小助手的使用方式

一. 引入插件

1. 在build.gradle配置依赖

buildscript {
    repositories {
        jcenter()
        //1. 加入Jitpack仓库
        maven { url 'https://jitpack.io' }
    }
    dependencies {
        ...
        //2. 加入obfuscator-plugin 插件  
        classpath 'com.github.helen-x:obfuscator-plugin:1.0'
    }
}

2. build.gradle 使用plugin

//使用混淆插件  
apply plugin: 'obfuscator-plugin'

引入完毕后,就可以直接使用啦.

二. 使用方法:

场景1:类中所有public成员不混淆(不包括内部类)

@ObfuscateKeepPublic
public class TestAnnotationKeepPublic{
}

场景2:类中所有成员不混淆(不包括内部类)(内部类如果不需要混淆也要加入此注释)

@ObfuscateKeepAll
public class TestAnnotationKeepAll {
}

场景3:保留所有实现IObfuscateKeepAll接口的类,(注意接口有传递性,他的子类也会被keep)(内部类如果没有继承此接口会被混淆)

public class TestInterfaceKeep implements IObfuscateKeepAll{
}

场景4:保留带注释的成员,适用于类和内部类

public class TestAnnotationKeepThisField {
    
    public String sName;
    public static String sSName;
    private int iValue;
    
    @ObfuscateKeepThisField
    private static int iSValue;
    
    @ObfuscateKeepThisField
    private void tFunc(){
    }
}

场景5:保留类中set/get/is函数

@ObfuscateKeepSetterGetter
public class TestAnnotationKeepSetterGetter {}
淘宝花钱买的最新版!需要的拿去! This asset obfuscates your code to make it harder for bad guys to reverse engineer your projects. Specifically designed for Unity, it seamlessly links in with its build process. The top priority of this package is to work straight out of the box with no extra steps required. While other obfuscators can stop a game from working, Beebyte's obfuscator looks for specific Unity related code that must be protected. The contents of your source files are unchanged, the obfuscation targets the compiled assembly. Features: - Supports IL2CPP - Supports Assembly Definition Files (Unity 2017.3+) - Removes Namespaces without any conflicts - Recognises Unity related code that must not be changed - Renames Classes (including MonoBehaviours) - Renames Methods - Renames Parameters - Renames Fields - Renames Properties - Renames Events - String literal obfuscation - Adds fake methods - Easy and extensive customisation using the Unity inspector window - Consistent name translations are possible across multiple builds and developers - Semantically secure cryptographic naming convention for renamed members The asset works for both Unity Free and Unity Pro version 4.2.0 onwards (including Unity 5 & 2017 & 2018). Build targets include Standalone, Android, iOS, WebGL, UWP. Other platforms are not guaranteed or supported but may become supported at a future date. IL2CPP builds are much harder to reverse engineer but strings and member information (class, method names etc) are visible in the global-metadata.dat file. Obfuscation will apply to this file adding further security. Why not complement your security with the Anti-Cheat Toolkit - a great third party asset. For more information about the Obfuscator, please see the FAQ
This asset obfuscates your code to make it harder for bad guys to reverse engineer your projects. Specifically designed for Unity, it seamlessly links in with its build process. The top priority of this package is to work straight out of the box with no extra steps required. While other obfuscators can stop a game from working, Beebyte's obfuscator looks for specific Unity related code that must be protected. The contents of your source files are unchanged, the obfuscation targets the compiled assembly. Features: - Supports IL2CPP - Supports Assembly Definition Files (Unity 2017.3+) - Removes Namespaces without any conflicts - Recognises Unity related code that must not be changed - Renames Classes (including MonoBehaviours†) - Renames Methods - Renames Parameters - Renames Fields - Renames Properties - Renames Events - String literal obfuscation - Adds fake methods - Easy and extensive customisation using the Unity inspector window - Consistent name translations are possible across multiple builds and developers - Semantically secure cryptographic naming convention for renamed members The asset works for both Unity Free and Unity Pro version 4.2.0 onwards (including Unity 5 & 2017 & 2018). Build targets include Standalone, Android, iOS, WebGL, UWP. Other platforms are not guaranteed or supported but may become supported at a future date. † There is currently a bug with renaming MonoBehaviour classes on Unity 2018.2. A bug report has been submitted to Unity. IL2CPP builds are much harder to reverse engineer but strings and member information (class, method names etc) are visible in the global-metadata.dat file. Obfuscation will apply to this file adding further security. Why not complement your security with the Anti-Cheat Toolkit - a great third party asset. For more information about the Obfuscator, please see the FAQ
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值