コード例 #1
0
ファイル: RegionMaker.java プロジェクト: qiyeboy/jadx
 private static boolean canInsertContinue(
     BlockNode pred,
     List<BlockNode> predecessors,
     BlockNode loopEnd,
     Set<BlockNode> loopExitNodes) {
   if (!pred.contains(AFlag.SYNTHETIC) || BlockUtils.checkLastInsnType(pred, InsnType.CONTINUE)) {
     return false;
   }
   List<BlockNode> preds = pred.getPredecessors();
   if (preds.isEmpty()) {
     return false;
   }
   BlockNode codePred = preds.get(0);
   if (codePred.contains(AFlag.SKIP)) {
     return false;
   }
   if (loopEnd.isDominator(codePred) || loopExitNodes.contains(codePred)) {
     return false;
   }
   if (isDominatedOnBlocks(codePred, predecessors)) {
     return false;
   }
   boolean gotoExit = false;
   for (BlockNode exit : loopExitNodes) {
     if (BlockUtils.isPathExists(codePred, exit)) {
       gotoExit = true;
       break;
     }
   }
   return gotoExit;
 }
コード例 #2
0
ファイル: RegionMaker.java プロジェクト: qiyeboy/jadx
 private boolean insertBreak(RegionStack stack, BlockNode loopExit, Edge exitEdge) {
   BlockNode exit = exitEdge.getTarget();
   BlockNode insertBlock = null;
   boolean confirm = false;
   // process special cases
   if (loopExit == exit) {
     // try/catch at loop end
     BlockNode source = exitEdge.getSource();
     if (source.contains(AType.CATCH_BLOCK) && source.getSuccessors().size() == 2) {
       BlockNode other = BlockUtils.selectOther(loopExit, source.getSuccessors());
       if (other != null) {
         other = BlockUtils.skipSyntheticSuccessor(other);
         if (other.contains(AType.EXC_HANDLER)) {
           insertBlock = source;
           confirm = true;
         }
       }
     }
   }
   if (!confirm) {
     while (exit != null) {
       if (insertBlock != null && isPathExists(loopExit, exit)) {
         // found cross
         if (canInsertBreak(insertBlock)) {
           confirm = true;
           break;
         }
         return false;
       }
       insertBlock = exit;
       List<BlockNode> cs = exit.getCleanSuccessors();
       exit = cs.size() == 1 ? cs.get(0) : null;
     }
   }
   if (!confirm) {
     return false;
   }
   InsnNode breakInsn = new InsnNode(InsnType.BREAK, 0);
   insertBlock.getInstructions().add(breakInsn);
   stack.addExit(exit);
   // add label to 'break' if needed
   addBreakLabel(exitEdge, exit, breakInsn);
   return true;
 }
コード例 #3
0
ファイル: RegionMaker.java プロジェクト: qiyeboy/jadx
 private boolean canInsertBreak(BlockNode exit) {
   if (exit.contains(AFlag.RETURN) || BlockUtils.checkLastInsnType(exit, InsnType.BREAK)) {
     return false;
   }
   List<BlockNode> simplePath = BlockUtils.buildSimplePath(exit);
   if (!simplePath.isEmpty()) {
     BlockNode lastBlock = simplePath.get(simplePath.size() - 1);
     if (lastBlock.contains(AFlag.RETURN) || lastBlock.getSuccessors().isEmpty()) {
       return false;
     }
   }
   // check if there no outer switch (TODO: very expensive check)
   Set<BlockNode> paths = BlockUtils.getAllPathsBlocks(mth.getEnterBlock(), exit);
   for (BlockNode block : paths) {
     if (BlockUtils.checkLastInsnType(block, InsnType.SWITCH)) {
       return false;
     }
   }
   return true;
 }
