From 8f3cc3e8c178632e61fd57eccd5182bfee458fd3 Mon Sep 17 00:00:00 2001 From: Skylot <118523+skylot@users.noreply.github.com> Date: Mon, 14 Oct 2024 19:08:07 +0100 Subject: [PATCH] fix: add missing null check in codegen for classes generated by jadx --- jadx-cli/src/main/java/jadx/cli/JadxCLI.java | 30 ++++++++-------- .../src/test/java/jadx/cli/TestInput.java | 32 ++++++++++++++++++ jadx-cli/src/test/resources/samples/small.apk | Bin 0 -> 9886 bytes .../jadx/core/codegen/utils/CodeGenUtils.java | 4 ++- .../java/jadx/core/dex/nodes/ClassNode.java | 7 +++- 5 files changed, 57 insertions(+), 16 deletions(-) create mode 100644 jadx-cli/src/test/resources/samples/small.apk diff --git a/jadx-cli/src/main/java/jadx/cli/JadxCLI.java b/jadx-cli/src/main/java/jadx/cli/JadxCLI.java index 2959c23ee..ef5465fcb 100644 --- a/jadx-cli/src/main/java/jadx/cli/JadxCLI.java +++ b/jadx-cli/src/main/java/jadx/cli/JadxCLI.java @@ -22,26 +22,28 @@ public class JadxCLI { private static final Logger LOG = LoggerFactory.getLogger(JadxCLI.class); public static void main(String[] args) { - int result = 0; + int result = 1; try { result = execute(args); - } catch (JadxArgsValidateException e) { - LOG.error("Incorrect arguments: {}", e.getMessage()); - result = 1; - } catch (Throwable e) { - LOG.error("Process error:", e); - result = 1; } finally { System.exit(result); } } public static int execute(String[] args) { - JadxCLIArgs jadxArgs = new JadxCLIArgs(); - if (jadxArgs.processArgs(args)) { - return processAndSave(jadxArgs); + try { + JadxCLIArgs jadxArgs = new JadxCLIArgs(); + if (jadxArgs.processArgs(args)) { + return processAndSave(jadxArgs); + } + return 0; + } catch (JadxArgsValidateException e) { + LOG.error("Incorrect arguments: {}", e.getMessage()); + return 1; + } catch (Throwable e) { + LOG.error("Process error:", e); + return 1; } - return 0; } private static int processAndSave(JadxCLIArgs cliArgs) { @@ -66,11 +68,11 @@ public class JadxCLI { if (errorsCount != 0) { jadx.printErrorsReport(); LOG.error("finished with errors, count: {}", errorsCount); - } else { - LOG.info("done"); + return 1; } + LOG.info("done"); + return 0; } - return 0; } private static void initCodeWriterProvider(JadxArgs jadxArgs) { diff --git a/jadx-cli/src/test/java/jadx/cli/TestInput.java b/jadx-cli/src/test/java/jadx/cli/TestInput.java index b8e3adece..b11dfb4cf 100644 --- a/jadx-cli/src/test/java/jadx/cli/TestInput.java +++ b/jadx-cli/src/test/java/jadx/cli/TestInput.java @@ -12,6 +12,7 @@ import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.assertj.core.api.Condition; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import org.slf4j.Logger; @@ -36,6 +37,22 @@ public class TestInput { assertThat(result).isEqualTo(0); } + @Test + public void testApkInput() throws Exception { + int result = JadxCLI.execute(buildArgs(List.of(), "samples/small.apk")); + assertThat(result).isEqualTo(0); + List resultFiles = collectAllFilesInDir(testDir); + printFiles(resultFiles); + assertThat(resultFiles) + .describedAs("check output files") + .map(p -> p.getFileName().toString()) + .haveExactly(2, new Condition<>(f -> f.endsWith(".java"), "java classes")) + .haveExactly(9, new Condition<>(f -> f.endsWith(".xml"), "xml resources")) + .haveExactly(1, new Condition<>(f -> f.equals("classes.dex"), "dex")) + .haveExactly(1, new Condition<>(f -> f.equals("AndroidManifest.xml"), "manifest")) + .hasSize(13); + } + @Test public void testDexInput() throws Exception { decompile("samples/hello.dex"); @@ -110,11 +127,26 @@ public class TestInput { return args.toArray(new String[0]); } + private void printFiles(List files) { + LOG.info("Output files (count: {}):", files.size()); + for (Path file : files) { + LOG.info(" {}", testDir.relativize(file)); + } + } + private static List collectJavaFilesInDir(Path dir) throws IOException { PathMatcher javaMatcher = dir.getFileSystem().getPathMatcher("glob:**.java"); return collectFilesInDir(dir, javaMatcher); } + private static List collectAllFilesInDir(Path dir) throws IOException { + try (Stream pathStream = Files.walk(dir)) { + return pathStream + .filter(Files::isRegularFile) + .collect(Collectors.toList()); + } + } + private static List collectFilesInDir(Path dir, PathMatcher matcher) throws IOException { try (Stream pathStream = Files.walk(dir)) { return pathStream diff --git a/jadx-cli/src/test/resources/samples/small.apk b/jadx-cli/src/test/resources/samples/small.apk new file mode 100644 index 0000000000000000000000000000000000000000..7ad35494a6edeb0ccd9d24cfe8d0f35a9d20f90f GIT binary patch literal 9886 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 CPt=0| literal 0 HcmV?d00001 diff --git a/jadx-core/src/main/java/jadx/core/codegen/utils/CodeGenUtils.java b/jadx-core/src/main/java/jadx/core/codegen/utils/CodeGenUtils.java index 778cbe668..b703df797 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/utils/CodeGenUtils.java +++ b/jadx-core/src/main/java/jadx/core/codegen/utils/CodeGenUtils.java @@ -140,7 +140,9 @@ public class CodeGenUtils { String inputFileName = cls.getClsData().getInputFileName(); if (inputFileName != null) { ClassNode declCls = cls.getDeclaringClass(); - if (declCls != null && inputFileName.equals(declCls.getClsData().getInputFileName())) { + if (declCls != null + && declCls.getClsData() != null + && inputFileName.equals(declCls.getClsData().getInputFileName())) { // don't add same comment for inner classes return; } diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java index fa97363ba..13d13c43b 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/ClassNode.java @@ -864,7 +864,12 @@ public class ClassNode extends NotificationAttrNode } } - public IClassData getClsData() { + /** + * Low level class data access. + * + * @return null for classes generated by jadx + */ + public @Nullable IClassData getClsData() { return clsData; }