From 0ca2671b4fef558abe8c68bf941d3e0a4a4a474d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Pluta?= Date: Thu, 11 Apr 2019 14:06:02 +0200 Subject: [PATCH] 14: Add JADX decompiler --- .../bartek/esa/analyser/apk/ApkAnalyser.java | 14 +++-- .../esa/analyser/di/AnalyserModule.java | 2 +- .../esa/decompiler/archetype/Decompiler.java | 10 ++++ .../{Decompiler.java => CfrDecompiler.java} | 25 +++++++-- .../decompiler/decompiler/JadxDecompiler.java | 53 +++++++++++++++++++ .../esa/decompiler/di/DecompilerModule.java | 9 ++-- .../decompiler/process/ProcessExecutor.java | 35 ++++++------ 7 files changed, 110 insertions(+), 38 deletions(-) create mode 100644 src/main/java/com/bartek/esa/decompiler/archetype/Decompiler.java rename src/main/java/com/bartek/esa/decompiler/decompiler/{Decompiler.java => CfrDecompiler.java} (82%) create mode 100644 src/main/java/com/bartek/esa/decompiler/decompiler/JadxDecompiler.java diff --git a/src/main/java/com/bartek/esa/analyser/apk/ApkAnalyser.java b/src/main/java/com/bartek/esa/analyser/apk/ApkAnalyser.java index ab4b5fc..41fd4ce 100644 --- a/src/main/java/com/bartek/esa/analyser/apk/ApkAnalyser.java +++ b/src/main/java/com/bartek/esa/analyser/apk/ApkAnalyser.java @@ -3,7 +3,7 @@ package com.bartek.esa.analyser.apk; import com.bartek.esa.analyser.core.Analyser; import com.bartek.esa.core.archetype.Plugin; import com.bartek.esa.core.executor.PluginExecutor; -import com.bartek.esa.decompiler.decompiler.Decompiler; +import com.bartek.esa.decompiler.archetype.Decompiler; import com.bartek.esa.error.EsaException; import com.bartek.esa.file.cleaner.FileCleaner; import com.bartek.esa.file.matcher.GlobMatcher; @@ -12,11 +12,9 @@ import com.bartek.esa.file.provider.FileProvider; import java.io.File; import java.util.Set; -public class ApkAnalyser extends Analyser { - private static final String ANDROID_MANIFEST_GLOB = "**/" + Decompiler.XML_FILES_DIR + "/AndroidManifest.xml"; - private static final String JAVA_GLOB = "**/" + Decompiler.JAVA_FILES_DIR + "/**/*.java"; - private static final String LAYOUT_GLOB = "**/" + Decompiler.XML_FILES_DIR + "/**/layout*/*.xml"; +import static java.lang.String.format; +public class ApkAnalyser extends Analyser { private final Decompiler decompiler; private final FileCleaner fileCleaner; private final GlobMatcher globMatcher; @@ -43,17 +41,17 @@ public class ApkAnalyser extends Analyser { @Override protected String getAndroidManifestGlob() { - return ANDROID_MANIFEST_GLOB; + return format("**/%s/AndroidManifest.xml", decompiler.getAndroidManifestFolder()); } @Override protected String getJavaGlob() { - return JAVA_GLOB; + return format("**/%s/**/*.java", decompiler.getJavaSourcesFolder()); } @Override protected String getLayoutGlob() { - return LAYOUT_GLOB; + return format("**/%s/layout*/*.xml", decompiler.getResFolder()); } @Override diff --git a/src/main/java/com/bartek/esa/analyser/di/AnalyserModule.java b/src/main/java/com/bartek/esa/analyser/di/AnalyserModule.java index f977f89..e8a3b0e 100644 --- a/src/main/java/com/bartek/esa/analyser/di/AnalyserModule.java +++ b/src/main/java/com/bartek/esa/analyser/di/AnalyserModule.java @@ -4,7 +4,7 @@ import com.bartek.esa.analyser.apk.ApkAnalyser; import com.bartek.esa.analyser.source.SourceAnalyser; import com.bartek.esa.core.archetype.Plugin; import com.bartek.esa.core.executor.PluginExecutor; -import com.bartek.esa.decompiler.decompiler.Decompiler; +import com.bartek.esa.decompiler.archetype.Decompiler; import com.bartek.esa.file.cleaner.FileCleaner; import com.bartek.esa.file.matcher.GlobMatcher; import com.bartek.esa.file.provider.FileProvider; diff --git a/src/main/java/com/bartek/esa/decompiler/archetype/Decompiler.java b/src/main/java/com/bartek/esa/decompiler/archetype/Decompiler.java new file mode 100644 index 0000000..66d908f --- /dev/null +++ b/src/main/java/com/bartek/esa/decompiler/archetype/Decompiler.java @@ -0,0 +1,10 @@ +package com.bartek.esa.decompiler.archetype; + +import java.io.File; + +public interface Decompiler { + File decompile(File inputApk, boolean debug); + String getAndroidManifestFolder(); + String getResFolder(); + String getJavaSourcesFolder(); +} diff --git a/src/main/java/com/bartek/esa/decompiler/decompiler/Decompiler.java b/src/main/java/com/bartek/esa/decompiler/decompiler/CfrDecompiler.java similarity index 82% rename from src/main/java/com/bartek/esa/decompiler/decompiler/Decompiler.java rename to src/main/java/com/bartek/esa/decompiler/decompiler/CfrDecompiler.java index 21ca070..e17b102 100644 --- a/src/main/java/com/bartek/esa/decompiler/decompiler/Decompiler.java +++ b/src/main/java/com/bartek/esa/decompiler/decompiler/CfrDecompiler.java @@ -1,5 +1,6 @@ package com.bartek.esa.decompiler.decompiler; +import com.bartek.esa.decompiler.archetype.Decompiler; import com.bartek.esa.decompiler.process.ProcessExecutor; import com.bartek.esa.file.cleaner.FileCleaner; import com.bartek.esa.file.provider.FileProvider; @@ -10,9 +11,9 @@ import javax.inject.Inject; import java.io.File; import java.util.Set; -public class Decompiler { - public static final String XML_FILES_DIR = "xml"; - public static final String JAVA_FILES_DIR = "java"; +public class CfrDecompiler implements Decompiler { + private static final String XML_FILES_DIR = "xml"; + private static final String JAVA_FILES_DIR = "java"; private static final String APK_UNZIPPED_DIR = "apk_unzipped"; private static final String JAR_FILES_DIR = "jar"; @@ -23,13 +24,14 @@ public class Decompiler { private final FileCleaner fileCleaner; @Inject - public Decompiler(FileProvider fileProvider, ProcessExecutor processExecutor1, ZipTool zipTool, FileCleaner fileCleaner) { + public CfrDecompiler(FileProvider fileProvider, ProcessExecutor processExecutor1, ZipTool zipTool, FileCleaner fileCleaner) { this.fileProvider = fileProvider; this.processExecutor = processExecutor1; this.zipTool = zipTool; this.fileCleaner = fileCleaner; } + @Override public File decompile(File inputApk, boolean debug) { File tmp = fileProvider.createTemporaryDirectory(); File javaDirectory = new File(tmp, JAVA_FILES_DIR); @@ -41,6 +43,21 @@ public class Decompiler { return tmp; } + @Override + public String getAndroidManifestFolder() { + return ""; + } + + @Override + public String getResFolder() { + return XML_FILES_DIR; + } + + @Override + public String getJavaSourcesFolder() { + return JAVA_FILES_DIR; + } + private void decompileJavaFiles(File inputApk, File tmp, File javaDirectory, boolean debug) { File unzippedApkDirectory = new File(tmp, APK_UNZIPPED_DIR); File jarDirectory = new File(tmp, JAR_FILES_DIR); diff --git a/src/main/java/com/bartek/esa/decompiler/decompiler/JadxDecompiler.java b/src/main/java/com/bartek/esa/decompiler/decompiler/JadxDecompiler.java new file mode 100644 index 0000000..b8cb4b0 --- /dev/null +++ b/src/main/java/com/bartek/esa/decompiler/decompiler/JadxDecompiler.java @@ -0,0 +1,53 @@ +package com.bartek.esa.decompiler.decompiler; + +import com.bartek.esa.decompiler.archetype.Decompiler; +import com.bartek.esa.decompiler.process.ProcessExecutor; +import com.bartek.esa.file.provider.FileProvider; + +import javax.inject.Inject; +import java.io.File; + +public class JadxDecompiler implements Decompiler { + private static final String RESOURCES_FILES_DIR = "resources"; + private static final String JAVA_FILES_DIR = "sources"; + + private final FileProvider fileProvider; + private final ProcessExecutor processExecutor; + + @Inject + public JadxDecompiler(FileProvider fileProvider, ProcessExecutor processExecutor) { + this.fileProvider = fileProvider; + this.processExecutor = processExecutor; + } + + @Override + public File decompile(File inputApk, boolean debug) { + File tmp = fileProvider.createTemporaryDirectory(); + runJadx(inputApk, tmp, debug); + return tmp; + } + + private void runJadx(File inputApk, File tmp, boolean debug) { + String[] cmd = { + "jadx", inputApk.getAbsolutePath(), + "-ds", new File(tmp, JAVA_FILES_DIR).getAbsolutePath(), + "-dr", new File(tmp, RESOURCES_FILES_DIR).getAbsolutePath() + }; + processExecutor.execute(cmd, debug); + } + + @Override + public String getAndroidManifestFolder() { + return RESOURCES_FILES_DIR; + } + + @Override + public String getResFolder() { + return RESOURCES_FILES_DIR + "/res"; + } + + @Override + public String getJavaSourcesFolder() { + return JAVA_FILES_DIR; + } +} diff --git a/src/main/java/com/bartek/esa/decompiler/di/DecompilerModule.java b/src/main/java/com/bartek/esa/decompiler/di/DecompilerModule.java index 77534af..a5b3826 100644 --- a/src/main/java/com/bartek/esa/decompiler/di/DecompilerModule.java +++ b/src/main/java/com/bartek/esa/decompiler/di/DecompilerModule.java @@ -1,10 +1,9 @@ package com.bartek.esa.decompiler.di; -import com.bartek.esa.decompiler.decompiler.Decompiler; +import com.bartek.esa.decompiler.archetype.Decompiler; +import com.bartek.esa.decompiler.decompiler.JadxDecompiler; import com.bartek.esa.decompiler.process.ProcessExecutor; -import com.bartek.esa.file.cleaner.FileCleaner; import com.bartek.esa.file.provider.FileProvider; -import com.bartek.esa.file.zip.ZipTool; import dagger.Module; import dagger.Provides; @@ -17,7 +16,7 @@ public class DecompilerModule { } @Provides - public Decompiler decompiler(FileProvider fileProvider, ProcessExecutor processExecutor, ZipTool zipTool, FileCleaner fileCleaner) { - return new Decompiler(fileProvider, processExecutor, zipTool, fileCleaner); + public Decompiler decompiler(FileProvider fileProvider, ProcessExecutor processExecutor) { + return new JadxDecompiler(fileProvider, processExecutor); } } diff --git a/src/main/java/com/bartek/esa/decompiler/process/ProcessExecutor.java b/src/main/java/com/bartek/esa/decompiler/process/ProcessExecutor.java index e4e2b5d..f804736 100644 --- a/src/main/java/com/bartek/esa/decompiler/process/ProcessExecutor.java +++ b/src/main/java/com/bartek/esa/decompiler/process/ProcessExecutor.java @@ -21,44 +21,39 @@ public class ProcessExecutor { .getOrElseThrow(EsaException::new); printStdOutAndStdErrFromProcess(debug, process); waitForProcess(process); - checkExitValue(process, command[0]); } private void printCommandLine(String[] command, boolean debug) { - if(debug) { + if (debug) { System.out.println(String.join(" ", command)); } } private void printStdOutAndStdErrFromProcess(boolean debug, Process process) { - if(debug) { - BufferedReader stdout = new BufferedReader(new InputStreamReader(process.getInputStream())); - BufferedReader stderr = new BufferedReader(new InputStreamReader(process.getErrorStream())); - String line; - try { - while ((line = stdout.readLine()) != null) { + BufferedReader stdout = new BufferedReader(new InputStreamReader(process.getInputStream())); + BufferedReader stderr = new BufferedReader(new InputStreamReader(process.getErrorStream())); + String line; + try { + while ((line = stdout.readLine()) != null) { + if (debug) { System.out.println(line); } + } - while ((line = stderr.readLine()) != null) { + while ((line = stderr.readLine()) != null) { + if (debug) { System.err.println(line); } - - stdout.close(); - stderr.close(); - } catch (IOException e) { - throw new EsaException(e); } + + stdout.close(); + stderr.close(); + } catch (IOException e) { + throw new EsaException(e); } } private void waitForProcess(Process process) { Try.run(process::waitFor).getOrElseThrow(EsaException::new); } - - private void checkExitValue(Process process, String commandName) { - if (process.exitValue() != 0) { - throw new EsaException("'" + commandName + "' process has finished with non-zero code. Interrupting..."); - } - } }