Roman Shevchenko
2018-10-23 20:59:12 +02:00
14 changed files with 621 additions and 16 deletions

View File

@@ -107,6 +107,10 @@ public class MethodProcessorRunnable implements Runnable {
if (ExceptionDeobfuscator.hasObfuscatedExceptions(graph)) {
DecompilerContext.getLogger().writeMessage("Heavily obfuscated exception ranges found!", IFernflowerLogger.Severity.WARN);
if(!ExceptionDeobfuscator.handleMultipleEntryExceptionRanges(graph)) {
DecompilerContext.getLogger().writeMessage("Found multiple entry exception ranges which could not be splitted", IFernflowerLogger.Severity.WARN);
}
ExceptionDeobfuscator.insertDummyExceptionHandlerBlocks(graph, cl.getBytecodeVersion());
}
RootStatement root = DomHelper.parseGraph(graph);

View File

@@ -178,7 +178,7 @@ public class SimplifyExprentsHelper {
}
// expr++ and expr--
if (isIPPorIMM(current, next)) {
if (isIPPorIMM(current, next) || isIPPorIMM2(current, next)) {
list.remove(index + 1);
res = true;
continue;
@@ -458,6 +458,48 @@ public class SimplifyExprentsHelper {
return false;
}
private static boolean isIPPorIMM2(Exprent first, Exprent second) {
if (first.type != Exprent.EXPRENT_ASSIGNMENT || second.type != Exprent.EXPRENT_ASSIGNMENT) {
return false;
}
AssignmentExprent af = (AssignmentExprent)first;
AssignmentExprent as = (AssignmentExprent)second;
if(as.getRight().type != Exprent.EXPRENT_FUNCTION) {
return false;
}
FunctionExprent func = (FunctionExprent)as.getRight();
if(func.getFuncType() != FunctionExprent.FUNCTION_ADD && func.getFuncType() != FunctionExprent.FUNCTION_SUB) {
return false;
}
Exprent econd = func.getLstOperands().get(0);
Exprent econst = func.getLstOperands().get(1);
if(econst.type != Exprent.EXPRENT_CONST && econd.type == Exprent.EXPRENT_CONST && func.getFuncType() == FunctionExprent.FUNCTION_ADD) {
econd = econst;
econst = func.getLstOperands().get(0);
}
if(econst.type == Exprent.EXPRENT_CONST && ((ConstExprent)econst).hasValueOne()) {
if(af.getLeft().equals(econd) && af.getRight().equals(as.getLeft()) && (af.getLeft().getExprentUse() & Exprent.MULTIPLE_USES) != 0) {
int type = func.getFuncType() == FunctionExprent.FUNCTION_ADD ? FunctionExprent.FUNCTION_IPP : FunctionExprent.FUNCTION_IMM;
FunctionExprent ret = new FunctionExprent(type, af.getRight(), func.bytecode);
ret.setImplicitType(VarType.VARTYPE_INT);
af.setRight(ret);
return true;
}
}
return false;
}
private static boolean isMonitorExit(Exprent first) {
if (first.type == Exprent.EXPRENT_MONITOR) {
MonitorExprent expr = (MonitorExprent)first;

View File

@@ -134,8 +134,9 @@ public class DominatorTreeExceptionFilter {
exit = childid;
}
else {
// exit = map.containsKey(handler)?-1:mapChild.get(handler); FIXME: Eclipse bug?
exit = map.containsKey(handler) ? -1 : mapChild.get(handler);
// after replacing 'new Integer(-1)' with '-1' Eclipse throws a NullPointerException on the following line
// could be a bug in Eclipse or some obscure specification glitch, FIXME: needs further investigation
exit = map.containsKey(handler) ? new Integer(-1) : mapChild.get(handler);
}
if (exit != null) {

View File

@@ -8,6 +8,8 @@ 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.main.DecompilerContext;
import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
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;
@@ -233,7 +235,7 @@ public class ExceptionDeobfuscator {
if (rangeList.contains(handler)) { // TODO: better removing strategy
List<BasicBlock> lstRemBlocks = getReachableBlocksRestricted(range, engine);
List<BasicBlock> lstRemBlocks = getReachableBlocksRestricted(range.getHandler(), range, engine);
if (lstRemBlocks.size() < rangeList.size() || rangeList.size() == 1) {
for (BasicBlock block : lstRemBlocks) {
@@ -249,22 +251,21 @@ public class ExceptionDeobfuscator {
}
}
private static List<BasicBlock> getReachableBlocksRestricted(ExceptionRangeCFG range, GenericDominatorEngine engine) {
private static List<BasicBlock> getReachableBlocksRestricted(BasicBlock start, ExceptionRangeCFG range, GenericDominatorEngine engine) {
List<BasicBlock> lstRes = new ArrayList<>();
LinkedList<BasicBlock> stack = new LinkedList<>();
Set<BasicBlock> setVisited = new HashSet<>();
BasicBlock handler = range.getHandler();
stack.addFirst(handler);
stack.addFirst(start);
while (!stack.isEmpty()) {
BasicBlock block = stack.removeFirst();
setVisited.add(block);
if (range.getProtectedRange().contains(block) && engine.isDominator(block, handler)) {
if (range.getProtectedRange().contains(block) && engine.isDominator(block, start)) {
lstRes.add(block);
List<BasicBlock> lstSuccs = new ArrayList<>(block.getSuccs());
@@ -308,4 +309,139 @@ public class ExceptionDeobfuscator {
return false;
}
public static boolean handleMultipleEntryExceptionRanges(ControlFlowGraph graph) {
GenericDominatorEngine engine = new GenericDominatorEngine(new IGraph() {
public List<? extends IGraphNode> getReversePostOrderList() {
return graph.getReversePostOrder();
}
public Set<? extends IGraphNode> getRoots() {
return new HashSet<>(Collections.singletonList(graph.getFirst()));
}
});
engine.initialize();
boolean found = false;
while(true) {
found = false;
boolean splitted = false;
for(ExceptionRangeCFG range : graph.getExceptions()) {
Set<BasicBlock> setEntries = getRangeEntries(range);
if(setEntries.size() > 1) { // multiple-entry protected range
found = true;
if(splitExceptionRange(range, setEntries, graph, engine)) {
splitted = true;
break;
}
}
}
if(!splitted) {
break;
}
}
return !found;
}
private static Set<BasicBlock> getRangeEntries(ExceptionRangeCFG range) {
Set<BasicBlock> setEntries = new HashSet<>();
Set<BasicBlock> setRange= new HashSet<>(range.getProtectedRange());
for(BasicBlock block : range.getProtectedRange()) {
Set<BasicBlock> setPreds = new HashSet<>(block.getPreds());
setPreds.removeAll(setRange);
if (!setPreds.isEmpty()) {
setEntries.add(block);
}
}
return setEntries;
}
private static boolean splitExceptionRange(ExceptionRangeCFG range, Set<BasicBlock> setEntries, ControlFlowGraph graph, GenericDominatorEngine engine) {
for(BasicBlock entry : setEntries) {
List<BasicBlock> lstSubrangeBlocks = getReachableBlocksRestricted(entry, range, engine);
if(!lstSubrangeBlocks.isEmpty() && lstSubrangeBlocks.size() < range.getProtectedRange().size()) {
// add new range
ExceptionRangeCFG subRange = new ExceptionRangeCFG(lstSubrangeBlocks, range.getHandler(), range.getExceptionTypes());
graph.getExceptions().add(subRange);
// shrink the original range
range.getProtectedRange().removeAll(lstSubrangeBlocks);
return true;
} else {
// should not happen
DecompilerContext.getLogger().writeMessage("Inconsistency found while splitting protected range", IFernflowerLogger.Severity.WARN);
}
}
return false;
}
public static void insertDummyExceptionHandlerBlocks(ControlFlowGraph graph, int bytecode_version) {
Map<BasicBlock, Set<ExceptionRangeCFG>> mapRanges = new HashMap<>();
for (ExceptionRangeCFG range : graph.getExceptions()) {
mapRanges.computeIfAbsent(range.getHandler(), k -> new HashSet<>()).add(range);
}
for (Entry<BasicBlock, Set<ExceptionRangeCFG>> ent : mapRanges.entrySet()) {
BasicBlock handler = ent.getKey();
Set<ExceptionRangeCFG> ranges = ent.getValue();
if(ranges.size() == 1) {
continue;
}
for(ExceptionRangeCFG range : ranges) {
// add some dummy instructions to prevent optimizing away the empty block
SimpleInstructionSequence seq = new SimpleInstructionSequence();
seq.addInstruction(Instruction.create(CodeConstants.opc_bipush, false, CodeConstants.GROUP_GENERAL, bytecode_version, new int[]{0}), -1);
seq.addInstruction(Instruction.create(CodeConstants.opc_pop, false, CodeConstants.GROUP_GENERAL, bytecode_version, null), -1);
BasicBlock dummyBlock = new BasicBlock(++graph.last_id);
dummyBlock.setSeq(seq);
graph.getBlocks().addWithKey(dummyBlock, dummyBlock.id);
// only exception predecessors from this range considered
List<BasicBlock> lstPredExceptions = new ArrayList<>(handler.getPredExceptions());
lstPredExceptions.retainAll(range.getProtectedRange());
// replace predecessors
for (BasicBlock pred : lstPredExceptions) {
pred.replaceSuccessor(handler, dummyBlock);
}
// replace handler
range.setHandler(dummyBlock);
// add common exception edges
Set<BasicBlock> commonHandlers = new HashSet<>(handler.getSuccExceptions());
for(BasicBlock pred : lstPredExceptions) {
commonHandlers.retainAll(pred.getSuccExceptions());
}
// TODO: more sanity checks?
for(BasicBlock commonHandler : commonHandlers) {
ExceptionRangeCFG commonRange = graph.getExceptionRange(commonHandler, handler);
dummyBlock.addSuccessorException(commonHandler);
commonRange.getProtectedRange().add(dummyBlock);
}
dummyBlock.addSuccessor(handler);
}
}
}
}

View File

@@ -54,7 +54,7 @@ public class FieldExprent extends Exprent {
@Override
public int getExprentUse() {
return instance == null ? Exprent.MULTIPLE_USES : instance.getExprentUse() & Exprent.MULTIPLE_USES;
return 0; // multiple references to a field considered dangerous in a multithreaded environment, thus no Exprent.MULTIPLE_USES set here
}
@Override