14: Add JADX decompiler

This commit is contained in:
Bartłomiej Pluta
2019-04-11 14:06:02 +02:00
parent 9b2dd2817a
commit 0ca2671b4f
7 changed files with 110 additions and 38 deletions

View File

@@ -3,7 +3,7 @@ package com.bartek.esa.analyser.apk;
import com.bartek.esa.analyser.core.Analyser; import com.bartek.esa.analyser.core.Analyser;
import com.bartek.esa.core.archetype.Plugin; import com.bartek.esa.core.archetype.Plugin;
import com.bartek.esa.core.executor.PluginExecutor; 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.error.EsaException;
import com.bartek.esa.file.cleaner.FileCleaner; import com.bartek.esa.file.cleaner.FileCleaner;
import com.bartek.esa.file.matcher.GlobMatcher; import com.bartek.esa.file.matcher.GlobMatcher;
@@ -12,11 +12,9 @@ import com.bartek.esa.file.provider.FileProvider;
import java.io.File; import java.io.File;
import java.util.Set; import java.util.Set;
public class ApkAnalyser extends Analyser { import static java.lang.String.format;
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";
public class ApkAnalyser extends Analyser {
private final Decompiler decompiler; private final Decompiler decompiler;
private final FileCleaner fileCleaner; private final FileCleaner fileCleaner;
private final GlobMatcher globMatcher; private final GlobMatcher globMatcher;
@@ -43,17 +41,17 @@ public class ApkAnalyser extends Analyser {
@Override @Override
protected String getAndroidManifestGlob() { protected String getAndroidManifestGlob() {
return ANDROID_MANIFEST_GLOB; return format("**/%s/AndroidManifest.xml", decompiler.getAndroidManifestFolder());
} }
@Override @Override
protected String getJavaGlob() { protected String getJavaGlob() {
return JAVA_GLOB; return format("**/%s/**/*.java", decompiler.getJavaSourcesFolder());
} }
@Override @Override
protected String getLayoutGlob() { protected String getLayoutGlob() {
return LAYOUT_GLOB; return format("**/%s/layout*/*.xml", decompiler.getResFolder());
} }
@Override @Override

View File

@@ -4,7 +4,7 @@ import com.bartek.esa.analyser.apk.ApkAnalyser;
import com.bartek.esa.analyser.source.SourceAnalyser; import com.bartek.esa.analyser.source.SourceAnalyser;
import com.bartek.esa.core.archetype.Plugin; import com.bartek.esa.core.archetype.Plugin;
import com.bartek.esa.core.executor.PluginExecutor; 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.cleaner.FileCleaner;
import com.bartek.esa.file.matcher.GlobMatcher; import com.bartek.esa.file.matcher.GlobMatcher;
import com.bartek.esa.file.provider.FileProvider; import com.bartek.esa.file.provider.FileProvider;

View File

@@ -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();
}

View File

@@ -1,5 +1,6 @@
package com.bartek.esa.decompiler.decompiler; package com.bartek.esa.decompiler.decompiler;
import com.bartek.esa.decompiler.archetype.Decompiler;
import com.bartek.esa.decompiler.process.ProcessExecutor; import com.bartek.esa.decompiler.process.ProcessExecutor;
import com.bartek.esa.file.cleaner.FileCleaner; import com.bartek.esa.file.cleaner.FileCleaner;
import com.bartek.esa.file.provider.FileProvider; import com.bartek.esa.file.provider.FileProvider;
@@ -10,9 +11,9 @@ import javax.inject.Inject;
import java.io.File; import java.io.File;
import java.util.Set; import java.util.Set;
public class Decompiler { public class CfrDecompiler implements Decompiler {
public static final String XML_FILES_DIR = "xml"; private static final String XML_FILES_DIR = "xml";
public static final String JAVA_FILES_DIR = "java"; private static final String JAVA_FILES_DIR = "java";
private static final String APK_UNZIPPED_DIR = "apk_unzipped"; private static final String APK_UNZIPPED_DIR = "apk_unzipped";
private static final String JAR_FILES_DIR = "jar"; private static final String JAR_FILES_DIR = "jar";
@@ -23,13 +24,14 @@ public class Decompiler {
private final FileCleaner fileCleaner; private final FileCleaner fileCleaner;
@Inject @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.fileProvider = fileProvider;
this.processExecutor = processExecutor1; this.processExecutor = processExecutor1;
this.zipTool = zipTool; this.zipTool = zipTool;
this.fileCleaner = fileCleaner; this.fileCleaner = fileCleaner;
} }
@Override
public File decompile(File inputApk, boolean debug) { public File decompile(File inputApk, boolean debug) {
File tmp = fileProvider.createTemporaryDirectory(); File tmp = fileProvider.createTemporaryDirectory();
File javaDirectory = new File(tmp, JAVA_FILES_DIR); File javaDirectory = new File(tmp, JAVA_FILES_DIR);
@@ -41,6 +43,21 @@ public class Decompiler {
return tmp; 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) { private void decompileJavaFiles(File inputApk, File tmp, File javaDirectory, boolean debug) {
File unzippedApkDirectory = new File(tmp, APK_UNZIPPED_DIR); File unzippedApkDirectory = new File(tmp, APK_UNZIPPED_DIR);
File jarDirectory = new File(tmp, JAR_FILES_DIR); File jarDirectory = new File(tmp, JAR_FILES_DIR);

View File

@@ -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;
}
}

View File

@@ -1,10 +1,9 @@
package com.bartek.esa.decompiler.di; 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.decompiler.process.ProcessExecutor;
import com.bartek.esa.file.cleaner.FileCleaner;
import com.bartek.esa.file.provider.FileProvider; import com.bartek.esa.file.provider.FileProvider;
import com.bartek.esa.file.zip.ZipTool;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
@@ -17,7 +16,7 @@ public class DecompilerModule {
} }
@Provides @Provides
public Decompiler decompiler(FileProvider fileProvider, ProcessExecutor processExecutor, ZipTool zipTool, FileCleaner fileCleaner) { public Decompiler decompiler(FileProvider fileProvider, ProcessExecutor processExecutor) {
return new Decompiler(fileProvider, processExecutor, zipTool, fileCleaner); return new JadxDecompiler(fileProvider, processExecutor);
} }
} }

View File

@@ -21,44 +21,39 @@ public class ProcessExecutor {
.getOrElseThrow(EsaException::new); .getOrElseThrow(EsaException::new);
printStdOutAndStdErrFromProcess(debug, process); printStdOutAndStdErrFromProcess(debug, process);
waitForProcess(process); waitForProcess(process);
checkExitValue(process, command[0]);
} }
private void printCommandLine(String[] command, boolean debug) { private void printCommandLine(String[] command, boolean debug) {
if(debug) { if (debug) {
System.out.println(String.join(" ", command)); System.out.println(String.join(" ", command));
} }
} }
private void printStdOutAndStdErrFromProcess(boolean debug, Process process) { private void printStdOutAndStdErrFromProcess(boolean debug, Process process) {
if(debug) { BufferedReader stdout = new BufferedReader(new InputStreamReader(process.getInputStream()));
BufferedReader stdout = new BufferedReader(new InputStreamReader(process.getInputStream())); BufferedReader stderr = new BufferedReader(new InputStreamReader(process.getErrorStream()));
BufferedReader stderr = new BufferedReader(new InputStreamReader(process.getErrorStream())); String line;
String line; try {
try { while ((line = stdout.readLine()) != null) {
while ((line = stdout.readLine()) != null) { if (debug) {
System.out.println(line); System.out.println(line);
} }
}
while ((line = stderr.readLine()) != null) { while ((line = stderr.readLine()) != null) {
if (debug) {
System.err.println(line); 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) { private void waitForProcess(Process process) {
Try.run(process::waitFor).getOrElseThrow(EsaException::new); 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...");
}
}
} }