Skip to content

Commit ebf72f5

Browse files
committed
Bytecode utils
1 parent 2ed36d4 commit ebf72f5

11 files changed

+1135
-223
lines changed

BYTECODE_UTILS_GUIDE.md

Lines changed: 249 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,249 @@
1+
# Bytecode Utilities Guide
2+
3+
This project now includes comprehensive bytecode manipulation utilities that can be used independently or as part of the obfuscation framework.
4+
5+
## Core Utilities
6+
7+
### BytecodeUtils
8+
Provides static utility methods for common bytecode operations:
9+
10+
```java
11+
// Push integer constants efficiently
12+
BytecodeUtils.pushInteger(mv, 42);
13+
14+
// Check instruction types
15+
if (BytecodeUtils.isConstantInstruction(opcode)) { ... }
16+
if (BytecodeUtils.isIntegerConstant(opcode)) { ... }
17+
18+
// Get constant values
19+
int value = BytecodeUtils.getConstantValue(Opcodes.ICONST_1); // returns 1
20+
21+
// Check if methods/fields should be skipped
22+
if (BytecodeUtils.isMethodSkippable("main", access)) { ... }
23+
```
24+
25+
### InstructionObfuscator
26+
Handles instruction-level obfuscation with multiple strategies:
27+
28+
```java
29+
Random random = new Random();
30+
InstructionObfuscator obfuscator = new InstructionObfuscator(random,
31+
InstructionObfuscator.ObfuscationStrategy.ARITHMETIC);
32+
33+
// Obfuscate a constant instruction
34+
obfuscator.obfuscateConstant(mv, Opcodes.ICONST_1);
35+
36+
// Obfuscate specific integer values
37+
obfuscator.obfuscateIntegerValue(mv, 42);
38+
```
39+
40+
Available strategies:
41+
- `ARITHMETIC`: Uses arithmetic operations (add, subtract, multiply, divide, xor)
42+
- `BITWISE`: Uses bitwise operations (and, or, shift)
43+
- `STACK_MANIPULATION`: Uses stack manipulation techniques
44+
- `MIXED`: Randomly combines all strategies
45+
46+
## Abstract Base Classes
47+
48+
### AbstractMethodVisitor
49+
Extends ASM's MethodVisitor with common functionality:
50+
51+
```java
52+
public class MyMethodVisitor extends AbstractMethodVisitor {
53+
public MyMethodVisitor(MethodVisitor mv, ObfuscationContext context,
54+
String className, String methodName, String methodDescriptor) {
55+
super(mv, context, className, methodName, methodDescriptor);
56+
}
57+
58+
@Override
59+
public void visitInsn(int opcode) {
60+
if (shouldProcessWithProbability(0.1f)) {
61+
emitGarbage(); // Add random garbage instructions
62+
logTransformation("Added garbage instruction");
63+
}
64+
super.visitInsn(opcode);
65+
}
66+
67+
@Override
68+
protected String getTransformerName() {
69+
return "MyTransformer";
70+
}
71+
}
72+
```
73+
74+
### AbstractClassVisitor
75+
Extends ASM's ClassVisitor with common patterns:
76+
77+
```java
78+
public class MyClassVisitor extends AbstractClassVisitor {
79+
public MyClassVisitor(ClassVisitor cv, ObfuscationContext context) {
80+
super(cv, context);
81+
}
82+
83+
@Override
84+
protected MethodVisitor createMethodVisitor(MethodVisitor mv, int access, String name,
85+
String descriptor, String signature, String[] exceptions) {
86+
return new MyMethodVisitor(mv, context, currentClassName, name, descriptor);
87+
}
88+
89+
@Override
90+
protected String getTransformerName() {
91+
return "MyTransformer";
92+
}
93+
}
94+
```
95+
96+
## Transformation Factory
97+
98+
The `BytecodeTransformationFactory` provides pre-built transformers:
99+
100+
### Quick Transformers
101+
```java
102+
// Create a constant obfuscator with 15% probability
103+
ClassVisitor constantObfuscator = BytecodeTransformationFactory.createConstantObfuscator(
104+
writer, context, 0.15f);
105+
106+
// Create a garbage injector with 5% probability
107+
ClassVisitor garbageInjector = BytecodeTransformationFactory.createGarbageInjector(
108+
writer, context, 0.05f);
109+
110+
// Create an instruction obfuscator with mixed strategy
111+
ClassVisitor instructionObfuscator = BytecodeTransformationFactory.createInstructionObfuscator(
112+
writer, context, InstructionObfuscator.ObfuscationStrategy.MIXED, 0.1f);
113+
```
114+
115+
### Custom Transformers
116+
```java
117+
ClassVisitor customTransformer = BytecodeTransformationFactory.createCustomTransformer(
118+
writer, context, "MyTransformer",
119+
builder -> builder
120+
.withInstructionObfuscation(InstructionObfuscator.ObfuscationStrategy.ARITHMETIC, 0.2f)
121+
.withGarbageInjection(0.1f)
122+
.build()
123+
);
124+
```
125+
126+
## Creating Your Own Transformer
127+
128+
### Simple Transformer
129+
```java
130+
public class MySimpleTransformer extends AbstractTransformer {
131+
public MySimpleTransformer() {
132+
super("MySimpleTransformer", 100);
133+
}
134+
135+
@Override
136+
public void transform(ClassReader reader, ClassWriter writer, ObfuscationContext context) {
137+
ClassVisitor visitor = BytecodeTransformationFactory.createConstantObfuscator(
138+
writer, context, 0.2f);
139+
reader.accept(visitor, 0);
140+
}
141+
142+
@Override
143+
public boolean isEnabled(ObfuscationContext context) {
144+
return context.getConfig().isObfuscateConditions();
145+
}
146+
}
147+
```
148+
149+
### Complex Transformer with Chaining
150+
```java
151+
public class MyComplexTransformer extends AbstractTransformer {
152+
public MyComplexTransformer() {
153+
super("MyComplexTransformer", 200);
154+
}
155+
156+
@Override
157+
public void transform(ClassReader reader, ClassWriter writer, ObfuscationContext context) {
158+
// Chain multiple transformations
159+
ClassVisitor chain = writer;
160+
161+
// Add garbage injection
162+
chain = BytecodeTransformationFactory.createGarbageInjector(chain, context, 0.05f);
163+
164+
// Add instruction obfuscation
165+
chain = BytecodeTransformationFactory.createInstructionObfuscator(
166+
chain, context, InstructionObfuscator.ObfuscationStrategy.BITWISE, 0.15f);
167+
168+
// Add constant obfuscation
169+
chain = BytecodeTransformationFactory.createConstantObfuscator(chain, context, 0.2f);
170+
171+
reader.accept(chain, 0);
172+
}
173+
174+
@Override
175+
public boolean isEnabled(ObfuscationContext context) {
176+
return true; // Always enabled
177+
}
178+
}
179+
```
180+
181+
## Advanced Usage
182+
183+
### Custom Method Visitor
184+
```java
185+
public class MyAdvancedMethodVisitor extends AbstractMethodVisitor {
186+
private final InstructionObfuscator obfuscator;
187+
188+
public MyAdvancedMethodVisitor(MethodVisitor mv, ObfuscationContext context,
189+
String className, String methodName, String methodDescriptor) {
190+
super(mv, context, className, methodName, methodDescriptor);
191+
this.obfuscator = new InstructionObfuscator(random, InstructionObfuscator.ObfuscationStrategy.MIXED);
192+
}
193+
194+
@Override
195+
public void visitInsn(int opcode) {
196+
// Custom logic for specific opcodes
197+
if (opcode == Opcodes.RETURN && shouldProcessWithProbability(0.3f)) {
198+
// Add dummy instructions before return
199+
BytecodeUtils.pushInteger(super.mv, random.nextInt(1000));
200+
super.visitInsn(Opcodes.POP);
201+
logTransformation("Added dummy instruction before return");
202+
}
203+
204+
// Apply obfuscation to constants
205+
if (BytecodeUtils.isIntegerConstant(opcode) && shouldProcessWithProbability(0.2f)) {
206+
obfuscator.obfuscateConstant(super.mv, opcode);
207+
logTransformation("Obfuscated constant");
208+
} else {
209+
super.visitInsn(opcode);
210+
}
211+
}
212+
213+
@Override
214+
public void visitFieldInsn(int opcode, String owner, String name, String descriptor) {
215+
// Custom field access obfuscation
216+
if (isInPackageScope() && shouldProcessWithProbability(0.1f)) {
217+
emitNOP(); // Add NOP before field access
218+
logTransformation("Added NOP before field access");
219+
}
220+
super.visitFieldInsn(opcode, owner, name, descriptor);
221+
}
222+
223+
@Override
224+
protected String getTransformerName() {
225+
return "MyAdvancedTransformer";
226+
}
227+
}
228+
```
229+
230+
## Best Practices
231+
232+
1. **Chain Transformations**: Use multiple small transformers instead of one large one
233+
2. **Use Probabilities**: Don't obfuscate every instruction - use reasonable probabilities
234+
3. **Extend Abstract Classes**: Use `AbstractTransformer`, `AbstractClassVisitor`, and `AbstractMethodVisitor`
235+
4. **Leverage Utilities**: Use `BytecodeUtils` and `InstructionObfuscator` for common operations
236+
5. **Log Transformations**: Use `logTransformation()` for debugging and verbose output
237+
6. **Check Scopes**: Always check if classes/methods are in the obfuscation scope
238+
7. **Handle Edge Cases**: Use utility methods to skip special methods and fields
239+
240+
## Integration
241+
242+
To use these utilities in your own project, simply:
243+
244+
1. Copy the utility classes from `net.cvs0.utils`
245+
2. Copy the abstract base classes from `net.cvs0.core`
246+
3. Adapt the `ObfuscationContext` to your needs, or create your own context interface
247+
4. Use the factory methods to create transformers quickly
248+
249+
The utilities are designed to be loosely coupled and can work independently of the main obfuscation framework.
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package net.cvs0.core;
2+
3+
import net.cvs0.context.ObfuscationContext;
4+
import net.cvs0.utils.BytecodeUtils;
5+
import org.objectweb.asm.ClassVisitor;
6+
import org.objectweb.asm.FieldVisitor;
7+
import org.objectweb.asm.MethodVisitor;
8+
import org.objectweb.asm.Opcodes;
9+
10+
public abstract class AbstractClassVisitor extends ClassVisitor
11+
{
12+
protected final ObfuscationContext context;
13+
protected String currentClassName;
14+
15+
public AbstractClassVisitor(ClassVisitor classVisitor, ObfuscationContext context)
16+
{
17+
super(Opcodes.ASM9, classVisitor);
18+
this.context = context;
19+
}
20+
21+
@Override
22+
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces)
23+
{
24+
this.currentClassName = name;
25+
super.visit(version, access, name, signature, superName, interfaces);
26+
}
27+
28+
@Override
29+
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions)
30+
{
31+
MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
32+
33+
if (!shouldProcessMethod(name, access)) {
34+
return mv;
35+
}
36+
37+
return createMethodVisitor(mv, access, name, descriptor, signature, exceptions);
38+
}
39+
40+
@Override
41+
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value)
42+
{
43+
if (!shouldProcessField(name, access)) {
44+
return super.visitField(access, name, descriptor, signature, value);
45+
}
46+
47+
return createFieldVisitor(super.visitField(access, name, descriptor, signature, value),
48+
access, name, descriptor, signature, value);
49+
}
50+
51+
protected boolean shouldProcessMethod(String name, int access)
52+
{
53+
if (BytecodeUtils.isMethodSkippable(name, access)) {
54+
return false;
55+
}
56+
57+
if (!context.getConfig().isInPackageScope(currentClassName)) {
58+
return false;
59+
}
60+
61+
return !context.getConfig().shouldKeepMethod(currentClassName, name, getDescriptorFromMethodVisitor());
62+
}
63+
64+
protected boolean shouldProcessField(String name, int access)
65+
{
66+
if (BytecodeUtils.isFieldSkippable(name, access)) {
67+
return false;
68+
}
69+
70+
if (!context.getConfig().isInPackageScope(currentClassName)) {
71+
return false;
72+
}
73+
74+
return !context.getConfig().shouldKeepField(currentClassName, name);
75+
}
76+
77+
protected boolean shouldProcessClass()
78+
{
79+
return context.getConfig().isInPackageScope(currentClassName) &&
80+
!context.getConfig().shouldKeepClass(currentClassName);
81+
}
82+
83+
protected void logTransformation(String message)
84+
{
85+
if (context.getConfig().isVerbose()) {
86+
System.out.println("[" + getTransformerName() + "] " + message + " in " + currentClassName);
87+
}
88+
}
89+
90+
protected abstract String getTransformerName();
91+
92+
protected abstract MethodVisitor createMethodVisitor(MethodVisitor mv, int access, String name,
93+
String descriptor, String signature, String[] exceptions);
94+
95+
protected FieldVisitor createFieldVisitor(FieldVisitor fv, int access, String name,
96+
String descriptor, String signature, Object value)
97+
{
98+
return fv;
99+
}
100+
101+
private String getDescriptorFromMethodVisitor()
102+
{
103+
return "";
104+
}
105+
}

0 commit comments

Comments
 (0)