From aceefd42bcfb9b7720c2b3ed5d91ed9e73263ba4 Mon Sep 17 00:00:00 2001 From: Prateek <129204458+prateek-who@users.noreply.github.com> Date: Mon, 9 Feb 2026 19:17:13 +0530 Subject: [PATCH 1/4] --riplibs update User can remove libs if they do not need it --- .../app/morphe/cli/command/PatchCommand.kt | 19 +++++ .../morphe/cli/command/model/PatchingStep.kt | 1 + .../app/morphe/gui/util/ApkLibraryStripper.kt | 74 +++++++++++++++++++ 3 files changed, 94 insertions(+) create mode 100644 src/main/kotlin/app/morphe/gui/util/ApkLibraryStripper.kt diff --git a/src/main/kotlin/app/morphe/cli/command/PatchCommand.kt b/src/main/kotlin/app/morphe/cli/command/PatchCommand.kt index 52155b9..4f0e6a4 100644 --- a/src/main/kotlin/app/morphe/cli/command/PatchCommand.kt +++ b/src/main/kotlin/app/morphe/cli/command/PatchCommand.kt @@ -6,6 +6,7 @@ import app.morphe.cli.command.model.PatchingStep import app.morphe.cli.command.model.PatchingStepResult import app.morphe.cli.command.model.addStepResult import app.morphe.cli.command.model.toSerializablePatch +import app.morphe.gui.util.ApkLibraryStripper import app.morphe.library.ApkUtils import app.morphe.library.ApkUtils.applyTo import app.morphe.library.installation.installer.* @@ -257,6 +258,13 @@ internal object PatchCommand : Runnable { ) private var unsigned: Boolean = false + @CommandLine.Option( + names = ["--riplibs"], + description = ["Architectures to keep, comma-separated (e.g. arm64-v8a, x86). Strips all other native libs."], + split = ",", + ) + private var riplibs: List = emptyList() + override fun run() { // region Setup @@ -408,6 +416,17 @@ internal object PatchCommand : Runnable { patcherResult.applyTo(this) } ) + }.also { rebuiltApk -> + if (riplibs.isNotEmpty()) { + patchingResult.addStepResult( + PatchingStep.STRIPPING_LIBS, + { + ApkLibraryStripper.stripLibraries(rebuiltApk, riplibs) { msg -> + logger.info(msg) + } + } + ) + } }.let { patchedApkFile -> if (!mount && !unsigned) { patchingResult.addStepResult( diff --git a/src/main/kotlin/app/morphe/cli/command/model/PatchingStep.kt b/src/main/kotlin/app/morphe/cli/command/model/PatchingStep.kt index 8f521da..cef96eb 100644 --- a/src/main/kotlin/app/morphe/cli/command/model/PatchingStep.kt +++ b/src/main/kotlin/app/morphe/cli/command/model/PatchingStep.kt @@ -3,6 +3,7 @@ package app.morphe.cli.command.model enum class PatchingStep { PATCHING, REBUILDING, + STRIPPING_LIBS, SIGNING, INSTALLING } \ No newline at end of file diff --git a/src/main/kotlin/app/morphe/gui/util/ApkLibraryStripper.kt b/src/main/kotlin/app/morphe/gui/util/ApkLibraryStripper.kt new file mode 100644 index 0000000..4380ab5 --- /dev/null +++ b/src/main/kotlin/app/morphe/gui/util/ApkLibraryStripper.kt @@ -0,0 +1,74 @@ +package app.morphe.gui.util + +import java.io.File +import java.util.zip.ZipFile +import java.util.zip.ZipEntry +import java.util.zip.ZipOutputStream + +/** + * Strips native libraries from an APK, keeping only specified architectures. + */ +object ApkLibraryStripper { + + /** + * Strips native libraries from an APK file, keeping only the specified architectures. + * + * @param apkFile The APK file to strip libraries from (modified in-place). + * @param architecturesToKeep List of architectures to keep (e.g., ["arm64-v8a"]). + * @param onProgress Optional callback for progress updates. + */ + fun stripLibraries(apkFile: File, architecturesToKeep: List, onProgress: (String) -> Unit = {}) { + val keepSet = architecturesToKeep.toSet() + val tempFile = File(apkFile.parentFile, "${apkFile.name}.tmp") + + var strippedCount = 0 + + ZipFile(apkFile).use { zip -> + ZipOutputStream(tempFile.outputStream().buffered()).use { zos -> + val entries = zip.entries() + while (entries.hasMoreElements()) { + val entry = entries.nextElement() + + if (shouldStripEntry(entry.name, keepSet)) { + strippedCount++ + continue + } + + val newEntry = ZipEntry(entry.name).apply { + if (entry.method == ZipEntry.STORED) { + method = ZipEntry.STORED + size = entry.size + compressedSize = entry.compressedSize + crc = entry.crc + } + entry.extra?.let { extra = it } + } + + zos.putNextEntry(newEntry) + zip.getInputStream(entry).use { it.copyTo(zos) } + zos.closeEntry() + } + } + } + + onProgress("Stripped $strippedCount native library files") + + // Replace original with stripped version + apkFile.delete() + tempFile.renameTo(apkFile) + } + + /** + * Returns true if the ZIP entry should be stripped (is a native lib for an architecture not in the keep set). + */ + private fun shouldStripEntry(entryName: String, keepSet: Set): Boolean { + if (!entryName.startsWith("lib/")) return false + + // Entry format: lib//libname.so + val parts = entryName.removePrefix("lib/").split("/", limit = 2) + if (parts.size < 2) return false + + val arch = parts[0] + return arch !in keepSet + } +} From b15006fcba02d95e3ee61a5aaa2c1b689f3dd494 Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Mon, 9 Feb 2026 16:41:16 +0100 Subject: [PATCH 2/4] Show warning if keeping unrecognized architecture. Print what architectures were kept. --- .../app/morphe/gui/util/ApkLibraryStripper.kt | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/app/morphe/gui/util/ApkLibraryStripper.kt b/src/main/kotlin/app/morphe/gui/util/ApkLibraryStripper.kt index 4380ab5..c2a7d11 100644 --- a/src/main/kotlin/app/morphe/gui/util/ApkLibraryStripper.kt +++ b/src/main/kotlin/app/morphe/gui/util/ApkLibraryStripper.kt @@ -1,6 +1,7 @@ package app.morphe.gui.util import java.io.File +import java.util.logging.Logger import java.util.zip.ZipFile import java.util.zip.ZipEntry import java.util.zip.ZipOutputStream @@ -10,6 +11,31 @@ import java.util.zip.ZipOutputStream */ object ApkLibraryStripper { + private val VALID_ARCHITECTURES = setOf( + "armeabi-v7a", + "arm64-v8a", + "x86", + "x86_64", + // Old obsolete architectures. Only found in Android 6.0 and earlier. + "armeabi", + "mips", + "mips64", + ) + + /** + * Validates that all requested architectures are known. + * Throws IllegalArgumentException if any are invalid. + */ + private fun validateArchitectures(architectures: List) { + val invalid = architectures.filter { it !in VALID_ARCHITECTURES } + if (invalid.isNotEmpty()) { + Logger.getLogger(this::class.java.name).warning( + "Ignoring unrecognized keep library: '$invalid'. " + + "Valid riplibs architectures are: $VALID_ARCHITECTURES" + ) + } + } + /** * Strips native libraries from an APK file, keeping only the specified architectures. * @@ -18,6 +44,8 @@ object ApkLibraryStripper { * @param onProgress Optional callback for progress updates. */ fun stripLibraries(apkFile: File, architecturesToKeep: List, onProgress: (String) -> Unit = {}) { + validateArchitectures(architecturesToKeep) + val keepSet = architecturesToKeep.toSet() val tempFile = File(apkFile.parentFile, "${apkFile.name}.tmp") @@ -51,7 +79,7 @@ object ApkLibraryStripper { } } - onProgress("Stripped $strippedCount native library files") + onProgress("Kept $architecturesToKeep, stripped $strippedCount native library files") // Replace original with stripped version apkFile.delete() From 2afe290617cde5a20ea385eb136d693f2e065b2c Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Mon, 9 Feb 2026 16:48:09 +0100 Subject: [PATCH 3/4] use 'striplibs' to match code --- .../kotlin/app/morphe/cli/command/PatchCommand.kt | 11 +++++------ .../kotlin/app/morphe/gui/util/ApkLibraryStripper.kt | 4 ++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/app/morphe/cli/command/PatchCommand.kt b/src/main/kotlin/app/morphe/cli/command/PatchCommand.kt index 4f0e6a4..bf88cd4 100644 --- a/src/main/kotlin/app/morphe/cli/command/PatchCommand.kt +++ b/src/main/kotlin/app/morphe/cli/command/PatchCommand.kt @@ -3,7 +3,6 @@ package app.morphe.cli.command import app.morphe.cli.command.model.FailedPatch import app.morphe.cli.command.model.PatchingResult import app.morphe.cli.command.model.PatchingStep -import app.morphe.cli.command.model.PatchingStepResult import app.morphe.cli.command.model.addStepResult import app.morphe.cli.command.model.toSerializablePatch import app.morphe.gui.util.ApkLibraryStripper @@ -259,11 +258,11 @@ internal object PatchCommand : Runnable { private var unsigned: Boolean = false @CommandLine.Option( - names = ["--riplibs"], - description = ["Architectures to keep, comma-separated (e.g. arm64-v8a, x86). Strips all other native libs."], + names = ["--striplibs"], + description = ["Architectures to keep, comma-separated (e.g. arm64-v8a,x86). Strips all other native architectures."], split = ",", ) - private var riplibs: List = emptyList() + private var striplibs: List = emptyList() override fun run() { // region Setup @@ -417,11 +416,11 @@ internal object PatchCommand : Runnable { } ) }.also { rebuiltApk -> - if (riplibs.isNotEmpty()) { + if (striplibs.isNotEmpty()) { patchingResult.addStepResult( PatchingStep.STRIPPING_LIBS, { - ApkLibraryStripper.stripLibraries(rebuiltApk, riplibs) { msg -> + ApkLibraryStripper.stripLibraries(rebuiltApk, striplibs) { msg -> logger.info(msg) } } diff --git a/src/main/kotlin/app/morphe/gui/util/ApkLibraryStripper.kt b/src/main/kotlin/app/morphe/gui/util/ApkLibraryStripper.kt index c2a7d11..67482ee 100644 --- a/src/main/kotlin/app/morphe/gui/util/ApkLibraryStripper.kt +++ b/src/main/kotlin/app/morphe/gui/util/ApkLibraryStripper.kt @@ -30,8 +30,8 @@ object ApkLibraryStripper { val invalid = architectures.filter { it !in VALID_ARCHITECTURES } if (invalid.isNotEmpty()) { Logger.getLogger(this::class.java.name).warning( - "Ignoring unrecognized keep library: '$invalid'. " + - "Valid riplibs architectures are: $VALID_ARCHITECTURES" + "Ignoring unrecognized architecture: '$invalid'. " + + "Valid architectures are: $VALID_ARCHITECTURES" ) } } From 8947ac55c930201db5ce3f52f9659270f415283c Mon Sep 17 00:00:00 2001 From: LisoUseInAIKyrios <118716522+LisoUseInAIKyrios@users.noreply.github.com> Date: Mon, 9 Feb 2026 16:58:59 +0100 Subject: [PATCH 4/4] add more validation of architectures --- .../kotlin/app/morphe/gui/util/ApkLibraryStripper.kt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/app/morphe/gui/util/ApkLibraryStripper.kt b/src/main/kotlin/app/morphe/gui/util/ApkLibraryStripper.kt index 67482ee..53dfbf4 100644 --- a/src/main/kotlin/app/morphe/gui/util/ApkLibraryStripper.kt +++ b/src/main/kotlin/app/morphe/gui/util/ApkLibraryStripper.kt @@ -27,10 +27,17 @@ object ApkLibraryStripper { * Throws IllegalArgumentException if any are invalid. */ private fun validateArchitectures(architectures: List) { + // Error on no recognizable architectures. + require(architectures.isNotEmpty() && architectures.any { it in VALID_ARCHITECTURES }) { + "No valid architectures specified with --striplibs: $architectures " + + "Valid architectures are: $VALID_ARCHITECTURES" + } + + // Warn on unrecognizable. val invalid = architectures.filter { it !in VALID_ARCHITECTURES } if (invalid.isNotEmpty()) { Logger.getLogger(this::class.java.name).warning( - "Ignoring unrecognized architecture: '$invalid'. " + + "Ignoring unrecognized --striplibs architecture: '$invalid' " + "Valid architectures are: $VALID_ARCHITECTURES" ) }