summaryrefslogtreecommitdiff
path: root/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java')
-rw-r--r--plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java330
1 files changed, 330 insertions, 0 deletions
diff --git a/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java
new file mode 100644
index 000000000000..7614807c99a4
--- /dev/null
+++ b/plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/modules/decompiler/deobfuscator/ExceptionDeobfuscator.java
@@ -0,0 +1,330 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jetbrains.java.decompiler.modules.decompiler.deobfuscator;
+
+import org.jetbrains.java.decompiler.code.CodeConstants;
+import org.jetbrains.java.decompiler.code.Instruction;
+import org.jetbrains.java.decompiler.code.InstructionSequence;
+import org.jetbrains.java.decompiler.code.SimpleInstructionSequence;
+import org.jetbrains.java.decompiler.code.cfg.BasicBlock;
+import org.jetbrains.java.decompiler.code.cfg.ControlFlowGraph;
+import org.jetbrains.java.decompiler.code.cfg.ExceptionRangeCFG;
+import org.jetbrains.java.decompiler.modules.decompiler.decompose.GenericDominatorEngine;
+import org.jetbrains.java.decompiler.modules.decompiler.decompose.IGraph;
+import org.jetbrains.java.decompiler.modules.decompiler.decompose.IGraphNode;
+import org.jetbrains.java.decompiler.util.InterpreterUtil;
+
+import java.util.*;
+import java.util.Map.Entry;
+
+public class ExceptionDeobfuscator {
+
+ private static class Range {
+ private final BasicBlock handler;
+ private final String uniqueStr;
+ private final Set<BasicBlock> protectedRange;
+ private final ExceptionRangeCFG rangeCFG;
+
+ private Range(BasicBlock handler, String uniqueStr, Set<BasicBlock> protectedRange, ExceptionRangeCFG rangeCFG) {
+ this.handler = handler;
+ this.uniqueStr = uniqueStr;
+ this.protectedRange = protectedRange;
+ this.rangeCFG = rangeCFG;
+ }
+ }
+
+ public static void restorePopRanges(ControlFlowGraph graph) {
+
+ List<Range> lstRanges = new ArrayList<Range>();
+
+ // aggregate ranges
+ for (ExceptionRangeCFG range : graph.getExceptions()) {
+ boolean found = false;
+ for (Range arr : lstRanges) {
+ if (arr.handler == range.getHandler() && InterpreterUtil.equalObjects(range.getUniqueExceptionsString(), arr.uniqueStr)) {
+ arr.protectedRange.addAll(range.getProtectedRange());
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ // doesn't matter, which range chosen
+ lstRanges.add(new Range(range.getHandler(), range.getUniqueExceptionsString(), new HashSet<BasicBlock>(range.getProtectedRange()), range));
+ }
+ }
+
+ // process aggregated ranges
+ for (Range range : lstRanges) {
+
+ if (range.uniqueStr != null) {
+
+ BasicBlock handler = range.handler;
+ InstructionSequence seq = handler.getSeq();
+
+ Instruction firstinstr;
+ if (seq.length() > 0) {
+ firstinstr = seq.getInstr(0);
+
+ if (firstinstr.opcode == CodeConstants.opc_pop ||
+ firstinstr.opcode == CodeConstants.opc_astore) {
+ Set<BasicBlock> setrange = new HashSet<BasicBlock>(range.protectedRange);
+
+ for (Range range_super : lstRanges) { // finally or strict superset
+
+ if (range != range_super) {
+
+ Set<BasicBlock> setrange_super = new HashSet<BasicBlock>(range_super.protectedRange);
+
+ if (!setrange.contains(range_super.handler) && !setrange_super.contains(handler)
+ && (range_super.uniqueStr == null || setrange_super.containsAll(setrange))) {
+
+ if (range_super.uniqueStr == null) {
+ setrange_super.retainAll(setrange);
+ }
+ else {
+ setrange_super.removeAll(setrange);
+ }
+
+ if (!setrange_super.isEmpty()) {
+
+ BasicBlock newblock = handler;
+
+ // split the handler
+ if (seq.length() > 1) {
+ newblock = new BasicBlock(++graph.last_id);
+ InstructionSequence newseq = new SimpleInstructionSequence();
+ newseq.addInstruction(firstinstr.clone(), -1);
+
+ newblock.setSeq(newseq);
+ graph.getBlocks().addWithKey(newblock, newblock.id);
+
+
+ List<BasicBlock> lstTemp = new ArrayList<BasicBlock>();
+ lstTemp.addAll(handler.getPreds());
+ lstTemp.addAll(handler.getPredExceptions());
+
+ // replace predecessors
+ for (BasicBlock pred : lstTemp) {
+ pred.replaceSuccessor(handler, newblock);
+ }
+
+ // replace handler
+ for (ExceptionRangeCFG range_ext : graph.getExceptions()) {
+ if (range_ext.getHandler() == handler) {
+ range_ext.setHandler(newblock);
+ }
+ else if (range_ext.getProtectedRange().contains(handler)) {
+ newblock.addSuccessorException(range_ext.getHandler());
+ range_ext.getProtectedRange().add(newblock);
+ }
+ }
+
+ newblock.addSuccessor(handler);
+ if (graph.getFirst() == handler) {
+ graph.setFirst(newblock);
+ }
+
+ // remove the first pop in the handler
+ seq.removeInstruction(0);
+ }
+
+ newblock.addSuccessorException(range_super.handler);
+ range_super.rangeCFG.getProtectedRange().add(newblock);
+
+ handler = range.rangeCFG.getHandler();
+ seq = handler.getSeq();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public static void insertEmptyExceptionHandlerBlocks(ControlFlowGraph graph) {
+
+ Set<BasicBlock> setVisited = new HashSet<BasicBlock>();
+
+ for (ExceptionRangeCFG range : graph.getExceptions()) {
+ BasicBlock handler = range.getHandler();
+
+ if (setVisited.contains(handler)) {
+ continue;
+ }
+ setVisited.add(handler);
+
+ BasicBlock emptyblock = new BasicBlock(++graph.last_id);
+ graph.getBlocks().addWithKey(emptyblock, emptyblock.id);
+
+ List<BasicBlock> lstTemp = new ArrayList<BasicBlock>();
+ // only exception predecessors considered
+ lstTemp.addAll(handler.getPredExceptions());
+
+ // replace predecessors
+ for (BasicBlock pred : lstTemp) {
+ pred.replaceSuccessor(handler, emptyblock);
+ }
+
+ // replace handler
+ for (ExceptionRangeCFG range_ext : graph.getExceptions()) {
+ if (range_ext.getHandler() == handler) {
+ range_ext.setHandler(emptyblock);
+ }
+ else if (range_ext.getProtectedRange().contains(handler)) {
+ emptyblock.addSuccessorException(range_ext.getHandler());
+ range_ext.getProtectedRange().add(emptyblock);
+ }
+ }
+
+ emptyblock.addSuccessor(handler);
+ if (graph.getFirst() == handler) {
+ graph.setFirst(emptyblock);
+ }
+ }
+ }
+
+ public static void removeEmptyRanges(ControlFlowGraph graph) {
+
+ List<ExceptionRangeCFG> lstRanges = graph.getExceptions();
+ for (int i = lstRanges.size() - 1; i >= 0; i--) {
+ ExceptionRangeCFG range = lstRanges.get(i);
+
+ boolean isEmpty = true;
+ for (BasicBlock block : range.getProtectedRange()) {
+ if (!block.getSeq().isEmpty()) {
+ isEmpty = false;
+ break;
+ }
+ }
+
+ if (isEmpty) {
+ for (BasicBlock block : range.getProtectedRange()) {
+ block.removeSuccessorException(range.getHandler());
+ }
+
+ lstRanges.remove(i);
+ }
+ }
+ }
+
+ public static void removeCircularRanges(final ControlFlowGraph graph) {
+
+ GenericDominatorEngine engine = new GenericDominatorEngine(new IGraph() {
+ public List<? extends IGraphNode> getReversePostOrderList() {
+ return graph.getReversePostOrder();
+ }
+
+ public Set<? extends IGraphNode> getRoots() {
+ return new HashSet<IGraphNode>(Arrays.asList(new IGraphNode[]{graph.getFirst()}));
+ }
+ });
+
+ engine.initialize();
+
+ List<ExceptionRangeCFG> lstRanges = graph.getExceptions();
+ for (int i = lstRanges.size() - 1; i >= 0; i--) {
+ ExceptionRangeCFG range = lstRanges.get(i);
+
+ BasicBlock handler = range.getHandler();
+ List<BasicBlock> rangeList = range.getProtectedRange();
+
+ if (rangeList.contains(handler)) { // TODO: better removing strategy
+
+ List<BasicBlock> lstRemBlocks = getReachableBlocksRestricted(range, engine);
+
+ if (lstRemBlocks.size() < rangeList.size() || rangeList.size() == 1) {
+ for (BasicBlock block : lstRemBlocks) {
+ block.removeSuccessorException(handler);
+ rangeList.remove(block);
+ }
+ }
+
+ if (rangeList.isEmpty()) {
+ lstRanges.remove(i);
+ }
+ }
+ }
+ }
+
+ private static List<BasicBlock> getReachableBlocksRestricted(ExceptionRangeCFG range, GenericDominatorEngine engine) {
+
+ List<BasicBlock> lstRes = new ArrayList<BasicBlock>();
+
+ LinkedList<BasicBlock> stack = new LinkedList<BasicBlock>();
+ Set<BasicBlock> setVisited = new HashSet<BasicBlock>();
+
+ BasicBlock handler = range.getHandler();
+ stack.addFirst(handler);
+
+ while (!stack.isEmpty()) {
+ BasicBlock block = stack.removeFirst();
+
+ setVisited.add(block);
+
+ if (range.getProtectedRange().contains(block) && engine.isDominator(block, handler)) {
+ lstRes.add(block);
+
+ List<BasicBlock> lstSuccs = new ArrayList<BasicBlock>(block.getSuccs());
+ lstSuccs.addAll(block.getSuccExceptions());
+
+ for (BasicBlock succ : lstSuccs) {
+ if (!setVisited.contains(succ)) {
+ stack.add(succ);
+ }
+ }
+ }
+ }
+
+ return lstRes;
+ }
+
+
+ public static boolean hasObfuscatedExceptions(ControlFlowGraph graph) {
+
+ Map<BasicBlock, Set<BasicBlock>> mapRanges = new HashMap<BasicBlock, Set<BasicBlock>>();
+ for (ExceptionRangeCFG range : graph.getExceptions()) {
+ Set<BasicBlock> set = mapRanges.get(range.getHandler());
+ if (set == null) {
+ mapRanges.put(range.getHandler(), set = new HashSet<BasicBlock>());
+ }
+ set.addAll(range.getProtectedRange());
+ }
+
+ for (Entry<BasicBlock, Set<BasicBlock>> ent : mapRanges.entrySet()) {
+ Set<BasicBlock> setEntries = new HashSet<BasicBlock>();
+
+ for (BasicBlock block : ent.getValue()) {
+ Set<BasicBlock> setTemp = new HashSet<BasicBlock>(block.getPreds());
+ setTemp.removeAll(ent.getValue());
+
+ if (!setTemp.isEmpty()) {
+ setEntries.add(block);
+ }
+ }
+
+ if (!setEntries.isEmpty()) {
+ if (setEntries.size() > 1 /*|| ent.getValue().contains(first)*/) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+}