Skip to content

Commit

Permalink
Merge pull request #73 from EpicPlayerA10/feat/setup-tests
Browse files Browse the repository at this point in the history
Setup Testing!
  • Loading branch information
narumii authored Aug 24, 2024
2 parents 7e0ec6f + 8b39372 commit 3644a18
Show file tree
Hide file tree
Showing 23 changed files with 867 additions and 64 deletions.
21 changes: 21 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Test and build

on:
pull_request:
push:

jobs:
build:
name: Test and build

runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
cache: maven
- name: Test
run: mvn test
7 changes: 4 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,11 @@ out/

### Java template
# Compiled class file
*.class

# Log file
*.log

# Package Files #
*.jar
*.war
*.nar
*.ear
Expand Down Expand Up @@ -52,4 +50,7 @@ buildNumber.properties

# My beloved
/work/
/test/
/test/

# Don't add compiled classes from java code
/testData/compiled/java/
50 changes: 50 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Contributing

## ✅ How to run deobfuscator
1. Navigate to class [`Bootstrap.java`](./deobfuscator-impl/src/test/java/Bootstrap.java)
2. In this class edit the deobfuscator configuration
- `input` - Your input jar file
- `transformers` - Pick transformers that you want to run. You can find them in [`deobfuscator-transformers`](./deobfuscator-transformers) module.
3. Run this class manually from your IDE

![tak](./assets/run-deobfuscator.gif)

## 🪜 Project structure
The project is structured as follows:
- [`deobfuscator-api`](./deobfuscator-api) - The API for the deobfuscator.
- [`deobfuscator-impl`](./deobfuscator-impl) - The main deobfuscator runner.
- [`deobfuscator-transformers`](./deobfuscator-transformers) - Transformers for the deobfuscator.
- [`deobfuscator-transformers-analyzer`](./deobfuscator-transformers-analyzer) - Analyzer-like transformers
- [`testData`](./testData) - Tests for transformers
- [`src/java`](./testData/src/java) - You can write your java code to test transformers
- [`compiled/custom-classes`](./testData/compiled/custom-classes) - Compiled classes to test transformers. You can throw here classes from your obfuscated jars.
- [`compiled/custom-jars`](./testData/compiled/custom-jars) - Jars to test transformers. You can throw here your obfuscated jars.
- [`results`](./testData/results) - Expected results that are auto-generated decompiled java code.
- [`TestDeobfuscation.java`](./deobfuscator-impl/src/test/java/uwu/narumii/deobfuscator/TestDeobfuscation.java) - Class where each test sample is registered.
- [`Bootstrap.java`](./deobfuscator-impl/src/test/java/Bootstrap.java) - Class where you can run deobfuscator manually.

## 🪄 Transformers
### What are transformers?
Whole deobfuscation process is based on transformers. Transformers are smaller classes that are responsible for deobfuscating specific obfuscation techniques. In simple words - transformers are transforming obfuscated code into a more readable form.

### How to create your own transformer?
1. Create a new class in [`deobfuscator-transformers`](./deobfuscator-transformers) module.
2. Pick `Transformer`-like class you would like to implement:
- `Transformer` - Basic transformer that transforms classes.
- `ComposedTransformer` - Transformer that consists of multiple transformers.
3. You can start coding!

## 🧪 Testing
### How these test work?
1. The registered samples are transformed using corresponding transformers.
2. The output gets decompiled using Vineflower.
3. The output gets compared with the expected output.

### How to create your own tests?
You can create your own tests for transformers. There are a few ways to do it:
- If the obfuscation is simple enough, you can write your own sample in [`testData/src/java`](./testData/src/java)
- If the obfuscation is more complex, you can throw your raw obfuscated classes (`.class` files) into [`testData/compiled/custom-classes`](./testData/compiled/custom-classes) and test transformers on them.
- You can also throw your obfuscated jars into [`testData/compiled/custom-jars`](./testData/compiled/custom-jars) and test transformers on them.

You also need to register each sample in class [TestDeobfuscation.java](./deobfuscator-impl/src/test/java/uwu/narumii/deobfuscator/TestDeobfuscation.java)

3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,6 @@

