Drop v1 signature & UI improvement
This commit is contained in:
parent
b1691a95e2
commit
2e2c818de6
|
|
@ -25,8 +25,6 @@ object Patcher {
|
||||||
add("-o"); add(lspApp.tmpApkDir.absolutePath)
|
add("-o"); add(lspApp.tmpApkDir.absolutePath)
|
||||||
if (config.debuggable) add("-d")
|
if (config.debuggable) add("-d")
|
||||||
add("-l"); add(config.sigBypassLevel.toString())
|
add("-l"); add(config.sigBypassLevel.toString())
|
||||||
add("--v1"); add(config.v1.toString())
|
|
||||||
add("--v2"); add(config.v2.toString())
|
|
||||||
if (config.useManager) add("--manager")
|
if (config.useManager) add("--manager")
|
||||||
if (config.overrideVersionCode) add("-r")
|
if (config.overrideVersionCode) add("-r")
|
||||||
if (Configs.detailPatchLogs) add("-v")
|
if (Configs.detailPatchLogs) add("-v")
|
||||||
|
|
|
||||||
|
|
@ -176,7 +176,6 @@ private fun sigBypassLvStr(level: Int) = when (level) {
|
||||||
else -> throw IllegalArgumentException("Invalid sigBypassLv: $level")
|
else -> throw IllegalArgumentException("Invalid sigBypassLv: $level")
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun PatchOptionsBody(modifier: Modifier, onAddEmbed: () -> Unit) {
|
private fun PatchOptionsBody(modifier: Modifier, onAddEmbed: () -> Unit) {
|
||||||
val viewModel = viewModel<NewPatchViewModel>()
|
val viewModel = viewModel<NewPatchViewModel>()
|
||||||
|
|
@ -236,34 +235,6 @@ private fun PatchOptionsBody(modifier: Modifier, onAddEmbed: () -> Unit) {
|
||||||
title = stringResource(R.string.patch_override_version_code),
|
title = stringResource(R.string.patch_override_version_code),
|
||||||
desc = stringResource(R.string.patch_override_version_code_desc)
|
desc = stringResource(R.string.patch_override_version_code_desc)
|
||||||
)
|
)
|
||||||
var signExpanded by remember { mutableStateOf(false) }
|
|
||||||
AnywhereDropdown(
|
|
||||||
expanded = signExpanded,
|
|
||||||
onDismissRequest = { signExpanded = false },
|
|
||||||
onClick = { signExpanded = true },
|
|
||||||
surface = {
|
|
||||||
SettingsItem(
|
|
||||||
icon = Icons.Outlined.Edit,
|
|
||||||
title = stringResource(R.string.patch_sign),
|
|
||||||
desc = viewModel.sign
|
|
||||||
.mapIndexedNotNull { index, on -> if (on) "V" + (index + 1) else null }
|
|
||||||
.joinToString(" + ")
|
|
||||||
.ifEmpty { "None" }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
repeat(2) { index ->
|
|
||||||
DropdownMenuItem(
|
|
||||||
text = {
|
|
||||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
|
||||||
Checkbox(checked = viewModel.sign[index], onCheckedChange = { viewModel.sign[index] = !viewModel.sign[index] })
|
|
||||||
Text("V" + (index + 1))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onClick = { viewModel.sign[index] = !viewModel.sign[index] }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var bypassExpanded by remember { mutableStateOf(false) }
|
var bypassExpanded by remember { mutableStateOf(false) }
|
||||||
AnywhereDropdown(
|
AnywhereDropdown(
|
||||||
expanded = bypassExpanded,
|
expanded = bypassExpanded,
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,6 @@ class NewPatchViewModel : ViewModel() {
|
||||||
var useManager by mutableStateOf(true)
|
var useManager by mutableStateOf(true)
|
||||||
var debuggable by mutableStateOf(false)
|
var debuggable by mutableStateOf(false)
|
||||||
var overrideVersionCode by mutableStateOf(false)
|
var overrideVersionCode by mutableStateOf(false)
|
||||||
val sign = mutableStateListOf(false, true)
|
|
||||||
var sigBypassLevel by mutableStateOf(2)
|
var sigBypassLevel by mutableStateOf(2)
|
||||||
var embeddedModules = emptyList<AppInfo>()
|
var embeddedModules = emptyList<AppInfo>()
|
||||||
|
|
||||||
|
|
@ -89,7 +88,7 @@ class NewPatchViewModel : ViewModel() {
|
||||||
Log.d(TAG, "Submit patch")
|
Log.d(TAG, "Submit patch")
|
||||||
if (useManager) embeddedModules = emptyList()
|
if (useManager) embeddedModules = emptyList()
|
||||||
patchOptions = Patcher.Options(
|
patchOptions = Patcher.Options(
|
||||||
config = PatchConfig(useManager, debuggable, overrideVersionCode, sign[0], sign[1], sigBypassLevel, null, null),
|
config = PatchConfig(useManager, debuggable, overrideVersionCode, sigBypassLevel, null, null),
|
||||||
apkPaths = listOf(patchApp.app.sourceDir) + (patchApp.app.splitSourceDirs ?: emptyArray()),
|
apkPaths = listOf(patchApp.app.sourceDir) + (patchApp.app.splitSourceDirs ?: emptyArray()),
|
||||||
embeddedModules = embeddedModules.flatMap { listOf(it.app.sourceDir) + (it.app.splitSourceDirs ?: emptyArray()) }
|
embeddedModules = embeddedModules.flatMap { listOf(it.app.sourceDir) + (it.app.splitSourceDirs ?: emptyArray()) }
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -21,12 +21,10 @@ import org.lsposed.lspatch.Constants.PATCH_FILE_SUFFIX
|
||||||
import org.lsposed.lspatch.config.ConfigManager
|
import org.lsposed.lspatch.config.ConfigManager
|
||||||
import org.lsposed.lspatch.config.Configs
|
import org.lsposed.lspatch.config.Configs
|
||||||
import org.lsposed.lspatch.lspApp
|
import org.lsposed.lspatch.lspApp
|
||||||
import org.lsposed.patch.util.ManifestParser
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.text.Collator
|
import java.text.Collator
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.zip.ZipFile
|
|
||||||
import kotlin.coroutines.resume
|
import kotlin.coroutines.resume
|
||||||
import kotlin.coroutines.suspendCoroutine
|
import kotlin.coroutines.suspendCoroutine
|
||||||
|
|
||||||
|
|
@ -150,9 +148,8 @@ object LSPPackageManager {
|
||||||
suspend fun getAppInfoFromApks(apks: List<Uri>): Result<AppInfo> {
|
suspend fun getAppInfoFromApks(apks: List<Uri>): Result<AppInfo> {
|
||||||
return withContext(Dispatchers.IO) {
|
return withContext(Dispatchers.IO) {
|
||||||
runCatching {
|
runCatching {
|
||||||
val app = ApplicationInfo()
|
var primary: ApplicationInfo? = null
|
||||||
if (apks.size > 1) app.splitSourceDirs = Array<String?>(apks.size - 1) { null }
|
val splits = apks.mapNotNull { uri ->
|
||||||
apks.forEachIndexed { index, uri ->
|
|
||||||
val src = DocumentFile.fromSingleUri(lspApp, uri)
|
val src = DocumentFile.fromSingleUri(lspApp, uri)
|
||||||
?: throw IOException("DocumentFile is null")
|
?: throw IOException("DocumentFile is null")
|
||||||
val dst = lspApp.tmpApkDir.resolve(src.name!!)
|
val dst = lspApp.tmpApkDir.resolve(src.name!!)
|
||||||
|
|
@ -163,21 +160,18 @@ object LSPPackageManager {
|
||||||
input.copyTo(output)
|
input.copyTo(output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ZipFile(dst).use { zip ->
|
if (primary == null) {
|
||||||
val entry = zip.getEntry("AndroidManifest.xml")
|
primary = lspApp.packageManager.getPackageArchiveInfo(dst.absolutePath, 0)?.applicationInfo
|
||||||
?: throw IOException("AndroidManifest.xml is not found")
|
if (primary != null) return@mapNotNull null
|
||||||
zip.getInputStream(entry).use {
|
|
||||||
val info = ManifestParser.parseManifestFile(it)
|
|
||||||
if (app.packageName != null && app.packageName != info.packageName) {
|
|
||||||
throw IOException("Selected apks are not of the same app")
|
|
||||||
}
|
|
||||||
app.packageName = info.packageName
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (index == 0) app.sourceDir = dst.absolutePath
|
dst.absolutePath
|
||||||
else app.splitSourceDirs[index - 1] = dst.absolutePath
|
|
||||||
}
|
}
|
||||||
AppInfo(app, app.packageName)
|
|
||||||
|
// TODO: Check selected apks are from the same app
|
||||||
|
if (primary == null) throw IllegalArgumentException("No primary apk")
|
||||||
|
val label = lspApp.packageManager.getApplicationLabel(primary!!).toString()
|
||||||
|
if (splits.isNotEmpty()) primary!!.splitSourceDirs = splits.toTypedArray()
|
||||||
|
AppInfo(primary!!, label)
|
||||||
}.recoverCatching { t ->
|
}.recoverCatching { t ->
|
||||||
cleanTmpApkDir()
|
cleanTmpApkDir()
|
||||||
Log.e(TAG, "Failed to load apks", t)
|
Log.e(TAG, "Failed to load apks", t)
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,6 @@
|
||||||
<string name="patch_portable_desc">Patch an app with modules embedded.\nThe patched app can run without the manager, but cannot be managed dynamically.\nPortable patched apps can be used on devices that do not have LSPatch Manager installed.</string>
|
<string name="patch_portable_desc">Patch an app with modules embedded.\nThe patched app can run without the manager, but cannot be managed dynamically.\nPortable patched apps can be used on devices that do not have LSPatch Manager installed.</string>
|
||||||
<string name="patch_embed_modules">Embed modules</string>
|
<string name="patch_embed_modules">Embed modules</string>
|
||||||
<string name="patch_debuggable">Debuggable</string>
|
<string name="patch_debuggable">Debuggable</string>
|
||||||
<string name="patch_sign">Sign</string>
|
|
||||||
<string name="patch_sigbypass">Signature bypass</string>
|
<string name="patch_sigbypass">Signature bypass</string>
|
||||||
<string name="patch_sigbypasslv0">lv0: Off</string>
|
<string name="patch_sigbypasslv0">lv0: Off</string>
|
||||||
<string name="patch_sigbypasslv1">lv1: Bypass PM</string>
|
<string name="patch_sigbypasslv1">lv1: Bypass PM</string>
|
||||||
|
|
|
||||||
|
|
@ -79,12 +79,6 @@ public class LSPatch {
|
||||||
@Parameter(names = {"-k", "--keystore"}, arity = 4, description = "Set custom signature keystore. Followed by 4 arguments: keystore path, keystore password, keystore alias, keystore alias password")
|
@Parameter(names = {"-k", "--keystore"}, arity = 4, description = "Set custom signature keystore. Followed by 4 arguments: keystore path, keystore password, keystore alias, keystore alias password")
|
||||||
private List<String> keystoreArgs = Arrays.asList(null, "123456", "key0", "123456");
|
private List<String> keystoreArgs = Arrays.asList(null, "123456", "key0", "123456");
|
||||||
|
|
||||||
@Parameter(names = {"--v1"}, arity = 1, description = "Sign with v1 signature")
|
|
||||||
private boolean v1 = false;
|
|
||||||
|
|
||||||
@Parameter(names = {"--v2"}, arity = 1, description = "Sign with v2 signature")
|
|
||||||
private boolean v2 = true;
|
|
||||||
|
|
||||||
@Parameter(names = {"--manager"}, description = "Use manager (Cannot work with embedding modules)")
|
@Parameter(names = {"--manager"}, description = "Use manager (Cannot work with embedding modules)")
|
||||||
private boolean useManager = false;
|
private boolean useManager = false;
|
||||||
|
|
||||||
|
|
@ -207,8 +201,7 @@ public class LSPatch {
|
||||||
var entry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(keystoreArgs.get(2), new KeyStore.PasswordProtection(keystoreArgs.get(3).toCharArray()));
|
var entry = (KeyStore.PrivateKeyEntry) keyStore.getEntry(keystoreArgs.get(2), new KeyStore.PasswordProtection(keystoreArgs.get(3).toCharArray()));
|
||||||
new SigningExtension(SigningOptions.builder()
|
new SigningExtension(SigningOptions.builder()
|
||||||
.setMinSdkVersion(28)
|
.setMinSdkVersion(28)
|
||||||
.setV1SigningEnabled(v1)
|
.setV2SigningEnabled(true)
|
||||||
.setV2SigningEnabled(v2)
|
|
||||||
.setCertificates((X509Certificate[]) entry.getCertificateChain())
|
.setCertificates((X509Certificate[]) entry.getCertificateChain())
|
||||||
.setKey(entry.getPrivateKey())
|
.setKey(entry.getPrivateKey())
|
||||||
.build()).register(dstZFile);
|
.build()).register(dstZFile);
|
||||||
|
|
@ -239,7 +232,7 @@ public class LSPatch {
|
||||||
|
|
||||||
logger.i("Patching apk...");
|
logger.i("Patching apk...");
|
||||||
// modify manifest
|
// modify manifest
|
||||||
final var config = new PatchConfig(useManager, debuggableFlag, overrideVersionCode, v1, v2, sigbypassLevel, originalSignature, appComponentFactory);
|
final var config = new PatchConfig(useManager, debuggableFlag, overrideVersionCode, sigbypassLevel, originalSignature, appComponentFactory);
|
||||||
final var configBytes = new Gson().toJson(config).getBytes(StandardCharsets.UTF_8);
|
final var configBytes = new Gson().toJson(config).getBytes(StandardCharsets.UTF_8);
|
||||||
final var metadata = Base64.getEncoder().encodeToString(configBytes);
|
final var metadata = Base64.getEncoder().encodeToString(configBytes);
|
||||||
try (var is = new ByteArrayInputStream(modifyManifestFile(manifestEntry.open(), metadata))) {
|
try (var is = new ByteArrayInputStream(modifyManifestFile(manifestEntry.open(), metadata))) {
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,6 @@ public class PatchConfig {
|
||||||
public final boolean useManager;
|
public final boolean useManager;
|
||||||
public final boolean debuggable;
|
public final boolean debuggable;
|
||||||
public final boolean overrideVersionCode;
|
public final boolean overrideVersionCode;
|
||||||
public final boolean v1;
|
|
||||||
public final boolean v2;
|
|
||||||
public final int sigBypassLevel;
|
public final int sigBypassLevel;
|
||||||
public final String originalSignature;
|
public final String originalSignature;
|
||||||
public final String appComponentFactory;
|
public final String appComponentFactory;
|
||||||
|
|
@ -16,8 +14,6 @@ public class PatchConfig {
|
||||||
boolean useManager,
|
boolean useManager,
|
||||||
boolean debuggable,
|
boolean debuggable,
|
||||||
boolean overrideVersionCode,
|
boolean overrideVersionCode,
|
||||||
boolean v1,
|
|
||||||
boolean v2,
|
|
||||||
int sigBypassLevel,
|
int sigBypassLevel,
|
||||||
String originalSignature,
|
String originalSignature,
|
||||||
String appComponentFactory
|
String appComponentFactory
|
||||||
|
|
@ -25,8 +21,6 @@ public class PatchConfig {
|
||||||
this.useManager = useManager;
|
this.useManager = useManager;
|
||||||
this.debuggable = debuggable;
|
this.debuggable = debuggable;
|
||||||
this.overrideVersionCode = overrideVersionCode;
|
this.overrideVersionCode = overrideVersionCode;
|
||||||
this.v1 = v1;
|
|
||||||
this.v2 = v2;
|
|
||||||
this.sigBypassLevel = sigBypassLevel;
|
this.sigBypassLevel = sigBypassLevel;
|
||||||
this.originalSignature = originalSignature;
|
this.originalSignature = originalSignature;
|
||||||
this.appComponentFactory = appComponentFactory;
|
this.appComponentFactory = appComponentFactory;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue