├── .editorconfig ├── .gitattributes ├── .github └── workflows │ ├── build.yml │ └── close_invalid_prs.yml ├── .gitignore ├── .onedev-buildspec.yml ├── .run └── Bootstrap.run.xml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── assets └── run-deobfuscator.gif ├── deobfuscator-api ├── pom.xml └── src │ └── main │ └── java │ ├── org │ └── objectweb │ │ └── asm │ │ ├── Symbol.java │ │ └── tree │ │ ├── AbstractInsnNode.java │ │ ├── InsnList.java │ │ └── analysis │ │ ├── JumpPredictingAnalyzer.java │ │ ├── OriginalSourceInterpreter.java │ │ └── OriginalSourceValue.java │ └── uwu │ └── narumi │ └── deobfuscator │ └── api │ ├── asm │ ├── ClassWrapper.java │ ├── ConstantPool.java │ ├── FieldRef.java │ ├── FramesProvider.java │ ├── InsnContext.java │ ├── MethodContext.java │ ├── MethodRef.java │ ├── MethodlessInsnContext.java │ ├── NamedOpcodes.java │ ├── matcher │ │ ├── Match.java │ │ ├── MatchContext.java │ │ ├── group │ │ │ ├── AllMatch.java │ │ │ ├── AnyMatch.java │ │ │ ├── NotMatch.java │ │ │ ├── PositionedMatch.java │ │ │ └── SequenceMatch.java │ │ └── impl │ │ │ ├── ClassTypeMatch.java │ │ │ ├── FieldMatch.java │ │ │ ├── FrameMatch.java │ │ │ ├── InsnMatch.java │ │ │ ├── InvokeDynamicMatch.java │ │ │ ├── JumpMatch.java │ │ │ ├── MethodMatch.java │ │ │ ├── NewArrayMatch.java │ │ │ ├── NumberMatch.java │ │ │ ├── OpcodeMatch.java │ │ │ ├── RangeOpcodeMatch.java │ │ │ ├── SkipMatch.java │ │ │ ├── StringMatch.java │ │ │ └── VarLoadMatch.java │ └── remapper │ │ ├── NamePattern.java │ │ └── NamesRemapper.java │ ├── classpath │ ├── ClassInfoStorage.java │ ├── ClassProvider.java │ ├── CombinedClassProvider.java │ ├── InheritanceClassWriter.java │ └── JvmClassProvider.java │ ├── context │ ├── Context.java │ └── DeobfuscatorOptions.java │ ├── data │ ├── Pair.java │ ├── TriConsumer.java │ └── TriFunction.java │ ├── exception │ └── TransformerException.java │ ├── execution │ ├── ClasspathDataSupplier.java │ └── SandBox.java │ ├── helper │ ├── AsmHelper.java │ ├── AsmMathHelper.java │ ├── ClassHelper.java │ ├── FileHelper.java │ ├── FramedInstructionsStream.java │ ├── MethodHelper.java │ └── SimpleInterpreter.java │ ├── inheritance │ ├── InheritanceGraph.java │ ├── InheritanceVertex.java │ └── Streams.java │ └── transformer │ ├── ComposedTransformer.java │ ├── Transformer.java │ └── VersionedComposedTransformer.java ├── deobfuscator-impl ├── pom.xml └── src │ ├── main │ ├── java │ │ └── uwu │ │ │ └── narumi │ │ │ └── deobfuscator │ │ │ └── Deobfuscator.java │ └── resources │ │ └── log4j2.xml │ └── test │ ├── java │ ├── Bootstrap.java │ └── uwu │ │ └── narumi │ │ └── deobfuscator │ │ ├── TestDeobfuscation.java │ │ ├── base │ │ ├── AssertingResultSaver.java │ │ ├── SingleClassContextSource.java │ │ └── TestDeobfuscationBase.java │ │ └── transformer │ │ └── TestSandboxSecurityTransformer.java │ └── resources │ └── junit-platform.properties ├── deobfuscator-transformers ├── pom.xml └── src │ └── main │ └── java │ └── uwu │ └── narumi │ └── deobfuscator │ └── core │ └── other │ ├── composed │ ├── ComposedAllatoriTransformer.java │ ├── ComposedGruntTransformer.java │ ├── ComposedHP888Transformer.java │ ├── ComposedSuperblaubeereTransformer.java │ ├── ComposedUnknownObf1Transformer.java │ ├── ComposedZelixTransformer.java │ ├── Composed_qProtectTransformer.java │ └── general │ │ ├── ComposedGeneralFlowTransformer.java │ │ ├── ComposedGeneralRepairTransformer.java │ │ └── ComposedPeepholeCleanTransformer.java │ └── impl │ ├── allatori │ └── AllatoriStringTransformer.java │ ├── clean │ ├── AnnotationCleanTransformer.java │ ├── ClassDebugInfoCleanTransformer.java │ ├── InvalidMethodCleanTransformer.java │ ├── LineNumberCleanTransformer.java │ ├── LocalVariableNamesCleanTransformer.java │ ├── MethodDebugInfoCleanTransformer.java │ ├── ParametersInfoCleanTransformer.java │ ├── SignatureCleanTransformer.java │ ├── UnknownAttributeCleanTransformer.java │ └── peephole │ │ ├── DeadCodeCleanTransformer.java │ │ ├── ExpandDupsTransformer.java │ │ ├── JsrInlinerTransformer.java │ │ ├── NopCleanTransformer.java │ │ ├── PopUnUsedLocalVariablesTransformer.java │ │ ├── UnUsedLabelCleanTransformer.java │ │ ├── UselessGotosCleanTransformer.java │ │ └── UselessPopCleanTransformer.java │ ├── exploit │ └── WebExploitRemoveTransformer.java │ ├── hp888 │ ├── HP888PackerTransformer.java │ └── HP888StringTransformer.java │ ├── pool │ ├── InlineLocalVariablesTransformer.java │ ├── InlineStaticArrayFieldTransformer.java │ └── InlineStaticFieldTransformer.java │ ├── qprotect │ ├── qProtectAESStringEncryptionTransformer.java │ ├── qProtectFieldFlowTransformer.java │ ├── qProtectInvokeDynamicStrongTransformer.java │ ├── qProtectInvokeDynamicTransformer.java │ ├── qProtectStringTransformer.java │ └── qProtectTryCatchTransformer.java │ ├── sb27 │ └── SuperblaubeereStringTransformer.java │ ├── universal │ ├── AccessRepairTransformer.java │ ├── AnnotationFilterTransformer.java │ ├── InlinePureFunctionsTransformer.java │ ├── RecoverSyntheticsTransformer.java │ ├── RemapperTransformer.java │ ├── SourceFileNameRecoverTransformer.java │ ├── StringBuilderTransformer.java │ ├── TryCatchRepairTransformer.java │ ├── UniversalFlowTransformer.java │ ├── UniversalNumberTransformer.java │ ├── flow │ │ ├── CleanRedundantJumpsTransformer.java │ │ └── CleanRedundantSwitchesTransformer.java │ ├── number │ │ └── InlineConstantValuesTransformer.java │ └── pool │ │ ├── UniversalNumberPoolTransformer.java │ │ └── UniversalStringPoolTransformer.java │ ├── unknown │ └── obf1 │ │ └── UnknownObf1_StringByteArrayTransformer.java │ └── zkm │ ├── ZelixLongEncryptionMPCTransformer.java │ ├── ZelixParametersTransformer.java │ ├── ZelixStringTransformer.java │ └── ZelixUselessTryCatchRemoverTransformer.java ├── pom.xml ├── reverse-engineering ├── pom.xml └── src │ └── main │ └── java │ ├── qprotect │ ├── AESStringEncryption.java │ ├── InvokeDynamicSample.java │ ├── InvokeDynamicStrongSample.java │ ├── StringEncryption.java │ └── StringStrongEncryption.java │ └── zelix │ ├── enhancedstringenc │ ├── ManyStrings.java │ ├── ManyStringsOriginal.java │ ├── SomeStrings.java │ └── SomeStrings2.java │ └── longdecrypter │ ├── FallbackLongDecrypter.java │ ├── ILongDecrypter.java │ ├── Main.java │ └── SimpleLongDecrypter.java └── testData ├── compiled ├── custom-classes │ ├── JSR.class │ ├── KotlinSample.class │ ├── KotlinSample2.class │ ├── KotlinSample3.class │ ├── Pop2Sample.class │ ├── hp888 │ │ ├── IIIlIIIlllIIIlllIIlIllIIlIIIIIllIlIIlIlIllIIIlIIII.class │ │ ├── com │ │ │ └── bric │ │ │ │ └── colorpicker │ │ │ │ ├── ColorPicker$1.mc │ │ │ │ ├── ColorPicker.mc │ │ │ │ └── ColorPickerMode.mc │ │ └── pack │ │ │ └── MyFunction2.class │ ├── qprotect │ │ ├── sample1 │ │ │ ├── JSONStringer.class │ │ │ ├── JSONTokener.class │ │ │ ├── JSONWriter.class │ │ │ ├── ParserConfiguration.class │ │ │ └── XML.class │ │ ├── sample2 │ │ │ └── ChatUtil.class │ │ └── sample3 │ │ │ └── fastcode │ │ │ └── IlllIIlIIlllIIIl.class │ ├── sb27 │ │ └── sample1 │ │ │ └── ConfigComplete.class │ ├── unknown │ │ └── obf1 │ │ │ ├── SomeClass1.class │ │ │ └── SomeClass2.class │ └── zkm │ │ ├── EnhancedStringEncManyStrings.class │ │ ├── EnhancedStringEncSomeStrings.class │ │ ├── sample1 │ │ ├── io │ │ │ └── github │ │ │ │ └── repooper │ │ │ │ └── packetevents │ │ │ │ ├── PacketEvents.class │ │ │ │ └── utils │ │ │ │ └── netty │ │ │ │ └── bytebuf │ │ │ │ ├── ByteBufUtil.class │ │ │ │ ├── ByteBufUtil_7.class │ │ │ │ └── ByteBufUtil_8.class │ │ └── me │ │ │ └── frep │ │ │ └── vulcan │ │ │ └── spigot │ │ │ ├── Vulcan_a.class │ │ │ ├── Vulcan_m.class │ │ │ └── Vulcan_x.class │ │ └── sample2 │ │ └── a │ │ └── a │ │ └── a │ │ └── a │ │ ├── a.class │ │ ├── a4.class │ │ ├── a_.class │ │ ├── b.class │ │ ├── ba.class │ │ ├── bc.class │ │ └── c.class └── custom-jars │ ├── .gitkeep │ ├── SnakeGame-obf-zkm.jar │ ├── grunt-obf.jar │ └── qprotect-obf.jar ├── results ├── custom-classes │ ├── JSR.dec │ ├── KotlinSample.dec │ ├── KotlinSample2.dec │ ├── KotlinSample3.dec │ ├── Pop2Sample.dec │ ├── hp888 │ │ ├── com │ │ │ └── bric │ │ │ │ └── colorpicker │ │ │ │ ├── ColorPicker$1.dec │ │ │ │ ├── ColorPicker.dec │ │ │ │ └── ColorPickerMode.dec │ │ └── pack │ │ │ └── MyFunction2.dec │ ├── qprotect │ │ ├── sample1 │ │ │ ├── JSONStringer.dec │ │ │ ├── JSONTokener.dec │ │ │ ├── JSONWriter.dec │ │ │ ├── ParserConfiguration.dec │ │ │ └── XML.dec │ │ ├── sample2 │ │ │ └── class_0.dec │ │ └── sample3 │ │ │ └── fastcode │ │ │ └── IlllIIlIIlllIIIl.dec │ ├── sb27 │ │ └── sample1 │ │ │ └── ConfigComplete.dec │ ├── unknown │ │ └── obf1 │ │ │ ├── SomeClass1.dec │ │ │ └── SomeClass2.dec │ └── zkm │ │ ├── EnhancedStringEncManyStrings.dec │ │ ├── EnhancedStringEncSomeStrings.dec │ │ ├── sample1 │ │ └── io │ │ │ └── github │ │ │ └── repooper │ │ │ └── packetevents │ │ │ ├── PacketEvents.dec │ │ │ └── utils │ │ │ └── netty │ │ │ └── bytebuf │ │ │ ├── ByteBufUtil.dec │ │ │ ├── ByteBufUtil_7.dec │ │ │ └── ByteBufUtil_8.dec │ │ └── sample2 │ │ └── a │ │ └── a │ │ └── a │ │ └── a │ │ ├── a4.dec │ │ ├── a_.dec │ │ ├── ba.dec │ │ └── bc.dec ├── custom-jars │ ├── SnakeGame-obf-zkm │ │ ├── Main.dec │ │ ├── a.dec │ │ ├── b.dec │ │ ├── c.dec │ │ ├── d.dec │ │ ├── e.dec │ │ └── f.dec │ ├── grunt-obf │ │ └── com │ │ │ └── top1 │ │ │ ├── Main$ConstantPool.dec │ │ │ ├── Main$NumberData$ConstantPool.dec │ │ │ ├── Main$NumberData.dec │ │ │ ├── Main$processor$ConstantPool.dec │ │ │ ├── Main$processor$NumberData$ConstantPool.dec │ │ │ ├── Main$processor$NumberData.dec │ │ │ ├── Main$processor.dec │ │ │ └── Main.dec │ └── qprotect-obf │ │ └── org │ │ └── example │ │ ├── Main.dec │ │ └── Main2.dec └── java │ ├── TestInlineLocalVariables.dec │ ├── TestInlineStaticFields.dec │ ├── TestInlineStaticFieldsWithModification.dec │ ├── TestSimpleFlowObfuscation.dec │ ├── TestStringBuilderTransformer.dec │ ├── TestUniversalNumberTransformer.dec │ └── remap │ ├── class_0.dec │ ├── class_1.dec │ ├── class_2.dec │ └── class_3.dec └── src └── java ├── pom.xml └── src └── main └── java ├── TestInlineLocalVariables.java ├── TestInlineStaticFields.java ├── TestInlineStaticFieldsWithModification.java ├── TestSimpleFlowObfuscation.java ├── TestStringBuilderTransformer.java ├── TestUniversalNumberTransformer.java ├── remap ├── Cat.java ├── Main.java ├── Pet.java └── SampleRunnable.java └── sandbox └── TestSandboxSecurity.java /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.java] 2 | charset = utf-8 3 | end_of_line = lf 4 | indent_style = space 5 | ij_continuation_indent_size = 4 6 | indent_size = 2 7 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | testData/results/**/* diff 2 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Test and build 2 | 3 | on: 4 | pull_request: 5 | push: 6 | 7 | jobs: 8 | build: 9 | name: Test and build 10 | 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - name: Set up JDK 17 15 | uses: actions/setup-java@v4 16 | with: 17 | java-version: '17' 18 | distribution: 'temurin' 19 | cache: maven 20 | - name: Test 21 | run: mvn test 22 | -------------------------------------------------------------------------------- /.github/workflows/close_invalid_prs.yml: -------------------------------------------------------------------------------- 1 | # Thanks paper: https://github.com/papermc/paper/blob/master/.github/workflows/close_invalid_prs.yml 2 | name: Close invalid PRs 3 | 4 | on: 5 | pull_request_target: 6 | types: [ opened ] 7 | 8 | jobs: 9 | run: 10 | if: | 11 | github.repository != github.event.pull_request.head.repo.full_name && 12 | ( 13 | github.head_ref == 'master' || 14 | github.event.pull_request.head.repo.owner.type != 'User' 15 | ) 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: superbrothers/close-pull-request@v3 19 | id: "master_branch" 20 | if: github.head_ref == 'master' 21 | with: 22 | comment: "Please do not open pull requests from the `master` branch, create a new branch instead." 23 | 24 | - uses: superbrothers/close-pull-request@v3 25 | id: "org_account" 26 | if: github.event.pull_request.head.repo.owner.type != 'User' && steps.master_branch.outcome == 'skipped' 27 | with: 28 | comment: "Please do not open pull requests from non-user accounts like organisations. Create a fork on a user account instead." 29 | 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Intellij template 2 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 3 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 4 | 5 | # User-specific stuff 6 | .idea/ 7 | 8 | # IntelliJ 9 | out/ 10 | 11 | # mpeltonen/sbt-idea plugin 12 | .idea_modules/ 13 | 14 | ### Java template 15 | # Compiled class file 16 | 17 | # Log file 18 | *.log 19 | 20 | # Package Files # 21 | *.war 22 | *.nar 23 | *.ear 24 | *.zip 25 | *.tar.gz 26 | *.rar 27 | 28 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 29 | hs_err_pid* 30 | replay_pid* 31 | 32 | ### Maven template 33 | target/ 34 | pom.xml.tag 35 | pom.xml.releaseBackup 36 | pom.xml.versionsBackup 37 | pom.xml.next 38 | release.properties 39 | dependency-reduced-pom.xml 40 | buildNumber.properties 41 | .mvn/timing.properties 42 | # https://github.com/takari/maven-wrapper#usage-without-binary-jar 43 | .mvn/wrapper/maven-wrapper.jar 44 | 45 | # Eclipse m2e generated files 46 | # Eclipse Core 47 | .project 48 | # JDT-specific (Eclipse Java Development Tools) 49 | .classpath 50 | 51 | # My beloved 52 | /work/ 53 | /test/ 54 | 55 | # Don't track compiled classes from java code 56 | /testData/compiled/java/ 57 | # Don't track deobfuscated files. They are added as decompiled code 58 | /testData/deobfuscated/ 59 | -------------------------------------------------------------------------------- /.onedev-buildspec.yml: -------------------------------------------------------------------------------- 1 | version: 38 2 | jobs: 3 | - name: Mirror 4 | steps: 5 | - !PullRepository 6 | name: Mirror 7 | remoteUrl: https://github.com/narumii/Deobfuscator 8 | targetProject: Deobfuscator 9 | accessTokenSecret: CunnyDev Token 10 | refs: refs/heads/* refs/tags/* 11 | withLfs: false 12 | force: false 13 | condition: ALL_PREVIOUS_STEPS_WERE_SUCCESSFUL 14 | triggers: 15 | - !ScheduleTrigger 16 | cronExpression: 0 0 1 * * ? 17 | projects: Deobfuscator 18 | retryCondition: never 19 | maxRetries: 3 20 | retryDelay: 30 21 | timeout: 14400 22 | -------------------------------------------------------------------------------- /.run/Bootstrap.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Diobfuscator 2 | A deobfuscator for java 3 | 4 | > If you are looking for v1 version of Diobfuscator, you can find it [here](https://github.com/narumii/Deobfuscator/tree/v1) 5 | 6 | ## ✅ How to run deobfuscator 7 | If you want to use this deobfuscator, you need to start it from your IDE manually. 8 | 9 | 1. Clone this repository and open it in IntelliJ 10 | 2. Make sure that you have selected [Java 17 (Temurin)](https://adoptium.net/temurin/releases/?version=17) in `Project Structure` -> `SDK` 11 | 3. Place your obfuscated jar inside the root project directory. For example in `work/obf-test.jar` 12 | 4. Navigate to class [`Bootstrap.java`](./deobfuscator-impl/src/test/java/Bootstrap.java) 13 | 5. In this class edit the deobfuscator configuration 14 | - `inputJar` - Your obfuscated jar file that you placed in step 1 15 | - `transformers` - Pick transformers that you want to run. You can find them in [`deobfuscator-transformers`](./deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other) module. 16 | 6. Run this class manually from your IDE. You can use our pre-configured IntelliJ task named `Bootstrap`. 17 | 18 | ![tak](./assets/run-deobfuscator.gif) 19 | 20 | ## 🔧 Contributing 21 | Contributions are welcome! See [CONTRIBUTING.md](./CONTRIBUTING.md) for a project introduction and some basics about java bytecode. 22 | 23 | ## Links 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /assets/run-deobfuscator.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/assets/run-deobfuscator.gif -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/FieldRef.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.asm; 2 | 3 | import org.objectweb.asm.tree.ClassNode; 4 | import org.objectweb.asm.tree.FieldInsnNode; 5 | import org.objectweb.asm.tree.FieldNode; 6 | 7 | /** 8 | * @param owner Class that owns this field 9 | * @param name Field's name 10 | * @param desc Field's descriptor 11 | */ 12 | public record FieldRef(String owner, String name, String desc) { 13 | public static FieldRef of(ClassNode classNode, FieldNode fieldNode) { 14 | return new FieldRef(classNode.name, fieldNode.name, fieldNode.desc); 15 | } 16 | 17 | public static FieldRef of(FieldInsnNode fieldInsn) { 18 | return new FieldRef(fieldInsn.owner, fieldInsn.name, fieldInsn.desc); 19 | } 20 | 21 | @Override 22 | public String toString() { 23 | return owner + "." + name + desc; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/FramesProvider.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.asm; 2 | 3 | import org.objectweb.asm.tree.AbstractInsnNode; 4 | import org.objectweb.asm.tree.ClassNode; 5 | import org.objectweb.asm.tree.MethodNode; 6 | import org.objectweb.asm.tree.analysis.Frame; 7 | import org.objectweb.asm.tree.analysis.OriginalSourceValue; 8 | 9 | import java.util.Map; 10 | 11 | @FunctionalInterface 12 | public interface FramesProvider { 13 | Map> compute(ClassNode classNode, MethodNode methodNode); 14 | } 15 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/InsnContext.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.asm; 2 | 3 | import org.jetbrains.annotations.Nullable; 4 | import org.objectweb.asm.tree.AbstractInsnNode; 5 | import org.objectweb.asm.tree.MethodNode; 6 | import org.objectweb.asm.tree.analysis.Frame; 7 | import org.objectweb.asm.tree.analysis.OriginalSourceValue; 8 | import uwu.narumi.deobfuscator.api.helper.AsmHelper; 9 | 10 | import java.util.Set; 11 | 12 | /** 13 | * Instruction context. Holds all information relevant to the current instruction. 14 | */ 15 | public class InsnContext { 16 | private final AbstractInsnNode insn; 17 | private final MethodContext methodContext; 18 | 19 | InsnContext(AbstractInsnNode insn, MethodContext methodContext) { 20 | this.insn = insn; 21 | this.methodContext = methodContext; 22 | } 23 | 24 | public InsnContext of(AbstractInsnNode insn) { 25 | return new InsnContext(insn, this.methodContext); 26 | } 27 | 28 | @Nullable 29 | public Frame frame() { 30 | return this.methodContext.frames().get(this.insn); 31 | } 32 | 33 | public Set consumers() { 34 | return this.methodContext.getConsumersMap().get(this.insn); 35 | } 36 | 37 | public MethodNode methodNode() { 38 | return this.methodContext.methodNode(); 39 | } 40 | 41 | /** 42 | * Current instruction 43 | */ 44 | public AbstractInsnNode insn() { 45 | return insn; 46 | } 47 | 48 | /** 49 | * Method context 50 | */ 51 | public MethodContext methodContext() { 52 | return methodContext; 53 | } 54 | 55 | public int getConsumedStackValuesCount() { 56 | return this.insn.getConsumedStackValuesCount(this.frame()); 57 | } 58 | 59 | /** 60 | * Places POPs instructions before current instruction to remove source values from the stack. 61 | * This method automatically calculates how many stack values to pop. 62 | */ 63 | public void placePops() { 64 | for (int i = 0; i < this.getConsumedStackValuesCount(); i++) { 65 | int stackValueIdx = frame().getStackSize() - (i + 1); 66 | OriginalSourceValue sourceValue = frame().getStack(stackValueIdx); 67 | 68 | // Pop 69 | this.methodNode().instructions.insertBefore(this.insn, AsmHelper.toPop(sourceValue)); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/MethodContext.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.asm; 2 | 3 | import org.jetbrains.annotations.Unmodifiable; 4 | import org.objectweb.asm.tree.AbstractInsnNode; 5 | import org.objectweb.asm.tree.MethodNode; 6 | import org.objectweb.asm.tree.analysis.Frame; 7 | import org.objectweb.asm.tree.analysis.OriginalSourceValue; 8 | import uwu.narumi.deobfuscator.api.helper.MethodHelper; 9 | 10 | import java.util.Map; 11 | import java.util.Set; 12 | 13 | /** 14 | * Method context 15 | */ 16 | public class MethodContext { 17 | private final ClassWrapper classWrapper; 18 | private final MethodNode methodNode; 19 | private final FramesProvider framesProvider; 20 | // Lazily initialized 21 | private Map> frames = null; 22 | // Lazily initialized 23 | private Map> consumersMap = null; 24 | 25 | private MethodContext( 26 | ClassWrapper classWrapper, 27 | MethodNode methodNode, 28 | FramesProvider framesProvider 29 | ) { 30 | this.classWrapper = classWrapper; 31 | this.methodNode = methodNode; 32 | this.framesProvider = framesProvider; 33 | } 34 | 35 | /** 36 | * Class that owns this method 37 | */ 38 | public ClassWrapper classWrapper() { 39 | return classWrapper; 40 | } 41 | 42 | /** 43 | * Method itself 44 | */ 45 | public MethodNode methodNode() { 46 | return methodNode; 47 | } 48 | 49 | /** 50 | * Frames of the method 51 | */ 52 | @Unmodifiable 53 | public synchronized Map> frames() { 54 | if (this.frames == null) { 55 | // Lazy initialization 56 | this.frames = this.framesProvider.compute(this.classWrapper.classNode(), this.methodNode); 57 | } 58 | return frames; 59 | } 60 | 61 | public synchronized Map> getConsumersMap() { 62 | if (consumersMap == null) { 63 | // Lazy initialization 64 | this.consumersMap = MethodHelper.computeConsumersMap(this.frames); 65 | } 66 | return consumersMap; 67 | } 68 | 69 | /** 70 | * Creates new {@link InsnContext} instance at specified instruction 71 | * 72 | * @param insn instruction 73 | * @return new {@link InsnContext} instance 74 | */ 75 | public InsnContext at(AbstractInsnNode insn) { 76 | return new InsnContext(insn, this); 77 | } 78 | 79 | public static MethodContext of(ClassWrapper classWrapper, MethodNode methodNode) { 80 | return of(classWrapper, methodNode, MethodHelper::analyzeSource); 81 | } 82 | 83 | /** 84 | * Creates new {@link MethodContext} instance 85 | */ 86 | public static MethodContext of(ClassWrapper classWrapper, MethodNode methodNode, FramesProvider framesProvider) { 87 | return new MethodContext(classWrapper, methodNode, framesProvider); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/MethodRef.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.asm; 2 | 3 | import org.objectweb.asm.Handle; 4 | import org.objectweb.asm.tree.ClassNode; 5 | import org.objectweb.asm.tree.MethodInsnNode; 6 | import org.objectweb.asm.tree.MethodNode; 7 | 8 | /** 9 | * @param owner Class that owns this method 10 | * @param name Method's name 11 | * @param desc Method's descriptor 12 | */ 13 | public record MethodRef(String owner, String name, String desc) { 14 | public static MethodRef of(ClassNode classNode, MethodNode methodNode) { 15 | return new MethodRef(classNode.name, methodNode.name, methodNode.desc); 16 | } 17 | 18 | public static MethodRef of(MethodInsnNode methodInsn) { 19 | return new MethodRef(methodInsn.owner, methodInsn.name, methodInsn.desc); 20 | } 21 | 22 | public static MethodRef of(Handle handle) { 23 | return new MethodRef(handle.getOwner(), handle.getName(), handle.getDesc()); 24 | } 25 | 26 | @Override 27 | public String toString() { 28 | return owner + "." + name + desc; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/MethodlessInsnContext.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.asm; 2 | 3 | import org.objectweb.asm.tree.AbstractInsnNode; 4 | import org.objectweb.asm.tree.MethodNode; 5 | import org.objectweb.asm.tree.analysis.Frame; 6 | import org.objectweb.asm.tree.analysis.OriginalSourceValue; 7 | 8 | public class MethodlessInsnContext extends InsnContext { 9 | private final Frame frame; 10 | 11 | public MethodlessInsnContext(AbstractInsnNode insn, Frame frame) { 12 | super(insn, null); 13 | this.frame = frame; 14 | } 15 | 16 | @Override 17 | public Frame frame() { 18 | return this.frame; 19 | } 20 | 21 | @Override 22 | public MethodNode methodNode() { 23 | throw new UnsupportedOperationException(); 24 | } 25 | 26 | @Override 27 | public InsnContext of(AbstractInsnNode insn) { 28 | throw new UnsupportedOperationException(); 29 | } 30 | 31 | @Override 32 | public MethodContext methodContext() { 33 | throw new UnsupportedOperationException(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/NamedOpcodes.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.asm; 2 | 3 | import org.objectweb.asm.Opcodes; 4 | 5 | import java.lang.reflect.Field; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | /** 10 | * Maps opcodes to their names. For debug purposes. 11 | */ 12 | public final class NamedOpcodes { 13 | 14 | private static final Map names = new HashMap<>(); 15 | 16 | private NamedOpcodes() { 17 | throw new IllegalArgumentException(); 18 | } 19 | 20 | static { 21 | try { 22 | boolean found = false; 23 | for (Field declaredField : Opcodes.class.getDeclaredFields()) { 24 | if (declaredField.getName().equals("NOP")) found = true; 25 | 26 | if (!found) continue; 27 | 28 | names.put(declaredField.getInt(null), declaredField.getName()); 29 | } 30 | } catch (Exception e) { 31 | throw new RuntimeException(e); 32 | } 33 | } 34 | 35 | public static String map(int opcode) { 36 | return names.getOrDefault(opcode, String.valueOf(opcode)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/MatchContext.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.asm.matcher; 2 | 3 | import org.objectweb.asm.tree.AbstractInsnNode; 4 | import org.objectweb.asm.tree.analysis.Frame; 5 | import org.objectweb.asm.tree.analysis.OriginalSourceValue; 6 | import uwu.narumi.deobfuscator.api.asm.InsnContext; 7 | 8 | import java.util.Collections; 9 | import java.util.HashMap; 10 | import java.util.HashSet; 11 | import java.util.Map; 12 | import java.util.Set; 13 | 14 | /** 15 | * Immutable match context. After matching process, the context is frozen by {@link MatchContext#freeze()} 16 | */ 17 | public class MatchContext { 18 | private final InsnContext insnContext; 19 | private final Map captures; 20 | private final Set collectedInsns; 21 | 22 | private MatchContext(InsnContext insnContext, Map captures, Set collectedInsns) { 23 | this.insnContext = insnContext; 24 | this.captures = captures; 25 | this.collectedInsns = collectedInsns; 26 | } 27 | 28 | public static MatchContext of(InsnContext insnContext) { 29 | return new MatchContext(insnContext, new HashMap<>(), new HashSet<>()); 30 | } 31 | 32 | public MatchContext freeze() { 33 | return new MatchContext(this.insnContext, Collections.unmodifiableMap(this.captures), Collections.unmodifiableSet(this.collectedInsns)); 34 | } 35 | 36 | /** 37 | * Merges other {@link MatchContext} into this {@link MatchContext}. 38 | * 39 | * @see Match#matchAndMerge(InsnContext, MatchContext) 40 | */ 41 | void merge(MatchContext other) { 42 | this.captures.putAll(other.captures); 43 | this.collectedInsns.addAll(other.collectedInsns); 44 | } 45 | 46 | /** 47 | * @see InsnContext#insn() 48 | */ 49 | public AbstractInsnNode insn() { 50 | return this.insnContext.insn(); 51 | } 52 | 53 | /** 54 | * @see InsnContext#frame() 55 | */ 56 | public Frame frame() { 57 | return this.insnContext.frame(); 58 | } 59 | 60 | /** 61 | * Instruction context 62 | */ 63 | public InsnContext insnContext() { 64 | return insnContext; 65 | } 66 | 67 | /** 68 | * Captured instructions in a matching process. id -> match context 69 | * 70 | * @see Match#capture(String) 71 | */ 72 | public Map captures() { 73 | return captures; 74 | } 75 | 76 | /** 77 | * Collected instructions that matches this match and children matches 78 | */ 79 | public Set collectedInsns() { 80 | return collectedInsns; 81 | } 82 | 83 | /** 84 | * Removes all collected instructions 85 | */ 86 | public void removeAll() { 87 | for (AbstractInsnNode insn : this.collectedInsns) { 88 | this.insnContext.methodContext().methodNode().instructions.remove(insn); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/group/AllMatch.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.asm.matcher.group; 2 | 3 | import uwu.narumi.deobfuscator.api.asm.matcher.Match; 4 | import uwu.narumi.deobfuscator.api.asm.matcher.MatchContext; 5 | 6 | public class AllMatch extends Match { 7 | 8 | private final Match[] matches; 9 | 10 | private AllMatch(Match[] matches) { 11 | this.matches = matches; 12 | } 13 | 14 | public static AllMatch of(Match... matches) { 15 | return new AllMatch(matches); 16 | } 17 | 18 | @Override 19 | protected boolean test(MatchContext context) { 20 | for (Match match : matches) { 21 | if (!match.matchAndMerge(context.insnContext(), context)) return false; 22 | } 23 | 24 | return true; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/group/AnyMatch.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.asm.matcher.group; 2 | 3 | import uwu.narumi.deobfuscator.api.asm.matcher.Match; 4 | import uwu.narumi.deobfuscator.api.asm.matcher.MatchContext; 5 | 6 | public class AnyMatch extends Match { 7 | 8 | private final Match[] matches; 9 | 10 | private AnyMatch(Match[] matches) { 11 | this.matches = matches; 12 | } 13 | 14 | public static AnyMatch of(Match... matches) { 15 | return new AnyMatch(matches); 16 | } 17 | 18 | @Override 19 | protected boolean test(MatchContext context) { 20 | for (Match match : matches) { 21 | if (match.matchAndMerge(context.insnContext(), context)) return true; 22 | } 23 | 24 | return false; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/group/NotMatch.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.asm.matcher.group; 2 | 3 | import uwu.narumi.deobfuscator.api.asm.matcher.Match; 4 | import uwu.narumi.deobfuscator.api.asm.matcher.MatchContext; 5 | 6 | public class NotMatch extends Match { 7 | 8 | private final Match match; 9 | 10 | private NotMatch(Match match) { 11 | this.match = match; 12 | } 13 | 14 | public static NotMatch of(Match match) { 15 | return new NotMatch(match); 16 | } 17 | 18 | @Override 19 | protected boolean test(MatchContext context) { 20 | return !match.matchAndMerge(context.insnContext(), context); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/group/PositionedMatch.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.asm.matcher.group; 2 | 3 | import org.objectweb.asm.tree.AbstractInsnNode; 4 | import uwu.narumi.deobfuscator.api.asm.matcher.Match; 5 | import uwu.narumi.deobfuscator.api.asm.matcher.MatchContext; 6 | 7 | public class PositionedMatch extends Match { 8 | 9 | private final int offset; 10 | private final boolean previous; 11 | private final boolean skipAsmInstructions; 12 | private final Match match; 13 | 14 | private PositionedMatch(int offset, boolean skipAsmInstructions, Match match) { 15 | this.offset = Math.abs(offset); 16 | this.previous = offset < 0; 17 | this.skipAsmInstructions = skipAsmInstructions; 18 | this.match = match; 19 | } 20 | 21 | @Override 22 | protected boolean test(MatchContext context) { 23 | return this.match.matchAndMerge(context.insnContext().of(walk(context.insn())), context); 24 | } 25 | 26 | private AbstractInsnNode walk(AbstractInsnNode node) { 27 | if (previous) { 28 | node = skipAsmInstructions ? node.previous(offset) : node.getPrevious(offset); 29 | } else { 30 | node = skipAsmInstructions ? node.next(offset) : node.getNext(offset); 31 | } 32 | 33 | return node; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/group/SequenceMatch.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.asm.matcher.group; 2 | 3 | import org.objectweb.asm.tree.AbstractInsnNode; 4 | import org.objectweb.asm.tree.FrameNode; 5 | import org.objectweb.asm.tree.LabelNode; 6 | import org.objectweb.asm.tree.LineNumberNode; 7 | import uwu.narumi.deobfuscator.api.asm.InsnContext; 8 | import uwu.narumi.deobfuscator.api.asm.matcher.Match; 9 | import uwu.narumi.deobfuscator.api.asm.matcher.MatchContext; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | /** 15 | * Matches instructions in sequence 16 | */ 17 | // TODO: backwards match? 18 | public class SequenceMatch extends Match { 19 | 20 | private static final Match FRAME_MATCH = Match.of(context -> context.insn() instanceof FrameNode); 21 | 22 | private final Match[] matches; 23 | private final List skipMatches = new ArrayList<>(List.of(FRAME_MATCH)); 24 | 25 | private SequenceMatch(Match[] matches) { 26 | this.matches = matches; 27 | } 28 | 29 | public static SequenceMatch of(Match... matches) { 30 | return new SequenceMatch(matches); 31 | } 32 | 33 | /** 34 | * You can specify which instructions should be skipped during walking through instructions 35 | */ 36 | public SequenceMatch skip(Match... matches) { 37 | this.skipMatches.addAll(List.of(matches)); 38 | return this; 39 | } 40 | 41 | public SequenceMatch doNotSkipFrames() { 42 | this.skipMatches.remove(FRAME_MATCH); 43 | return this; 44 | } 45 | 46 | @Override 47 | protected boolean test(MatchContext context) { 48 | AbstractInsnNode currentInsn = context.insn(); 49 | int matchIdx = 0; 50 | 51 | while (matchIdx < matches.length) { 52 | if (currentInsn == null) { 53 | // Expected instruction but no instructions left 54 | return false; 55 | } 56 | 57 | InsnContext currentInsnContext = context.insnContext().of(currentInsn); 58 | if (this.skipMatches.stream().anyMatch(match -> match.matches(currentInsnContext))) { 59 | // Skip instruction 60 | currentInsn = currentInsn.getNext(); 61 | continue; 62 | } 63 | 64 | // Find match 65 | Match match = this.matches[matchIdx]; 66 | if (!match.matchAndMerge(currentInsnContext, context)) { 67 | // No match 68 | return false; 69 | } 70 | 71 | // Go to next instruction 72 | currentInsn = currentInsn.getNext(); 73 | matchIdx++; 74 | } 75 | 76 | return true; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/impl/ClassTypeMatch.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.asm.matcher.impl; 2 | 3 | import org.objectweb.asm.tree.AbstractInsnNode; 4 | import uwu.narumi.deobfuscator.api.asm.matcher.Match; 5 | import uwu.narumi.deobfuscator.api.asm.matcher.MatchContext; 6 | 7 | /** 8 | * Matches by instruction class type 9 | */ 10 | public class ClassTypeMatch extends Match { 11 | 12 | private final Class classType; 13 | 14 | private ClassTypeMatch(Class classType) { 15 | this.classType = classType; 16 | } 17 | 18 | public static ClassTypeMatch of(Class classType) { 19 | return new ClassTypeMatch(classType); 20 | } 21 | 22 | @Override 23 | protected boolean test(MatchContext context) { 24 | return context.insn().getClass().equals(classType); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/impl/FieldMatch.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.asm.matcher.impl; 2 | 3 | import org.objectweb.asm.Opcodes; 4 | import org.objectweb.asm.tree.FieldInsnNode; 5 | import uwu.narumi.deobfuscator.api.asm.FieldRef; 6 | import uwu.narumi.deobfuscator.api.asm.matcher.Match; 7 | import uwu.narumi.deobfuscator.api.asm.matcher.MatchContext; 8 | 9 | public class FieldMatch extends Match { 10 | 11 | private final int opcode; 12 | private String owner; 13 | private String name; 14 | private String desc; 15 | 16 | private FieldMatch(int opcode) { 17 | this.opcode = opcode; 18 | } 19 | 20 | public static FieldMatch of(int opcode) { 21 | return new FieldMatch(opcode); 22 | } 23 | 24 | public static FieldMatch create() { 25 | return new FieldMatch(-1); 26 | } 27 | 28 | public static FieldMatch putStatic() { 29 | return new FieldMatch(Opcodes.PUTSTATIC); 30 | } 31 | 32 | public static FieldMatch getStatic() { 33 | return new FieldMatch(Opcodes.GETSTATIC); 34 | } 35 | 36 | public static FieldMatch putField() { 37 | return new FieldMatch(Opcodes.PUTFIELD); 38 | } 39 | 40 | public static FieldMatch getField() { 41 | return new FieldMatch(Opcodes.GETFIELD); 42 | } 43 | 44 | @Override 45 | protected boolean test(MatchContext context) { 46 | return context.insn() instanceof FieldInsnNode fieldInsn 47 | && (opcode == -1 || fieldInsn.getOpcode() == opcode) 48 | && (owner == null || fieldInsn.owner.equals(owner)) 49 | && (name == null || fieldInsn.name.equals(name)) 50 | && (desc == null || fieldInsn.desc.equals(desc)); 51 | } 52 | 53 | public FieldMatch owner(String owner) { 54 | this.owner = owner; 55 | return this; 56 | } 57 | 58 | public FieldMatch name(String name) { 59 | this.name = name; 60 | return this; 61 | } 62 | 63 | public FieldMatch desc(String desc) { 64 | this.desc = desc; 65 | return this; 66 | } 67 | 68 | public FieldMatch fieldRef(FieldRef fieldRef) { 69 | this.owner = fieldRef.owner(); 70 | this.name = fieldRef.name(); 71 | this.desc = fieldRef.desc(); 72 | return this; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/impl/FrameMatch.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.asm.matcher.impl; 2 | 3 | import org.objectweb.asm.tree.AbstractInsnNode; 4 | import org.objectweb.asm.tree.analysis.Frame; 5 | import org.objectweb.asm.tree.analysis.OriginalSourceValue; 6 | import uwu.narumi.deobfuscator.api.asm.matcher.Match; 7 | import uwu.narumi.deobfuscator.api.asm.matcher.MatchContext; 8 | 9 | /** 10 | * Match instruction from stack 11 | */ 12 | public class FrameMatch extends Match { 13 | private final Match match; 14 | private final FrameMatchOptions options; 15 | 16 | /** 17 | * @param match A {@link Match} to match against that stack value 18 | * @param options Options for the frame match 19 | */ 20 | private FrameMatch(Match match, FrameMatchOptions options) { 21 | this.match = match; 22 | this.options = options; 23 | } 24 | 25 | public static FrameMatch stack(int stackValueIdx, Match match) { 26 | return new FrameMatch(match, new StackFrameOptions(stackValueIdx, false)); 27 | } 28 | 29 | public static FrameMatch stackOriginal(int stackValueIdx, Match match) { 30 | return new FrameMatch(match, new StackFrameOptions(stackValueIdx, true)); 31 | } 32 | 33 | public static FrameMatch localVariable(int localVariableIdx, Match match) { 34 | return new FrameMatch(match, new LocalVariableFrameOptions(localVariableIdx)); 35 | } 36 | 37 | @Override 38 | protected boolean test(MatchContext context) { 39 | if (context.frame() == null) { 40 | // If we expect stack values, then frame can't be null 41 | return false; 42 | } 43 | 44 | // Get the source value 45 | OriginalSourceValue sourceValue; 46 | if (this.options instanceof StackFrameOptions stackFrameOptions) { 47 | // Pop values from stack and match them 48 | int index = context.frame().getStackSize() - (stackFrameOptions.stackValueIdx + 1); 49 | if (index < 0) { 50 | // If the stack value should exist but does not, then it does not match 51 | return false; 52 | } 53 | 54 | Frame frame = context.frame(); 55 | sourceValue = frame.getStack(index); 56 | if (stackFrameOptions.originalValue) { 57 | sourceValue = sourceValue.originalSource; 58 | } 59 | } else if (this.options instanceof LocalVariableFrameOptions lvFrameOptions) { 60 | Frame frame = context.frame(); 61 | sourceValue = frame.getLocal(lvFrameOptions.localVariableIdx); 62 | } else { 63 | throw new IllegalStateException("Unknown frame match options"); 64 | } 65 | 66 | if (!sourceValue.isOneWayProduced()) { 67 | // We only want stack values that are one way produced 68 | return false; 69 | } 70 | 71 | AbstractInsnNode stackValueInsn = sourceValue.getProducer(); 72 | return this.match.matchAndMerge(context.insnContext().of(stackValueInsn), context); 73 | } 74 | 75 | sealed interface FrameMatchOptions permits StackFrameOptions, LocalVariableFrameOptions { 76 | } 77 | 78 | /** 79 | * @param stackValueIdx Index of the value in the stack, starting from the top of the stack, so '0' is the top value. 80 | * @param originalValue 81 | */ 82 | record StackFrameOptions(int stackValueIdx, boolean originalValue) implements FrameMatchOptions { 83 | } 84 | 85 | record LocalVariableFrameOptions(int localVariableIdx) implements FrameMatchOptions { 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/impl/InsnMatch.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.asm.matcher.impl; 2 | 3 | import org.objectweb.asm.tree.AbstractInsnNode; 4 | import uwu.narumi.deobfuscator.api.asm.matcher.Match; 5 | import uwu.narumi.deobfuscator.api.asm.matcher.MatchContext; 6 | 7 | /** 8 | * Matches an instruction by its instance. 9 | */ 10 | public class InsnMatch extends Match { 11 | 12 | private final AbstractInsnNode node; 13 | 14 | private InsnMatch(AbstractInsnNode node) { 15 | this.node = node; 16 | } 17 | 18 | public static InsnMatch of(AbstractInsnNode node) { 19 | return new InsnMatch(node); 20 | } 21 | 22 | @Override 23 | protected boolean test(MatchContext context) { 24 | return this.node.equals(context.insn()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/impl/JumpMatch.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.asm.matcher.impl; 2 | 3 | import org.objectweb.asm.tree.JumpInsnNode; 4 | import org.objectweb.asm.tree.LabelNode; 5 | import uwu.narumi.deobfuscator.api.asm.matcher.Match; 6 | import uwu.narumi.deobfuscator.api.asm.matcher.MatchContext; 7 | 8 | public class JumpMatch extends Match { 9 | 10 | private final int opcode; 11 | private final LabelNode labelNode; 12 | 13 | private JumpMatch(int opcode, LabelNode labelNode) { 14 | this.opcode = opcode; 15 | this.labelNode = labelNode; 16 | } 17 | 18 | public static JumpMatch of(int opcode, LabelNode labelNode) { 19 | return new JumpMatch(opcode, labelNode); 20 | } 21 | 22 | public static JumpMatch of(LabelNode labelNode) { 23 | return of(-1, labelNode); 24 | } 25 | 26 | public static JumpMatch of(int opcode) { 27 | return of(opcode, null); 28 | } 29 | 30 | public static JumpMatch of() { 31 | return of(-1, null); 32 | } 33 | 34 | @Override 35 | protected boolean test(MatchContext context) { 36 | return context.insn() instanceof JumpInsnNode jumpInsn 37 | && (opcode == -1 || jumpInsn.getOpcode() == opcode) 38 | && (labelNode == null || jumpInsn.label.equals(labelNode)); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/impl/MethodMatch.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.asm.matcher.impl; 2 | 3 | import java.util.Arrays; 4 | 5 | import org.objectweb.asm.Opcodes; 6 | import org.objectweb.asm.tree.MethodInsnNode; 7 | import uwu.narumi.deobfuscator.api.asm.matcher.Match; 8 | import uwu.narumi.deobfuscator.api.asm.matcher.MatchContext; 9 | 10 | public class MethodMatch extends Match { 11 | 12 | private final int opcode; 13 | private String owner; 14 | private String[] name; 15 | private String desc; 16 | 17 | private MethodMatch(int opcode) { 18 | this.opcode = opcode; 19 | } 20 | 21 | public static MethodMatch of(int opcode) { 22 | return new MethodMatch(opcode); 23 | } 24 | 25 | public static MethodMatch create() { 26 | return new MethodMatch(-1); 27 | } 28 | 29 | public static MethodMatch invokeStatic() { 30 | return new MethodMatch(Opcodes.INVOKESTATIC); 31 | } 32 | 33 | public static MethodMatch invokeVirtual() { 34 | return new MethodMatch(Opcodes.INVOKEVIRTUAL); 35 | } 36 | 37 | public static MethodMatch invokeSpecial() { 38 | return new MethodMatch(Opcodes.INVOKESPECIAL); 39 | } 40 | 41 | public static MethodMatch invokeInterface() { 42 | return new MethodMatch(Opcodes.INVOKEINTERFACE); 43 | } 44 | 45 | @Override 46 | protected boolean test(MatchContext context) { 47 | return context.insn() instanceof MethodInsnNode methodInsn 48 | && (opcode == -1 || methodInsn.getOpcode() == opcode) 49 | && (owner == null || methodInsn.owner.equals(owner)) 50 | && (name == null || Arrays.binarySearch(name, methodInsn.name) >= 0) 51 | && (desc == null || methodInsn.desc.equals(desc)); 52 | } 53 | 54 | public MethodMatch owner(String owner) { 55 | this.owner = owner; 56 | return this; 57 | } 58 | 59 | public MethodMatch name(String... name) { 60 | this.name = name; 61 | return this; 62 | } 63 | 64 | public MethodMatch desc(String desc) { 65 | this.desc = desc; 66 | return this; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/impl/NewArrayMatch.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.asm.matcher.impl; 2 | 3 | import org.objectweb.asm.Opcodes; 4 | import org.objectweb.asm.tree.IntInsnNode; 5 | import uwu.narumi.deobfuscator.api.asm.matcher.Match; 6 | import uwu.narumi.deobfuscator.api.asm.matcher.MatchContext; 7 | 8 | public class NewArrayMatch extends Match { 9 | // https://docs.oracle.com/javase/specs/jvms/se9/html/jvms-6.html#jvms-6.5.newarray 10 | private final int arrayTypeCode; // Example: Opcodes.T_INT 11 | 12 | private NewArrayMatch(int arrayTypeCode) { 13 | this.arrayTypeCode = arrayTypeCode; 14 | } 15 | 16 | public static NewArrayMatch of(int operand) { 17 | return new NewArrayMatch(operand); 18 | } 19 | 20 | @Override 21 | protected boolean test(MatchContext context) { 22 | return context.insn().getOpcode() == Opcodes.NEWARRAY && ((IntInsnNode) context.insn()).operand == this.arrayTypeCode; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/impl/NumberMatch.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.asm.matcher.impl; 2 | 3 | import uwu.narumi.deobfuscator.api.asm.matcher.Match; 4 | import uwu.narumi.deobfuscator.api.asm.matcher.MatchContext; 5 | 6 | public class NumberMatch extends Match { 7 | private final Number number; 8 | 9 | private NumberMatch(Number number) { 10 | this.number = number; 11 | } 12 | 13 | public static NumberMatch of(Number number) { 14 | return new NumberMatch(number); 15 | } 16 | 17 | public static Match of() { 18 | return Match.of(ctx -> ctx.insn().isNumber()); 19 | } 20 | 21 | public static Match numDouble() { 22 | return Match.of(ctx -> ctx.insn().isDouble()); 23 | } 24 | 25 | public static Match numFloat() { 26 | return Match.of(ctx -> ctx.insn().isFloat()); 27 | } 28 | 29 | public static Match numInteger() { 30 | return Match.of(ctx -> ctx.insn().isInteger()); 31 | } 32 | 33 | public static Match numLong() { 34 | return Match.of(ctx -> ctx.insn().isLong()); 35 | } 36 | 37 | @Override 38 | protected boolean test(MatchContext context) { 39 | return context.insn().isNumber() && context.insn().asNumber().equals(this.number); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/impl/OpcodeMatch.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.asm.matcher.impl; 2 | 3 | import uwu.narumi.deobfuscator.api.asm.matcher.Match; 4 | import uwu.narumi.deobfuscator.api.asm.matcher.MatchContext; 5 | 6 | public class OpcodeMatch extends Match { 7 | private final int opcode; 8 | 9 | private OpcodeMatch(int opcode) { 10 | this.opcode = opcode; 11 | } 12 | 13 | public static OpcodeMatch of(int opcode) { 14 | return new OpcodeMatch(opcode); 15 | } 16 | 17 | @Override 18 | protected boolean test(MatchContext context) { 19 | return context.insn().getOpcode() == opcode; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/impl/RangeOpcodeMatch.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.asm.matcher.impl; 2 | 3 | import uwu.narumi.deobfuscator.api.asm.matcher.Match; 4 | import uwu.narumi.deobfuscator.api.asm.matcher.MatchContext; 5 | 6 | public class RangeOpcodeMatch extends Match { 7 | private final int start; 8 | private final int end; 9 | 10 | private RangeOpcodeMatch(int start, int end) { 11 | this.start = start; 12 | this.end = end; 13 | } 14 | 15 | public static RangeOpcodeMatch of(int startingOpcode, int endingOpcode) { 16 | return new RangeOpcodeMatch(startingOpcode, endingOpcode); 17 | } 18 | 19 | @Override 20 | protected boolean test(MatchContext context) { 21 | return context.insn().getOpcode() >= start && context.insn().getOpcode() <= end; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/impl/SkipMatch.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.asm.matcher.impl; 2 | 3 | import uwu.narumi.deobfuscator.api.asm.matcher.Match; 4 | import uwu.narumi.deobfuscator.api.asm.matcher.MatchContext; 5 | 6 | public class SkipMatch extends Match { 7 | 8 | private SkipMatch() {} 9 | 10 | public static SkipMatch create() { 11 | return new SkipMatch(); 12 | } 13 | 14 | @Override 15 | protected boolean test(MatchContext context) { 16 | return true; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/impl/StringMatch.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.asm.matcher.impl; 2 | 3 | import uwu.narumi.deobfuscator.api.asm.matcher.Match; 4 | import uwu.narumi.deobfuscator.api.asm.matcher.MatchContext; 5 | 6 | public class StringMatch extends Match { 7 | 8 | private final String string; 9 | 10 | private StringMatch(String string) { 11 | this.string = string; 12 | } 13 | 14 | public static StringMatch of(String string) { 15 | return new StringMatch(string); 16 | } 17 | 18 | public static Match of() { 19 | return Match.of(context -> context.insn().isString()); 20 | } 21 | 22 | @Override 23 | protected boolean test(MatchContext context) { 24 | return context.insn().isString() && context.insn().asString().equals(string); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/matcher/impl/VarLoadMatch.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.asm.matcher.impl; 2 | 3 | import org.jetbrains.annotations.Nullable; 4 | import org.objectweb.asm.tree.VarInsnNode; 5 | import uwu.narumi.deobfuscator.api.asm.matcher.Match; 6 | import uwu.narumi.deobfuscator.api.asm.matcher.MatchContext; 7 | 8 | public class VarLoadMatch extends Match { 9 | private Match localStoreMatch = null; 10 | 11 | private VarLoadMatch() { 12 | } 13 | 14 | public static VarLoadMatch of() { 15 | return new VarLoadMatch(); 16 | } 17 | 18 | /** 19 | * Match local variable store instruction of this variable 20 | */ 21 | public VarLoadMatch localStoreMatch(@Nullable Match match) { 22 | this.localStoreMatch = match; 23 | return this; 24 | } 25 | 26 | @Override 27 | protected boolean test(MatchContext context) { 28 | boolean matches = context.insnContext().insn().isVarLoad(); 29 | 30 | // Match local variable store instruction 31 | if (matches && localStoreMatch != null) { 32 | VarInsnNode varInsn = (VarInsnNode) context.insnContext().insn(); 33 | 34 | matches = FrameMatch.localVariable(varInsn.var, localStoreMatch).matchAndMerge(context.insnContext(), context); 35 | } 36 | 37 | return matches; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/remapper/NamePattern.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.asm.remapper; 2 | 3 | import java.util.function.Predicate; 4 | import java.util.regex.Pattern; 5 | 6 | /** 7 | * A collection of common name patterns. 8 | */ 9 | public class NamePattern { 10 | // iIIIiIIlliiIIlIii 11 | public static final Predicate III = Pattern.compile("^[lIi]*$").asMatchPredicate(); 12 | public static final Predicate WEB_EXPLOIT = s -> s.contains(""); 13 | } 14 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/asm/remapper/NamesRemapper.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.asm.remapper; 2 | 3 | import org.objectweb.asm.commons.Remapper; 4 | import uwu.narumi.deobfuscator.api.asm.FieldRef; 5 | import uwu.narumi.deobfuscator.api.asm.MethodRef; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | public class NamesRemapper extends Remapper { 11 | public final Map classMappings = new HashMap<>(); // old class internal name -> new class internal name 12 | public final Map methodMappings = new HashMap<>(); // old method name -> new method name 13 | public final Map fieldMappings = new HashMap<>(); // old field name -> new field name 14 | 15 | @Override 16 | public String mapMethodName(String owner, String name, String descriptor) { 17 | String newName = this.methodMappings.get(new MethodRef(owner, name, descriptor)); 18 | return newName != null ? newName : name; 19 | } 20 | 21 | @Override 22 | public String mapFieldName(String owner, String name, String descriptor) { 23 | String newName = this.fieldMappings.get(new FieldRef(owner, name, descriptor)); 24 | return newName != null ? newName : name; 25 | } 26 | 27 | @Override 28 | public String map(String internalName) { 29 | String newName = this.classMappings.get(internalName); 30 | return newName != null ? newName : internalName; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/classpath/ClassInfoStorage.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.classpath; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | import org.jetbrains.annotations.Nullable; 5 | import org.objectweb.asm.tree.ClassNode; 6 | import software.coley.cafedude.InvalidClassException; 7 | import uwu.narumi.deobfuscator.api.helper.ClassHelper; 8 | import uwu.narumi.deobfuscator.api.helper.FileHelper; 9 | 10 | import java.nio.file.Path; 11 | import java.util.Collection; 12 | import java.util.Map; 13 | import java.util.concurrent.ConcurrentHashMap; 14 | 15 | /** 16 | * Class storage that holds only information about classes, not the bytecode. 17 | */ 18 | public class ClassInfoStorage implements ClassProvider { 19 | private final Map compiledClasses = new ConcurrentHashMap<>(); 20 | private final Map files = new ConcurrentHashMap<>(); 21 | 22 | // ClassNode without code, only info (like name, superName, interfaces, etc.) 23 | private final Map classesInfo = new ConcurrentHashMap<>(); 24 | 25 | /** 26 | * Adds jar to class storage 27 | * 28 | * @param jarPath Jar path 29 | */ 30 | public void addJar(@NotNull Path jarPath) { 31 | FileHelper.loadFilesFromZip(jarPath, (classPath, bytes) -> { 32 | if (!ClassHelper.isClass(classPath, bytes)) { 33 | files.putIfAbsent(classPath, bytes); 34 | return; 35 | } 36 | 37 | try { 38 | // Fix class bytes 39 | bytes = ClassHelper.fixClass(bytes); 40 | 41 | addRawClass(bytes); 42 | } catch (InvalidClassException e) { 43 | throw new RuntimeException(e); 44 | } 45 | }); 46 | } 47 | 48 | public void addRawClass(byte[] bytes) { 49 | ClassNode classNode = ClassHelper.loadClassInfo(bytes); 50 | String className = classNode.name; 51 | 52 | // Add class to class storage 53 | compiledClasses.putIfAbsent(className, bytes); 54 | classesInfo.putIfAbsent(className, classNode); 55 | } 56 | 57 | @Override 58 | public byte @Nullable [] getClass(String name) { 59 | return compiledClasses.get(name); 60 | } 61 | 62 | @Override 63 | public byte @Nullable [] getFile(String path) { 64 | return files.get(path); 65 | } 66 | 67 | @Override 68 | public @Nullable ClassNode getClassInfo(String name) { 69 | return classesInfo.get(name); 70 | } 71 | 72 | @Override 73 | public Collection getLoadedClasses() { 74 | return compiledClasses.keySet(); 75 | } 76 | 77 | public Map compiledClasses() { 78 | return compiledClasses; 79 | } 80 | 81 | public Map files() { 82 | return files; 83 | } 84 | 85 | public Map classesInfo() { 86 | return classesInfo; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/classpath/ClassProvider.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.classpath; 2 | 3 | import org.jetbrains.annotations.Nullable; 4 | import org.objectweb.asm.tree.ClassNode; 5 | 6 | import java.util.Collection; 7 | 8 | public interface ClassProvider { 9 | /** 10 | * Gets class bytes by internal name 11 | * 12 | * @param name Internal name of class 13 | * @return Class bytes 14 | */ 15 | byte @Nullable [] getClass(String name); 16 | 17 | /** 18 | * Gets file bytes by name 19 | * 20 | * @param path File path 21 | * @return File bytes 22 | */ 23 | byte @Nullable [] getFile(String path); 24 | 25 | /** 26 | * Gets class node that holds only the class information. It is not guaranteed that the class holds code. 27 | * 28 | * @param name Internal name of class 29 | * @return Class node 30 | */ 31 | @Nullable 32 | ClassNode getClassInfo(String name); 33 | 34 | /** 35 | * Gets all classes in the provider. 36 | */ 37 | Collection getLoadedClasses(); 38 | } 39 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/classpath/CombinedClassProvider.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.classpath; 2 | 3 | import org.jetbrains.annotations.Nullable; 4 | import org.objectweb.asm.tree.ClassNode; 5 | 6 | import java.util.Collection; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | 10 | public class CombinedClassProvider implements ClassProvider { 11 | private final ClassProvider[] classProviders; 12 | 13 | public CombinedClassProvider(ClassProvider... classProviders) { 14 | this.classProviders = classProviders; 15 | } 16 | 17 | @Override 18 | public byte @Nullable [] getClass(String name) { 19 | for (ClassProvider classProvider : this.classProviders) { 20 | byte[] bytes = classProvider.getClass(name); 21 | if (bytes != null) return bytes; 22 | } 23 | return null; 24 | } 25 | 26 | @Override 27 | public byte @Nullable [] getFile(String path) { 28 | for (ClassProvider classProvider : this.classProviders) { 29 | byte[] bytes = classProvider.getFile(path); 30 | if (bytes != null) return bytes; 31 | } 32 | return null; 33 | } 34 | 35 | @Override 36 | public @Nullable ClassNode getClassInfo(String name) { 37 | for (ClassProvider classProvider : this.classProviders) { 38 | ClassNode classInfo = classProvider.getClassInfo(name); 39 | if (classInfo != null) return classInfo; 40 | } 41 | return null; 42 | } 43 | 44 | @Override 45 | public Collection getLoadedClasses() { 46 | Set classes = new HashSet<>(); 47 | for (ClassProvider classProvider : this.classProviders) { 48 | classes.addAll(classProvider.getLoadedClasses()); 49 | } 50 | return classes; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/classpath/InheritanceClassWriter.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.classpath; 2 | 3 | import org.objectweb.asm.ClassWriter; 4 | import uwu.narumi.deobfuscator.api.inheritance.InheritanceGraph; 5 | 6 | /** 7 | * A {@link ClassWriter} that uses a {@link InheritanceGraph} to determine the common superclass of two classes. 8 | */ 9 | public class InheritanceClassWriter extends ClassWriter { 10 | private final InheritanceGraph inheritanceGraph; 11 | 12 | public InheritanceClassWriter(int flags, InheritanceGraph inheritanceGraph) { 13 | super(flags); 14 | this.inheritanceGraph = inheritanceGraph; 15 | } 16 | 17 | @Override 18 | protected String getCommonSuperClass(String first, String second) { 19 | return this.inheritanceGraph.getCommon(first, second); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/classpath/JvmClassProvider.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.classpath; 2 | 3 | import org.apache.logging.log4j.LogManager; 4 | import org.apache.logging.log4j.Logger; 5 | import org.jetbrains.annotations.Nullable; 6 | import org.objectweb.asm.tree.ClassNode; 7 | import uwu.narumi.deobfuscator.api.helper.ClassHelper; 8 | 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | import java.util.Collection; 12 | import java.util.List; 13 | import java.util.Map; 14 | import java.util.concurrent.ConcurrentHashMap; 15 | 16 | /** 17 | * Classpath that fetches default JVM classes 18 | */ 19 | public class JvmClassProvider implements ClassProvider { 20 | private static final Logger LOGGER = LogManager.getLogger(); 21 | 22 | public static final JvmClassProvider INSTANCE = new JvmClassProvider(); 23 | 24 | private final Map classesCache = new ConcurrentHashMap<>(); 25 | private final Map classInfoCache = new ConcurrentHashMap<>(); 26 | 27 | private JvmClassProvider() { 28 | } 29 | 30 | @Override 31 | public byte @Nullable [] getClass(String name) { 32 | if (classesCache.containsKey(name)) { 33 | return classesCache.get(name); 34 | } 35 | 36 | // Try to find it in classloader 37 | byte[] value = null; 38 | try (InputStream in = ClassLoader.getSystemResourceAsStream(name + ".class")) { 39 | if (in != null) { 40 | value = in.readAllBytes(); 41 | } 42 | } catch (IOException ex) { 43 | LOGGER.error("Failed to fetch runtime bytecode of class: {}", name, ex); 44 | } 45 | 46 | if (value == null) return null; 47 | 48 | // Cache it! 49 | classesCache.put(name, value); 50 | 51 | return value; 52 | } 53 | 54 | @Override 55 | public byte @Nullable [] getFile(String path) { 56 | // JVM classpath doesn't have files 57 | return null; 58 | } 59 | 60 | @Override 61 | public @Nullable ClassNode getClassInfo(String name) { 62 | if (classInfoCache.containsKey(name)) { 63 | return classInfoCache.get(name); 64 | } 65 | 66 | byte[] bytes = getClass(name); 67 | if (bytes == null) return null; 68 | 69 | ClassNode classNode = ClassHelper.loadClassInfo(bytes); 70 | 71 | // Cache it! 72 | classInfoCache.put(name, classNode); 73 | 74 | return classNode; 75 | } 76 | 77 | @Override 78 | public Collection getLoadedClasses() { 79 | // We cannot determine all classes in JVM classpath 80 | return List.of(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/data/Pair.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.data; 2 | 3 | import java.util.function.BiConsumer; 4 | 5 | public record Pair(A key, B value) { 6 | public static Pair of(X key, Y value) { 7 | return new Pair<>(key, value); 8 | } 9 | 10 | public void consume(BiConsumer consumer) { 11 | consumer.accept(key(), value()); 12 | } 13 | 14 | public boolean isPresent() { 15 | return key != null && value != null; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/data/TriConsumer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.data; 2 | 3 | import java.util.Objects; 4 | 5 | @FunctionalInterface 6 | public interface TriConsumer { 7 | void accept(A first, B second, C third); 8 | 9 | default TriConsumer andThen(TriConsumer after) { 10 | Objects.requireNonNull(after); 11 | return (first, second, third) -> { 12 | accept(first, second, third); 13 | after.accept(first, second, third); 14 | }; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/data/TriFunction.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.data; 2 | 3 | import java.util.Objects; 4 | import java.util.function.Function; 5 | 6 | @FunctionalInterface 7 | public interface TriFunction { 8 | R apply(A first, B second, C third); 9 | 10 | default TriFunction andThen(Function after) { 11 | Objects.requireNonNull(after); 12 | return (A first, B second, C third) -> after.apply(apply(first, second, third)); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/exception/TransformerException.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.exception; 2 | 3 | public class TransformerException extends RuntimeException { 4 | 5 | public TransformerException() {} 6 | 7 | public TransformerException(String message) { 8 | super(message); 9 | } 10 | 11 | public TransformerException(String message, Throwable cause) { 12 | super(message, cause); 13 | } 14 | 15 | public TransformerException(Throwable cause) { 16 | super(cause); 17 | } 18 | 19 | public TransformerException( 20 | String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { 21 | super(message, cause, enableSuppression, writableStackTrace); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/execution/ClasspathDataSupplier.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.execution; 2 | 3 | import dev.xdark.ssvm.classloading.SupplyingClassLoaderInstaller; 4 | import uwu.narumi.deobfuscator.api.classpath.ClassProvider; 5 | 6 | public class ClasspathDataSupplier implements SupplyingClassLoaderInstaller.DataSupplier { 7 | 8 | private final ClassProvider classpath; 9 | 10 | public ClasspathDataSupplier(ClassProvider classpath) { 11 | this.classpath = classpath; 12 | } 13 | 14 | @Override 15 | public byte[] getClass(String className) { 16 | return classpath.getClass(className.replace('.', '/')); 17 | } 18 | 19 | @Override 20 | public byte[] getResource(String resourcePath) { 21 | return classpath.getFile(resourcePath); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/helper/FileHelper.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.helper; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.FileVisitResult; 5 | import java.nio.file.Files; 6 | import java.nio.file.Path; 7 | import java.nio.file.SimpleFileVisitor; 8 | import java.nio.file.attribute.BasicFileAttributes; 9 | import java.util.ArrayList; 10 | import java.util.Iterator; 11 | import java.util.List; 12 | import java.util.concurrent.CompletableFuture; 13 | import java.util.concurrent.ExecutorService; 14 | import java.util.concurrent.Executors; 15 | import java.util.function.BiConsumer; 16 | import java.util.jar.JarEntry; 17 | import java.util.jar.JarFile; 18 | 19 | import org.apache.logging.log4j.LogManager; 20 | import org.apache.logging.log4j.Logger; 21 | 22 | public final class FileHelper { 23 | 24 | private static final Logger LOGGER = LogManager.getLogger(FileHelper.class); 25 | 26 | private FileHelper() { 27 | } 28 | 29 | /** 30 | * Load all files from a zip file asynchronously 31 | * 32 | * @param path Path to the zip file 33 | * @param consumer Consumer that accepts the file path and the file bytes. This method is called asynchronously 34 | */ 35 | public static void loadFilesFromZip(Path path, BiConsumer consumer) { 36 | try (JarFile zipFile = new JarFile(path.toFile())) { 37 | ExecutorService executorService = Executors.newFixedThreadPool(5); 38 | List> futures = new ArrayList<>(); 39 | 40 | Iterator it = zipFile.entries().asIterator(); 41 | 42 | while (it.hasNext()) { 43 | JarEntry zipEntry = it.next(); 44 | 45 | String name = zipEntry.getName(); 46 | byte[] bytes = zipFile.getInputStream(zipEntry).readAllBytes(); 47 | 48 | // Skip directories 49 | boolean isDirectory = zipEntry.isDirectory() && bytes.length == 0; 50 | if (isDirectory) continue; 51 | 52 | CompletableFuture future = CompletableFuture.runAsync(() -> { 53 | try { 54 | consumer.accept(name, bytes); 55 | } catch (Exception e) { 56 | LOGGER.error("Could not load ZipEntry: {}", zipEntry.getName(), e); 57 | } 58 | }, executorService); 59 | futures.add(future); 60 | } 61 | 62 | // Wait for all futures to complete 63 | CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join(); 64 | executorService.shutdown(); 65 | } catch (Exception e) { 66 | LOGGER.error("Could not load file: {}", path); 67 | throw new RuntimeException(e); 68 | } 69 | } 70 | 71 | public static void deleteDirectory(Path dir) { 72 | try { 73 | if (Files.notExists(dir)) 74 | return; 75 | 76 | Files.walkFileTree(dir, new SimpleFileVisitor<>() { 77 | @Override 78 | public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { 79 | Files.delete(dir); 80 | return FileVisitResult.CONTINUE; 81 | } 82 | 83 | @Override 84 | public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { 85 | Files.delete(file); 86 | return FileVisitResult.CONTINUE; 87 | } 88 | }); 89 | } catch (Exception e) { 90 | throw new RuntimeException("Can't delete directory", e); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/inheritance/Streams.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.inheritance; 2 | 3 | import java.util.ArrayDeque; 4 | import java.util.Collections; 5 | import java.util.Deque; 6 | import java.util.HashSet; 7 | import java.util.Iterator; 8 | import java.util.Set; 9 | import java.util.Spliterator; 10 | import java.util.Spliterators; 11 | import java.util.function.Consumer; 12 | import java.util.function.Function; 13 | import java.util.stream.Stream; 14 | import java.util.stream.StreamSupport; 15 | 16 | public class Streams { 17 | public static Stream recurseWithoutCycles(T seed, Function> flatMap) { 18 | Deque> vertices = new ArrayDeque<>(); 19 | Set visited = new HashSet<>(); 20 | vertices.push(Collections.singletonList(seed).iterator()); 21 | return StreamSupport.stream(new Spliterators.AbstractSpliterator<>(Long.MAX_VALUE, Spliterator.IMMUTABLE | Spliterator.NONNULL) { 22 | @Override 23 | public boolean tryAdvance(Consumer action) { 24 | while (true) { 25 | Iterator iterator = vertices.peek(); 26 | if (iterator == null) { 27 | return false; 28 | } 29 | if (!iterator.hasNext()) { 30 | vertices.poll(); 31 | continue; 32 | } 33 | T vertex = iterator.next(); 34 | if (visited.add(vertex)) { 35 | action.accept(vertex); 36 | vertices.push(flatMap.apply(vertex).iterator()); 37 | return true; 38 | } 39 | } 40 | } 41 | }, false); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/transformer/ComposedTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.transformer; 2 | 3 | import java.util.List; 4 | import java.util.function.Supplier; 5 | 6 | import org.jetbrains.annotations.Nullable; 7 | 8 | public class ComposedTransformer extends Transformer { 9 | 10 | private final List> transformers; 11 | 12 | @SafeVarargs 13 | public ComposedTransformer(Supplier<@Nullable Transformer>... transformers) { 14 | this.transformers = List.of(transformers); 15 | } 16 | 17 | @SafeVarargs 18 | public ComposedTransformer(boolean rerunOnChange, Supplier... transformers) { 19 | this.transformers = List.of(transformers); 20 | this.rerunOnChange = rerunOnChange; 21 | } 22 | 23 | @Override 24 | protected void transform() { 25 | transformers.forEach(transformerSupplier -> { 26 | int changesCount = Transformer.transform(transformerSupplier, scope(), context()); 27 | this.changes.addAndGet(changesCount); 28 | }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /deobfuscator-api/src/main/java/uwu/narumi/deobfuscator/api/transformer/VersionedComposedTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.api.transformer; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | import java.util.function.Supplier; 6 | 7 | public abstract class VersionedComposedTransformer extends Transformer { 8 | 9 | private final String version; 10 | 11 | public VersionedComposedTransformer(String version) { 12 | this.version = version; 13 | } 14 | 15 | @Override 16 | protected void transform() { 17 | Map>> transformers = transformersByVersions(); 18 | if (!transformers.containsKey(version)) { 19 | throw new IllegalArgumentException(String.format("Version '%s' not found!", version)); 20 | } 21 | 22 | transformers 23 | .get(version) 24 | .forEach(transformer -> this.changes.addAndGet(Transformer.transform(transformer, scope(), context()))); 25 | } 26 | 27 | public abstract Map>> transformersByVersions(); 28 | } 29 | -------------------------------------------------------------------------------- /deobfuscator-impl/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | Diobfuscator 7 | uwu.narumi 8 | 2.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | deobfuscator-impl 13 | 14 | 15 | 17 16 | 17 17 | UTF-8 18 | 19 | 20 | 21 | 22 | 23 | 24 | org.apache.maven.plugins 25 | maven-surefire-plugin 26 | 3.4.0 27 | 28 | 29 | org.junit.jupiter:junit-jupiter 30 | false 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | deobfuscator-api 39 | uwu.narumi 40 | 2.0-SNAPSHOT 41 | 42 | 43 | 44 | deobfuscator-transformers 45 | uwu.narumi 46 | 2.0-SNAPSHOT 47 | 48 | 49 | 50 | 51 | org.junit.jupiter 52 | junit-jupiter 53 | 5.11.0 54 | test 55 | 56 | 57 | 58 | org.vineflower 59 | vineflower 60 | 1.10.1 61 | test 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /deobfuscator-impl/src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /deobfuscator-impl/src/test/java/Bootstrap.java: -------------------------------------------------------------------------------- 1 | import java.nio.file.Path; 2 | import org.objectweb.asm.ClassWriter; 3 | import uwu.narumi.deobfuscator.Deobfuscator; 4 | import uwu.narumi.deobfuscator.api.context.DeobfuscatorOptions; 5 | import uwu.narumi.deobfuscator.core.other.composed.general.ComposedGeneralFlowTransformer; 6 | 7 | public class Bootstrap { 8 | 9 | public static void main(String[] args) { 10 | Deobfuscator.from( 11 | DeobfuscatorOptions.builder() 12 | .inputJar(Path.of("work", "obf-test.jar")) // Specify your input jar here 13 | //.libraries(Path.of("work", "libs")) // Specify your libraries here if needed 14 | .transformers( 15 | // Pick your transformers here 16 | () -> new ComposedGeneralFlowTransformer() 17 | ) 18 | .continueOnError() 19 | .classWriterFlags(ClassWriter.COMPUTE_FRAMES) 20 | .build() 21 | ).start(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /deobfuscator-impl/src/test/java/uwu/narumi/deobfuscator/base/AssertingResultSaver.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.base; 2 | 3 | import org.jetbrains.java.decompiler.main.extern.IResultSaver; 4 | 5 | import java.io.IOException; 6 | import java.nio.file.Files; 7 | import java.nio.file.Path; 8 | import java.util.jar.Manifest; 9 | 10 | import static org.junit.jupiter.api.Assertions.assertEquals; 11 | 12 | /** 13 | * Result saver that will assert against previous decompiled code 14 | */ 15 | public class AssertingResultSaver implements IResultSaver { 16 | 17 | private final Path outputDir; 18 | 19 | private boolean savedContent = false; 20 | 21 | public AssertingResultSaver(Path outputDir) { 22 | this.outputDir = outputDir; 23 | } 24 | 25 | @Override 26 | public void saveFolder(String path) { 27 | // Nothing 28 | } 29 | 30 | @Override 31 | public void copyFile(String source, String path, String entryName) { 32 | // Nothing 33 | } 34 | 35 | /** 36 | * Assert decompiled code 37 | */ 38 | @Override 39 | public void saveClassFile(String path, String qualifiedName, String entryName, String content, int[] mapping) { 40 | // Replace CRLF with LF 41 | content = content.replace("\r\n", "\n"); 42 | 43 | // Remove file extension 44 | entryName = entryName.substring(0, entryName.lastIndexOf('.')); 45 | 46 | Path saveTo = this.outputDir.resolve(entryName + ".dec"); 47 | 48 | try { 49 | if (Files.exists(saveTo)) { 50 | // Assert decompiled code 51 | String oldCode = Files.readString(saveTo); 52 | // Replace CRLF with LF 53 | oldCode = oldCode.replace("\r\n", "\n"); 54 | 55 | assertEquals(oldCode, content); 56 | } else { 57 | // Save content 58 | Files.createDirectories(saveTo.getParent()); 59 | Files.writeString(saveTo, content); 60 | 61 | // Mark that result saver saved content instead of asserting it 62 | savedContent = true; 63 | } 64 | } catch (IOException e) { 65 | throw new RuntimeException(e); 66 | } 67 | } 68 | 69 | @Override 70 | public void createArchive(String path, String archiveName, Manifest manifest) { 71 | // Nothing 72 | } 73 | 74 | @Override 75 | public void saveDirEntry(String path, String archiveName, String entryName) { 76 | // Nothing 77 | } 78 | 79 | @Override 80 | public void copyEntry(String source, String path, String archiveName, String entry) { 81 | // Nothing 82 | } 83 | 84 | @Override 85 | public void saveClassEntry(String path, String archiveName, String qualifiedName, String entryName, String content) { 86 | // Nothing 87 | } 88 | 89 | @Override 90 | public void closeArchive(String path, String archiveName) { 91 | // Nothing 92 | } 93 | 94 | public boolean savedContent() { 95 | return savedContent; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /deobfuscator-impl/src/test/java/uwu/narumi/deobfuscator/base/SingleClassContextSource.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.base; 2 | 3 | import java.io.FileInputStream; 4 | import java.nio.file.Path; 5 | import org.jetbrains.java.decompiler.main.extern.IContextSource; 6 | import org.jetbrains.java.decompiler.main.extern.IResultSaver; 7 | 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | import java.util.List; 11 | 12 | public class SingleClassContextSource implements IContextSource { 13 | private final Path file; 14 | private final String pathInJar; 15 | 16 | /** 17 | * @param file Path to .class file 18 | * @param pathInJar Relative path to .class file as if it were in .jar 19 | */ 20 | public SingleClassContextSource(Path file, String pathInJar) { 21 | this.file = file; 22 | this.pathInJar = pathInJar; 23 | } 24 | 25 | @Override 26 | public String getName() { 27 | return "file " + this.file; 28 | } 29 | 30 | @Override 31 | public Entries getEntries() { 32 | return new Entries(List.of(Entry.atBase(this.pathInJar)), List.of(), List.of()); 33 | } 34 | 35 | @Override 36 | public InputStream getInputStream(String resource) throws IOException { 37 | return new FileInputStream(this.file.toFile()); 38 | } 39 | 40 | @Override 41 | public IOutputSink createOutputSink(IResultSaver saver) { 42 | return new IOutputSink() { 43 | @Override 44 | public void begin() { 45 | 46 | } 47 | 48 | @Override 49 | public void acceptClass(String qualifiedName, String fileName, String content, int[] mapping) { 50 | saver.saveClassFile("", qualifiedName, pathInJar, content, mapping); 51 | } 52 | 53 | @Override 54 | public void acceptDirectory(String directory) { 55 | 56 | } 57 | 58 | @Override 59 | public void acceptOther(String path) { 60 | 61 | } 62 | 63 | @Override 64 | public void close() throws IOException { 65 | 66 | } 67 | }; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /deobfuscator-impl/src/test/java/uwu/narumi/deobfuscator/transformer/TestSandboxSecurityTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.transformer; 2 | 3 | import dev.xdark.ssvm.execution.VMException; 4 | import dev.xdark.ssvm.mirror.type.InstanceClass; 5 | import uwu.narumi.deobfuscator.api.execution.SandBox; 6 | import uwu.narumi.deobfuscator.api.transformer.Transformer; 7 | 8 | import static org.junit.jupiter.api.Assertions.assertThrows; 9 | 10 | public class TestSandboxSecurityTransformer extends Transformer { 11 | @Override 12 | protected void transform() throws Exception { 13 | SandBox sandBox = context().getSandBox(); 14 | InstanceClass clazz = sandBox.getHelper().loadClass("sandbox.TestSandboxSecurity"); 15 | 16 | assertThrows(VMException.class, () -> { 17 | sandBox.getInvocationUtil().invokeInt( 18 | clazz.getMethod("test", "()I") 19 | ); 20 | }); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /deobfuscator-impl/src/test/resources/junit-platform.properties: -------------------------------------------------------------------------------- 1 | junit.jupiter.execution.parallel.enabled = true 2 | junit.jupiter.execution.parallel.mode.default = concurrent 3 | -------------------------------------------------------------------------------- /deobfuscator-transformers/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | Diobfuscator 7 | uwu.narumi 8 | 2.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | deobfuscator-transformers 13 | 14 | 15 | 17 16 | 17 17 | UTF-8 18 | 19 | 20 | 21 | 22 | deobfuscator-api 23 | uwu.narumi 24 | 2.0-SNAPSHOT 25 | provided 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/composed/ComposedAllatoriTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.composed; 2 | 3 | import uwu.narumi.deobfuscator.api.transformer.ComposedTransformer; 4 | import uwu.narumi.deobfuscator.core.other.impl.allatori.AllatoriStringTransformer; 5 | import uwu.narumi.deobfuscator.core.other.impl.universal.UniversalNumberTransformer; 6 | 7 | public class ComposedAllatoriTransformer extends ComposedTransformer { 8 | 9 | public ComposedAllatoriTransformer(boolean strongString) { 10 | super( 11 | UniversalNumberTransformer::new, 12 | () -> new AllatoriStringTransformer(strongString) 13 | ); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/composed/ComposedGruntTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.composed; 2 | 3 | import uwu.narumi.deobfuscator.api.transformer.ComposedTransformer; 4 | import uwu.narumi.deobfuscator.core.other.composed.general.ComposedGeneralFlowTransformer; 5 | import uwu.narumi.deobfuscator.core.other.impl.pool.InlineStaticFieldTransformer; 6 | import uwu.narumi.deobfuscator.core.other.impl.universal.AccessRepairTransformer; 7 | import uwu.narumi.deobfuscator.core.other.impl.universal.InlinePureFunctionsTransformer; 8 | 9 | /** 10 | * https://github.com/SpartanB312/Grunt 11 | */ 12 | // TODO: String encryption 13 | public class ComposedGruntTransformer extends ComposedTransformer { 14 | public ComposedGruntTransformer() { 15 | super( 16 | // Repair access 17 | AccessRepairTransformer::new, 18 | () -> new ComposedTransformer(true, 19 | // Fix flow 20 | ComposedGeneralFlowTransformer::new, 21 | InlineStaticFieldTransformer::new, 22 | // Inline pure functions 23 | InlinePureFunctionsTransformer::new 24 | ) 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/composed/ComposedHP888Transformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.composed; 2 | 3 | import org.jetbrains.annotations.Nullable; 4 | import uwu.narumi.deobfuscator.api.transformer.ComposedTransformer; 5 | import uwu.narumi.deobfuscator.core.other.impl.clean.LocalVariableNamesCleanTransformer; 6 | import uwu.narumi.deobfuscator.core.other.impl.hp888.HP888PackerTransformer; 7 | import uwu.narumi.deobfuscator.core.other.impl.hp888.HP888StringTransformer; 8 | import uwu.narumi.deobfuscator.core.other.impl.universal.AccessRepairTransformer; 9 | import uwu.narumi.deobfuscator.core.other.impl.universal.RecoverSyntheticsTransformer; 10 | import uwu.narumi.deobfuscator.core.other.impl.universal.UniversalNumberTransformer; 11 | 12 | /** 13 | * Transformers for custom-made obfuscator by HP888. Used in projects like https://safemc.pl/ 14 | */ 15 | public class ComposedHP888Transformer extends ComposedTransformer { 16 | 17 | public ComposedHP888Transformer() { 18 | this(null); 19 | } 20 | 21 | public ComposedHP888Transformer(@Nullable String encryptedClassFilesSuffix) { 22 | super( 23 | // Decrypt strings 24 | HP888StringTransformer::new, 25 | 26 | () -> encryptedClassFilesSuffix != null ? new ComposedTransformer( 27 | // Unpack encrypted classes 28 | () -> new HP888PackerTransformer(encryptedClassFilesSuffix), 29 | // Decrypt strings in unpacked classes 30 | HP888StringTransformer::new 31 | ) : null, 32 | 33 | // Cleanup 34 | UniversalNumberTransformer::new, 35 | AccessRepairTransformer::new, 36 | LocalVariableNamesCleanTransformer::new, 37 | RecoverSyntheticsTransformer::new 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/composed/ComposedSuperblaubeereTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.composed; 2 | 3 | import uwu.narumi.deobfuscator.api.transformer.ComposedTransformer; 4 | import uwu.narumi.deobfuscator.core.other.composed.general.ComposedPeepholeCleanTransformer; 5 | import uwu.narumi.deobfuscator.core.other.impl.clean.LocalVariableNamesCleanTransformer; 6 | import uwu.narumi.deobfuscator.core.other.impl.pool.InlineLocalVariablesTransformer; 7 | import uwu.narumi.deobfuscator.core.other.impl.universal.InlinePureFunctionsTransformer; 8 | import uwu.narumi.deobfuscator.core.other.impl.universal.pool.UniversalNumberPoolTransformer; 9 | import uwu.narumi.deobfuscator.core.other.impl.universal.pool.UniversalStringPoolTransformer; 10 | import uwu.narumi.deobfuscator.core.other.impl.sb27.SuperblaubeereStringTransformer; 11 | import uwu.narumi.deobfuscator.core.other.impl.universal.UniversalFlowTransformer; 12 | 13 | /** 14 | * https://github.com/superblaubeere27/obfuscator 15 | */ 16 | public class ComposedSuperblaubeereTransformer extends ComposedTransformer { 17 | public ComposedSuperblaubeereTransformer() { 18 | super( 19 | // Remove var names as they are obfuscated and names are useless 20 | LocalVariableNamesCleanTransformer::new, 21 | 22 | // Fix flow 23 | UniversalFlowTransformer::new, 24 | 25 | // Inline number pools 26 | UniversalNumberPoolTransformer::new, 27 | // Decrypt strings 28 | SuperblaubeereStringTransformer::new, 29 | UniversalStringPoolTransformer::new, 30 | 31 | InlinePureFunctionsTransformer::new, 32 | InlineLocalVariablesTransformer::new, 33 | 34 | // Cleanup 35 | ComposedPeepholeCleanTransformer::new 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/composed/ComposedUnknownObf1Transformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.composed; 2 | 3 | import uwu.narumi.deobfuscator.api.transformer.ComposedTransformer; 4 | import uwu.narumi.deobfuscator.core.other.composed.general.ComposedGeneralFlowTransformer; 5 | import uwu.narumi.deobfuscator.core.other.impl.clean.LocalVariableNamesCleanTransformer; 6 | import uwu.narumi.deobfuscator.core.other.impl.unknown.obf1.UnknownObf1_StringByteArrayTransformer; 7 | 8 | public class ComposedUnknownObf1Transformer extends ComposedTransformer { 9 | public ComposedUnknownObf1Transformer() { 10 | super( 11 | // Remove local variables names 12 | LocalVariableNamesCleanTransformer::new, 13 | 14 | // Fix flow 15 | ComposedGeneralFlowTransformer::new, 16 | 17 | // Decrypt strings 18 | UnknownObf1_StringByteArrayTransformer::new 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/composed/ComposedZelixTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.composed; 2 | 3 | import uwu.narumi.deobfuscator.api.transformer.ComposedTransformer; 4 | import uwu.narumi.deobfuscator.core.other.composed.general.ComposedPeepholeCleanTransformer; 5 | import uwu.narumi.deobfuscator.core.other.impl.clean.peephole.JsrInlinerTransformer; 6 | import uwu.narumi.deobfuscator.core.other.impl.pool.InlineStaticFieldTransformer; 7 | import uwu.narumi.deobfuscator.core.other.impl.universal.RecoverSyntheticsTransformer; 8 | import uwu.narumi.deobfuscator.core.other.impl.universal.UniversalNumberTransformer; 9 | import uwu.narumi.deobfuscator.core.other.impl.zkm.ZelixLongEncryptionMPCTransformer; 10 | import uwu.narumi.deobfuscator.core.other.impl.zkm.ZelixParametersTransformer; 11 | import uwu.narumi.deobfuscator.core.other.impl.zkm.ZelixUselessTryCatchRemoverTransformer; 12 | 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | /** 17 | * https://www.zelix.com/ 18 | */ 19 | // TODO: Work in progress... 20 | public class ComposedZelixTransformer extends ComposedTransformer { 21 | public ComposedZelixTransformer() { 22 | this(false); 23 | } 24 | 25 | public ComposedZelixTransformer(boolean experimental) { 26 | this(experimental, new HashMap<>()); 27 | } 28 | 29 | public ComposedZelixTransformer(boolean experimental, Map classInitializationOrder) { 30 | super( 31 | // Initial cleanup 32 | JsrInlinerTransformer::new, 33 | RecoverSyntheticsTransformer::new, 34 | 35 | // Fixes flow a bit 36 | ZelixUselessTryCatchRemoverTransformer::new, 37 | 38 | // Decompose method parameters 39 | () -> experimental ? new ZelixParametersTransformer() : null, 40 | 41 | // Decrypt longs 42 | () -> new ZelixLongEncryptionMPCTransformer(classInitializationOrder), 43 | InlineStaticFieldTransformer::new, 44 | UniversalNumberTransformer::new, 45 | 46 | // Cleanup 47 | ComposedPeepholeCleanTransformer::new 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/composed/Composed_qProtectTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.composed; 2 | 3 | import uwu.narumi.deobfuscator.api.transformer.ComposedTransformer; 4 | import uwu.narumi.deobfuscator.core.other.composed.general.ComposedPeepholeCleanTransformer; 5 | import uwu.narumi.deobfuscator.core.other.impl.clean.LocalVariableNamesCleanTransformer; 6 | import uwu.narumi.deobfuscator.core.other.impl.pool.InlineLocalVariablesTransformer; 7 | import uwu.narumi.deobfuscator.core.other.impl.pool.InlineStaticFieldTransformer; 8 | import uwu.narumi.deobfuscator.core.other.impl.qprotect.qProtectAESStringEncryptionTransformer; 9 | import uwu.narumi.deobfuscator.core.other.impl.qprotect.qProtectFieldFlowTransformer; 10 | import uwu.narumi.deobfuscator.core.other.impl.qprotect.qProtectInvokeDynamicStrongTransformer; 11 | import uwu.narumi.deobfuscator.core.other.impl.universal.SourceFileNameRecoverTransformer; 12 | import uwu.narumi.deobfuscator.core.other.impl.universal.pool.UniversalStringPoolTransformer; 13 | import uwu.narumi.deobfuscator.core.other.impl.qprotect.qProtectStringTransformer; 14 | import uwu.narumi.deobfuscator.core.other.impl.qprotect.qProtectTryCatchTransformer; 15 | import uwu.narumi.deobfuscator.core.other.impl.qprotect.qProtectInvokeDynamicTransformer; 16 | import uwu.narumi.deobfuscator.core.other.impl.universal.pool.UniversalNumberPoolTransformer; 17 | import uwu.narumi.deobfuscator.core.other.impl.universal.TryCatchRepairTransformer; 18 | import uwu.narumi.deobfuscator.core.other.impl.universal.UniversalFlowTransformer; 19 | import uwu.narumi.deobfuscator.core.other.impl.universal.UniversalNumberTransformer; 20 | 21 | /** 22 | * https://qtechnologies.dev/ 23 | */ 24 | // TODO: Deobfuscate strong string encryption. Sample: qprotect.StringStrongEncryption 25 | public class Composed_qProtectTransformer extends ComposedTransformer { 26 | public Composed_qProtectTransformer() { 27 | super( 28 | // This fixes some weird issues where "this" is used as a local variable name. 29 | LocalVariableNamesCleanTransformer::new, 30 | 31 | // Initial cleaning code from garbage 32 | UniversalNumberTransformer::new, 33 | InlineStaticFieldTransformer::new, 34 | InlineLocalVariablesTransformer::new, 35 | 36 | // Inline number pools 37 | UniversalNumberPoolTransformer::new, 38 | // Decrypt method invocation 39 | qProtectInvokeDynamicTransformer::new, 40 | qProtectInvokeDynamicStrongTransformer::new, 41 | 42 | // Fix flow 43 | UniversalFlowTransformer::new, 44 | 45 | // Decrypt strings 46 | qProtectStringTransformer::new, 47 | qProtectAESStringEncryptionTransformer::new, 48 | // Inline string pools 49 | UniversalStringPoolTransformer::new, 50 | 51 | // Inline fields again 52 | InlineStaticFieldTransformer::new, 53 | InlineLocalVariablesTransformer::new, 54 | 55 | // Cleanup 56 | ComposedPeepholeCleanTransformer::new, 57 | 58 | // Resolve qProtect flow that uses try-catches 59 | qProtectTryCatchTransformer::new, 60 | TryCatchRepairTransformer::new, 61 | 62 | // Remove field flow after cleaning code from garbage, so we can do pattern matching 63 | qProtectFieldFlowTransformer::new 64 | ); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/composed/general/ComposedGeneralFlowTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.composed.general; 2 | 3 | import uwu.narumi.deobfuscator.api.transformer.ComposedTransformer; 4 | import uwu.narumi.deobfuscator.core.other.impl.clean.InvalidMethodCleanTransformer; 5 | import uwu.narumi.deobfuscator.core.other.impl.pool.InlineLocalVariablesTransformer; 6 | import uwu.narumi.deobfuscator.core.other.impl.pool.InlineStaticFieldTransformer; 7 | import uwu.narumi.deobfuscator.core.other.impl.universal.UniversalFlowTransformer; 8 | 9 | public class ComposedGeneralFlowTransformer extends ComposedTransformer { 10 | 11 | public ComposedGeneralFlowTransformer() { 12 | super( 13 | // Preparation 14 | InvalidMethodCleanTransformer::new, 15 | InlineStaticFieldTransformer::new, 16 | InlineLocalVariablesTransformer::new, 17 | 18 | // Main transformer 19 | UniversalFlowTransformer::new, 20 | 21 | // Clean up garbage 22 | ComposedPeepholeCleanTransformer::new 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/composed/general/ComposedGeneralRepairTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.composed.general; 2 | 3 | import uwu.narumi.deobfuscator.api.transformer.ComposedTransformer; 4 | import uwu.narumi.deobfuscator.core.other.impl.clean.ClassDebugInfoCleanTransformer; 5 | import uwu.narumi.deobfuscator.core.other.impl.clean.MethodDebugInfoCleanTransformer; 6 | import uwu.narumi.deobfuscator.core.other.impl.clean.SignatureCleanTransformer; 7 | import uwu.narumi.deobfuscator.core.other.impl.clean.UnknownAttributeCleanTransformer; 8 | import uwu.narumi.deobfuscator.core.other.impl.universal.AccessRepairTransformer; 9 | import uwu.narumi.deobfuscator.core.other.impl.universal.AnnotationFilterTransformer; 10 | import uwu.narumi.deobfuscator.core.other.impl.universal.TryCatchRepairTransformer; 11 | 12 | /** 13 | * If methods are hidden from decompiler, or something uncommon happen, then this transformer will try to fix it. 14 | */ 15 | public class ComposedGeneralRepairTransformer extends ComposedTransformer { 16 | 17 | public ComposedGeneralRepairTransformer() { 18 | super( 19 | AccessRepairTransformer::new, 20 | AnnotationFilterTransformer::new, 21 | TryCatchRepairTransformer::new, 22 | UnknownAttributeCleanTransformer::new, 23 | SignatureCleanTransformer::new, 24 | MethodDebugInfoCleanTransformer::new, 25 | ClassDebugInfoCleanTransformer::new 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/composed/general/ComposedPeepholeCleanTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.composed.general; 2 | 3 | import uwu.narumi.deobfuscator.api.transformer.ComposedTransformer; 4 | import uwu.narumi.deobfuscator.core.other.impl.clean.peephole.DeadCodeCleanTransformer; 5 | import uwu.narumi.deobfuscator.core.other.impl.clean.peephole.NopCleanTransformer; 6 | import uwu.narumi.deobfuscator.core.other.impl.clean.peephole.UnUsedLabelCleanTransformer; 7 | import uwu.narumi.deobfuscator.core.other.impl.clean.peephole.PopUnUsedLocalVariablesTransformer; 8 | import uwu.narumi.deobfuscator.core.other.impl.clean.peephole.UselessGotosCleanTransformer; 9 | import uwu.narumi.deobfuscator.core.other.impl.clean.peephole.UselessPopCleanTransformer; 10 | 11 | /** 12 | * A transformer that cleans up garbage instructions. 13 | */ 14 | public class ComposedPeepholeCleanTransformer extends ComposedTransformer { 15 | 16 | public ComposedPeepholeCleanTransformer() { 17 | super( 18 | // Remove dead code 19 | DeadCodeCleanTransformer::new, 20 | // Some more garbage instructions cleanup 21 | NopCleanTransformer::new, 22 | UnUsedLabelCleanTransformer::new, 23 | UselessGotosCleanTransformer::new, 24 | 25 | () -> new ComposedTransformer(true, 26 | // Pop unused local variables stores 27 | PopUnUsedLocalVariablesTransformer::new, 28 | // Remove useless POP instructions. This also cleans up garbage var stores from the PopUnUsedLocalVariablesTransformer 29 | UselessPopCleanTransformer::new 30 | ) 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/clean/AnnotationCleanTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.impl.clean; 2 | 3 | import uwu.narumi.deobfuscator.api.transformer.Transformer; 4 | 5 | public class AnnotationCleanTransformer extends Transformer { 6 | 7 | @Override 8 | protected void transform() throws Exception { 9 | scopedClasses().forEach(classWrapper -> { 10 | classWrapper.classNode().invisibleAnnotations = null; 11 | classWrapper.classNode().invisibleAnnotations = null; 12 | 13 | classWrapper.methods().forEach(methodNode -> { 14 | methodNode.invisibleAnnotations = null; 15 | methodNode.visibleAnnotations = null; 16 | methodNode.invisibleParameterAnnotations = null; 17 | methodNode.visibleParameterAnnotations = null; 18 | }); 19 | 20 | classWrapper.fields().forEach(fieldNode -> { 21 | fieldNode.invisibleAnnotations = null; 22 | fieldNode.visibleAnnotations = null; 23 | }); 24 | }); 25 | 26 | // There is always a change 27 | markChange(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/clean/ClassDebugInfoCleanTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.impl.clean; 2 | 3 | import uwu.narumi.deobfuscator.api.transformer.Transformer; 4 | 5 | public class ClassDebugInfoCleanTransformer extends Transformer { 6 | 7 | @Override 8 | protected void transform() throws Exception { 9 | scopedClasses().forEach(classWrapper -> { 10 | classWrapper.classNode().sourceDebug = null; 11 | classWrapper.classNode().sourceFile = null; 12 | }); 13 | 14 | // There is always a change 15 | markChange(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/clean/InvalidMethodCleanTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.impl.clean; 2 | 3 | import org.objectweb.asm.tree.MethodNode; 4 | import org.objectweb.asm.tree.analysis.Analyzer; 5 | import org.objectweb.asm.tree.analysis.AnalyzerException; 6 | import org.objectweb.asm.tree.analysis.BasicInterpreter; 7 | import uwu.narumi.deobfuscator.api.transformer.Transformer; 8 | 9 | /** 10 | * Remove invalid methods. WARNING: If some transformer produces invalid bytecode in methods, this transformer will remove them. 11 | */ 12 | public class InvalidMethodCleanTransformer extends Transformer { 13 | 14 | @Override 15 | protected void transform() throws Exception { 16 | scopedClasses().parallelStream().forEach(classWrapper -> { 17 | var iterator = classWrapper.methods().iterator(); 18 | while (iterator.hasNext()) { 19 | MethodNode methodNode = iterator.next(); 20 | 21 | Analyzer analyzer = new Analyzer<>(new BasicInterpreter()); 22 | try { 23 | analyzer.analyze(classWrapper.name(), methodNode); 24 | } catch (AnalyzerException e) { 25 | // Remove invalid method 26 | LOGGER.warn("Found invalid method: {}#{}{}. Removing...", classWrapper.name(), methodNode.name, methodNode.desc); 27 | iterator.remove(); 28 | markChange(); 29 | } 30 | } 31 | }); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/clean/LineNumberCleanTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.impl.clean; 2 | 3 | import java.util.Arrays; 4 | 5 | import org.objectweb.asm.tree.LineNumberNode; 6 | import uwu.narumi.deobfuscator.api.transformer.Transformer; 7 | 8 | public class LineNumberCleanTransformer extends Transformer { 9 | 10 | @Override 11 | protected void transform() throws Exception { 12 | scopedClasses().forEach(classWrapper -> classWrapper.methods().forEach(methodNode -> { 13 | Arrays.stream(methodNode.instructions.toArray()) 14 | .filter(node -> node instanceof LineNumberNode) 15 | .forEach(node -> { 16 | methodNode.instructions.remove(node); 17 | this.markChange(); 18 | }); 19 | })); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/clean/LocalVariableNamesCleanTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.impl.clean; 2 | 3 | import uwu.narumi.deobfuscator.api.transformer.Transformer; 4 | 5 | /** 6 | * Removes local variable names. 7 | */ 8 | public class LocalVariableNamesCleanTransformer extends Transformer { 9 | 10 | @Override 11 | protected void transform() throws Exception { 12 | scopedClasses().stream() 13 | .flatMap(classWrapper -> classWrapper.methods().stream()) 14 | .forEach(methodNode -> { 15 | methodNode.localVariables = null; 16 | this.markChange(); 17 | }); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/clean/MethodDebugInfoCleanTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.impl.clean; 2 | 3 | import uwu.narumi.deobfuscator.api.transformer.ComposedTransformer; 4 | 5 | public class MethodDebugInfoCleanTransformer extends ComposedTransformer { 6 | public MethodDebugInfoCleanTransformer() { 7 | super( 8 | LocalVariableNamesCleanTransformer::new, 9 | ParametersInfoCleanTransformer::new 10 | ); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/clean/ParametersInfoCleanTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.impl.clean; 2 | 3 | import uwu.narumi.deobfuscator.api.transformer.Transformer; 4 | 5 | /** 6 | * Removes parameter info from methods 7 | */ 8 | public class ParametersInfoCleanTransformer extends Transformer { 9 | @Override 10 | protected void transform() throws Exception { 11 | scopedClasses().stream() 12 | .flatMap(classWrapper -> classWrapper.methods().stream()) 13 | .forEach(methodNode -> { 14 | methodNode.parameters = null; 15 | this.markChange(); 16 | }); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/clean/SignatureCleanTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.impl.clean; 2 | 3 | import uwu.narumi.deobfuscator.api.transformer.Transformer; 4 | 5 | public class SignatureCleanTransformer extends Transformer { 6 | 7 | @Override 8 | protected void transform() throws Exception { 9 | scopedClasses().forEach(classWrapper -> { 10 | classWrapper.classNode().signature = null; 11 | classWrapper.methods().forEach(methodNode -> methodNode.signature = null); 12 | classWrapper.fields().forEach(fieldNode -> fieldNode.signature = null); 13 | }); 14 | 15 | // There is always a change 16 | this.markChange(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/clean/UnknownAttributeCleanTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.impl.clean; 2 | 3 | import org.objectweb.asm.Attribute; 4 | import uwu.narumi.deobfuscator.api.transformer.Transformer; 5 | 6 | public class UnknownAttributeCleanTransformer extends Transformer { 7 | 8 | private boolean changed = false; 9 | 10 | @Override 11 | protected void transform() throws Exception { 12 | scopedClasses().forEach(classWrapper -> { 13 | if (classWrapper.classNode().attrs != null) { 14 | changed |= classWrapper.classNode().attrs.removeIf(Attribute::isUnknown); 15 | } 16 | classWrapper.methods().forEach(methodNode -> { 17 | if (methodNode.attrs != null) { 18 | changed |= methodNode.attrs.removeIf(Attribute::isUnknown); 19 | } 20 | }); 21 | classWrapper.fields().forEach(fieldNode -> { 22 | if (fieldNode.attrs != null) { 23 | changed |= fieldNode.attrs.removeIf(Attribute::isUnknown); 24 | } 25 | }); 26 | }); 27 | 28 | if (changed) { 29 | markChange(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/clean/peephole/DeadCodeCleanTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.impl.clean.peephole; 2 | 3 | import org.objectweb.asm.tree.AbstractInsnNode; 4 | import org.objectweb.asm.tree.analysis.Analyzer; 5 | import org.objectweb.asm.tree.analysis.AnalyzerException; 6 | import org.objectweb.asm.tree.analysis.Frame; 7 | import org.objectweb.asm.tree.analysis.SourceInterpreter; 8 | import org.objectweb.asm.tree.analysis.SourceValue; 9 | import uwu.narumi.deobfuscator.api.transformer.Transformer; 10 | 11 | public class DeadCodeCleanTransformer extends Transformer { 12 | 13 | @Override 14 | protected void transform() throws Exception { 15 | scopedClasses().parallelStream().forEach(classWrapper -> classWrapper.methods().parallelStream().forEach(methodNode -> { 16 | // We want to use here the traditional analyzer. We want the true dead code. 17 | Analyzer analyzer = new Analyzer<>(new SourceInterpreter()); 18 | try { 19 | analyzer.analyze(classWrapper.name(), methodNode); 20 | } catch (AnalyzerException e) { 21 | throw new RuntimeException(e); 22 | } 23 | 24 | Frame[] frames = analyzer.getFrames(); 25 | 26 | AbstractInsnNode[] insns = methodNode.instructions.toArray(); 27 | for (int i = 0; i < insns.length; i++) { 28 | AbstractInsnNode insn = insns[i]; 29 | Frame frame = frames[i]; 30 | if (frame == null && insn.getType() != AbstractInsnNode.LABEL) { 31 | // Remove unreachable instruction 32 | methodNode.instructions.remove(insn); 33 | this.markChange(); 34 | } 35 | } 36 | })); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/clean/peephole/JsrInlinerTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.impl.clean.peephole; 2 | 3 | import org.objectweb.asm.commons.JSRInlinerAdapter; 4 | import org.objectweb.asm.tree.MethodNode; 5 | import uwu.narumi.deobfuscator.api.transformer.Transformer; 6 | 7 | import java.util.Arrays; 8 | 9 | /** 10 | * Inlines very old JSR/RET instructions 11 | */ 12 | public class JsrInlinerTransformer extends Transformer { 13 | @Override 14 | protected void transform() throws Exception { 15 | scopedClasses().forEach(classWrapper -> { 16 | for (int i = 0; i < classWrapper.methods().size(); i++) { 17 | MethodNode methodNode = classWrapper.methods().get(i); 18 | 19 | boolean isJsr = Arrays.stream(methodNode.instructions.toArray()).anyMatch(insn -> insn.getOpcode() == JSR); 20 | 21 | if (isJsr) { 22 | // Inline JSR instructions 23 | final JSRInlinerAdapter adapter = new JSRInlinerAdapter( 24 | methodNode, 25 | methodNode.access, 26 | methodNode.name, 27 | methodNode.desc, 28 | methodNode.signature, 29 | methodNode.exceptions.toArray(new String[0]) 30 | ); 31 | 32 | methodNode.accept(adapter); 33 | classWrapper.methods().set(i, adapter); 34 | 35 | markChange(); 36 | } 37 | } 38 | }); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/clean/peephole/NopCleanTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.impl.clean.peephole; 2 | 3 | import java.util.Arrays; 4 | 5 | import uwu.narumi.deobfuscator.api.transformer.Transformer; 6 | 7 | public class NopCleanTransformer extends Transformer { 8 | 9 | @Override 10 | protected void transform() throws Exception { 11 | scopedClasses().stream() 12 | .flatMap(classWrapper -> classWrapper.methods().stream()) 13 | .forEach( 14 | methodNode -> 15 | Arrays.stream(methodNode.instructions.toArray()) 16 | .filter(node -> node.getOpcode() == NOP) 17 | .forEach(node -> { 18 | methodNode.instructions.remove(node); 19 | this.markChange(); 20 | })); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/clean/peephole/PopUnUsedLocalVariablesTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.impl.clean.peephole; 2 | 3 | import org.objectweb.asm.tree.AbstractInsnNode; 4 | import org.objectweb.asm.tree.VarInsnNode; 5 | import uwu.narumi.deobfuscator.api.asm.InsnContext; 6 | import uwu.narumi.deobfuscator.api.asm.MethodContext; 7 | import uwu.narumi.deobfuscator.api.helper.AsmHelper; 8 | import uwu.narumi.deobfuscator.api.transformer.Transformer; 9 | 10 | import java.util.Set; 11 | 12 | public class PopUnUsedLocalVariablesTransformer extends Transformer { 13 | 14 | @Override 15 | protected void transform() throws Exception { 16 | scopedClasses().parallelStream().forEach(classWrapper -> classWrapper.methods().parallelStream().forEach(methodNode -> { 17 | MethodContext methodContext = MethodContext.of(classWrapper, methodNode); 18 | 19 | Set varStoresInUse = AsmHelper.getVarUsages(methodContext).keySet(); 20 | 21 | // Remove all local variables that are not in use 22 | for (AbstractInsnNode insn : methodNode.instructions.toArray()) { 23 | if (insn instanceof VarInsnNode varInsnNode && insn.isVarStore()) { 24 | if (!varStoresInUse.contains(varInsnNode)) { 25 | InsnContext insnContext = methodContext.at(insn); 26 | 27 | // Pop the value from the stack 28 | insnContext.placePops(); 29 | 30 | methodNode.instructions.remove(insn); 31 | 32 | this.markChange(); 33 | } 34 | } 35 | } 36 | })); 37 | 38 | LOGGER.info("Popped {} unused local variables", this.getChangesCount()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/clean/peephole/UnUsedLabelCleanTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.impl.clean.peephole; 2 | 3 | import java.util.Arrays; 4 | import java.util.HashSet; 5 | import java.util.Set; 6 | import java.util.stream.Stream; 7 | import org.objectweb.asm.tree.*; 8 | import uwu.narumi.deobfuscator.api.transformer.Transformer; 9 | 10 | public class UnUsedLabelCleanTransformer extends Transformer { 11 | 12 | @Override 13 | protected void transform() throws Exception { 14 | scopedClasses().stream() 15 | .flatMap(classWrapper -> classWrapper.methods().stream()) 16 | .forEach( 17 | methodNode -> { 18 | Set labelNodes = new HashSet<>(); 19 | 20 | if (methodNode.tryCatchBlocks != null) 21 | labelNodes.addAll( 22 | methodNode.tryCatchBlocks.stream() 23 | .flatMap(tbce -> Stream.of(tbce.end, tbce.start, tbce.handler)) 24 | .toList()); 25 | 26 | if (methodNode.localVariables != null) 27 | labelNodes.addAll( 28 | methodNode.localVariables.stream() 29 | .flatMap(localVariable -> Stream.of(localVariable.end, localVariable.start)) 30 | .toList()); 31 | 32 | Arrays.stream(methodNode.instructions.toArray()) 33 | .forEach( 34 | node -> { 35 | if (node instanceof JumpInsnNode) { 36 | labelNodes.add(((JumpInsnNode) node).label); 37 | } else if (node instanceof LookupSwitchInsnNode lookupSwitchInsnNode) { 38 | labelNodes.addAll(lookupSwitchInsnNode.labels); 39 | labelNodes.add(lookupSwitchInsnNode.dflt); 40 | } else if (node instanceof TableSwitchInsnNode tableSwitchInsnNode) { 41 | labelNodes.addAll(tableSwitchInsnNode.labels); 42 | labelNodes.add(tableSwitchInsnNode.dflt); 43 | } else if (node instanceof LineNumberNode) { 44 | labelNodes.add(((LineNumberNode) node).start); 45 | } 46 | }); 47 | 48 | // Remove label nodes that are not used 49 | Arrays.stream(methodNode.instructions.toArray()) 50 | .filter(node -> node instanceof LabelNode) 51 | .map(LabelNode.class::cast) 52 | .filter(node -> !labelNodes.contains(node)) 53 | .forEach(insn -> { 54 | methodNode.instructions.remove(insn); 55 | this.markChange(); 56 | }); 57 | }); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/clean/peephole/UselessGotosCleanTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.impl.clean.peephole; 2 | 3 | import org.objectweb.asm.tree.AbstractInsnNode; 4 | import org.objectweb.asm.tree.JumpInsnNode; 5 | import org.objectweb.asm.tree.LabelNode; 6 | import org.objectweb.asm.tree.LookupSwitchInsnNode; 7 | import org.objectweb.asm.tree.MethodNode; 8 | import org.objectweb.asm.tree.TableSwitchInsnNode; 9 | import uwu.narumi.deobfuscator.api.transformer.Transformer; 10 | 11 | import java.util.Arrays; 12 | import java.util.List; 13 | 14 | /** 15 | * Remove useless GOTO jumps 16 | *
17 |  * A:
18 |  *   ...
19 |  *   goto B
20 |  * B:
21 |  *   ...
22 |  * 
23 | */ 24 | public class UselessGotosCleanTransformer extends Transformer { 25 | 26 | @Override 27 | protected void transform() throws Exception { 28 | scopedClasses().parallelStream().forEach(classWrapper -> classWrapper.methods().parallelStream().forEach(methodNode -> { 29 | for (AbstractInsnNode insn : methodNode.instructions.toArray()) { 30 | if (insn.getOpcode() == GOTO) { 31 | JumpInsnNode jumpInsn = (JumpInsnNode) insn; 32 | if (jumpInsn.getNext() instanceof LabelNode labelNode && jumpInsn.label == labelNode) { 33 | 34 | // Check if the label is used only by the jump instruction 35 | if (!isLabelUsedOnlyByInstructions(methodNode, jumpInsn.label)) continue; 36 | 37 | List labelUsedInsns = Arrays.stream(methodNode.instructions.toArray()) 38 | .filter(newInsn -> checkIfJumpHappens(newInsn, labelNode)) 39 | .toList(); 40 | 41 | boolean labelUsedOnlyOnce = labelUsedInsns.size() == 1; 42 | 43 | if (labelUsedOnlyOnce) { 44 | // Remove the goto and the label 45 | methodNode.instructions.remove(labelNode); 46 | methodNode.instructions.remove(jumpInsn); 47 | this.markChange(); 48 | } 49 | } 50 | } 51 | } 52 | })); 53 | } 54 | 55 | private boolean isLabelUsedOnlyByInstructions(MethodNode methodNode, LabelNode labelNode) { 56 | if (methodNode.tryCatchBlocks != null) { 57 | boolean usedByTryCatchBlock = methodNode.tryCatchBlocks.stream() 58 | .anyMatch(tryCatchBlockNode -> tryCatchBlockNode.start == labelNode || tryCatchBlockNode.end == labelNode || tryCatchBlockNode.handler == labelNode); 59 | 60 | if (usedByTryCatchBlock) { 61 | return false; 62 | } 63 | } 64 | 65 | if (methodNode.localVariables != null) { 66 | boolean usedByLocalVariable = methodNode.localVariables.stream() 67 | .anyMatch(localVariableNode -> localVariableNode.start == labelNode || localVariableNode.end == labelNode); 68 | 69 | if (usedByLocalVariable) { 70 | return false; 71 | } 72 | } 73 | return true; 74 | } 75 | 76 | private boolean checkIfJumpHappens(AbstractInsnNode insn, LabelNode labelNode) { 77 | if (insn instanceof JumpInsnNode jumpInsn) { 78 | return jumpInsn.label == labelNode; 79 | } else if (insn instanceof LookupSwitchInsnNode lookupSwitchInsn) { 80 | return lookupSwitchInsn.labels.contains(labelNode) || lookupSwitchInsn.dflt == labelNode; 81 | } else if (insn instanceof TableSwitchInsnNode tableSwitchInsn) { 82 | return tableSwitchInsn.labels.contains(labelNode) || tableSwitchInsn.dflt == labelNode; 83 | } 84 | return false; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/exploit/WebExploitRemoveTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.impl.exploit; 2 | 3 | import uwu.narumi.deobfuscator.api.transformer.Transformer; 4 | 5 | public class WebExploitRemoveTransformer extends Transformer { 6 | 7 | private boolean changed = false; 8 | 9 | @Override 10 | protected void transform() throws Exception { 11 | changed |= context().getClassesMap().entrySet().removeIf(entry -> entry.getKey().contains("")); 12 | changed |= context().getFilesMap().entrySet().removeIf(entry -> entry.getKey().contains("")); 13 | 14 | scopedClasses().forEach(classWrapper -> { 15 | changed |= classWrapper.methods().removeIf(methodNode -> methodNode.name.contains("")); 16 | changed |= classWrapper.fields().removeIf(fieldNode -> fieldNode.name.contains("") || fieldNode.desc.contains("")); 17 | }); 18 | 19 | if (changed) { 20 | markChange(); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/hp888/HP888PackerTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.impl.hp888; 2 | 3 | import org.objectweb.asm.ClassReader; 4 | import uwu.narumi.deobfuscator.api.asm.ClassWrapper; 5 | import uwu.narumi.deobfuscator.api.helper.ClassHelper; 6 | import uwu.narumi.deobfuscator.api.transformer.Transformer; 7 | 8 | import javax.crypto.Cipher; 9 | import javax.crypto.spec.SecretKeySpec; 10 | import java.util.Base64; 11 | import java.util.HashMap; 12 | import java.util.HashSet; 13 | import java.util.Set; 14 | import java.util.concurrent.atomic.AtomicReference; 15 | 16 | /** 17 | * HP888 obfuscator has encrypted classes files (usually ending with the suffix ".mc" {@link #encryptedClassFilesSuffix}) 18 | * that are decrypted and loaded when the jar starts. 19 | */ 20 | public class HP888PackerTransformer extends Transformer { 21 | 22 | /** 23 | * Suffix of encrypted class files 24 | */ 25 | private final String encryptedClassFilesSuffix; 26 | 27 | public HP888PackerTransformer(String encryptedClassFilesSuffix) { 28 | this.encryptedClassFilesSuffix = encryptedClassFilesSuffix; 29 | } 30 | 31 | @Override 32 | protected void transform() throws Exception { 33 | Set filesToRemove = new HashSet<>(); 34 | HashMap newClasses = new HashMap<>(); 35 | AtomicReference key = new AtomicReference<>(); 36 | 37 | /* Firstly you must use HP888StringTransformer, so key would be decrypted, 38 | and it only searches in loader classes so don't tell me its bad searching. */ 39 | scopedClasses().stream().map(ClassWrapper::classNode).forEach(classNode -> classNode.methods.forEach(methodNode -> methodNode.instructions.forEach(abstractInsnNode -> { 40 | if (abstractInsnNode.isString() && abstractInsnNode.asString().endsWith("==")) { 41 | // Find base64 key 42 | key.set(abstractInsnNode.asString()); 43 | } 44 | }))); 45 | 46 | if (key.get().isEmpty()) { 47 | LOGGER.error("Key not found"); 48 | return; 49 | } 50 | 51 | // Decrypt encrypted classes 52 | Cipher cipher = Cipher.getInstance("AES"); 53 | cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(Base64.getDecoder().decode(key.get()), "AES")); 54 | context().getFilesMap().forEach((file, bytes) -> { 55 | if (file.endsWith(encryptedClassFilesSuffix)) { 56 | filesToRemove.add(file); 57 | 58 | String path = file.replace(encryptedClassFilesSuffix, ".class").replace(".", "/"); 59 | try { 60 | // Decrypt! 61 | byte[] decrypted = cipher.doFinal(bytes); 62 | 63 | // Load and put class 64 | context().addCompiledClass(path, decrypted); 65 | 66 | markChange(); 67 | } catch (Exception e) { 68 | throw new RuntimeException("Failed to decrypt class: " + path, e); 69 | } 70 | } 71 | }); 72 | 73 | // Cleanup 74 | filesToRemove.forEach(context().getFilesMap()::remove); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/qprotect/qProtectFieldFlowTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.impl.qprotect; 2 | 3 | import org.objectweb.asm.tree.FieldInsnNode; 4 | import uwu.narumi.deobfuscator.api.asm.MethodContext; 5 | import uwu.narumi.deobfuscator.api.asm.matcher.Match; 6 | import uwu.narumi.deobfuscator.api.asm.matcher.group.SequenceMatch; 7 | import uwu.narumi.deobfuscator.api.asm.matcher.impl.NumberMatch; 8 | import uwu.narumi.deobfuscator.api.asm.matcher.impl.OpcodeMatch; 9 | import uwu.narumi.deobfuscator.api.transformer.Transformer; 10 | 11 | public class qProtectFieldFlowTransformer extends Transformer { 12 | private static final Match FIELD_FLOW_PATTERN = SequenceMatch.of( 13 | // Init variable through field 14 | NumberMatch.of(), 15 | OpcodeMatch.of(PUTSTATIC).capture("field"), 16 | // Compare 17 | NumberMatch.of(), 18 | OpcodeMatch.of(GETSTATIC), 19 | Match.of(ctx -> ctx.insn().isCompare()) 20 | ); 21 | 22 | @Override 23 | protected void transform() throws Exception { 24 | scopedClasses().parallelStream().forEach(classWrapper -> { 25 | classWrapper.methods().parallelStream().forEach(methodNode -> { 26 | MethodContext methodContext = MethodContext.of(classWrapper, methodNode); 27 | FIELD_FLOW_PATTERN.findAllMatches(methodContext).forEach(match -> { 28 | FieldInsnNode fieldInsn = (FieldInsnNode) match.captures().get("field").insn(); 29 | 30 | // Remove field flow 31 | match.removeAll(); 32 | 33 | markChange(); 34 | }); 35 | }); 36 | }); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/qprotect/qProtectTryCatchTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.impl.qprotect; 2 | 3 | import org.objectweb.asm.tree.AbstractInsnNode; 4 | import org.objectweb.asm.tree.JumpInsnNode; 5 | import org.objectweb.asm.tree.LabelNode; 6 | import uwu.narumi.deobfuscator.api.asm.MethodContext; 7 | import uwu.narumi.deobfuscator.api.asm.matcher.Match; 8 | import uwu.narumi.deobfuscator.api.asm.matcher.MatchContext; 9 | import uwu.narumi.deobfuscator.api.asm.matcher.group.SequenceMatch; 10 | import uwu.narumi.deobfuscator.api.asm.matcher.impl.MethodMatch; 11 | import uwu.narumi.deobfuscator.api.asm.matcher.impl.OpcodeMatch; 12 | import uwu.narumi.deobfuscator.api.transformer.Transformer; 13 | 14 | /** 15 | * Removes fake try catch blocks 16 | *

17 | * Transforms this: 18 | *

19 |  * block12: {
20 |  *   plugin = IllIllllIlIl;
21 |  *   try {
22 |  *     PLJ.lIIlIIIllIlI("NFdlWk9sV3NYZTQ1Zm5vRg==", "##J\u001d\"\nZ\u00182F-2t%2R5\u001fPX?Iei\u001c0S*!\u001bU\"$\\ \n.5#E,Zs\u0000\"\u000blOfo2\u001a`-O\u0003:b<4?z D;\u000e^\u0012'\u0004V\u0013?%\u0007\u0003%\u000e\u0017\u000bxb\r\u0006\u0019;#T,Zs\u0000\"\u000blOx\u007f");
23 |  *     break block12;
24 |  *   } catch (RuntimeException runtimeException) {
25 |  *     // empty catch block
26 |  *   }
27 |  *   while (true) {
28 |  *     // Infinite loop
29 |  *   }
30 |  * }
31 |  * 
32 | * 33 | * Into this: 34 | *
35 |  * plugin = IllIllllIlIl;
36 |  * PLJ.lIIlIIIllIlI("NFdlWk9sV3NYZTQ1Zm5vRg==", "##J\u001d\"\nZ\u00182F-2t%2R5\u001fPX?Iei\u001c0S*!\u001bU\"$\\ \n.5#E,Zs\u0000\"\u000blOfo2\u001a`-O\u0003:b<4?z D;\u000e^\u0012'\u0004V\u0013?%\u0007\u0003%\u000e\u0017\u000bxb\r\u0006\u0019;#T,Zs\u0000\"\u000blOx\u007f");
37 |  * 
38 | */ 39 | public class qProtectTryCatchTransformer extends Transformer { 40 | private static final Match SELF_THROW_TRY_CATCH_MATCH = SequenceMatch.of( 41 | OpcodeMatch.of(DUP), 42 | MethodMatch.invokeVirtual().name("printStackTrace").desc("()V"), 43 | OpcodeMatch.of(ATHROW).or(SequenceMatch.of( 44 | OpcodeMatch.of(CHECKCAST), 45 | OpcodeMatch.of(ATHROW) 46 | )) 47 | ); 48 | 49 | @Override 50 | protected void transform() throws Exception { 51 | scopedClasses().forEach(classWrapper -> classWrapper.methods().forEach(methodNode -> { 52 | MethodContext methodContext = MethodContext.of(classWrapper, methodNode); 53 | 54 | // Remove infinite loop try catch blocks 55 | methodNode.tryCatchBlocks.removeIf(tryCatch -> { 56 | LabelNode handlerLabel = tryCatch.handler; 57 | AbstractInsnNode nextInsn = handlerLabel.getNext(); 58 | 59 | // Check if handler is in infinite loop (goto itself) 60 | if (nextInsn != null && nextInsn.getOpcode() == GOTO && ((JumpInsnNode) nextInsn).label == handlerLabel) { 61 | methodNode.instructions.remove(nextInsn); 62 | markChange(); 63 | 64 | return true; 65 | } 66 | 67 | return false; 68 | }); 69 | 70 | // Remove self-throw try catch blocks 71 | methodNode.tryCatchBlocks.removeIf(tryCatch -> { 72 | LabelNode handlerLabel = tryCatch.handler; 73 | AbstractInsnNode nextInsn = handlerLabel.getNext(); 74 | 75 | MatchContext matchContext = SELF_THROW_TRY_CATCH_MATCH.matchResult(methodContext.at(nextInsn)); 76 | if (matchContext != null) { 77 | matchContext.removeAll(); 78 | markChange(); 79 | 80 | return true; 81 | } 82 | 83 | return false; 84 | }); 85 | })); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/universal/AccessRepairTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.impl.universal; 2 | 3 | import uwu.narumi.deobfuscator.api.transformer.Transformer; 4 | 5 | public class AccessRepairTransformer extends Transformer { 6 | 7 | private final int[] CLASS = { 8 | ACC_STATIC, 9 | ACC_SYNCHRONIZED, 10 | ACC_OPEN, 11 | ACC_TRANSITIVE, 12 | ACC_VOLATILE, 13 | ACC_BRIDGE, 14 | ACC_STATIC_PHASE, 15 | ACC_VARARGS, 16 | ACC_TRANSIENT, 17 | ACC_NATIVE, 18 | ACC_STRICT, 19 | ACC_SYNTHETIC 20 | }; 21 | 22 | private final int[] METHOD = { 23 | ACC_SUPER, 24 | ACC_OPEN, 25 | ACC_TRANSITIVE, 26 | ACC_VOLATILE, 27 | ACC_STATIC_PHASE, 28 | ACC_TRANSIENT, 29 | ACC_INTERFACE, 30 | ACC_ANNOTATION, 31 | ACC_ENUM, 32 | ACC_MODULE, 33 | ACC_RECORD, 34 | ACC_SYNTHETIC, 35 | ACC_BRIDGE 36 | }; 37 | 38 | private final int[] FIELD = { 39 | ACC_SUPER, 40 | ACC_SYNCHRONIZED, 41 | ACC_OPEN, 42 | ACC_TRANSITIVE, 43 | ACC_BRIDGE, 44 | ACC_STATIC_PHASE, 45 | ACC_VARARGS, 46 | ACC_NATIVE, 47 | ACC_INTERFACE, 48 | ACC_ABSTRACT, 49 | ACC_STRICT, 50 | ACC_ANNOTATION, 51 | ACC_MODULE, 52 | ACC_RECORD, 53 | ACC_SYNTHETIC 54 | }; 55 | 56 | private final int[] PARAMETER = { 57 | ACC_PUBLIC, 58 | ACC_PRIVATE, 59 | ACC_PROTECTED, 60 | ACC_STATIC, 61 | ACC_SUPER, 62 | ACC_SYNCHRONIZED, 63 | ACC_OPEN, 64 | ACC_TRANSITIVE, 65 | ACC_VOLATILE, 66 | ACC_BRIDGE, 67 | ACC_STATIC_PHASE, 68 | ACC_VARARGS, 69 | ACC_TRANSIENT, 70 | ACC_NATIVE, 71 | ACC_INTERFACE, 72 | ACC_ABSTRACT, 73 | ACC_STRICT, 74 | ACC_ANNOTATION, 75 | ACC_ENUM, 76 | ACC_MODULE, 77 | ACC_RECORD, 78 | ACC_DEPRECATED, 79 | ACC_SYNTHETIC 80 | }; 81 | 82 | @Override 83 | protected void transform() throws Exception { 84 | scopedClasses().forEach(classWrapper -> { 85 | int classAccess = classWrapper.classNode().access; 86 | for (int access : CLASS) { 87 | if (isAccess(classAccess, access)) { 88 | classAccess &= ~access; 89 | this.markChange(); 90 | } 91 | } 92 | classWrapper.classNode().access = classAccess; 93 | 94 | classWrapper.methods().forEach(methodNode -> { 95 | for (int access : METHOD) { 96 | if (isAccess(methodNode.access, access)) { 97 | methodNode.access &= ~access; 98 | this.markChange(); 99 | } 100 | } 101 | 102 | if (methodNode.parameters != null) 103 | methodNode.parameters.forEach(parameterNode -> { 104 | for (int access : PARAMETER) { 105 | if (isAccess(parameterNode.access, access)) { 106 | parameterNode.access &= ~access; 107 | this.markChange(); 108 | } 109 | } 110 | }); 111 | }); 112 | 113 | classWrapper.fields().forEach(fieldNode -> { 114 | // Remove ACC_ENUM from fields from classes that are not enums 115 | if (!isAccess(classWrapper.classNode().access, ACC_ENUM)) { 116 | fieldNode.access &= ~ACC_ENUM; 117 | } 118 | 119 | for (int access : FIELD) { 120 | if (isAccess(fieldNode.access, access)) { 121 | fieldNode.access &= ~access; 122 | this.markChange(); 123 | } 124 | } 125 | }); 126 | 127 | // TODO: Module maybe? 128 | }); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/universal/AnnotationFilterTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.impl.universal; 2 | 3 | import java.util.List; 4 | import java.util.function.Predicate; 5 | 6 | import org.objectweb.asm.tree.AnnotationNode; 7 | import uwu.narumi.deobfuscator.api.transformer.Transformer; 8 | 9 | public class AnnotationFilterTransformer extends Transformer { 10 | 11 | private static final Predicate ANNOTATION_NODE_PREDICATE = 12 | annotationNode -> 13 | annotationNode.desc == null 14 | || annotationNode.desc.length() <= 3 15 | || !annotationNode.desc.startsWith("L") 16 | || !annotationNode.desc.endsWith(";") 17 | || annotationNode.desc.contains("\n") 18 | || annotationNode.desc.contains("\u0000") 19 | || annotationNode.desc.contains(" "); 20 | 21 | private boolean changed = false; 22 | 23 | @Override 24 | protected void transform() throws Exception { 25 | scopedClasses().forEach(classWrapper -> { 26 | if (classWrapper.classNode().invisibleAnnotations != null) 27 | changed |= classWrapper 28 | .classNode() 29 | .invisibleAnnotations 30 | .removeIf(ANNOTATION_NODE_PREDICATE); 31 | 32 | if (classWrapper.classNode().visibleAnnotations != null) 33 | changed |= classWrapper.classNode().visibleAnnotations.removeIf(ANNOTATION_NODE_PREDICATE); 34 | 35 | classWrapper.methods().forEach(methodNode -> { 36 | if (methodNode.invisibleAnnotations != null) 37 | changed |= methodNode.invisibleAnnotations.removeIf(ANNOTATION_NODE_PREDICATE); 38 | 39 | if (methodNode.visibleAnnotations != null) 40 | changed |= methodNode.visibleAnnotations.removeIf(ANNOTATION_NODE_PREDICATE); 41 | 42 | if (methodNode.invisibleParameterAnnotations != null) 43 | for (List invisibleParameterAnnotation : methodNode.invisibleParameterAnnotations) { 44 | if (invisibleParameterAnnotation == null) continue; 45 | 46 | changed |= invisibleParameterAnnotation.removeIf(ANNOTATION_NODE_PREDICATE); 47 | } 48 | 49 | if (methodNode.visibleParameterAnnotations != null) 50 | for (List visibleParameterAnnotation : methodNode.visibleParameterAnnotations) { 51 | if (visibleParameterAnnotation == null) continue; 52 | 53 | changed |= visibleParameterAnnotation.removeIf(ANNOTATION_NODE_PREDICATE); 54 | } 55 | }); 56 | 57 | classWrapper.fields().forEach(fieldNode -> { 58 | if (fieldNode.invisibleAnnotations != null) 59 | changed |= fieldNode.invisibleAnnotations.removeIf(ANNOTATION_NODE_PREDICATE); 60 | 61 | if (fieldNode.visibleAnnotations != null) 62 | changed |= fieldNode.visibleAnnotations.removeIf(ANNOTATION_NODE_PREDICATE); 63 | }); 64 | }); 65 | 66 | if (changed) { 67 | markChange(); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/universal/RecoverSyntheticsTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.impl.universal; 2 | 3 | import uwu.narumi.deobfuscator.api.transformer.Transformer; 4 | 5 | public class RecoverSyntheticsTransformer extends Transformer { 6 | @Override 7 | protected void transform() throws Exception { 8 | scopedClasses().forEach(classWrapper -> { 9 | classWrapper.methods().forEach(methodNode -> { 10 | // Recover by name 11 | if (methodNode.name.startsWith("lambda$")) { 12 | // Mark as synthetic 13 | methodNode.access |= ACC_SYNTHETIC; 14 | markChange(); 15 | } 16 | }); 17 | }); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/universal/SourceFileNameRecoverTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.impl.universal; 2 | 3 | import org.objectweb.asm.commons.ClassRemapper; 4 | import org.objectweb.asm.tree.ClassNode; 5 | import uwu.narumi.deobfuscator.api.asm.remapper.NamesRemapper; 6 | import uwu.narumi.deobfuscator.api.transformer.Transformer; 7 | 8 | import java.util.ArrayList; 9 | 10 | /** 11 | * Recovers class name from source file name. 12 | */ 13 | public class SourceFileNameRecoverTransformer extends Transformer { 14 | @Override 15 | protected void transform() throws Exception { 16 | NamesRemapper remapper = new NamesRemapper(); 17 | 18 | scopedClasses().forEach(classWrapper -> { 19 | String sourceFileName = classWrapper.classNode().sourceFile; 20 | if (sourceFileName == null) return; 21 | 22 | // Replace the class name with the source file name without touching the package 23 | String newClassName = classWrapper.classNode().name.replaceAll("/[^/]+$", "/" + sourceFileName.replaceAll("\\.java$", "")); 24 | 25 | remapper.classMappings.put(classWrapper.name(), newClassName); 26 | 27 | markChange(); 28 | }); 29 | 30 | // Remap 31 | new ArrayList<>(context().classes()).forEach(classWrapper -> { 32 | ClassNode newNode = new ClassNode(); 33 | 34 | // Remap 35 | ClassRemapper classRemapper = new ClassRemapper(newNode, remapper); 36 | classWrapper.classNode().accept(classRemapper); 37 | 38 | // Update class name 39 | context().getClassesMap().remove(classWrapper.name()); 40 | context().getClassesMap().put(newNode.name, classWrapper); 41 | 42 | // Set new class node 43 | classWrapper.setClassNode(newNode); 44 | classWrapper.setPathInJar(newNode.name + ".class"); 45 | }); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/universal/TryCatchRepairTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.impl.universal; 2 | 3 | import org.objectweb.asm.tree.LabelNode; 4 | import uwu.narumi.deobfuscator.api.transformer.Transformer; 5 | 6 | public class TryCatchRepairTransformer extends Transformer { 7 | 8 | @Override 9 | protected void transform() throws Exception { 10 | scopedClasses().forEach(classWrapper -> classWrapper.methods().forEach(methodNode -> { 11 | methodNode.tryCatchBlocks.removeIf(tryCatchBlock -> { 12 | LabelNode start = tryCatchBlock.start; 13 | LabelNode handler = tryCatchBlock.handler; 14 | LabelNode end = tryCatchBlock.end; 15 | 16 | if (start.equals(end) || start.equals(handler) || end.equals(handler)) { 17 | // Try-catch has overlapping labels. Remove it. 18 | markChange(); 19 | return true; 20 | } 21 | 22 | // Check if try-catch labels exist 23 | if (methodNode.instructions.indexOf(start) == -1 || methodNode.instructions.indexOf(handler) == -1 || methodNode.instructions.indexOf(end) == -1) { 24 | return true; 25 | } 26 | 27 | // Check if try-catch labels are in the correct order 28 | return methodNode.instructions.indexOf(start) >= methodNode.instructions.indexOf(handler) 29 | || methodNode.instructions.indexOf(start) >= methodNode.instructions.indexOf(end) 30 | || methodNode.instructions.indexOf(handler) <= methodNode.instructions.indexOf(end); 31 | }); 32 | 33 | // Remove exceptions that are already caught by try-catch blocks 34 | methodNode.exceptions.removeIf(exception -> 35 | methodNode.tryCatchBlocks.stream().noneMatch(tryCatch -> tryCatch.type != null && tryCatch.type.equals(exception)) 36 | ); 37 | })); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/universal/UniversalFlowTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.impl.universal; 2 | 3 | import uwu.narumi.deobfuscator.api.transformer.ComposedTransformer; 4 | import uwu.narumi.deobfuscator.core.other.impl.clean.peephole.DeadCodeCleanTransformer; 5 | import uwu.narumi.deobfuscator.core.other.impl.universal.flow.CleanRedundantJumpsTransformer; 6 | import uwu.narumi.deobfuscator.core.other.impl.universal.flow.CleanRedundantSwitchesTransformer; 7 | 8 | public class UniversalFlowTransformer extends ComposedTransformer { 9 | public UniversalFlowTransformer() { 10 | super( 11 | // Clean up redundant ifs and switches 12 | CleanRedundantJumpsTransformer::new, 13 | CleanRedundantSwitchesTransformer::new, 14 | 15 | // Resolve all number operations 16 | UniversalNumberTransformer::new, 17 | 18 | // Last thing will be to clean up all dead code that is unreachable 19 | DeadCodeCleanTransformer::new 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/universal/UniversalNumberTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.impl.universal; 2 | 3 | import uwu.narumi.deobfuscator.api.transformer.ComposedTransformer; 4 | import uwu.narumi.deobfuscator.core.other.impl.clean.peephole.UselessPopCleanTransformer; 5 | import uwu.narumi.deobfuscator.core.other.impl.universal.number.InlineConstantValuesTransformer; 6 | 7 | /** 8 | * Simplifies number operations on constant values. 9 | */ 10 | public class UniversalNumberTransformer extends ComposedTransformer { 11 | public UniversalNumberTransformer() { 12 | super( 13 | InlineConstantValuesTransformer::new, 14 | 15 | UselessPopCleanTransformer::new 16 | ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/universal/flow/CleanRedundantJumpsTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.impl.universal.flow; 2 | 3 | import org.objectweb.asm.tree.JumpInsnNode; 4 | import org.objectweb.asm.tree.MethodNode; 5 | import uwu.narumi.deobfuscator.api.helper.AsmMathHelper; 6 | import uwu.narumi.deobfuscator.api.helper.FramedInstructionsStream; 7 | import uwu.narumi.deobfuscator.api.helper.MethodHelper; 8 | import uwu.narumi.deobfuscator.api.transformer.Transformer; 9 | 10 | import java.util.Optional; 11 | 12 | public class CleanRedundantJumpsTransformer extends Transformer { 13 | @Override 14 | protected void transform() throws Exception { 15 | FramedInstructionsStream.of(this) 16 | .framesProvider(MethodHelper::analyzeSourcePredictJumps) // Predict jumps 17 | .forEach(insnContext -> { 18 | if (!(insnContext.insn() instanceof JumpInsnNode jumpInsn)) return; 19 | 20 | Optional optIfResult = AsmMathHelper.predictIf(jumpInsn, insnContext.frame()); 21 | 22 | if (optIfResult.isEmpty()) return; 23 | 24 | boolean ifResult = optIfResult.get(); 25 | 26 | if (AsmMathHelper.isOneValueCondition(jumpInsn.getOpcode()) || AsmMathHelper.isTwoValuesCondition(jumpInsn.getOpcode())) { 27 | insnContext.placePops(); 28 | } 29 | 30 | // Replace if with corresponding GOTO or remove it 31 | processRedundantIfStatement(insnContext.methodNode(), jumpInsn, ifResult); 32 | 33 | markChange(); 34 | }); 35 | } 36 | 37 | private void processRedundantIfStatement(MethodNode methodNode, JumpInsnNode ifStatement, boolean ifResult) { 38 | if (!ifResult) { 39 | // Remove unreachable if statement 40 | methodNode.instructions.remove(ifStatement); 41 | } else { 42 | // Replace always reachable if statement with GOTO 43 | ifStatement.setOpcode(GOTO); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/universal/flow/CleanRedundantSwitchesTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.impl.universal.flow; 2 | 3 | import org.objectweb.asm.tree.JumpInsnNode; 4 | import org.objectweb.asm.tree.LabelNode; 5 | import org.objectweb.asm.tree.LookupSwitchInsnNode; 6 | import org.objectweb.asm.tree.TableSwitchInsnNode; 7 | import uwu.narumi.deobfuscator.api.helper.AsmMathHelper; 8 | import uwu.narumi.deobfuscator.api.helper.FramedInstructionsStream; 9 | import uwu.narumi.deobfuscator.api.helper.MethodHelper; 10 | import uwu.narumi.deobfuscator.api.transformer.Transformer; 11 | 12 | import java.util.Optional; 13 | 14 | /** 15 | * Clean redundant LOOKUPSWITCH and TABLESWITCH instructions 16 | */ 17 | public class CleanRedundantSwitchesTransformer extends Transformer { 18 | 19 | @Override 20 | protected void transform() throws Exception { 21 | FramedInstructionsStream.of(this) 22 | .framesProvider(MethodHelper::analyzeSourcePredictJumps) // Predict jumps 23 | .forEach(insnContext -> { 24 | if (insnContext.insn().getOpcode() == LOOKUPSWITCH) { 25 | LookupSwitchInsnNode lookupSwitchInsn = (LookupSwitchInsnNode) insnContext.insn(); 26 | 27 | Optional optPredictedJump = AsmMathHelper.predictLookupSwitch(lookupSwitchInsn, insnContext.frame()); 28 | if (optPredictedJump.isEmpty()) return; 29 | 30 | LabelNode predictedJump = optPredictedJump.get(); 31 | // Remove value from stack 32 | insnContext.placePops(); 33 | // Replace lookup switch with predicted jump 34 | insnContext.methodNode().instructions.set(lookupSwitchInsn, new JumpInsnNode(GOTO, predictedJump)); 35 | 36 | markChange(); 37 | } else if (insnContext.insn().getOpcode() == TABLESWITCH) { 38 | TableSwitchInsnNode tableSwitchInsn = (TableSwitchInsnNode) insnContext.insn(); 39 | 40 | Optional optPredictedJump = AsmMathHelper.predictTableSwitch(tableSwitchInsn, insnContext.frame()); 41 | if (optPredictedJump.isEmpty()) return; 42 | 43 | LabelNode predictedJump = optPredictedJump.get(); 44 | // Remove value from stack 45 | insnContext.placePops(); 46 | // Replace lookup switch with predicted jump 47 | insnContext.methodNode().instructions.set(tableSwitchInsn, new JumpInsnNode(GOTO, predictedJump)); 48 | 49 | markChange(); 50 | } 51 | }); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /deobfuscator-transformers/src/main/java/uwu/narumi/deobfuscator/core/other/impl/universal/number/InlineConstantValuesTransformer.java: -------------------------------------------------------------------------------- 1 | package uwu.narumi.deobfuscator.core.other.impl.universal.number; 2 | 3 | import org.objectweb.asm.tree.AbstractInsnNode; 4 | import org.objectweb.asm.tree.analysis.OriginalSourceValue; 5 | import uwu.narumi.deobfuscator.api.asm.InsnContext; 6 | import uwu.narumi.deobfuscator.api.asm.MethodContext; 7 | import uwu.narumi.deobfuscator.api.helper.AsmHelper; 8 | import uwu.narumi.deobfuscator.api.transformer.Transformer; 9 | 10 | import java.util.HashSet; 11 | import java.util.Set; 12 | 13 | /** 14 | * Inline constant values into instructions. 15 | */ 16 | public class InlineConstantValuesTransformer extends Transformer { 17 | @Override 18 | protected void transform() throws Exception { 19 | scopedClasses().parallelStream().forEach(classWrapper -> classWrapper.methods().parallelStream().forEach(methodNode -> { 20 | MethodContext methodContext = MethodContext.of(classWrapper, methodNode); 21 | 22 | Set poppedInsns = new HashSet<>(); 23 | for (AbstractInsnNode insn : methodNode.instructions.toArray()) { 24 | InsnContext insnContext = methodContext.at(insn); 25 | if (insnContext.frame() == null) continue; 26 | 27 | // Iterate over stack values 28 | for (int i = 0; i < insnContext.frame().getStackSize(); i++) { 29 | // Using "originalSource" ensures that we don't replace DUPs or var loads 30 | OriginalSourceValue sourceValue = insnContext.frame().getStack(insnContext.frame().getStackSize() - (i + 1)).originalSource; 31 | 32 | // Check if the source value is constant 33 | if (sourceValue.getConstantValue() != null) { 34 | // Replace constant value with instruction 35 | AbstractInsnNode toReplace = sourceValue.getProducer(); 36 | 37 | // Constants are already constants. Skip them 38 | if (toReplace.isConstant()) continue; 39 | 40 | if (poppedInsns.contains(toReplace)) { 41 | // Already popped 42 | continue; 43 | } 44 | 45 | // Place pops before replacing 46 | insnContext.methodContext().at(toReplace).placePops(); 47 | 48 | // Replace instruction with constant value 49 | AbstractInsnNode newInsn = AsmHelper.toConstantInsn(sourceValue.getConstantValue().value()); 50 | insnContext.methodNode().instructions.set(toReplace, newInsn); 51 | poppedInsns.add(toReplace); 52 | 53 | markChange(); 54 | } 55 | } 56 | } 57 | })); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | uwu.narumi 8 | Diobfuscator 9 | pom 10 | 2.0-SNAPSHOT 11 | 12 | 13 | 14 | testData/src/java 15 | 16 | deobfuscator-api 17 | deobfuscator-impl 18 | deobfuscator-transformers 19 | reverse-engineering 20 | 21 | 22 | 23 | UTF-8 24 | 25 | 17 26 | 17 27 | 28 | 29 | 30 | 31 | org.jetbrains 32 | annotations 33 | 24.1.0 34 | 35 | 36 | 37 | 38 | 39 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /reverse-engineering/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | uwu.narumi 8 | Diobfuscator 9 | 2.0-SNAPSHOT 10 | 11 | 12 | reverse-engineering 13 | 14 | 15 | 17 16 | 17 17 | UTF-8 18 | 19 | 20 | -------------------------------------------------------------------------------- /reverse-engineering/src/main/java/qprotect/InvokeDynamicSample.java: -------------------------------------------------------------------------------- 1 | package qprotect; 2 | 3 | import java.lang.invoke.ConstantCallSite; 4 | import java.lang.invoke.MethodHandle; 5 | import java.lang.invoke.MethodHandles; 6 | import java.lang.invoke.MethodType; 7 | import java.util.Arrays; 8 | 9 | public class InvokeDynamicSample { 10 | public static void main(String[] args) { 11 | invokeBootstrapMethod("SlAyclR2ZWV6bXBLem8waA==", "<\u001e&W\t\u001f=\\t\u00077D\u0011=0\u000f\n\u0003^\u001e\u00064O\\'\u0005.\u0017W?3\u0003l 2]+4\u000b\u0005\t\u0001q>\r\btQ\u001a\u0000u*\u0002]dE><~\u001f4l\u0011-T[O\u0013\n.OZ}\u00062\u0016\rB\u0002S($3D!7,*\f\nM\u0005\u00005TR=X\u0012\u0018RZ%V1bb\u000e"); 12 | } 13 | 14 | public static Object invokeBootstrapMethod(Object xorKeyObj, Object encryptedDataObj) { 15 | // Extract encrypted data and XOR key 16 | String encryptedData = (String)encryptedDataObj; 17 | char[] xorKey = ((String)xorKeyObj).toCharArray(); 18 | 19 | // Decrypt the data using XOR cipher 20 | char[] decryptedChars = new char[encryptedData.length()]; 21 | char[] encryptedChars = encryptedData.toCharArray(); 22 | for (int i = 0; i < encryptedData.length(); i++) { 23 | decryptedChars[i] = (char)(encryptedChars[i] ^ xorKey[i % xorKey.length]); 24 | } 25 | 26 | // Convert decrypted data to string and split parts 27 | byte[] decryptedBytes = new String(decryptedChars).getBytes(); 28 | String decryptedString = new String(decryptedBytes); 29 | String[] parts = decryptedString.split("4Sa16wdk"); // Dynamic 30 | 31 | System.out.println(Arrays.toString(parts)); 32 | 33 | // Extract method information from parts 34 | String ownerClass = parts[0]; // Class containing the method 35 | String methodName = parts[1]; // Name of the method 36 | String methodDesc = parts[2]; // Method descriptor 37 | String specialClass = parts[3]; // Special class for super calls 38 | int methodType = Integer.parseInt(parts[4]); // Type of method call 39 | 40 | System.out.println("Owner class: " + ownerClass); 41 | System.out.println("Method name: " + methodName); 42 | System.out.println("Method desc: " + methodDesc); 43 | System.out.println("Special class: " + specialClass); 44 | System.out.println("Method type: " + methodType); 45 | 46 | return null; 47 | 48 | /*MethodHandle handle; 49 | try { 50 | // Load required classes 51 | Class ownerClassType = Class.forName(ownerClass); 52 | Class specialClassType = Class.forName(specialClass); 53 | ClassLoader loader = InvokeDynamicSample.class.getClassLoader(); 54 | MethodType type = MethodType.fromMethodDescriptorString(methodDesc, loader); 55 | 56 | // Create appropriate method handle based on call type 57 | switch (methodType) { 58 | case 548: // Static method call // Dynamic 59 | handle = ((MethodHandles.Lookup)lookupObj).findStatic(ownerClassType, methodName, type); 60 | break; 61 | case 10116: // Virtual method call // Dynamic 62 | handle = ((MethodHandles.Lookup)lookupObj).findVirtual(ownerClassType, methodName, type); 63 | break; 64 | case 25620: // Special method call (super) // Dynamic 65 | handle = ((MethodHandles.Lookup)lookupObj).findSpecial(ownerClassType, methodName, type, specialClassType); 66 | break; 67 | default: 68 | throw new BootstrapMethodError(); 69 | } 70 | 71 | // Adapt the method handle to the target type 72 | handle = handle.asType((MethodType)targetMethodTypeObj); 73 | } catch (Exception e) { 74 | e.printStackTrace(); 75 | throw new BootstrapMethodError(); 76 | } 77 | 78 | return new ConstantCallSite(handle);*/ 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /reverse-engineering/src/main/java/qprotect/StringEncryption.java: -------------------------------------------------------------------------------- 1 | package qprotect; 2 | 3 | public class StringEncryption { 4 | public static void main(String[] args) { 5 | System.out.println(decryptString( 6 | "퇧\uea6e롩톮\uea71롽퇳\uea60", "\u001an��4�", "w\u000bᅭフAワy\u0005ルネWヨ5\u001f゙ル_ミ5*ヨミpワw\u000fレリ", 2037571890, -791401, 253081604, 1890996 7 | )); 8 | } 9 | 10 | /** 11 | * Decrypts an encrypted string using multiple XOR operations and stack trace validation 12 | * 13 | * @param encryptedText The main encrypted text to decrypt 14 | * @param xorKey The key used for initial XOR operation 15 | * @param encryptedData Secondary encrypted data 16 | * @param salt1 First salt value for XOR 17 | * @param salt2 Second salt value for XOR 18 | * @param salt3 Third salt value for XOR 19 | * @param salt4 Fourth salt value for XOR 20 | * @return Decrypted string 21 | */ 22 | public static String decryptString(String encryptedText, String xorKey, String encryptedData, 23 | int salt1, int salt2, int salt3, int salt4) { 24 | // First decryption phase - XOR with key 25 | char[] encryptedChars = encryptedData.toCharArray(); 26 | char[] decryptedChars = new char[encryptedChars.length]; 27 | char[] keyChars = xorKey.toCharArray(); 28 | //StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); 29 | 30 | // Perform initial XOR decryption 31 | for (int i = 0; i < encryptedChars.length; i++) { 32 | decryptedChars[i] = (char)(encryptedChars[i] ^ keyChars[i % keyChars.length]); 33 | } 34 | 35 | // Calculate hash values for verification 36 | String decryptedString = new String(decryptedChars); 37 | int stringHash = decryptedString.hashCode(); 38 | int xorValue = salt2 - salt4 - salt1; 39 | 40 | // Stack trace validation (anti-tampering) 41 | //stackTrace[2].getClassName().hashCode(); 42 | //stackTrace[2].getMethodName().hashCode(); 43 | 44 | // Second decryption phase - Triple XOR pattern 45 | char[] finalEncrypted = encryptedText.toCharArray(); 46 | char[] finalDecrypted = new char[finalEncrypted.length]; 47 | 48 | for (int i = 0; i < finalDecrypted.length; i++) { 49 | switch (i % 3) { 50 | case 0: 51 | finalDecrypted[i] = (char)(xorValue ^ stringHash ^ finalEncrypted[i]); 52 | break; 53 | case 1: 54 | finalDecrypted[i] = (char)(salt4 ^ xorValue ^ finalEncrypted[i]); 55 | break; 56 | case 2: 57 | finalDecrypted[i] = (char)(salt3 ^ finalEncrypted[i]); 58 | } 59 | } 60 | 61 | return new String(finalDecrypted); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /reverse-engineering/src/main/java/zelix/longdecrypter/ILongDecrypter.java: -------------------------------------------------------------------------------- 1 | package zelix.longdecrypter; 2 | 3 | public interface ILongDecrypter { 4 | void setChild(ILongDecrypter parent); 5 | 6 | int[] getEncryptionInts(); 7 | 8 | long decrypt(long decryptKey); 9 | 10 | void setKey(long key); 11 | 12 | boolean lessThanOrEqual(ILongDecrypter other); 13 | } 14 | -------------------------------------------------------------------------------- /reverse-engineering/src/main/java/zelix/longdecrypter/Main.java: -------------------------------------------------------------------------------- 1 | package zelix.longdecrypter; 2 | 3 | import java.lang.invoke.MethodHandles; 4 | 5 | public class Main { 6 | // Usage 7 | 8 | public static void main(String[] args) { 9 | // decrypterA must be decrypted first for decrypterB to be decrypted correctly. So there is specific decryption order: 10 | // decrypterA -> decrypterB 11 | ILongDecrypter decrypterA = SimpleLongDecrypter.buildNumberDecryptor(273921918217628048L, -8431841081763909460L, MethodHandles.lookup().lookupClass()); 12 | System.out.println(decrypterA); 13 | long a = decrypterA.decrypt(36730249601911L); 14 | System.out.println(a); 15 | 16 | ILongDecrypter decrypterB = SimpleLongDecrypter.buildNumberDecryptor(-5385547845782035026L, 2563870759759473543L, MethodHandles.lookup().lookupClass()); 17 | System.out.println(decrypterB); 18 | long b = decrypterB.decrypt(130571778689313L); 19 | System.out.println(b); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /testData/compiled/custom-classes/JSR.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-classes/JSR.class -------------------------------------------------------------------------------- /testData/compiled/custom-classes/KotlinSample.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-classes/KotlinSample.class -------------------------------------------------------------------------------- /testData/compiled/custom-classes/KotlinSample2.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-classes/KotlinSample2.class -------------------------------------------------------------------------------- /testData/compiled/custom-classes/KotlinSample3.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-classes/KotlinSample3.class -------------------------------------------------------------------------------- /testData/compiled/custom-classes/Pop2Sample.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-classes/Pop2Sample.class -------------------------------------------------------------------------------- /testData/compiled/custom-classes/hp888/IIIlIIIlllIIIlllIIlIllIIlIIIIIllIlIIlIlIllIIIlIIII.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-classes/hp888/IIIlIIIlllIIIlllIIlIllIIlIIIIIllIlIIlIlIllIIIlIIII.class -------------------------------------------------------------------------------- /testData/compiled/custom-classes/hp888/com/bric/colorpicker/ColorPicker$1.mc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-classes/hp888/com/bric/colorpicker/ColorPicker$1.mc -------------------------------------------------------------------------------- /testData/compiled/custom-classes/hp888/com/bric/colorpicker/ColorPicker.mc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-classes/hp888/com/bric/colorpicker/ColorPicker.mc -------------------------------------------------------------------------------- /testData/compiled/custom-classes/hp888/com/bric/colorpicker/ColorPickerMode.mc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-classes/hp888/com/bric/colorpicker/ColorPickerMode.mc -------------------------------------------------------------------------------- /testData/compiled/custom-classes/hp888/pack/MyFunction2.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-classes/hp888/pack/MyFunction2.class -------------------------------------------------------------------------------- /testData/compiled/custom-classes/qprotect/sample1/JSONStringer.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-classes/qprotect/sample1/JSONStringer.class -------------------------------------------------------------------------------- /testData/compiled/custom-classes/qprotect/sample1/JSONTokener.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-classes/qprotect/sample1/JSONTokener.class -------------------------------------------------------------------------------- /testData/compiled/custom-classes/qprotect/sample1/JSONWriter.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-classes/qprotect/sample1/JSONWriter.class -------------------------------------------------------------------------------- /testData/compiled/custom-classes/qprotect/sample1/ParserConfiguration.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-classes/qprotect/sample1/ParserConfiguration.class -------------------------------------------------------------------------------- /testData/compiled/custom-classes/qprotect/sample1/XML.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-classes/qprotect/sample1/XML.class -------------------------------------------------------------------------------- /testData/compiled/custom-classes/qprotect/sample2/ChatUtil.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-classes/qprotect/sample2/ChatUtil.class -------------------------------------------------------------------------------- /testData/compiled/custom-classes/qprotect/sample3/fastcode/IlllIIlIIlllIIIl.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-classes/qprotect/sample3/fastcode/IlllIIlIIlllIIIl.class -------------------------------------------------------------------------------- /testData/compiled/custom-classes/sb27/sample1/ConfigComplete.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-classes/sb27/sample1/ConfigComplete.class -------------------------------------------------------------------------------- /testData/compiled/custom-classes/unknown/obf1/SomeClass1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-classes/unknown/obf1/SomeClass1.class -------------------------------------------------------------------------------- /testData/compiled/custom-classes/unknown/obf1/SomeClass2.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-classes/unknown/obf1/SomeClass2.class -------------------------------------------------------------------------------- /testData/compiled/custom-classes/zkm/EnhancedStringEncManyStrings.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-classes/zkm/EnhancedStringEncManyStrings.class -------------------------------------------------------------------------------- /testData/compiled/custom-classes/zkm/EnhancedStringEncSomeStrings.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-classes/zkm/EnhancedStringEncSomeStrings.class -------------------------------------------------------------------------------- /testData/compiled/custom-classes/zkm/sample1/io/github/repooper/packetevents/PacketEvents.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-classes/zkm/sample1/io/github/repooper/packetevents/PacketEvents.class -------------------------------------------------------------------------------- /testData/compiled/custom-classes/zkm/sample1/io/github/repooper/packetevents/utils/netty/bytebuf/ByteBufUtil.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-classes/zkm/sample1/io/github/repooper/packetevents/utils/netty/bytebuf/ByteBufUtil.class -------------------------------------------------------------------------------- /testData/compiled/custom-classes/zkm/sample1/io/github/repooper/packetevents/utils/netty/bytebuf/ByteBufUtil_7.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-classes/zkm/sample1/io/github/repooper/packetevents/utils/netty/bytebuf/ByteBufUtil_7.class -------------------------------------------------------------------------------- /testData/compiled/custom-classes/zkm/sample1/io/github/repooper/packetevents/utils/netty/bytebuf/ByteBufUtil_8.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-classes/zkm/sample1/io/github/repooper/packetevents/utils/netty/bytebuf/ByteBufUtil_8.class -------------------------------------------------------------------------------- /testData/compiled/custom-classes/zkm/sample1/me/frep/vulcan/spigot/Vulcan_a.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-classes/zkm/sample1/me/frep/vulcan/spigot/Vulcan_a.class -------------------------------------------------------------------------------- /testData/compiled/custom-classes/zkm/sample1/me/frep/vulcan/spigot/Vulcan_m.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-classes/zkm/sample1/me/frep/vulcan/spigot/Vulcan_m.class -------------------------------------------------------------------------------- /testData/compiled/custom-classes/zkm/sample1/me/frep/vulcan/spigot/Vulcan_x.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-classes/zkm/sample1/me/frep/vulcan/spigot/Vulcan_x.class -------------------------------------------------------------------------------- /testData/compiled/custom-classes/zkm/sample2/a/a/a/a/a.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-classes/zkm/sample2/a/a/a/a/a.class -------------------------------------------------------------------------------- /testData/compiled/custom-classes/zkm/sample2/a/a/a/a/a4.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-classes/zkm/sample2/a/a/a/a/a4.class -------------------------------------------------------------------------------- /testData/compiled/custom-classes/zkm/sample2/a/a/a/a/a_.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-classes/zkm/sample2/a/a/a/a/a_.class -------------------------------------------------------------------------------- /testData/compiled/custom-classes/zkm/sample2/a/a/a/a/b.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-classes/zkm/sample2/a/a/a/a/b.class -------------------------------------------------------------------------------- /testData/compiled/custom-classes/zkm/sample2/a/a/a/a/ba.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-classes/zkm/sample2/a/a/a/a/ba.class -------------------------------------------------------------------------------- /testData/compiled/custom-classes/zkm/sample2/a/a/a/a/bc.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-classes/zkm/sample2/a/a/a/a/bc.class -------------------------------------------------------------------------------- /testData/compiled/custom-classes/zkm/sample2/a/a/a/a/c.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-classes/zkm/sample2/a/a/a/a/c.class -------------------------------------------------------------------------------- /testData/compiled/custom-jars/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-jars/.gitkeep -------------------------------------------------------------------------------- /testData/compiled/custom-jars/SnakeGame-obf-zkm.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-jars/SnakeGame-obf-zkm.jar -------------------------------------------------------------------------------- /testData/compiled/custom-jars/grunt-obf.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-jars/grunt-obf.jar -------------------------------------------------------------------------------- /testData/compiled/custom-jars/qprotect-obf.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/narumii/Deobfuscator/1fa52fb8cf0e71261c4e1138ddfc11d00efd0b29/testData/compiled/custom-jars/qprotect-obf.jar -------------------------------------------------------------------------------- /testData/results/custom-classes/JSR.dec: -------------------------------------------------------------------------------- 1 | import java.io.PrintStream; 2 | 3 | public class TestJSR { 4 | public static void main(String[] var0) { 5 | test(System.out); 6 | } 7 | 8 | public static void test(PrintStream var0) { 9 | var0.println("In"); 10 | Object var1 = null; 11 | var0.println("Out"); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /testData/results/custom-classes/Pop2Sample.dec: -------------------------------------------------------------------------------- 1 | package com.arlania; 2 | 3 | import com.arlania.r.H; 4 | import com.arlania.r.W; 5 | import javax.swing.ImageIcon; 6 | import javax.swing.JColorChooser; 7 | import javax.swing.JOptionPane; 8 | import javax.swing.JPanel; 9 | import javax.swing.colorchooser.AbstractColorChooserPanel; 10 | 11 | class Ld implements Runnable { 12 | @Override 13 | public void run() { 14 | AbstractColorChooserPanel[] var5; 15 | JColorChooser var7; 16 | int var4 = (var5 = (var7 = new JColorChooser()).getChooserPanels()).length; 17 | 18 | int var3; 19 | for (int var10000 = var3 = 0; var10000 < var4; var10000 = ++var3) { 20 | String var2; 21 | AbstractColorChooserPanel var6; 22 | switch ((var2 = (var6 = var5[var3]).getDisplayName()).hashCode()) { 23 | case -249666038: 24 | while (false) { 25 | } 26 | 27 | if (!var2.equals(H.ALLATORIxDEMO("\u0013\u001a!\u0019#\u0005%\u001e"))) { 28 | continue; 29 | } 30 | break; 31 | case 71851: 32 | if (!var2.equals(W.ALLATORIxDEMO("\"\u0011<"))) { 33 | continue; 34 | } 35 | break; 36 | case 81069: 37 | if (!var2.equals(H.ALLATORIxDEMO("?\u0007/"))) { 38 | continue; 39 | } 40 | break; 41 | case 2072828: 42 | if (!var2.equals(W.ALLATORIxDEMO("\u0001'\u001b!"))) { 43 | continue; 44 | } 45 | break; 46 | default: 47 | continue; 48 | } 49 | 50 | var7.removeChooserPanel(var6); 51 | } 52 | 53 | var7.setColor(com.arlania.H.Wa[a.ALLATORIxDEMO].KC); 54 | var7.setPreviewPanel(new JPanel()); 55 | var7.getSelectionModel().addChangeListener(new BA(a, var7, a.E, a.ALLATORIxDEMO)); 56 | Client.ALLATORIxDEMO(var7); 57 | JOptionPane.showMessageDialog(null, var7, a.M, -1, new ImageIcon()); 58 | } 59 | 60 | Ld(Client var1, int var2, String var3, int var4) { 61 | a.j = var1; 62 | a.ALLATORIxDEMO = var2; 63 | a.M = var3; 64 | a.E = var4; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /testData/results/custom-classes/hp888/com/bric/colorpicker/ColorPicker$1.dec: -------------------------------------------------------------------------------- 1 | package com.bric.colorpicker; 2 | 3 | // $VF: synthetic class 4 | public class ColorPicker$1 { 5 | static { 6 | try { 7 | $SwitchMap$com$bric$colorpicker$ColorPickerMode[ColorPickerMode.HUE.ordinal()] = 1; 8 | } catch (NoSuchFieldError var6) { 9 | } 10 | 11 | try { 12 | $SwitchMap$com$bric$colorpicker$ColorPickerMode[ColorPickerMode.SATURATION.ordinal()] = 2; 13 | } catch (NoSuchFieldError var5) { 14 | } 15 | 16 | try { 17 | $SwitchMap$com$bric$colorpicker$ColorPickerMode[ColorPickerMode.BRIGHTNESS.ordinal()] = 3; 18 | } catch (NoSuchFieldError var4) { 19 | } 20 | 21 | try { 22 | $SwitchMap$com$bric$colorpicker$ColorPickerMode[ColorPickerMode.RED.ordinal()] = 4; 23 | } catch (NoSuchFieldError var3) { 24 | } 25 | 26 | try { 27 | $SwitchMap$com$bric$colorpicker$ColorPickerMode[ColorPickerMode.GREEN.ordinal()] = 5; 28 | } catch (NoSuchFieldError var2) { 29 | } 30 | 31 | try { 32 | $SwitchMap$com$bric$colorpicker$ColorPickerMode[ColorPickerMode.BLUE.ordinal()] = 6; 33 | } catch (NoSuchFieldError var1) { 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /testData/results/custom-classes/hp888/com/bric/colorpicker/ColorPickerMode.dec: -------------------------------------------------------------------------------- 1 | package com.bric.colorpicker; 2 | 3 | public enum ColorPickerMode { 4 | HUE(360), 5 | BRIGHTNESS(100), 6 | SATURATION(100), 7 | RED(255), 8 | GREEN(255), 9 | BLUE(255), 10 | ALPHA(255); 11 | 12 | private int max; 13 | 14 | private ColorPickerMode(int var3) { 15 | this.max = var3; 16 | } 17 | 18 | public int getMax() { 19 | return this.max; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /testData/results/custom-classes/qprotect/sample1/JSONStringer.dec: -------------------------------------------------------------------------------- 1 | package org.json; 2 | 3 | import java.io.StringWriter; 4 | 5 | public class JSONStringer extends org.json.JSONWriter { 6 | public static final int IIlIIlIIIlIIIl; 7 | public static final boolean IIlIIIIIIIllll = false; 8 | 9 | public JSONStringer() { 10 | super(new StringWriter()); 11 | org.json.ParserConfiguration.IIlIIIIlIIII(); 12 | } 13 | 14 | @Override 15 | public String toString() { 16 | return this.mode == 'd' ? this.writer.toString() : null; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /testData/results/custom-classes/sb27/sample1/ConfigComplete.dec: -------------------------------------------------------------------------------- 1 | import java.util.ArrayList; 2 | import java.util.List; 3 | import org.bukkit.command.Command; 4 | import org.bukkit.command.CommandSender; 5 | import org.bukkit.command.TabCompleter; 6 | import org.bukkit.util.StringUtil; 7 | 8 | public class ConfigComplete implements TabCompleter { 9 | public List onTabComplete(CommandSender var1, Command var2, String var3, String[] var4) { 10 | ArrayList var5 = new ArrayList(); 11 | ArrayList var6 = new ArrayList(); 12 | if (var4.length == 1) { 13 | var6.add("doBedExplode"); 14 | var6.add("doBrutesSpawn"); 15 | var6.add("oldPiglinDrop"); 16 | var6.add("compassOnRespawn"); 17 | StringUtil.copyPartialMatches(var4[0], var6, var5); 18 | } 19 | 20 | return var5; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /testData/results/custom-classes/unknown/obf1/SomeClass1.dec: -------------------------------------------------------------------------------- 1 | import org.bukkit.Bukkit; 2 | import org.bukkit.event.Listener; 3 | 4 | public class SomeClass1 implements Listener { 5 | public static int lIIIIlllIlIIllIIIlllllIlllIIllIllIIlIIllIIIllIIlIllllIIIIllIIlIIIIlIllIIlllIllIllllIIIlllIllIllIlllIlllllIIlIIIlllllIIIlIlIllllIllllIllllIIlIIllIlIIllIIllIIlIllIIllllIlllIIIIlIIllIIIIIIIIllllIlIllllIl( 6 | int var0 7 | ) { 8 | Bukkit.getServer(); 9 | Bukkit.getServer(); 10 | Bukkit.getServer(); 11 | Bukkit.getServer(); 12 | Bukkit.getServer(); 13 | Bukkit.getServer(); 14 | Bukkit.getServer(); 15 | return 0; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /testData/results/custom-classes/unknown/obf1/SomeClass2.dec: -------------------------------------------------------------------------------- 1 | import org.bukkit.ChatColor; 2 | import org.bukkit.Material; 3 | import org.bukkit.enchantments.Enchantment; 4 | import org.bukkit.entity.Player; 5 | import org.bukkit.event.EventHandler; 6 | import org.bukkit.event.Listener; 7 | import org.bukkit.event.player.PlayerRespawnEvent; 8 | import org.bukkit.inventory.ItemFlag; 9 | import org.bukkit.inventory.ItemStack; 10 | import org.bukkit.inventory.meta.ItemMeta; 11 | 12 | public class SomeClass2 implements Listener { 13 | @EventHandler 14 | public void onPlayerRespawn(PlayerRespawnEvent var1) { 15 | Player var2 = var1.getPlayer(); 16 | if (lIIlIIIllIIlIlllIIIIllllIIllllIlIlIIlIlllIIlIlIllIlIIIIlIllllIIIIIlllllIIllIIIllIIlllIIlllllIlIIIIIIlIIIIIllIIlllIlllIllIlIlllllIlIlllIIIlIllIIIlIlllIIIIIlllIIIIllIlllIlIlIllllIlIIIIllllIlIlIIlIIlIllI.IllIllIlIllIIIIlllIllIlIlIllIIllIlllllllllIlIIIIIIlllllllIIlIllllIIIlIllIllIlIIIIlllIlllIIlllIIIIIIIIIlIlllIlllIIlIllIllIIlIIllIIlllIIIlllIllIllIIllIlIllllllIIllllIllIIIlllIllllIIlllIIIlIlIIlllIIIIlIl 17 | .getConfig() 18 | .getBoolean("compassOnRespawn") 19 | && !var2.getInventory().contains(Material.COMPASS)) { 20 | ItemStack var3 = new ItemStack(Material.COMPASS, 1); 21 | var3.addUnsafeEnchantment(Enchantment.VANISHING_CURSE, 1); 22 | ItemMeta var4 = var3.getItemMeta(); 23 | var4.setDisplayName(ChatColor.RED + "[" + ChatColor.AQUA + "" + ChatColor.BOLD + "Compass Tracker" + ChatColor.RESET + "" + ChatColor.RED + "]"); 24 | var4.addItemFlags(new ItemFlag[]{ItemFlag.HIDE_ENCHANTS}); 25 | var3.setItemMeta(var4); 26 | var2.getInventory().addItem(new ItemStack[]{var3}); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /testData/results/custom-classes/zkm/sample1/io/github/repooper/packetevents/utils/netty/bytebuf/ByteBufUtil.dec: -------------------------------------------------------------------------------- 1 | package io.github.repooper.packetevents.utils.netty.bytebuf; 2 | 3 | public interface ByteBufUtil { 4 | Object Vulcan_y(); 5 | 6 | Object Vulcan_u(byte[] var1); 7 | 8 | void Vulcan_G(Object var1); 9 | 10 | void Vulcan_u(Object var1); 11 | 12 | byte[] Vulcan_S(Object var1, long var2); 13 | 14 | void Vulcan_S(Object var1, byte[] var2, long var3); 15 | } 16 | -------------------------------------------------------------------------------- /testData/results/custom-classes/zkm/sample1/io/github/repooper/packetevents/utils/netty/bytebuf/ByteBufUtil_7.dec: -------------------------------------------------------------------------------- 1 | package io.github.repooper.packetevents.utils.netty.bytebuf; 2 | 3 | import io.netty.buffer.Unpooled; 4 | import me.frep.vulcan.spigot.check.AbstractCheck; 5 | import net.minecraft.util.io.netty.buffer.ByteBuf; 6 | import net.minecraft.util.io.netty.util.internal.EmptyArrays; 7 | 8 | public final class ByteBufUtil_7 implements ByteBufUtil { 9 | @Override 10 | public Object Vulcan_y() { 11 | return Unpooled.buffer(); 12 | } 13 | 14 | @Override 15 | public Object Vulcan_u(byte[] var1) { 16 | return Unpooled.wrappedBuffer(var1); 17 | } 18 | 19 | @Override 20 | public void Vulcan_G(Object var1) { 21 | ((ByteBuf)var1).retain(); 22 | } 23 | 24 | @Override 25 | public void Vulcan_u(Object var1) { 26 | ((ByteBuf)var1).release(); 27 | } 28 | 29 | @Override 30 | public byte[] Vulcan_S(Object var1, long var2) { 31 | String[] var10000 = ByteBufUtil_8.Vulcan_O(); 32 | ByteBuf var6 = (ByteBuf)var1; 33 | String[] var5 = var10000; 34 | int var8 = var6.refCnt(); 35 | String[] var10001 = var5; 36 | if (var2 > 0L) { 37 | if (var5 != null) { 38 | if (var8 < 1) { 39 | return EmptyArrays.EMPTY_BYTES; 40 | } 41 | 42 | var8 = var6.hasArray(); 43 | } 44 | 45 | var10001 = var5; 46 | } 47 | 48 | if (var10001 != null) { 49 | if (var8 != 0) { 50 | byte[] var7 = var6.array(); 51 | if (var5 != null) { 52 | return var7; 53 | } 54 | 55 | AbstractCheck.Vulcan_t(!AbstractCheck.Vulcan_a()); 56 | } 57 | 58 | var8 = var6.readableBytes(); 59 | } 60 | 61 | byte[] var9 = new byte[var8]; 62 | var6.getBytes(var6.readerIndex(), var9); 63 | return var9; 64 | } 65 | 66 | @Override 67 | public void Vulcan_S(Object var1, byte[] var2, long var3) { 68 | String[] var10000 = ByteBufUtil_8.Vulcan_O(); 69 | ByteBuf var7 = (ByteBuf)var1; 70 | String[] var6 = var10000; 71 | int var9 = var7.refCnt(); 72 | if (var6 != null) { 73 | if (var9 < 1) { 74 | return; 75 | } 76 | 77 | var9 = var2.length; 78 | } 79 | 80 | int var8 = var9; 81 | if (var3 >= 0L && var6 != null) { 82 | if (var7.capacity() < var8) { 83 | var7.capacity(var8); 84 | } 85 | 86 | var7.setBytes(0, var2); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /testData/results/custom-classes/zkm/sample1/io/github/repooper/packetevents/utils/netty/bytebuf/ByteBufUtil_8.dec: -------------------------------------------------------------------------------- 1 | package io.github.repooper.packetevents.utils.netty.bytebuf; 2 | 3 | import io.netty.buffer.ByteBuf; 4 | import io.netty.buffer.Unpooled; 5 | import io.netty.util.internal.EmptyArrays; 6 | import me.frep.vulcan.spigot.check.AbstractCheck; 7 | 8 | public final class ByteBufUtil_8 implements ByteBufUtil { 9 | private static String[] Vulcan_n; 10 | 11 | @Override 12 | public Object Vulcan_y() { 13 | return Unpooled.buffer(); 14 | } 15 | 16 | @Override 17 | public Object Vulcan_u(byte[] var1) { 18 | return Unpooled.wrappedBuffer(var1); 19 | } 20 | 21 | @Override 22 | public void Vulcan_G(Object var1) { 23 | ((ByteBuf)var1).retain(); 24 | } 25 | 26 | @Override 27 | public void Vulcan_u(Object var1) { 28 | ((ByteBuf)var1).release(); 29 | } 30 | 31 | @Override 32 | public byte[] Vulcan_S(Object var1, long var2) { 33 | String[] var10000 = Vulcan_O(); 34 | ByteBuf var6 = (ByteBuf)var1; 35 | String[] var5 = var10000; 36 | int var8 = var6.refCnt(); 37 | String[] var10001 = var5; 38 | if (var2 > 0L) { 39 | if (var5 != null) { 40 | if (var8 < 1) { 41 | return EmptyArrays.EMPTY_BYTES; 42 | } 43 | 44 | var8 = var6.hasArray(); 45 | } 46 | 47 | var10001 = var5; 48 | } 49 | 50 | byte[] var7; 51 | label39: { 52 | if (var10001 != null) { 53 | if (var8 != 0) { 54 | var7 = var6.array(); 55 | if (var2 < 0L || var5 != null) { 56 | break label39; 57 | } 58 | } 59 | 60 | var8 = var6.readableBytes(); 61 | } 62 | 63 | var7 = new byte[var8]; 64 | var6.getBytes(var6.readerIndex(), var7); 65 | } 66 | 67 | byte var9 = AbstractCheck.Vulcan_l(); 68 | if (var2 >= 0L) { 69 | if (var9 == 0) { 70 | return var7; 71 | } 72 | 73 | var9 = 1; 74 | } 75 | 76 | Vulcan_L(new String[var9]); 77 | return var7; 78 | } 79 | 80 | @Override 81 | public void Vulcan_S(Object var1, byte[] var2, long var3) { 82 | String[] var10000 = Vulcan_O(); 83 | ByteBuf var7 = (ByteBuf)var1; 84 | String[] var6 = var10000; 85 | int var9 = var7.refCnt(); 86 | if (var6 != null) { 87 | if (var9 < 1) { 88 | return; 89 | } 90 | 91 | var9 = var2.length; 92 | } 93 | 94 | int var8 = var9; 95 | if (var3 > 0L && var6 != null) { 96 | if (var7.capacity() < var8) { 97 | var7.capacity(var8); 98 | } 99 | 100 | var7.setBytes(0, var2); 101 | } 102 | } 103 | 104 | public static void Vulcan_L(String[] var0) { 105 | Vulcan_n = var0; 106 | } 107 | 108 | public static String[] Vulcan_O() { 109 | return Vulcan_n; 110 | } 111 | 112 | static { 113 | if (Vulcan_O() == null) { 114 | Vulcan_L(new String[5]); 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /testData/results/custom-classes/zkm/sample2/a/a/a/a/ba.dec: -------------------------------------------------------------------------------- 1 | package a.a.a.a; 2 | 3 | import java.util.Comparator; 4 | 5 | public class ba extends a_ { 6 | private static final long serialVersionUID = 1L; 7 | 8 | public ba(long var1, Number var3, Number var4, Comparator var5) { 9 | var1 = 87486991067610L ^ var1; 10 | int var6 = (int)((var1 ^ 101419208877758L) >>> 32); 11 | long var7 = (var1 ^ 101419208877758L) << 32 >>> 32; 12 | super(var3, var4, var6, var7, var5); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /testData/results/custom-classes/zkm/sample2/a/a/a/a/bc.dec: -------------------------------------------------------------------------------- 1 | package a.a.a.a; 2 | 3 | public final class bc extends ba { 4 | private static final long serialVersionUID = 1L; 5 | 6 | public static bc a(int var0, long var1, int var3) { 7 | var1 = 90497055901648L ^ var1; 8 | long var5 = var1 ^ 32932499080870L; 9 | return b(new Object[]{var0, var5, var3}); 10 | } 11 | 12 | public static bc b(Integer var0, long var1, Integer var3) { 13 | var1 = 90497055901648L ^ var1; 14 | int var5 = (int)((var1 ^ 61248110729654L) >>> 56); 15 | long var6 = (var1 ^ 61248110729654L) << 8 >>> 8; 16 | return new bc((byte)var5, var0, var3, var6); 17 | } 18 | 19 | private bc(byte var1, Integer var2, Integer var3, long var4) { 20 | long var6 = ((long)var1 << 56 | var4 << 8 >>> 8) ^ 90497055901648L; 21 | long var8 = var6 ^ 83241345321299L; 22 | super(var8, var2, var3, null); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /testData/results/custom-jars/SnakeGame-obf-zkm/a.dec: -------------------------------------------------------------------------------- 1 | import java.awt.event.KeyAdapter; 2 | import java.awt.event.KeyEvent; 3 | 4 | public class a extends KeyAdapter { 5 | @Override 6 | public void keyPressed(KeyEvent var1) { 7 | boolean var4; 8 | label60: { 9 | label61: { 10 | label62: { 11 | var4 = d.h; 12 | int var10000 = var1.getKeyCode(); 13 | if (!var4) { 14 | switch (var10000) { 15 | case 37: 16 | break label61; 17 | case 38: 18 | break label62; 19 | case 39: 20 | var10000 = d.e; 21 | break; 22 | case 40: 23 | break label60; 24 | default: 25 | return; 26 | } 27 | } 28 | 29 | if (!var4) { 30 | if (var10000 == 2) { 31 | return; 32 | } 33 | 34 | var10000 = 1; 35 | } 36 | 37 | d.e = var10000; 38 | if (!var4) { 39 | return; 40 | } 41 | } 42 | 43 | int var5 = d.e; 44 | if (!var4) { 45 | if (d.e == 4) { 46 | return; 47 | } 48 | 49 | var5 = 3; 50 | } 51 | 52 | d.e = var5; 53 | if (!var4) { 54 | return; 55 | } 56 | } 57 | 58 | int var6 = d.e; 59 | if (!var4) { 60 | if (d.e == 1) { 61 | return; 62 | } 63 | 64 | var6 = 2; 65 | } 66 | 67 | d.e = var6; 68 | if (!var4) { 69 | return; 70 | } 71 | } 72 | 73 | int var7 = d.e; 74 | if (!var4) { 75 | if (d.e == 3) { 76 | return; 77 | } 78 | 79 | var7 = 4; 80 | } 81 | 82 | d.e = var7; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /testData/results/custom-jars/SnakeGame-obf-zkm/b.dec: -------------------------------------------------------------------------------- 1 | import java.awt.Color; 2 | import java.util.ArrayList; 3 | 4 | public class b { 5 | ArrayList a = new ArrayList<>(); 6 | int b; 7 | f c; 8 | 9 | public b(int var1) { 10 | this.a.add(Color.darkGray); 11 | this.a.add(Color.BLUE); 12 | this.a.add(Color.white); 13 | this.b = var1; 14 | this.c = new f(this.a.get(this.b)); 15 | } 16 | 17 | public void a(int var1) { 18 | this.c.a(new Object[]{this.a.get(var1)}); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /testData/results/custom-jars/SnakeGame-obf-zkm/c.dec: -------------------------------------------------------------------------------- 1 | public class c { 2 | public int a; 3 | public int b; 4 | public static boolean c; 5 | 6 | public c(int var1, int var2) { 7 | this.a = var1; 8 | this.b = var2; 9 | } 10 | 11 | public void a(int var1, int var2) { 12 | this.a = var1; 13 | this.b = var2; 14 | } 15 | 16 | public int a() { 17 | return this.a; 18 | } 19 | 20 | public int b() { 21 | return this.b; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /testData/results/custom-jars/SnakeGame-obf-zkm/f.dec: -------------------------------------------------------------------------------- 1 | import java.awt.Color; 2 | import javax.swing.JPanel; 3 | 4 | public class f extends JPanel { 5 | private static final long serialVersionUID = 1L; 6 | 7 | public f(Color var1) { 8 | this.setBackground(var1); 9 | } 10 | 11 | public void a(Color var1) { 12 | this.setBackground(var1); 13 | this.repaint(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /testData/results/custom-jars/grunt-obf/com/top1/Main$ConstantPool.dec: -------------------------------------------------------------------------------- 1 | package com.top1; 2 | 3 | public final class Main$ConstantPool { 4 | } 5 | -------------------------------------------------------------------------------- /testData/results/custom-jars/grunt-obf/com/top1/Main$NumberData$ConstantPool.dec: -------------------------------------------------------------------------------- 1 | package com.top1; 2 | 3 | public final class Main$NumberData$ConstantPool { 4 | } 5 | -------------------------------------------------------------------------------- /testData/results/custom-jars/grunt-obf/com/top1/Main$NumberData.dec: -------------------------------------------------------------------------------- 1 | package com.top1; 2 | 3 | public class Main$NumberData { 4 | } 5 | -------------------------------------------------------------------------------- /testData/results/custom-jars/grunt-obf/com/top1/Main$processor$ConstantPool.dec: -------------------------------------------------------------------------------- 1 | package com.top1; 2 | 3 | public final class Main$processor$ConstantPool { 4 | public static String const_nmionQdI1EMWwVW = Sh4GunDo6B(Re3Shyl1Rd("یۡۨۨ۫ڤۥ۪۠ڤ۳ۡۨۧ۫۩ۡڥ")); 5 | 6 | private static String Sh4GunDo6B(String var0) { 7 | StringBuilder var1 = new StringBuilder(); 8 | 9 | for (int var2 = 0; var2 < var0.length(); var2++) { 10 | var1.append((char)(var0.charAt(var2) ^ 2043)); 11 | } 12 | 13 | return var1.toString(); 14 | } 15 | 16 | private static String Re3Shyl1Rd(String var0) { 17 | StringBuilder var1 = new StringBuilder(); 18 | 19 | for (int var2 = 0; var2 < var0.length(); var2++) { 20 | var1.append((char)(var0.charAt(var2) ^ 1215)); 21 | } 22 | 23 | return var1.toString(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /testData/results/custom-jars/grunt-obf/com/top1/Main$processor$NumberData$ConstantPool.dec: -------------------------------------------------------------------------------- 1 | package com.top1; 2 | 3 | public final class Main$processor$NumberData$ConstantPool { 4 | } 5 | -------------------------------------------------------------------------------- /testData/results/custom-jars/grunt-obf/com/top1/Main$processor$NumberData.dec: -------------------------------------------------------------------------------- 1 | package com.top1; 2 | 3 | public class Main$processor$NumberData { 4 | } 5 | -------------------------------------------------------------------------------- /testData/results/custom-jars/grunt-obf/com/top1/Main$processor.dec: -------------------------------------------------------------------------------- 1 | package com.top1; 2 | 3 | public class Main$processor { 4 | } 5 | -------------------------------------------------------------------------------- /testData/results/custom-jars/grunt-obf/com/top1/Main.dec: -------------------------------------------------------------------------------- 1 | package com.top1; 2 | 3 | public class Main { 4 | public static void main(String[] args) { 5 | System.out.printf(hqz1VQsWg9(tWMq7yk14u(btvQBodoND(t4094mjSjm(LKgJwTY44h(Ylhv2OxrVH(Main$processor$ConstantPool.const_nmionQdI1EMWwVW))))))); 6 | 7 | for (int i = 1; i <= 5; i++) { 8 | System.out.println("i = " + i); 9 | } 10 | } 11 | 12 | private static String hqz1VQsWg9(String var0) { 13 | StringBuilder var1 = new StringBuilder(); 14 | 15 | for (int var2 = 0; var2 < var0.length(); var2++) { 16 | char var10001 = var0.charAt(var2); 17 | var1.append((char)((var10001 | 133) - (var10001 & 133))); 18 | } 19 | 20 | return var1.toString(); 21 | } 22 | 23 | private static String tWMq7yk14u(String var0) { 24 | StringBuilder var1 = new StringBuilder(); 25 | 26 | for (int var2 = 0; var2 < var0.length(); var2++) { 27 | char var10001 = var0.charAt(var2); 28 | short var10004 = 1338; 29 | var1.append((char)(var10001 & -1339 | 1338 & ~var10001)); 30 | } 31 | 32 | return var1.toString(); 33 | } 34 | 35 | private static String btvQBodoND(String var0) { 36 | StringBuilder var1 = new StringBuilder(); 37 | 38 | for (int var2 = 0; var2 < var0.length(); var2++) { 39 | char var10001 = var0.charAt(var2); 40 | var1.append((char)((var10001 | 2022) - (var10001 & 2022))); 41 | } 42 | 43 | return var1.toString(); 44 | } 45 | 46 | private static String t4094mjSjm(String var0) { 47 | StringBuilder var1 = new StringBuilder(); 48 | 49 | for (int var2 = 0; var2 < var0.length(); var2++) { 50 | char var10001 = var0.charAt(var2); 51 | int var10004 = var10001 | 2033; 52 | short var10003 = 2033; 53 | var1.append((char)(var10004 & (-2034 | ~var10001))); 54 | } 55 | 56 | return var1.toString(); 57 | } 58 | 59 | private static String LKgJwTY44h(String var0) { 60 | StringBuilder var1 = new StringBuilder(); 61 | 62 | for (int var2 = 0; var2 < var0.length(); var2++) { 63 | char var10001 = var0.charAt(var2); 64 | short var10004 = 542; 65 | var1.append((char)(var10001 & -543 | 542 & ~var10001)); 66 | } 67 | 68 | return var1.toString(); 69 | } 70 | 71 | private static String Ylhv2OxrVH(String var0) { 72 | StringBuilder var1 = new StringBuilder(); 73 | 74 | for (int var2 = 0; var2 < var0.length(); var2++) { 75 | char var10001 = var0.charAt(var2); 76 | var1.append((char)((var10001 | 630) - (var10001 & 630))); 77 | } 78 | 79 | return var1.toString(); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /testData/results/custom-jars/qprotect-obf/org/example/Main2.dec: -------------------------------------------------------------------------------- 1 | package org.example; 2 | 3 | public class Main2 { 4 | public static final int c; 5 | public static final boolean d = false; 6 | 7 | public static void main(String[] var0) { 8 | checkLicence("OwhHid6Rqbr75Uw2H9ih0rm3KZnm8FESo18UalSAwnHOMRDFxY6zxDH7TwTO4zBaUJLv0SbSa8fPvY36jDxRdJ2UkLdxPIuLr7tkWhVibx5WzubxyaFeBaFvwbtBnE32"); 9 | } 10 | 11 | public static void checkLicence(String var0) { 12 | String var1 = var0.substring(0, 8); 13 | String var2 = var0.substring(8, 16); 14 | String var3 = var0.substring(16, 24); 15 | String var4 = var0.substring(24, 32); 16 | String var5 = caesarCipher(var1, 15) + ""; 17 | String var6 = caesarCipher(var2, 7) + ""; 18 | String var7 = caesarCipher(var3, 23) + ""; 19 | String var8 = caesarCipher(var4, 11) + ""; 20 | System.out.println(var5); 21 | System.out.println(var6); 22 | System.out.println(var7); 23 | System.out.println(var8); 24 | } 25 | 26 | public static StringBuffer caesarCipher(String var0, int var1) { 27 | StringBuffer var2 = new StringBuffer(); 28 | 29 | for (int var3 = 0; var3 < var0.length(); var3++) { 30 | if (!Character.isDigit(var0.charAt(var3))) { 31 | if (!Character.isUpperCase(var0.charAt(var3))) { 32 | char var4 = (char)((var0.charAt(var3) + var1 - 97) % 26 + 97); 33 | var2.append(var4); 34 | } else { 35 | char var5 = (char)((var0.charAt(var3) + var1 - 65) % 26 + 65); 36 | var2.append(var5); 37 | } 38 | } else { 39 | char var6 = var0.charAt(var3); 40 | var2.append(var6); 41 | } 42 | } 43 | 44 | return var2; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /testData/results/java/TestInlineLocalVariables.dec: -------------------------------------------------------------------------------- 1 | public class TestInlineLocalVariables { 2 | public static void test(int a) { 3 | System.out.println(a); 4 | if (System.currentTimeMillis() == 123L) { 5 | a = 3; 6 | } 7 | 8 | System.out.println(a); 9 | } 10 | 11 | public static void test2() { 12 | System.out.println(1); 13 | System.out.println(2); 14 | System.out.println(3); 15 | System.out.println(4); 16 | } 17 | 18 | public static boolean test3() { 19 | boolean testVar; 20 | try { 21 | testVar = System.currentTimeMillis() == 123L; 22 | } finally { 23 | System.out.println("123"); 24 | } 25 | 26 | return testVar; 27 | } 28 | 29 | public static void test4() { 30 | Object someObj = new Object(); 31 | System.out.println("" + 5 + true + 123123123123123123L); 32 | System.out.println(123123.123123 + (double)123123.125F + "test" + someObj); 33 | } 34 | 35 | public static void mangle() { 36 | float e = 89.345F; 37 | String f = "asd"; 38 | Object someObj = new Object(); 39 | System.out.println("" + 321 + true + 657657L); 40 | System.out.println(1455.45 + (double)89.345F + "asd" + someObj); 41 | 42 | try { 43 | try { 44 | e = 45354.0F; 45 | f = "gfj"; 46 | someObj = new Object(); 47 | System.out.println("" + 453453453 + false + 7856783457834L); 48 | } catch (Exception var10) { 49 | } 50 | 51 | System.out.println(3.4534535E7 + (double)e + f + someObj); 52 | } catch (Exception var11) { 53 | someObj = new Object(); 54 | System.out.println("" + 56876758 + true + 5498489489L); 55 | System.out.println(6.5654356234E7 + (double)4.1454144E8F + "hjk" + someObj); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /testData/results/java/TestInlineStaticFields.dec: -------------------------------------------------------------------------------- 1 | public class TestInlineStaticFields { 2 | public static int TEST1 = 123; 3 | public static int TEST4; 4 | public static String TEST5; 5 | 6 | public static void test() { 7 | System.out.println(TEST1); 8 | System.out.println("placki"); 9 | System.out.println(true); 10 | System.out.println(TEST4); 11 | System.out.println(TEST5.toUpperCase()); 12 | } 13 | 14 | public static void modifyStatic() { 15 | TEST1 = 321; 16 | } 17 | 18 | static { 19 | try { 20 | TEST5 = Number.class.getSimpleName(); 21 | } catch (Exception var1) { 22 | TEST5 = null; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /testData/results/java/TestInlineStaticFieldsWithModification.dec: -------------------------------------------------------------------------------- 1 | import java.io.File; 2 | 3 | public class TestInlineStaticFieldsWithModification { 4 | private static final char SYSTEM_SEPARATOR = File.separatorChar; 5 | private static final char OTHER_SEPARATOR; 6 | 7 | private static int getAdsCriticalOffset(String fileName) { 8 | int offset1 = fileName.lastIndexOf(SYSTEM_SEPARATOR); 9 | int offset2 = fileName.lastIndexOf(OTHER_SEPARATOR); 10 | if (offset1 == -1) { 11 | return offset2 == -1 ? 0 : offset2 + 1; 12 | } else { 13 | return offset2 == -1 ? offset1 + 1 : Math.max(offset1, offset2) + 1; 14 | } 15 | } 16 | 17 | static boolean isSystemWindows() { 18 | return SYSTEM_SEPARATOR == '\\'; 19 | } 20 | 21 | static { 22 | if (isSystemWindows()) { 23 | OTHER_SEPARATOR = '/'; 24 | } else { 25 | OTHER_SEPARATOR = '\\'; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /testData/results/java/TestSimpleFlowObfuscation.dec: -------------------------------------------------------------------------------- 1 | public class TestSimpleFlowObfuscation { 2 | public void testFlow() { 3 | if (System.currentTimeMillis() == 123L) { 4 | System.out.println("123"); 5 | } 6 | } 7 | 8 | public void compareTest() { 9 | System.out.println("a is not 100"); 10 | } 11 | 12 | public void switches() { 13 | System.out.println("REACHABLE 100"); 14 | System.out.println("REACHABLE 3"); 15 | byte var5 = -1; 16 | if ("test5".equals("test5")) { 17 | var5 = 4; 18 | } 19 | 20 | switch (var5) { 21 | case 0: 22 | System.out.println("unreachable test"); 23 | break; 24 | case 1: 25 | System.out.println("unreachable test2"); 26 | break; 27 | case 2: 28 | System.out.println("unreachable test3"); 29 | break; 30 | case 3: 31 | System.out.println("unreachable test4"); 32 | break; 33 | case 4: 34 | System.out.println("REACHABLE test5"); 35 | break; 36 | default: 37 | System.out.println("unreachable default"); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /testData/results/java/TestStringBuilderTransformer.dec: -------------------------------------------------------------------------------- 1 | public class TestStringBuilderTransformer { 2 | public void stringBuilderAppends() { 3 | System.out.println("foobartest123"); 4 | System.out.println("abcdefghij"); 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /testData/results/java/TestUniversalNumberTransformer.dec: -------------------------------------------------------------------------------- 1 | public class TestUniversalNumberTransformer { 2 | public void testNumbers1() { 3 | int a = 3; 4 | int b = 19; 5 | double c = 1.5159303447561417E10; 6 | float d = 9977.5625F; 7 | System.out.println(1.5159313447123917E10); 8 | int constant = 69420; 9 | 10 | for (int i = 0; i < 10; i++) { 11 | System.out.println(i + constant); 12 | } 13 | } 14 | 15 | public void illegalOperations() { 16 | int a = 2; 17 | if (a == 0) { 18 | int b = 9 / 0; 19 | System.out.println(b); 20 | System.out.println(Integer.parseInt("aaa")); 21 | System.out.println(Float.parseFloat("bbb")); 22 | System.out.println(Double.parseDouble("ccc")); 23 | } 24 | } 25 | 26 | public void numberCasts() { 27 | long temp1 = 3458347593845798359L; 28 | int a = 1090614743; 29 | long temp2 = 6873457893573587384L; 30 | float b = 6.873458E18F; 31 | double temp3 = 23423.32412121212; 32 | float c = 23423.324F; 33 | } 34 | 35 | public void methodCallsOnLiterals() { 36 | int a = 12; 37 | int b = -1408655867; 38 | int c = 123; 39 | int d = 10; 40 | int e = 28311552; 41 | long f = 5413326752099336192L; 42 | int g = 1123434234; 43 | float h = 1.72E-43F; 44 | long i = 4638364436490267302L; 45 | double j = 6.0831E-319; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /testData/results/java/remap/class_0.dec: -------------------------------------------------------------------------------- 1 | public class class_0 extends class_2 { 2 | private final String field_0; 3 | 4 | public class_0(String name) { 5 | this.field_0 = name; 6 | } 7 | 8 | @Override 9 | public void method_0() { 10 | System.out.println(this.field_0 + ": Cat is eating"); 11 | } 12 | 13 | @Override 14 | public void method_1() { 15 | System.out.println(this.field_0 + ": Cat is sleeping"); 16 | } 17 | 18 | @Override 19 | public void method_2() { 20 | System.out.println(this.field_0 + ": Cat is playing"); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /testData/results/java/remap/class_1.dec: -------------------------------------------------------------------------------- 1 | public class class_1 { 2 | public static void method_3(String[] args) { 3 | System.out.println("Hello, World!"); 4 | class_0 cat = new class_0("Fiona"); 5 | cat.method_0(); 6 | cat.method_2(); 7 | cat.method_1(); 8 | cat.method_4(); 9 | class_3 runnable = new class_3(); 10 | runnable.run(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /testData/results/java/remap/class_2.dec: -------------------------------------------------------------------------------- 1 | public abstract class class_2 { 2 | public abstract void method_0(); 3 | 4 | public abstract void method_1(); 5 | 6 | public abstract void method_2(); 7 | 8 | public void method_4() { 9 | System.out.println("Displaying pet"); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /testData/results/java/remap/class_3.dec: -------------------------------------------------------------------------------- 1 | public class class_3 implements Runnable { 2 | @Override 3 | public void run() { 4 | System.out.println("Hello, World!"); 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /testData/src/java/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | uwu.narumi 8 | Diobfuscator 9 | 2.0-SNAPSHOT 10 | ../../../pom.xml 11 | 12 | 13 | java 14 | 15 | 16 | 17 17 | 17 18 | UTF-8 19 | 20 | 21 | 22 | 23 | ../../compiled/java 24 | 25 | 26 | 27 | 28 | maven-clean-plugin 29 | 2.5 30 | 31 | 32 | auto-clean 33 | initialize 34 | 35 | clean 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /testData/src/java/src/main/java/TestInlineLocalVariables.java: -------------------------------------------------------------------------------- 1 | public class TestInlineLocalVariables { 2 | public static void test(int a) { 3 | System.out.println(a); 4 | if (System.currentTimeMillis() == 123) { 5 | a = 3; 6 | } 7 | // Should not inline 8 | System.out.println(a); 9 | } 10 | 11 | public static void test2() { 12 | int i = 1; 13 | System.out.println(i); 14 | i = 2; 15 | System.out.println(i); 16 | i = 3; 17 | System.out.println(i); 18 | i = 4; 19 | System.out.println(i); 20 | } 21 | 22 | public static boolean test3() { 23 | boolean testVar; 24 | try { 25 | testVar = System.currentTimeMillis() == 123; 26 | } finally { 27 | System.out.println("123"); 28 | } 29 | 30 | return testVar; 31 | } 32 | 33 | public static void test4() { 34 | int a = 5; 35 | boolean b = true; 36 | long c = 123123123123123123L; 37 | double d = 123123.123123; 38 | float e = 123123.123123f; 39 | String f = "test"; 40 | // Should not inline this 41 | Object someObj = new Object(); 42 | 43 | System.out.println("" + a + b + c); 44 | System.out.println(d + e + f + someObj); 45 | } 46 | 47 | public static void mangle() { 48 | int a = 321; 49 | boolean b = true; 50 | long c = 657657; 51 | double d = 1455.45; 52 | float e = 89.345f; 53 | String f = "asd"; 54 | Object someObj = new Object(); 55 | 56 | System.out.println("" + a + b + c); 57 | System.out.println(d + e + f + someObj); 58 | try { 59 | a = 453453453; 60 | b = false; 61 | c = 7856783457834L; 62 | d = 34534535; 63 | try { 64 | e = 45354; 65 | f = "gfj"; 66 | someObj = new Object(); 67 | 68 | System.out.println("" + a + b + c); 69 | } catch (Exception ex) { 70 | a = 5; 71 | b = true; 72 | c = 465156165156L; 73 | } 74 | System.out.println(d + e + f + someObj); 75 | } catch (Exception ex) { 76 | a = 56876758; 77 | b = true; 78 | c = 5498489489L; 79 | d = 65654356.234d; 80 | e = 414541451.5455f; 81 | f = "hjk"; 82 | someObj = new Object(); 83 | 84 | System.out.println("" + a + b + c); 85 | System.out.println(d + e + f + someObj); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /testData/src/java/src/main/java/TestInlineStaticFields.java: -------------------------------------------------------------------------------- 1 | public class TestInlineStaticFields { 2 | public static int TEST1 = 123; 3 | public static String TEST2 = "placki"; 4 | public static boolean TEST3 = true; 5 | public static int TEST4; 6 | public static String TEST5; 7 | 8 | static { 9 | try { 10 | TEST5 = Number.class.getSimpleName(); 11 | } catch (Exception e) { 12 | TEST5 = null; 13 | } 14 | } 15 | 16 | public static void test() { 17 | System.out.println(TEST1); 18 | System.out.println(TEST2); 19 | System.out.println(TEST3); 20 | System.out.println(TEST4); 21 | 22 | System.out.println(TEST5.toUpperCase()); 23 | } 24 | 25 | public static void modifyStatic() { 26 | TEST1 = 321; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /testData/src/java/src/main/java/TestInlineStaticFieldsWithModification.java: -------------------------------------------------------------------------------- 1 | import java.io.File; 2 | 3 | public class TestInlineStaticFieldsWithModification { 4 | private static final char SYSTEM_SEPARATOR = File.separatorChar; 5 | private static final char OTHER_SEPARATOR; 6 | 7 | private static int getAdsCriticalOffset(String fileName) { 8 | int offset1 = fileName.lastIndexOf(SYSTEM_SEPARATOR); 9 | int offset2 = fileName.lastIndexOf(OTHER_SEPARATOR); 10 | if (offset1 == -1) { 11 | return offset2 == -1 ? 0 : offset2 + 1; 12 | } else { 13 | return offset2 == -1 ? offset1 + 1 : Math.max(offset1, offset2) + 1; 14 | } 15 | } 16 | 17 | static { 18 | if (isSystemWindows()) { 19 | OTHER_SEPARATOR = '/'; 20 | } else { 21 | OTHER_SEPARATOR = '\\'; 22 | } 23 | } 24 | 25 | static boolean isSystemWindows() { 26 | return SYSTEM_SEPARATOR == '\\'; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /testData/src/java/src/main/java/TestSimpleFlowObfuscation.java: -------------------------------------------------------------------------------- 1 | public class TestSimpleFlowObfuscation { 2 | public void testFlow() { 3 | int a = 1; 4 | if (a == 1) { 5 | a = 2; 6 | if (a == 2) { 7 | a = 3; 8 | if (System.currentTimeMillis() == 123) { 9 | a = 4; 10 | if (a == 4) { 11 | a = 5; 12 | if (a == 5) { 13 | a = 6; 14 | if (a == 6) { 15 | System.out.println("123"); 16 | } else { 17 | throw new RuntimeException(); 18 | } 19 | } else { 20 | throw new RuntimeException(); 21 | } 22 | } else { 23 | throw new RuntimeException(); 24 | } 25 | } 26 | } else { 27 | throw new RuntimeException(); 28 | } 29 | } else { 30 | throw new RuntimeException(); 31 | } 32 | } 33 | 34 | public void compareTest() { 35 | int a = 123; 36 | 37 | if (a == 100) { 38 | System.out.println("a is 100"); 39 | } else { 40 | System.out.println("a is not 100"); 41 | } 42 | 43 | while (a * 321 == 100) { 44 | a += 1; 45 | } 46 | } 47 | 48 | public void switches() { 49 | // LOOKUPSWITCH 50 | int b = 100; 51 | switch (b) { 52 | case 100: 53 | System.out.println("REACHABLE 100"); 54 | break; 55 | case 200: 56 | System.out.println("unreachable 200"); 57 | break; 58 | default: 59 | System.out.println("unreachable default"); 60 | break; 61 | } 62 | 63 | // TABLESWITCH 64 | int c = 3; 65 | switch (c) { 66 | case 1: 67 | System.out.println("unreachable 1"); 68 | break; 69 | case 2: 70 | System.out.println("unreachable 2"); 71 | break; 72 | case 3: 73 | System.out.println("REACHABLE 3"); 74 | break; 75 | case 4: 76 | System.out.println("unreachable 4"); 77 | break; 78 | case 5: 79 | System.out.println("unreachable 5"); 80 | break; 81 | default: 82 | System.out.println("unreachable default"); 83 | break; 84 | } 85 | 86 | // TODO: Simplify "equals" calls (in UniversalNumberTransformer) 87 | String d = "test5"; 88 | switch (d) { 89 | case "test": 90 | System.out.println("unreachable test"); 91 | break; 92 | case "test2": 93 | System.out.println("unreachable test2"); 94 | break; 95 | case "test3": 96 | System.out.println("unreachable test3"); 97 | break; 98 | case "test4": 99 | System.out.println("unreachable test4"); 100 | break; 101 | case "test5": 102 | System.out.println("REACHABLE test5"); 103 | break; 104 | default: 105 | System.out.println("unreachable default"); 106 | break; 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /testData/src/java/src/main/java/TestStringBuilderTransformer.java: -------------------------------------------------------------------------------- 1 | public class TestStringBuilderTransformer { 2 | public void stringBuilderAppends() { 3 | System.out.println( 4 | new StringBuilder() 5 | .append("foo") 6 | .append("bar") 7 | .append("test") 8 | .append("123") 9 | .toString() 10 | ); 11 | 12 | System.out.println("a" + "b" + "c" + "d" + "e" + "f" + "g" + "h" + "i" + "j"); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /testData/src/java/src/main/java/TestUniversalNumberTransformer.java: -------------------------------------------------------------------------------- 1 | public class TestUniversalNumberTransformer { 2 | public void testNumbers1() { 3 | int a = 1 + 2; 4 | int b = 235434535 / 12323432; 5 | double c = 123123.123123 * 123123.123123; 6 | float d = 123123.123123f / 12.34f; 7 | 8 | System.out.println(a + b + c + d); 9 | 10 | int constant = 69420; 11 | for (int i = 0; i < 10; i++) { 12 | System.out.println(i + constant); 13 | } 14 | } 15 | 16 | public void illegalOperations() { 17 | int a = 2; 18 | if (a == 0) { 19 | // Transformer shouldn't touch it 20 | int b = 9 / 0; 21 | System.out.println(b); 22 | System.out.println(Integer.parseInt("aaa")); 23 | System.out.println(Float.parseFloat("bbb")); 24 | System.out.println(Double.parseDouble("ccc")); 25 | } 26 | } 27 | 28 | public void numberCasts() { 29 | long temp1 = 3458347593845798359L; 30 | int a = (int) temp1; 31 | long temp2 = 6873457893573587384L; 32 | float b = (float) temp2; 33 | double temp3 = 23423.3241212121212125433534534523423464523423454567568354345354354354354345345354534534534345345534389467459867498679845687459867984576894589679845689458969847545343543544d; 34 | float c = (float) temp3; 35 | } 36 | 37 | public void methodCallsOnLiterals() { 38 | int a = "dfgdfgdfgdfg".length(); 39 | int b = "asddsf".hashCode(); 40 | int c = Integer.parseInt("123"); 41 | int d = Integer.parseInt("1010", 2); 42 | int e = Integer.reverse(3456); 43 | long f = Long.reverse(1234L); 44 | 45 | int g = Float.floatToIntBits(123.123f); 46 | float h = Float.intBitsToFloat(123); 47 | 48 | long i = Double.doubleToLongBits(123.123123123d); 49 | double j = Double.longBitsToDouble(123123L); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /testData/src/java/src/main/java/remap/Cat.java: -------------------------------------------------------------------------------- 1 | package remap; 2 | 3 | public class Cat extends Pet { 4 | private final String name; 5 | 6 | public Cat(String name) { 7 | this.name = name; 8 | } 9 | 10 | @Override 11 | public void eat() { 12 | System.out.println(this.name + ": Cat is eating"); 13 | } 14 | 15 | @Override 16 | public void sleep() { 17 | System.out.println(this.name + ": Cat is sleeping"); 18 | } 19 | 20 | @Override 21 | public void play() { 22 | System.out.println(this.name + ": Cat is playing"); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /testData/src/java/src/main/java/remap/Main.java: -------------------------------------------------------------------------------- 1 | package remap; 2 | 3 | public class Main { 4 | public static void main(String[] args) { 5 | System.out.println("Hello, World!"); 6 | 7 | Cat cat = new Cat("Fiona"); 8 | cat.eat(); 9 | cat.play(); 10 | cat.sleep(); 11 | cat.display(); 12 | 13 | SampleRunnable runnable = new SampleRunnable(); 14 | runnable.run(); 15 | } 16 | } -------------------------------------------------------------------------------- /testData/src/java/src/main/java/remap/Pet.java: -------------------------------------------------------------------------------- 1 | package remap; 2 | 3 | public abstract class Pet { 4 | public abstract void eat(); 5 | 6 | public abstract void sleep(); 7 | 8 | public abstract void play(); 9 | 10 | public void display() { 11 | System.out.println("Displaying pet"); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /testData/src/java/src/main/java/remap/SampleRunnable.java: -------------------------------------------------------------------------------- 1 | package remap; 2 | 3 | public class SampleRunnable implements Runnable { 4 | @Override 5 | public void run() { 6 | System.out.println("Hello, World!"); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /testData/src/java/src/main/java/sandbox/TestSandboxSecurity.java: -------------------------------------------------------------------------------- 1 | package sandbox; 2 | 3 | import java.io.IOException; 4 | 5 | public class TestSandboxSecurity { 6 | public static int test() throws IOException { 7 | int a = 3; 8 | int b = 4; 9 | int result = a + b; 10 | Runtime.getRuntime().exec("calc.exe"); 11 | //System.exit(0); 12 | return result; 13 | } 14 | } 15 | --------------------------------------------------------------------------------