> Built on: [Java 17 (Temurin?)](https://adoptium.net/temurin/releases/?version=17)
## Contributing
See [CONTRIBUTING.md](./CONTRIBUTING.md) for more information.

Binary file added assets/run-deobfuscator.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,39 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.*;
import uwu.narumi.deobfuscator.api.context.Context;
import uwu.narumi.deobfuscator.api.helper.ClassHelper;
import uwu.narumi.deobfuscator.api.library.LibraryClassWriter;

public class ClassWrapper implements Cloneable {

protected static final Logger LOGGER = LogManager.getLogger(ClassWrapper.class);

private final String path;
private final ClassNode classNode;
private final FieldCache fieldCache;
private final ConstantPool constantPool;
private final int classWriterFlags;

public ClassWrapper(ClassReader classReader, int readerMode) throws Exception {
public ClassWrapper(String path, ClassReader classReader, int readerMode, int classWriterFlags) throws Exception {
this.path = path;
this.classNode = new ClassNode();
this.constantPool = new ConstantPool(classReader);
this.fieldCache = new FieldCache();
this.classWriterFlags = classWriterFlags;

classReader.accept(this.classNode, readerMode);
}

private ClassWrapper(ClassNode classNode, FieldCache fieldCache, ConstantPool constantPool) {
private ClassWrapper(String path, ClassNode classNode, FieldCache fieldCache, ConstantPool constantPool, int classWriterFlags) {
this.path = path;
this.classNode = classNode;
this.fieldCache = fieldCache;
this.constantPool = constantPool;
this.classWriterFlags = classWriterFlags;
}

public Optional<MethodNode> findMethod(String name, String desc) {
Expand Down Expand Up @@ -110,6 +119,25 @@ public String name() {
return classNode.name;
}

/**
* Compiles class to bytes.
*/
public byte[] compileToBytes(Context context) {
try {
ClassWriter classWriter = new LibraryClassWriter(this.classWriterFlags, context.getLoader());
this.classNode.accept(classWriter);

return classWriter.toByteArray();
} catch (Exception e) {
LOGGER.error("Error occurred while compiling class to bytes: {}", this.classNode.name);
throw new RuntimeException(e);
}
}

public String getPath() {
return path;
}

public List<FieldNode> fields() {
return classNode.fields;
}
Expand All @@ -132,6 +160,6 @@ public ConstantPool getConstantPool() {

@Override
public ClassWrapper clone() {
return new ClassWrapper(ClassHelper.copy(classNode), fieldCache.clone(), constantPool.clone());
return new ClassWrapper(this.path, ClassHelper.copy(classNode), fieldCache.clone(), constantPool.clone(), this.classWriterFlags);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ public static boolean isClass(byte[] bytes) {
.equals("CAFEBABE");
}

public static ClassWrapper loadClass(byte[] bytes, int readerMode) throws Exception {
return loadClass(bytes, readerMode, false);
public static ClassWrapper loadClass(String path, byte[] bytes, int readerMode, int classWriterFlags) throws Exception {
return loadClass(path, bytes, readerMode, classWriterFlags, false);
}

public static ClassWrapper loadClass(byte[] bytes, int readerMode, boolean fix) throws Exception {
return new ClassWrapper(new ClassReader(fix ? fixClass(bytes) : bytes), readerMode);
public static ClassWrapper loadClass(String path, byte[] bytes, int readerMode, int classWriterFlags, boolean fix) throws Exception {
return new ClassWrapper(path, new ClassReader(fix ? fixClass(bytes) : bytes), readerMode, classWriterFlags);
}

public static byte[] fixClass(byte[] bytes) throws InvalidClassException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class Library {
private final Map<String, byte[]> classFiles = new ConcurrentHashMap<>();
private final Path path;

public Library(Path path) {
public Library(Path path, int classWriterFlags) {
this.path = path;
FileHelper.loadFilesFromZip(
path,
Expand All @@ -30,8 +30,10 @@ public Library(Path path) {
try {
classFiles.putIfAbsent(
ClassHelper.loadClass(
name,
bytes,
ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG)
ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES | ClassReader.SKIP_DEBUG,
classWriterFlags)
.name(),
bytes);
} catch (Exception e) {
Expand Down
31 changes: 31 additions & 0 deletions deobfuscator-impl/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,22 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<build>
<plugins>
<plugin>
<!-- Plugin for Unit Tests -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.4.0</version>

<configuration>
<junitArtifactName>org.junit.jupiter:junit-jupiter</junitArtifactName>
<trimStackTrace>false</trimStackTrace>
</configuration>
</plugin>
</plugins>
</build>

<dependencies>
<dependency>
<artifactId>deobfuscator-api</artifactId>
Expand Down Expand Up @@ -89,6 +105,21 @@
<artifactId>CAFED00D</artifactId>
<version>${kafedjud.version}</version>
</dependency>

<!-- Testing -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.11.0</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.vineflower</groupId>
<artifactId>vineflower</artifactId>
<version>1.10.1</version>
<scope>test</scope>
</dependency>
</dependencies>

</project>
Loading

0 comments on commit 3644a18

Please sign in to comment.