diff --git a/CHANGELOG.MD b/CHANGELOG.MD index 1146418..5bbd200 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -1,5 +1,24 @@ # CHANGELOG +## 1.5.0 + +从该版本开始,运行混淆会自动创建一个 `class-obf-lib` 目录 + +用户如果遇到依赖报错,可以将混淆 `class` 需要的依赖 `jar` 放入该目录解决报错问题 + +更新日志: + +- [重要] 解决某些情况下需要正确依赖才能运行的 `BUG` +- [优化] 生成 `AES` 字符串解密方法时避免抛出异常 +- [优化] 提高解密方法内的 `Basse64` 解码兼容性 + +感谢以下用户的贡献: + +- 4ra1n (https://github.com/4ra1n) +- lz520520 (https://github.com/lz520520) + +可供下载的文件都由 `Github Actions` 构建,使用 `java -jar class-obf.jar` 启动 + ## 1.4.0 支持将 `INVOKE*` 指令转为反射调用,结合其他配置可完成进阶混淆 diff --git a/README.md b/README.md index e615ba1..b1687e2 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,10 @@ ![](img/008.png) +从 `1.5.0` 版本解决了缺少依赖的问题,如果你混淆时遇到报错找不到某些依赖类: + +请将依赖的 `jar` 文件都放在当前目录下的 `class-obf-lib` 目录(会自动生成) + ## 背景 `jar-analyzer` 系列曾有一款工具 `jar-obfuscator` 实现 `jar` 包的混淆 diff --git a/class-obf-lib/README.md b/class-obf-lib/README.md new file mode 100644 index 0000000..d0e5274 --- /dev/null +++ b/class-obf-lib/README.md @@ -0,0 +1,5 @@ +# README + +一些情况下混淆可能需要接触依赖库 + +请将依赖放在 `class-obf-lib` 目录中 \ No newline at end of file diff --git a/src/main/java/me/n1ar4/clazz/obfuscator/core/Runner.java b/src/main/java/me/n1ar4/clazz/obfuscator/core/Runner.java index bfd9190..160efbb 100644 --- a/src/main/java/me/n1ar4/clazz/obfuscator/core/Runner.java +++ b/src/main/java/me/n1ar4/clazz/obfuscator/core/Runner.java @@ -7,6 +7,7 @@ import me.n1ar4.clazz.obfuscator.base.ClassReference; import me.n1ar4.clazz.obfuscator.base.MethodReference; import me.n1ar4.clazz.obfuscator.config.BaseCmd; import me.n1ar4.clazz.obfuscator.config.BaseConfig; +import me.n1ar4.clazz.obfuscator.loader.CustomClassLoader; import me.n1ar4.clazz.obfuscator.transform.*; import me.n1ar4.clazz.obfuscator.utils.ASMUtil; import me.n1ar4.clazz.obfuscator.utils.ColorUtil; @@ -16,6 +17,7 @@ import me.n1ar4.log.LogManager; import me.n1ar4.log.Logger; import org.objectweb.asm.ClassReader; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -55,6 +57,19 @@ public class Runner { throw new RuntimeException(ex); } + Path dirPath = Paths.get(CustomClassLoader.LIB_DIR); + try { + Files.createDirectory(dirPath); + logger.info("已成功创建 {} 目录", CustomClassLoader.LIB_DIR); + Files.write(dirPath.resolve(Paths.get("README.md")), ("# README\n" + + "\n" + + "一些情况下混淆可能需要接触依赖库\n" + + "\n" + + "请将依赖放在 `class-obf-lib` 目录中").getBytes(StandardCharsets.UTF_8)); + } catch (Exception ignored) { + logger.warn("无法创建 {} 目录", CustomClassLoader.LIB_DIR); + } + addClass(path); logger.info("add the input class to analyze"); @@ -201,51 +216,53 @@ public class Runner { ColorUtil.blue("#################################################################")); } + CustomClassLoader loader = new CustomClassLoader(); + if (config.isEnableReflect()) { - ReflectTransformer.transform(); + ReflectTransformer.transform(loader); logger.info("run reflect transformer finish"); } if (config.isEnableDeleteCompileInfo()) { - DeleteInfoTransformer.transform(); + DeleteInfoTransformer.transform(loader); logger.info("run delete info transformer finish"); } if (config.isEnableMethodName()) { - MethodNameTransformer.transform(); + MethodNameTransformer.transform(loader); logger.info("run method name transformer finish"); } if (config.isEnableFieldName()) { - FieldNameTransformer.transform(); + FieldNameTransformer.transform(loader); logger.info("run field name transformer finish"); } if (config.isEnableParamName()) { - ParameterTransformer.transform(); + ParameterTransformer.transform(loader); logger.info("run parameter transformer finish"); } if (config.isEnableXOR()) { - XORTransformer.transform(); + XORTransformer.transform(loader); logger.info("run xor transformer finish"); } if (config.isEnableAdvanceString()) { - StringArrayTransformer.transform(); + StringArrayTransformer.transform(loader); if (config.isEnableXOR()) { - XORTransformer.transform(); + XORTransformer.transform(loader); } logger.info("run string array transformer finish"); } if (config.isEnableAES()) { - StringEncryptTransformer.transform(config.getAesKey(), config.getAesDecName()); + StringEncryptTransformer.transform(config.getAesKey(), config.getAesDecName(), loader); logger.info("run string aes transformer finish"); } if (config.isEnableJunk()) { - JunkCodeTransformer.transform(config); + JunkCodeTransformer.transform(config, loader); logger.info("run junk transformer finish"); } if (!config.isQuiet()) { diff --git a/src/main/java/me/n1ar4/clazz/obfuscator/loader/CustomClassLoader.java b/src/main/java/me/n1ar4/clazz/obfuscator/loader/CustomClassLoader.java index 823a96c..d1f45ae 100644 --- a/src/main/java/me/n1ar4/clazz/obfuscator/loader/CustomClassLoader.java +++ b/src/main/java/me/n1ar4/clazz/obfuscator/loader/CustomClassLoader.java @@ -1,24 +1,64 @@ package me.n1ar4.clazz.obfuscator.loader; -import me.n1ar4.clazz.obfuscator.Const; +import me.n1ar4.log.LogManager; +import me.n1ar4.log.Logger; -import java.nio.file.Files; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.Enumeration; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; public class CustomClassLoader extends ClassLoader { + private static final Logger logger = LogManager.getLogger(); + public static final String LIB_DIR = "class-obf-lib"; + @Override public Class findClass(String name) { - byte[] b = loadClassData(); - if (b != null) { - return defineClass(name, b, 0, b.length); + byte[] classData = loadClassData(name); + if (classData != null) { + return defineClass(name, classData, 0, classData.length); } + logger.warn("not found class: " + name); return null; } - private byte[] loadClassData() { - try { - return Files.readAllBytes(Const.TEMP_PATH); - } catch (Exception ignored) { + private byte[] loadClassData(String className) { + File libDir = new File(LIB_DIR); + if (!libDir.exists() || !libDir.isDirectory()) { return null; } + File[] jarFiles = libDir.listFiles((dir, name) -> name.endsWith(".jar")); + if (jarFiles == null) { + return null; + } + for (File jarFile : jarFiles) { + try (JarFile jar = new JarFile(jarFile)) { + Enumeration entries = jar.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + if (entry.getName().endsWith(".class")) { + String entryClassName = entry.getName().replace("/", ".") + .replace(".class", ""); + if (entryClassName.equals(className)) { + try (InputStream inputStream = jar.getInputStream(entry); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { + byte[] buffer = new byte[1024]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + return outputStream.toByteArray(); + } + } + } + } + } catch (IOException e) { + logger.error("load class error: {}", e.getMessage()); + } + } + return null; } -} +} \ No newline at end of file diff --git a/src/main/java/me/n1ar4/clazz/obfuscator/transform/DeleteInfoTransformer.java b/src/main/java/me/n1ar4/clazz/obfuscator/transform/DeleteInfoTransformer.java index 352a898..90d31ff 100644 --- a/src/main/java/me/n1ar4/clazz/obfuscator/transform/DeleteInfoTransformer.java +++ b/src/main/java/me/n1ar4/clazz/obfuscator/transform/DeleteInfoTransformer.java @@ -3,6 +3,8 @@ package me.n1ar4.clazz.obfuscator.transform; import me.n1ar4.clazz.obfuscator.Const; import me.n1ar4.clazz.obfuscator.asm.CompileInfoClassVisitor; import me.n1ar4.clazz.obfuscator.core.ObfEnv; +import me.n1ar4.clazz.obfuscator.loader.CustomClassLoader; +import me.n1ar4.clazz.obfuscator.loader.CustomClassWriter; import me.n1ar4.log.LogManager; import me.n1ar4.log.Logger; import org.objectweb.asm.ClassReader; @@ -14,7 +16,7 @@ import java.nio.file.Path; public class DeleteInfoTransformer { private static final Logger logger = LogManager.getLogger(); - public static void transform() { + public static void transform(CustomClassLoader loader) { Path classPath = Const.TEMP_PATH; if (!Files.exists(classPath)) { logger.error("class not exist: {}", classPath.toString()); @@ -22,8 +24,8 @@ public class DeleteInfoTransformer { } try { ClassReader classReader = new ClassReader(Files.readAllBytes(classPath)); - ClassWriter classWriter = new ClassWriter(classReader, - ObfEnv.config.isAsmAutoCompute() ? Const.WriterASMOptions : 0); + ClassWriter classWriter = new CustomClassWriter(classReader, + ObfEnv.config.isAsmAutoCompute() ? Const.WriterASMOptions : 0, loader); CompileInfoClassVisitor changer = new CompileInfoClassVisitor(classWriter); classReader.accept(changer, Const.ReaderASMOptions); Files.delete(classPath); diff --git a/src/main/java/me/n1ar4/clazz/obfuscator/transform/FieldNameTransformer.java b/src/main/java/me/n1ar4/clazz/obfuscator/transform/FieldNameTransformer.java index ef67f77..00dfc85 100644 --- a/src/main/java/me/n1ar4/clazz/obfuscator/transform/FieldNameTransformer.java +++ b/src/main/java/me/n1ar4/clazz/obfuscator/transform/FieldNameTransformer.java @@ -3,6 +3,8 @@ package me.n1ar4.clazz.obfuscator.transform; import me.n1ar4.clazz.obfuscator.Const; import me.n1ar4.clazz.obfuscator.asm.FieldNameClassVisitor; import me.n1ar4.clazz.obfuscator.core.ObfEnv; +import me.n1ar4.clazz.obfuscator.loader.CustomClassLoader; +import me.n1ar4.clazz.obfuscator.loader.CustomClassWriter; import me.n1ar4.log.LogManager; import me.n1ar4.log.Logger; import org.objectweb.asm.ClassReader; @@ -15,7 +17,7 @@ import java.nio.file.Path; public class FieldNameTransformer { private static final Logger logger = LogManager.getLogger(); - public static void transform() { + public static void transform(CustomClassLoader loader) { Path newClassPath = Const.TEMP_PATH; if (!Files.exists(newClassPath)) { logger.error("class not exist: {}", newClassPath.toString()); @@ -23,8 +25,8 @@ public class FieldNameTransformer { } try { ClassReader classReader = new ClassReader(Files.readAllBytes(newClassPath)); - ClassWriter classWriter = new ClassWriter(classReader, - ObfEnv.config.isAsmAutoCompute() ? Const.WriterASMOptions : 0); + ClassWriter classWriter = new CustomClassWriter(classReader, + ObfEnv.config.isAsmAutoCompute() ? Const.WriterASMOptions : 0, loader); FieldNameClassVisitor changer = new FieldNameClassVisitor(classWriter); classReader.accept(changer, Const.ReaderASMOptions); Files.delete(newClassPath); diff --git a/src/main/java/me/n1ar4/clazz/obfuscator/transform/JunkCodeTransformer.java b/src/main/java/me/n1ar4/clazz/obfuscator/transform/JunkCodeTransformer.java index 77d7656..c5f3176 100644 --- a/src/main/java/me/n1ar4/clazz/obfuscator/transform/JunkCodeTransformer.java +++ b/src/main/java/me/n1ar4/clazz/obfuscator/transform/JunkCodeTransformer.java @@ -19,7 +19,7 @@ import java.nio.file.Path; public class JunkCodeTransformer { private static final Logger logger = LogManager.getLogger(); - public static void transform(BaseConfig config) { + public static void transform(BaseConfig config, CustomClassLoader loader) { Path newClassPath = Const.TEMP_PATH; if (!Files.exists(newClassPath)) { logger.error("class not exist: {}", newClassPath.toString()); @@ -27,8 +27,6 @@ public class JunkCodeTransformer { } try { ClassReader classReader = new ClassReader(Files.readAllBytes(newClassPath)); - CustomClassLoader loader = new CustomClassLoader(); - // COMPUTE_FRAMES 需要修改 CLASSLOADER 来计算 ClassWriter classWriter = new CustomClassWriter(classReader, ObfEnv.config.isAsmAutoCompute() ? Const.WriterASMOptions : 0, loader); JunkCodeClassVisitor changer = new JunkCodeClassVisitor(classWriter, config); diff --git a/src/main/java/me/n1ar4/clazz/obfuscator/transform/MethodNameTransformer.java b/src/main/java/me/n1ar4/clazz/obfuscator/transform/MethodNameTransformer.java index 3fefaa6..148718c 100644 --- a/src/main/java/me/n1ar4/clazz/obfuscator/transform/MethodNameTransformer.java +++ b/src/main/java/me/n1ar4/clazz/obfuscator/transform/MethodNameTransformer.java @@ -3,6 +3,8 @@ package me.n1ar4.clazz.obfuscator.transform; import me.n1ar4.clazz.obfuscator.Const; import me.n1ar4.clazz.obfuscator.asm.MethodNameClassVisitor; import me.n1ar4.clazz.obfuscator.core.ObfEnv; +import me.n1ar4.clazz.obfuscator.loader.CustomClassLoader; +import me.n1ar4.clazz.obfuscator.loader.CustomClassWriter; import me.n1ar4.log.LogManager; import me.n1ar4.log.Logger; import org.objectweb.asm.ClassReader; @@ -15,7 +17,7 @@ import java.nio.file.Path; public class MethodNameTransformer { private static final Logger logger = LogManager.getLogger(); - public static void transform() { + public static void transform(CustomClassLoader loader) { Path newClassPath = Const.TEMP_PATH; if (!Files.exists(newClassPath)) { logger.error("class not exist: {}", newClassPath.toString()); @@ -23,8 +25,8 @@ public class MethodNameTransformer { } try { ClassReader classReader = new ClassReader(Files.readAllBytes(newClassPath)); - ClassWriter classWriter = new ClassWriter(classReader, - ObfEnv.config.isAsmAutoCompute() ? Const.WriterASMOptions : 0); + ClassWriter classWriter = new CustomClassWriter(classReader, + ObfEnv.config.isAsmAutoCompute() ? Const.WriterASMOptions : 0, loader); MethodNameClassVisitor changer = new MethodNameClassVisitor(classWriter); classReader.accept(changer, Const.ReaderASMOptions); Files.delete(newClassPath); diff --git a/src/main/java/me/n1ar4/clazz/obfuscator/transform/ParameterTransformer.java b/src/main/java/me/n1ar4/clazz/obfuscator/transform/ParameterTransformer.java index 21b483c..c308938 100644 --- a/src/main/java/me/n1ar4/clazz/obfuscator/transform/ParameterTransformer.java +++ b/src/main/java/me/n1ar4/clazz/obfuscator/transform/ParameterTransformer.java @@ -3,6 +3,8 @@ package me.n1ar4.clazz.obfuscator.transform; import me.n1ar4.clazz.obfuscator.Const; import me.n1ar4.clazz.obfuscator.asm.ParameterClassVisitor; import me.n1ar4.clazz.obfuscator.core.ObfEnv; +import me.n1ar4.clazz.obfuscator.loader.CustomClassLoader; +import me.n1ar4.clazz.obfuscator.loader.CustomClassWriter; import me.n1ar4.log.LogManager; import me.n1ar4.log.Logger; import org.objectweb.asm.ClassReader; @@ -15,7 +17,7 @@ import java.nio.file.Path; public class ParameterTransformer { private static final Logger logger = LogManager.getLogger(); - public static void transform() { + public static void transform(CustomClassLoader loader) { Path newClassPath = Const.TEMP_PATH; if (!Files.exists(newClassPath)) { logger.error("class not exist: {}", newClassPath.toString()); @@ -23,8 +25,8 @@ public class ParameterTransformer { } try { ClassReader classReader = new ClassReader(Files.readAllBytes(newClassPath)); - ClassWriter classWriter = new ClassWriter(classReader, - ObfEnv.config.isAsmAutoCompute() ? Const.WriterASMOptions : 0); + ClassWriter classWriter = new CustomClassWriter(classReader, + ObfEnv.config.isAsmAutoCompute() ? Const.WriterASMOptions : 0, loader); ParameterClassVisitor changer = new ParameterClassVisitor(classWriter); classReader.accept(changer, Const.ReaderASMOptions); Files.delete(newClassPath); diff --git a/src/main/java/me/n1ar4/clazz/obfuscator/transform/ReflectTransformer.java b/src/main/java/me/n1ar4/clazz/obfuscator/transform/ReflectTransformer.java index d79ceb3..4600243 100644 --- a/src/main/java/me/n1ar4/clazz/obfuscator/transform/ReflectTransformer.java +++ b/src/main/java/me/n1ar4/clazz/obfuscator/transform/ReflectTransformer.java @@ -3,6 +3,8 @@ package me.n1ar4.clazz.obfuscator.transform; import me.n1ar4.clazz.obfuscator.Const; import me.n1ar4.clazz.obfuscator.asm.ReflectClassVisitor; import me.n1ar4.clazz.obfuscator.core.ObfEnv; +import me.n1ar4.clazz.obfuscator.loader.CustomClassLoader; +import me.n1ar4.clazz.obfuscator.loader.CustomClassWriter; import me.n1ar4.log.LogManager; import me.n1ar4.log.Logger; import org.objectweb.asm.ClassReader; @@ -14,7 +16,7 @@ import java.nio.file.Path; public class ReflectTransformer { private static final Logger logger = LogManager.getLogger(); - public static void transform() { + public static void transform(CustomClassLoader loader) { Path classPath = Const.TEMP_PATH; if (!Files.exists(classPath)) { logger.error("class not exist: {}", classPath.toString()); @@ -22,8 +24,8 @@ public class ReflectTransformer { } try { ClassReader classReader = new ClassReader(Files.readAllBytes(classPath)); - ClassWriter classWriter = new ClassWriter(classReader, - ObfEnv.config.isAsmAutoCompute() ? Const.WriterASMOptions : 0); + ClassWriter classWriter = new CustomClassWriter(classReader, + ObfEnv.config.isAsmAutoCompute() ? Const.WriterASMOptions : 0, loader); ReflectClassVisitor changer = new ReflectClassVisitor(classWriter); classReader.accept(changer, Const.ReaderASMOptions); Files.delete(classPath); diff --git a/src/main/java/me/n1ar4/clazz/obfuscator/transform/StringArrayTransformer.java b/src/main/java/me/n1ar4/clazz/obfuscator/transform/StringArrayTransformer.java index 188621a..31f91ab 100644 --- a/src/main/java/me/n1ar4/clazz/obfuscator/transform/StringArrayTransformer.java +++ b/src/main/java/me/n1ar4/clazz/obfuscator/transform/StringArrayTransformer.java @@ -3,6 +3,8 @@ package me.n1ar4.clazz.obfuscator.transform; import me.n1ar4.clazz.obfuscator.Const; import me.n1ar4.clazz.obfuscator.asm.StringArrayClassVisitor; import me.n1ar4.clazz.obfuscator.core.ObfEnv; +import me.n1ar4.clazz.obfuscator.loader.CustomClassLoader; +import me.n1ar4.clazz.obfuscator.loader.CustomClassWriter; import me.n1ar4.log.LogManager; import me.n1ar4.log.Logger; import org.objectweb.asm.ClassReader; @@ -16,7 +18,7 @@ public class StringArrayTransformer { private static final Logger logger = LogManager.getLogger(); public static int INDEX = 0; - public static void transform() { + public static void transform(CustomClassLoader loader) { Path newClassPath = Const.TEMP_PATH; if (!Files.exists(newClassPath)) { logger.error("class not exist: {}", newClassPath.toString()); @@ -25,8 +27,8 @@ public class StringArrayTransformer { try { INDEX = 0; ClassReader classReader = new ClassReader(Files.readAllBytes(newClassPath)); - ClassWriter classWriter = new ClassWriter(classReader, - ObfEnv.config.isAsmAutoCompute() ? Const.WriterASMOptions : 0); + ClassWriter classWriter = new CustomClassWriter(classReader, + ObfEnv.config.isAsmAutoCompute() ? Const.WriterASMOptions : 0, loader); StringArrayClassVisitor changer = new StringArrayClassVisitor(classWriter); classReader.accept(changer, Const.ReaderASMOptions); Files.delete(newClassPath); diff --git a/src/main/java/me/n1ar4/clazz/obfuscator/transform/StringEncryptTransformer.java b/src/main/java/me/n1ar4/clazz/obfuscator/transform/StringEncryptTransformer.java index 1622964..4060b23 100644 --- a/src/main/java/me/n1ar4/clazz/obfuscator/transform/StringEncryptTransformer.java +++ b/src/main/java/me/n1ar4/clazz/obfuscator/transform/StringEncryptTransformer.java @@ -3,6 +3,8 @@ package me.n1ar4.clazz.obfuscator.transform; import me.n1ar4.clazz.obfuscator.Const; import me.n1ar4.clazz.obfuscator.asm.StringEncryptClassVisitor; import me.n1ar4.clazz.obfuscator.core.ObfEnv; +import me.n1ar4.clazz.obfuscator.loader.CustomClassLoader; +import me.n1ar4.clazz.obfuscator.loader.CustomClassWriter; import me.n1ar4.log.LogManager; import me.n1ar4.log.Logger; import org.objectweb.asm.ClassReader; @@ -15,7 +17,7 @@ import java.nio.file.Path; public class StringEncryptTransformer { private static final Logger logger = LogManager.getLogger(); - public static void transform(String aesKey, String aesDecName) { + public static void transform(String aesKey, String aesDecName, CustomClassLoader loader) { Path newClassPath = Const.TEMP_PATH; if (!Files.exists(newClassPath)) { logger.error("class not exist: {}", newClassPath.toString()); @@ -23,13 +25,14 @@ public class StringEncryptTransformer { } try { ClassReader classReader = new ClassReader(Files.readAllBytes(newClassPath)); - ClassWriter classWriter = new ClassWriter(classReader, - ObfEnv.config.isAsmAutoCompute() ? Const.WriterASMOptions : 0); + ClassWriter classWriter = new CustomClassWriter(classReader, + ObfEnv.config.isAsmAutoCompute() ? Const.WriterASMOptions : 0, loader); StringEncryptClassVisitor changer = new StringEncryptClassVisitor(classWriter, aesKey, aesDecName); classReader.accept(changer, Const.ReaderASMOptions); Files.delete(newClassPath); Files.write(newClassPath, classWriter.toByteArray()); } catch (Exception ex) { + ex.printStackTrace(); logger.error("transform error: {}", ex.toString()); } } diff --git a/src/main/java/me/n1ar4/clazz/obfuscator/transform/XORTransformer.java b/src/main/java/me/n1ar4/clazz/obfuscator/transform/XORTransformer.java index aeac0d4..bf492a9 100644 --- a/src/main/java/me/n1ar4/clazz/obfuscator/transform/XORTransformer.java +++ b/src/main/java/me/n1ar4/clazz/obfuscator/transform/XORTransformer.java @@ -3,6 +3,8 @@ package me.n1ar4.clazz.obfuscator.transform; import me.n1ar4.clazz.obfuscator.Const; import me.n1ar4.clazz.obfuscator.asm.IntToXorClassVisitor; import me.n1ar4.clazz.obfuscator.core.ObfEnv; +import me.n1ar4.clazz.obfuscator.loader.CustomClassLoader; +import me.n1ar4.clazz.obfuscator.loader.CustomClassWriter; import me.n1ar4.log.LogManager; import me.n1ar4.log.Logger; import org.objectweb.asm.ClassReader; @@ -15,7 +17,7 @@ import java.nio.file.Path; public class XORTransformer { private static final Logger logger = LogManager.getLogger(); - public static void transform() { + public static void transform(ClassLoader loader) { Path newClassPath = Const.TEMP_PATH; if (!Files.exists(newClassPath)) { logger.error("class not exist: {}", newClassPath.toString()); @@ -23,8 +25,8 @@ public class XORTransformer { } try { ClassReader classReader = new ClassReader(Files.readAllBytes(newClassPath)); - ClassWriter classWriter = new ClassWriter(classReader, - ObfEnv.config.isAsmAutoCompute() ? Const.WriterASMOptions : 0); + ClassWriter classWriter = new CustomClassWriter(classReader, + ObfEnv.config.isAsmAutoCompute() ? Const.WriterASMOptions : 0, loader); IntToXorClassVisitor changer = new IntToXorClassVisitor(classWriter); classReader.accept(changer, Const.ReaderASMOptions); Files.delete(newClassPath); diff --git a/src/main/java/me/n1ar4/clazz/obfuscator/utils/AESUtil.java b/src/main/java/me/n1ar4/clazz/obfuscator/utils/AESUtil.java index 37d2154..5c92d83 100644 --- a/src/main/java/me/n1ar4/clazz/obfuscator/utils/AESUtil.java +++ b/src/main/java/me/n1ar4/clazz/obfuscator/utils/AESUtil.java @@ -1,18 +1,30 @@ package me.n1ar4.clazz.obfuscator.utils; -import org.objectweb.asm.ClassVisitor; -import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.*; import static org.objectweb.asm.Opcodes.*; public class AESUtil { public static void addAesDecodeCode(ClassVisitor cv, String aesDecName) { - MethodVisitor methodVisitor = cv.visitMethod( - ACC_PUBLIC | ACC_STATIC, - aesDecName, - "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", - null, new String[]{"java/lang/Exception"}); + MethodVisitor methodVisitor = cv.visitMethod(ACC_PUBLIC | ACC_STATIC, aesDecName, + "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", null, null); methodVisitor.visitCode(); + Label label0 = new Label(); + Label label1 = new Label(); + Label label2 = new Label(); + methodVisitor.visitTryCatchBlock(label0, label1, label2, "java/lang/Exception"); + Label label3 = new Label(); + Label label4 = new Label(); + Label label5 = new Label(); + methodVisitor.visitTryCatchBlock(label3, label4, label5, "java/lang/Exception"); + Label label6 = new Label(); + Label label7 = new Label(); + Label label8 = new Label(); + methodVisitor.visitTryCatchBlock(label6, label7, label8, "java/lang/Exception"); + Label label9 = new Label(); + Label label10 = new Label(); + methodVisitor.visitTryCatchBlock(label9, label10, label8, "java/lang/Exception"); + methodVisitor.visitLabel(label6); methodVisitor.visitTypeInsn(NEW, "java/lang/StringBuilder"); methodVisitor.visitInsn(DUP); methodVisitor.visitVarInsn(ALOAD, 1); @@ -41,18 +53,100 @@ public class AESUtil { methodVisitor.visitInsn(ICONST_2); methodVisitor.visitVarInsn(ALOAD, 2); methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "javax/crypto/Cipher", "init", "(ILjava/security/Key;)V", false); - methodVisitor.visitVarInsn(ALOAD, 3); - methodVisitor.visitMethodInsn(INVOKESTATIC, "java/util/Base64", "getDecoder", "()Ljava/util/Base64$Decoder;", false); - methodVisitor.visitVarInsn(ALOAD, 0); - methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/util/Base64$Decoder", "decode", "(Ljava/lang/String;)[B", false); - methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "javax/crypto/Cipher", "doFinal", "([B)[B", false); + methodVisitor.visitInsn(ACONST_NULL); + methodVisitor.visitVarInsn(ASTORE, 5); + methodVisitor.visitLabel(label0); + methodVisitor.visitLdcInsn("java.util.Base64"); + methodVisitor.visitMethodInsn(INVOKESTATIC, "java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;", false); methodVisitor.visitVarInsn(ASTORE, 4); + methodVisitor.visitVarInsn(ALOAD, 4); + methodVisitor.visitLdcInsn("getDecoder"); + methodVisitor.visitInsn(ACONST_NULL); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;", false); + methodVisitor.visitVarInsn(ALOAD, 4); + methodVisitor.visitInsn(ACONST_NULL); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/reflect/Method", "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;", false); + methodVisitor.visitVarInsn(ASTORE, 6); + methodVisitor.visitVarInsn(ALOAD, 6); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false); + methodVisitor.visitLdcInsn("decode"); + methodVisitor.visitInsn(ICONST_1); + methodVisitor.visitTypeInsn(ANEWARRAY, "java/lang/Class"); + methodVisitor.visitInsn(DUP); + methodVisitor.visitInsn(ICONST_0); + methodVisitor.visitLdcInsn(Type.getType(String.class)); + methodVisitor.visitInsn(AASTORE); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;", false); + methodVisitor.visitVarInsn(ALOAD, 6); + methodVisitor.visitInsn(ICONST_1); + methodVisitor.visitTypeInsn(ANEWARRAY, "java/lang/Object"); + methodVisitor.visitInsn(DUP); + methodVisitor.visitInsn(ICONST_0); + methodVisitor.visitVarInsn(ALOAD, 0); + methodVisitor.visitInsn(AASTORE); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/reflect/Method", "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;", false); + methodVisitor.visitTypeInsn(CHECKCAST, "[B"); + methodVisitor.visitTypeInsn(CHECKCAST, "[B"); + methodVisitor.visitVarInsn(ASTORE, 5); + methodVisitor.visitLabel(label1); + methodVisitor.visitJumpInsn(GOTO, label9); + methodVisitor.visitLabel(label2); + methodVisitor.visitFrame(Opcodes.F_FULL, 6, new Object[]{"java/lang/String", "java/lang/String", "javax/crypto/spec/SecretKeySpec", "javax/crypto/Cipher", Opcodes.TOP, "[B"}, 1, new Object[]{"java/lang/Exception"}); + methodVisitor.visitVarInsn(ASTORE, 6); + methodVisitor.visitLabel(label3); + methodVisitor.visitLdcInsn("sun.misc.BASE64Decoder"); + methodVisitor.visitMethodInsn(INVOKESTATIC, "java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;", false); + methodVisitor.visitVarInsn(ASTORE, 4); + methodVisitor.visitVarInsn(ALOAD, 4); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "newInstance", "()Ljava/lang/Object;", false); + methodVisitor.visitVarInsn(ASTORE, 7); + methodVisitor.visitVarInsn(ALOAD, 7); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false); + methodVisitor.visitLdcInsn("decodeBuffer"); + methodVisitor.visitInsn(ICONST_1); + methodVisitor.visitTypeInsn(ANEWARRAY, "java/lang/Class"); + methodVisitor.visitInsn(DUP); + methodVisitor.visitInsn(ICONST_0); + methodVisitor.visitLdcInsn(Type.getType(String.class)); + methodVisitor.visitInsn(AASTORE); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;", false); + methodVisitor.visitVarInsn(ALOAD, 7); + methodVisitor.visitInsn(ICONST_1); + methodVisitor.visitTypeInsn(ANEWARRAY, "java/lang/Object"); + methodVisitor.visitInsn(DUP); + methodVisitor.visitInsn(ICONST_0); + methodVisitor.visitVarInsn(ALOAD, 0); + methodVisitor.visitInsn(AASTORE); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/reflect/Method", "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;", false); + methodVisitor.visitTypeInsn(CHECKCAST, "[B"); + methodVisitor.visitTypeInsn(CHECKCAST, "[B"); + methodVisitor.visitVarInsn(ASTORE, 5); + methodVisitor.visitLabel(label4); + methodVisitor.visitJumpInsn(GOTO, label9); + methodVisitor.visitLabel(label5); + methodVisitor.visitFrame(Opcodes.F_FULL, 7, new Object[]{"java/lang/String", "java/lang/String", "javax/crypto/spec/SecretKeySpec", "javax/crypto/Cipher", Opcodes.TOP, "[B", "java/lang/Exception"}, 1, new Object[]{"java/lang/Exception"}); + methodVisitor.visitVarInsn(ASTORE, 7); + methodVisitor.visitLdcInsn(""); + methodVisitor.visitLabel(label7); + methodVisitor.visitInsn(ARETURN); + methodVisitor.visitLabel(label9); + methodVisitor.visitFrame(Opcodes.F_FULL, 6, new Object[]{"java/lang/String", "java/lang/String", "javax/crypto/spec/SecretKeySpec", "javax/crypto/Cipher", "java/lang/Class", "[B"}, 0, new Object[]{}); + methodVisitor.visitVarInsn(ALOAD, 3); + methodVisitor.visitVarInsn(ALOAD, 5); + methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "javax/crypto/Cipher", "doFinal", "([B)[B", false); + methodVisitor.visitVarInsn(ASTORE, 6); methodVisitor.visitTypeInsn(NEW, "java/lang/String"); methodVisitor.visitInsn(DUP); - methodVisitor.visitVarInsn(ALOAD, 4); + methodVisitor.visitVarInsn(ALOAD, 6); methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/String", "", "([B)V", false); + methodVisitor.visitLabel(label10); methodVisitor.visitInsn(ARETURN); - methodVisitor.visitMaxs(4, 5); + methodVisitor.visitLabel(label8); + methodVisitor.visitFrame(Opcodes.F_FULL, 2, new Object[]{"java/lang/String", "java/lang/String"}, 1, new Object[]{"java/lang/Exception"}); + methodVisitor.visitVarInsn(ASTORE, 2); + methodVisitor.visitLdcInsn(""); + methodVisitor.visitInsn(ARETURN); + methodVisitor.visitMaxs(6, 8); methodVisitor.visitEnd(); } } diff --git a/src/main/java/me/n1ar4/templates/AESTemplates.java b/src/main/java/me/n1ar4/templates/AESTemplates.java index b03f51f..f4318ed 100644 --- a/src/main/java/me/n1ar4/templates/AESTemplates.java +++ b/src/main/java/me/n1ar4/templates/AESTemplates.java @@ -17,14 +17,35 @@ public class AESTemplates { return new StringBuilder(base).reverse().toString(); } - public static String decrypt(String encryptedData, String key) throws Exception { - key = new StringBuilder(key).reverse().toString(); - encryptedData = new StringBuilder(encryptedData).reverse().toString(); - SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), "AES"); - Cipher cipher = Cipher.getInstance("AES"); - cipher.init(Cipher.DECRYPT_MODE, secretKey); - byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedData)); - return new String(decryptedBytes); + public static String decrypt(String encryptedData, String key) { + try { + key = new StringBuilder(key).reverse().toString(); + encryptedData = new StringBuilder(encryptedData).reverse().toString(); + SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), "AES"); + Cipher cipher = Cipher.getInstance("AES"); + cipher.init(Cipher.DECRYPT_MODE, secretKey); + Class base64; + byte[] value = null; + try { + base64 = Class.forName("java.util.Base64"); + Object decoder = base64.getMethod("getDecoder", null).invoke(base64, null); + value = (byte[]) decoder.getClass().getMethod("decode", + new Class[]{String.class}).invoke(decoder, new Object[]{encryptedData}); + } catch (Exception e) { + try { + base64 = Class.forName("sun.misc.BASE64Decoder"); + Object decoder = base64.newInstance(); + value = (byte[]) decoder.getClass().getMethod("decodeBuffer", + new Class[]{String.class}).invoke(decoder, new Object[]{encryptedData}); + } catch (Exception ex) { + return ""; + } + } + byte[] decryptedBytes = cipher.doFinal(value); + return new String(decryptedBytes); + } catch (Exception ignored) { + return ""; + } } public static void main(String[] args) {