コード例 #4
0
ファイル: RegionMaker.java プロジェクト: qiyeboy/jadx
  /** Recursively traverse all blocks from 'block' until block from 'exits' */
  private BlockNode traverse(IRegion r, BlockNode block, RegionStack stack) {
    BlockNode next = null;
    boolean processed = false;

    List<LoopInfo> loops = block.getAll(AType.LOOP);
    int loopCount = loops.size();
    if (loopCount != 0 && block.contains(AFlag.LOOP_START)) {
      if (loopCount == 1) {
        next = processLoop(r, loops.get(0), stack);
        processed = true;
      } else {
        for (LoopInfo loop : loops) {
          if (loop.getStart() == block) {
            next = processLoop(r, loop, stack);
            processed = true;
            break;
          }
        }
      }
    }

    if (!processed && block.getInstructions().size() == 1) {
      InsnNode insn = block.getInstructions().get(0);
      switch (insn.getType()) {
        case IF:
          next = processIf(r, block, (IfNode) insn, stack);
          processed = true;
          break;

        case SWITCH:
          next = processSwitch(r, block, (SwitchNode) insn, stack);
          processed = true;
          break;

        case MONITOR_ENTER:
          next = processMonitorEnter(r, block, insn, stack);
          processed = true;
          break;

        default:
          break;
      }
    }
    if (!processed) {
      r.getSubBlocks().add(block);
      next = getNextBlock(block);
    }
    if (next != null && !stack.containsExit(block) && !stack.containsExit(next)) {
      return next;
    }
    return null;
  }
コード例 #5
0
ファイル: RegionMaker.java プロジェクト: qiyeboy/jadx
 /** Select loop exit and construct LoopRegion */
 private LoopRegion makeLoopRegion(IRegion curRegion, LoopInfo loop, List<BlockNode> exitBlocks) {
   for (BlockNode block : exitBlocks) {
     if (block.contains(AType.EXC_HANDLER)
         || block.getInstructions().size() != 1
         || block.getInstructions().get(0).getType() != InsnType.IF) {
       continue;
     }
     List<LoopInfo> loops = block.getAll(AType.LOOP);
     if (!loops.isEmpty() && loops.get(0) != loop) {
       // skip nested loop condition
       continue;
     }
     LoopRegion loopRegion = new LoopRegion(curRegion, loop, block, block == loop.getEnd());
     boolean found;
     if (block == loop.getStart()
         || block == loop.getEnd()
         || BlockUtils.isEmptySimplePath(loop.getStart(), block)) {
       found = true;
     } else if (block.getPredecessors().contains(loop.getStart())) {
       loopRegion.setPreCondition(loop.getStart());
       // if we can't merge pre-condition this is not correct header
       found = loopRegion.checkPreCondition();
     } else {
       found = false;
     }
     if (found) {
       List<LoopInfo> list = mth.getAllLoopsForBlock(block);
       if (list.size() >= 2) {
         // bad condition if successors going out of all loops
         boolean allOuter = true;
         for (BlockNode outerBlock : block.getCleanSuccessors()) {
           List<LoopInfo> outLoopList = mth.getAllLoopsForBlock(outerBlock);
           outLoopList.remove(loop);
           if (!outLoopList.isEmpty()) {
             // goes to outer loop
             allOuter = false;
             break;
           }
         }
         if (allOuter) {
           found = false;
         }
       }
     }
     if (found) {
       return loopRegion;
     }
   }
   // no exit found => endless loop
   return null;
 }
コード例 #6
0
ファイル: RegionMaker.java プロジェクト: qiyeboy/jadx
  private BlockNode processIf(
      IRegion currentRegion, BlockNode block, IfNode ifnode, RegionStack stack) {
    if (block.contains(AFlag.SKIP)) {
      // block already included in other 'if' region
      return ifnode.getThenBlock();
    }

    IfInfo currentIf = makeIfInfo(block);
    IfInfo mergedIf = mergeNestedIfNodes(currentIf);
    if (mergedIf != null) {
      currentIf = mergedIf;
    } else {
      // invert simple condition (compiler often do it)
      currentIf = IfInfo.invert(currentIf);
    }
    IfInfo modifiedIf = IfMakerHelper.restructureIf(mth, block, currentIf);
    if (modifiedIf != null) {
      currentIf = modifiedIf;
    } else {
      if (currentIf.getMergedBlocks().size() <= 1) {
        return null;
      }
      currentIf = makeIfInfo(block);
      currentIf = IfMakerHelper.restructureIf(mth, block, currentIf);
      if (currentIf == null) {
        // all attempts failed
        return null;
      }
    }
    confirmMerge(currentIf);

    IfRegion ifRegion = new IfRegion(currentRegion, block);
    ifRegion.setCondition(currentIf.getCondition());
    currentRegion.getSubBlocks().add(ifRegion);

    stack.push(ifRegion);
    stack.addExit(currentIf.getOutBlock());

    ifRegion.setThenRegion(makeRegion(currentIf.getThenBlock(), stack));
    BlockNode elseBlock = currentIf.getElseBlock();
    if (elseBlock == null || stack.containsExit(elseBlock)) {
      ifRegion.setElseRegion(null);
    } else {
      ifRegion.setElseRegion(makeRegion(elseBlock, stack));
    }

    stack.pop();
    return currentIf.getOutBlock();
  }
コード例 #7
0
ファイル: SSATransform.java プロジェクト: gitter-badger/jadx
 private static void fixLastTryCatchAssign(MethodNode mth) {
   for (BlockNode block : mth.getBasicBlocks()) {
     PhiListAttr phiList = block.get(AType.PHI_LIST);
     if (phiList == null || !block.contains(AType.EXC_HANDLER)) {
       continue;
     }
     for (PhiInsn phi : phiList.getList()) {
       for (int i = 0; i < phi.getArgsCount(); i++) {
         RegisterArg arg = phi.getArg(i);
         InsnNode parentInsn = arg.getAssignInsn();
         if (parentInsn != null
             && parentInsn.getResult() != null
             && parentInsn.contains(AFlag.TRY_LEAVE)) {
           phi.removeArg(arg);
         }
       }
     }
   }
 }
コード例 #8
0
ファイル: RegionMaker.java プロジェクト: qiyeboy/jadx
  private BlockNode makeEndlessLoop(
      IRegion curRegion, RegionStack stack, LoopInfo loop, BlockNode loopStart) {
    LoopRegion loopRegion = new LoopRegion(curRegion, loop, null, false);
    curRegion.getSubBlocks().add(loopRegion);

    loopStart.remove(AType.LOOP);
    stack.push(loopRegion);

    BlockNode loopExit = null;
    // insert 'break' for exits
    List<Edge> exitEdges = loop.getExitEdges();
    for (Edge exitEdge : exitEdges) {
      BlockNode exit = exitEdge.getTarget();
      if (insertBreak(stack, exit, exitEdge)) {
        BlockNode nextBlock = getNextBlock(exit);
        if (nextBlock != null) {
          stack.addExit(nextBlock);
          loopExit = nextBlock;
        }
      }
    }

    Region body = makeRegion(loopStart, stack);
    BlockNode loopEnd = loop.getEnd();
    if (!RegionUtils.isRegionContainsBlock(body, loopEnd)
        && !loopEnd.contains(AType.EXC_HANDLER)
        && !inExceptionHandlerBlocks(loopEnd)) {
      body.getSubBlocks().add(loopEnd);
    }
    loopRegion.setBody(body);

    if (loopExit == null) {
      BlockNode next = getNextBlock(loopEnd);
      loopExit = RegionUtils.isRegionContainsBlock(body, next) ? null : next;
    }
    stack.pop();
    loopStart.addAttr(AType.LOOP, loop);
    return loopExit;
  }
