[功能] 支持 enableInvokeDynamic 配置使 IDEA 报错

This commit is contained in:
4ra1n
2025-10-14 21:06:51 +08:00
parent 3566a77f55
commit 86b405cfe6
10 changed files with 238 additions and 5 deletions

View File

@@ -1,10 +1,11 @@
# CHANGELOG
## 1.8.1
## 1.9.0
更新日志:
- [功能] 新增配置 `antiAI` 允许简单的对抗 `AI` 分析
- [功能] 支持 `enableInvokeDynamic` 配置使 `IDEA` 报错
- [功能] 支持 `antiAI` 允许简单的对抗 `AI` 分析
感谢以下用户的贡献:
@@ -16,7 +17,7 @@
<dependency>
<groupId>io.github.4ra1n</groupId>
<artifactId>class-obf</artifactId>
<version>1.8.1</version>
<version>1.9.0</version>
</dependency>
```

View File

@@ -23,6 +23,12 @@
![](img/011.png)
`1.9.0` 版本后新增简单的 `AI` 对抗和 `IDEA` 报错对抗
![](img/012.png)
![](img/013.png)
本项目已深度集成到 `web-chains` 项目中 (https://github.com/vulhub/java-chains)
![](img/006.png)
@@ -46,7 +52,7 @@
`class-obf` 相比商业化混淆器:
- 混淆强度不如商业化混淆器:弱于 `zkm` 混淆,接近 `allatori` 混淆
- 混淆强度接近商业化混淆器:弱于 `zkm` 混淆,接近 `allatori` 混淆
- 保持更新,学习先进商业混淆器的思路,逐步完善
- 完全开源,有功能问题可以提 `PR` 贡献
- 配置简单,配置的参数 **远少于** 商业化混淆器,上手非常容易
@@ -211,6 +217,11 @@ badAnnoTextFile: bad-anno.txt
# 插入对抗 PROMPT 使得 AI 分析混淆代码可能失效
# 测试功能 实际发现很多大模型无法被打断
antiAI: false
# 是否启用 InvokeDynamic 混淆
# 将普通的 invoke 指令转换为 invokedynamic 指令
# 注意:只支持 STATIC 方法 且未经过完善的测试 可能不够稳定
enableInvokeDynamic: false
```
## 如何测试

BIN
img/013.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

@@ -0,0 +1,110 @@
package me.n1ar4.clazz.obfuscator.asm;
import org.objectweb.asm.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
public class InvokeDynamicClassVisitor extends ClassVisitor {
private static final String BOOTSTRAP_METHOD_NAME = "bootstrap";
private static final String BOOTSTRAP_METHOD_DESC = "(Ljava/lang/invoke/MethodHandles$Lookup;" +
"Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/invoke/CallSite;";
private final Random random = new Random();
private String className;
private boolean hasBootstrapMethod = false;
public InvokeDynamicClassVisitor(ClassVisitor classVisitor) {
super(Opcodes.ASM9, classVisitor);
}
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
this.className = name;
super.visit(version, access, name, signature, superName, interfaces);
}
@Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
return new InvokeDynamicMethodAdapter(mv);
}
@Override
public void visitEnd() {
if (hasBootstrapMethod) {
addBootstrapMethod();
}
super.visitEnd();
}
private void addBootstrapMethod() {
MethodVisitor mv = super.visitMethod(
Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC,
BOOTSTRAP_METHOD_NAME,
BOOTSTRAP_METHOD_DESC,
null,
new String[]{"java/lang/Exception"}
);
mv.visitCode();
mv.visitTypeInsn(Opcodes.NEW, "java/lang/invoke/ConstantCallSite");
mv.visitInsn(Opcodes.DUP);
mv.visitVarInsn(Opcodes.ALOAD, 3);
mv.visitMethodInsn(Opcodes.INVOKESTATIC,
"java/lang/Class",
"forName",
"(Ljava/lang/String;)Ljava/lang/Class;",
false);
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitInsn(Opcodes.SWAP);
mv.visitVarInsn(Opcodes.ALOAD, 4);
mv.visitVarInsn(Opcodes.ALOAD, 2);
mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
"java/lang/invoke/MethodHandles$Lookup",
"findStatic",
"(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;",
false);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL,
"java/lang/invoke/ConstantCallSite",
"<init>",
"(Ljava/lang/invoke/MethodHandle;)V",
false);
mv.visitInsn(Opcodes.ARETURN);
mv.visitMaxs(6, 5);
mv.visitEnd();
}
private class InvokeDynamicMethodAdapter extends MethodVisitor {
public InvokeDynamicMethodAdapter(MethodVisitor methodVisitor) {
super(Opcodes.ASM9, methodVisitor);
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String descriptor, boolean isInterface) {
// 只处理静态方法调用
if (opcode == Opcodes.INVOKESTATIC && shouldObfuscate(owner, name)) {
hasBootstrapMethod = true;
Handle bootstrapHandle = new Handle(
Opcodes.H_INVOKESTATIC,
className,
BOOTSTRAP_METHOD_NAME,
BOOTSTRAP_METHOD_DESC,
false
);
super.visitInvokeDynamicInsn(
name,
descriptor,
bootstrapHandle,
owner.replace('/', '.'),
name
);
} else {
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
}
}
private boolean shouldObfuscate(String owner, String name) {
if (owner.startsWith("java/") || owner.startsWith("javax/") ||
owner.startsWith("sun/") || name.equals("<init>") || name.equals("<clinit>")) {
return false;
}
return !name.equals("main");
}
}
}

View File

@@ -49,6 +49,8 @@ public class BaseConfig {
private boolean antiAI;
private boolean enableInvokeDynamic;
/**
* 如果配置没问题可以启动就返回 true
*
@@ -124,6 +126,8 @@ public class BaseConfig {
config.setEnableEvilString(false);
// 默认关闭 anti AI
config.setAntiAI(false);
// 默认关闭 invoke dynamic
config.setEnableInvokeDynamic(false);
return config;
}
@@ -429,4 +433,12 @@ public class BaseConfig {
public void setAntiAI(boolean antiAI) {
this.antiAI = antiAI;
}
public boolean isEnableInvokeDynamic() {
return enableInvokeDynamic;
}
public void setEnableInvokeDynamic(boolean enableInvokeDynamic) {
this.enableInvokeDynamic = enableInvokeDynamic;
}
}

View File

@@ -271,6 +271,11 @@ public class Runner {
logger.info("run anti ai transformer finish");
}
if (config.isEnableInvokeDynamic()) {
InvokeDynamicTransformer.transform(loader);
logger.info("run invoke dynamic transformer finish");
}
if (config.isEnableDeleteCompileInfo()) {
DeleteInfoTransformer.transform(loader);
logger.info("run delete info transformer finish");

View File

@@ -0,0 +1,37 @@
package me.n1ar4.clazz.obfuscator.transform;
import me.n1ar4.clazz.obfuscator.Const;
import me.n1ar4.clazz.obfuscator.asm.AntiPromptClassVisitor;
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;
import org.objectweb.asm.ClassWriter;
import java.nio.file.Files;
import java.nio.file.Path;
public class AntiPromptTransformer {
private static final Logger logger = LogManager.getLogger();
public static void transform(CustomClassLoader loader) {
Path classPath = Const.TEMP_PATH;
if (!Files.exists(classPath)) {
logger.error("class not exist: {}", classPath.toString());
return;
}
try {
ClassReader classReader = new ClassReader(Files.readAllBytes(classPath));
ClassWriter classWriter = new CustomClassWriter(classReader,
ObfEnv.config.isAsmAutoCompute() ? Const.WriterASMOptions : 0, loader);
AntiPromptClassVisitor changer = new AntiPromptClassVisitor(classWriter);
classReader.accept(changer, Const.ReaderASMOptions);
Files.delete(classPath);
Files.write(classPath, classWriter.toByteArray());
} catch (Exception ex) {
logger.error("transform error: {}", ex.toString());
}
}
}

View File

@@ -0,0 +1,47 @@
package me.n1ar4.clazz.obfuscator.transform;
import me.n1ar4.clazz.obfuscator.Const;
import me.n1ar4.clazz.obfuscator.asm.InvokeDynamicClassVisitor;
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;
import org.objectweb.asm.ClassWriter;
import java.nio.file.Files;
import java.nio.file.Path;
/**
* InvokeDynamic混淆转换器
* 将普通的invoke指令转换为invokedynamic指令增加静态分析难度
*/
public class InvokeDynamicTransformer {
private static final Logger logger = LogManager.getLogger();
public static void transform(CustomClassLoader loader) {
Path classPath = Const.TEMP_PATH;
if (!Files.exists(classPath)) {
logger.error("class not exist: {}", classPath.toString());
return;
}
try {
logger.info("applying invoke dynamic obfuscation");
ClassReader classReader = new ClassReader(Files.readAllBytes(classPath));
ClassWriter classWriter = new CustomClassWriter(classReader,
ObfEnv.config.isAsmAutoCompute() ? Const.WriterASMOptions : 0, loader);
InvokeDynamicClassVisitor changer = new InvokeDynamicClassVisitor(classWriter);
classReader.accept(changer, Const.ReaderASMOptions);
Files.delete(classPath);
Files.write(classPath, classWriter.toByteArray());
logger.info("invoke dynamic obfuscation completed");
} catch (Exception ex) {
logger.error("invoke dynamic transform error: {}", ex.toString());
}
}
}

View File

@@ -95,4 +95,9 @@ badAnnoTextFile: bad-anno.txt
# 可能的 AI 反编译对抗
# 插入对抗 PROMPT 使得 AI 分析混淆代码可能失效
# 测试功能 实际发现很多大模型无法被打断
antiAI: false
antiAI: false
# 是否启用 InvokeDynamic 混淆
# 将普通的 invoke 指令转换为 invokedynamic 指令
# 注意:只支持 STATIC 方法 且未经过完善的测试 可能不够稳定
enableInvokeDynamic: false

View File

@@ -13,6 +13,10 @@ public class Test {
System.out.println("Counter: " + counter);
}
public static void test(){
System.out.println("static");
}
public void testConditionalBranches() {
if (flag) {
System.out.println("Flag is true");
@@ -119,6 +123,7 @@ public class Test {
}
public static void main(String[] args) {
test();
System.out.println("=== 开始测试控制流混淆和花指令 ===");
Test test = new Test();