Skip to content

Commit

Permalink
feat: add APKM support (PR #2379)
Browse files Browse the repository at this point in the history
* feat: Add APKM support

* fix: Removed unused imports

With spotlessApply :P
  • Loading branch information
iscle authored Dec 22, 2024
1 parent a46e35c commit 7a2dad8
Show file tree
Hide file tree
Showing 12 changed files with 151 additions and 3 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ and also packed to `build/jadx-<version>.zip`

### Usage
```
jadx[-gui] [command] [options] <input files> (.apk, .dex, .jar, .class, .smali, .zip, .aar, .arsc, .aab, .xapk, .jadx.kts)
jadx[-gui] [command] [options] <input files> (.apk, .dex, .jar, .class, .smali, .zip, .aar, .arsc, .aab, .xapk, .apkm, .jadx.kts)
commands (use '<command> --help' for command options):
plugins - manage jadx plugins
Expand Down
1 change: 1 addition & 0 deletions jadx-cli/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ dependencies {
runtimeOnly(project(":jadx-plugins:jadx-script:jadx-script-plugin"))
runtimeOnly(project(":jadx-plugins:jadx-xapk-input"))
runtimeOnly(project(":jadx-plugins:jadx-aab-input"))
runtimeOnly(project(":jadx-plugins:jadx-apkm-input"))

implementation("org.jcommander:jcommander:2.0")
implementation("ch.qos.logback:logback-classic:1.5.13")
Expand Down
2 changes: 1 addition & 1 deletion jadx-cli/src/main/java/jadx/cli/JadxCLIArgs.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

public class JadxCLIArgs {

@Parameter(description = "<input files> (.apk, .dex, .jar, .class, .smali, .zip, .aar, .arsc, .aab, .xapk, .jadx.kts)")
@Parameter(description = "<input files> (.apk, .dex, .jar, .class, .smali, .zip, .aar, .arsc, .aab, .xapk, .apkm, .jadx.kts)")
protected List<String> files = new ArrayList<>(1);

@Parameter(names = { "-d", "--output-dir" }, description = "output directory")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
public class FileDialogWrapper {

private static final List<String> OPEN_FILES_EXTS = Arrays.asList(
"apk", "dex", "jar", "class", "smali", "zip", "aar", "arsc", "jadx.kts", "xapk");
"apk", "dex", "jar", "class", "smali", "zip", "aar", "arsc", "jadx.kts", "xapk", "apkm");

private final MainWindow mainWindow;

Expand Down
11 changes: 11 additions & 0 deletions jadx-plugins/jadx-apkm-input/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
plugins {
id("jadx-library")
id("jadx-kotlin")
}

dependencies {
api(project(":jadx-core"))

implementation(project(":jadx-plugins:jadx-dex-input"))
implementation("com.google.code.gson:gson:2.11.0")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package jadx.plugins.input.apkm

import jadx.api.plugins.input.ICodeLoader
import jadx.api.plugins.input.JadxCodeInput
import jadx.api.plugins.utils.CommonFileUtils
import jadx.api.plugins.utils.ZipSecurity
import java.io.File
import java.nio.file.Path

class ApkmCustomCodeInput(
private val plugin: ApkmInputPlugin,
) : JadxCodeInput {
override fun loadFiles(input: List<Path>): ICodeLoader {
val apkFiles = mutableListOf<File>()
for (file in input.map { it.toFile() }) {
// Check if this is a valid APKM file
val manifest = ApkmUtils.getManifest(file) ?: continue
if (!ApkmUtils.isSupported(manifest)) continue

// Load all files ending with .apk
ZipSecurity.visitZipEntries<Any>(file) { zip, entry ->
if (entry.name.endsWith(".apk")) {
val tmpFile = ZipSecurity.getInputStreamForEntry(zip, entry).use {
CommonFileUtils.saveToTempFile(it, ".apk").toFile()
}
apkFiles.add(tmpFile)
}
null
}
}

val codeLoader = plugin.dexInputPlugin.loadFiles(apkFiles.map { it.toPath() })

apkFiles.forEach { CommonFileUtils.safeDeleteFile(it) }

return codeLoader
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package jadx.plugins.input.apkm

import jadx.api.ResourceFile
import jadx.api.ResourcesLoader
import jadx.api.plugins.CustomResourcesLoader
import jadx.api.plugins.utils.CommonFileUtils
import jadx.api.plugins.utils.ZipSecurity
import java.io.File

class ApkmCustomResourcesLoader : CustomResourcesLoader {
private val tmpFiles = mutableListOf<File>()

override fun load(loader: ResourcesLoader, list: MutableList<ResourceFile>, file: File): Boolean {
// Check if this is a valid APKM file
val manifest = ApkmUtils.getManifest(file) ?: return false
if (!ApkmUtils.isSupported(manifest)) return false

// Load all files ending with .apk
ZipSecurity.visitZipEntries<Any>(file) { zip, entry ->
if (entry.name.endsWith(".apk")) {
val tmpFile = ZipSecurity.getInputStreamForEntry(zip, entry).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()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package jadx.plugins.input.apkm

import jadx.api.plugins.JadxPlugin
import jadx.api.plugins.JadxPluginContext
import jadx.api.plugins.JadxPluginInfo
import jadx.plugins.input.dex.DexInputPlugin

class ApkmInputPlugin : JadxPlugin {
private val codeInput = ApkmCustomCodeInput(this)
private val resourcesLoader = ApkmCustomResourcesLoader()
internal lateinit var dexInputPlugin: DexInputPlugin

override fun getPluginInfo() = JadxPluginInfo(
"apkm-input",
"APKM Input",
"Load .apkm files",
)

override fun init(context: JadxPluginContext) {
dexInputPlugin = context.plugins().getInstance(DexInputPlugin::class.java)
context.addCodeInput(codeInput)
context.decompiler.addCustomResourcesLoader(resourcesLoader)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package jadx.plugins.input.apkm

import com.google.gson.annotations.SerializedName

data class ApkmManifest(
@SerializedName("apkm_version")
var apkmVersion: Int = -1,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package jadx.plugins.input.apkm

import jadx.api.plugins.utils.ZipSecurity
import jadx.core.utils.GsonUtils.buildGson
import jadx.core.utils.files.FileUtils
import jadx.core.utils.files.ZipFile
import java.io.File
import java.io.InputStreamReader

object ApkmUtils {
fun getManifest(file: File): ApkmManifest? {
if (!FileUtils.isZipFile(file)) return null
try {
ZipFile(file).use { zip ->
val manifestEntry = zip.getEntry("info.json") ?: return null
return InputStreamReader(ZipSecurity.getInputStreamForEntry(zip, manifestEntry)).use {
buildGson().fromJson(it, ApkmManifest::class.java)
}
}
} catch (e: Exception) {
return null
}
}

fun isSupported(manifest: ApkmManifest): Boolean {
return manifest.apkmVersion != -1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
jadx.plugins.input.apkm.ApkmInputPlugin
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ include("jadx-plugins:jadx-rename-mappings")
include("jadx-plugins:jadx-kotlin-metadata")
include("jadx-plugins:jadx-xapk-input")
include("jadx-plugins:jadx-aab-input")
include("jadx-plugins:jadx-apkm-input")

include("jadx-plugins:jadx-script:jadx-script-plugin")
include("jadx-plugins:jadx-script:jadx-script-runtime")
Expand Down

0 comments on commit 7a2dad8

Please sign in to comment.