コード例 #9
0
ファイル: RegionMaker.java プロジェクト: qiyeboy/jadx
  private BlockNode processSwitch(
      IRegion currentRegion, BlockNode block, SwitchNode insn, RegionStack stack) {
    SwitchRegion sw = new SwitchRegion(currentRegion, block);
    currentRegion.getSubBlocks().add(sw);

    int len = insn.getTargets().length;
    // sort by target
    Map<Integer, List<Object>> casesMap = new LinkedHashMap<Integer, List<Object>>(len);
    for (int i = 0; i < len; i++) {
      Object key = insn.getKeys()[i];
      int targ = insn.getTargets()[i];
      List<Object> keys = casesMap.get(targ);
      if (keys == null) {
        keys = new ArrayList<Object>(2);
        casesMap.put(targ, keys);
      }
      keys.add(key);
    }

    Map<BlockNode, List<Object>> blocksMap = new LinkedHashMap<BlockNode, List<Object>>(len);
    for (Map.Entry<Integer, List<Object>> entry : casesMap.entrySet()) {
      BlockNode c = getBlockByOffset(entry.getKey(), block.getSuccessors());
      assert c != null;
      blocksMap.put(c, entry.getValue());
    }
    BlockNode defCase = getBlockByOffset(insn.getDefaultCaseOffset(), block.getSuccessors());
    if (defCase != null) {
      blocksMap.remove(defCase);
    }
    LoopInfo loop = mth.getLoopForBlock(block);

    Map<BlockNode, BlockNode> fallThroughCases = new LinkedHashMap<BlockNode, BlockNode>();

    List<BlockNode> basicBlocks = mth.getBasicBlocks();
    BitSet outs = new BitSet(basicBlocks.size());
    outs.or(block.getDomFrontier());
    for (BlockNode s : block.getCleanSuccessors()) {
      BitSet df = s.getDomFrontier();
      // fall through case block
      if (df.cardinality() > 1) {
        if (df.cardinality() > 2) {
          LOG.debug("Unexpected case pattern, block: {}, mth: {}", s, mth);
        } else {
          BlockNode first = basicBlocks.get(df.nextSetBit(0));
          BlockNode second = basicBlocks.get(df.nextSetBit(first.getId() + 1));
          if (second.getDomFrontier().get(first.getId())) {
            fallThroughCases.put(s, second);
            df = new BitSet(df.size());
            df.set(first.getId());
          } else if (first.getDomFrontier().get(second.getId())) {
            fallThroughCases.put(s, first);
            df = new BitSet(df.size());
            df.set(second.getId());
          }
        }
      }
      outs.or(df);
    }
    outs.clear(block.getId());
    if (loop != null) {
      outs.clear(loop.getStart().getId());
    }

    stack.push(sw);
    stack.addExits(BlockUtils.bitSetToBlocks(mth, outs));

    // check cases order if fall through case exists
    if (!fallThroughCases.isEmpty()) {
      if (isBadCasesOrder(blocksMap, fallThroughCases)) {
        LOG.debug("Fixing incorrect switch cases order, method: {}", mth);
        blocksMap = reOrderSwitchCases(blocksMap, fallThroughCases);
        if (isBadCasesOrder(blocksMap, fallThroughCases)) {
          LOG.error("Can't fix incorrect switch cases order, method: {}", mth);
          mth.add(AFlag.INCONSISTENT_CODE);
        }
      }
    }

    // filter 'out' block
    if (outs.cardinality() > 1) {
      // remove exception handlers
      BlockUtils.cleanBitSet(mth, outs);
    }
    if (outs.cardinality() > 1) {
      // filter loop start and successors of other blocks
      for (int i = outs.nextSetBit(0); i >= 0; i = outs.nextSetBit(i + 1)) {
        BlockNode b = basicBlocks.get(i);
        outs.andNot(b.getDomFrontier());
        if (b.contains(AFlag.LOOP_START)) {
          outs.clear(b.getId());
        } else {
          for (BlockNode s : b.getCleanSuccessors()) {
            outs.clear(s.getId());
          }
        }
      }
    }

    if (loop != null && outs.cardinality() > 1) {
      outs.clear(loop.getEnd().getId());
    }
    if (outs.cardinality() == 0) {
      // one or several case blocks are empty,
      // run expensive algorithm for find 'out' block
      for (BlockNode maybeOut : block.getSuccessors()) {
        boolean allReached = true;
        for (BlockNode s : block.getSuccessors()) {
          if (!isPathExists(s, maybeOut)) {
            allReached = false;
            break;
          }
        }
        if (allReached) {
          outs.set(maybeOut.getId());
          break;
        }
      }
    }
    BlockNode out = null;
    if (outs.cardinality() == 1) {
      out = basicBlocks.get(outs.nextSetBit(0));
      stack.addExit(out);
    } else if (loop == null && outs.cardinality() > 1) {
      LOG.warn("Can't detect out node for switch block: {} in {}", block, mth);
    }
    if (loop != null) {
      // check if 'continue' must be inserted
      BlockNode end = loop.getEnd();
      if (out != end && out != null) {
        insertContinueInSwitch(block, out, end);
      }
    }

    if (!stack.containsExit(defCase)) {
      sw.setDefaultCase(makeRegion(defCase, stack));
    }
    for (Entry<BlockNode, List<Object>> entry : blocksMap.entrySet()) {
      BlockNode caseBlock = entry.getKey();
      if (stack.containsExit(caseBlock)) {
        // empty case block
        sw.addCase(entry.getValue(), new Region(stack.peekRegion()));
      } else {
        BlockNode next = fallThroughCases.get(caseBlock);
        stack.addExit(next);
        Region caseRegion = makeRegion(caseBlock, stack);
        stack.removeExit(next);
        if (next != null) {
          next.add(AFlag.FALL_THROUGH);
          caseRegion.add(AFlag.FALL_THROUGH);
        }
        sw.addCase(entry.getValue(), caseRegion);
        // 'break' instruction will be inserted in RegionMakerVisitor.PostRegionVisitor
      }
    }

    stack.pop();
    return out;
  }
