feat: add APKM support (PR #2379)
* feat: Add APKM support * fix: Removed unused imports With spotlessApply :P
This commit is contained in:
@@ -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")
|
||||
}
|
||||
+38
@@ -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
|
||||
}
|
||||
}
|
||||
+36
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
)
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
jadx.plugins.input.apkm.ApkmInputPlugin
|
||||
Reference in New Issue
Block a user