From b52f35259b73e790522f24f21f27019555f6e587 Mon Sep 17 00:00:00 2001 From: Skylot Date: Sun, 28 Jan 2018 00:02:41 +0300 Subject: [PATCH] core: support 'not-int' and 'not-long' instructions --- jadx-core/build.gradle | 3 +- .../main/java/jadx/core/codegen/InsnGen.java | 29 +++++++++------ .../core/dex/instructions/InsnDecoder.java | 12 +++++++ .../jadx/core/dex/instructions/InsnType.java | 1 + .../java/jadx/core/utils/ErrorsCounter.java | 20 ++--------- .../java/jadx/tests/api/IntegrationTest.java | 17 +++------ .../test/java/jadx/tests/api/SmaliTest.java | 36 ++++++++++--------- .../tests/api/compiler/StaticCompiler.java | 2 +- .../arith}/TestArithConst.java | 4 +-- .../tests/integration/arith/TestArithNot.java | 35 ++++++++++++++++++ .../smali/{ => arith}/TestArithConst.smali | 0 .../src/test/smali/arith/TestArithNot.smali | 20 +++++++++++ 12 files changed, 118 insertions(+), 61 deletions(-) rename jadx-core/src/test/java/jadx/tests/{smali => integration/arith}/TestArithConst.java (77%) create mode 100644 jadx-core/src/test/java/jadx/tests/integration/arith/TestArithNot.java rename jadx-core/src/test/smali/{ => arith}/TestArithConst.smali (100%) create mode 100644 jadx-core/src/test/smali/arith/TestArithNot.smali diff --git a/jadx-core/build.gradle b/jadx-core/build.gradle index b863e94d9..645f2c9cb 100644 --- a/jadx-core/build.gradle +++ b/jadx-core/build.gradle @@ -9,6 +9,7 @@ dependencies { compile 'com.intellij:annotations:12.0' compile 'uk.com.robust-it:cloning:1.9.2' - testCompile 'org.smali:smali:2.0.3' + testCompile 'org.smali:smali:2.2.2' + testCompile 'org.smali:baksmali:2.2.2' } diff --git a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java index 31f58a15e..14cd86398 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java @@ -270,18 +270,13 @@ public class InsnGen { makeArith((ArithNode) insn, code, state); break; - case NEG: { - boolean wrap = state.contains(Flags.BODY_ONLY); - if (wrap) { - code.add('('); - } - code.add('-'); - addArg(code, insn.getArg(0)); - if (wrap) { - code.add(')'); - } + case NEG: + oneArgInsn(code, insn, state, '-'); + break; + + case NOT: + oneArgInsn(code, insn, state, '~'); break; - } case RETURN: if (insn.getArgsCount() != 0) { @@ -525,6 +520,18 @@ public class InsnGen { } } + private void oneArgInsn(CodeWriter code, InsnNode insn, Set state, char op) throws CodegenException { + boolean wrap = state.contains(Flags.BODY_ONLY); + if (wrap) { + code.add('('); + } + code.add(op); + addArg(code, insn.getArg(0)); + if (wrap) { + code.add(')'); + } + } + private void fallbackOnlyInsn(InsnNode insn) throws CodegenException { if (!fallback) { throw new CodegenException(insn.getType() + " can be used only in fallback mode"); diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/InsnDecoder.java b/jadx-core/src/main/java/jadx/core/dex/instructions/InsnDecoder.java index 5142a94f2..920802d99 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/InsnDecoder.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/InsnDecoder.java @@ -324,6 +324,11 @@ public class InsnDecoder { case Opcodes.NEG_DOUBLE: return neg(insn, ArgType.DOUBLE); + case Opcodes.NOT_INT: + return not(insn, ArgType.INT); + case Opcodes.NOT_LONG: + return not(insn, ArgType.LONG); + case Opcodes.INT_TO_BYTE: return cast(insn, ArgType.INT, ArgType.BYTE); case Opcodes.INT_TO_CHAR: @@ -699,6 +704,13 @@ public class InsnDecoder { return inode; } + private InsnNode not(DecodedInstruction insn, ArgType type) { + InsnNode inode = new InsnNode(InsnType.NOT, 1); + inode.setResult(InsnArg.reg(insn, 0, type)); + inode.addArg(InsnArg.reg(insn, 1, type)); + return inode; + } + private InsnNode insn(InsnType type, RegisterArg res) { InsnNode node = new InsnNode(type, 0); node.setResult(res); diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/InsnType.java b/jadx-core/src/main/java/jadx/core/dex/instructions/InsnType.java index 7465f8261..7162b61cd 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/InsnType.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/InsnType.java @@ -8,6 +8,7 @@ public enum InsnType { ARITH, NEG, + NOT, MOVE, CAST, diff --git a/jadx-core/src/main/java/jadx/core/utils/ErrorsCounter.java b/jadx-core/src/main/java/jadx/core/utils/ErrorsCounter.java index 0a08b7823..db7c64bec 100644 --- a/jadx-core/src/main/java/jadx/core/utils/ErrorsCounter.java +++ b/jadx-core/src/main/java/jadx/core/utils/ErrorsCounter.java @@ -81,26 +81,10 @@ public class ErrorsCounter { } public static String formatErrorMsg(ClassNode cls, String msg) { - return msg + " in class: " + cls; + return msg + " in class: " + cls + ", dex: " + cls.dex().getDexFile().getName(); } public static String formatErrorMsg(MethodNode mth, String msg) { - return msg + " in method: " + mth; - } - - private String formatException(Throwable e) { - if (e == null || e.getMessage() == null) { - return ""; - } else { - return "\n error: " + e.getMessage(); - } - } - - public String formatErrorMsg(ClassNode cls, String msg, Throwable e) { - return formatErrorMsg(cls, msg) + formatException(e); - } - - public String formatErrorMsg(MethodNode mth, String msg, Throwable e) { - return formatErrorMsg(mth, msg) + formatException(e); + return msg + " in method: " + mth + ", dex: " + mth.dex().getDexFile().getName(); } } diff --git a/jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java b/jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java index 4aab04b17..04c5ac700 100644 --- a/jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java +++ b/jadx-core/src/test/java/jadx/tests/api/IntegrationTest.java @@ -35,7 +35,6 @@ import jadx.tests.api.compiler.StaticCompiler; import jadx.tests.api.utils.TestUtils; import static jadx.core.utils.files.FileUtils.addFileToJar; -import static jadx.core.utils.files.FileUtils.close; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.is; @@ -261,11 +260,11 @@ public abstract class IntegrationTest extends TestUtils { assertThat("File list is empty", list, not(empty())); File temp = createTempFile(".jar"); - JarOutputStream jo = new JarOutputStream(new FileOutputStream(temp)); - for (File file : list) { - addFileToJar(jo, file, path + "/" + file.getName()); + try (JarOutputStream jo = new JarOutputStream(new FileOutputStream(temp))) { + for (File file : list) { + addFileToJar(jo, file, path + "/" + file.getName()); + } } - close(jo); return temp; } @@ -336,13 +335,7 @@ public abstract class IntegrationTest extends TestUtils { outTmp.deleteOnExit(); List files = StaticCompiler.compile(compileFileList, outTmp, withDebugInfo); // remove classes which are parents for test class - Iterator iterator = files.iterator(); - while (iterator.hasNext()) { - File next = iterator.next(); - if (!next.getName().contains(cls.getSimpleName())) { - iterator.remove(); - } - } + files.removeIf(next -> !next.getName().contains(cls.getSimpleName())); for (File clsFile : files) { clsFile.deleteOnExit(); } diff --git a/jadx-core/src/test/java/jadx/tests/api/SmaliTest.java b/jadx-core/src/test/java/jadx/tests/api/SmaliTest.java index f82748b31..7b79d3504 100644 --- a/jadx-core/src/test/java/jadx/tests/api/SmaliTest.java +++ b/jadx-core/src/test/java/jadx/tests/api/SmaliTest.java @@ -1,28 +1,33 @@ package jadx.tests.api; import java.io.File; -import java.util.ArrayList; -import java.util.List; -import org.jf.smali.main; +import org.jf.smali.Smali; +import org.jf.smali.SmaliOptions; import jadx.core.dex.nodes.ClassNode; -import static org.junit.Assert.fail; - public abstract class SmaliTest extends IntegrationTest { private static final String SMALI_TESTS_PROJECT = "jadx-core"; private static final String SMALI_TESTS_DIR = "src/test/smali"; private static final String SMALI_TESTS_EXT = ".smali"; - protected ClassNode getClassNodeFromSmali(String clsName) { - File smaliFile = getSmaliFile(clsName); + protected ClassNode getClassNodeFromSmali(String file, String clsName) { + File smaliFile = getSmaliFile(file); File outDex = createTempFile(".dex"); compileSmali(smaliFile, outDex); return getClassNodeFromFile(outDex, clsName); } + protected ClassNode getClassNodeFromSmaliWithPath(String path, String clsName) { + return getClassNodeFromSmali(path + File.separatorChar + clsName, clsName); + } + + protected ClassNode getClassNodeFromSmali(String clsName) { + return getClassNodeFromSmali(clsName, clsName); + } + private static File getSmaliFile(String clsName) { File smaliFile = new File(SMALI_TESTS_DIR, clsName + SMALI_TESTS_EXT); if (smaliFile.exists()) { @@ -32,18 +37,17 @@ public abstract class SmaliTest extends IntegrationTest { if (smaliFile.exists()) { return smaliFile; } - fail("Smali file not found: " + SMALI_TESTS_DIR + "/" + clsName + SMALI_TESTS_EXT); - return null; + throw new AssertionError("Smali file not found: " + SMALI_TESTS_DIR + "/" + clsName + SMALI_TESTS_EXT); } private static boolean compileSmali(File input, File output) { - List args = new ArrayList<>(); - args.add(input.getAbsolutePath()); - - args.add("-o"); - args.add(output.getAbsolutePath()); - - main.main(args.toArray(new String[args.size()])); + try { + SmaliOptions params = new SmaliOptions(); + params.outputDexFile = output.getAbsolutePath(); + Smali.assemble(params, input.getAbsolutePath()); + } catch (Exception e) { + throw new AssertionError("Smali assemble error", e); + } return true; } } diff --git a/jadx-core/src/test/java/jadx/tests/api/compiler/StaticCompiler.java b/jadx-core/src/test/java/jadx/tests/api/compiler/StaticCompiler.java index bf0ec4bd1..fbc986623 100644 --- a/jadx-core/src/test/java/jadx/tests/api/compiler/StaticCompiler.java +++ b/jadx-core/src/test/java/jadx/tests/api/compiler/StaticCompiler.java @@ -22,7 +22,7 @@ import static javax.tools.JavaCompiler.CompilationTask; public class StaticCompiler { - private static final List COMMON_ARGS = Arrays.asList("-source 1.7 -target 1.7".split(" ")); + private static final List COMMON_ARGS = Arrays.asList("-source 1.8 -target 1.8".split(" ")); public static List compile(List files, File outDir, boolean includeDebugInfo) throws IOException { diff --git a/jadx-core/src/test/java/jadx/tests/smali/TestArithConst.java b/jadx-core/src/test/java/jadx/tests/integration/arith/TestArithConst.java similarity index 77% rename from jadx-core/src/test/java/jadx/tests/smali/TestArithConst.java rename to jadx-core/src/test/java/jadx/tests/integration/arith/TestArithConst.java index b6a872368..8476f3354 100644 --- a/jadx-core/src/test/java/jadx/tests/smali/TestArithConst.java +++ b/jadx-core/src/test/java/jadx/tests/integration/arith/TestArithConst.java @@ -1,4 +1,4 @@ -package jadx.tests.smali; +package jadx.tests.integration.arith; import org.junit.Test; @@ -13,7 +13,7 @@ public class TestArithConst extends SmaliTest { @Test public void test() { noDebugInfo(); - ClassNode cls = getClassNodeFromSmali("TestArithConst"); + ClassNode cls = getClassNodeFromSmaliWithPath("arith", "TestArithConst"); String code = cls.getCode().toString(); assertThat(code, containsOne("return i + CONST_INT;")); diff --git a/jadx-core/src/test/java/jadx/tests/integration/arith/TestArithNot.java b/jadx-core/src/test/java/jadx/tests/integration/arith/TestArithNot.java new file mode 100644 index 000000000..dc7944077 --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/arith/TestArithNot.java @@ -0,0 +1,35 @@ +package jadx.tests.integration.arith; + +import org.junit.Test; + +import jadx.core.dex.nodes.ClassNode; +import jadx.tests.api.SmaliTest; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertThat; + +public class TestArithNot extends SmaliTest { + /* + Smali Code equivalent: + public static class TestCls { + public int test1(int a) { + return ~a; + } + + public long test2(long b) { + return ~b; + } + } + */ + + @Test + public void test() { + ClassNode cls = getClassNodeFromSmaliWithPath("arith", "TestArithNot"); + String code = cls.getCode().toString(); + + assertThat(code, containsString("return ~a;")); + assertThat(code, containsString("return ~b;")); + assertThat(code, not(containsString("^"))); + } +} diff --git a/jadx-core/src/test/smali/TestArithConst.smali b/jadx-core/src/test/smali/arith/TestArithConst.smali similarity index 100% rename from jadx-core/src/test/smali/TestArithConst.smali rename to jadx-core/src/test/smali/arith/TestArithConst.smali diff --git a/jadx-core/src/test/smali/arith/TestArithNot.smali b/jadx-core/src/test/smali/arith/TestArithNot.smali new file mode 100644 index 000000000..b40c27616 --- /dev/null +++ b/jadx-core/src/test/smali/arith/TestArithNot.smali @@ -0,0 +1,20 @@ +.class public LTestArithNot; +.super Ljava/lang/Object; + +.method private test1(I)I + .registers 2 + .param p1, "a" + + not-int v0, p1 + + return v0 +.end method + +.method private test2(J)J + .registers 4 + .param p1, "b" + + not-long v0, p1 + + return v0 +.end method