[功能] 支持 enableInvokeDynamic 配置使 IDEA 报错
This commit is contained in:
@@ -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>
|
||||
```
|
||||
|
||||
|
||||
13
README.md
13
README.md
@@ -23,6 +23,12 @@
|
||||
|
||||

|
||||
|
||||
从 `1.9.0` 版本后新增简单的 `AI` 对抗和 `IDEA` 报错对抗
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
本项目已深度集成到 `web-chains` 项目中 (https://github.com/vulhub/java-chains)
|
||||
|
||||

|
||||
@@ -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
BIN
img/013.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.8 KiB |
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,4 +95,9 @@ badAnnoTextFile: bad-anno.txt
|
||||
# 可能的 AI 反编译对抗
|
||||
# 插入对抗 PROMPT 使得 AI 分析混淆代码可能失效
|
||||
# 测试功能 实际发现很多大模型无法被打断
|
||||
antiAI: false
|
||||
antiAI: false
|
||||
|
||||
# 是否启用 InvokeDynamic 混淆
|
||||
# 将普通的 invoke 指令转换为 invokedynamic 指令
|
||||
# 注意:只支持 STATIC 方法 且未经过完善的测试 可能不够稳定
|
||||
enableInvokeDynamic: false
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user