diff --git a/jadx-cli/build.gradle.kts b/jadx-cli/build.gradle.kts index a3f097276..0d9c579cb 100644 --- a/jadx-cli/build.gradle.kts +++ b/jadx-cli/build.gradle.kts @@ -23,6 +23,7 @@ dependencies { runtimeOnly(project(":jadx-plugins:jadx-xapk-input")) runtimeOnly(project(":jadx-plugins:jadx-aab-input")) runtimeOnly(project(":jadx-plugins:jadx-apkm-input")) + runtimeOnly(project(":jadx-plugins:jadx-apks-input")) implementation("org.jcommander:jcommander:2.0") implementation("ch.qos.logback:logback-classic:1.5.18") diff --git a/jadx-gui/src/main/java/jadx/gui/ui/filedialog/FileDialogWrapper.java b/jadx-gui/src/main/java/jadx/gui/ui/filedialog/FileDialogWrapper.java index 4a6381fdd..b6d5c69ef 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/filedialog/FileDialogWrapper.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/filedialog/FileDialogWrapper.java @@ -17,7 +17,7 @@ import jadx.gui.utils.NLS; public class FileDialogWrapper { private static final List OPEN_FILES_EXTS = Arrays.asList( - "apk", "dex", "jar", "class", "smali", "zip", "aar", "arsc", "jadx.kts", "xapk", "apkm"); + "apk", "dex", "jar", "class", "smali", "zip", "aar", "arsc", "jadx.kts", "xapk", "apkm", "apks"); private final MainWindow mainWindow; diff --git a/jadx-plugins/jadx-apks-input/build.gradle.kts b/jadx-plugins/jadx-apks-input/build.gradle.kts new file mode 100644 index 000000000..153c81184 --- /dev/null +++ b/jadx-plugins/jadx-apks-input/build.gradle.kts @@ -0,0 +1,10 @@ +plugins { + id("jadx-library") + id("jadx-kotlin") +} + +dependencies { + api(project(":jadx-core")) + + implementation(project(":jadx-plugins:jadx-dex-input")) +} diff --git a/jadx-plugins/jadx-apks-input/src/main/java/jadx/plugins/input/apks/ApksCustomCodeInput.kt b/jadx-plugins/jadx-apks-input/src/main/java/jadx/plugins/input/apks/ApksCustomCodeInput.kt new file mode 100644 index 000000000..0a879602c --- /dev/null +++ b/jadx-plugins/jadx-apks-input/src/main/java/jadx/plugins/input/apks/ApksCustomCodeInput.kt @@ -0,0 +1,38 @@ +package jadx.plugins.input.apks + +import jadx.api.plugins.input.ICodeLoader +import jadx.api.plugins.input.JadxCodeInput +import jadx.api.plugins.utils.CommonFileUtils +import jadx.plugins.input.dex.DexInputPlugin +import jadx.zip.ZipReader +import java.io.File +import java.nio.file.Path + +class ApksCustomCodeInput( + private val dexInputPlugin: DexInputPlugin, + private val zipReader: ZipReader, +) : JadxCodeInput { + override fun loadFiles(input: List): ICodeLoader { + val apkFiles = mutableListOf() + for (file in input.map { it.toFile() }) { + if (!file.name.endsWith(".apks")) continue + + // Load all files ending with .apk + zipReader.visitEntries(file) { entry -> + if (entry.name.endsWith(".apk")) { + val tmpFile = entry.inputStream.use { + CommonFileUtils.saveToTempFile(it, ".apk").toFile() + } + apkFiles.add(tmpFile) + } + null + } + } + + val codeLoader = dexInputPlugin.loadFiles(apkFiles.map { it.toPath() }) + + apkFiles.forEach { CommonFileUtils.safeDeleteFile(it) } + + return codeLoader + } +} diff --git a/jadx-plugins/jadx-apks-input/src/main/java/jadx/plugins/input/apks/ApksCustomResourcesLoader.kt b/jadx-plugins/jadx-apks-input/src/main/java/jadx/plugins/input/apks/ApksCustomResourcesLoader.kt new file mode 100644 index 000000000..ae7d045ba --- /dev/null +++ b/jadx-plugins/jadx-apks-input/src/main/java/jadx/plugins/input/apks/ApksCustomResourcesLoader.kt @@ -0,0 +1,36 @@ +package jadx.plugins.input.apks + +import jadx.api.ResourceFile +import jadx.api.ResourcesLoader +import jadx.api.plugins.CustomResourcesLoader +import jadx.api.plugins.utils.CommonFileUtils +import jadx.zip.ZipReader +import java.io.File + +class ApksCustomResourcesLoader( + private val zipReader: ZipReader, +) : CustomResourcesLoader { + private val tmpFiles = mutableListOf() + + override fun load(loader: ResourcesLoader, list: MutableList, file: File): Boolean { + if (!file.name.endsWith(".apks")) return false + + // Load all files ending with .apk + zipReader.visitEntries(file) { entry -> + if (entry.name.endsWith(".apk")) { + val tmpFile = entry.inputStream.use { + CommonFileUtils.saveToTempFile(it, ".apk").toFile() + } + loader.defaultLoadFile(list, tmpFile, entry.name + "/") + tmpFiles += tmpFile + } + null + } + return true + } + + override fun close() { + tmpFiles.forEach(CommonFileUtils::safeDeleteFile) + tmpFiles.clear() + } +} diff --git a/jadx-plugins/jadx-apks-input/src/main/java/jadx/plugins/input/apks/ApksInputPlugin.kt b/jadx-plugins/jadx-apks-input/src/main/java/jadx/plugins/input/apks/ApksInputPlugin.kt new file mode 100644 index 000000000..d3bb01d08 --- /dev/null +++ b/jadx-plugins/jadx-apks-input/src/main/java/jadx/plugins/input/apks/ApksInputPlugin.kt @@ -0,0 +1,20 @@ +package jadx.plugins.input.apks + +import jadx.api.plugins.JadxPlugin +import jadx.api.plugins.JadxPluginContext +import jadx.api.plugins.JadxPluginInfo +import jadx.plugins.input.dex.DexInputPlugin + +class ApksInputPlugin : JadxPlugin { + override fun getPluginInfo() = JadxPluginInfo( + "apks-input", + "APKS Input", + "Load .apks files", + ) + + override fun init(context: JadxPluginContext) { + val dexInputPlugin = context.plugins().getInstance(DexInputPlugin::class.java) + context.addCodeInput(ApksCustomCodeInput(dexInputPlugin, context.zipReader)) + context.decompiler.addCustomResourcesLoader(ApksCustomResourcesLoader(context.zipReader)) + } +} diff --git a/jadx-plugins/jadx-apks-input/src/main/resources/META-INF/services/jadx.api.plugins.JadxPlugin b/jadx-plugins/jadx-apks-input/src/main/resources/META-INF/services/jadx.api.plugins.JadxPlugin new file mode 100644 index 000000000..1297b0fdf --- /dev/null +++ b/jadx-plugins/jadx-apks-input/src/main/resources/META-INF/services/jadx.api.plugins.JadxPlugin @@ -0,0 +1 @@ +jadx.plugins.input.apks.ApksInputPlugin diff --git a/settings.gradle.kts b/settings.gradle.kts index aaec6aae4..26a711fab 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -29,6 +29,7 @@ include("jadx-plugins:jadx-kotlin-source-debug-extension") include("jadx-plugins:jadx-xapk-input") include("jadx-plugins:jadx-aab-input") include("jadx-plugins:jadx-apkm-input") +include("jadx-plugins:jadx-apks-input") include("jadx-plugins:jadx-script:jadx-script-plugin") include("jadx-plugins:jadx-script:jadx-script-runtime")