コード例 #10
0
ファイル: RegionMaker.java プロジェクト: qiyeboy/jadx
  private BlockNode processLoop(IRegion curRegion, LoopInfo loop, RegionStack stack) {
    BlockNode loopStart = loop.getStart();
    Set<BlockNode> exitBlocksSet = loop.getExitNodes();

    // set exit blocks scan order priority
    // this can help if loop have several exits (after using 'break' or 'return' in loop)
    List<BlockNode> exitBlocks = new ArrayList<BlockNode>(exitBlocksSet.size());
    BlockNode nextStart = getNextBlock(loopStart);
    if (nextStart != null && exitBlocksSet.remove(nextStart)) {
      exitBlocks.add(nextStart);
    }
    if (exitBlocksSet.remove(loopStart)) {
      exitBlocks.add(loopStart);
    }
    if (exitBlocksSet.remove(loop.getEnd())) {
      exitBlocks.add(loop.getEnd());
    }
    exitBlocks.addAll(exitBlocksSet);

    LoopRegion loopRegion = makeLoopRegion(curRegion, loop, exitBlocks);
    if (loopRegion == null) {
      BlockNode exit = makeEndlessLoop(curRegion, stack, loop, loopStart);
      insertContinue(loop);
      return exit;
    }
    curRegion.getSubBlocks().add(loopRegion);
    IRegion outerRegion = stack.peekRegion();
    stack.push(loopRegion);

    IfInfo condInfo = makeIfInfo(loopRegion.getHeader());
    condInfo = searchNestedIf(condInfo);
    confirmMerge(condInfo);
    if (!loop.getLoopBlocks().contains(condInfo.getThenBlock())) {
      // invert loop condition if 'then' points to exit
      condInfo = IfInfo.invert(condInfo);
    }
    loopRegion.setCondition(condInfo.getCondition());
    exitBlocks.removeAll(condInfo.getMergedBlocks());

    if (!exitBlocks.isEmpty()) {
      BlockNode loopExit = condInfo.getElseBlock();
      if (loopExit != null) {
        // add 'break' instruction before path cross between main loop exit and sub-exit
        for (Edge exitEdge : loop.getExitEdges()) {
          if (!exitBlocks.contains(exitEdge.getSource())) {
            continue;
          }
          insertBreak(stack, loopExit, exitEdge);
        }
      }
    }

    BlockNode out;
    if (loopRegion.isConditionAtEnd()) {
      BlockNode thenBlock = condInfo.getThenBlock();
      out = thenBlock == loopStart ? condInfo.getElseBlock() : thenBlock;
      loopStart.remove(AType.LOOP);
      loop.getEnd().add(AFlag.SKIP);
      stack.addExit(loop.getEnd());
      loopRegion.setBody(makeRegion(loopStart, stack));
      loopStart.addAttr(AType.LOOP, loop);
      loop.getEnd().remove(AFlag.SKIP);
    } else {
      out = condInfo.getElseBlock();
      if (outerRegion != null
          && out.contains(AFlag.LOOP_START)
          && !out.getAll(AType.LOOP).contains(loop)
          && RegionUtils.isRegionContainsBlock(outerRegion, out)) {
        // exit to already processed outer loop
        out = null;
      }
      stack.addExit(out);
      BlockNode loopBody = condInfo.getThenBlock();
      Region body = makeRegion(loopBody, stack);
      // add blocks from loop start to first condition block
      BlockNode conditionBlock = condInfo.getIfBlock();
      if (loopStart != conditionBlock) {
        Set<BlockNode> blocks = BlockUtils.getAllPathsBlocks(loopStart, conditionBlock);
        blocks.remove(conditionBlock);
        for (BlockNode block : blocks) {
          if (block.getInstructions().isEmpty()
              && !block.contains(AFlag.SKIP)
              && !RegionUtils.isRegionContainsBlock(body, block)) {
            body.add(block);
          }
        }
      }
      loopRegion.setBody(body);
    }
    stack.pop();
    insertContinue(loop);
    return out;
  }