-
Notifications
You must be signed in to change notification settings - Fork 60
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #82 from EpicPlayerA10/feat/simplify-switches
Simplify switches flow
- Loading branch information
Showing
6 changed files
with
262 additions
and
106 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
116 changes: 10 additions & 106 deletions
116
...main/java/uwu/narumi/deobfuscator/core/other/impl/universal/UniversalFlowTransformer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,110 +1,14 @@ | ||
package uwu.narumi.deobfuscator.core.other.impl.universal; | ||
|
||
import org.objectweb.asm.tree.AbstractInsnNode; | ||
import org.objectweb.asm.tree.JumpInsnNode; | ||
import org.objectweb.asm.tree.MethodNode; | ||
import org.objectweb.asm.tree.analysis.Frame; | ||
import org.objectweb.asm.tree.analysis.OriginalSourceValue; | ||
import uwu.narumi.deobfuscator.api.asm.ClassWrapper; | ||
import uwu.narumi.deobfuscator.api.context.Context; | ||
import uwu.narumi.deobfuscator.api.helper.AsmMathHelper; | ||
import uwu.narumi.deobfuscator.api.transformer.Transformer; | ||
|
||
import java.util.Map; | ||
|
||
public class UniversalFlowTransformer extends Transformer { | ||
|
||
private boolean changed = false; | ||
|
||
@Override | ||
protected boolean transform(ClassWrapper scope, Context context) throws Exception { | ||
context.classes(scope).forEach(classWrapper -> classWrapper.methods().forEach(methodNode -> { | ||
simplifyJumpInstructions(classWrapper, methodNode); | ||
})); | ||
|
||
return changed; | ||
} | ||
|
||
// TODO: Add LOOKUPSWITCH and TABLESWITCH | ||
private void simplifyJumpInstructions(ClassWrapper classWrapper, MethodNode methodNode) { | ||
Map<AbstractInsnNode, Frame<OriginalSourceValue>> frames = analyzeOriginalSource(classWrapper.getClassNode(), methodNode); | ||
if (frames == null) return; | ||
|
||
// Simplify 'jump' instructions | ||
for (AbstractInsnNode insn : methodNode.instructions.toArray()) { | ||
if (AsmMathHelper.isOneValueCondition(insn.getOpcode())) { | ||
// One-value if statement | ||
|
||
Frame<OriginalSourceValue> frame = frames.get(insn); | ||
if (frame == null) continue; | ||
|
||
JumpInsnNode jumpInsn = (JumpInsnNode) insn; | ||
|
||
// Get instruction from stack that is passed to if statement | ||
OriginalSourceValue sourceValue = frame.getStack(frame.getStackSize() - 1); | ||
if (!sourceValue.originalSource.isOneWayProduced()) continue; | ||
|
||
AbstractInsnNode valueInsn = sourceValue.originalSource.getProducer(); | ||
|
||
// Process if statement | ||
if (valueInsn.isInteger()) { | ||
boolean ifResult = AsmMathHelper.condition( | ||
valueInsn.asInteger(), // Value | ||
jumpInsn.getOpcode() // Opcode | ||
); | ||
|
||
// Correctly transform redundant if statement | ||
processRedundantIfStatement(methodNode, jumpInsn, ifResult); | ||
|
||
// Cleanup value | ||
methodNode.instructions.remove(sourceValue.getProducer()); | ||
|
||
changed = true; | ||
} | ||
} else if (AsmMathHelper.isTwoValuesCondition(insn.getOpcode())) { | ||
// Two-value if statements | ||
|
||
Frame<OriginalSourceValue> frame = frames.get(insn); | ||
if (frame == null) continue; | ||
|
||
JumpInsnNode jumpInsn = (JumpInsnNode) insn; | ||
|
||
// Get instructions from stack that are passed to if statement | ||
OriginalSourceValue sourceValue1 = frame.getStack(frame.getStackSize() - 2); | ||
OriginalSourceValue sourceValue2 = frame.getStack(frame.getStackSize() - 1); | ||
if (!sourceValue1.originalSource.isOneWayProduced() || !sourceValue2.originalSource.isOneWayProduced()) continue; | ||
|
||
AbstractInsnNode valueInsn1 = sourceValue1.originalSource.getProducer(); | ||
AbstractInsnNode valueInsn2 = sourceValue2.originalSource.getProducer(); | ||
|
||
// Process if statement | ||
if (valueInsn1.isInteger() && valueInsn2.isInteger()) { | ||
boolean ifResult = AsmMathHelper.condition( | ||
valueInsn1.asInteger(), // First value | ||
valueInsn2.asInteger(), // Second value | ||
jumpInsn.getOpcode() // Opcode | ||
); | ||
|
||
// Correctly transform redundant if statement | ||
processRedundantIfStatement(methodNode, jumpInsn, ifResult); | ||
|
||
// Cleanup values | ||
methodNode.instructions.remove(sourceValue1.getProducer()); | ||
methodNode.instructions.remove(sourceValue2.getProducer()); | ||
|
||
changed = true; | ||
} | ||
} | ||
} | ||
} | ||
|
||
private void processRedundantIfStatement(MethodNode methodNode, JumpInsnNode ifStatement, boolean ifResult) { | ||
if (!ifResult) { | ||
// Remove unreachable if statement | ||
methodNode.instructions.remove(ifStatement); | ||
} else { | ||
// Replace always reachable if statement with GOTO | ||
ifStatement.setOpcode(GOTO); | ||
} | ||
import uwu.narumi.deobfuscator.api.transformer.ComposedTransformer; | ||
import uwu.narumi.deobfuscator.core.other.impl.universal.flow.JumpFlowTransformer; | ||
import uwu.narumi.deobfuscator.core.other.impl.universal.flow.SwitchFlowTransformer; | ||
|
||
public class UniversalFlowTransformer extends ComposedTransformer { | ||
public UniversalFlowTransformer() { | ||
super( | ||
JumpFlowTransformer::new, | ||
SwitchFlowTransformer::new | ||
); | ||
} | ||
} |
85 changes: 85 additions & 0 deletions
85
...main/java/uwu/narumi/deobfuscator/core/other/impl/universal/flow/JumpFlowTransformer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package uwu.narumi.deobfuscator.core.other.impl.universal.flow; | ||
|
||
import org.objectweb.asm.tree.AbstractInsnNode; | ||
import org.objectweb.asm.tree.JumpInsnNode; | ||
import org.objectweb.asm.tree.MethodNode; | ||
import org.objectweb.asm.tree.analysis.Frame; | ||
import org.objectweb.asm.tree.analysis.OriginalSourceValue; | ||
import uwu.narumi.deobfuscator.api.asm.ClassWrapper; | ||
import uwu.narumi.deobfuscator.api.helper.AsmMathHelper; | ||
import uwu.narumi.deobfuscator.api.transformer.FramedInstructionsTransformer; | ||
|
||
public class JumpFlowTransformer extends FramedInstructionsTransformer { | ||
@Override | ||
protected boolean transformInstruction(ClassWrapper classWrapper, MethodNode methodNode, AbstractInsnNode insn, Frame<OriginalSourceValue> frame) { | ||
if (AsmMathHelper.isOneValueCondition(insn.getOpcode())) { | ||
// One-value if statement | ||
|
||
JumpInsnNode jumpInsn = (JumpInsnNode) insn; | ||
|
||
// Get instruction from stack that is passed to if statement | ||
OriginalSourceValue sourceValue = frame.getStack(frame.getStackSize() - 1); | ||
if (!sourceValue.originalSource.isOneWayProduced()) return false; | ||
|
||
AbstractInsnNode valueInsn = sourceValue.originalSource.getProducer(); | ||
|
||
// Process if statement | ||
if (valueInsn.isInteger()) { | ||
boolean ifResult = AsmMathHelper.condition( | ||
valueInsn.asInteger(), // Value | ||
jumpInsn.getOpcode() // Opcode | ||
); | ||
|
||
// Correctly transform redundant if statement | ||
processRedundantIfStatement(methodNode, jumpInsn, ifResult); | ||
|
||
// Cleanup value | ||
methodNode.instructions.remove(sourceValue.getProducer()); | ||
|
||
return true; | ||
} | ||
} else if (AsmMathHelper.isTwoValuesCondition(insn.getOpcode())) { | ||
// Two-value if statements | ||
|
||
JumpInsnNode jumpInsn = (JumpInsnNode) insn; | ||
|
||
// Get instructions from stack that are passed to if statement | ||
OriginalSourceValue sourceValue1 = frame.getStack(frame.getStackSize() - 2); | ||
OriginalSourceValue sourceValue2 = frame.getStack(frame.getStackSize() - 1); | ||
if (!sourceValue1.originalSource.isOneWayProduced() || !sourceValue2.originalSource.isOneWayProduced()) return false; | ||
|
||
AbstractInsnNode valueInsn1 = sourceValue1.originalSource.getProducer(); | ||
AbstractInsnNode valueInsn2 = sourceValue2.originalSource.getProducer(); | ||
|
||
// Process if statement | ||
if (valueInsn1.isInteger() && valueInsn2.isInteger()) { | ||
boolean ifResult = AsmMathHelper.condition( | ||
valueInsn1.asInteger(), // First value | ||
valueInsn2.asInteger(), // Second value | ||
jumpInsn.getOpcode() // Opcode | ||
); | ||
|
||
// Correctly transform redundant if statement | ||
processRedundantIfStatement(methodNode, jumpInsn, ifResult); | ||
|
||
// Cleanup values | ||
methodNode.instructions.remove(sourceValue1.getProducer()); | ||
methodNode.instructions.remove(sourceValue2.getProducer()); | ||
|
||
return true; | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
|
||
private void processRedundantIfStatement(MethodNode methodNode, JumpInsnNode ifStatement, boolean ifResult) { | ||
if (!ifResult) { | ||
// Remove unreachable if statement | ||
methodNode.instructions.remove(ifStatement); | ||
} else { | ||
// Replace always reachable if statement with GOTO | ||
ifStatement.setOpcode(GOTO); | ||
} | ||
} | ||
} |
72 changes: 72 additions & 0 deletions
72
...in/java/uwu/narumi/deobfuscator/core/other/impl/universal/flow/SwitchFlowTransformer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package uwu.narumi.deobfuscator.core.other.impl.universal.flow; | ||
|
||
import org.objectweb.asm.tree.AbstractInsnNode; | ||
import org.objectweb.asm.tree.JumpInsnNode; | ||
import org.objectweb.asm.tree.LabelNode; | ||
import org.objectweb.asm.tree.LookupSwitchInsnNode; | ||
import org.objectweb.asm.tree.MethodNode; | ||
import org.objectweb.asm.tree.TableSwitchInsnNode; | ||
import org.objectweb.asm.tree.analysis.Frame; | ||
import org.objectweb.asm.tree.analysis.OriginalSourceValue; | ||
import uwu.narumi.deobfuscator.api.asm.ClassWrapper; | ||
import uwu.narumi.deobfuscator.api.transformer.FramedInstructionsTransformer; | ||
|
||
/** | ||
* Simplify LOOKUPSWITCH and TABLESWITCH instructions | ||
*/ | ||
public class SwitchFlowTransformer extends FramedInstructionsTransformer { | ||
|
||
@Override | ||
protected boolean transformInstruction(ClassWrapper classWrapper, MethodNode methodNode, AbstractInsnNode insn, Frame<OriginalSourceValue> frame) { | ||
if (insn.getOpcode() == LOOKUPSWITCH) { | ||
LookupSwitchInsnNode lookupSwitchInsn = (LookupSwitchInsnNode) insn; | ||
|
||
OriginalSourceValue sourceValue = frame.getStack(frame.getStackSize() - 1); | ||
if (!sourceValue.originalSource.isOneWayProduced()) return false; | ||
|
||
AbstractInsnNode valueInsn = sourceValue.originalSource.getProducer(); | ||
if (valueInsn.isInteger()) { | ||
int value = valueInsn.asInteger(); | ||
int index = lookupSwitchInsn.keys.indexOf(value); | ||
|
||
if (index == -1) { | ||
// Jump to default | ||
methodNode.instructions.set(lookupSwitchInsn, new JumpInsnNode(GOTO, lookupSwitchInsn.dflt)); | ||
} else { | ||
// Match found! Jump to target | ||
LabelNode targetLabel = lookupSwitchInsn.labels.get(index); | ||
methodNode.instructions.set(lookupSwitchInsn, new JumpInsnNode(GOTO, targetLabel)); | ||
} | ||
// Remove value from stack | ||
methodNode.instructions.remove(sourceValue.getProducer()); | ||
|
||
return true; | ||
} | ||
} else if (insn.getOpcode() == TABLESWITCH) { | ||
TableSwitchInsnNode tableSwitchInsn = (TableSwitchInsnNode) insn; | ||
|
||
OriginalSourceValue sourceValue = frame.getStack(frame.getStackSize() - 1); | ||
if (!sourceValue.originalSource.isOneWayProduced()) return false; | ||
|
||
AbstractInsnNode valueInsn = sourceValue.originalSource.getProducer(); | ||
if (valueInsn.isInteger()) { | ||
int value = valueInsn.asInteger(); | ||
int index = value - tableSwitchInsn.min; | ||
|
||
if (index < 0 || index >= tableSwitchInsn.labels.size()) { | ||
// Jump to default | ||
methodNode.instructions.set(tableSwitchInsn, new JumpInsnNode(GOTO, tableSwitchInsn.dflt)); | ||
} else { | ||
// Match found! Jump to target | ||
LabelNode targetLabel = tableSwitchInsn.labels.get(index); | ||
methodNode.instructions.set(tableSwitchInsn, new JumpInsnNode(GOTO, targetLabel)); | ||
} | ||
// Remove value from stack | ||
methodNode.instructions.remove(sourceValue.getProducer()); | ||
|
||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters