├── .github └── workflows │ ├── build.yml │ ├── leak.yml │ └── maven.yml ├── .gitignore ├── CHANGELOG.MD ├── LICENSE ├── README.md ├── check-version.bat ├── img └── 001.png ├── package.bat ├── pom.xml ├── release └── README.md ├── src └── main │ ├── java │ └── me │ │ └── n1ar4 │ │ ├── jar │ │ └── obfuscator │ │ │ ├── Const.java │ │ │ ├── Logo.java │ │ │ ├── Main.java │ │ │ ├── analyze │ │ │ ├── DiscoveryClassVisitor.java │ │ │ ├── DiscoveryMethodAdapter.java │ │ │ ├── MethodCallClassVisitor.java │ │ │ └── MethodCallMethodVisitor.java │ │ │ ├── asm │ │ │ ├── ClassNameVisitor.java │ │ │ ├── CompileInfoVisitor.java │ │ │ ├── FieldNameVisitor.java │ │ │ ├── IntToXorVisitor.java │ │ │ ├── JunkCodeVisitor.java │ │ │ ├── MainMethodVisitor.java │ │ │ ├── MethodNameVisitor.java │ │ │ ├── ParameterVisitor.java │ │ │ ├── StringArrayVisitor.java │ │ │ └── StringVisitor.java │ │ │ ├── base │ │ │ ├── ClassField.java │ │ │ ├── ClassFileEntity.java │ │ │ ├── ClassReference.java │ │ │ └── MethodReference.java │ │ │ ├── config │ │ │ ├── BaseCmd.java │ │ │ ├── BaseConfig.java │ │ │ ├── Manager.java │ │ │ └── Parser.java │ │ │ ├── core │ │ │ ├── AnalyzeEnv.java │ │ │ ├── DiscoveryRunner.java │ │ │ ├── MethodCallRunner.java │ │ │ ├── ObfEnv.java │ │ │ ├── ObfHashMap.java │ │ │ └── Runner.java │ │ │ ├── loader │ │ │ ├── CustomClassLoader.java │ │ │ └── CustomClassWriter.java │ │ │ ├── templates │ │ │ ├── StringDecrypt.java │ │ │ └── StringDecryptDump.java │ │ │ ├── transform │ │ │ ├── ClassNameTransformer.java │ │ │ ├── DeleteInfoTransformer.java │ │ │ ├── FieldNameTransformer.java │ │ │ ├── JunkCodeTransformer.java │ │ │ ├── MainClassTransformer.java │ │ │ ├── MethodNameTransformer.java │ │ │ ├── ParameterTransformer.java │ │ │ ├── StringArrayTransformer.java │ │ │ ├── StringTransformer.java │ │ │ └── XORTransformer.java │ │ │ └── utils │ │ │ ├── ByteUtil.java │ │ │ ├── ColorUtil.java │ │ │ ├── DescUtil.java │ │ │ ├── DirUtil.java │ │ │ ├── FileUtil.java │ │ │ ├── IOUtils.java │ │ │ ├── JunkUtil.java │ │ │ ├── NameUtil.java │ │ │ ├── PackageUtil.java │ │ │ └── RandomUtil.java │ │ ├── jrandom │ │ └── core │ │ │ └── JRandom.java │ │ └── log │ │ ├── Log.java │ │ ├── LogLevel.java │ │ ├── LogManager.java │ │ ├── LogUtil.java │ │ ├── Logger.java │ │ └── LoggingStream.java │ └── resources │ ├── config.yaml │ └── thanks.txt └── test ├── README.md ├── origin.jar ├── shiro.jar ├── test-01.yaml ├── test-02.yaml ├── test-03.yaml ├── test-sb-01.yaml └── test-sb-02.yaml /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: jar obfuscator build 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | env: 7 | VERSION: "2.0.0-RC2" 8 | 9 | jobs: 10 | 11 | build: 12 | runs-on: windows-2019 13 | steps: 14 | - name: checkout the source 15 | uses: actions/checkout@v4 16 | with: 17 | path: jar-obfuscator 18 | 19 | - name: set up java 8 20 | uses: actions/setup-java@v4 21 | with: 22 | java-version: '8' 23 | distribution: 'zulu' 24 | cache: maven 25 | 26 | - name: build core 27 | run: | 28 | .\package.bat 29 | mv .\target\jar-obfuscator-${{ env.VERSION }}-jar-with-dependencies.jar jar-obfuscator-${{ env.VERSION }}.jar 30 | working-directory: jar-obfuscator 31 | 32 | - name: upload artifact 33 | uses: actions/upload-artifact@v4 34 | with: 35 | name: jar-obfuscator 36 | path: | 37 | jar-obfuscator/jar-obfuscator-${{ env.VERSION }}.jar 38 | -------------------------------------------------------------------------------- /.github/workflows/leak.yml: -------------------------------------------------------------------------------- 1 | name: leak check 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | jobs: 10 | 11 | check: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | with: 16 | fetch-depth: 0 17 | - uses: gitleaks/gitleaks-action@v2 18 | env: 19 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 20 | GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }} -------------------------------------------------------------------------------- /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | name: maven check 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v3 16 | - name: Set up JDK 8 17 | uses: actions/setup-java@v3 18 | with: 19 | java-version: '8' 20 | distribution: 'zulu' 21 | cache: maven 22 | - name: Build with Maven 23 | run: mvn -B package --file pom.xml -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | 6 | ### IntelliJ IDEA ### 7 | .idea/modules.xml 8 | .idea/jarRepositories.xml 9 | .idea/compiler.xml 10 | .idea/libraries/ 11 | *.iws 12 | *.iml 13 | *.ipr 14 | 15 | ### Eclipse ### 16 | .apt_generated 17 | .classpath 18 | .factorypath 19 | .project 20 | .settings 21 | .springBeans 22 | .sts4-cache 23 | 24 | ### NetBeans ### 25 | /nbproject/private/ 26 | /nbbuild/ 27 | /dist/ 28 | /nbdist/ 29 | /.nb-gradle/ 30 | build/ 31 | !**/src/main/**/build/ 32 | !**/src/test/**/build/ 33 | 34 | ### VS Code ### 35 | .vscode/ 36 | 37 | ### Mac OS ### 38 | .DS_Store 39 | 40 | ### OTHERS ### 41 | .idea/ 42 | log/ 43 | *.jar 44 | *.yml 45 | *.yaml 46 | cmake-*/ 47 | code-encryptor-temp/ 48 | jar-obfuscator-temp/ 49 | token.txt 50 | -------------------------------------------------------------------------------- /CHANGELOG.MD: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## 2.0.0-RC2 4 | 5 | 更新日志: 6 | 7 | - [重要] 开启配置 `useSpringBoot` 初步支持 `SpringBoot` 混淆 8 | - [重要] 开启配置 `useWebWar` 初步支持 `war` 包混淆 9 | - [优化] 优化 `jar` 压缩代码兼容 `SpringBoot` 等禁用压缩环境 10 | 11 | 感谢以下用户的贡献: 12 | 13 | - 4ra1n (https://github.com/4ra1n) 14 | 15 | 可供下载的文件由 `Github Actions` 构建,使用 `java -jar` 启动 16 | 17 | ## 2.0.0-RC1 18 | 19 | jar-obfuscator v2 初版 RC1 发布(非正式 仅供测试) 20 | 21 | 主要更新内容:简化配置和文档,删除某些不必要的功能,尽可能简化,更多功能日后更新 22 | 23 | 目前 `README.md` 和所有代码已经变更,如果有 `v1` 版本需求可以在 `release` 下载历史代码 24 | 25 | 感谢以下用户的贡献: 26 | 27 | - 4ra1n (https://github.com/4ra1n) 28 | 29 | 可供下载的文件由 `Github Actions` 构建,使用 `java -jar` 启动 30 | 31 | ## 0.2.0 32 | 33 | 支持了 `GUI` 界面,但没有过多测试,之后逐步完善 34 | 35 | 解决了目标代码写法存在 `class.getResource("/dir/")` 时得到 `null` 的问题,这是最终打包 `JAR` 部分的代码问题 36 | 37 | 更新日志: 38 | 39 | - [重要] 支持 `GUI` 界面(beta) 40 | - [功能] 字符串替换时处理反射字符串类名 41 | - [功能] 添加对泛型 `signature` 的处理 42 | - [优化] 优化 `builtin` 黑名单解析和注释 43 | - [BUG] 修复 `NATIVE` 的 `malloc` 错误 44 | - [BUG] 修复没有添加目录到 `JAR ENTRY` 的问题 45 | - [BUG] 花指令混淆部分遇到类加载报错解决 46 | - [BUG] 修复 `MANIFEST.MF` 没有完全修改的问题 47 | 48 | 感谢以下用户的贡献: 49 | 50 | - 4ra1n (https://github.com/4ra1n) 51 | - pcdlrzxx (https://github.com/pcdlrzxx) 52 | 53 | 可供下载的文件由 `Github Actions` 构建,使用 `java -jar` 启动 54 | 55 | ## 0.1.1 56 | 57 | 更新日志: 58 | 59 | - [功能] 花指令数添加简单的控制流 60 | - [功能] 花指令从单 `int` 类型拓展到更多 61 | - [功能] 花指令支持添加无意义的垃圾方法 62 | - [优化] 不应该对接口和枚举等类型进行花指令 63 | - [优化] 优化命令行输出的信息 64 | - [其他] 在 `CI` 增加 `Gitleaks` 检查 65 | - [其他] 提供 `Dockerfile` 和构建脚本 66 | 67 | 感谢以下用户的贡献: 68 | 69 | - 4ra1n (https://github.com/4ra1n) 70 | 71 | 可供下载的文件由 `Github Actions` 构建,使用 `java -jar` 启动 72 | 73 | ## 0.1.0 74 | 75 | 修复了一些已知问题,随机数支持直接调用 `CPU` 的 `RDRAND` 指令生成 76 | 77 | 注意,配置文件可能不兼容,请重新生成 78 | 79 | 更新日志: 80 | 81 | - [功能] 支持配置随机数来源为 `CPU` 的 `RDRAND` 指令 82 | - [功能] 类黑名单新增正则配置 `classBlackRegexList` 83 | - [功能] 对于错误和默认的 `AES KEY` 配置自动生成随机的 84 | - [BUG] 不应该对 `JNI` 类进行混淆 85 | - [BUG] 处理错误配置文件可能导致的异常 86 | 87 | 感谢以下用户的贡献: 88 | 89 | - 4ra1n (https://github.com/4ra1n) 90 | 91 | 可供下载的文件由 `Github Actions` 构建,使用 `java -jar` 启动 92 | 93 | ## 0.0.8-beta 94 | 95 | 修复了某些配置的 `BUG` 并参考 `Y4tacker` 议题提供隐藏功能 96 | 97 | 注意,配置文件可能不兼容,请重新生成 98 | 99 | 更新日志: 100 | 101 | - [功能] 通过添加 `SYNTHETIC` 使反编译时可以隐藏方法 102 | - [功能] 通过添加 `SYNTHETIC` 使反编译时可以隐藏字段 103 | - [功能] 解决报错并支持高版本 `JAVA` 的字节码加密功能 104 | - [BUG] 修复了开启包名混淆但不开启类名混淆无法生效的问题 105 | - [BUG] 不允许开启 `enablePackageName` 时使用字节码加密 106 | 107 | 感谢以下用户的贡献: 108 | 109 | - 4ra1n (https://github.com/4ra1n) 110 | - pcdlrzxx (https://github.com/pcdlrzxx) 111 | - Y4tacker (https://github.com/Y4tacker) 112 | 113 | 可供下载的文件由 `Github Actions` 构建,使用 `java -jar` 启动 114 | 115 | ## 0.0.7-beta 116 | 117 | 更新日志: 118 | 119 | - [功能] 记录贡献者列表并在启动时打印感谢信息 120 | - [功能] 增加一个新参数仅检查是否有新版本 121 | - [BUG] 修复花指令混淆功能某些情况抛出的异常 122 | - [优化] 错误的配置会在启动时输出原因并停止 123 | - [优化] 调整 `JNI` 日志级别减少无用信息 124 | - [优化] 检查字节码加密的 `Java 8` 环境 125 | 126 | 感谢以下用户的贡献: 127 | 128 | - 4ra1n (https://github.com/4ra1n) 129 | - pcdlrzxx (https://github.com/pcdlrzxx) 130 | 131 | 可供下载的文件由 `Github Actions` 构建,使用 `java -jar` 启动 132 | 133 | ## 0.0.6-beta 134 | 135 | 内置 `父类/接口->方法` 的映射黑名单,阻止某些 `@Override` 方法的混淆 136 | 137 | 注意,配置文件可能不兼容,请重新生成 138 | 139 | 更新日志: 140 | 141 | - [功能] 内置黑名单阻止某些类的继承/实现方法混淆 142 | - [功能] 启动时请求 `OSS` 检测是否有新版本可用 143 | - [功能] 字符串解密类的类名方法名可以配置 144 | - [功能] 优化命令行输出并允许彩色输出 145 | 146 | 感谢以下用户的贡献: 147 | 148 | - 4ra1n (https://github.com/4ra1n) 149 | 150 | 可供下载的文件由 `Github Actions` 构建,使用 `java -jar` 启动 151 | 152 | ## 0.0.5-beta 153 | 154 | 不建议开启 `enableMethodName` 配置,建议其他选项混合搭配测试 155 | 156 | 更新日志: 157 | 158 | - [BUG] 修复方法名混淆可能遇到的关键 `BUG` 159 | - [BUG] 方法 `OWNER` 和 `DESC` 替换不完善 160 | - [优化] 优化启动提示和日志提示信息 161 | - [优化] 进一步优化依赖缩小 `JAR` 包体积 162 | - [优化] 优化 `README` 文档提供更多配置教程 163 | 164 | 感谢以下用户的贡献: 165 | 166 | - 4ra1n (https://github.com/4ra1n) 167 | 168 | 可供下载的文件由 `Github Actions` 构建,使用 `java -jar` 启动 169 | 170 | ## 0.0.4-beta 171 | 172 | 修复关键 `BUG` 并完善配置文件注释和文档。之前的版本不开启类名混淆时,无法单独使用其他混淆方法。混淆配置有很多选项,其中部分配置会分析依赖和引用并修改,可能出现预期外的行为。另外有多种不修改引用的混淆方式,例如字符串加密和异或加密整数,可以单独搭配使用这些不修改引用的混淆方式 173 | 174 | 注意,配置文件可能不兼容,请重新生成 175 | 176 | 更新日志: 177 | 178 | - [BUG] 字符串加密编码不一致导致乱码问题 179 | - [BUG] 修复不开启类名混淆某些混淆无法运行问题 180 | - [BUG] 修复解压 `JAR` 时无法访问文件的问题 181 | - [优化] 优化 `README` 和配置文件中的注释 182 | - [优化] 提示 `MAC` 中不支持使用字节码加密 183 | 184 | 感谢以下用户的贡献: 185 | 186 | - 4ra1n (https://github.com/4ra1n) 187 | 188 | 可供下载的文件由 `Github Actions` 构建,使用 `java -jar` 启动 189 | 190 | ## 0.0.3-beta 191 | 192 | 修复了一些 `BUG` 补全配置文件和文档 193 | 194 | 注意,配置文件可能不兼容,请重新生成 195 | 196 | 更新日志: 197 | 198 | - [功能] 增加混淆类黑名单(具体内容参考配置注释) 199 | - [功能] 增加混淆项目根包名配置(具体内容参考配置注释) 200 | - [BUG] 修复混淆时引用处理的错误 201 | - [BUG] 修复 `module-info` 导致的报错 202 | - [BUG] 解压 `JAR` 文件可能存在报错终止 203 | - [BUG] 字符串加密处理的过程设置正确的 `flag` 204 | - [优化] 优化各种黑名单和配置选项的逻辑 205 | - [文档] 完善 `README` 中的实战配置文档 206 | 207 | 感谢以下用户的贡献: 208 | 209 | - 4ra1n (https://github.com/4ra1n) 210 | 211 | 可供下载的文件由 `Github Actions` 构建,使用 `java -jar` 启动 212 | 213 | ## 0.0.2-beta 214 | 215 | 注意,配置文件可能不兼容,请重新生成 216 | 217 | 更新日志: 218 | 219 | - [功能] 混淆指令使用简单的随机生成 220 | - [功能] 字符串加密的 `AES KEY` 可以配置 221 | - [功能] 允许配置临时目录是否自动删除 222 | - [优化] 花指令级别更细致的级别划分 223 | - [优化] 处理可能的 `MethodTooLargeException` 224 | - [优化] 删除无意义代码逻辑提高效率 225 | - [NATIVE] 解密字节码时不应该打印过多敏感信息 226 | 227 | 感谢以下用户的贡献: 228 | 229 | - 4ra1n (https://github.com/4ra1n) 230 | 231 | 可供下载的文件由 `Github Actions` 构建,使用 `java -jar` 启动 232 | 233 | ## 0.0.1-beta 234 | 235 | 初始版本(非稳定版本,测试有 BUG 欢迎反馈) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 4ra1n (Jar Analyzer Team) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Jar-Obfuscator V2 2 | 3 | gitleaks badge 4 | 5 | ![](https://github.com/jar-analyzer/jar-obfuscator/workflows/maven%20check/badge.svg) 6 | ![](https://github.com/jar-analyzer/jar-obfuscator/workflows/leak%20check/badge.svg) 7 | ![](https://img.shields.io/badge/build-Java%208-orange) 8 | ![](https://img.shields.io/github/downloads/jar-analyzer/jar-obfuscator/total) 9 | ![](https://img.shields.io/github/v/release/jar-analyzer/jar-obfuscator) 10 | 11 | [CHANGE LOG](CHANGELOG.MD) 12 | 13 | `Jar Obfuscator V2` 是一个 `JAR` 文件混淆工具 14 | 15 | - 命令行模式,简单易用 16 | - 仅单个 `JAR` 文件小于 `1 MB` 超轻量 17 | - 简洁的配置文件快速上手 18 | - 输入 `JAR` 直接输出混淆后的 `JAR` 19 | 20 | 注意:目前是 `v2` 版本,如果你需要 `v1` 版本可以从 `release` 页面下载 21 | 22 | ## 开始 23 | 24 | [前往下载](https://github.com/jar-analyzer/jar-obfuscator/releases/latest) 25 | 26 | 简单命令即可启动(第一次启动将自动生成配置文件) 27 | 28 | ```shell 29 | java -jar jar-obfuscator.jar --jar test.jar --config config.yaml 30 | ``` 31 | 32 | jar-obfuscator-pro 和 jar-obfuscator-ultra 版本正在开发中 暂时不会公开 33 | 34 | | 功能 | jar-obfuscator 开源版 | jar-obfuscator-pro | jar-obfuscator-ultra | 35 | |--------------------|--------------------|--------------------|----------------------| 36 | | 类名混淆(包含引用修改) | ✅ | ✅ | ✅ | 37 | | 包名混淆(包含引用修改) | ✅ | ✅ | ✅ | 38 | | 方法名混淆(包含引用修改) | ✅ | ✅ | ✅ | 39 | | 字段名混淆(包含引用修改) | ✅ | ✅ | ✅ | 40 | | 方法参数名混淆(包含引用修改) | ✅ | ✅ | ✅ | 41 | | 删除编译调试信息 | ✅ | ✅ | ✅ | 42 | | 字符串 AES 加密运行时解密 | ✅ | ✅ | ✅ | 43 | | 字符串修改为访问全局列表方式 | ✅ | ✅ | ✅ | 44 | | 整型常数多重异或混淆 | ✅ | ✅ | ✅ | 45 | | 添加垃圾代码(可指定多级别) | ✅ | ✅ | ✅ | 46 | | IDEA 反编译时隐藏方法 | ✅ | ✅ | ✅ | 47 | | IDEA 反编译时隐藏字段 | ✅ | ✅ | ✅ | 48 | | 初步支持 SpringBoot 混淆 | ✅ | ✅ | ✅ | 49 | | 初步支持 Web WAR 混淆 | ✅ | ✅ | ✅ | 50 | 51 | ## 配置 52 | 53 | ```yaml 54 | # jar obfuscator v2 配置文件 55 | # jar obfuscator v2 by jar-analyzer team (4ra1n) 56 | # https://github.com/jar-analyzer/jar-obfuscator 57 | 58 | # 日志级别 59 | # debug info warn error 60 | logLevel: info 61 | 62 | # 如果你是 springboot 请开启 63 | useSpringBoot: false 64 | # 如果你是 war web 项目请开启 65 | useWebWar: false 66 | 67 | # 混淆字符配置 68 | # 类名方法名等信息会根据字符进行随机排列组合 69 | obfuscateChars: 70 | - "i" 71 | - "l" 72 | - "L" 73 | - "1" 74 | - "I" 75 | # 不对某些类做混淆(不混淆其中的所有内容) 76 | # 通常情况必须加入 main 入口 77 | classBlackList: 78 | - "com.test.Main" 79 | # 不对指定正则的类进行混淆 80 | # 注意这里的类名匹配是 java/lang/String 而不是 java.lang.String 81 | # 该配置和 classBlackList 同时生效 82 | classBlackRegexList: 83 | - "java/.*" 84 | - "com/intellij/.*" 85 | # 不对某些 method 名做混淆 正则 86 | # visit.* 忽略 JAVA ASM 的 visitCode visitMethod 等方法 87 | # start.* 忽略 JAVAFX 因为启动基于 start 方法 88 | # 以此类推某些方法和类是不能混淆的(类继承和接口实现等) 89 | methodBlackList: 90 | - "visit.*" 91 | - "start.*" 92 | 93 | # 开启类名混淆 94 | enableClassName: true 95 | # 开启包名混淆 96 | enablePackageName: true 97 | # 开启方法名混淆 98 | enableMethodName: true 99 | # 开启字段混淆 100 | enableFieldName: true 101 | # 开启参数名混淆 102 | enableParamName: true 103 | # 开启数字异或混淆 104 | enableXOR: true 105 | 106 | # 开启加密字符串 107 | enableEncryptString: true 108 | # 加密使用 AES KEY 109 | # 注意长度必须是 16 且不包含中文 110 | stringAesKey: Y4SuperSecretKey 111 | # 开启进阶字符串混淆 112 | enableAdvanceString: true 113 | # 进阶字符串处理参数 114 | advanceStringName: GIiIiLA 115 | # 字符串解密类名 116 | decryptClassName: org.apache.commons.collections.list.AbstractHashMap 117 | # 字符串解密方法名 118 | decryptMethodName: newMap 119 | # 字符串 AES KEY 名字 120 | decryptKeyName: LiLiLLLiiiLLiiLLi 121 | 122 | # 是否隐藏方法 123 | enableHideMethod: true 124 | # 是否隐藏字段 125 | enableHideField: true 126 | 127 | # 开启删除编译信息选项 128 | enableDeleteCompileInfo: true 129 | 130 | # 开启花指令混淆 131 | enableJunk: true 132 | # 花指令级别 133 | # 最低1 最高5 134 | # 使用 3 以上会生成垃圾方法 135 | junkLevel: 5 136 | # 一个类中的花指令数量上限 137 | maxJunkOneClass: 2000 138 | 139 | # 是否打印所有主函数 140 | showAllMainMethods: true 141 | 142 | # 是否保留临时类文件 143 | keepTempFile: false 144 | ``` 145 | 146 | ## 配置指南 147 | 148 | 大概思路如下: 149 | 150 | - 通常情况必须把 `main` 入口加入 `classBlackList` 151 | - 通常需要配置 `classBlackRegexList` 拉黑所有的第三方库类(如 `org/apache/.*` 等) 152 | - 如果某些类集成或者实现某些库的接口,重写方法不允许重命名,注意配置 `methodBlackList` 153 | - 建议测试配置时不要开启 `enableJunk` 和 `enableHide*` 方法,一切没问题再开启这些 154 | 155 | ## 更新内容 156 | 157 | 2.0.0-RC2 版本已初步支持 `SpringBoot` 混淆 158 | 159 | ![](img/001.png) -------------------------------------------------------------------------------- /check-version.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | mvn versions:display-dependency-updates -------------------------------------------------------------------------------- /img/001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jar-analyzer/jar-obfuscator/9d12c8c12562cffbc73875f771397c143ac1d74e/img/001.png -------------------------------------------------------------------------------- /package.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | setlocal 3 | 4 | set "MAVEN_OPTS=-Dorg.slf4j.simpleLogger.defaultLogLevel=warn" 5 | mvn -B clean package -DskipTests --file pom.xml 6 | 7 | endlocal -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | me.n1ar4.jar.obfuscator 8 | jar-obfuscator 9 | 2.0.0-RC2 10 | 11 | jar 12 | Jar Obfuscator Project V2 13 | 14 | 15 | 16 | 9.7.1 17 | 1.82 18 | 2.4 19 | 3.5.1 20 | 7.0.3 21 | 22 | 23 | 3.3.1 24 | 25 | 3.7.1 26 | 27 | 3.13.0 28 | 29 | 2.17.1 30 | 31 | 8 32 | 8 33 | 34 | UTF-8 35 | 36 | me.n1ar4.jar.obfuscator.Main 37 | 38 | 39 | 40 | 41 | org.ow2.asm 42 | asm 43 | ${asm.version} 44 | 45 | 46 | org.ow2.asm 47 | asm-commons 48 | ${asm.version} 49 | 50 | 51 | com.beust 52 | jcommander 53 | ${jcommander.version} 54 | 55 | 56 | org.yaml 57 | snakeyaml 58 | ${snake.yaml.version} 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | org.apache.maven.plugins 67 | maven-resources-plugin 68 | ${maven.resources.version} 69 | 70 | 71 | org.apache.maven.plugins 72 | maven-assembly-plugin 73 | ${maven.assembly.version} 74 | 75 | 76 | org.apache.maven.plugins 77 | maven-compiler-plugin 78 | ${maven.compiler.version} 79 | 80 | 81 | org.codehaus.mojo 82 | versions-maven-plugin 83 | ${maven.versions.version} 84 | 85 | 86 | 87 | 88 | 89 | org.codehaus.mojo 90 | versions-maven-plugin 91 | 92 | 93 | 94 | 95 | regex 96 | (.+-SNAPSHOT|.+-M\d) 97 | 98 | 99 | regex 100 | .+-(alpha|beta).* 101 | 102 | 103 | regex 104 | .*\.android.* 105 | 106 | 107 | regex 108 | .*\-RC.* 109 | 110 | 111 | 112 | 113 | 114 | 115 | org.apache.maven.plugins 116 | maven-resources-plugin 117 | 118 | 119 | org.apache.maven.plugins 120 | maven-assembly-plugin 121 | 122 | 123 | build 124 | 125 | false 126 | true 127 | 128 | jar-with-dependencies 129 | 130 | 131 | 132 | ${main.name} 133 | 134 | 135 | true 136 | 137 | 138 | 139 | package 140 | 141 | single 142 | 143 | 144 | 145 | 146 | 147 | org.apache.maven.plugins 148 | maven-compiler-plugin 149 | 150 | ${maven.compiler.source} 151 | ${maven.compiler.target} 152 | 153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /release/README.md: -------------------------------------------------------------------------------- 1 | # RELEASE 2 | 3 | - run `check-version.bat` 4 | - change `pom.xml` 5 | - change `build.yml` 6 | - change `VERSION` in `Const.java` 7 | - build -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/Const.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator; 2 | 3 | import org.objectweb.asm.Opcodes; 4 | 5 | import java.nio.file.Path; 6 | import java.nio.file.Paths; 7 | 8 | public interface Const { 9 | String VERSION = "2.0.0-RC2"; 10 | String PROJECT_URL = "https://github.com/jar-analyzer/jar-obfuscator"; 11 | String TEMP_DIR = "jar-obfuscator-temp"; 12 | Path configPath = Paths.get("config.yaml"); 13 | int ASMVersion = Opcodes.ASM9; 14 | int AnalyzeASMOptions = 0; 15 | } -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/Logo.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator; 2 | 3 | import me.n1ar4.jar.obfuscator.utils.ColorUtil; 4 | import me.n1ar4.jar.obfuscator.utils.IOUtils; 5 | 6 | import java.io.InputStream; 7 | 8 | public class Logo { 9 | public static void printLogo() { 10 | System.out.println(ColorUtil.green( 11 | " ____.________ ___. _____ __ \n" + 12 | " | |\\_____ \\\\_ |___/ ____\\_ __ ______ ____ _____ _/ |_ ___________ \n" + 13 | " | | / | \\| __ \\ __\\ | \\/ ___// ___\\\\__ \\\\ __\\/ _ \\_ __ \\\n" + 14 | "/\\__| |/ | \\ \\_\\ \\ | | | /\\___ \\\\ \\___ / __ \\| | ( <_> ) | \\/\n" + 15 | "\\________|\\_______ /___ /__| |____//____ >\\___ >____ /__| \\____/|__| \n" + 16 | " \\/ \\/ \\/ \\/ \\/ ")); 17 | System.out.println(ColorUtil.blue("Jar Obfuscator V2 - An Open-Source Java Bytecode Obfuscation Tool")); 18 | System.out.println(ColorUtil.yellow("Jar Obfuscator V2 - 一个开源的配置简单容易上手的 JAVA 字节码混淆工具")); 19 | System.out.println("Version: " + ColorUtil.red(Const.VERSION) + 20 | " URL: " + ColorUtil.red(Const.PROJECT_URL) + "\n"); 21 | 22 | InputStream is = Logo.class.getClassLoader().getResourceAsStream("thanks.txt"); 23 | if (is != null) { 24 | try { 25 | byte[] data = IOUtils.readAllBytes(is); 26 | String a = new String(data); 27 | String[] splits = a.split("\n"); 28 | if (splits.length > 1) { 29 | System.out.println(ColorUtil.green("感谢以下用户对本项目的贡献:")); 30 | } 31 | for (String s : splits) { 32 | if (s.endsWith("\r")) { 33 | s = s.substring(0, s.length() - 1); 34 | } 35 | String[] temp = s.split(" "); 36 | System.out.println(" -> " + ColorUtil.blue(temp[0]) + 37 | " " + ColorUtil.yellow(temp[1])); 38 | } 39 | System.out.println(); 40 | } catch (Exception ignored) { 41 | } 42 | } 43 | 44 | System.out.println(ColorUtil.green("感谢使用!有问题和建议欢迎在 GITHUB ISSUE 反馈")); 45 | System.out.println(ColorUtil.yellow("LINK: https://github.com/jar-analyzer/jar-obfuscator/issues/new")); 46 | 47 | System.out.println(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/Main.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator; 2 | 3 | import com.beust.jcommander.JCommander; 4 | import me.n1ar4.jar.obfuscator.config.BaseCmd; 5 | import me.n1ar4.jar.obfuscator.config.BaseConfig; 6 | import me.n1ar4.jar.obfuscator.config.Manager; 7 | import me.n1ar4.jar.obfuscator.config.Parser; 8 | import me.n1ar4.jar.obfuscator.core.Runner; 9 | import me.n1ar4.log.LogManager; 10 | import me.n1ar4.log.Logger; 11 | 12 | import java.nio.file.Files; 13 | import java.nio.file.Path; 14 | import java.nio.file.Paths; 15 | 16 | public class Main { 17 | private static final BaseCmd baseCmd = new BaseCmd(); 18 | private static final Logger logger = LogManager.getLogger(); 19 | 20 | public static void main(String[] args) { 21 | Logo.printLogo(); 22 | Parser parser = new Parser(); 23 | JCommander commander = JCommander.newBuilder() 24 | .addObject(baseCmd) 25 | .build(); 26 | try { 27 | commander.parse(args); 28 | } catch (Exception ignored) { 29 | commander.usage(); 30 | return; 31 | } 32 | if (baseCmd.isGenerate()) { 33 | parser.generateConfig(); 34 | logger.info("generate config.yaml file"); 35 | return; 36 | } 37 | if (baseCmd.getConfig() == null || baseCmd.getConfig().isEmpty()) { 38 | baseCmd.setConfig("config.yaml"); 39 | } 40 | if (baseCmd.getPath() == null || baseCmd.getPath().isEmpty()) { 41 | logger.error("need --jar file"); 42 | commander.usage(); 43 | return; 44 | } 45 | BaseConfig config = parser.parse(Paths.get(baseCmd.getConfig())); 46 | if (config == null) { 47 | logger.warn("need config.yaml config"); 48 | logger.info("generate config.yaml file"); 49 | parser.generateConfig(); 50 | return; 51 | } 52 | String p = baseCmd.getPath(); 53 | Path path = Paths.get(p); 54 | if (!Files.exists(path)) { 55 | logger.error("jar file not exist"); 56 | commander.usage(); 57 | return; 58 | } 59 | 60 | boolean success = Manager.initConfig(config); 61 | if (!success) { 62 | return; 63 | } 64 | 65 | logger.info("start class obfuscate"); 66 | Runner.run(path, config); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/analyze/DiscoveryClassVisitor.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.analyze; 2 | 3 | import me.n1ar4.jar.obfuscator.Const; 4 | import me.n1ar4.jar.obfuscator.base.ClassReference; 5 | import me.n1ar4.jar.obfuscator.base.MethodReference; 6 | import org.objectweb.asm.*; 7 | 8 | import java.util.*; 9 | 10 | public class DiscoveryClassVisitor extends ClassVisitor { 11 | private String name; 12 | private String superName; 13 | private String[] interfaces; 14 | private boolean isInterface; 15 | private boolean isEnum; 16 | private List members; 17 | private ClassReference.Handle classHandle; 18 | private Set annotations; 19 | private final Set discoveredClasses; 20 | private final Set discoveredMethods; 21 | private final Map> fieldsInClassMap; 22 | private final List fieldsList = new ArrayList<>(); 23 | private final String jar; 24 | 25 | public DiscoveryClassVisitor(Set discoveredClasses, 26 | Set discoveredMethods, 27 | Map> fieldsInClassMap, 28 | String jarName) { 29 | super(Const.ASMVersion); 30 | this.fieldsInClassMap = fieldsInClassMap; 31 | this.discoveredClasses = discoveredClasses; 32 | this.discoveredMethods = discoveredMethods; 33 | this.jar = jarName; 34 | } 35 | 36 | @Override 37 | public void visit(int version, int access, String name, 38 | String signature, String superName, String[] interfaces) { 39 | this.name = name; 40 | this.superName = superName; 41 | this.interfaces = interfaces; 42 | this.isInterface = (access & Opcodes.ACC_INTERFACE) != 0; 43 | this.isEnum = (access & Opcodes.ACC_ENUM) != 0; 44 | this.members = new ArrayList<>(); 45 | this.classHandle = new ClassReference.Handle(name); 46 | annotations = new HashSet<>(); 47 | super.visit(version, access, name, signature, superName, interfaces); 48 | } 49 | 50 | @Override 51 | public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { 52 | annotations.add(descriptor); 53 | return super.visitAnnotation(descriptor, visible); 54 | } 55 | 56 | public FieldVisitor visitField(int access, String name, String desc, 57 | String signature, Object value) { 58 | if ((access & Opcodes.ACC_STATIC) == 0) { 59 | Type type = Type.getType(desc); 60 | String typeName; 61 | if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) { 62 | typeName = type.getInternalName(); 63 | } else { 64 | typeName = type.getDescriptor(); 65 | } 66 | members.add(new ClassReference.Member(name, access, new ClassReference.Handle(typeName))); 67 | } 68 | fieldsList.add(name); 69 | return super.visitField(access, name, desc, signature, value); 70 | } 71 | 72 | @Override 73 | public MethodVisitor visitMethod(int access, String name, String desc, 74 | String signature, String[] exceptions) { 75 | boolean isStatic = (access & Opcodes.ACC_STATIC) != 0; 76 | Set mAnno = new HashSet<>(); 77 | discoveredMethods.add(new MethodReference( 78 | classHandle, 79 | name, 80 | desc, 81 | isStatic, 82 | mAnno, access)); 83 | MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); 84 | return new DiscoveryMethodAdapter(Const.ASMVersion, mv, mAnno, this.name); 85 | } 86 | 87 | @Override 88 | public void visitEnd() { 89 | ClassReference classReference = new ClassReference( 90 | name, 91 | superName, 92 | Arrays.asList(interfaces), 93 | isInterface, 94 | isEnum, 95 | members, 96 | annotations, 97 | jar); 98 | discoveredClasses.add(classReference); 99 | fieldsInClassMap.put(name, fieldsList); 100 | super.visitEnd(); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/analyze/DiscoveryMethodAdapter.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.analyze; 2 | 3 | import me.n1ar4.jar.obfuscator.base.ClassReference; 4 | import me.n1ar4.jar.obfuscator.core.ObfEnv; 5 | import org.objectweb.asm.AnnotationVisitor; 6 | import org.objectweb.asm.MethodVisitor; 7 | 8 | import java.util.ArrayList; 9 | import java.util.Set; 10 | 11 | public class DiscoveryMethodAdapter extends MethodVisitor { 12 | private final Set anno; 13 | private final String className; 14 | private final ArrayList strings = new ArrayList<>(); 15 | 16 | protected DiscoveryMethodAdapter(int api, MethodVisitor methodVisitor, 17 | Set anno, String className) { 18 | super(api, methodVisitor); 19 | this.anno = anno; 20 | this.className = className; 21 | } 22 | 23 | @Override 24 | public void visitLdcInsn(Object value) { 25 | if (value instanceof String) { 26 | String valS = (String) value; 27 | strings.add(valS); 28 | } 29 | super.visitLdcInsn(value); 30 | } 31 | 32 | @Override 33 | public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { 34 | anno.add(descriptor); 35 | return super.visitAnnotation(descriptor, visible); 36 | } 37 | 38 | @Override 39 | public AnnotationVisitor visitParameterAnnotation(int parameter, String descriptor, boolean visible) { 40 | anno.add(descriptor); 41 | return super.visitParameterAnnotation(parameter, descriptor, visible); 42 | } 43 | 44 | @Override 45 | public void visitEnd() { 46 | ClassReference.Handle key = new ClassReference.Handle(this.className); 47 | ArrayList list = ObfEnv.stringInClass.get(key); 48 | if (list == null) { 49 | ObfEnv.stringInClass.put(new ClassReference.Handle(this.className), this.strings); 50 | } else { 51 | list.addAll(this.strings); 52 | ObfEnv.stringInClass.put(new ClassReference.Handle(this.className), list); 53 | } 54 | super.visitEnd(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/analyze/MethodCallClassVisitor.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.analyze; 2 | 3 | import me.n1ar4.jar.obfuscator.Const; 4 | import me.n1ar4.jar.obfuscator.base.MethodReference; 5 | import org.objectweb.asm.ClassVisitor; 6 | import org.objectweb.asm.MethodVisitor; 7 | 8 | import java.util.HashMap; 9 | import java.util.HashSet; 10 | 11 | public class MethodCallClassVisitor extends ClassVisitor { 12 | private String name; 13 | 14 | private final HashMap> methodCalls; 15 | 16 | public MethodCallClassVisitor(HashMap> methodCalls) { 18 | super(Const.ASMVersion); 19 | this.methodCalls = methodCalls; 20 | } 21 | 22 | @Override 23 | public void visit(int version, int access, String name, String signature, 24 | String superName, String[] interfaces) { 25 | super.visit(version, access, name, signature, superName, interfaces); 26 | this.name = name; 27 | } 28 | 29 | public MethodVisitor visitMethod(int access, String name, String desc, 30 | String signature, String[] exceptions) { 31 | MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); 32 | return new MethodCallMethodVisitor(api, mv, this.name, name, desc, methodCalls); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/analyze/MethodCallMethodVisitor.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.analyze; 2 | 3 | import me.n1ar4.jar.obfuscator.base.ClassReference; 4 | import me.n1ar4.jar.obfuscator.base.MethodReference; 5 | import org.objectweb.asm.Handle; 6 | import org.objectweb.asm.MethodVisitor; 7 | 8 | import java.util.HashMap; 9 | import java.util.HashSet; 10 | 11 | public class MethodCallMethodVisitor extends MethodVisitor { 12 | private final HashSet calledMethods; 13 | 14 | public MethodCallMethodVisitor(final int api, final MethodVisitor mv, 15 | final String owner, String name, String desc, 16 | HashMap> methodCalls) { 18 | super(api, mv); 19 | this.calledMethods = new HashSet<>(); 20 | methodCalls.put( 21 | new MethodReference.Handle( 22 | new ClassReference.Handle(owner), name, desc), calledMethods); 23 | } 24 | 25 | @Override 26 | public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { 27 | calledMethods.add( 28 | new MethodReference.Handle( 29 | new ClassReference.Handle(owner), name, desc)); 30 | super.visitMethodInsn(opcode, owner, name, desc, itf); 31 | } 32 | 33 | @Override 34 | public void visitInvokeDynamicInsn(String name, String descriptor, 35 | Handle bootstrapMethodHandle, 36 | Object... bootstrapMethodArguments) { 37 | for (Object bsmArg : bootstrapMethodArguments) { 38 | if (bsmArg instanceof Handle) { 39 | Handle handle = (Handle) bsmArg; 40 | calledMethods.add(new MethodReference.Handle( 41 | new ClassReference.Handle(handle.getOwner()), 42 | handle.getName(), handle.getDesc())); 43 | } 44 | } 45 | super.visitInvokeDynamicInsn(name, descriptor, 46 | bootstrapMethodHandle, bootstrapMethodArguments); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/asm/CompileInfoVisitor.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.asm; 2 | 3 | import me.n1ar4.jar.obfuscator.Const; 4 | import org.objectweb.asm.ClassVisitor; 5 | import org.objectweb.asm.Label; 6 | import org.objectweb.asm.MethodVisitor; 7 | 8 | 9 | public class CompileInfoVisitor extends ClassVisitor { 10 | public CompileInfoVisitor(ClassVisitor classVisitor) { 11 | super(Const.ASMVersion, classVisitor); 12 | } 13 | 14 | @Override 15 | public void visitSource(String source, String debug) { 16 | } 17 | 18 | @Override 19 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { 20 | MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); 21 | return new CompileInfoChangerMethodAdapter(mv); 22 | } 23 | 24 | static class CompileInfoChangerMethodAdapter extends MethodVisitor { 25 | CompileInfoChangerMethodAdapter(MethodVisitor mv) { 26 | super(Const.ASMVersion, mv); 27 | } 28 | 29 | @Override 30 | public void visitLineNumber(int line, Label start) { 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/asm/FieldNameVisitor.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.asm; 2 | 3 | import me.n1ar4.jar.obfuscator.Const; 4 | import me.n1ar4.jar.obfuscator.base.ClassField; 5 | import me.n1ar4.jar.obfuscator.core.ObfEnv; 6 | import org.objectweb.asm.*; 7 | 8 | public class FieldNameVisitor extends ClassVisitor { 9 | private String className; 10 | 11 | public FieldNameVisitor(ClassVisitor classVisitor) { 12 | super(Const.ASMVersion, classVisitor); 13 | } 14 | 15 | @Override 16 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { 17 | this.className = name; 18 | super.visit(version, access, name, signature, superName, interfaces); 19 | } 20 | 21 | @Override 22 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { 23 | MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); 24 | return new FieldNameChangerMethodAdapter(mv); 25 | } 26 | 27 | @Override 28 | public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { 29 | return super.visitAnnotation(descriptor, visible); 30 | } 31 | 32 | @Override 33 | public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) { 34 | return super.visitTypeAnnotation(typeRef, typePath, descriptor, visible); 35 | } 36 | 37 | @Override 38 | public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { 39 | ClassField cf = new ClassField(); 40 | cf.setClassName(this.className); 41 | cf.setFieldName(name); 42 | ClassField newCF = ObfEnv.fieldNameObfMapping.getOrDefault(cf, cf); 43 | if (ObfEnv.config.isEnableHideField()) { 44 | access = access | Opcodes.ACC_SYNTHETIC; 45 | } 46 | return super.visitField(access, newCF.getFieldName(), descriptor, signature, value); 47 | } 48 | 49 | @Override 50 | public ModuleVisitor visitModule(String name, int access, String version) { 51 | return super.visitModule(name, access, version); 52 | } 53 | 54 | @Override 55 | public RecordComponentVisitor visitRecordComponent(String name, String descriptor, String signature) { 56 | return super.visitRecordComponent(name, descriptor, signature); 57 | } 58 | 59 | @Override 60 | public void visitAttribute(Attribute attribute) { 61 | super.visitAttribute(attribute); 62 | } 63 | 64 | @Override 65 | public void visitEnd() { 66 | super.visitEnd(); 67 | } 68 | 69 | @Override 70 | public void visitInnerClass(String name, String outerName, String innerName, int access) { 71 | super.visitInnerClass(name, outerName, innerName, access); 72 | } 73 | 74 | @Override 75 | public void visitNestHost(String nestHost) { 76 | super.visitNestHost(nestHost); 77 | } 78 | 79 | @Override 80 | public void visitNestMember(String nestMember) { 81 | super.visitNestMember(nestMember); 82 | } 83 | 84 | @Override 85 | public void visitOuterClass(String owner, String name, String descriptor) { 86 | super.visitOuterClass(owner, name, descriptor); 87 | } 88 | 89 | @Override 90 | public void visitPermittedSubclass(String permittedSubclass) { 91 | super.visitPermittedSubclass(permittedSubclass); 92 | } 93 | 94 | @Override 95 | public void visitSource(String source, String debug) { 96 | super.visitSource(source, debug); 97 | } 98 | 99 | @Override 100 | public ClassVisitor getDelegate() { 101 | return super.getDelegate(); 102 | } 103 | 104 | static class FieldNameChangerMethodAdapter extends MethodVisitor { 105 | FieldNameChangerMethodAdapter(MethodVisitor mv) { 106 | super(Const.ASMVersion, mv); 107 | } 108 | 109 | @Override 110 | public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) { 111 | super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); 112 | } 113 | 114 | @Override 115 | public void visitFieldInsn(int opcode, String owner, String name, String descriptor) { 116 | ClassField cf = new ClassField(); 117 | cf.setClassName(owner); 118 | cf.setFieldName(name); 119 | ClassField newCF = ObfEnv.fieldNameObfMapping.getOrDefault(cf, cf); 120 | super.visitFieldInsn(opcode, owner, newCF.getFieldName(), descriptor); 121 | } 122 | 123 | @Override 124 | public void visitTypeInsn(int opcode, String type) { 125 | super.visitTypeInsn(opcode, type); 126 | } 127 | 128 | @Override 129 | public void visitAttribute(Attribute attribute) { 130 | super.visitAttribute(attribute); 131 | } 132 | 133 | @Override 134 | public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) { 135 | return super.visitTypeAnnotation(typeRef, typePath, descriptor, visible); 136 | } 137 | 138 | @Override 139 | public MethodVisitor getDelegate() { 140 | return super.getDelegate(); 141 | } 142 | 143 | @Override 144 | public void visitEnd() { 145 | super.visitEnd(); 146 | } 147 | 148 | @Override 149 | public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { 150 | return super.visitAnnotation(descriptor, visible); 151 | } 152 | 153 | @Override 154 | public AnnotationVisitor visitAnnotationDefault() { 155 | return super.visitAnnotationDefault(); 156 | } 157 | 158 | @Override 159 | public AnnotationVisitor visitInsnAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) { 160 | return super.visitInsnAnnotation(typeRef, typePath, descriptor, visible); 161 | } 162 | 163 | @Override 164 | public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, TypePath typePath, Label[] start, Label[] end, int[] index, String descriptor, boolean visible) { 165 | return super.visitLocalVariableAnnotation(typeRef, typePath, start, end, index, descriptor, visible); 166 | } 167 | 168 | @Override 169 | public AnnotationVisitor visitParameterAnnotation(int parameter, String descriptor, boolean visible) { 170 | return super.visitParameterAnnotation(parameter, descriptor, visible); 171 | } 172 | 173 | @Override 174 | public AnnotationVisitor visitTryCatchAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) { 175 | return super.visitTryCatchAnnotation(typeRef, typePath, descriptor, visible); 176 | } 177 | 178 | @Override 179 | public void visitAnnotableParameterCount(int parameterCount, boolean visible) { 180 | super.visitAnnotableParameterCount(parameterCount, visible); 181 | } 182 | 183 | @Override 184 | public void visitCode() { 185 | super.visitCode(); 186 | } 187 | 188 | @Override 189 | public void visitFrame(int type, int numLocal, Object[] local, int numStack, Object[] stack) { 190 | super.visitFrame(type, numLocal, local, numStack, stack); 191 | } 192 | 193 | @Override 194 | public void visitIincInsn(int varIndex, int increment) { 195 | super.visitIincInsn(varIndex, increment); 196 | } 197 | 198 | @Override 199 | public void visitInsn(int opcode) { 200 | super.visitInsn(opcode); 201 | } 202 | 203 | @Override 204 | public void visitIntInsn(int opcode, int operand) { 205 | super.visitIntInsn(opcode, operand); 206 | } 207 | 208 | @Override 209 | public void visitInvokeDynamicInsn(String name, String descriptor, Handle bootstrapMethodHandle, Object... bootstrapMethodArguments) { 210 | super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments); 211 | } 212 | 213 | @Override 214 | public void visitJumpInsn(int opcode, Label label) { 215 | super.visitJumpInsn(opcode, label); 216 | } 217 | 218 | @Override 219 | public void visitLabel(Label label) { 220 | super.visitLabel(label); 221 | } 222 | 223 | @Override 224 | public void visitLdcInsn(Object value) { 225 | super.visitLdcInsn(value); 226 | } 227 | 228 | @Override 229 | public void visitLineNumber(int line, Label start) { 230 | super.visitLineNumber(line, start); 231 | } 232 | 233 | @Override 234 | public void visitLocalVariable(String name, String descriptor, String signature, Label start, Label end, int index) { 235 | super.visitLocalVariable(name, descriptor, signature, start, end, index); 236 | } 237 | 238 | @Override 239 | public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { 240 | super.visitLookupSwitchInsn(dflt, keys, labels); 241 | } 242 | 243 | @Override 244 | public void visitMaxs(int maxStack, int maxLocals) { 245 | super.visitMaxs(maxStack, maxLocals); 246 | } 247 | 248 | @Override 249 | public void visitMultiANewArrayInsn(String descriptor, int numDimensions) { 250 | super.visitMultiANewArrayInsn(descriptor, numDimensions); 251 | } 252 | 253 | @Override 254 | public void visitParameter(String name, int access) { 255 | super.visitParameter(name, access); 256 | } 257 | 258 | @Override 259 | public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) { 260 | super.visitTableSwitchInsn(min, max, dflt, labels); 261 | } 262 | 263 | @Override 264 | public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { 265 | super.visitTryCatchBlock(start, end, handler, type); 266 | } 267 | 268 | @Override 269 | public void visitVarInsn(int opcode, int varIndex) { 270 | super.visitVarInsn(opcode, varIndex); 271 | } 272 | } 273 | } -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/asm/IntToXorVisitor.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.asm; 2 | 3 | import me.n1ar4.jar.obfuscator.Const; 4 | import me.n1ar4.jrandom.core.JRandom; 5 | import org.objectweb.asm.ClassVisitor; 6 | import org.objectweb.asm.MethodVisitor; 7 | import org.objectweb.asm.Opcodes; 8 | 9 | public class IntToXorVisitor extends ClassVisitor { 10 | public IntToXorVisitor(ClassVisitor cv) { 11 | super(Opcodes.ASM9, cv); 12 | } 13 | 14 | @Override 15 | public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { 16 | MethodVisitor mv = cv.visitMethod(access, name, descriptor, signature, exceptions); 17 | return new XORMethodAdapter(mv); 18 | } 19 | 20 | private static class XORMethodAdapter extends MethodVisitor { 21 | XORMethodAdapter(MethodVisitor mv) { 22 | super(Const.ASMVersion, mv); 23 | } 24 | 25 | @Override 26 | public void visitInsn(int opcode) { 27 | if (opcode >= Opcodes.ICONST_M1 && opcode <= Opcodes.ICONST_5) { 28 | int value = opcode - Opcodes.ICONST_0; 29 | replaceIntWithXor(value); 30 | } else { 31 | super.visitInsn(opcode); 32 | } 33 | } 34 | 35 | @Override 36 | public void visitIntInsn(int opcode, int operand) { 37 | if (opcode == Opcodes.BIPUSH || opcode == Opcodes.SIPUSH) { 38 | replaceIntWithXor(operand); 39 | } else { 40 | super.visitIntInsn(opcode, operand); 41 | } 42 | } 43 | 44 | @Override 45 | public void visitLdcInsn(Object cst) { 46 | if (cst instanceof Integer) { 47 | replaceIntWithXor((Integer) cst); 48 | } else { 49 | super.visitLdcInsn(cst); 50 | } 51 | } 52 | 53 | private void replaceIntWithXor(int value) { 54 | JRandom rand = JRandom.getInstance(); 55 | int partA = 10000000 + rand.getInt(0, 90000000); 56 | int partB = partA ^ value; 57 | super.visitLdcInsn(partA); 58 | super.visitLdcInsn(partB); 59 | super.visitInsn(Opcodes.IXOR); 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/asm/MainMethodVisitor.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.asm; 2 | 3 | import me.n1ar4.jar.obfuscator.Const; 4 | import org.objectweb.asm.ClassVisitor; 5 | import org.objectweb.asm.MethodVisitor; 6 | 7 | public class MainMethodVisitor extends ClassVisitor { 8 | private boolean hasMainMethod = false; 9 | 10 | public MainMethodVisitor() { 11 | super(Const.ASMVersion); 12 | } 13 | 14 | @Override 15 | public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) { 16 | if ("main".equals(name) && descriptor.equals("([Ljava/lang/String;)V") && access == 9) { 17 | hasMainMethod = true; 18 | } 19 | return super.visitMethod(access, name, descriptor, signature, exceptions); 20 | } 21 | 22 | public boolean hasMainMethod() { 23 | return hasMainMethod; 24 | } 25 | } -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/asm/MethodNameVisitor.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.asm; 2 | 3 | import me.n1ar4.jar.obfuscator.Const; 4 | import me.n1ar4.jar.obfuscator.base.ClassReference; 5 | import me.n1ar4.jar.obfuscator.base.MethodReference; 6 | import me.n1ar4.jar.obfuscator.core.AnalyzeEnv; 7 | import me.n1ar4.jar.obfuscator.core.ObfEnv; 8 | import org.objectweb.asm.*; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | @SuppressWarnings("all") 15 | public class MethodNameVisitor extends ClassVisitor { 16 | private String owner; 17 | private final List ignoreMethods = new ArrayList<>(); 18 | private final List ignoreMethodString = new ArrayList<>(); 19 | 20 | public MethodNameVisitor(ClassVisitor classVisitor) { 21 | super(Const.ASMVersion, classVisitor); 22 | } 23 | 24 | @Override 25 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { 26 | this.owner = name; 27 | // 查接口 不改接口方法 28 | for (String in : interfaces) { 29 | List mList = AnalyzeEnv.methodsInClassMap.get(new ClassReference.Handle(in)); 30 | if (mList == null) { 31 | continue; 32 | } 33 | ignoreMethods.addAll(mList); 34 | } 35 | 36 | // 检查内置黑名单 37 | String key = null; 38 | for (Map.Entry entry : ObfEnv.classNameObfMapping.entrySet()) { 39 | if (entry.getValue().equals(name)) { 40 | key = entry.getKey(); 41 | break; 42 | } 43 | } 44 | if (key != null) { 45 | List methodNames = ObfEnv.ignoredClassMethodsMapping.get(key); 46 | if (methodNames != null) { 47 | ignoreMethodString.addAll(methodNames); 48 | } 49 | } 50 | super.visit(version, access, name, signature, superName, interfaces); 51 | } 52 | 53 | @Override 54 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { 55 | MethodVisitor mv; 56 | 57 | for (MethodReference mr : ignoreMethods) { 58 | if (mr.getName().equals(name) && mr.getDesc().equals(desc)) { 59 | return super.visitMethod(access, name, desc, signature, exceptions); 60 | } 61 | } 62 | 63 | for (String method : this.ignoreMethodString) { 64 | if (method.equals(name)) { 65 | return super.visitMethod(access, name, desc, signature, exceptions); 66 | } 67 | } 68 | 69 | if ("main".equals(name) && desc.equals("([Ljava/lang/String;)V") && access == 9) { 70 | mv = super.visitMethod(access, name, desc, signature, exceptions); 71 | } else if (name.equals("") || name.equals("")) { 72 | mv = super.visitMethod(access, name, desc, signature, exceptions); 73 | } else { 74 | MethodReference.Handle m = ObfEnv.methodNameObfMapping.get(new MethodReference.Handle( 75 | new ClassReference.Handle(owner), 76 | name, 77 | desc 78 | )); 79 | if (ObfEnv.config.isEnableHideMethod()) { 80 | access = access | Opcodes.ACC_SYNTHETIC; 81 | } 82 | if (m == null) { 83 | mv = super.visitMethod(access, name, desc, signature, exceptions); 84 | return new MethodNameChangerMethodAdapter(mv); 85 | } else { 86 | mv = super.visitMethod(access, m.getName(), m.getDesc(), signature, exceptions); 87 | return new MethodNameChangerMethodAdapter(mv); 88 | } 89 | } 90 | return new MethodNameChangerMethodAdapter(mv); 91 | } 92 | 93 | @Override 94 | public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { 95 | return super.visitAnnotation(descriptor, visible); 96 | } 97 | 98 | @Override 99 | public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) { 100 | return super.visitTypeAnnotation(typeRef, typePath, descriptor, visible); 101 | } 102 | 103 | @Override 104 | public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { 105 | return super.visitField(access, name, descriptor, signature, value); 106 | } 107 | 108 | @Override 109 | public ModuleVisitor visitModule(String name, int access, String version) { 110 | return super.visitModule(name, access, version); 111 | } 112 | 113 | @Override 114 | public RecordComponentVisitor visitRecordComponent(String name, String descriptor, String signature) { 115 | return super.visitRecordComponent(name, descriptor, signature); 116 | } 117 | 118 | @Override 119 | public void visitAttribute(Attribute attribute) { 120 | super.visitAttribute(attribute); 121 | } 122 | 123 | @Override 124 | public void visitEnd() { 125 | super.visitEnd(); 126 | } 127 | 128 | @Override 129 | public void visitInnerClass(String name, String outerName, String innerName, int access) { 130 | super.visitInnerClass(name, outerName, innerName, access); 131 | } 132 | 133 | @Override 134 | public void visitNestHost(String nestHost) { 135 | super.visitNestHost(nestHost); 136 | } 137 | 138 | @Override 139 | public void visitNestMember(String nestMember) { 140 | super.visitNestMember(nestMember); 141 | } 142 | 143 | @Override 144 | public void visitOuterClass(String owner, String name, String descriptor) { 145 | super.visitOuterClass(owner, name, descriptor); 146 | } 147 | 148 | @Override 149 | public void visitPermittedSubclass(String permittedSubclass) { 150 | super.visitPermittedSubclass(permittedSubclass); 151 | } 152 | 153 | @Override 154 | public void visitSource(String source, String debug) { 155 | super.visitSource(source, debug); 156 | } 157 | 158 | @Override 159 | public ClassVisitor getDelegate() { 160 | return super.getDelegate(); 161 | } 162 | 163 | static class MethodNameChangerMethodAdapter extends MethodVisitor { 164 | MethodNameChangerMethodAdapter(MethodVisitor mv) { 165 | super(Const.ASMVersion, mv); 166 | } 167 | 168 | @Override 169 | public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) { 170 | MethodReference.Handle m = ObfEnv.methodNameObfMapping.get(new MethodReference.Handle( 171 | new ClassReference.Handle(owner), 172 | name, 173 | descriptor 174 | )); 175 | if (m != null) { 176 | super.visitMethodInsn(opcode, m.getClassReference().getName(), m.getName(), m.getDesc(), isInterface); 177 | return; 178 | } 179 | super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); 180 | } 181 | 182 | @Override 183 | public void visitFieldInsn(int opcode, String owner, String name, String descriptor) { 184 | super.visitFieldInsn(opcode, owner, name, descriptor); 185 | } 186 | 187 | @Override 188 | public void visitTypeInsn(int opcode, String type) { 189 | super.visitTypeInsn(opcode, type); 190 | } 191 | 192 | @Override 193 | public void visitAttribute(Attribute attribute) { 194 | super.visitAttribute(attribute); 195 | } 196 | 197 | @Override 198 | public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) { 199 | return super.visitTypeAnnotation(typeRef, typePath, descriptor, visible); 200 | } 201 | 202 | @Override 203 | public MethodVisitor getDelegate() { 204 | return super.getDelegate(); 205 | } 206 | 207 | @Override 208 | public void visitEnd() { 209 | super.visitEnd(); 210 | } 211 | 212 | @Override 213 | public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { 214 | return super.visitAnnotation(descriptor, visible); 215 | } 216 | 217 | @Override 218 | public AnnotationVisitor visitAnnotationDefault() { 219 | return super.visitAnnotationDefault(); 220 | } 221 | 222 | @Override 223 | public AnnotationVisitor visitInsnAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) { 224 | return super.visitInsnAnnotation(typeRef, typePath, descriptor, visible); 225 | } 226 | 227 | @Override 228 | public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, TypePath typePath, Label[] start, Label[] end, int[] index, String descriptor, boolean visible) { 229 | return super.visitLocalVariableAnnotation(typeRef, typePath, start, end, index, descriptor, visible); 230 | } 231 | 232 | @Override 233 | public AnnotationVisitor visitParameterAnnotation(int parameter, String descriptor, boolean visible) { 234 | return super.visitParameterAnnotation(parameter, descriptor, visible); 235 | } 236 | 237 | @Override 238 | public AnnotationVisitor visitTryCatchAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) { 239 | return super.visitTryCatchAnnotation(typeRef, typePath, descriptor, visible); 240 | } 241 | 242 | @Override 243 | public void visitAnnotableParameterCount(int parameterCount, boolean visible) { 244 | super.visitAnnotableParameterCount(parameterCount, visible); 245 | } 246 | 247 | @Override 248 | public void visitCode() { 249 | super.visitCode(); 250 | } 251 | 252 | @Override 253 | public void visitFrame(int type, int numLocal, Object[] local, int numStack, Object[] stack) { 254 | super.visitFrame(type, numLocal, local, numStack, stack); 255 | } 256 | 257 | @Override 258 | public void visitIincInsn(int varIndex, int increment) { 259 | super.visitIincInsn(varIndex, increment); 260 | } 261 | 262 | @Override 263 | public void visitInsn(int opcode) { 264 | super.visitInsn(opcode); 265 | } 266 | 267 | @Override 268 | public void visitIntInsn(int opcode, int operand) { 269 | super.visitIntInsn(opcode, operand); 270 | } 271 | 272 | @Override 273 | public void visitInvokeDynamicInsn(String name, String descriptor, Handle bootstrapMethodHandle, Object... bootstrapMethodArguments) { 274 | MethodReference.Handle m = ObfEnv.methodNameObfMapping.get(new MethodReference.Handle( 275 | new ClassReference.Handle(bootstrapMethodHandle.getOwner()), 276 | bootstrapMethodHandle.getName(), 277 | bootstrapMethodHandle.getDesc() 278 | )); 279 | Handle handle; 280 | if (m != null) { 281 | handle = new Handle( 282 | bootstrapMethodHandle.getTag(), 283 | m.getClassReference().getName(), 284 | m.getName(), 285 | m.getDesc(), 286 | bootstrapMethodHandle.isInterface()); 287 | } else { 288 | handle = bootstrapMethodHandle; 289 | } 290 | 291 | for (int i = 0; i < bootstrapMethodArguments.length; i++) { 292 | Object obj = bootstrapMethodArguments[i]; 293 | if (obj instanceof Handle) { 294 | Handle ho = (Handle) obj; 295 | MethodReference.Handle mo = ObfEnv.methodNameObfMapping.get(new MethodReference.Handle( 296 | new ClassReference.Handle(ho.getOwner()), 297 | ho.getName(), 298 | ho.getDesc() 299 | )); 300 | Handle tempHandle; 301 | if (mo != null) { 302 | tempHandle = new Handle( 303 | ho.getTag(), 304 | mo.getClassReference().getName(), 305 | mo.getName(), 306 | mo.getDesc(), 307 | ho.isInterface()); 308 | } else { 309 | tempHandle = ho; 310 | } 311 | bootstrapMethodArguments[i] = tempHandle; 312 | } 313 | } 314 | super.visitInvokeDynamicInsn(name, descriptor, handle, bootstrapMethodArguments); 315 | } 316 | 317 | @Override 318 | public void visitJumpInsn(int opcode, Label label) { 319 | super.visitJumpInsn(opcode, label); 320 | } 321 | 322 | @Override 323 | public void visitLabel(Label label) { 324 | super.visitLabel(label); 325 | } 326 | 327 | @Override 328 | public void visitLdcInsn(Object value) { 329 | super.visitLdcInsn(value); 330 | } 331 | 332 | @Override 333 | public void visitLineNumber(int line, Label start) { 334 | super.visitLineNumber(line, start); 335 | } 336 | 337 | @Override 338 | public void visitLocalVariable(String name, String descriptor, String signature, Label start, Label end, int index) { 339 | super.visitLocalVariable(name, descriptor, signature, start, end, index); 340 | } 341 | 342 | @Override 343 | public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { 344 | super.visitLookupSwitchInsn(dflt, keys, labels); 345 | } 346 | 347 | @Override 348 | public void visitMaxs(int maxStack, int maxLocals) { 349 | super.visitMaxs(maxStack, maxLocals); 350 | } 351 | 352 | @Override 353 | public void visitMultiANewArrayInsn(String descriptor, int numDimensions) { 354 | super.visitMultiANewArrayInsn(descriptor, numDimensions); 355 | } 356 | 357 | @Override 358 | public void visitParameter(String name, int access) { 359 | super.visitParameter(name, access); 360 | } 361 | 362 | @Override 363 | public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) { 364 | super.visitTableSwitchInsn(min, max, dflt, labels); 365 | } 366 | 367 | @Override 368 | public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { 369 | super.visitTryCatchBlock(start, end, handler, type); 370 | } 371 | 372 | @Override 373 | public void visitVarInsn(int opcode, int varIndex) { 374 | super.visitVarInsn(opcode, varIndex); 375 | } 376 | } 377 | } -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/asm/ParameterVisitor.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.asm; 2 | 3 | import me.n1ar4.jar.obfuscator.Const; 4 | import me.n1ar4.jar.obfuscator.utils.NameUtil; 5 | import org.objectweb.asm.*; 6 | 7 | import java.util.HashSet; 8 | 9 | public class ParameterVisitor extends ClassVisitor { 10 | public ParameterVisitor(ClassVisitor classVisitor) { 11 | super(Const.ASMVersion, classVisitor); 12 | } 13 | 14 | @Override 15 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { 16 | super.visit(version, access, name, signature, superName, interfaces); 17 | } 18 | 19 | @Override 20 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { 21 | MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); 22 | return new ParameterChangerMethodAdapter(mv); 23 | } 24 | 25 | @Override 26 | public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { 27 | return super.visitAnnotation(descriptor, visible); 28 | } 29 | 30 | @Override 31 | public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) { 32 | return super.visitTypeAnnotation(typeRef, typePath, descriptor, visible); 33 | } 34 | 35 | @Override 36 | public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { 37 | return super.visitField(access, name, descriptor, signature, value); 38 | } 39 | 40 | @Override 41 | public ModuleVisitor visitModule(String name, int access, String version) { 42 | return super.visitModule(name, access, version); 43 | } 44 | 45 | @Override 46 | public RecordComponentVisitor visitRecordComponent(String name, String descriptor, String signature) { 47 | return super.visitRecordComponent(name, descriptor, signature); 48 | } 49 | 50 | @Override 51 | public void visitAttribute(Attribute attribute) { 52 | super.visitAttribute(attribute); 53 | } 54 | 55 | @Override 56 | public void visitEnd() { 57 | super.visitEnd(); 58 | } 59 | 60 | @Override 61 | public void visitInnerClass(String name, String outerName, String innerName, int access) { 62 | super.visitInnerClass(name, outerName, innerName, access); 63 | } 64 | 65 | @Override 66 | public void visitNestHost(String nestHost) { 67 | super.visitNestHost(nestHost); 68 | } 69 | 70 | @Override 71 | public void visitNestMember(String nestMember) { 72 | super.visitNestMember(nestMember); 73 | } 74 | 75 | @Override 76 | public void visitOuterClass(String owner, String name, String descriptor) { 77 | super.visitOuterClass(owner, name, descriptor); 78 | } 79 | 80 | @Override 81 | public void visitPermittedSubclass(String permittedSubclass) { 82 | super.visitPermittedSubclass(permittedSubclass); 83 | } 84 | 85 | @Override 86 | public void visitSource(String source, String debug) { 87 | super.visitSource(source, debug); 88 | } 89 | 90 | @Override 91 | public ClassVisitor getDelegate() { 92 | return super.getDelegate(); 93 | } 94 | 95 | static class ParameterChangerMethodAdapter extends MethodVisitor { 96 | private final HashSet obfNames = new HashSet<>(); 97 | 98 | ParameterChangerMethodAdapter(MethodVisitor mv) { 99 | super(Const.ASMVersion, mv); 100 | } 101 | 102 | @Override 103 | public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) { 104 | super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); 105 | } 106 | 107 | @Override 108 | public void visitFieldInsn(int opcode, String owner, String name, String descriptor) { 109 | super.visitFieldInsn(opcode, owner, name, descriptor); 110 | } 111 | 112 | @Override 113 | public void visitTypeInsn(int opcode, String type) { 114 | super.visitTypeInsn(opcode, type); 115 | } 116 | 117 | @Override 118 | public void visitAttribute(Attribute attribute) { 119 | super.visitAttribute(attribute); 120 | } 121 | 122 | @Override 123 | public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) { 124 | return super.visitTypeAnnotation(typeRef, typePath, descriptor, visible); 125 | } 126 | 127 | @Override 128 | public MethodVisitor getDelegate() { 129 | return super.getDelegate(); 130 | } 131 | 132 | @Override 133 | public void visitEnd() { 134 | super.visitEnd(); 135 | } 136 | 137 | @Override 138 | public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { 139 | return super.visitAnnotation(descriptor, visible); 140 | } 141 | 142 | @Override 143 | public AnnotationVisitor visitAnnotationDefault() { 144 | return super.visitAnnotationDefault(); 145 | } 146 | 147 | @Override 148 | public AnnotationVisitor visitInsnAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) { 149 | return super.visitInsnAnnotation(typeRef, typePath, descriptor, visible); 150 | } 151 | 152 | @Override 153 | public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, TypePath typePath, Label[] start, Label[] end, int[] index, String descriptor, boolean visible) { 154 | return super.visitLocalVariableAnnotation(typeRef, typePath, start, end, index, descriptor, visible); 155 | } 156 | 157 | @Override 158 | public AnnotationVisitor visitParameterAnnotation(int parameter, String descriptor, boolean visible) { 159 | return super.visitParameterAnnotation(parameter, descriptor, visible); 160 | } 161 | 162 | @Override 163 | public AnnotationVisitor visitTryCatchAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) { 164 | return super.visitTryCatchAnnotation(typeRef, typePath, descriptor, visible); 165 | } 166 | 167 | @Override 168 | public void visitAnnotableParameterCount(int parameterCount, boolean visible) { 169 | super.visitAnnotableParameterCount(parameterCount, visible); 170 | } 171 | 172 | @Override 173 | public void visitCode() { 174 | super.visitCode(); 175 | } 176 | 177 | @Override 178 | public void visitFrame(int type, int numLocal, Object[] local, int numStack, Object[] stack) { 179 | super.visitFrame(type, numLocal, local, numStack, stack); 180 | } 181 | 182 | @Override 183 | public void visitIincInsn(int varIndex, int increment) { 184 | super.visitIincInsn(varIndex, increment); 185 | } 186 | 187 | @Override 188 | public void visitInsn(int opcode) { 189 | super.visitInsn(opcode); 190 | } 191 | 192 | @Override 193 | public void visitIntInsn(int opcode, int operand) { 194 | super.visitIntInsn(opcode, operand); 195 | } 196 | 197 | @Override 198 | public void visitInvokeDynamicInsn(String name, String descriptor, Handle bootstrapMethodHandle, Object... bootstrapMethodArguments) { 199 | super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments); 200 | } 201 | 202 | @Override 203 | public void visitJumpInsn(int opcode, Label label) { 204 | super.visitJumpInsn(opcode, label); 205 | } 206 | 207 | @Override 208 | public void visitLabel(Label label) { 209 | super.visitLabel(label); 210 | } 211 | 212 | @Override 213 | public void visitLdcInsn(Object value) { 214 | super.visitLdcInsn(value); 215 | } 216 | 217 | @Override 218 | public void visitLineNumber(int line, Label start) { 219 | super.visitLineNumber(line, start); 220 | } 221 | 222 | @Override 223 | public void visitLocalVariable(String name, String descriptor, String signature, Label start, Label end, int index) { 224 | if (name.equals("this") || name.equals("super")) { 225 | super.visitLocalVariable(name, descriptor, signature, start, end, index); 226 | return; 227 | } 228 | name = NameUtil.genWithSet(obfNames); 229 | super.visitLocalVariable(name, descriptor, signature, start, end, index); 230 | } 231 | 232 | @Override 233 | public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { 234 | super.visitLookupSwitchInsn(dflt, keys, labels); 235 | } 236 | 237 | @Override 238 | public void visitMaxs(int maxStack, int maxLocals) { 239 | super.visitMaxs(maxStack, maxLocals); 240 | } 241 | 242 | @Override 243 | public void visitMultiANewArrayInsn(String descriptor, int numDimensions) { 244 | super.visitMultiANewArrayInsn(descriptor, numDimensions); 245 | } 246 | 247 | @Override 248 | public void visitParameter(String name, int access) { 249 | super.visitParameter(name, access); 250 | } 251 | 252 | @Override 253 | public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) { 254 | super.visitTableSwitchInsn(min, max, dflt, labels); 255 | } 256 | 257 | @Override 258 | public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { 259 | super.visitTryCatchBlock(start, end, handler, type); 260 | } 261 | 262 | @Override 263 | public void visitVarInsn(int opcode, int varIndex) { 264 | super.visitVarInsn(opcode, varIndex); 265 | } 266 | } 267 | } -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/asm/StringArrayVisitor.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.asm; 2 | 3 | import me.n1ar4.jar.obfuscator.Const; 4 | import me.n1ar4.jar.obfuscator.core.ObfEnv; 5 | import me.n1ar4.jar.obfuscator.transform.StringArrayTransformer; 6 | import org.objectweb.asm.*; 7 | import org.objectweb.asm.commons.GeneratorAdapter; 8 | import org.objectweb.asm.commons.Method; 9 | 10 | import java.util.ArrayList; 11 | 12 | @SuppressWarnings("all") 13 | public class StringArrayVisitor extends ClassVisitor { 14 | private String className; 15 | private boolean isClinitPresent = false; 16 | private boolean isInterface; 17 | 18 | public StringArrayVisitor(ClassVisitor classVisitor) { 19 | super(Const.ASMVersion, classVisitor); 20 | } 21 | 22 | @Override 23 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { 24 | super.visit(version, access, name, signature, superName, interfaces); 25 | this.className = name; 26 | isInterface = (access & Opcodes.ACC_INTERFACE) != 0; 27 | super.visit(version, access, name, signature, superName, interfaces); 28 | } 29 | 30 | @Override 31 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { 32 | MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); 33 | if (isInterface) { 34 | return mv; 35 | } 36 | if ("".equals(name)) { 37 | isClinitPresent = true; 38 | String fieldDesc = "Ljava/util/ArrayList;"; 39 | GeneratorAdapter ga = new GeneratorAdapter(mv, access, name, desc); 40 | ga.visitCode(); 41 | ga.newInstance(Type.getType("Ljava/util/ArrayList;")); 42 | ga.dup(); 43 | ga.invokeConstructor(Type.getType("Ljava/util/ArrayList;"), new Method("", "()V")); 44 | ga.putStatic(Type.getObjectType(className), ObfEnv.ADVANCE_STRING_NAME, Type.getType("Ljava/util/ArrayList;")); 45 | ArrayList list = ObfEnv.newStringInClass.get(className); 46 | if (list != null && !list.isEmpty()) { 47 | for (String s : list) { 48 | ga.getStatic(Type.getObjectType(className), ObfEnv.ADVANCE_STRING_NAME, Type.getType(fieldDesc)); 49 | ga.push(s); 50 | ga.invokeVirtual(Type.getType("Ljava/util/ArrayList;"), 51 | new Method("add", "(Ljava/lang/Object;)Z")); 52 | ga.pop(); 53 | } 54 | } 55 | // DO NOT RETURN 56 | ga.endMethod(); 57 | return new StringArrayMethodAdapter(ga, className); 58 | } 59 | return new StringArrayMethodAdapter(mv, className); 60 | } 61 | 62 | @Override 63 | public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { 64 | return super.visitAnnotation(descriptor, visible); 65 | } 66 | 67 | @Override 68 | public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) { 69 | return super.visitTypeAnnotation(typeRef, typePath, descriptor, visible); 70 | } 71 | 72 | @Override 73 | public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { 74 | return super.visitField(access, name, descriptor, signature, value); 75 | } 76 | 77 | @Override 78 | public ModuleVisitor visitModule(String name, int access, String version) { 79 | return super.visitModule(name, access, version); 80 | } 81 | 82 | @Override 83 | public RecordComponentVisitor visitRecordComponent(String name, String descriptor, String signature) { 84 | return super.visitRecordComponent(name, descriptor, signature); 85 | } 86 | 87 | @Override 88 | public void visitAttribute(Attribute attribute) { 89 | super.visitAttribute(attribute); 90 | } 91 | 92 | @Override 93 | public void visitEnd() { 94 | if (isInterface) { 95 | super.visitEnd(); 96 | return; 97 | } 98 | String fieldDesc = "Ljava/util/ArrayList;"; 99 | FieldVisitor fv = super.visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC, 100 | ObfEnv.ADVANCE_STRING_NAME, fieldDesc, "Ljava/util/ArrayList;", null); 101 | if (fv != null) { 102 | fv.visitEnd(); 103 | } 104 | if (!isClinitPresent) { 105 | MethodVisitor mv = super.visitMethod(Opcodes.ACC_STATIC, "", "()V", null, null); 106 | GeneratorAdapter ga = new GeneratorAdapter(mv, Opcodes.ACC_STATIC, "", "()V"); 107 | ga.visitCode(); 108 | 109 | ga.newInstance(Type.getType("Ljava/util/ArrayList;")); 110 | ga.dup(); 111 | ga.invokeConstructor(Type.getType("Ljava/util/ArrayList;"), new Method("", "()V")); 112 | ga.putStatic(Type.getObjectType(className), ObfEnv.ADVANCE_STRING_NAME, Type.getType("Ljava/util/ArrayList;")); 113 | 114 | ArrayList list = ObfEnv.newStringInClass.get(className); 115 | if (list != null && !list.isEmpty()) { 116 | for (String s : list) { 117 | ga.getStatic(Type.getObjectType(className), ObfEnv.ADVANCE_STRING_NAME, Type.getType(fieldDesc)); 118 | ga.push(s); 119 | ga.invokeVirtual(Type.getType("Ljava/util/ArrayList;"), 120 | new Method("add", "(Ljava/lang/Object;)Z")); 121 | ga.pop(); 122 | } 123 | } 124 | ga.returnValue(); 125 | ga.endMethod(); 126 | } 127 | super.visitEnd(); 128 | } 129 | 130 | @Override 131 | public void visitInnerClass(String name, String outerName, String innerName, int access) { 132 | super.visitInnerClass(name, outerName, innerName, access); 133 | } 134 | 135 | @Override 136 | public void visitNestHost(String nestHost) { 137 | super.visitNestHost(nestHost); 138 | } 139 | 140 | @Override 141 | public void visitNestMember(String nestMember) { 142 | super.visitNestMember(nestMember); 143 | } 144 | 145 | @Override 146 | public void visitOuterClass(String owner, String name, String descriptor) { 147 | super.visitOuterClass(owner, name, descriptor); 148 | } 149 | 150 | @Override 151 | public void visitPermittedSubclass(String permittedSubclass) { 152 | super.visitPermittedSubclass(permittedSubclass); 153 | } 154 | 155 | @Override 156 | public void visitSource(String source, String debug) { 157 | super.visitSource(source, debug); 158 | } 159 | 160 | @Override 161 | public ClassVisitor getDelegate() { 162 | return super.getDelegate(); 163 | } 164 | 165 | static class StringArrayMethodAdapter extends MethodVisitor { 166 | private final String className; 167 | 168 | StringArrayMethodAdapter(MethodVisitor mv, String name) { 169 | super(Const.ASMVersion, mv); 170 | this.className = name; 171 | } 172 | 173 | @Override 174 | public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) { 175 | super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); 176 | } 177 | 178 | @Override 179 | public void visitFieldInsn(int opcode, String owner, String name, String descriptor) { 180 | super.visitFieldInsn(opcode, owner, name, descriptor); 181 | } 182 | 183 | @Override 184 | public void visitTypeInsn(int opcode, String type) { 185 | super.visitTypeInsn(opcode, type); 186 | } 187 | 188 | @Override 189 | public void visitAttribute(Attribute attribute) { 190 | super.visitAttribute(attribute); 191 | } 192 | 193 | @Override 194 | public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) { 195 | return super.visitTypeAnnotation(typeRef, typePath, descriptor, visible); 196 | } 197 | 198 | @Override 199 | public MethodVisitor getDelegate() { 200 | return super.getDelegate(); 201 | } 202 | 203 | @Override 204 | public void visitEnd() { 205 | super.visitEnd(); 206 | } 207 | 208 | @Override 209 | public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { 210 | return super.visitAnnotation(descriptor, visible); 211 | } 212 | 213 | @Override 214 | public AnnotationVisitor visitAnnotationDefault() { 215 | return super.visitAnnotationDefault(); 216 | } 217 | 218 | @Override 219 | public AnnotationVisitor visitInsnAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) { 220 | return super.visitInsnAnnotation(typeRef, typePath, descriptor, visible); 221 | } 222 | 223 | @Override 224 | public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, TypePath typePath, Label[] start, Label[] end, int[] index, String descriptor, boolean visible) { 225 | return super.visitLocalVariableAnnotation(typeRef, typePath, start, end, index, descriptor, visible); 226 | } 227 | 228 | @Override 229 | public AnnotationVisitor visitParameterAnnotation(int parameter, String descriptor, boolean visible) { 230 | return super.visitParameterAnnotation(parameter, descriptor, visible); 231 | } 232 | 233 | @Override 234 | public AnnotationVisitor visitTryCatchAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) { 235 | return super.visitTryCatchAnnotation(typeRef, typePath, descriptor, visible); 236 | } 237 | 238 | @Override 239 | public void visitAnnotableParameterCount(int parameterCount, boolean visible) { 240 | super.visitAnnotableParameterCount(parameterCount, visible); 241 | } 242 | 243 | @Override 244 | public void visitCode() { 245 | super.visitCode(); 246 | } 247 | 248 | @Override 249 | public void visitFrame(int type, int numLocal, Object[] local, int numStack, Object[] stack) { 250 | super.visitFrame(type, numLocal, local, numStack, stack); 251 | } 252 | 253 | @Override 254 | public void visitIincInsn(int varIndex, int increment) { 255 | super.visitIincInsn(varIndex, increment); 256 | } 257 | 258 | @Override 259 | public void visitInsn(int opcode) { 260 | super.visitInsn(opcode); 261 | } 262 | 263 | @Override 264 | public void visitIntInsn(int opcode, int operand) { 265 | super.visitIntInsn(opcode, operand); 266 | } 267 | 268 | @Override 269 | public void visitInvokeDynamicInsn(String name, String descriptor, Handle bootstrapMethodHandle, Object... bootstrapMethodArguments) { 270 | super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments); 271 | } 272 | 273 | @Override 274 | public void visitJumpInsn(int opcode, Label label) { 275 | super.visitJumpInsn(opcode, label); 276 | } 277 | 278 | @Override 279 | public void visitLabel(Label label) { 280 | super.visitLabel(label); 281 | } 282 | 283 | @Override 284 | public void visitLdcInsn(Object value) { 285 | if (value instanceof String) { 286 | visitFieldInsn(Opcodes.GETSTATIC, className, ObfEnv.ADVANCE_STRING_NAME, 287 | "Ljava/util/ArrayList;"); 288 | visitIntInsn(Opcodes.SIPUSH, StringArrayTransformer.INDEX); 289 | StringArrayTransformer.INDEX++; 290 | visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/util/ArrayList", "get", 291 | "(I)Ljava/lang/Object;", false); 292 | visitTypeInsn(Opcodes.CHECKCAST, "java/lang/String"); 293 | } else { 294 | super.visitLdcInsn(value); 295 | } 296 | } 297 | 298 | @Override 299 | public void visitLineNumber(int line, Label start) { 300 | super.visitLineNumber(line, start); 301 | } 302 | 303 | @Override 304 | public void visitLocalVariable(String name, String descriptor, String signature, Label start, Label end, int index) { 305 | super.visitLocalVariable(name, descriptor, signature, start, end, index); 306 | } 307 | 308 | @Override 309 | public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { 310 | super.visitLookupSwitchInsn(dflt, keys, labels); 311 | } 312 | 313 | @Override 314 | public void visitMaxs(int maxStack, int maxLocals) { 315 | super.visitMaxs(maxStack, maxLocals); 316 | } 317 | 318 | @Override 319 | public void visitMultiANewArrayInsn(String descriptor, int numDimensions) { 320 | super.visitMultiANewArrayInsn(descriptor, numDimensions); 321 | } 322 | 323 | @Override 324 | public void visitParameter(String name, int access) { 325 | super.visitParameter(name, access); 326 | } 327 | 328 | @Override 329 | public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) { 330 | super.visitTableSwitchInsn(min, max, dflt, labels); 331 | } 332 | 333 | @Override 334 | public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { 335 | super.visitTryCatchBlock(start, end, handler, type); 336 | } 337 | 338 | @Override 339 | public void visitVarInsn(int opcode, int varIndex) { 340 | super.visitVarInsn(opcode, varIndex); 341 | } 342 | } 343 | } -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/asm/StringVisitor.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.asm; 2 | 3 | import me.n1ar4.jar.obfuscator.Const; 4 | import me.n1ar4.jar.obfuscator.templates.StringDecrypt; 5 | import me.n1ar4.jar.obfuscator.templates.StringDecryptDump; 6 | import org.objectweb.asm.*; 7 | import org.objectweb.asm.commons.GeneratorAdapter; 8 | import org.objectweb.asm.commons.Method; 9 | 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | 13 | 14 | public class StringVisitor extends ClassVisitor { 15 | private final Map fieldValues = new HashMap<>(); 16 | private String className; 17 | private boolean isInterface = false; 18 | private boolean noClinit = true; 19 | 20 | public StringVisitor(ClassVisitor classVisitor) { 21 | super(Const.ASMVersion, classVisitor); 22 | } 23 | 24 | @Override 25 | public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { 26 | super.visit(version, access, name, signature, superName, interfaces); 27 | this.className = name; 28 | isInterface = (access & Opcodes.ACC_INTERFACE) != 0; 29 | } 30 | 31 | @Override 32 | public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { 33 | if (name.equals("")) { 34 | noClinit = false; 35 | } 36 | MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); 37 | return new StringChangerMethodAdapter(mv); 38 | } 39 | 40 | @Override 41 | public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { 42 | return super.visitAnnotation(descriptor, visible); 43 | } 44 | 45 | @Override 46 | public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) { 47 | return super.visitTypeAnnotation(typeRef, typePath, descriptor, visible); 48 | } 49 | 50 | @Override 51 | public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) { 52 | if (value instanceof String) { 53 | fieldValues.put(name, (String) value); 54 | } 55 | return super.visitField(access, name, descriptor, signature, null); 56 | } 57 | 58 | @Override 59 | public ModuleVisitor visitModule(String name, int access, String version) { 60 | return super.visitModule(name, access, version); 61 | } 62 | 63 | @Override 64 | public RecordComponentVisitor visitRecordComponent(String name, String descriptor, String signature) { 65 | return super.visitRecordComponent(name, descriptor, signature); 66 | } 67 | 68 | @Override 69 | public void visitAttribute(Attribute attribute) { 70 | super.visitAttribute(attribute); 71 | } 72 | 73 | @Override 74 | public void visitEnd() { 75 | if (isInterface && noClinit) { 76 | generateClinit(); 77 | } 78 | super.visitEnd(); 79 | } 80 | 81 | private void generateClinit() { 82 | MethodVisitor mv = cv.visitMethod(Opcodes.ACC_STATIC, "", "()V", null, null); 83 | GeneratorAdapter ga = new GeneratorAdapter(mv, Opcodes.ACC_STATIC, "", "()V"); 84 | ga.visitCode(); 85 | 86 | fieldValues.forEach((fieldName, encryptedValue) -> { 87 | encryptedValue = StringDecrypt.encrypt(encryptedValue); 88 | ga.push(encryptedValue); 89 | ga.invokeStatic(Type.getObjectType(StringDecryptDump.className), 90 | new Method("I", "(Ljava/lang/String;)Ljava/lang/String;")); 91 | ga.putStatic(Type.getObjectType(className), fieldName, Type.getType("Ljava/lang/String;")); 92 | }); 93 | 94 | ga.returnValue(); 95 | ga.endMethod(); 96 | } 97 | 98 | @Override 99 | public void visitInnerClass(String name, String outerName, String innerName, int access) { 100 | super.visitInnerClass(name, outerName, innerName, access); 101 | } 102 | 103 | @Override 104 | public void visitNestHost(String nestHost) { 105 | super.visitNestHost(nestHost); 106 | } 107 | 108 | @Override 109 | public void visitNestMember(String nestMember) { 110 | super.visitNestMember(nestMember); 111 | } 112 | 113 | @Override 114 | public void visitOuterClass(String owner, String name, String descriptor) { 115 | super.visitOuterClass(owner, name, descriptor); 116 | } 117 | 118 | @Override 119 | public void visitPermittedSubclass(String permittedSubclass) { 120 | super.visitPermittedSubclass(permittedSubclass); 121 | } 122 | 123 | @Override 124 | public void visitSource(String source, String debug) { 125 | super.visitSource(source, debug); 126 | } 127 | 128 | @Override 129 | public ClassVisitor getDelegate() { 130 | return super.getDelegate(); 131 | } 132 | 133 | static class StringChangerMethodAdapter extends MethodVisitor { 134 | StringChangerMethodAdapter(MethodVisitor mv) { 135 | super(Const.ASMVersion, mv); 136 | } 137 | 138 | @Override 139 | public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) { 140 | super.visitMethodInsn(opcode, owner, name, descriptor, isInterface); 141 | } 142 | 143 | @Override 144 | public void visitFieldInsn(int opcode, String owner, String name, String descriptor) { 145 | super.visitFieldInsn(opcode, owner, name, descriptor); 146 | } 147 | 148 | @Override 149 | public void visitTypeInsn(int opcode, String type) { 150 | super.visitTypeInsn(opcode, type); 151 | } 152 | 153 | @Override 154 | public void visitAttribute(Attribute attribute) { 155 | super.visitAttribute(attribute); 156 | } 157 | 158 | @Override 159 | public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) { 160 | return super.visitTypeAnnotation(typeRef, typePath, descriptor, visible); 161 | } 162 | 163 | @Override 164 | public MethodVisitor getDelegate() { 165 | return super.getDelegate(); 166 | } 167 | 168 | @Override 169 | public void visitEnd() { 170 | super.visitEnd(); 171 | } 172 | 173 | @Override 174 | public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) { 175 | return super.visitAnnotation(descriptor, visible); 176 | } 177 | 178 | @Override 179 | public AnnotationVisitor visitAnnotationDefault() { 180 | return super.visitAnnotationDefault(); 181 | } 182 | 183 | @Override 184 | public AnnotationVisitor visitInsnAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) { 185 | return super.visitInsnAnnotation(typeRef, typePath, descriptor, visible); 186 | } 187 | 188 | @Override 189 | public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, TypePath typePath, Label[] start, Label[] end, int[] index, String descriptor, boolean visible) { 190 | return super.visitLocalVariableAnnotation(typeRef, typePath, start, end, index, descriptor, visible); 191 | } 192 | 193 | @Override 194 | public AnnotationVisitor visitParameterAnnotation(int parameter, String descriptor, boolean visible) { 195 | return super.visitParameterAnnotation(parameter, descriptor, visible); 196 | } 197 | 198 | @Override 199 | public AnnotationVisitor visitTryCatchAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) { 200 | return super.visitTryCatchAnnotation(typeRef, typePath, descriptor, visible); 201 | } 202 | 203 | @Override 204 | public void visitAnnotableParameterCount(int parameterCount, boolean visible) { 205 | super.visitAnnotableParameterCount(parameterCount, visible); 206 | } 207 | 208 | @Override 209 | public void visitCode() { 210 | super.visitCode(); 211 | } 212 | 213 | @Override 214 | public void visitFrame(int type, int numLocal, Object[] local, int numStack, Object[] stack) { 215 | super.visitFrame(type, numLocal, local, numStack, stack); 216 | } 217 | 218 | @Override 219 | public void visitIincInsn(int varIndex, int increment) { 220 | super.visitIincInsn(varIndex, increment); 221 | } 222 | 223 | @Override 224 | public void visitInsn(int opcode) { 225 | super.visitInsn(opcode); 226 | } 227 | 228 | @Override 229 | public void visitIntInsn(int opcode, int operand) { 230 | super.visitIntInsn(opcode, operand); 231 | } 232 | 233 | @Override 234 | public void visitInvokeDynamicInsn(String name, String descriptor, Handle bootstrapMethodHandle, Object... bootstrapMethodArguments) { 235 | super.visitInvokeDynamicInsn(name, descriptor, bootstrapMethodHandle, bootstrapMethodArguments); 236 | } 237 | 238 | @Override 239 | public void visitJumpInsn(int opcode, Label label) { 240 | super.visitJumpInsn(opcode, label); 241 | } 242 | 243 | @Override 244 | public void visitLabel(Label label) { 245 | super.visitLabel(label); 246 | } 247 | 248 | @Override 249 | public void visitLdcInsn(Object value) { 250 | if (value instanceof String) { 251 | String decryptedValue = StringDecrypt.encrypt((String) value); 252 | if (decryptedValue != null) { 253 | super.visitLdcInsn(decryptedValue); 254 | super.visitMethodInsn(Opcodes.INVOKESTATIC, 255 | StringDecryptDump.className, 256 | StringDecryptDump.methodName, 257 | "(Ljava/lang/String;)Ljava/lang/String;", 258 | false); 259 | return; 260 | } 261 | } 262 | super.visitLdcInsn(value); 263 | } 264 | 265 | @Override 266 | public void visitLineNumber(int line, Label start) { 267 | super.visitLineNumber(line, start); 268 | } 269 | 270 | @Override 271 | public void visitLocalVariable(String name, String descriptor, String signature, Label start, Label end, int index) { 272 | super.visitLocalVariable(name, descriptor, signature, start, end, index); 273 | } 274 | 275 | @Override 276 | public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { 277 | super.visitLookupSwitchInsn(dflt, keys, labels); 278 | } 279 | 280 | @Override 281 | public void visitMaxs(int maxStack, int maxLocals) { 282 | super.visitMaxs(maxStack, maxLocals); 283 | } 284 | 285 | @Override 286 | public void visitMultiANewArrayInsn(String descriptor, int numDimensions) { 287 | super.visitMultiANewArrayInsn(descriptor, numDimensions); 288 | } 289 | 290 | @Override 291 | public void visitParameter(String name, int access) { 292 | super.visitParameter(name, access); 293 | } 294 | 295 | @Override 296 | public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) { 297 | super.visitTableSwitchInsn(min, max, dflt, labels); 298 | } 299 | 300 | @Override 301 | public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { 302 | super.visitTryCatchBlock(start, end, handler, type); 303 | } 304 | 305 | @Override 306 | public void visitVarInsn(int opcode, int varIndex) { 307 | super.visitVarInsn(opcode, varIndex); 308 | } 309 | } 310 | } -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/base/ClassField.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.base; 2 | 3 | import java.util.Objects; 4 | 5 | public class ClassField { 6 | private String className; 7 | private String fieldName; 8 | 9 | public String getClassName() { 10 | return className; 11 | } 12 | 13 | public void setClassName(String className) { 14 | this.className = className; 15 | } 16 | 17 | public String getFieldName() { 18 | return fieldName; 19 | } 20 | 21 | public void setFieldName(String fieldName) { 22 | this.fieldName = fieldName; 23 | } 24 | 25 | @Override 26 | public boolean equals(Object o) { 27 | if (this == o) return true; 28 | if (o == null || getClass() != o.getClass()) return false; 29 | ClassField that = (ClassField) o; 30 | return Objects.equals(className, that.className) && Objects.equals(fieldName, that.fieldName); 31 | } 32 | 33 | @Override 34 | public int hashCode() { 35 | return Objects.hash(className, fieldName); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/base/ClassFileEntity.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.base; 2 | 3 | import me.n1ar4.log.LogManager; 4 | import me.n1ar4.log.Logger; 5 | 6 | import java.nio.file.Files; 7 | import java.nio.file.Path; 8 | 9 | public class ClassFileEntity { 10 | private static final Logger logger = LogManager.getLogger(); 11 | private Path path; 12 | private String jarName; 13 | 14 | public Path getPath() { 15 | return path; 16 | } 17 | 18 | public void setPath(Path path) { 19 | this.path = path; 20 | } 21 | 22 | public String getJarName() { 23 | return jarName; 24 | } 25 | 26 | public void setJarName(String jarName) { 27 | this.jarName = jarName; 28 | } 29 | 30 | public ClassFileEntity() { 31 | } 32 | 33 | public byte[] getFile() { 34 | try { 35 | return Files.readAllBytes(this.path); 36 | } catch (Exception e) { 37 | logger.error("get file error: {}", e.toString()); 38 | } 39 | return null; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/base/ClassReference.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.base; 2 | 3 | import java.util.List; 4 | import java.util.Objects; 5 | import java.util.Set; 6 | 7 | @SuppressWarnings("unused") 8 | public class ClassReference { 9 | private final String name; 10 | private final String superClass; 11 | private final List interfaces; 12 | private final boolean isInterface; 13 | private final boolean isEnum; 14 | private final List members; 15 | private final Set annotations; 16 | private final String jar; 17 | 18 | public static class Member { 19 | private final String name; 20 | private final int modifiers; 21 | private final Handle type; 22 | 23 | public Member(String name, int modifiers, Handle type) { 24 | this.name = name; 25 | this.modifiers = modifiers; 26 | this.type = type; 27 | } 28 | 29 | public String getName() { 30 | return name; 31 | } 32 | 33 | public int getModifiers() { 34 | return modifiers; 35 | } 36 | 37 | public Handle getType() { 38 | return type; 39 | } 40 | } 41 | 42 | public ClassReference(String name, String superClass, List interfaces, 43 | boolean isInterface, boolean isEnum, List members, Set annotations, String jar) { 44 | this.name = name; 45 | this.superClass = superClass; 46 | this.interfaces = interfaces; 47 | this.isInterface = isInterface; 48 | this.isEnum = isEnum; 49 | this.members = members; 50 | this.annotations = annotations; 51 | this.jar = jar; 52 | } 53 | 54 | public String getJar() { 55 | return jar; 56 | } 57 | 58 | public String getName() { 59 | return name; 60 | } 61 | 62 | public String getSuperClass() { 63 | return superClass; 64 | } 65 | 66 | public List getInterfaces() { 67 | return interfaces; 68 | } 69 | 70 | public boolean isInterface() { 71 | return isInterface; 72 | } 73 | 74 | public boolean isEnum() { 75 | return isEnum; 76 | } 77 | 78 | public List getMembers() { 79 | return members; 80 | } 81 | 82 | public Handle getHandle() { 83 | return new Handle(name); 84 | } 85 | 86 | public Set getAnnotations() { 87 | return annotations; 88 | } 89 | 90 | public static class Handle { 91 | private final String name; 92 | 93 | public Handle(String name) { 94 | this.name = name; 95 | } 96 | 97 | public String getName() { 98 | return name; 99 | } 100 | 101 | @Override 102 | public boolean equals(Object o) { 103 | if (this == o) { 104 | return true; 105 | } 106 | if (o == null || getClass() != o.getClass()) { 107 | return false; 108 | } 109 | Handle handle = (Handle) o; 110 | return Objects.equals(name, handle.name); 111 | } 112 | 113 | @Override 114 | public int hashCode() { 115 | return name != null ? name.hashCode() : 0; 116 | } 117 | } 118 | 119 | @Override 120 | public boolean equals(Object o) { 121 | if (this == o) { 122 | return true; 123 | } 124 | if (o == null || getClass() != o.getClass()) { 125 | return false; 126 | } 127 | ClassReference c = (ClassReference) o; 128 | return Objects.equals(name, c.name); 129 | } 130 | 131 | @Override 132 | public int hashCode() { 133 | return name != null ? name.hashCode() : 0; 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/base/MethodReference.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.base; 2 | 3 | import java.util.Objects; 4 | import java.util.Set; 5 | 6 | public class MethodReference { 7 | private ClassReference.Handle classReference; 8 | private final Set annotations; 9 | private final String name; 10 | private final String desc; 11 | private final int access; 12 | private final boolean isStatic; 13 | 14 | public MethodReference(ClassReference.Handle classReference, 15 | String name, String desc, boolean isStatic, 16 | Set annotations, 17 | int access) { 18 | this.classReference = classReference; 19 | this.name = name; 20 | this.desc = desc; 21 | this.isStatic = isStatic; 22 | this.annotations = annotations; 23 | this.access = access; 24 | } 25 | 26 | public void setClassReference(ClassReference.Handle classReference) { 27 | this.classReference = classReference; 28 | } 29 | 30 | public int getAccess() { 31 | return access; 32 | } 33 | 34 | public ClassReference.Handle getClassReference() { 35 | return classReference; 36 | } 37 | 38 | public String getName() { 39 | return name; 40 | } 41 | 42 | public String getDesc() { 43 | return desc; 44 | } 45 | 46 | public Set getAnnotations() { 47 | return annotations; 48 | } 49 | 50 | public boolean isStatic() { 51 | return isStatic; 52 | } 53 | 54 | public Handle getHandle() { 55 | return new Handle(classReference, name, desc); 56 | } 57 | 58 | public static class Handle { 59 | private final ClassReference.Handle classReference; 60 | private final String name; 61 | private final String desc; 62 | 63 | public Handle(ClassReference.Handle classReference, String name, String desc) { 64 | this.classReference = classReference; 65 | this.name = name; 66 | this.desc = desc; 67 | } 68 | 69 | public ClassReference.Handle getClassReference() { 70 | return classReference; 71 | } 72 | 73 | public String getName() { 74 | return name; 75 | } 76 | 77 | public String getDesc() { 78 | return desc; 79 | } 80 | 81 | @Override 82 | public boolean equals(Object o) { 83 | if (this == o) return true; 84 | if (o == null || getClass() != o.getClass()) return false; 85 | Handle handle = (Handle) o; 86 | if (!Objects.equals(classReference, handle.classReference)) 87 | return false; 88 | if (!Objects.equals(name, handle.name)) 89 | return false; 90 | return Objects.equals(desc, handle.desc); 91 | } 92 | 93 | @Override 94 | public int hashCode() { 95 | int result = classReference != null ? classReference.hashCode() : 0; 96 | result = 31 * result + (name != null ? name.hashCode() : 0); 97 | result = 31 * result + (desc != null ? desc.hashCode() : 0); 98 | return result; 99 | } 100 | } 101 | 102 | @Override 103 | public boolean equals(Object o) { 104 | if (this == o) return true; 105 | if (o == null || getClass() != o.getClass()) return false; 106 | MethodReference handle = (MethodReference) o; 107 | if (!Objects.equals(classReference, handle.classReference)) 108 | return false; 109 | if (!Objects.equals(name, handle.name)) 110 | return false; 111 | return Objects.equals(desc, handle.desc); 112 | } 113 | 114 | @Override 115 | public int hashCode() { 116 | int result = classReference != null ? classReference.hashCode() : 0; 117 | result = 31 * result + (name != null ? name.hashCode() : 0); 118 | result = 31 * result + (desc != null ? desc.hashCode() : 0); 119 | return result; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/config/BaseCmd.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.config; 2 | 3 | import com.beust.jcommander.Parameter; 4 | 5 | public class BaseCmd { 6 | @Parameter(names = {"-j", "--jar"}, description = "jar file path") 7 | private String path; 8 | @Parameter(names = {"-c", "--config"}, description = "config yaml file") 9 | private String config; 10 | @Parameter(names = {"-g", "--generate"}, description = "generate config file") 11 | private boolean generate; 12 | 13 | public boolean isGenerate() { 14 | return generate; 15 | } 16 | 17 | public void setGenerate(boolean generate) { 18 | this.generate = generate; 19 | } 20 | 21 | public String getPath() { 22 | return path; 23 | } 24 | 25 | public void setPath(String path) { 26 | this.path = path; 27 | } 28 | 29 | public String getConfig() { 30 | return config; 31 | } 32 | 33 | public void setConfig(String config) { 34 | this.config = config; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/config/BaseConfig.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.config; 2 | 3 | public class BaseConfig { 4 | private String logLevel; 5 | private boolean useSpringBoot; 6 | private boolean useWebWar; 7 | private String[] obfuscateChars; 8 | private String[] classBlackList; 9 | private String[] classBlackRegexList; 10 | private String[] methodBlackList; 11 | private boolean enableClassName; 12 | private boolean enablePackageName; 13 | private boolean enableMethodName; 14 | private boolean enableFieldName; 15 | private boolean enableParamName; 16 | private boolean enableXOR; 17 | private boolean enableEncryptString; 18 | private String stringAesKey; 19 | private boolean enableAdvanceString; 20 | private String advanceStringName; 21 | private String decryptClassName; 22 | private String decryptMethodName; 23 | private String decryptKeyName; 24 | private boolean enableHideMethod; 25 | private boolean enableHideField; 26 | private boolean enableDeleteCompileInfo; 27 | private boolean enableJunk; 28 | private int junkLevel; 29 | private int maxJunkOneClass; 30 | private boolean showAllMainMethods; 31 | private boolean keepTempFile; 32 | 33 | public boolean isUseSpringBoot() { 34 | return useSpringBoot; 35 | } 36 | 37 | public void setUseSpringBoot(boolean useSpringBoot) { 38 | this.useSpringBoot = useSpringBoot; 39 | } 40 | 41 | public boolean isUseWebWar() { 42 | return useWebWar; 43 | } 44 | 45 | public void setUseWebWar(boolean useWebWar) { 46 | this.useWebWar = useWebWar; 47 | } 48 | 49 | public String getLogLevel() { 50 | return logLevel; 51 | } 52 | 53 | public void setLogLevel(String logLevel) { 54 | this.logLevel = logLevel; 55 | } 56 | 57 | public String[] getObfuscateChars() { 58 | return obfuscateChars; 59 | } 60 | 61 | public void setObfuscateChars(String[] obfuscateChars) { 62 | this.obfuscateChars = obfuscateChars; 63 | } 64 | 65 | public String[] getClassBlackList() { 66 | return classBlackList; 67 | } 68 | 69 | public void setClassBlackList(String[] classBlackList) { 70 | this.classBlackList = classBlackList; 71 | } 72 | 73 | public String[] getClassBlackRegexList() { 74 | return classBlackRegexList; 75 | } 76 | 77 | public void setClassBlackRegexList(String[] classBlackRegexList) { 78 | this.classBlackRegexList = classBlackRegexList; 79 | } 80 | 81 | public String[] getMethodBlackList() { 82 | return methodBlackList; 83 | } 84 | 85 | public void setMethodBlackList(String[] methodBlackList) { 86 | this.methodBlackList = methodBlackList; 87 | } 88 | 89 | public boolean isEnableClassName() { 90 | return enableClassName; 91 | } 92 | 93 | public void setEnableClassName(boolean enableClassName) { 94 | this.enableClassName = enableClassName; 95 | } 96 | 97 | public boolean isEnablePackageName() { 98 | return enablePackageName; 99 | } 100 | 101 | public void setEnablePackageName(boolean enablePackageName) { 102 | this.enablePackageName = enablePackageName; 103 | } 104 | 105 | public boolean isEnableMethodName() { 106 | return enableMethodName; 107 | } 108 | 109 | public void setEnableMethodName(boolean enableMethodName) { 110 | this.enableMethodName = enableMethodName; 111 | } 112 | 113 | public boolean isEnableFieldName() { 114 | return enableFieldName; 115 | } 116 | 117 | public void setEnableFieldName(boolean enableFieldName) { 118 | this.enableFieldName = enableFieldName; 119 | } 120 | 121 | public boolean isEnableParamName() { 122 | return enableParamName; 123 | } 124 | 125 | public void setEnableParamName(boolean enableParamName) { 126 | this.enableParamName = enableParamName; 127 | } 128 | 129 | public boolean isEnableXOR() { 130 | return enableXOR; 131 | } 132 | 133 | public void setEnableXOR(boolean enableXOR) { 134 | this.enableXOR = enableXOR; 135 | } 136 | 137 | public boolean isEnableEncryptString() { 138 | return enableEncryptString; 139 | } 140 | 141 | public void setEnableEncryptString(boolean enableEncryptString) { 142 | this.enableEncryptString = enableEncryptString; 143 | } 144 | 145 | public String getStringAesKey() { 146 | return stringAesKey; 147 | } 148 | 149 | public void setStringAesKey(String stringAesKey) { 150 | this.stringAesKey = stringAesKey; 151 | } 152 | 153 | public boolean isEnableAdvanceString() { 154 | return enableAdvanceString; 155 | } 156 | 157 | public void setEnableAdvanceString(boolean enableAdvanceString) { 158 | this.enableAdvanceString = enableAdvanceString; 159 | } 160 | 161 | public String getAdvanceStringName() { 162 | return advanceStringName; 163 | } 164 | 165 | public void setAdvanceStringName(String advanceStringName) { 166 | this.advanceStringName = advanceStringName; 167 | } 168 | 169 | public String getDecryptClassName() { 170 | return decryptClassName; 171 | } 172 | 173 | public void setDecryptClassName(String decryptClassName) { 174 | this.decryptClassName = decryptClassName; 175 | } 176 | 177 | public String getDecryptMethodName() { 178 | return decryptMethodName; 179 | } 180 | 181 | public void setDecryptMethodName(String decryptMethodName) { 182 | this.decryptMethodName = decryptMethodName; 183 | } 184 | 185 | public String getDecryptKeyName() { 186 | return decryptKeyName; 187 | } 188 | 189 | public void setDecryptKeyName(String decryptKeyName) { 190 | this.decryptKeyName = decryptKeyName; 191 | } 192 | 193 | public boolean isEnableHideMethod() { 194 | return enableHideMethod; 195 | } 196 | 197 | public void setEnableHideMethod(boolean enableHideMethod) { 198 | this.enableHideMethod = enableHideMethod; 199 | } 200 | 201 | public boolean isEnableHideField() { 202 | return enableHideField; 203 | } 204 | 205 | public void setEnableHideField(boolean enableHideField) { 206 | this.enableHideField = enableHideField; 207 | } 208 | 209 | public boolean isEnableDeleteCompileInfo() { 210 | return enableDeleteCompileInfo; 211 | } 212 | 213 | public void setEnableDeleteCompileInfo(boolean enableDeleteCompileInfo) { 214 | this.enableDeleteCompileInfo = enableDeleteCompileInfo; 215 | } 216 | 217 | public boolean isEnableJunk() { 218 | return enableJunk; 219 | } 220 | 221 | public void setEnableJunk(boolean enableJunk) { 222 | this.enableJunk = enableJunk; 223 | } 224 | 225 | public int getJunkLevel() { 226 | return junkLevel; 227 | } 228 | 229 | public void setJunkLevel(int junkLevel) { 230 | this.junkLevel = junkLevel; 231 | } 232 | 233 | public int getMaxJunkOneClass() { 234 | return maxJunkOneClass; 235 | } 236 | 237 | public void setMaxJunkOneClass(int maxJunkOneClass) { 238 | this.maxJunkOneClass = maxJunkOneClass; 239 | } 240 | 241 | public boolean isShowAllMainMethods() { 242 | return showAllMainMethods; 243 | } 244 | 245 | public void setShowAllMainMethods(boolean showAllMainMethods) { 246 | this.showAllMainMethods = showAllMainMethods; 247 | } 248 | 249 | public boolean isKeepTempFile() { 250 | return keepTempFile; 251 | } 252 | 253 | public void setKeepTempFile(boolean keepTempFile) { 254 | this.keepTempFile = keepTempFile; 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/config/Manager.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.config; 2 | 3 | import me.n1ar4.jar.obfuscator.asm.JunkCodeVisitor; 4 | import me.n1ar4.jar.obfuscator.core.ObfEnv; 5 | import me.n1ar4.jar.obfuscator.templates.StringDecrypt; 6 | import me.n1ar4.jar.obfuscator.templates.StringDecryptDump; 7 | import me.n1ar4.jar.obfuscator.utils.NameUtil; 8 | import me.n1ar4.jrandom.core.JRandom; 9 | import me.n1ar4.log.LogLevel; 10 | import me.n1ar4.log.LogManager; 11 | import me.n1ar4.log.Logger; 12 | import me.n1ar4.log.LoggingStream; 13 | 14 | public class Manager { 15 | private static final Logger logger = LogManager.getLogger(); 16 | 17 | public static boolean initConfig(BaseConfig config) { 18 | JRandom random = new JRandom(); 19 | JRandom.setInstance(random); 20 | 21 | // LOG LEVEL 22 | String logLevel = config.getLogLevel(); 23 | switch (logLevel) { 24 | case "debug": 25 | LogManager.setLevel(LogLevel.DEBUG); 26 | break; 27 | case "info": 28 | LogManager.setLevel(LogLevel.INFO); 29 | break; 30 | case "warn": 31 | LogManager.setLevel(LogLevel.WARN); 32 | break; 33 | case "error": 34 | LogManager.setLevel(LogLevel.ERROR); 35 | break; 36 | default: 37 | logger.error("error log level"); 38 | return false; 39 | } 40 | System.setOut(new LoggingStream(System.out, logger)); 41 | System.out.println("set log io-streams"); 42 | System.setErr(new LoggingStream(System.err, logger)); 43 | System.err.println("set log err-streams"); 44 | 45 | // CHARS 46 | if (config.getObfuscateChars() == null || 47 | config.getObfuscateChars().length < 3) { 48 | logger.warn("obfuscate chars length too small"); 49 | NameUtil.CHAR_POOL = new char[]{'i', 'l', 'L', '1', 'I'}; 50 | } else { 51 | char[] data = new char[config.getObfuscateChars().length]; 52 | for (int i = 0; i < config.getObfuscateChars().length; i++) { 53 | String s = config.getObfuscateChars()[i]; 54 | if (s == null || s.isEmpty()) { 55 | logger.error("null in obfuscate chars"); 56 | return false; 57 | } 58 | data[i] = s.charAt(0); 59 | } 60 | NameUtil.CHAR_POOL = data; 61 | } 62 | 63 | JunkCodeVisitor.MAX_JUNK_NUM = config.getMaxJunkOneClass(); 64 | ObfEnv.ADVANCE_STRING_NAME = config.getAdvanceStringName(); 65 | 66 | StringDecrypt.changeKEY(config.getStringAesKey()); 67 | StringDecryptDump.changeKEY(); 68 | StringDecryptDump.initName( 69 | config.getDecryptClassName(), 70 | config.getDecryptMethodName(), 71 | config.getDecryptKeyName()); 72 | 73 | return true; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/config/Parser.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.config; 2 | 3 | import me.n1ar4.jar.obfuscator.Const; 4 | import me.n1ar4.jar.obfuscator.utils.IOUtils; 5 | import me.n1ar4.log.LogManager; 6 | import me.n1ar4.log.Logger; 7 | import org.yaml.snakeyaml.LoaderOptions; 8 | import org.yaml.snakeyaml.Yaml; 9 | import org.yaml.snakeyaml.constructor.Constructor; 10 | import org.yaml.snakeyaml.inspector.TagInspector; 11 | 12 | import java.io.ByteArrayInputStream; 13 | import java.io.InputStream; 14 | import java.nio.charset.StandardCharsets; 15 | import java.nio.file.Files; 16 | import java.nio.file.Path; 17 | 18 | public class Parser { 19 | private static final Logger logger = LogManager.getLogger(); 20 | private String TEMPLATE; 21 | private final Yaml yaml; 22 | 23 | public Parser() { 24 | LoaderOptions options = new LoaderOptions(); 25 | TagInspector taginspector = 26 | tag -> tag.getClassName().equals(BaseConfig.class.getName()); 27 | options.setTagInspector(taginspector); 28 | yaml = new Yaml(new Constructor(BaseConfig.class, options)); 29 | InputStream is = Parser.class.getClassLoader().getResourceAsStream("config.yaml"); 30 | if (is == null) { 31 | return; 32 | } 33 | try { 34 | byte[] data = IOUtils.readAllBytes(is); 35 | TEMPLATE = new String(data, StandardCharsets.UTF_8); 36 | } catch (Exception ex) { 37 | logger.error("read template error: {}", ex); 38 | } 39 | } 40 | 41 | public void generateConfig() { 42 | try { 43 | Files.write(Const.configPath, TEMPLATE.getBytes(StandardCharsets.UTF_8)); 44 | } catch (Exception ex) { 45 | logger.error("write config file error: {}", ex.toString()); 46 | } 47 | } 48 | 49 | public BaseConfig parse(Path file) { 50 | if (!Files.exists(file)) { 51 | logger.error("config file not exist"); 52 | return null; 53 | } 54 | try { 55 | InputStream is = new ByteArrayInputStream(Files.readAllBytes(file)); 56 | return yaml.load(is); 57 | } catch (Exception ex) { 58 | logger.error("parse config error: {}", ex.toString()); 59 | } 60 | return null; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/core/AnalyzeEnv.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.core; 2 | 3 | import me.n1ar4.jar.obfuscator.base.ClassFileEntity; 4 | import me.n1ar4.jar.obfuscator.base.ClassReference; 5 | import me.n1ar4.jar.obfuscator.base.MethodReference; 6 | 7 | import java.util.*; 8 | 9 | public class AnalyzeEnv { 10 | public static Set classFileList = new HashSet<>(); 11 | public static final Set discoveredClasses = new HashSet<>(); 12 | public static final Set discoveredMethods = new HashSet<>(); 13 | public static final Map> methodsInClassMap = new HashMap<>(); 14 | public static final Map> fieldsInClassMap = new HashMap<>(); 15 | public static final Map classMap = new HashMap<>(); 16 | public static final Map methodMap = new HashMap<>(); 17 | public static final HashMap> methodCalls = new HashMap<>(); 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/core/DiscoveryRunner.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.core; 2 | 3 | import me.n1ar4.jar.obfuscator.Const; 4 | import me.n1ar4.jar.obfuscator.analyze.DiscoveryClassVisitor; 5 | import me.n1ar4.jar.obfuscator.base.ClassFileEntity; 6 | import me.n1ar4.jar.obfuscator.base.ClassReference; 7 | import me.n1ar4.jar.obfuscator.base.MethodReference; 8 | import me.n1ar4.log.LogManager; 9 | import me.n1ar4.log.Logger; 10 | import org.objectweb.asm.ClassReader; 11 | 12 | import java.util.List; 13 | import java.util.Map; 14 | import java.util.Set; 15 | 16 | public class DiscoveryRunner { 17 | private static final Logger logger = LogManager.getLogger(); 18 | 19 | public static void start(Set classFileList, 20 | Set discoveredClasses, 21 | Set discoveredMethods, 22 | Map classMap, 23 | Map methodMap, 24 | Map> fieldsInClassMap) { 25 | logger.info("start class analyze"); 26 | for (ClassFileEntity file : classFileList) { 27 | try { 28 | DiscoveryClassVisitor dcv = new DiscoveryClassVisitor(discoveredClasses, 29 | discoveredMethods, fieldsInClassMap, file.getJarName()); 30 | ClassReader cr = new ClassReader(file.getFile()); 31 | cr.accept(dcv, Const.AnalyzeASMOptions); 32 | } catch (Exception e) { 33 | logger.error("discovery error: {}", e.toString()); 34 | } 35 | } 36 | for (ClassReference clazz : discoveredClasses) { 37 | classMap.put(clazz.getHandle(), clazz); 38 | } 39 | for (MethodReference method : discoveredMethods) { 40 | methodMap.put(method.getHandle(), method); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/core/MethodCallRunner.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.core; 2 | 3 | import me.n1ar4.jar.obfuscator.Const; 4 | import me.n1ar4.jar.obfuscator.analyze.MethodCallClassVisitor; 5 | import me.n1ar4.jar.obfuscator.base.ClassFileEntity; 6 | import me.n1ar4.jar.obfuscator.base.MethodReference; 7 | import me.n1ar4.log.LogManager; 8 | import me.n1ar4.log.Logger; 9 | import org.objectweb.asm.ClassReader; 10 | 11 | import java.util.HashMap; 12 | import java.util.HashSet; 13 | import java.util.Set; 14 | 15 | public class MethodCallRunner { 16 | private static final Logger logger = LogManager.getLogger(); 17 | 18 | public static void start(Set classFileList, HashMap> methodCalls) { 20 | logger.info("start analyze method calls"); 21 | for (ClassFileEntity file : classFileList) { 22 | try { 23 | MethodCallClassVisitor mcv = 24 | new MethodCallClassVisitor(methodCalls); 25 | ClassReader cr = new ClassReader(file.getFile()); 26 | cr.accept(mcv, Const.AnalyzeASMOptions); 27 | } catch (Exception e) { 28 | logger.error("method call error: {}", e.toString()); 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/core/ObfEnv.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.core; 2 | 3 | import me.n1ar4.jar.obfuscator.base.ClassField; 4 | import me.n1ar4.jar.obfuscator.base.ClassReference; 5 | import me.n1ar4.jar.obfuscator.base.MethodReference; 6 | import me.n1ar4.jar.obfuscator.config.BaseConfig; 7 | 8 | import java.util.ArrayList; 9 | import java.util.HashMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | public class ObfEnv { 14 | public static BaseConfig config = null; 15 | public static String ADVANCE_STRING_NAME = null; 16 | public static Map classNameObfMapping = new ObfHashMap(); 17 | public static Map> ignoredClassMethodsMapping = new HashMap<>(); 18 | public static Map methodNameObfMapping = new HashMap<>(); 19 | public static Map fieldNameObfMapping = new HashMap<>(); 20 | public static final HashMap> stringInClass = new HashMap<>(); 21 | public static final HashMap> newStringInClass = new HashMap<>(); 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/core/ObfHashMap.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.core; 2 | 3 | import java.util.HashMap; 4 | 5 | 6 | public class ObfHashMap extends HashMap { 7 | @Override 8 | public String put(String key, String value) { 9 | value = value.replaceAll("^[/\\\\]+", ""); 10 | value = value.replaceAll("[/\\\\]+$", ""); 11 | if (value.endsWith(".class")) { 12 | value = value.substring(0, value.length() - 6); 13 | } 14 | return super.put(key, value); 15 | } 16 | 17 | @Override 18 | public String putIfAbsent(String key, String value) { 19 | value = value.replaceAll("^[/\\\\]+", ""); 20 | value = value.replaceAll("[/\\\\]+$", ""); 21 | if (value.endsWith(".class")) { 22 | value = value.substring(0, value.length() - 6); 23 | } 24 | return super.putIfAbsent(key, value); 25 | } 26 | } -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/loader/CustomClassLoader.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.loader; 2 | 3 | import me.n1ar4.jar.obfuscator.Const; 4 | 5 | import java.nio.file.Files; 6 | import java.nio.file.Path; 7 | import java.nio.file.Paths; 8 | 9 | public class CustomClassLoader extends ClassLoader { 10 | @Override 11 | public Class findClass(String name) { 12 | byte[] b = loadClassData(name); 13 | if (b != null) { 14 | return defineClass(name, b, 0, b.length); 15 | } 16 | return null; 17 | } 18 | 19 | private byte[] loadClassData(String name) { 20 | String classPath = name.replace('.', '/') + ".class"; 21 | Path path = Paths.get(Const.TEMP_DIR).resolve(classPath); 22 | try { 23 | return Files.readAllBytes(path); 24 | } catch (Exception ignored) { 25 | return null; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/loader/CustomClassWriter.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.loader; 2 | 3 | import me.n1ar4.log.LogManager; 4 | import me.n1ar4.log.Logger; 5 | import org.objectweb.asm.ClassReader; 6 | import org.objectweb.asm.ClassWriter; 7 | 8 | public class CustomClassWriter extends ClassWriter { 9 | private static final String defaultRet = "java/lang/Object"; 10 | private static final Logger logger = LogManager.getLogger(); 11 | private final ClassLoader classLoader; 12 | 13 | public CustomClassWriter(ClassReader classReader, int flags, ClassLoader classLoader) { 14 | super(classReader, flags); 15 | this.classLoader = classLoader; 16 | } 17 | 18 | @Override 19 | protected String getCommonSuperClass(final String type1, final String type2) { 20 | if (type1 == null || type2 == null) { 21 | return defaultRet; 22 | } 23 | if (type1.trim().isEmpty() || type2.trim().isEmpty()) { 24 | return defaultRet; 25 | } 26 | Class c, d; 27 | try { 28 | c = Class.forName(type1.replace('/', '.'), false, classLoader); 29 | d = Class.forName(type2.replace('/', '.'), false, classLoader); 30 | } catch (Exception e) { 31 | logger.debug("junk code obfuscate warn: {}", e.toString()); 32 | return defaultRet; 33 | } 34 | if (c.isAssignableFrom(d)) { 35 | return type1; 36 | } 37 | if (d.isAssignableFrom(c)) { 38 | return type2; 39 | } 40 | if (c.isInterface() || d.isInterface()) { 41 | return defaultRet; 42 | } else { 43 | do { 44 | c = c.getSuperclass(); 45 | } while (!c.isAssignableFrom(d)); 46 | return c.getName().replace('.', '/'); 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/templates/StringDecrypt.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.templates; 2 | 3 | import me.n1ar4.jrandom.core.JRandom; 4 | import me.n1ar4.log.LogManager; 5 | import me.n1ar4.log.Logger; 6 | 7 | import javax.crypto.Cipher; 8 | import javax.crypto.spec.SecretKeySpec; 9 | import java.nio.charset.Charset; 10 | import java.nio.charset.StandardCharsets; 11 | import java.util.Base64; 12 | 13 | public class StringDecrypt { 14 | private static final Logger logger = LogManager.getLogger(); 15 | public static String KEY = null; 16 | private static final String ALGORITHM = "AES"; 17 | private static final Charset CHARSET = StandardCharsets.UTF_8; 18 | 19 | public static void changeKEY(String key) { 20 | if (key == null) { 21 | KEY = JRandom.getInstance().randomString(16); 22 | logger.info("设置随机 AES_KEY 为 " + KEY); 23 | return; 24 | } 25 | if (key.equals("Y4SuperSecretKey")) { 26 | logger.warn("默认 AES_KEY 是不安全的"); 27 | KEY = JRandom.getInstance().randomString(16); 28 | logger.info("设置随机 AES_KEY 为 " + KEY); 29 | } else { 30 | if (key.length() == 16) { 31 | KEY = key; 32 | logger.info("change encrypt aes key to: {}", key); 33 | } else { 34 | logger.warn("AES_KEY 长度必须是 16 当前长度是 " + key.length()); 35 | KEY = JRandom.getInstance().randomString(16); 36 | logger.info("设置随机 AES_KEY 为 " + KEY); 37 | } 38 | } 39 | } 40 | 41 | public static String encrypt(String input) { 42 | try { 43 | SecretKeySpec key = new SecretKeySpec(KEY.getBytes(CHARSET), ALGORITHM); 44 | Cipher cipher = Cipher.getInstance(ALGORITHM); 45 | cipher.init(Cipher.ENCRYPT_MODE, key); 46 | byte[] encrypted = cipher.doFinal(input.getBytes(CHARSET)); 47 | return new String(Base64.getEncoder().encode(encrypted), CHARSET); 48 | } catch (Exception e) { 49 | return null; 50 | } 51 | } 52 | 53 | @SuppressWarnings("unused") 54 | public static String decrypt(String encrypted) { 55 | try { 56 | SecretKeySpec key = new SecretKeySpec(KEY.getBytes(CHARSET), ALGORITHM); 57 | Cipher cipher = Cipher.getInstance(ALGORITHM); 58 | cipher.init(Cipher.DECRYPT_MODE, key); 59 | byte[] data = encrypted.getBytes(CHARSET); 60 | byte[] original = cipher.doFinal(Base64.getDecoder().decode(data)); 61 | return new String(original, CHARSET); 62 | } catch (Exception e) { 63 | return null; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/templates/StringDecryptDump.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.templates; 2 | 3 | import org.objectweb.asm.*; 4 | 5 | public class StringDecryptDump implements Opcodes { 6 | public static String AES_KEY = null; 7 | public static String className = null; 8 | public static String methodName = null; 9 | private static String keyName = null; 10 | 11 | public static void changeKEY() { 12 | AES_KEY = StringDecrypt.KEY; 13 | } 14 | 15 | public static void initName(String c, String m, String k) { 16 | String defaultClassName = "org/apache/commons/collections/list/AbstractHashMap"; 17 | String defaultMethodName = "newMap"; 18 | String defaultKeyName = "LiLiLLLiiiLLiiLLi"; 19 | if (c == null || m == null || k == null) { 20 | className = defaultClassName; 21 | methodName = defaultMethodName; 22 | keyName = defaultKeyName; 23 | return; 24 | } 25 | c = c.replace(".", "/"); 26 | if (c.isEmpty()) { 27 | className = defaultClassName; 28 | } else { 29 | className = c; 30 | } 31 | if (m.isEmpty()) { 32 | methodName = defaultMethodName; 33 | } else { 34 | methodName = m; 35 | } 36 | if (k.isEmpty()) { 37 | keyName = defaultKeyName; 38 | } else { 39 | keyName = k; 40 | } 41 | } 42 | 43 | public static byte[] dump() { 44 | ClassWriter classWriter = new ClassWriter(0); 45 | FieldVisitor fieldVisitor; 46 | MethodVisitor methodVisitor; 47 | classWriter.visit(V1_8, ACC_PUBLIC | ACC_SUPER, className, null, "java/lang/Object", null); 48 | classWriter.visitInnerClass("java/util/Base64$Encoder", "java/util/Base64", "Encoder", ACC_PUBLIC | ACC_STATIC); 49 | classWriter.visitInnerClass("java/util/Base64$Decoder", "java/util/Base64", "Decoder", ACC_PUBLIC | ACC_STATIC); 50 | { 51 | fieldVisitor = classWriter.visitField(ACC_PRIVATE | ACC_STATIC, keyName, "Ljava/lang/String;", null, null); 52 | fieldVisitor.visitEnd(); 53 | } 54 | { 55 | fieldVisitor = classWriter.visitField(ACC_PRIVATE | ACC_FINAL | ACC_STATIC, "ALGORITHM", "Ljava/lang/String;", null, "AES"); 56 | fieldVisitor.visitEnd(); 57 | } 58 | { 59 | fieldVisitor = classWriter.visitField(ACC_PRIVATE | ACC_FINAL | ACC_STATIC, "CHARSET", "Ljava/nio/charset/Charset;", null, null); 60 | fieldVisitor.visitEnd(); 61 | } 62 | { 63 | methodVisitor = classWriter.visitMethod(ACC_PUBLIC | ACC_STATIC, methodName, "(Ljava/lang/String;)Ljava/lang/String;", null, null); 64 | methodVisitor.visitCode(); 65 | Label label0 = new Label(); 66 | Label label1 = new Label(); 67 | Label label2 = new Label(); 68 | methodVisitor.visitTryCatchBlock(label0, label1, label2, "java/lang/Exception"); 69 | methodVisitor.visitLabel(label0); 70 | methodVisitor.visitTypeInsn(NEW, "javax/crypto/spec/SecretKeySpec"); 71 | methodVisitor.visitInsn(DUP); 72 | methodVisitor.visitFieldInsn(GETSTATIC, className, keyName, "Ljava/lang/String;"); 73 | methodVisitor.visitFieldInsn(GETSTATIC, className, "CHARSET", "Ljava/nio/charset/Charset;"); 74 | methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "getBytes", "(Ljava/nio/charset/Charset;)[B", false); 75 | methodVisitor.visitLdcInsn("AES"); 76 | methodVisitor.visitMethodInsn(INVOKESPECIAL, "javax/crypto/spec/SecretKeySpec", "", "([BLjava/lang/String;)V", false); 77 | methodVisitor.visitVarInsn(ASTORE, 1); 78 | Label label3 = new Label(); 79 | methodVisitor.visitLabel(label3); 80 | methodVisitor.visitLdcInsn("AES"); 81 | methodVisitor.visitMethodInsn(INVOKESTATIC, "javax/crypto/Cipher", "getInstance", "(Ljava/lang/String;)Ljavax/crypto/Cipher;", false); 82 | methodVisitor.visitVarInsn(ASTORE, 2); 83 | Label label4 = new Label(); 84 | methodVisitor.visitLabel(label4); 85 | methodVisitor.visitVarInsn(ALOAD, 2); 86 | methodVisitor.visitInsn(ICONST_2); 87 | methodVisitor.visitVarInsn(ALOAD, 1); 88 | methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "javax/crypto/Cipher", "init", "(ILjava/security/Key;)V", false); 89 | Label label5 = new Label(); 90 | methodVisitor.visitLabel(label5); 91 | methodVisitor.visitVarInsn(ALOAD, 0); 92 | methodVisitor.visitFieldInsn(GETSTATIC, className, "CHARSET", "Ljava/nio/charset/Charset;"); 93 | methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "getBytes", "(Ljava/nio/charset/Charset;)[B", false); 94 | methodVisitor.visitVarInsn(ASTORE, 3); 95 | Label label6 = new Label(); 96 | methodVisitor.visitLabel(label6); 97 | methodVisitor.visitVarInsn(ALOAD, 2); 98 | methodVisitor.visitMethodInsn(INVOKESTATIC, "java/util/Base64", "getDecoder", "()Ljava/util/Base64$Decoder;", false); 99 | methodVisitor.visitVarInsn(ALOAD, 3); 100 | methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/util/Base64$Decoder", "decode", "([B)[B", false); 101 | methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "javax/crypto/Cipher", "doFinal", "([B)[B", false); 102 | methodVisitor.visitVarInsn(ASTORE, 4); 103 | Label label7 = new Label(); 104 | methodVisitor.visitLabel(label7); 105 | methodVisitor.visitTypeInsn(NEW, "java/lang/String"); 106 | methodVisitor.visitInsn(DUP); 107 | methodVisitor.visitVarInsn(ALOAD, 4); 108 | methodVisitor.visitFieldInsn(GETSTATIC, className, "CHARSET", "Ljava/nio/charset/Charset;"); 109 | methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/String", "", "([BLjava/nio/charset/Charset;)V", false); 110 | methodVisitor.visitLabel(label1); 111 | methodVisitor.visitInsn(ARETURN); 112 | methodVisitor.visitLabel(label2); 113 | methodVisitor.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[]{"java/lang/Exception"}); 114 | methodVisitor.visitVarInsn(ASTORE, 1); 115 | Label label8 = new Label(); 116 | methodVisitor.visitLabel(label8); 117 | methodVisitor.visitInsn(ACONST_NULL); 118 | methodVisitor.visitInsn(ARETURN); 119 | Label label9 = new Label(); 120 | methodVisitor.visitLabel(label9); 121 | methodVisitor.visitLocalVariable("key", "Ljavax/crypto/spec/SecretKeySpec;", null, label3, label2, 1); 122 | methodVisitor.visitLocalVariable("cipher", "Ljavax/crypto/Cipher;", null, label4, label2, 2); 123 | methodVisitor.visitLocalVariable("data", "[B", null, label6, label2, 3); 124 | methodVisitor.visitLocalVariable("original", "[B", null, label7, label2, 4); 125 | methodVisitor.visitLocalVariable("e", "Ljava/lang/Exception;", null, label8, label9, 1); 126 | methodVisitor.visitLocalVariable("encrypted", "Ljava/lang/String;", null, label0, label9, 0); 127 | methodVisitor.visitMaxs(4, 5); 128 | methodVisitor.visitEnd(); 129 | } 130 | { 131 | methodVisitor = classWriter.visitMethod(ACC_STATIC, "", "()V", null, null); 132 | methodVisitor.visitCode(); 133 | Label label0 = new Label(); 134 | methodVisitor.visitLabel(label0); 135 | methodVisitor.visitLdcInsn(AES_KEY); 136 | methodVisitor.visitFieldInsn(PUTSTATIC, className, keyName, "Ljava/lang/String;"); 137 | Label label1 = new Label(); 138 | methodVisitor.visitLabel(label1); 139 | methodVisitor.visitFieldInsn(GETSTATIC, "java/nio/charset/StandardCharsets", "UTF_8", "Ljava/nio/charset/Charset;"); 140 | methodVisitor.visitFieldInsn(PUTSTATIC, className, "CHARSET", "Ljava/nio/charset/Charset;"); 141 | methodVisitor.visitInsn(RETURN); 142 | methodVisitor.visitMaxs(1, 0); 143 | methodVisitor.visitEnd(); 144 | } 145 | classWriter.visitEnd(); 146 | return classWriter.toByteArray(); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/transform/ClassNameTransformer.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.transform; 2 | 3 | import me.n1ar4.jar.obfuscator.Const; 4 | import me.n1ar4.jar.obfuscator.asm.ClassNameVisitor; 5 | import me.n1ar4.jar.obfuscator.core.ObfEnv; 6 | import me.n1ar4.log.LogManager; 7 | import me.n1ar4.log.Logger; 8 | import org.objectweb.asm.ClassReader; 9 | import org.objectweb.asm.ClassWriter; 10 | 11 | import java.nio.file.Files; 12 | import java.nio.file.Path; 13 | import java.nio.file.Paths; 14 | import java.util.Map; 15 | 16 | public class ClassNameTransformer { 17 | private static final Logger logger = LogManager.getLogger(); 18 | 19 | public static void transform() { 20 | for (Map.Entry entry : ObfEnv.classNameObfMapping.entrySet()) { 21 | String originalName = entry.getKey(); 22 | String newName = entry.getValue(); 23 | Path tempDir = Paths.get(Const.TEMP_DIR); 24 | 25 | if (ObfEnv.config.isUseSpringBoot()) { 26 | tempDir = tempDir.resolve("BOOT-INF/classes/"); 27 | } 28 | if (ObfEnv.config.isUseWebWar()) { 29 | tempDir = tempDir.resolve("WEB-INF/classes/"); 30 | } 31 | 32 | Path classPath = tempDir.resolve(Paths.get(originalName + ".class")); 33 | Path newClassPath = tempDir.resolve(Paths.get(newName + ".class")); 34 | try { 35 | Files.createDirectories(newClassPath.getParent()); 36 | } catch (Exception ignored) { 37 | } 38 | if (!Files.exists(classPath)) { 39 | logger.debug("class not exist: {}", classPath.toString()); 40 | continue; 41 | } 42 | try { 43 | ClassReader classReader = new ClassReader(Files.readAllBytes(classPath)); 44 | ClassWriter classWriter = new ClassWriter(classReader, 0); 45 | ClassNameVisitor changer = new ClassNameVisitor(classWriter); 46 | classReader.accept(changer, Const.AnalyzeASMOptions); 47 | Files.delete(classPath); 48 | Files.write(newClassPath, classWriter.toByteArray()); 49 | } catch (Exception ex) { 50 | logger.error("transform error: {}", ex.toString()); 51 | } 52 | } 53 | logger.info("rename class name finish"); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/transform/DeleteInfoTransformer.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.transform; 2 | 3 | import me.n1ar4.jar.obfuscator.Const; 4 | import me.n1ar4.jar.obfuscator.asm.CompileInfoVisitor; 5 | import me.n1ar4.jar.obfuscator.core.ObfEnv; 6 | import me.n1ar4.log.LogManager; 7 | import me.n1ar4.log.Logger; 8 | import org.objectweb.asm.ClassReader; 9 | import org.objectweb.asm.ClassWriter; 10 | 11 | import java.nio.file.Files; 12 | import java.nio.file.Path; 13 | import java.nio.file.Paths; 14 | import java.util.Map; 15 | 16 | public class DeleteInfoTransformer { 17 | private static final Logger logger = LogManager.getLogger(); 18 | 19 | public static void transform() { 20 | for (Map.Entry entry : ObfEnv.classNameObfMapping.entrySet()) { 21 | String originalName = entry.getKey(); 22 | Path tempDir = Paths.get(Const.TEMP_DIR); 23 | 24 | if (ObfEnv.config.isUseSpringBoot()) { 25 | tempDir = tempDir.resolve("BOOT-INF/classes/"); 26 | } 27 | if (ObfEnv.config.isUseWebWar()) { 28 | tempDir = tempDir.resolve("WEB-INF/classes/"); 29 | } 30 | 31 | Path classPath = tempDir.resolve(Paths.get(originalName + ".class")); 32 | if (!Files.exists(classPath)) { 33 | logger.debug("class not exist: {}", classPath.toString()); 34 | continue; 35 | } 36 | try { 37 | ClassReader classReader = new ClassReader(Files.readAllBytes(classPath)); 38 | ClassWriter classWriter = new ClassWriter(classReader, 0); 39 | CompileInfoVisitor changer = new CompileInfoVisitor(classWriter); 40 | classReader.accept(changer, Const.AnalyzeASMOptions); 41 | Files.delete(classPath); 42 | Files.write(classPath, classWriter.toByteArray()); 43 | } catch (Exception ex) { 44 | logger.error("transform error: {}", ex.toString()); 45 | } 46 | } 47 | logger.info("delete compile info finish"); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/transform/FieldNameTransformer.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.transform; 2 | 3 | import me.n1ar4.jar.obfuscator.Const; 4 | import me.n1ar4.jar.obfuscator.asm.FieldNameVisitor; 5 | import me.n1ar4.jar.obfuscator.core.ObfEnv; 6 | import me.n1ar4.log.LogManager; 7 | import me.n1ar4.log.Logger; 8 | import org.objectweb.asm.ClassReader; 9 | import org.objectweb.asm.ClassWriter; 10 | 11 | import java.nio.file.Files; 12 | import java.nio.file.Path; 13 | import java.nio.file.Paths; 14 | import java.util.Map; 15 | 16 | @SuppressWarnings("all") 17 | public class FieldNameTransformer { 18 | private static final Logger logger = LogManager.getLogger(); 19 | 20 | public static void transform() { 21 | for (Map.Entry entry : ObfEnv.classNameObfMapping.entrySet()) { 22 | String newName = entry.getValue(); 23 | Path tempDir = Paths.get(Const.TEMP_DIR); 24 | 25 | if (ObfEnv.config.isUseSpringBoot()) { 26 | tempDir = tempDir.resolve("BOOT-INF/classes/"); 27 | } 28 | if (ObfEnv.config.isUseWebWar()) { 29 | tempDir = tempDir.resolve("WEB-INF/classes/"); 30 | } 31 | 32 | Path newClassPath = tempDir.resolve(Paths.get(newName + ".class")); 33 | if (!Files.exists(newClassPath)) { 34 | logger.debug("class not exist: {}", newClassPath.toString()); 35 | continue; 36 | } 37 | try { 38 | ClassReader classReader = new ClassReader(Files.readAllBytes(newClassPath)); 39 | ClassWriter classWriter = new ClassWriter(classReader, 0); 40 | FieldNameVisitor changer = new FieldNameVisitor(classWriter); 41 | classReader.accept(changer, Const.AnalyzeASMOptions); 42 | Files.delete(newClassPath); 43 | Files.write(newClassPath, classWriter.toByteArray()); 44 | } catch (Exception ex) { 45 | logger.error("transform error: {}", ex.toString()); 46 | } 47 | } 48 | logger.info("rename field name finish"); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/transform/JunkCodeTransformer.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.transform; 2 | 3 | import me.n1ar4.jar.obfuscator.Const; 4 | import me.n1ar4.jar.obfuscator.asm.JunkCodeVisitor; 5 | import me.n1ar4.jar.obfuscator.config.BaseConfig; 6 | import me.n1ar4.jar.obfuscator.core.ObfEnv; 7 | import me.n1ar4.jar.obfuscator.loader.CustomClassLoader; 8 | import me.n1ar4.jar.obfuscator.loader.CustomClassWriter; 9 | import me.n1ar4.log.LogManager; 10 | import me.n1ar4.log.Logger; 11 | import org.objectweb.asm.ClassReader; 12 | import org.objectweb.asm.ClassWriter; 13 | import org.objectweb.asm.MethodTooLargeException; 14 | 15 | import java.nio.file.Files; 16 | import java.nio.file.Path; 17 | import java.nio.file.Paths; 18 | import java.util.Map; 19 | 20 | @SuppressWarnings("all") 21 | public class JunkCodeTransformer { 22 | private static final Logger logger = LogManager.getLogger(); 23 | 24 | public static void transform(BaseConfig config) { 25 | for (Map.Entry entry : ObfEnv.classNameObfMapping.entrySet()) { 26 | String newName = entry.getValue(); 27 | Path tempDir = Paths.get(Const.TEMP_DIR); 28 | 29 | if (ObfEnv.config.isUseSpringBoot()) { 30 | tempDir = tempDir.resolve("BOOT-INF/classes/"); 31 | } 32 | if (ObfEnv.config.isUseWebWar()) { 33 | tempDir = tempDir.resolve("WEB-INF/classes/"); 34 | } 35 | 36 | Path newClassPath = tempDir.resolve(Paths.get(newName + ".class")); 37 | if (!Files.exists(newClassPath)) { 38 | logger.debug("class not exist: {}", newClassPath.toString()); 39 | continue; 40 | } 41 | try { 42 | ClassReader classReader = new ClassReader(Files.readAllBytes(newClassPath)); 43 | CustomClassLoader loader = new CustomClassLoader(); 44 | // COMPUTE_FRAMES 需要修改 CLASSLOADER 来计算 45 | ClassWriter classWriter = new CustomClassWriter(classReader, 46 | ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS, loader); 47 | JunkCodeVisitor changer = new JunkCodeVisitor(classWriter, config); 48 | try { 49 | classReader.accept(changer, Const.AnalyzeASMOptions); 50 | } catch (Exception ignored) { 51 | // CustomClassLoader 可能会找不到类 52 | // 这个地方目前做法是跳过这个类的花指令混淆 53 | logger.debug("花指令混淆使用自定义类加载器出现错误"); 54 | continue; 55 | } 56 | Files.delete(newClassPath); 57 | Files.write(newClassPath, classWriter.toByteArray()); 58 | } catch (MethodTooLargeException ex) { 59 | logger.error("method too large"); 60 | logger.error("please check max junk config"); 61 | return; 62 | } catch (Exception ex) { 63 | logger.error("transform error: {}", ex.toString()); 64 | } 65 | } 66 | logger.info("junk code transform finish"); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/transform/MainClassTransformer.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.transform; 2 | 3 | import me.n1ar4.jar.obfuscator.Const; 4 | import me.n1ar4.jar.obfuscator.asm.MainMethodVisitor; 5 | import me.n1ar4.jar.obfuscator.core.ObfEnv; 6 | import me.n1ar4.log.LogManager; 7 | import me.n1ar4.log.Logger; 8 | import org.objectweb.asm.ClassReader; 9 | 10 | import java.nio.file.Files; 11 | import java.nio.file.Path; 12 | import java.nio.file.Paths; 13 | import java.util.Map; 14 | 15 | public class MainClassTransformer { 16 | private static final Logger logger = LogManager.getLogger(); 17 | 18 | public static void transform() { 19 | for (Map.Entry entry : ObfEnv.classNameObfMapping.entrySet()) { 20 | try { 21 | String originalName = entry.getKey(); 22 | Path tempDir = Paths.get(Const.TEMP_DIR); 23 | 24 | if (ObfEnv.config.isUseSpringBoot()) { 25 | tempDir = tempDir.resolve("BOOT-INF/classes/"); 26 | } 27 | if (ObfEnv.config.isUseWebWar()) { 28 | tempDir = tempDir.resolve("WEB-INF/classes/"); 29 | } 30 | 31 | Path classPath = tempDir.resolve(Paths.get(originalName + ".class")); 32 | byte[] classBytes = Files.readAllBytes(classPath); 33 | ClassReader classReader = new ClassReader(classBytes); 34 | MainMethodVisitor checker = new MainMethodVisitor(); 35 | classReader.accept(checker, Const.AnalyzeASMOptions); 36 | if (checker.hasMainMethod()) { 37 | logger.info("find main class: {}", originalName); 38 | } 39 | } catch (Exception ex) { 40 | logger.error("transform error: {}", ex.toString()); 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/transform/MethodNameTransformer.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.transform; 2 | 3 | import me.n1ar4.jar.obfuscator.Const; 4 | import me.n1ar4.jar.obfuscator.asm.MethodNameVisitor; 5 | import me.n1ar4.jar.obfuscator.core.ObfEnv; 6 | import me.n1ar4.log.LogManager; 7 | import me.n1ar4.log.Logger; 8 | import org.objectweb.asm.ClassReader; 9 | import org.objectweb.asm.ClassWriter; 10 | 11 | import java.nio.file.Files; 12 | import java.nio.file.Path; 13 | import java.nio.file.Paths; 14 | import java.util.Map; 15 | 16 | @SuppressWarnings("all") 17 | public class MethodNameTransformer { 18 | private static final Logger logger = LogManager.getLogger(); 19 | 20 | public static void transform() { 21 | for (Map.Entry entry : ObfEnv.classNameObfMapping.entrySet()) { 22 | String newName = entry.getValue(); 23 | Path tempDir = Paths.get(Const.TEMP_DIR); 24 | 25 | if (ObfEnv.config.isUseSpringBoot()) { 26 | tempDir = tempDir.resolve("BOOT-INF/classes/"); 27 | } 28 | if (ObfEnv.config.isUseWebWar()) { 29 | tempDir = tempDir.resolve("WEB-INF/classes/"); 30 | } 31 | 32 | Path newClassPath = tempDir.resolve(Paths.get(newName + ".class")); 33 | if (!Files.exists(newClassPath)) { 34 | logger.debug("class not exist: {}", newClassPath.toString()); 35 | continue; 36 | } 37 | try { 38 | ClassReader classReader = new ClassReader(Files.readAllBytes(newClassPath)); 39 | ClassWriter classWriter = new ClassWriter(classReader, 0); 40 | MethodNameVisitor changer = new MethodNameVisitor(classWriter); 41 | classReader.accept(changer, Const.AnalyzeASMOptions); 42 | Files.delete(newClassPath); 43 | Files.write(newClassPath, classWriter.toByteArray()); 44 | } catch (Exception ex) { 45 | logger.error("transform error: {}", ex.toString()); 46 | } 47 | } 48 | logger.info("rename method name finish"); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/transform/ParameterTransformer.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.transform; 2 | 3 | import me.n1ar4.jar.obfuscator.Const; 4 | import me.n1ar4.jar.obfuscator.asm.ParameterVisitor; 5 | import me.n1ar4.jar.obfuscator.core.ObfEnv; 6 | import me.n1ar4.log.LogManager; 7 | import me.n1ar4.log.Logger; 8 | import org.objectweb.asm.ClassReader; 9 | import org.objectweb.asm.ClassWriter; 10 | 11 | import java.nio.file.Files; 12 | import java.nio.file.Path; 13 | import java.nio.file.Paths; 14 | import java.util.Map; 15 | 16 | @SuppressWarnings("all") 17 | public class ParameterTransformer { 18 | private static final Logger logger = LogManager.getLogger(); 19 | 20 | public static void transform() { 21 | for (Map.Entry entry : ObfEnv.classNameObfMapping.entrySet()) { 22 | String newName = entry.getValue(); 23 | Path tempDir = Paths.get(Const.TEMP_DIR); 24 | 25 | if (ObfEnv.config.isUseSpringBoot()) { 26 | tempDir = tempDir.resolve("BOOT-INF/classes/"); 27 | } 28 | if (ObfEnv.config.isUseWebWar()) { 29 | tempDir = tempDir.resolve("WEB-INF/classes/"); 30 | } 31 | 32 | Path newClassPath = tempDir.resolve(Paths.get(newName + ".class")); 33 | if (!Files.exists(newClassPath)) { 34 | logger.debug("class not exist: {}", newClassPath.toString()); 35 | continue; 36 | } 37 | try { 38 | ClassReader classReader = new ClassReader(Files.readAllBytes(newClassPath)); 39 | ClassWriter classWriter = new ClassWriter(classReader, 0); 40 | ParameterVisitor changer = new ParameterVisitor(classWriter); 41 | classReader.accept(changer, Const.AnalyzeASMOptions); 42 | Files.delete(newClassPath); 43 | Files.write(newClassPath, classWriter.toByteArray()); 44 | } catch (Exception ex) { 45 | logger.error("transform error: {}", ex.toString()); 46 | } 47 | } 48 | logger.info("rename parameter name finish"); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/transform/StringArrayTransformer.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.transform; 2 | 3 | import me.n1ar4.jar.obfuscator.Const; 4 | import me.n1ar4.jar.obfuscator.asm.StringArrayVisitor; 5 | import me.n1ar4.jar.obfuscator.core.ObfEnv; 6 | import me.n1ar4.log.LogManager; 7 | import me.n1ar4.log.Logger; 8 | import org.objectweb.asm.ClassReader; 9 | import org.objectweb.asm.ClassWriter; 10 | 11 | import java.nio.file.Files; 12 | import java.nio.file.Path; 13 | import java.nio.file.Paths; 14 | import java.util.Map; 15 | 16 | @SuppressWarnings("all") 17 | public class StringArrayTransformer { 18 | private static final Logger logger = LogManager.getLogger(); 19 | public static int INDEX = 0; 20 | 21 | public static void transform() { 22 | for (Map.Entry entry : ObfEnv.classNameObfMapping.entrySet()) { 23 | String newName = entry.getValue(); 24 | Path tempDir = Paths.get(Const.TEMP_DIR); 25 | 26 | if (ObfEnv.config.isUseSpringBoot()) { 27 | tempDir = tempDir.resolve("BOOT-INF/classes/"); 28 | } 29 | if (ObfEnv.config.isUseWebWar()) { 30 | tempDir = tempDir.resolve("WEB-INF/classes/"); 31 | } 32 | 33 | Path newClassPath = tempDir.resolve(Paths.get(newName + ".class")); 34 | if (!Files.exists(newClassPath)) { 35 | logger.debug("class not exist: {}", newClassPath.toString()); 36 | continue; 37 | } 38 | try { 39 | INDEX = 0; 40 | ClassReader classReader = new ClassReader(Files.readAllBytes(newClassPath)); 41 | ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS); 42 | StringArrayVisitor changer = new StringArrayVisitor(classWriter); 43 | classReader.accept(changer, ClassReader.EXPAND_FRAMES); 44 | Files.delete(newClassPath); 45 | Files.write(newClassPath, classWriter.toByteArray()); 46 | } catch (Exception ex) { 47 | ex.printStackTrace(); 48 | logger.error("transform error: {}", ex.toString()); 49 | } 50 | } 51 | logger.info("advance encrypt string finish"); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/transform/StringTransformer.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.transform; 2 | 3 | import me.n1ar4.jar.obfuscator.Const; 4 | import me.n1ar4.jar.obfuscator.asm.StringVisitor; 5 | import me.n1ar4.jar.obfuscator.core.ObfEnv; 6 | import me.n1ar4.log.LogManager; 7 | import me.n1ar4.log.Logger; 8 | import org.objectweb.asm.ClassReader; 9 | import org.objectweb.asm.ClassWriter; 10 | 11 | import java.nio.file.Files; 12 | import java.nio.file.Path; 13 | import java.nio.file.Paths; 14 | import java.util.Map; 15 | 16 | @SuppressWarnings("all") 17 | public class StringTransformer { 18 | private static final Logger logger = LogManager.getLogger(); 19 | 20 | public static void transform() { 21 | for (Map.Entry entry : ObfEnv.classNameObfMapping.entrySet()) { 22 | String newName = entry.getValue(); 23 | Path tempDir = Paths.get(Const.TEMP_DIR); 24 | 25 | if (ObfEnv.config.isUseSpringBoot()) { 26 | tempDir = tempDir.resolve("BOOT-INF/classes/"); 27 | } 28 | if (ObfEnv.config.isUseWebWar()) { 29 | tempDir = tempDir.resolve("WEB-INF/classes/"); 30 | } 31 | 32 | Path newClassPath = tempDir.resolve(Paths.get(newName + ".class")); 33 | if (!Files.exists(newClassPath)) { 34 | logger.debug("class not exist: {}", newClassPath.toString()); 35 | continue; 36 | } 37 | try { 38 | ClassReader classReader = new ClassReader(Files.readAllBytes(newClassPath)); 39 | ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS); 40 | StringVisitor changer = new StringVisitor(classWriter); 41 | classReader.accept(changer, Const.AnalyzeASMOptions); 42 | Files.delete(newClassPath); 43 | Files.write(newClassPath, classWriter.toByteArray()); 44 | } catch (Exception ex) { 45 | logger.error("transform error: {}", ex.toString()); 46 | } 47 | } 48 | logger.info("encrypt string finish"); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/transform/XORTransformer.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.transform; 2 | 3 | import me.n1ar4.jar.obfuscator.Const; 4 | import me.n1ar4.jar.obfuscator.asm.IntToXorVisitor; 5 | import me.n1ar4.jar.obfuscator.core.ObfEnv; 6 | import me.n1ar4.log.LogManager; 7 | import me.n1ar4.log.Logger; 8 | import org.objectweb.asm.ClassReader; 9 | import org.objectweb.asm.ClassWriter; 10 | 11 | import java.nio.file.Files; 12 | import java.nio.file.Path; 13 | import java.nio.file.Paths; 14 | import java.util.Map; 15 | 16 | @SuppressWarnings("all") 17 | public class XORTransformer { 18 | private static final Logger logger = LogManager.getLogger(); 19 | 20 | public static void transform() { 21 | for (Map.Entry entry : ObfEnv.classNameObfMapping.entrySet()) { 22 | String newName = entry.getValue(); 23 | Path tempDir = Paths.get(Const.TEMP_DIR); 24 | 25 | if (ObfEnv.config.isUseSpringBoot()) { 26 | tempDir = tempDir.resolve("BOOT-INF/classes/"); 27 | } 28 | if (ObfEnv.config.isUseWebWar()) { 29 | tempDir = tempDir.resolve("WEB-INF/classes/"); 30 | } 31 | 32 | Path newClassPath = tempDir.resolve(Paths.get(newName + ".class")); 33 | if (!Files.exists(newClassPath)) { 34 | logger.debug("class not exist: {}", newClassPath.toString()); 35 | continue; 36 | } 37 | try { 38 | ClassReader classReader = new ClassReader(Files.readAllBytes(newClassPath)); 39 | ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS); 40 | IntToXorVisitor changer = new IntToXorVisitor(classWriter); 41 | classReader.accept(changer, Const.AnalyzeASMOptions); 42 | Files.delete(newClassPath); 43 | Files.write(newClassPath, classWriter.toByteArray()); 44 | } catch (Exception ex) { 45 | logger.error("transform error: {}", ex.toString()); 46 | } 47 | } 48 | logger.info("xor transform finish"); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/utils/ByteUtil.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.utils; 2 | 3 | /** 4 | * hex string < - > byte array 5 | */ 6 | @SuppressWarnings("unused") 7 | public class ByteUtil { 8 | public static byte[] hexStringToByteArray(String s) { 9 | int len = s.length(); 10 | byte[] data = new byte[len / 2]; 11 | for (int i = 0; i < len; i += 2) { 12 | data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) 13 | + Character.digit(s.charAt(i + 1), 16)); 14 | } 15 | return data; 16 | } 17 | 18 | public static String bytesToHex(byte[] bytes) { 19 | StringBuilder sb = new StringBuilder(); 20 | for (byte b : bytes) { 21 | sb.append(String.format("%02X", b)); 22 | } 23 | return sb.toString(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/utils/ColorUtil.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.utils; 2 | 3 | public class ColorUtil { 4 | private static final String ANSI_RESET = "\033[0m"; 5 | private static final String ANSI_RED = "\033[31m"; 6 | private static final String ANSI_GREEN = "\033[32m"; 7 | private static final String ANSI_BLUE = "\033[34m"; 8 | private static final String ANSI_YELLOW = "\033[33m"; 9 | 10 | public static String red(String input) { 11 | return ANSI_RED + input + ANSI_RESET; 12 | } 13 | 14 | public static String green(String input) { 15 | return ANSI_GREEN + input + ANSI_RESET; 16 | } 17 | 18 | public static String blue(String input) { 19 | return ANSI_BLUE + input + ANSI_RESET; 20 | } 21 | 22 | public static String yellow(String input) { 23 | return ANSI_YELLOW + input + ANSI_RESET; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/utils/DescUtil.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.utils; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.regex.Matcher; 6 | import java.util.regex.Pattern; 7 | 8 | public class DescUtil { 9 | public static List extractClassNames(String descriptor) { 10 | List classNames = new ArrayList<>(); 11 | Pattern pattern = Pattern.compile("L([^;]+);"); 12 | Matcher matcher = pattern.matcher(descriptor); 13 | while (matcher.find()) { 14 | classNames.add(matcher.group(1)); 15 | } 16 | return classNames; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/utils/DirUtil.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.utils; 2 | 3 | import me.n1ar4.jar.obfuscator.core.ObfEnv; 4 | import me.n1ar4.log.LogManager; 5 | import me.n1ar4.log.Logger; 6 | 7 | import java.io.File; 8 | import java.io.FileInputStream; 9 | import java.io.FileOutputStream; 10 | import java.io.IOException; 11 | import java.nio.file.Files; 12 | import java.nio.file.Paths; 13 | import java.util.Map; 14 | import java.util.Objects; 15 | import java.util.zip.CRC32; 16 | import java.util.zip.ZipEntry; 17 | import java.util.zip.ZipInputStream; 18 | import java.util.zip.ZipOutputStream; 19 | 20 | public class DirUtil { 21 | private static final Logger logger = LogManager.getLogger(); 22 | 23 | public static void unzip(String ZipFilePath, String destDir) throws IOException { 24 | File destDirectory = new File(destDir); 25 | if (!destDirectory.exists()) { 26 | boolean s = destDirectory.mkdirs(); 27 | if (!s) { 28 | logger.warn("create dir {} error", destDirectory); 29 | } 30 | } 31 | ZipInputStream ZipInputStream = new ZipInputStream(Files.newInputStream( 32 | Paths.get(ZipFilePath))); 33 | ZipEntry ZipEntry; 34 | while ((ZipEntry = ZipInputStream.getNextEntry()) != null) { 35 | File file = new File(destDir, ZipEntry.getName()); 36 | if (ZipEntry.isDirectory()) { 37 | boolean s = file.mkdirs(); 38 | if (!s) { 39 | logger.warn("create dir {} error", destDirectory); 40 | } 41 | } else { 42 | // BUG FIX 43 | try { 44 | Files.createDirectories(Paths.get(file.getParent())); 45 | } catch (Exception ignored) { 46 | } 47 | byte[] buffer = new byte[1024]; 48 | FileOutputStream fos; 49 | try { 50 | fos = new FileOutputStream(file); 51 | } catch (Exception ignored) { 52 | continue; 53 | } 54 | int bytesRead; 55 | while ((bytesRead = ZipInputStream.read(buffer)) != -1) { 56 | fos.write(buffer, 0, bytesRead); 57 | } 58 | fos.close(); 59 | } 60 | ZipInputStream.closeEntry(); 61 | } 62 | ZipInputStream.close(); 63 | } 64 | 65 | public static void zip(String sourceDir, String outputZip) { 66 | File sourceFolder = new File(sourceDir); 67 | try (ZipOutputStream jos = new ZipOutputStream(Files.newOutputStream(Paths.get(outputZip)))) { 68 | // 设置压缩方法为 STORED(不压缩) 69 | jos.setMethod(ZipOutputStream.STORED); 70 | 71 | if (sourceFolder.exists() && sourceFolder.isDirectory()) { 72 | File[] files = sourceFolder.listFiles(); 73 | if (files != null) { 74 | for (File file : files) { 75 | addToZip(file, jos, ""); 76 | } 77 | } 78 | } 79 | } catch (IOException e) { 80 | System.err.println("zip Zip error: " + e.toString()); 81 | } 82 | } 83 | 84 | public static void addToZip(File source, ZipOutputStream jos, String parentDir) throws IOException { 85 | if (source.isDirectory()) { 86 | String dirPath = parentDir + source.getName() + "/"; 87 | ZipEntry entry = new ZipEntry(dirPath); 88 | entry.setMethod(ZipEntry.STORED); 89 | entry.setSize(0); 90 | entry.setCompressedSize(0); 91 | entry.setCrc(0); 92 | jos.putNextEntry(entry); 93 | jos.closeEntry(); 94 | for (File file : Objects.requireNonNull(source.listFiles())) { 95 | addToZip(file, jos, dirPath); 96 | } 97 | } else { 98 | String entryName = parentDir + source.getName(); 99 | ZipEntry entry = new ZipEntry(entryName); 100 | entry.setMethod(ZipEntry.STORED); 101 | long size = source.length(); 102 | long crc = computeCRC32(source); 103 | 104 | entry.setSize(size); 105 | entry.setCompressedSize(size); 106 | entry.setCrc(crc); 107 | jos.putNextEntry(entry); 108 | try (FileInputStream fis = new FileInputStream(source)) { 109 | byte[] buffer = new byte[1024]; 110 | int bytesRead; 111 | while ((bytesRead = fis.read(buffer)) != -1) { 112 | jos.write(buffer, 0, bytesRead); 113 | } 114 | } 115 | jos.closeEntry(); 116 | } 117 | } 118 | 119 | private static long computeCRC32(File file) throws IOException { 120 | CRC32 crc = new CRC32(); 121 | try (FileInputStream fis = new FileInputStream(file)) { 122 | byte[] buffer = new byte[1024]; 123 | int bytesRead; 124 | while ((bytesRead = fis.read(buffer)) != -1) { 125 | crc.update(buffer, 0, bytesRead); 126 | } 127 | } 128 | return crc.getValue(); 129 | } 130 | public static void deleteDirectory(File directory) { 131 | File[] files = directory.listFiles(); 132 | if (files != null) { 133 | for (File file : files) { 134 | if (file.isDirectory()) { 135 | deleteDirectory(file); 136 | } else { 137 | boolean s = file.delete(); 138 | if (!s) { 139 | logger.debug("delete dir {} error", file); 140 | } 141 | } 142 | } 143 | } 144 | boolean s = directory.delete(); 145 | if (!s) { 146 | logger.debug("delete dir {} error", directory); 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/utils/FileUtil.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.utils; 2 | 3 | import java.nio.file.Path; 4 | 5 | public class FileUtil { 6 | public static String getFileNameWithoutExt(Path path) { 7 | String fileName = path.getFileName().toString(); 8 | int dotIndex = fileName.lastIndexOf('.'); 9 | if (dotIndex > 0) { 10 | fileName = fileName.substring(0, dotIndex); 11 | } 12 | return fileName; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/utils/IOUtils.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.utils; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.List; 8 | 9 | public class IOUtils { 10 | private static final int DEFAULT_BUFFER_SIZE = 8192; 11 | private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8; 12 | 13 | public static byte[] readAllBytes(InputStream is) throws IOException { 14 | return readNBytes(is, Integer.MAX_VALUE); 15 | } 16 | 17 | public static byte[] readNBytes(InputStream is, int len) throws IOException { 18 | if (len < 0) { 19 | throw new IllegalArgumentException("len < 0"); 20 | } 21 | List bufs = null; 22 | byte[] result = null; 23 | int total = 0; 24 | int remaining = len; 25 | int n; 26 | do { 27 | byte[] buf = new byte[Math.min(remaining, DEFAULT_BUFFER_SIZE)]; 28 | int nread = 0; 29 | while ((n = is.read(buf, nread, 30 | Math.min(buf.length - nread, remaining))) > 0) { 31 | nread += n; 32 | remaining -= n; 33 | } 34 | 35 | if (nread > 0) { 36 | if (MAX_BUFFER_SIZE - total < nread) { 37 | throw new OutOfMemoryError("Required array size too large"); 38 | } 39 | total += nread; 40 | if (result == null) { 41 | result = buf; 42 | } else { 43 | if (bufs == null) { 44 | bufs = new ArrayList<>(); 45 | bufs.add(result); 46 | } 47 | bufs.add(buf); 48 | } 49 | } 50 | } while (n == 0 && remaining > 0); 51 | 52 | if (bufs == null) { 53 | if (result == null) { 54 | return new byte[0]; 55 | } 56 | return result.length == total ? 57 | result : Arrays.copyOf(result, total); 58 | } 59 | 60 | result = new byte[total]; 61 | int offset = 0; 62 | remaining = total; 63 | for (byte[] b : bufs) { 64 | int count = Math.min(b.length, remaining); 65 | System.arraycopy(b, 0, result, offset, count); 66 | offset += count; 67 | remaining -= count; 68 | } 69 | 70 | return result; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/utils/JunkUtil.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.utils; 2 | 3 | import me.n1ar4.jrandom.core.JRandom; 4 | import org.objectweb.asm.ClassVisitor; 5 | import org.objectweb.asm.MethodVisitor; 6 | import org.objectweb.asm.Opcodes; 7 | 8 | public class JunkUtil { 9 | public static void addHttpCode(ClassVisitor cv) { 10 | MethodVisitor newMethod = cv.visitMethod( 11 | Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, 12 | JRandom.getInstance().randomString(8), 13 | "()V", null, null); 14 | newMethod.visitCode(); 15 | newMethod.visitTypeInsn(Opcodes.NEW, "java/net/URL"); 16 | newMethod.visitInsn(Opcodes.DUP); 17 | newMethod.visitLdcInsn("https://" + JRandom.getInstance().randomString(16)); 18 | newMethod.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/net/URL", "", 19 | "(Ljava/lang/String;)V", false); 20 | newMethod.visitVarInsn(Opcodes.ASTORE, 0); 21 | newMethod.visitVarInsn(Opcodes.ALOAD, 0); 22 | newMethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/net/URL", "openConnection", 23 | "()Ljava/net/URLConnection;", false); 24 | newMethod.visitTypeInsn(Opcodes.CHECKCAST, "java/net/HttpURLConnection"); 25 | newMethod.visitVarInsn(Opcodes.ASTORE, 1); 26 | newMethod.visitVarInsn(Opcodes.ALOAD, 1); 27 | newMethod.visitLdcInsn("GET"); 28 | newMethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/net/HttpURLConnection", 29 | "setRequestMethod", "(Ljava/lang/String;)V", false); 30 | newMethod.visitVarInsn(Opcodes.ALOAD, 1); 31 | newMethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/net/HttpURLConnection", 32 | "getResponseCode", "()I", false); 33 | newMethod.visitInsn(Opcodes.POP); 34 | newMethod.visitInsn(Opcodes.RETURN); 35 | newMethod.visitMaxs(3, 2); 36 | newMethod.visitEnd(); 37 | } 38 | public static void addPrintMethod(ClassVisitor cv) { 39 | MethodVisitor newMethod = cv.visitMethod(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, 40 | JRandom.getInstance().randomString(8), "()V", null, null); 41 | newMethod.visitCode(); 42 | newMethod.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); 43 | newMethod.visitLdcInsn(JRandom.getInstance().randomString(32)); 44 | newMethod.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", 45 | "(Ljava/lang/String;)V", false); 46 | newMethod.visitInsn(Opcodes.RETURN); 47 | newMethod.visitMaxs(2, 1); 48 | newMethod.visitEnd(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/utils/NameUtil.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.utils; 2 | 3 | import me.n1ar4.jrandom.core.JRandom; 4 | 5 | import java.util.HashSet; 6 | 7 | @SuppressWarnings("all") 8 | public class NameUtil { 9 | public static char[] CHAR_POOL = null; 10 | private static final HashSet generatedStrings = new HashSet<>(); 11 | private static final HashSet generatedMethods = new HashSet<>(); 12 | private static final HashSet generatedFields = new HashSet<>(); 13 | private static final HashSet packageNames = new HashSet<>(); 14 | 15 | public static String genNewName() { 16 | return genBase(1); 17 | } 18 | 19 | public static String genNewMethod() { 20 | return genBase(2); 21 | } 22 | 23 | public static String genNewFields() { 24 | return genBase(3); 25 | } 26 | 27 | public static String genPackage() { 28 | return genBase(4); 29 | } 30 | 31 | public static String genWithSet(HashSet exists) { 32 | JRandom random = JRandom.getInstance(); 33 | while (true) { 34 | int length = 10 + random.getInt(0, 3); 35 | StringBuilder sb = new StringBuilder(); 36 | for (int i = 0; i < length; i++) { 37 | sb.append(CHAR_POOL[random.getInt(0, CHAR_POOL.length)]); 38 | } 39 | if (sb.charAt(0) == '~' || sb.charAt(0) == '1') { 40 | continue; 41 | } 42 | String result = sb.toString(); 43 | if (!exists.contains(result)) { 44 | exists.add(result); 45 | return result; 46 | } 47 | } 48 | } 49 | 50 | private static String genBase(int op) { 51 | JRandom random = JRandom.getInstance(); 52 | while (true) { 53 | int length = 10 + random.getInt(0, 3); 54 | StringBuilder sb = new StringBuilder(); 55 | for (int i = 0; i < length; i++) { 56 | sb.append(CHAR_POOL[random.getInt(0, CHAR_POOL.length)]); 57 | } 58 | if (sb.charAt(0) == '~' || sb.charAt(0) == '1') { 59 | continue; 60 | } 61 | String result = sb.toString(); 62 | if (op == 2) { 63 | if (!generatedMethods.contains(result)) { 64 | generatedMethods.add(result); 65 | return result; 66 | } 67 | } else if (op == 1) { 68 | if (!generatedStrings.contains(result)) { 69 | generatedStrings.add(result); 70 | return result; 71 | } 72 | } else if (op == 3) { 73 | if (!generatedFields.contains(result)) { 74 | generatedFields.add(result); 75 | return result; 76 | } 77 | } else if (op == 4) { 78 | if (!packageNames.contains(result)) { 79 | packageNames.add(result); 80 | return result; 81 | } 82 | } else { 83 | return null; 84 | } 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/utils/PackageUtil.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.utils; 2 | 3 | import me.n1ar4.jar.obfuscator.base.ClassReference; 4 | import me.n1ar4.jar.obfuscator.base.MethodReference; 5 | import me.n1ar4.jar.obfuscator.config.BaseConfig; 6 | import me.n1ar4.jar.obfuscator.core.AnalyzeEnv; 7 | import org.objectweb.asm.Opcodes; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.regex.Matcher; 13 | import java.util.regex.Pattern; 14 | 15 | public class PackageUtil { 16 | private static final List internalList = new ArrayList<>(); 17 | 18 | public static void buildInternalBlackList() { 19 | // JNI 的 CLASS 默认加到黑名单里面 20 | for (Map.Entry> entry : 21 | AnalyzeEnv.methodsInClassMap.entrySet()) { 22 | String className = entry.getKey().getName(); 23 | List ref = entry.getValue(); 24 | for (MethodReference m : ref) { 25 | int access = m.getAccess(); 26 | if ((access & Opcodes.ACC_NATIVE) != 0) { 27 | internalList.add(className); 28 | break; 29 | } 30 | } 31 | } 32 | } 33 | 34 | public static boolean inBlackClass(String className, BaseConfig config) { 35 | className = className.replace(".", "/"); 36 | for (String s : config.getClassBlackList()) { 37 | s = s.replace(".", "/"); 38 | if (className.equals(s)) { 39 | return true; 40 | } 41 | } 42 | for (String s : config.getClassBlackRegexList()) { 43 | className = className.replace(".", "/"); 44 | Pattern pattern = Pattern.compile(s, Pattern.DOTALL); 45 | Matcher matcher = pattern.matcher(className); 46 | if (matcher.matches()) { 47 | return true; 48 | } 49 | } 50 | for (String s : internalList) { 51 | s = s.replace(".", "/"); 52 | if (className.equals(s)) { 53 | return true; 54 | } 55 | } 56 | return false; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jar/obfuscator/utils/RandomUtil.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jar.obfuscator.utils; 2 | 3 | import me.n1ar4.jrandom.core.JRandom; 4 | import org.objectweb.asm.Opcodes; 5 | 6 | public class RandomUtil { 7 | public static int genInt(int start, int end) { 8 | return JRandom.getInstance().getInt(start, end); 9 | } 10 | 11 | public static int genICONSTOpcode() { 12 | switch (RandomUtil.genInt(0, 6)) { 13 | case 0: 14 | return Opcodes.ICONST_0; 15 | case 1: 16 | return Opcodes.ICONST_1; 17 | case 2: 18 | return Opcodes.ICONST_2; 19 | case 3: 20 | return Opcodes.ICONST_3; 21 | case 4: 22 | return Opcodes.ICONST_4; 23 | case 5: 24 | return Opcodes.ICONST_5; 25 | default: 26 | return 0; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/jrandom/core/JRandom.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.jrandom.core; 2 | 3 | import me.n1ar4.log.LogManager; 4 | import me.n1ar4.log.Logger; 5 | 6 | import java.security.SecureRandom; 7 | import java.util.List; 8 | 9 | @SuppressWarnings("all") 10 | public class JRandom { 11 | private static final Logger logger = LogManager.getLogger(); 12 | private static final String letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 13 | private static final String alphanumeric = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 14 | 15 | private final SecureRandom random = new SecureRandom(); 16 | 17 | private static JRandom fakeInstance = null; 18 | private static JRandom instance; 19 | 20 | public static JRandom getInstance() { 21 | if (instance != null) { 22 | return instance; 23 | } else { 24 | if (fakeInstance == null) { 25 | logger.info("使用普通的随机数模块"); 26 | fakeInstance = new JRandom(System.currentTimeMillis()); 27 | return fakeInstance; 28 | } else { 29 | return fakeInstance; 30 | } 31 | } 32 | } 33 | 34 | public static void setInstance(JRandom in) { 35 | instance = in; 36 | } 37 | 38 | public JRandom(long seed) { 39 | random.setSeed(System.currentTimeMillis()); 40 | } 41 | 42 | public JRandom() { 43 | random.setSeed(System.currentTimeMillis()); 44 | } 45 | 46 | /** 47 | * 最底层的获得随机的函数 48 | * 49 | * @return 随机的 long 类型 50 | */ 51 | private long getLong() { 52 | return random.nextLong(); 53 | } 54 | 55 | /** 56 | * 获得任意的 INT 57 | * 58 | * @return 随机的 INT 范围 59 | */ 60 | public int getInt() { 61 | long longValue = getLong(); 62 | return (int) (longValue & 0xFFFFFFFFL); 63 | } 64 | 65 | /** 66 | * 左闭右开指定范围的随机 67 | * 68 | * @param start 左 69 | * @param end 右 70 | * @return 左闭右开的 INT 随机 71 | */ 72 | public int getInt(int start, int end) { 73 | if (start >= end) { 74 | throw new IllegalArgumentException("start must be less than end"); 75 | } 76 | long range = (long) end - (long) start; 77 | long longValue = getLong(); 78 | int randomInRange = (int) (Math.abs(longValue) % range); 79 | return start + randomInRange; 80 | } 81 | 82 | /** 83 | * 获得指定长度的字符串(大小写字母和数字且开头不是数字) 84 | * 85 | * @param length 长度 86 | * @return 随即后的值 87 | */ 88 | public String randomString(int length) { 89 | if (length <= 0) { 90 | throw new IllegalArgumentException("length must be greater than 0"); 91 | } 92 | StringBuilder sb = new StringBuilder(length); 93 | int firstCharIndex = getInt(0, letters.length()); 94 | sb.append(letters.charAt(firstCharIndex)); 95 | for (int i = 1; i < length; i++) { 96 | int charIndex = getInt(0, alphanumeric.length()); 97 | sb.append(alphanumeric.charAt(charIndex)); 98 | } 99 | return sb.toString(); 100 | } 101 | 102 | /** 103 | * 获得随机的 BYTES 104 | * 105 | * @param length 长度 106 | * @return 随机的 BYTES 107 | */ 108 | public byte[] randomBytes(int length) { 109 | if (length <= 0) { 110 | throw new IllegalArgumentException("length must be greater than 0"); 111 | } 112 | byte[] bytes = new byte[length]; 113 | int index = 0; 114 | while (index < length) { 115 | int intValue = getInt(); 116 | for (int i = 0; i < 4 && index < length; i++) { 117 | bytes[index++] = (byte) ((intValue >>> (i * 8)) & 0xFF); 118 | } 119 | } 120 | return bytes; 121 | } 122 | 123 | /** 124 | * 获得随机的列表元素 125 | * 126 | * @param list List 127 | * @param Object 128 | * @return 随机的 129 | */ 130 | public T randomEle(List list) { 131 | if (list == null || list.isEmpty()) { 132 | throw new IllegalArgumentException("list must not be null or empty"); 133 | } 134 | int index = getInt(0, list.size()); 135 | return list.get(index); 136 | } 137 | 138 | /** 139 | * 获得随机的 INT 字符串 140 | * 141 | * @param n 长度 142 | * @return INT 字符串 143 | */ 144 | public String randomNumbers(int n) { 145 | if (n <= 0) { 146 | throw new IllegalArgumentException("length must be greater than 0"); 147 | } 148 | StringBuilder sb = new StringBuilder(n); 149 | for (int i = 0; i < n; i++) { 150 | int digit = getInt(0, 10); 151 | sb.append(digit); 152 | } 153 | return sb.toString(); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/log/Log.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.log; 2 | 3 | import java.time.LocalDateTime; 4 | import java.time.format.DateTimeFormatter; 5 | 6 | class Log { 7 | static final String ANSI_RESET = "\u001B[0m"; 8 | static final String ANSI_GREEN = "\u001B[32m"; 9 | static final String ANSI_YELLOW = "\u001B[33m"; 10 | static final String ANSI_RED = "\u001B[31m"; 11 | static final String ANSI_BLUE = "\u001B[34m"; 12 | 13 | static void info(String message) { 14 | log(LogLevel.INFO, message, ANSI_GREEN); 15 | } 16 | 17 | static void error(String message) { 18 | log(LogLevel.ERROR, message, ANSI_RED); 19 | } 20 | 21 | static void debug(String message) { 22 | log(LogLevel.DEBUG, message, ANSI_BLUE); 23 | } 24 | 25 | static void warn(String message) { 26 | log(LogLevel.WARN, message, ANSI_YELLOW); 27 | } 28 | 29 | static void log(LogLevel level, String message, String color) { 30 | if (level.compareTo(LogManager.logLevel) >= 0) { 31 | String timestamp = LocalDateTime.now().format( 32 | DateTimeFormatter.ofPattern("HH:mm:ss")); 33 | String className = LogUtil.getClassName(); 34 | String methodName = LogUtil.getMethodName(); 35 | String lineNumber = LogUtil.getLineNumber(); 36 | String logMessage; 37 | if (!lineNumber.equals("-1")) { 38 | logMessage = String.format("[%s] [%s%s%s] [%s:%s:%s] %s", 39 | timestamp, color, level.name(), ANSI_RESET, className, 40 | methodName, lineNumber, message); 41 | } else { 42 | logMessage = String.format("[%s] [%s%s%s] [%s:%s] %s", 43 | timestamp, color, level.name(), ANSI_RESET, className, 44 | methodName, message); 45 | } 46 | System.out.println(logMessage); 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/log/LogLevel.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.log; 2 | 3 | public enum LogLevel { 4 | DEBUG, 5 | INFO, 6 | WARN, 7 | ERROR, 8 | } -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/log/LogManager.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.log; 2 | 3 | public class LogManager { 4 | static LogLevel logLevel = LogLevel.INFO; 5 | 6 | public static void setLevel(LogLevel level) { 7 | logLevel = level; 8 | } 9 | 10 | public static Logger getLogger() { 11 | return new Logger(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/log/LogUtil.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.log; 2 | 3 | public class LogUtil { 4 | private static final int STACK_TRACE_INDEX = 5; 5 | 6 | public static String getClassName() { 7 | String fullClassName = Thread.currentThread() 8 | .getStackTrace()[STACK_TRACE_INDEX].getClassName(); 9 | int lastDotIndex = fullClassName.lastIndexOf('.'); 10 | if (lastDotIndex != -1) { 11 | return fullClassName.substring(lastDotIndex + 1); 12 | } else { 13 | return fullClassName; 14 | } 15 | } 16 | 17 | public static String getMethodName() { 18 | return Thread.currentThread() 19 | .getStackTrace()[STACK_TRACE_INDEX].getMethodName(); 20 | } 21 | 22 | public static String getLineNumber() { 23 | return String.valueOf(Thread.currentThread() 24 | .getStackTrace()[STACK_TRACE_INDEX].getLineNumber()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/log/Logger.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.log; 2 | 3 | 4 | public class Logger { 5 | private String formatMessage(String message, Object[] args) { 6 | int start = 0; 7 | StringBuilder sb = new StringBuilder(); 8 | int argIndex = 0; 9 | while (start < message.length()) { 10 | int open = message.indexOf("{}", start); 11 | if (open == -1) { 12 | sb.append(message.substring(start)); 13 | break; 14 | } 15 | sb.append(message, start, open); 16 | if (argIndex < args.length) { 17 | sb.append(args[argIndex++]); 18 | } else { 19 | sb.append("{}"); 20 | } 21 | start = open + 2; 22 | } 23 | return sb.toString(); 24 | } 25 | 26 | public void info(String message) { 27 | Log.info(message); 28 | } 29 | 30 | public void info(String message, Object... args) { 31 | Log.info(formatMessage(message, args)); 32 | } 33 | 34 | public void error(String message) { 35 | Log.error(message); 36 | } 37 | 38 | public void error(String message, Object... args) { 39 | Log.error(formatMessage(message, args)); 40 | } 41 | 42 | public void debug(String message) { 43 | Log.debug(message); 44 | } 45 | 46 | public void debug(String message, Object... args) { 47 | Log.debug(formatMessage(message, args)); 48 | } 49 | 50 | public void warn(String message) { 51 | Log.warn(message); 52 | } 53 | 54 | public void warn(String message, Object... args) { 55 | Log.warn(formatMessage(message, args)); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/me/n1ar4/log/LoggingStream.java: -------------------------------------------------------------------------------- 1 | package me.n1ar4.log; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStream; 5 | import java.io.PrintStream; 6 | 7 | public class LoggingStream extends PrintStream { 8 | private final Logger logger; 9 | private final OutputStream originalOut; 10 | 11 | public LoggingStream(OutputStream out, Logger logger) { 12 | super(out); 13 | this.logger = logger; 14 | this.originalOut = out; 15 | } 16 | 17 | @Override 18 | public void println(String x) { 19 | if (!isLoggerCall()) { 20 | logger.info(x); 21 | } else { 22 | directPrintln(x); 23 | } 24 | } 25 | 26 | private boolean isLoggerCall() { 27 | for (StackTraceElement element : Thread.currentThread().getStackTrace()) { 28 | if (element.getClassName().equals("me.n1ar4.log.Logger")) { 29 | return true; 30 | } 31 | } 32 | return false; 33 | } 34 | 35 | private void directPrintln(String x) { 36 | synchronized (this) { 37 | byte[] bytes = (x + System.lineSeparator()).getBytes(); 38 | try { 39 | originalOut.write(bytes); 40 | originalOut.flush(); 41 | } catch (IOException e) { 42 | setError(); 43 | } 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /src/main/resources/config.yaml: -------------------------------------------------------------------------------- 1 | # jar obfuscator v2 配置文件 2 | # jar obfuscator v2 by jar-analyzer team (4ra1n) 3 | # https://github.com/jar-analyzer/jar-obfuscator 4 | 5 | # 日志级别 6 | # debug info warn error 7 | logLevel: info 8 | 9 | # 如果你是 springboot 请开启 10 | useSpringBoot: false 11 | # 如果你是 war web 项目请开启 12 | useWebWar: false 13 | 14 | # 混淆字符配置 15 | # 类名方法名等信息会根据字符进行随机排列组合 16 | obfuscateChars: 17 | - "i" 18 | - "l" 19 | - "L" 20 | - "1" 21 | - "I" 22 | # 不对某些类做混淆(不混淆其中的所有内容) 23 | # 通常情况必须加入 main 入口 24 | classBlackList: 25 | - "com.test.Main" 26 | # 不对指定正则的类进行混淆 27 | # 注意这里的类名匹配是 java/lang/String 而不是 java.lang.String 28 | # 该配置和 classBlackList 同时生效 29 | classBlackRegexList: 30 | - "java/.*" 31 | - "com/intellij/.*" 32 | # 不对某些 method 名做混淆 正则 33 | # visit.* 忽略 JAVA ASM 的 visitCode visitMethod 等方法 34 | # start.* 忽略 JAVAFX 因为启动基于 start 方法 35 | # 以此类推某些方法和类是不能混淆的(类继承和接口实现等) 36 | methodBlackList: 37 | - "visit.*" 38 | - "start.*" 39 | 40 | # 开启类名混淆 41 | enableClassName: true 42 | # 开启包名混淆 43 | enablePackageName: true 44 | # 开启方法名混淆 45 | enableMethodName: true 46 | # 开启字段混淆 47 | enableFieldName: true 48 | # 开启参数名混淆 49 | enableParamName: true 50 | # 开启数字异或混淆 51 | enableXOR: true 52 | 53 | # 开启加密字符串 54 | enableEncryptString: true 55 | # 加密使用 AES KEY 56 | # 注意长度必须是 16 且不包含中文 57 | stringAesKey: Y4SuperSecretKey 58 | # 开启进阶字符串混淆 59 | enableAdvanceString: true 60 | # 进阶字符串处理参数 61 | advanceStringName: GIiIiLA 62 | # 字符串解密类名 63 | decryptClassName: org.apache.commons.collections.list.AbstractHashMap 64 | # 字符串解密方法名 65 | decryptMethodName: newMap 66 | # 字符串 AES KEY 名字 67 | decryptKeyName: LiLiLLLiiiLLiiLLi 68 | 69 | # 是否隐藏方法 70 | enableHideMethod: true 71 | # 是否隐藏字段 72 | enableHideField: true 73 | 74 | # 开启删除编译信息选项 75 | enableDeleteCompileInfo: true 76 | 77 | # 开启花指令混淆 78 | enableJunk: true 79 | # 花指令级别 80 | # 最低1 最高5 81 | # 使用 3 以上会生成垃圾方法 82 | junkLevel: 5 83 | # 一个类中的花指令数量上限 84 | maxJunkOneClass: 2000 85 | 86 | # 是否打印所有主函数 87 | showAllMainMethods: true 88 | 89 | # 是否保留临时类文件 90 | keepTempFile: false -------------------------------------------------------------------------------- /src/main/resources/thanks.txt: -------------------------------------------------------------------------------- 1 | 4ra1n (https://github.com/4ra1n) 2 | pcdlrzxx (https://github.com/pcdlrzxx) 3 | hadis91 (https://github.com/hadis91) 4 | Y4tacker (https://github.com/Y4tacker) -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | ## TEST 2 | 3 | test-01: 仅开启 class 混淆 4 | test-02: 开启 class package 混淆 5 | test-03: 基础混淆全开 -------------------------------------------------------------------------------- /test/origin.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jar-analyzer/jar-obfuscator/9d12c8c12562cffbc73875f771397c143ac1d74e/test/origin.jar -------------------------------------------------------------------------------- /test/shiro.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jar-analyzer/jar-obfuscator/9d12c8c12562cffbc73875f771397c143ac1d74e/test/shiro.jar -------------------------------------------------------------------------------- /test/test-01.yaml: -------------------------------------------------------------------------------- 1 | logLevel: info 2 | useSpringBoot: false 3 | useWebWar: false 4 | obfuscateChars: 5 | - "i" 6 | - "l" 7 | - "L" 8 | - "1" 9 | - "I" 10 | classBlackList: 11 | - "me.n1ar4.fake.gui.Application" 12 | classBlackRegexList: 13 | - "java.*" 14 | - "javax.*" 15 | - "kotlin.*" 16 | - "net.*" 17 | - "okhttp3.*" 18 | - "okio.*" 19 | - "org.*" 20 | - "com.*" 21 | - "edu.*" 22 | - "javassist.*" 23 | methodBlackList: 24 | - "visit.*" 25 | - "start.*" 26 | 27 | enableClassName: true 28 | enablePackageName: false 29 | enableMethodName: false 30 | enableFieldName: false 31 | enableParamName: false 32 | enableXOR: false 33 | 34 | enableEncryptString: false 35 | stringAesKey: Y4SuperSecretKey 36 | enableAdvanceString: false 37 | advanceStringName: GIiIiLA 38 | decryptClassName: org.apache.commons.collections.list.AbstractHashMap 39 | decryptMethodName: newMap 40 | decryptKeyName: LiLiLLLiiiLLiiLLi 41 | 42 | enableHideMethod: false 43 | enableHideField: false 44 | enableDeleteCompileInfo: false 45 | 46 | enableJunk: false 47 | junkLevel: 5 48 | maxJunkOneClass: 2000 49 | 50 | showAllMainMethods: false 51 | keepTempFile: false -------------------------------------------------------------------------------- /test/test-02.yaml: -------------------------------------------------------------------------------- 1 | logLevel: info 2 | useSpringBoot: false 3 | useWebWar: false 4 | obfuscateChars: 5 | - "i" 6 | - "l" 7 | - "L" 8 | - "1" 9 | - "I" 10 | classBlackList: 11 | - "me.n1ar4.fake.gui.Application" 12 | classBlackRegexList: 13 | - "java.*" 14 | - "javax.*" 15 | - "kotlin.*" 16 | - "net.*" 17 | - "okhttp3.*" 18 | - "okio.*" 19 | - "org.*" 20 | - "com.*" 21 | - "edu.*" 22 | - "javassist.*" 23 | methodBlackList: 24 | - "visit.*" 25 | - "start.*" 26 | 27 | enableClassName: true 28 | enablePackageName: true 29 | enableMethodName: false 30 | enableFieldName: false 31 | enableParamName: false 32 | enableXOR: false 33 | 34 | enableEncryptString: false 35 | stringAesKey: Y4SuperSecretKey 36 | enableAdvanceString: false 37 | advanceStringName: GIiIiLA 38 | decryptClassName: org.apache.commons.collections.list.AbstractHashMap 39 | decryptMethodName: newMap 40 | decryptKeyName: LiLiLLLiiiLLiiLLi 41 | 42 | enableHideMethod: false 43 | enableHideField: false 44 | enableDeleteCompileInfo: false 45 | 46 | enableJunk: false 47 | junkLevel: 5 48 | maxJunkOneClass: 2000 49 | 50 | showAllMainMethods: false 51 | keepTempFile: false -------------------------------------------------------------------------------- /test/test-03.yaml: -------------------------------------------------------------------------------- 1 | logLevel: info 2 | useSpringBoot: false 3 | useWebWar: false 4 | obfuscateChars: 5 | - "i" 6 | - "l" 7 | - "L" 8 | - "1" 9 | - "I" 10 | classBlackList: 11 | - "me.n1ar4.fake.gui.Application" 12 | classBlackRegexList: 13 | - "java.*" 14 | - "javax.*" 15 | - "kotlin.*" 16 | - "net.*" 17 | - "okhttp3.*" 18 | - "okio.*" 19 | - "org.*" 20 | - "com.*" 21 | - "edu.*" 22 | - "javassist.*" 23 | methodBlackList: 24 | - "visit.*" 25 | - "start.*" 26 | 27 | enableClassName: true 28 | enablePackageName: true 29 | enableMethodName: true 30 | enableFieldName: true 31 | enableParamName: true 32 | enableXOR: true 33 | 34 | enableEncryptString: false 35 | stringAesKey: Y4SuperSecretKey 36 | enableAdvanceString: false 37 | advanceStringName: GIiIiLA 38 | decryptClassName: org.apache.commons.collections.list.AbstractHashMap 39 | decryptMethodName: newMap 40 | decryptKeyName: LiLiLLLiiiLLiiLLi 41 | 42 | enableHideMethod: false 43 | enableHideField: false 44 | enableDeleteCompileInfo: false 45 | 46 | enableJunk: false 47 | junkLevel: 5 48 | maxJunkOneClass: 2000 49 | 50 | showAllMainMethods: false 51 | keepTempFile: false -------------------------------------------------------------------------------- /test/test-sb-01.yaml: -------------------------------------------------------------------------------- 1 | logLevel: info 2 | useSpringBoot: true 3 | useWebWar: false 4 | obfuscateChars: 5 | - "i" 6 | - "l" 7 | - "L" 8 | - "1" 9 | - "I" 10 | classBlackList: 11 | - "com.example.shiro.ShiroApplication" 12 | classBlackRegexList: 13 | - "java.*" 14 | - "javax.*" 15 | - "kotlin.*" 16 | - "net.*" 17 | - "okhttp3.*" 18 | - "okio.*" 19 | - "org.*" 20 | - "edu.*" 21 | - "javassist.*" 22 | methodBlackList: 23 | - "visit.*" 24 | - "start.*" 25 | 26 | enableClassName: true 27 | enablePackageName: false 28 | enableMethodName: false 29 | enableFieldName: false 30 | enableParamName: false 31 | enableXOR: false 32 | 33 | enableEncryptString: false 34 | stringAesKey: Y4SuperSecretKey 35 | enableAdvanceString: false 36 | advanceStringName: GIiIiLA 37 | decryptClassName: org.apache.commons.collections.list.AbstractHashMap 38 | decryptMethodName: newMap 39 | decryptKeyName: LiLiLLLiiiLLiiLLi 40 | 41 | enableHideMethod: false 42 | enableHideField: false 43 | enableDeleteCompileInfo: false 44 | 45 | enableJunk: false 46 | junkLevel: 5 47 | maxJunkOneClass: 2000 48 | 49 | showAllMainMethods: false 50 | keepTempFile: false -------------------------------------------------------------------------------- /test/test-sb-02.yaml: -------------------------------------------------------------------------------- 1 | logLevel: info 2 | useSpringBoot: true 3 | useWebWar: false 4 | obfuscateChars: 5 | - "i" 6 | - "l" 7 | - "L" 8 | - "1" 9 | - "I" 10 | classBlackList: 11 | - "com.example.shiro.ShiroApplication" 12 | classBlackRegexList: 13 | - "java.*" 14 | - "javax.*" 15 | - "kotlin.*" 16 | - "net.*" 17 | - "okhttp3.*" 18 | - "okio.*" 19 | - "org.*" 20 | - "edu.*" 21 | - "javassist.*" 22 | methodBlackList: 23 | - "doGetAuthorizationInfo" 24 | - "doGetAuthenticationInfo" 25 | 26 | enableClassName: true 27 | enablePackageName: false 28 | enableMethodName: true 29 | enableFieldName: true 30 | enableParamName: true 31 | enableXOR: true 32 | 33 | enableEncryptString: true 34 | stringAesKey: Y4SuperSecretKey 35 | enableAdvanceString: true 36 | advanceStringName: GIiIiLA 37 | decryptClassName: org.apache.commons.collections.list.AbstractHashMap 38 | decryptMethodName: newMap 39 | decryptKeyName: LiLiLLLiiiLLiiLLi 40 | 41 | enableHideMethod: false 42 | enableHideField: false 43 | enableDeleteCompileInfo: false 44 | 45 | enableJunk: true 46 | junkLevel: 5 47 | maxJunkOneClass: 2000 48 | 49 | showAllMainMethods: false 50 | keepTempFile: false --------------------------------------------------------------------------------