14: Add JADX decompiler
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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...");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user