# Obfuscator-boot **Repository Path**: eobard721/obfuscator-boot ## Basic Information - **Project Name**: Obfuscator-boot - **Description**: 对Java单体应用、安卓项目进行代码混淆 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-08-04 - **Last Updated**: 2025-10-23 ## Categories & Tags **Categories**: Uncategorized **Tags**: SpringBoot, 代码混淆, Javaparser ## README # Obfuscator-boot ## UI页面 ### 开始 1. 启动ObfuscatorApplication后端服务 2. 直接打开项目目录下`/resources/fronted/index.html`或在浏览器中访问`http://localhost:8080/fronted/index.html` 3. 执行相应混淆功能 ![image-20250911180651218](./readme_images/image-20250911180651218.png) > **⭐️⭐️⭐️⭐️⭐️注意:在执行代码混淆之前,请务必保存一份源代码或交给git管理防止因混淆出错造成源代码丢失的情况** ### 建议 * 建议每次只执行一个混淆器功能 * 玄学配置:`核心混淆器/重命名类名、重命名方法名、普通方法参数`的`是否只重命名被引用的xx`按钮 * 如果勾选这个按钮时,出现编译报错,那么就把它不勾选 * 如果不勾选这个按钮,出现编译报错,那么就把它勾选 * 如果两个都尝试过了,还是出现编译报错,那么设置`application.properties`文件中`obfuscate.enable.typeSolver.logging=true`,再次执行混淆,查看控制台日志中是否缺少相关源码类和方法,见`重命名方法名混淆器`章节`注意` ## 混淆核心功能介绍 ### 重命名类名混淆器 > **该功能会将指定包中类的名称进行随机重命名处理** ```java //1.注入核心配置类和执行器 @Autowired private ObfuscatorConfig obfuscatorConfig; @Autowired private ObfuscationExecutor executor; //2.设置java文件根路径代码(在顶级包的上一层父文件夹) obfuscatorConfig.setSourceRoot("/Users/eobardgu/IdeaProjects/MultiFramework/src/main/java"); //3.设置重命名类名参数配置类 RenameClassParams renameClassParams = RenameClassParams.builder() .isReferencedClassOnly(false) .packageList(List.of("com.eobard.controller")) .build(); //4.核心类设置对应参数 obfuscatorConfig.setRenameClassParams(renameClassParams); //5.执行混淆 executor.obfuscate(obfuscatorConfig); ``` > **isReferencedClassOnly:是否只需要重命名引用的类(true表示如果当前类被其它类引用了,那么就重命名当前类;false表示不管当前类有没有被引用都重命名)**
image-20250805110546398 image-20250805110634793
### 重命名方法名混淆器 > **该功能会将指定包中类的方法名称进行随机重命名,并在对应调用该方法的地方更改为新的方法名称** ```java //1.注入核心配置类和执行器 @Autowired private ObfuscatorConfig obfuscatorConfig; @Autowired private ObfuscationExecutor executor; //2.设置java文件根路径代码(在顶级包的上一层父文件夹) obfuscatorConfig.setSourceRoot("/Users/eobardgu/IdeaProjects/Sa-Token-Test/src/main/java"); //3.设置重命名方法名参数配置类 RenameMethodParams params = RenameMethodParams.builder() .isReferenceMethodOnly(false) .basePackage("com.eobard") .packageList(List.of("com.eobard.util","com.eobard.service")) .build(); //4.核心类设置对应参数 obfuscatorConfig.setRenameMethodParams(params); //5.执行混淆 executor.obfuscate(obfuscatorConfig); ``` > **isReferenceMethodOnly:是否只需要重命名引用的方法(true表示如果当前方法被其它方法引用了,那么就重命名当前方法;false表示不管当前方法有没有被引用都重命名)** * 原始代码
image-20250806181513836 image-20250806181527074
* 混淆代码结果
image-20250806181733980 image-20250806181749255
#### 注意 * **可变参数方法不能被混淆,eg:method(Object...args)** * **如果执行混淆遇到:`Unsolved symbol : We are unable to find the method declaration corresponding to 类.方法名(参数)`,则需要配置源码信息,参考`resources/temp/source`里面的写法补充信息即可** > **eg:Unsolved symbol : We are unable to find the method declaration corresponding to cn.dev33.satoken.config.SaTokenConfig.SaManager.getConfig()** 1. 出现上述报错,说明项目中使用了第三方库并且在你需要混淆的地方调用了该方法,那么就需要把它加入到混淆源码中。 2. 先获取当前SaManager源码的包所在路径,在**resources/temp/source**中创建对应的文件夹路径
image-20250911160558037
3. 创建对应的SaManager.java文件,并把缺失的方法补充上基本结构即可。 ```java package cn.dev33.satoken; import cn.dev33.satoken.config.SaTokenConfig; public class SaManager { public SaManager() { } //注意:不需要关注具体的实现逻辑,这里返回值是SaTokenConfig,那么同理需要在源码包中创建这个文件夹和这个类 public static SaTokenConfig getConfig() { return null; } } ``` ### 重命名字段名混淆器 > **该功能会混淆代码中引用到的字段是`static`、`final`、`static final` 声明的非空`基本类型或包装类型`的字段。** > > eg:修饰符 static/final/static final 基本类型/包装类型 字段名=值 ```java //1.注入核心配置类和执行器 @Autowired private ObfuscatorConfig obfuscatorConfig; @Autowired private ObfuscationExecutor executor; //2.设置java文件根路径代码(在顶级包的上一层父文件夹) obfuscatorConfig.setSourceRoot("/Users/eobardgu/IdeaProjects/Sa-Token-Test/src/main/java"); //3.设置重命名字段名参数配置类 RenameFieldParams params = RenameFieldParams.builder() .basePackage("com.eobard") // .packageList(List.of("com.eobard.consta")) .build(); //4.核心类设置对应参数 obfuscatorConfig.setRenameFieldParams(params); //5.执行混淆 executor.obfuscate(obfuscatorConfig); ``` > **packageList:如果不填写指定包列表会默认把basePackage中所有符合要求的字段重命名;否则只会重命名packageList中填写的包下符合要求的字段** * 原始代码
image-20250812143532219 image-20250812143555126 image-20250812143620164
* 混淆结果
image-20250812143435205 image-20250812143446910 image-20250812143502607
### 普通方法参数混淆器 > **该功能会混淆指定包中非接口、内部类的方法参数列表并在方法体中加上干扰代码,这些干扰代码(每次执行会生成新的干扰包,以Z...开头)的执行不会对原始业务代码造成影响,只会打印出等级最低的日志信息。** ```java //1.注入核心配置类和执行器 @Autowired private ObfuscatorConfig obfuscatorConfig; @Autowired private ObfuscationExecutor executor; //2.设置java文件根路径代码(在顶级包的上一层父文件夹) obfuscatorConfig.setSourceRoot("/Users/eobardgu/IdeaProjects/Sa-Token-Test/src/main/java"); //3.设置修改方法参数的参数配置类 MethodArgumentParams params = MethodArgumentParams.builder() .isReferenceMethodOnly(true) .basePackage("com.eobard") .packageList(List.of("com.eobard.util")) .minAddParamNum(1) //增加的最小参数数量 .maxAddParamNum(3) //增加的最大参数数量 .build(); //4.核心类设置对应参数 obfuscatorConfig.setMethodArgumentParams(params); //5.执行混淆 executor.obfuscate(obfuscatorConfig); ``` > **isReferenceMethodOnly:是否只需要重命名引用的方法(true表示如果当前方法被其它方法引用了,那么就重命名当前方法;false表示不管当前方法有没有被引用都重命名)** * 原始代码
image-20250807151034612 image-20250807151040587
* 混淆结果
image-20250807151046130 image-20250807151050696 image-20250807152012240
#### 注意 * 代码中使用了Lombok相关注解,eg: `@Data`,`@Slf4j`等,会存在异常情况`Unsolved symbol : We are unable to find the method declaration corresponding to xxx.getXXX()`和`Unsolved symbol in log.warn("") : log`,一般来说不会影响混淆功能,如果想要解决,可以取消使用Lombok注解或者手动添加getter、setter或使用java.util.logging.Logger日志 * 方法体中使用Java14的Record新特性 * 场景1,异常提示:`Unsolved symbol in record.xxx():Record的实体类` ,一般不会影响混淆功能,可以忽略该信息或将这个Record类型换成正常javabean类型即可 * 场景2,Record类型作为参数传递,会导致混淆失败 ```java //record类 public record ProductDto(String userId,Long productId,String secKillId) { } //方法声明 method(Integer userId,Long productId,String secKillId); //方法调用 method(productDto.userId(),productDto.productId(),productDto.secKillId()); //解决方案:将Record的字段提取成变量传递 String userId=productDto.userId(); Long productId=productDto.productId(); String secKillId=productDto.secKillId(); method(userId,productId,secKillId); ``` ### 接口与实现类方法参数混淆器 > **该功能会混淆包中父接口和子实现类的方法参数列表并在方法体中加上干扰代码,这些干扰代码(每次执行会生成新的干扰包,以Z...开头)的执行不会对原始业务代码造成影响,只会打印出等级最低的日志信息。** ```java //1.注入核心配置类和执行器 @Autowired private ObfuscatorConfig obfuscatorConfig; @Autowired private ObfuscationExecutor executor; //2.设置java文件根路径代码(在顶级包的上一层父文件夹) obfuscatorConfig.setSourceRoot("/Users/eobardgu/IdeaProjects/ProductSeckillDemo/src/main/java"); //3.设置修改接口与实现类方法参数的参数配置类 OverrideMethodArgumentParams params = OverrideMethodArgumentParams.builder() .basePackage("com.eobard") .minAddParamNum(1) //增加的最小参数数量 .maxAddParamNum(3) //增加的最大参数数量 .build(); //4.核心类设置对应参数 obfuscatorConfig.setOverrideMethodParams(params); //5.执行混淆 executor.obfuscate(obfuscatorConfig); ``` * 原始代码
image-20250808112808224 image-20250808112831384
* 混淆代码
image-20250808113003378 image-20250808113048854
#### 注意 * 如果遇到匿名内部类的方法不能被混淆 ```java new Thread(new Runnable() { @Override public void run() { xxx方法() } }) //两种方式解决: 1.改成lambda表达式形式 // 2.将xxx方法的调用改写成 当前类名.this.xxx方法() 方式调用即可解决 //原因:匿名内部类会新建作用域,JavaParser类型推导器无法正确解析作用域 ``` ### 随机重命名包名混淆器 > **该功能会将basePackage下的所有包及其子包进行随机重命名。** ```java //1.注入核心配置类和执行器 @Autowired private ObfuscatorConfig obfuscatorConfig; @Autowired private ObfuscationExecutor executor; //2.设置java文件根路径代码(在顶级包的上一层父文件夹) obfuscatorConfig.setSourceRoot("/Users/eobardgu/IdeaProjects/ProductSeckillDemo/src/main/java"); //3.设置随机重命名包名配置类 RandomRenamePackageParams params = RandomRenamePackageParams.builder() .basePackage("com.eobard") .excludePackages(Set.of("com.eobard.config","com.eobard.dto"))//这两个包及其子包都不会被随机重命名 .build(); //4.核心类设置对应参数 obfuscatorConfig.setRandomRenamePackageParams(params); //5.执行混淆 executor.obfuscate(obfuscatorConfig); ``` > **excludePackages:排除不想随机重命名的包列表;若不传,默认将basePackage下所有包进行随机重命名**
image-20250828181421246 image-20250828181433660 image-20250828181556844
### 类随机打乱混淆器 > **该功能会将basePackage下所有包及其子包的所有Java文件的所在位置进行打乱,每个文件会被随机移动到basePackage层级中的任意包下** ```java //1.注入核心配置类和执行器 @Autowired private ObfuscatorConfig obfuscatorConfig; @Autowired private ObfuscationExecutor executor; //2.设置java文件根路径代码(在顶级包的上一层父文件夹) obfuscatorConfig.setSourceRoot("/Users/eobardgu/IdeaProjects/Sa-Token-Test/src/main/java"); //3.设置类随机打乱混淆器参数配置类 ClassRandomShuffleParams params = ClassRandomShuffleParams.builder() .basePackage("com.eobard") //.excludePackages(Set.of("com.eobard.A","com.eobard.C","com.eobard.D")) .build(); //4.核心类设置对应参数 obfuscatorConfig.setClassRandomShuffleParams(params); //5.执行混淆 executor.obfuscate(obfuscatorConfig); ``` > **excludePackages:你想排除哪些包下的类不让它们进行重新分布,默认打乱basePackage下所有包的Java文件**
image-20250911175952964 image-20250911180507343
==注意:如果使用该功能混淆完后报错一般来说两个情况== * 混淆前处于同一包的Java文件彼此调用不用显式import对方;当混淆后,Java文件被拆分到不同的包中,这时候只需要使用快捷键导入对应的包即可 * 混淆前某个包中Java文件的某个方法修饰符是`protected`保护,处于同一个包的其它Java文件可以访问这个`protected`修饰符的方法;混淆后这两个类被放入到不同的包中,再次调用这个`protected`修饰符的方法会报错,让某个方法改为public * 解决方法1:把这个`protected`修饰符的包加入到排除包中 * 解决方法2:将`protected`修饰符改为`public`修饰符后进行混淆 * 解决方法3:混淆后,将它们移动到同一个包中即可 ## 混淆可选功能 ### 包移动混淆器 > **该功能会将指定包的所有Java文件移动(重命名)到不存在的目标包中** ```java //1.注入核心配置类和执行器 @Autowired private ObfuscatorConfig obfuscatorConfig; @Autowired private ObfuscationExecutor executor; //2.设置java文件根路径代码(在顶级包的上一层父文件夹) obfuscatorConfig.setSourceRoot("/Users/eobardgu/IdeaProjects/Sa-Token-Test/src/main/java"); //3.设置包移动参数配置类 PackageRemoveParams packageRemoveParams = PackageRemoveParams.builder() //是否为安卓项目,默认false; // 如果为true需要配置安卓的resourceRoot路径,会把安卓配置文件中引用到对应包中的信息一起替换 //.isAndroidProject(false) //包移动之后是否删除原始旧包,默认true删除旧包 //.delOldFiles(true) .sourcePackages(List.of("com.eobard.utils")) .targetPackages(List.of("com.eobard.util")) .build(); //4.核心类设置对应参数 obfuscatorConfig.setPackageRemoveParams(packageRemoveParams); //5.执行混淆 executor.obfuscate(obfuscatorConfig) ``` > **注意:sourcePackages和targetPackages需要一一对应,即sourcePackages的每一个包名会被转化为targetPackages的对应位置项**
image-20250805183226886 image-20250805183231250
### MNN转换器 > **该功能会将模型文件mnn转换为json文件,可以通过修改json文件中的权重值微调模型或对模型的md5值混淆,该工具类本质是通过封装系统命令执行脚本命令,MNN工具的使用文档:https://mnn-docs.readthedocs.io/en/latest/tools/convert.html** #### 环境配置 ##### pip安装(推荐使用) > **注意:通过python方式安装,请确保你的python环境在`3.6~3.10`,本机测试`3.8.18`正常运行** > > * https://github.com/alibaba/MNN/issues/1321 1. 如果你的python环境满足,则只需要运行 ```bash pip install -U MNN mnn ``` 2. 如果你的python环境不满足,则一键运行安装脚本:`resources/script/mac_install_mnn_python.sh` 3. 等到脚本运行,出现下面结果即安装成功 ```bash [INFO] ==> 验证 MNN 工具是否可用... [SUCCESS] ✅ MNN CLI 可用 (mnn) [SUCCESS] ✅ 已成功安装python版mnn库 ``` ##### 源码编译安装 1. 一键运行脚本文件:`resources/script/mac_install_mnn.sh`,以下环境为Mac或Linux系统可用 * **参数介绍:`bash mac_install_mnn.sh [可选参数1] [可选参数2]`** * 帮助文档:bash install_mnn.sh --help ```bash 用法: mac_install_mnn.sh [mnn的安装位置(可选参数)] [mnn的git地址(可选参数)] 参数: 参数1 指定安装目录 (默认: $HOME/mnn_workspace ,即用户的主目录下的mnn_workspace文件夹里) 参数2 指定仓库地址 (默认: https://github.com/alibaba/MNN) 示例: mac_install_mnn.sh 使用默认安装目录和仓库地址 mac_install_mnn.sh /opt/mnn 指定安装目录,使用默认仓库地址 mac_install_mnn.sh /opt/mnn https://github.com/alibaba/MNN 指定安装目录和仓库地址 ``` * 仓库克隆:如果git clone很慢,可以挂代理或直接到仓库下载zip文件(**如果下载zip文件,请把解压后里面的文件内容移动到$HOME/mnn_workspace/MNN里,然后再次运行脚本即可**) image-20250930184454775 * 编译:使用cmake进行编译,如果没有cmake,Mac默认使用brew install cmake安装,确保存在brew工具 * 安装:等到脚本运行,直到出现下面版本即成功 ```bash ✅ MNNConvert 编译成功! The device supports: i8sdot:1, fp16:1, i8mm: 1, sve2: 0, sme2: 1 3.2.4 #### 转换器使用 > 由于该转换器本质通过调用命令行转换,所以下面代码针对于Mac系统运行(Windows系统需要更改代码里的执行命令,即更改`MnnUtils.java中的脚本命令字符串`,经过上面编译后,Windows系统一般会生成MNNConvert.exe,将其改成对应的可执行文件即可) ![image-20250930184854004](./readme_images/image-20250930184854004.png) ##### MNN to Json ```java //1.注入核心配置类和执行器 @Autowired private ObfuscatorConfig obfuscatorConfig; @Autowired private ObfuscationExecutor executor; //2.设置MNN转换器配置类 MnnConverterParams params = MnnConverterParams.builder() //必填参数: 需要混淆的mnn文件的路径,即你自己项目中mnn文件所在的路径 .mnnModelPath("/Users/eobardgu/thai_project/action-live-mix-app-android/sdk/src/main/assets/models") //可选参数:默认为pip安装 .installEnum(PyInstallEnum.PIP) //可选参数: 克隆MNN仓库后的安装位置根路径,如果installEnum设置为SOURCE_CODE,则这里必填 .mnnInstallRoot("/Users/eobardgu/mnn_workspace/MNN/") //可选参数: 默认true为mnn转为json的过程,false为json转mnn的过程 .forwardConverter(true) //可选参数: 设置转换器的临时保存位置, // 默认位置System.getProperty("user.home")+ "/mnn_converter_output" // .converterPath("/Users/eobardgu/mnn_converter_output") //可选参数: 指定哪些mnn文件需要转换,默认把mnnModelPath下所有模型都转换为json //.mnnModelNames(List.of("Ik_v2.mnn", "Dt_v2.mnn")) .build(); //4.核心类设置对应参数 obfuscatorConfig.setMnnConverterParams(params); //5.执行混淆 executor.obfuscate(obfuscatorConfig); ``` image-20250930185751611 ##### Json to MNN * **这里会将converterPath中的json转为mnn,如果里面没文件则不能转换** * **只需要将上面的`forwardConverter`改为false即可** image-20250930185822941 ### MD5值修改混淆器 > 如果需要使用MD5修改功能,请提前安装python环境,因为该工具类本质通过调用系统命令行执行python脚本,只是将具体的调用过程封装了。 > > 如果要使用音频md5值修改,请先提前准备以下依赖: > > * Mac电脑安装ffmpeg:brew install ffmpeg > * python依赖:pip3 install pydub > > 如果要使用图片md5值修改,请先提前准备以下依赖: > > * python依赖:pip3 install opencv-python numpy pillow ```java //1.注入核心配置类和执行器 @Autowired private ObfuscatorConfig obfuscatorConfig; @Autowired private ObfuscationExecutor executor; //2.设置资源文件根路径代码 obfuscatorConfig.setResourceRoot("/Users/eobardgu/android/sdk/src/main/res"); //3.设置修改MD5值参数配置类 AlterMD5Params md5Params = AlterMD5Params.builder() .audioSourceDirs(List.of("raw", "raw-ur", "raw-en")) .imageSourceDirs(List.of("drawable-xhdpi")) .imageSaveTargetDirs(List.of("/Users/eobardgu/Desktop/混淆/t")) .build(); //4.核心类设置对应参数 obfuscatorConfig.setAlterMD5Params(md5Params); //5.执行混淆 executor.obfuscate(obfuscatorConfig) ``` > **注意事项:** > > * **如果不设置xxSaveTargetsDir默认会把修改后的文件保存到xxSourceDirs里面** > * **如果设置了xxSaveTargetsDir,则需要满足以下两种情况:** > > * **sourceDirs长度==saveTargetDirs长度:则把源路径文件夹修改后保存到目标位置文件夹** > * **sourceDirs长度>saveTargetDirs长度 && saveTargetDirs长度=1:则把源路经的所有文件夹修改保存到目标路径文件夹** > > * 如果程序运行出现**`当前系统并未安装 Python 解释器....`**错误,则需要在上面代码`第3步之前`设置对应的Python解释器,默认使用python3运行 > > ```java > public class PythonUtils { > //python2解释器 > public static final String PYTHON_EXPLAINER = "python"; > //python3解释器 > public static final String PYTHON3_EXPLAINER = "python3"; > > //默认解释器:使用python3 > private static String defaultExplainer = PYTHON3_EXPLAINER; > > //设置python解释器 > public static void setPythonExplainer(String explainer) { > defaultExplainer = explainer; > log.info("using {} to run script", defaultExplainer); > } > } ### 重命名安卓资源文件混淆器 > **该功能会把`安卓项目`资源文件夹下指定文件夹的所有文件的指定前缀替换成目标前缀,并且会把对应引用统一替换成新的文件名(eg:Java代码中R.raw.xxx和安卓资源文件xml的@drawable/xxx)** ```java //1.注入核心配置类和执行器 @Autowired private ObfuscatorConfig obfuscatorConfig; @Autowired private ObfuscationExecutor executor; //2.设置java文件和资源文件根路径代码 String sourceRoot = "/Users/eobardgu/android/sdk/src/main/java"; String resourceRoot = "/Users/eobardgu/android/sdk/src/main/res"; obfuscatorConfig.setSourceRoot(sourceRoot); obfuscatorConfig.setResourceRoot(resourceRoot); //3.设置运行项目为安卓项目 obfuscatorConfig.setRunningStatus(RunningEnum.ANDROID_PROjECT); //4.设置重命名安卓项目xml资源文件配置类 RenameResourceParams params = RenameResourceParams.builder() //resourceRoot下需要重命名资源文件的文件夹名称 .dirs(List.of("raw", "raw-en", "raw-ur","drawable")) //所有dirs中文件的公共前缀有哪些 .resourceOldPrefix(List.of("com_pxt_living", "com_pask_living")) //需要转换成新的前缀是什么 .resourceNewPrefix(List.of("com_gxp_living")) .build(); //5.核心类设置对应参数 obfuscatorConfig.setRenameResourceParams(md5Params); //6.执行混淆 executor.obfuscate(obfuscatorConfig) ``` > **注意事项:** > > * **使用该功能之后,因为涉及到xml文件的引用替换并重新写出文件,所以格式会乱,需要使用快捷键格式化一下** > > * **情况1: resourceNewPrefix.size() == resourceOldPrefix.size(), resourceOldPrefix的对应位置前缀转换为resourceNewPrefix的对应位置前缀** > * **情况2: resourceNewPrefix.size()==1 &&resourceNewPrefix.size() < resourceOldPrefix.size(), resourceOldPrefix的所有前缀全部转为resourceNewPrefix的前缀**
image-20250808151332488 image-20250808151410274