From b78349aef7ec04e43953e2aa6bc769cdfff2ee09 Mon Sep 17 00:00:00 2001 From: Ahmed Ashour Date: Wed, 27 Mar 2019 09:41:56 +0100 Subject: [PATCH] fix: handle boolean condition with bitwise OR and AND (#202) (PR #522) --- .../dex/regions/conditions/IfCondition.java | 59 +++++++++--- .../conditions/TestBitwiseAnd.java | 92 +++++++++++++++++++ .../integration/conditions/TestBitwiseOr.java | 92 +++++++++++++++++++ .../conditions/TestConditions17.java | 33 ------- 4 files changed, 232 insertions(+), 44 deletions(-) create mode 100644 jadx-core/src/test/java/jadx/tests/integration/conditions/TestBitwiseAnd.java create mode 100644 jadx-core/src/test/java/jadx/tests/integration/conditions/TestBitwiseOr.java delete mode 100644 jadx-core/src/test/java/jadx/tests/integration/conditions/TestConditions17.java diff --git a/jadx-core/src/main/java/jadx/core/dex/regions/conditions/IfCondition.java b/jadx-core/src/main/java/jadx/core/dex/regions/conditions/IfCondition.java index 14644ae6e..907a383ee 100644 --- a/jadx-core/src/main/java/jadx/core/dex/regions/conditions/IfCondition.java +++ b/jadx-core/src/main/java/jadx/core/dex/regions/conditions/IfCondition.java @@ -7,9 +7,10 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import jadx.core.dex.instructions.ArithNode; +import jadx.core.dex.instructions.ArithOp; import jadx.core.dex.instructions.IfNode; import jadx.core.dex.instructions.IfOp; -import jadx.core.dex.instructions.InsnType; import jadx.core.dex.instructions.args.InsnWrapArg; import jadx.core.dex.instructions.args.LiteralArg; import jadx.core.dex.instructions.args.RegisterArg; @@ -142,7 +143,10 @@ public final class IfCondition { public static IfCondition simplify(IfCondition cond) { if (cond.isCompare()) { Compare c = cond.getCompare(); - simplifyCmpOp(c); + IfCondition i = simplifyCmpOp(c); + if (i != null) { + return i; + } if (c.getOp() == IfOp.EQ && c.getB().isLiteral() && c.getB().equals(LiteralArg.FALSE)) { cond = not(new IfCondition(c.invert())); } else { @@ -190,20 +194,53 @@ public final class IfCondition { return cond; } - private static void simplifyCmpOp(Compare c) { + private static IfCondition simplifyCmpOp(Compare c) { if (!c.getA().isInsnWrap()) { - return; + return null; } - if (!c.getB().isLiteral() || ((LiteralArg) c.getB()).getLiteral() != 0) { - return; + if (!c.getB().isLiteral()) { + return null; } + long lit = ((LiteralArg) c.getB()).getLiteral(); + if (lit != 0 && lit != 1) { + return null; + } + InsnNode wrapInsn = ((InsnWrapArg) c.getA()).getWrapInsn(); - InsnType type = wrapInsn.getType(); - if (type != InsnType.CMP_L && type != InsnType.CMP_G) { - return; + switch (wrapInsn.getType()) { + case CMP_L: + case CMP_G: + if (lit == 0) { + IfNode insn = c.getInsn(); + insn.changeCondition(insn.getOp(), wrapInsn.getArg(0), wrapInsn.getArg(1)); + } + break; + + case ARITH: + ArithOp arithOp = ((ArithNode) wrapInsn).getOp(); + if (arithOp == ArithOp.OR || arithOp == ArithOp.AND) { + IfOp ifOp = c.getInsn().getOp(); + boolean isTrue = ifOp == IfOp.NE && lit == 0 + || ifOp == IfOp.EQ && lit == 1; + + IfOp op = isTrue ? IfOp.NE : IfOp.EQ; + Mode mode = isTrue && arithOp == ArithOp.OR || + !isTrue && arithOp == ArithOp.AND ? Mode.OR : Mode.AND; + + IfNode if1 = new IfNode(op, -1, wrapInsn.getArg(0), LiteralArg.FALSE); + IfNode if2 = new IfNode(op, -1, wrapInsn.getArg(1), LiteralArg.FALSE); + return new IfCondition(mode, + Arrays.asList(new IfCondition(new Compare(if1)), + new IfCondition(new Compare(if2)))); + } + break; + + default: + break; } - IfNode insn = c.getInsn(); - insn.changeCondition(insn.getOp(), wrapInsn.getArg(0), wrapInsn.getArg(1)); + + + return null; } public List getRegisterArgs() { diff --git a/jadx-core/src/test/java/jadx/tests/integration/conditions/TestBitwiseAnd.java b/jadx-core/src/test/java/jadx/tests/integration/conditions/TestBitwiseAnd.java new file mode 100644 index 000000000..0946eaca2 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/conditions/TestBitwiseAnd.java @@ -0,0 +1,92 @@ +package jadx.tests.integration.conditions; + +import org.junit.jupiter.api.Test; + +import jadx.core.dex.nodes.ClassNode; +import jadx.tests.api.IntegrationTest; + +import static jadx.tests.api.utils.JadxMatchers.containsOne; +import static org.hamcrest.MatcherAssert.assertThat; + +public class TestBitwiseAnd extends IntegrationTest { + + public static class TestCls { + private boolean a; + private boolean b; + + public void test() { + if ((a & b) != false) { + test(); + } + } + } + + @Test + public void test() { + noDebugInfo(); + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); + + assertThat(code, containsOne("if (this.a && this.b) {")); + } + + public static class TestCls2 { + private boolean a; + private boolean b; + + public void test() { + if ((a & b) != true) { + test(); + } + } + } + + @Test + public void test2() { + noDebugInfo(); + ClassNode cls = getClassNode(TestCls2.class); + String code = cls.getCode().toString(); + + assertThat(code, containsOne("if (!this.a || !this.b) {")); + } + + public static class TestCls3 { + private boolean a; + private boolean b; + + public void test() { + if ((a & b) == false) { + test(); + } + } + } + + @Test + public void test3() { + noDebugInfo(); + ClassNode cls = getClassNode(TestCls3.class); + String code = cls.getCode().toString(); + + assertThat(code, containsOne("if (!this.a || !this.b) {")); + } + + public static class TestCls4 { + private boolean a; + private boolean b; + + public void test() { + if ((a & b) == true) { + test(); + } + } + } + + @Test + public void test4() { + noDebugInfo(); + ClassNode cls = getClassNode(TestCls4.class); + String code = cls.getCode().toString(); + + assertThat(code, containsOne("if (this.a && this.b) {")); + } +} diff --git a/jadx-core/src/test/java/jadx/tests/integration/conditions/TestBitwiseOr.java b/jadx-core/src/test/java/jadx/tests/integration/conditions/TestBitwiseOr.java new file mode 100644 index 000000000..c585347d3 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/conditions/TestBitwiseOr.java @@ -0,0 +1,92 @@ +package jadx.tests.integration.conditions; + +import org.junit.jupiter.api.Test; + +import jadx.core.dex.nodes.ClassNode; +import jadx.tests.api.IntegrationTest; + +import static jadx.tests.api.utils.JadxMatchers.containsOne; +import static org.hamcrest.MatcherAssert.assertThat; + +public class TestBitwiseOr extends IntegrationTest { + + public static class TestCls { + private boolean a; + private boolean b; + + public void test() { + if ((a | b) != false) { + test(); + } + } + } + + @Test + public void test() { + noDebugInfo(); + ClassNode cls = getClassNode(TestCls.class); + String code = cls.getCode().toString(); + + assertThat(code, containsOne("if (this.a || this.b) {")); + } + + public static class TestCls2 { + private boolean a; + private boolean b; + + public void test() { + if ((a | b) != true) { + test(); + } + } + } + + @Test + public void test2() { + noDebugInfo(); + ClassNode cls = getClassNode(TestCls2.class); + String code = cls.getCode().toString(); + + assertThat(code, containsOne("if (!this.a && !this.b) {")); + } + + public static class TestCls3 { + private boolean a; + private boolean b; + + public void test() { + if ((a | b) == false) { + test(); + } + } + } + + @Test + public void test3() { + noDebugInfo(); + ClassNode cls = getClassNode(TestCls3.class); + String code = cls.getCode().toString(); + + assertThat(code, containsOne("if (!this.a && !this.b) {")); + } + + public static class TestCls4 { + private boolean a; + private boolean b; + + public void test() { + if ((a | b) == true) { + test(); + } + } + } + + @Test + public void test4() { + noDebugInfo(); + ClassNode cls = getClassNode(TestCls4.class); + String code = cls.getCode().toString(); + + assertThat(code, containsOne("if (this.a || this.b) {")); + } +} diff --git a/jadx-core/src/test/java/jadx/tests/integration/conditions/TestConditions17.java b/jadx-core/src/test/java/jadx/tests/integration/conditions/TestConditions17.java deleted file mode 100644 index 68a882a49..000000000 --- a/jadx-core/src/test/java/jadx/tests/integration/conditions/TestConditions17.java +++ /dev/null @@ -1,33 +0,0 @@ -package jadx.tests.integration.conditions; - -import org.junit.jupiter.api.Test; - -import jadx.NotYetImplemented; -import jadx.core.dex.nodes.ClassNode; -import jadx.tests.api.IntegrationTest; - -import static jadx.tests.api.utils.JadxMatchers.containsOne; -import static org.hamcrest.MatcherAssert.assertThat; - -public class TestConditions17 extends IntegrationTest { - - public static class TestCls { - private boolean a; - private boolean b; - - public void test() { - if ((a | b) != false) { - test(); - } - } - } - - @Test - @NotYetImplemented - public void test202() { - ClassNode cls = getClassNode(TestCls.class); - String code = cls.getCode().toString(); - - assertThat(code, containsOne("a || b")); - } -}