diff --git a/deob-ast/pom.xml b/deob-ast/pom.xml new file mode 100644 index 00000000..83dd3f64 --- /dev/null +++ b/deob-ast/pom.xml @@ -0,0 +1,30 @@ + + + 4.0.0 + + + dev.openrs2 + openrs2 + 1.0.0-SNAPSHOT + + + openrs2-deob-ast + jar + + OpenRS2 AST Deobfuscator + + + ${project.parent.basedir} + + + + + ch.qos.logback + logback-classic + + + com.github.javaparser + javaparser-symbol-solver-core + + + diff --git a/deob-ast/src/main/java/dev/openrs2/deob/ast/AstDeobfuscator.java b/deob-ast/src/main/java/dev/openrs2/deob/ast/AstDeobfuscator.java new file mode 100644 index 00000000..70a934fc --- /dev/null +++ b/deob-ast/src/main/java/dev/openrs2/deob/ast/AstDeobfuscator.java @@ -0,0 +1,80 @@ +package dev.openrs2.deob.ast; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import com.github.javaparser.ParserConfiguration; +import com.github.javaparser.printer.PrettyPrinter; +import com.github.javaparser.printer.PrettyPrinterConfiguration; +import com.github.javaparser.symbolsolver.JavaSymbolSolver; +import com.github.javaparser.symbolsolver.resolution.typesolvers.CombinedTypeSolver; +import com.github.javaparser.symbolsolver.resolution.typesolvers.JavaParserTypeSolver; +import com.github.javaparser.symbolsolver.resolution.typesolvers.ReflectionTypeSolver; +import com.github.javaparser.utils.SourceRoot; +import com.google.common.collect.ImmutableList; +import dev.openrs2.deob.ast.transform.AddSubTransformer; +import dev.openrs2.deob.ast.transform.ComplementTransformer; +import dev.openrs2.deob.ast.transform.Transformer; + +public final class AstDeobfuscator { + private static final ImmutableList TRANSFORMERS = ImmutableList.of( + new AddSubTransformer(), + new ComplementTransformer() + ); + + public static void main(String[] args) { + var deobfuscator = new AstDeobfuscator(ImmutableList.of( + Paths.get("nonfree/client/src/main/java"), + Paths.get("nonfree/gl/src/main/java"), + Paths.get("nonfree/gl-dri/src/main/java"), + Paths.get("nonfree/loader/src/main/java"), + Paths.get("nonfree/signlink/src/main/java"), + Paths.get("nonfree/unpack/src/main/java"), + Paths.get("nonfree/unpacker/src/main/java") + )); + deobfuscator.run(); + } + + private final ImmutableList modules; + + public AstDeobfuscator(ImmutableList modules) { + this.modules = modules; + } + + public void run() { + var solver = new CombinedTypeSolver(new ReflectionTypeSolver(true)); + for (var module : modules) { + solver.add(new JavaParserTypeSolver(module)); + } + + var config = new ParserConfiguration() + .setLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_8) + .setSymbolResolver(new JavaSymbolSolver(solver)); + + var printerConfig = new PrettyPrinterConfiguration() + .setIndentType(PrettyPrinterConfiguration.IndentType.TABS_WITH_SPACE_ALIGN) + .setIndentSize(1); + + var printer = new PrettyPrinter(printerConfig); + + for (var module : modules) { + var root = new SourceRoot(module, config); + + var results = root.tryToParseParallelized(); + for (var result : results) { + if (!result.isSuccessful()) { + throw new IllegalArgumentException(result.toString()); + } + } + + root.getCompilationUnits().forEach(unit -> { + TRANSFORMERS.forEach(transformer -> { + transformer.transform(unit); + }); + }); + + root.setPrinter(printer::print); + root.saveAll(); + } + } +} diff --git a/deob-ast/src/main/java/dev/openrs2/deob/ast/transform/AddSubTransformer.java b/deob-ast/src/main/java/dev/openrs2/deob/ast/transform/AddSubTransformer.java new file mode 100644 index 00000000..09c80429 --- /dev/null +++ b/deob-ast/src/main/java/dev/openrs2/deob/ast/transform/AddSubTransformer.java @@ -0,0 +1,36 @@ +package dev.openrs2.deob.ast.transform; + +import com.github.javaparser.ast.CompilationUnit; +import com.github.javaparser.ast.expr.Expression; +import com.github.javaparser.ast.expr.IntegerLiteralExpr; +import com.github.javaparser.ast.expr.UnaryExpr; +import dev.openrs2.deob.ast.util.ExprUtils; + +public final class AddSubTransformer extends Transformer { + private static Expression negate(Expression expr) { + if (expr.isIntegerLiteralExpr()) { + return new IntegerLiteralExpr(-expr.asIntegerLiteralExpr().asInt()); + } else if (expr.isLongLiteralExpr()) { + return ExprUtils.createLong(-expr.asLongLiteralExpr().asLong()); + } else { + throw new IllegalArgumentException(); + } + } + + @Override + public void transform(CompilationUnit unit) { + unit.findAll(UnaryExpr.class).forEach(expr -> { + var operand = expr.getExpression(); + if (!ExprUtils.isIntegerOrLongLiteral(operand)) { + return; + } + + var op = expr.getOperator(); + if (op == UnaryExpr.Operator.PLUS) { + expr.replace(operand); + } else if (op == UnaryExpr.Operator.MINUS) { + expr.replace(negate(operand)); + } + }); + } +} diff --git a/deob-ast/src/main/java/dev/openrs2/deob/ast/transform/ComplementTransformer.java b/deob-ast/src/main/java/dev/openrs2/deob/ast/transform/ComplementTransformer.java new file mode 100644 index 00000000..24c17aef --- /dev/null +++ b/deob-ast/src/main/java/dev/openrs2/deob/ast/transform/ComplementTransformer.java @@ -0,0 +1,66 @@ +package dev.openrs2.deob.ast.transform; + +import java.util.Optional; + +import com.github.javaparser.ast.CompilationUnit; +import com.github.javaparser.ast.expr.BinaryExpr; +import com.github.javaparser.ast.expr.Expression; +import com.github.javaparser.ast.expr.IntegerLiteralExpr; +import com.github.javaparser.ast.expr.UnaryExpr; +import dev.openrs2.deob.ast.util.ExprUtils; + +public final class ComplementTransformer extends Transformer { + private static boolean isComplement(Expression expr) { + return expr.isUnaryExpr() && expr.asUnaryExpr().getOperator() == UnaryExpr.Operator.BITWISE_COMPLEMENT; + } + + private static boolean isComplementOrLiteral(Expression expr) { + return isComplement(expr) || ExprUtils.isIntegerOrLongLiteral(expr); + } + + private static Optional complement(BinaryExpr.Operator op) { + switch (op) { + case EQUALS: + case NOT_EQUALS: + return Optional.of(op); + case GREATER: + return Optional.of(BinaryExpr.Operator.LESS); + case GREATER_EQUALS: + return Optional.of(BinaryExpr.Operator.LESS_EQUALS); + case LESS: + return Optional.of(BinaryExpr.Operator.GREATER); + case LESS_EQUALS: + return Optional.of(BinaryExpr.Operator.GREATER_EQUALS); + default: + return Optional.empty(); + } + } + + private static Expression complement(Expression expr) { + if (expr.isUnaryExpr()) { + return expr.asUnaryExpr().getExpression(); + } else if (expr.isIntegerLiteralExpr()) { + return new IntegerLiteralExpr(~expr.asIntegerLiteralExpr().asInt()); + } else if (expr.isLongLiteralExpr()) { + return ExprUtils.createLong(~expr.asLongLiteralExpr().asLong()); + } else { + throw new IllegalArgumentException(); + } + } + + @Override + public void transform(CompilationUnit unit) { + unit.findAll(BinaryExpr.class).forEach(expr -> { + complement(expr.getOperator()).ifPresent(op -> { + var left = expr.getLeft(); + var right = expr.getRight(); + + if (isComplementOrLiteral(left) && isComplementOrLiteral(right) && !(ExprUtils.isIntegerOrLongLiteral(left) && ExprUtils.isIntegerOrLongLiteral(right))) { + expr.setOperator(op); + expr.setLeft(complement(left)); + expr.setRight(complement(right)); + } + }); + }); + } +} diff --git a/deob-ast/src/main/java/dev/openrs2/deob/ast/transform/Transformer.java b/deob-ast/src/main/java/dev/openrs2/deob/ast/transform/Transformer.java new file mode 100644 index 00000000..afcaac67 --- /dev/null +++ b/deob-ast/src/main/java/dev/openrs2/deob/ast/transform/Transformer.java @@ -0,0 +1,7 @@ +package dev.openrs2.deob.ast.transform; + +import com.github.javaparser.ast.CompilationUnit; + +public abstract class Transformer { + public abstract void transform(CompilationUnit unit); +} diff --git a/deob-ast/src/main/java/dev/openrs2/deob/ast/util/ExprUtils.java b/deob-ast/src/main/java/dev/openrs2/deob/ast/util/ExprUtils.java new file mode 100644 index 00000000..a4409d16 --- /dev/null +++ b/deob-ast/src/main/java/dev/openrs2/deob/ast/util/ExprUtils.java @@ -0,0 +1,18 @@ +package dev.openrs2.deob.ast.util; + +import com.github.javaparser.ast.expr.Expression; +import com.github.javaparser.ast.expr.LongLiteralExpr; + +public final class ExprUtils { + public static boolean isIntegerOrLongLiteral(Expression expr) { + return expr.isIntegerLiteralExpr() || expr.isLongLiteralExpr(); + } + + public static LongLiteralExpr createLong(long value) { + return new LongLiteralExpr(Long.toString(value).concat("L")); + } + + private ExprUtils() { + /* empty */ + } +} diff --git a/pom.xml b/pom.xml index 6e37f07d..5ef0872d 100644 --- a/pom.xml +++ b/pom.xml @@ -21,6 +21,7 @@ decompiler deob deob-annotations + deob-ast game util @@ -52,6 +53,11 @@ guava 28.0-jre + + com.github.javaparser + javaparser-symbol-solver-core + 3.14.9 + dev.openrs2 fernflower