From 1152170f10cd80891df79caaff11e67f8d52ed20 Mon Sep 17 00:00:00 2001 From: 5ec1cff Date: Tue, 27 Feb 2024 16:02:30 +0800 Subject: [PATCH 1/4] cleanup code --- .../toolkit/coderstory/CorePatchForQ.java | 14 ++++------- .../toolkit/coderstory/CorePatchForR.java | 24 ++----------------- .../toolkit/coderstory/CorePatchForT.java | 17 ------------- .../toolkit/coderstory/ReturnConstant.java | 4 +++- .../java/toolkit/coderstory/XposedHelper.java | 4 ++-- 5 files changed, 11 insertions(+), 52 deletions(-) diff --git a/app/src/main/java/toolkit/coderstory/CorePatchForQ.java b/app/src/main/java/toolkit/coderstory/CorePatchForQ.java index c0e0007..5a6af4d 100644 --- a/app/src/main/java/toolkit/coderstory/CorePatchForQ.java +++ b/app/src/main/java/toolkit/coderstory/CorePatchForQ.java @@ -28,8 +28,7 @@ public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) Class packageClazz = XposedHelpers.findClass("android.content.pm.PackageParser.Package", loadPackageParam.classLoader); hookAllMethods("com.android.server.pm.PackageManagerService", loadPackageParam.classLoader, "checkDowngrade", new XC_MethodHook() { public void beforeHookedMethod(MethodHookParam methodHookParam) throws Throwable { - super.beforeHookedMethod(methodHookParam); - if (prefs.getBoolean("downgrade", true)) { + if (prefs.getBoolean("downgrade", true)) { Object packageInfoLite = methodHookParam.args[0]; if (prefs.getBoolean("downgrade", true)) { @@ -65,7 +64,6 @@ public void beforeHookedMethod(MethodHookParam methodHookParam) throws Throwable final Object newInstance = findConstructorExact.newInstance(signingDetailsArgs); hookAllMethods("android.util.apk.ApkSignatureVerifier", loadPackageParam.classLoader, "verifyV1Signature", new XC_MethodHook() { public void afterHookedMethod(MethodHookParam methodHookParam) throws Throwable { - super.afterHookedMethod(methodHookParam); if (prefs.getBoolean("authcreak", false)) { Throwable throwable = methodHookParam.getThrowable(); if (throwable != null) { @@ -90,7 +88,6 @@ public void afterHookedMethod(MethodHookParam methodHookParam) throws Throwable hookAllMethods(signingDetails, "checkCapability", new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { - super.beforeHookedMethod(param); if (prefs.getBoolean("digestCreak", true)) { if ((Integer) param.args[1] != 4 && prefs.getBoolean("authcreak", false)) { param.setResult(Boolean.TRUE); @@ -102,8 +99,7 @@ protected void beforeHookedMethod(MethodHookParam param) throws Throwable { new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { - super.beforeHookedMethod(param); - if (prefs.getBoolean("digestCreak", true)) { + if (prefs.getBoolean("digestCreak", true)) { if ((Integer) param.args[1] != 4 && prefs.getBoolean("authcreak", false)) { param.setResult(Boolean.TRUE); } @@ -115,8 +111,7 @@ protected void beforeHookedMethod(MethodHookParam param) throws Throwable { findAndHookMethod("android.content.pm.ApplicationInfo", loadPackageParam.classLoader, "isPackageWhitelistedForHiddenApis", new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { - super.beforeHookedMethod(param); - if (prefs.getBoolean("digestCreak", true)) { + if (prefs.getBoolean("digestCreak", true)) { ApplicationInfo info = (ApplicationInfo) param.thisObject; if ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0 || (info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { @@ -159,8 +154,7 @@ public void initZygote(StartupParam startupParam) { hookAllConstructors("android.util.jar.StrictJarVerifier", new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { - super.beforeHookedMethod(param); - if (prefs.getBoolean("enhancedMode", false)) { + if (prefs.getBoolean("enhancedMode", false)) { param.args[3] = Boolean.FALSE; } } diff --git a/app/src/main/java/toolkit/coderstory/CorePatchForR.java b/app/src/main/java/toolkit/coderstory/CorePatchForR.java index b91033b..a7548f2 100644 --- a/app/src/main/java/toolkit/coderstory/CorePatchForR.java +++ b/app/src/main/java/toolkit/coderstory/CorePatchForR.java @@ -115,23 +115,6 @@ public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) new ReturnConstant(prefs, "authcreak", 0)); } - // Package " + packageName + " signatures do not match previously installed version; ignoring!" - // public boolean checkCapability(String sha256String, @CertCapabilities int flags) { - // public boolean checkCapability(SigningDetails oldDetails, @CertCapabilities int flags) - hookAllMethods("android.content.pm.PackageParser", loadPackageParam.classLoader, "checkCapability", new XC_MethodHook() { - @Override - protected void beforeHookedMethod(MethodHookParam param) { - // Don't handle PERMISSION (grant SIGNATURE permissions to pkgs with this cert) - // Or applications will have all privileged permissions - // https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/content/pm/PackageParser.java;l=5947?q=CertCapabilities - if (prefs.getBoolean("authcreak", false)) { - if ((Integer) param.args[1] != 4) { - param.setResult(true); - } - } - } - }); - // 当verifyV1Signature抛出转换异常时,替换一个签名作为返回值 // 如果用户已安装apk,并且其定义了私有权限,则安装时会因签名与模块内硬编码的不一致而被拒绝。尝试从待安装apk中获取签名。如果其中apk的签名和已安装的一致(只动了内容)就没有问题。此策略可能有潜在的安全隐患。 Class pkc = XposedHelpers.findClass("sun.security.pkcs.PKCS7", loadPackageParam.classLoader); @@ -152,7 +135,6 @@ protected void beforeHookedMethod(MethodHookParam param) { Class parseResult = XposedHelpers.findClassIfExists("android.content.pm.parsing.result.ParseResult", loadPackageParam.classLoader); hookAllMethods("android.util.jar.StrictJarVerifier", loadPackageParam.classLoader, "verifyBytes", new XC_MethodHook() { public void afterHookedMethod(MethodHookParam param) throws Throwable { - super.afterHookedMethod(param); if (prefs.getBoolean("digestCreak", true)) { if (!prefs.getBoolean("UsePreSig", false)) { final Object block = constructor.newInstance(param.args[0]); @@ -262,8 +244,7 @@ protected void beforeHookedMethod(MethodHookParam param) { findAndHookMethod("android.content.pm.ApplicationInfo", loadPackageParam.classLoader, "isPackageWhitelistedForHiddenApis", new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { - super.beforeHookedMethod(param); - if (prefs.getBoolean("digestCreak", true)) { + if (prefs.getBoolean("digestCreak", true)) { ApplicationInfo info = (ApplicationInfo) param.thisObject; if ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0 || (info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { @@ -319,8 +300,7 @@ public void initZygote(StartupParam startupParam) { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { if (prefs.getBoolean("enhancedMode", false)) { - super.beforeHookedMethod(param); - param.args[3] = Boolean.FALSE; + param.args[3] = Boolean.FALSE; } } }); diff --git a/app/src/main/java/toolkit/coderstory/CorePatchForT.java b/app/src/main/java/toolkit/coderstory/CorePatchForT.java index 2c9d8f8..8974526 100644 --- a/app/src/main/java/toolkit/coderstory/CorePatchForT.java +++ b/app/src/main/java/toolkit/coderstory/CorePatchForT.java @@ -38,23 +38,6 @@ protected void beforeHookedMethod(MethodHookParam param) { } }); - // Package " + packageName + " signatures do not match previously installed version; ignoring!" - // public boolean checkCapability(String sha256String, @CertCapabilities int flags) { - // public boolean checkCapability(SigningDetails oldDetails, @CertCapabilities int flags) - hookAllMethods("android.content.pm.PackageParser", loadPackageParam.classLoader, "checkCapability", new XC_MethodHook() { - @Override - protected void beforeHookedMethod(MethodHookParam param) { - // Don't handle PERMISSION (grant SIGNATURE permissions to pkgs with this cert) - // Or applications will have all privileged permissions - // https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/content/pm/PackageParser.java;l=5947?q=CertCapabilities - if (prefs.getBoolean("authcreak", false)) { - if ((Integer) param.args[1] != 4) { - param.setResult(true); - } - } - } - }); - findAndHookMethod("com.android.server.pm.InstallPackageHelper", loadPackageParam.classLoader, "doesSignatureMatchForPermissions", String.class, "com.android.server.pm.parsing.pkg.ParsedPackage", int.class, new XC_MethodHook() { diff --git a/app/src/main/java/toolkit/coderstory/ReturnConstant.java b/app/src/main/java/toolkit/coderstory/ReturnConstant.java index 118ab98..0a3d3ec 100644 --- a/app/src/main/java/toolkit/coderstory/ReturnConstant.java +++ b/app/src/main/java/toolkit/coderstory/ReturnConstant.java @@ -1,7 +1,10 @@ package toolkit.coderstory; +import java.util.concurrent.atomic.AtomicLong; + import de.robv.android.xposed.XC_MethodHook; import de.robv.android.xposed.XSharedPreferences; +import de.robv.android.xposed.XposedBridge; public class ReturnConstant extends XC_MethodHook { private final XSharedPreferences prefs; @@ -16,7 +19,6 @@ public ReturnConstant(XSharedPreferences prefs, String prefsKey, Object value) { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { - super.beforeHookedMethod(param); prefs.reload(); if (prefs.getBoolean(prefsKey, true)) { param.setResult(value); diff --git a/app/src/main/java/toolkit/coderstory/XposedHelper.java b/app/src/main/java/toolkit/coderstory/XposedHelper.java index f02613e..9120307 100644 --- a/app/src/main/java/toolkit/coderstory/XposedHelper.java +++ b/app/src/main/java/toolkit/coderstory/XposedHelper.java @@ -4,6 +4,8 @@ import com.coderstory.toolkit.BuildConfig; +import java.util.Set; + import de.robv.android.xposed.XC_MethodHook; import de.robv.android.xposed.XposedBridge; import de.robv.android.xposed.XposedHelpers; @@ -34,12 +36,10 @@ public static void findAndHookMethod(Class clazz, String methodName, Object.. public static void hookAllMethods(String className, ClassLoader classLoader, String methodName, XC_MethodHook callback) { try { Class packageParser = findClass(className, classLoader); - XposedBridge.hookAllMethods(packageParser, methodName, callback); } catch (Throwable e) { if (BuildConfig.DEBUG) XposedBridge.log("E/" + MainHook.TAG + " " + Log.getStackTraceString(e)); } - } public void hookAllMethods(Class hookClass, String methodName, XC_MethodHook callback) { From 02aa4f1bb456fa5ed191a3de23cbd0ef4bd3c749 Mon Sep 17 00:00:00 2001 From: 5ec1cff Date: Tue, 27 Feb 2024 17:01:17 +0800 Subject: [PATCH 2/4] support bypass shared user verify for Android 11+ --- .../toolkit/coderstory/CorePatchForR.java | 260 +++++++++++++++++- .../toolkit/coderstory/CorePatchForT.java | 29 +- app/src/main/res/values-zh-rCN/strings.xml | 2 + app/src/main/res/values/strings.xml | 2 + app/src/main/res/xml/prefs.xml | 6 + 5 files changed, 289 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/toolkit/coderstory/CorePatchForR.java b/app/src/main/java/toolkit/coderstory/CorePatchForR.java index a7548f2..9e34487 100644 --- a/app/src/main/java/toolkit/coderstory/CorePatchForR.java +++ b/app/src/main/java/toolkit/coderstory/CorePatchForR.java @@ -1,6 +1,7 @@ package toolkit.coderstory; +import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.AndroidAppHelper; import android.content.pm.ApplicationInfo; @@ -12,6 +13,7 @@ import com.coderstory.toolkit.BuildConfig; +import java.io.PrintWriter; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; @@ -69,6 +71,7 @@ public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) XposedBridge.log("D/" + MainHook.TAG + " UsePreSig=" + prefs.getBoolean("UsePreSig", false)); XposedBridge.log("D/" + MainHook.TAG + " enhancedMode=" + prefs.getBoolean("enhancedMode", false)); XposedBridge.log("D/" + MainHook.TAG + " bypassBlock=" + prefs.getBoolean("bypassBlock", true)); + XposedBridge.log("D/" + MainHook.TAG + " sharedUser=" + prefs.getBoolean("sharedUser", false)); } var pmService = XposedHelpers.findClassIfExists("com.android.server.pm.PackageManagerService", @@ -254,15 +257,6 @@ protected void beforeHookedMethod(MethodHookParam param) throws Throwable { } }); - var utilClass = findClass("com.android.server.pm.PackageManagerServiceUtils", loadPackageParam.classLoader); - if (utilClass != null) { - try { - deoptimizeMethod(utilClass, "verifySignatures"); - } catch (Throwable e) { - XposedBridge.log("E/" + MainHook.TAG + " deoptimizing failed" + Log.getStackTraceString(e)); - } - } - var keySetManagerClass = findClass("com.android.server.pm.KeySetManagerService", loadPackageParam.classLoader); if (keySetManagerClass != null) { var shouldBypass = new ThreadLocal(); @@ -286,6 +280,131 @@ protected void afterHookedMethod(MethodHookParam param) { } }); } + + // for SharedUser + // "Package " + packageName + " has a signing lineage " + "that diverges from the lineage of the sharedUserId" + // https://cs.android.com/android/platform/superproject/+/android-11.0.0_r21:frameworks/base/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java;l=728;drc=02a58171a9d41ad0048d6a1a48d79dee585c22a5 + hookAllMethods(signingDetails, "hasCommonAncestor", new XC_MethodHook() { + @Override + protected void beforeHookedMethod(MethodHookParam param) throws Throwable { + if (prefs.getBoolean("digestCreak", true) + && prefs.getBoolean("sharedUser", false) + // because of LSPosed's bug, we can't hook verifySignatures while deoptimize it + && Arrays.stream(Thread.currentThread().getStackTrace()).anyMatch((o) -> "verifySignatures".equals(o.getMethodName())) + ) + param.setResult(true); + } + }); + + var utilClass = findClass("com.android.server.pm.PackageManagerServiceUtils", loadPackageParam.classLoader); + if (utilClass != null) { + try { + deoptimizeMethod(utilClass, "verifySignatures"); + } catch (Throwable e) { + XposedBridge.log("E/" + MainHook.TAG + " deoptimizing failed" + Log.getStackTraceString(e)); + } + } + + // choose a signature after all old signed packages are removed + var sharedUserSettingClass = XposedHelpers.findClass("com.android.server.pm.SharedUserSetting", loadPackageParam.classLoader); + XposedBridge.hookAllMethods( + sharedUserSettingClass, + "removePackage", + new XC_MethodHook() { + @Override + protected void beforeHookedMethod(MethodHookParam param) throws Throwable { + if (!prefs.getBoolean("digestCreak", true) || !prefs.getBoolean("sharedUser", false)) return; + var flags = (int) XposedHelpers.getObjectField(param.thisObject, "uidFlags"); + if ((flags & ApplicationInfo.FLAG_SYSTEM) != 0) return; // do not modify system's signature + var toRemove = param.args[0]; // PackageSetting + if (toRemove == null) return; + var removed = false; // Is toRemove really needed to be removed + var sharedUserSig = Setting_getSigningDetails(param.thisObject); + Object newSig = null; + var packages = /*Watchable?ArraySet*/ SharedUserSetting_packages(param.thisObject); + var size = (int) XposedHelpers.callMethod(packages, "size"); + for (var i = 0; i < size; i++) { + var p = XposedHelpers.callMethod(packages, "valueAt", i); + // skip the removed package + if (toRemove.equals(p)) { + removed = true; + continue; + } + var packageSig = Setting_getSigningDetails(p); + // if old signing exists, return + if ((boolean) callOriginMethod(packageSig, "checkCapability", sharedUserSig, 0) || (boolean) callOriginMethod(sharedUserSig, "checkCapability", packageSig, 0)) { + return; + } + // otherwise, choose the first signature we meet, and merge with others if possible + // https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/services/core/java/com/android/server/pm/ReconcilePackageUtils.java;l=193;drc=c9a8baf585e8eb0f3272443930301a61331b65c1 + // respect to system + if (newSig == null) newSig = packageSig; + else newSig = SigningDetails_mergeLineageWith(newSig, packageSig); + } + if (!removed || newSig == null) return; + XposedBridge.log("updating signature in sharedUser during remove: " + param.thisObject); + Setting_setSigningDetails(param.thisObject, newSig); + } + } + ); + + XposedBridge.hookAllMethods( + sharedUserSettingClass, + "addPackage", + new XC_MethodHook() { + @Override + protected void beforeHookedMethod(MethodHookParam param) throws Throwable { + if (!prefs.getBoolean("digestCreak", true) || !prefs.getBoolean("sharedUser", false)) return; + var flags = (int) XposedHelpers.getObjectField(param.thisObject, "uidFlags"); + if ((flags & ApplicationInfo.FLAG_SYSTEM) != 0) return; // do not modify system's signature + var toAdd = param.args[0]; // PackageSetting + if (toAdd == null) return; + var added = false; + var sharedUserSig = Setting_getSigningDetails(param.thisObject); + Object newSig = null; + var packages = /*Watchable?ArraySet*/ SharedUserSetting_packages(param.thisObject); + var size = (int) XposedHelpers.callMethod(packages, "size"); + for (var i = 0; i < size; i++) { + var p = XposedHelpers.callMethod(packages, "valueAt", i); + if (toAdd.equals(p)) { + // must be an existing package + added = true; + p = toAdd; + } + var packageSig = Setting_getSigningDetails(p); + // if old signing exists, return + if ((boolean) callOriginMethod(packageSig, "checkCapability", sharedUserSig, 0) || (boolean) callOriginMethod(sharedUserSig, "checkCapability", packageSig, 0)) { + return; + } + // otherwise, choose the first signature we meet, and merge with others if possible + // https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/services/core/java/com/android/server/pm/ReconcilePackageUtils.java;l=193;drc=c9a8baf585e8eb0f3272443930301a61331b65c1 + // respect to system + if (newSig == null) newSig = packageSig; + else newSig = SigningDetails_mergeLineageWith(newSig, packageSig); + } + if (!added || newSig == null) return; + XposedBridge.log("CorePatch: updating signature in sharedUser during add " + toAdd + ": " + param.thisObject); + Setting_setSigningDetails(param.thisObject, newSig); + } + } + ); + + if (BuildConfig.DEBUG) initializeDebugHook(loadPackageParam); + } + + static Object callOriginMethod(Object obj, String methodName, Object... args) { + try { + var method = XposedHelpers.findMethodBestMatch(obj.getClass(), methodName, args); + return XposedBridge.invokeOriginalMethod(method, obj, args); + } catch (IllegalAccessException e) { + // should not happen + XposedBridge.log(e); + throw new IllegalAccessError(e.getMessage()); + } catch (IllegalArgumentException e) { + throw e; + } catch (InvocationTargetException e) { + throw new RuntimeException(e.getCause()); + } } Class getSigningDetails(ClassLoader classLoader) { @@ -305,4 +424,127 @@ protected void beforeHookedMethod(MethodHookParam param) throws Throwable { } }); } + + Object mPMS = null; + + void initializeDebugHook(XC_LoadPackage.LoadPackageParam lpparam) throws IllegalAccessException, InvocationTargetException { + XposedBridge.hookAllMethods( + XposedHelpers.findClass("com.android.server.pm.PackageManagerShellCommand", lpparam.classLoader), + "onCommand", + new XC_MethodHook() { + @Override + protected void beforeHookedMethod(MethodHookParam param) throws Throwable { + try { + var pms = mPMS; + if (pms == null) return; + var cmd = (String) param.args[0]; + if (!"corepatch".equals(cmd)) return; + var self = param.thisObject; + var pw = (PrintWriter) XposedHelpers.callMethod(self, "getOutPrintWriter"); + var type = (String) XposedHelpers.callMethod(self, "getNextArgRequired"); + var settings = XposedHelpers.getObjectField(pms, "mSettings"); + if ("p".equals(type) || "package".equals(type)) { + var packageName = (String) XposedHelpers.callMethod(self, "getNextArgRequired"); + var packageSetting = XposedHelpers.callMethod(settings, "getPackageLPr", packageName); + if (packageSetting != null) { + dumpPackageSetting(packageSetting, pw, settings); + } else { + pw.println("no package " + packageName + " found"); + } + } else if ("su".equals(type) || "shareduser".equals(type)) { + var name = (String) XposedHelpers.callMethod(self, "getNextArgRequired"); + var su = getSharedUser(name, settings); + if (su != null) { + dumpSharedUserSetting(su, pw); + } else { + pw.println("no shared user " + name + " found"); + } + } else { + pw.println("usage: "); + } + param.setResult(0); + } catch (Throwable t) { + XposedBridge.log(t); + param.setThrowable(t); + } + } + } + ); + + var pmsClass = XposedHelpers.findClassIfExists("com.android.server.pm.PackageManagerService", + lpparam.classLoader); + + XposedBridge.hookAllConstructors(pmsClass, new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) throws Throwable { + mPMS = param.thisObject; + } + } + ); + + deoptimizeMethod(pmsClass, "onShellCommand"); + } + + void dumpPackageSetting(Object packageSetting, PrintWriter pw, Object /*Settings*/ settings) { + var signingDetails = Setting_getSigningDetails(packageSetting); + pw.println("signing for package " + packageSetting); + dumpSigningDetails(signingDetails, pw); + var pkg = XposedHelpers.getObjectField(packageSetting, "pkg"); // AndroidPackage + if (pkg == null) { + pw.println("android package is null!"); + return; + } + var id = (String) XposedHelpers.callMethod(pkg, "getSharedUserId"); + pw.println("shared user id:" + id); + if (settings != null) { + var su = getSharedUser(id, settings); + if (su != null) { + dumpSharedUserSetting(su, pw); + } + } + } + + Object getSharedUser(String id, Object /*Settings*/ settings) { + // TODO: use Setting.getSharedUserSettingLPr(appId)? + var sharedUserSettings = XposedHelpers.getObjectField(settings, "mSharedUsers"); + if (sharedUserSettings == null) return null; + return XposedHelpers.callMethod(sharedUserSettings, "get", id); + } + + void dumpSharedUserSetting(Object sharedUser, PrintWriter pw) { + var signingDetails = Setting_getSigningDetails(sharedUser); + pw.println("signing for shared user " + sharedUser); + dumpSigningDetails(signingDetails, pw); + } + + protected void dumpSigningDetails(Object signingDetails, PrintWriter pw) { + var i = 0; + for (var sign : (Signature[]) XposedHelpers.getObjectField(signingDetails, "signatures")) { + i++; + pw.println(i + ": " + sign.toCharsString()); + } + } + + /** + * Get signing details for PackageSetting or SharedUserSetting + */ + Object Setting_getSigningDetails(Object pkgOrSharedUser) { + // PackageSettingBase(A11)|PackageSetting(A13)|SharedUserSetting.signatures.mSigningDetails + return XposedHelpers.getObjectField(XposedHelpers.getObjectField(pkgOrSharedUser, "signatures"), "mSigningDetails"); + } + + /** + * Set signing details for PackageSetting or SharedUserSetting + */ + void Setting_setSigningDetails(Object pkgOrSharedUser, Object signingDetails) { + XposedHelpers.setObjectField(XposedHelpers.getObjectField(pkgOrSharedUser, "signatures"), "mSigningDetails", signingDetails); + } + + protected Object SharedUserSetting_packages(Object /*SharedUserSetting*/ sharedUser) { + return XposedHelpers.getObjectField(sharedUser, "packages"); + } + + protected Object SigningDetails_mergeLineageWith(Object self, Object other) { + return XposedHelpers.callMethod(self, "mergeLineageWith", other); + } } diff --git a/app/src/main/java/toolkit/coderstory/CorePatchForT.java b/app/src/main/java/toolkit/coderstory/CorePatchForT.java index 8974526..837e000 100644 --- a/app/src/main/java/toolkit/coderstory/CorePatchForT.java +++ b/app/src/main/java/toolkit/coderstory/CorePatchForT.java @@ -1,7 +1,9 @@ package toolkit.coderstory; +import android.content.pm.Signature; import android.util.Log; +import java.io.PrintWriter; import java.lang.reflect.InvocationTargetException; import de.robv.android.xposed.XC_MethodHook; @@ -80,10 +82,35 @@ protected void afterHookedMethod(MethodHookParam param) { } }); } + + // ensure verifySignatures success + // https://cs.android.com/android/platform/superproject/main/+/main:frameworks/base/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java;l=621;drc=2e50991320cbef77d3e8504a4b284adae8c2f4d2 + var utils = XposedHelpers.findClassIfExists("com.android.server.pm.PackageManagerServiceUtils", loadPackageParam.classLoader); + if (utils != null) { + deoptimizeMethod(utils, "canJoinSharedUserId"); + } } - @Override Class getSigningDetails(ClassLoader classLoader) { return XposedHelpers.findClassIfExists("android.content.pm.SigningDetails", classLoader); } + + @Override + protected void dumpSigningDetails(Object signingDetails, PrintWriter pw) { + var i = 0; + for (var sign : (Signature[]) XposedHelpers.callMethod(signingDetails, "getSignatures")) { + i++; + pw.println(i + ": " + sign.toCharsString()); + } + } + + @Override + protected Object SharedUserSetting_packages(Object sharedUser) { + return XposedHelpers.getObjectField(sharedUser, "mPackages"); + } + + @Override + protected Object SigningDetails_mergeLineageWith(Object self, Object other) { + return XposedHelpers.callMethod(self, "mergeLineageWith", other, 2 /*MERGE_RESTRICTED_CAPABILITY*/); + } } diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 226e7e0..47939d7 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -20,4 +20,6 @@ 绕过黑名单 绕过某些设备如 Nothing Phone 上的黑名单 配置初始化失败 + 绕过共享用户签名验证 + 允许安装与其共享用户签名不同的 app(需要同时打开“禁用APK签名验证” diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 42bae1c..74a4194 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -20,4 +20,6 @@ Bypass blocklist in some devices like Nothing Phone UsePreSig won\'t work on MiUI because its framework changes too much.\nWe don\'t have time to track the case. !! Any apk can override the installed one !!\nBe carefully when installing unknown apk + Bypass shared user verify + Allow install app with signature differ from its shared user (\'Disable compare signatures\' must be enabled too) diff --git a/app/src/main/res/xml/prefs.xml b/app/src/main/res/xml/prefs.xml index 8f3c4d6..ffbb0da 100644 --- a/app/src/main/res/xml/prefs.xml +++ b/app/src/main/res/xml/prefs.xml @@ -34,6 +34,12 @@ android:title="@string/bypassBlock" android:summary="@string/bypassBlock_summary" android:defaultValue="true" /> + + From 765119c0c897e427ad209c6aba14f1d509cb316f Mon Sep 17 00:00:00 2001 From: 5ec1cff Date: Thu, 29 Feb 2024 22:11:07 +0800 Subject: [PATCH 3/4] fix cleanup code --- app/src/main/java/toolkit/coderstory/XposedHelper.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/toolkit/coderstory/XposedHelper.java b/app/src/main/java/toolkit/coderstory/XposedHelper.java index 9120307..260d20a 100644 --- a/app/src/main/java/toolkit/coderstory/XposedHelper.java +++ b/app/src/main/java/toolkit/coderstory/XposedHelper.java @@ -36,6 +36,7 @@ public static void findAndHookMethod(Class clazz, String methodName, Object.. public static void hookAllMethods(String className, ClassLoader classLoader, String methodName, XC_MethodHook callback) { try { Class packageParser = findClass(className, classLoader); + XposedBridge.hookAllMethods(packageParser, methodName, callback); } catch (Throwable e) { if (BuildConfig.DEBUG) XposedBridge.log("E/" + MainHook.TAG + " " + Log.getStackTraceString(e)); From 7b1cb849805080cc85f774548dd4d4eaf4cf29de Mon Sep 17 00:00:00 2001 From: 5ec1cff Date: Thu, 29 Feb 2024 22:22:45 +0800 Subject: [PATCH 4/4] reformat code --- .../java/toolkit/coderstory/CorePatchForQ.java | 8 ++++---- .../java/toolkit/coderstory/CorePatchForR.java | 17 ++++++++++------- .../java/toolkit/coderstory/CorePatchForT.java | 1 - .../java/toolkit/coderstory/ReturnConstant.java | 3 --- .../toolkit/coderstory/SettingsActivity.java | 4 ---- .../java/toolkit/coderstory/XposedHelper.java | 4 ++-- 6 files changed, 16 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/toolkit/coderstory/CorePatchForQ.java b/app/src/main/java/toolkit/coderstory/CorePatchForQ.java index 5a6af4d..fc2ddb7 100644 --- a/app/src/main/java/toolkit/coderstory/CorePatchForQ.java +++ b/app/src/main/java/toolkit/coderstory/CorePatchForQ.java @@ -28,7 +28,7 @@ public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) Class packageClazz = XposedHelpers.findClass("android.content.pm.PackageParser.Package", loadPackageParam.classLoader); hookAllMethods("com.android.server.pm.PackageManagerService", loadPackageParam.classLoader, "checkDowngrade", new XC_MethodHook() { public void beforeHookedMethod(MethodHookParam methodHookParam) throws Throwable { - if (prefs.getBoolean("downgrade", true)) { + if (prefs.getBoolean("downgrade", true)) { Object packageInfoLite = methodHookParam.args[0]; if (prefs.getBoolean("downgrade", true)) { @@ -99,7 +99,7 @@ protected void beforeHookedMethod(MethodHookParam param) throws Throwable { new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { - if (prefs.getBoolean("digestCreak", true)) { + if (prefs.getBoolean("digestCreak", true)) { if ((Integer) param.args[1] != 4 && prefs.getBoolean("authcreak", false)) { param.setResult(Boolean.TRUE); } @@ -111,7 +111,7 @@ protected void beforeHookedMethod(MethodHookParam param) throws Throwable { findAndHookMethod("android.content.pm.ApplicationInfo", loadPackageParam.classLoader, "isPackageWhitelistedForHiddenApis", new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { - if (prefs.getBoolean("digestCreak", true)) { + if (prefs.getBoolean("digestCreak", true)) { ApplicationInfo info = (ApplicationInfo) param.thisObject; if ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0 || (info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { @@ -154,7 +154,7 @@ public void initZygote(StartupParam startupParam) { hookAllConstructors("android.util.jar.StrictJarVerifier", new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { - if (prefs.getBoolean("enhancedMode", false)) { + if (prefs.getBoolean("enhancedMode", false)) { param.args[3] = Boolean.FALSE; } } diff --git a/app/src/main/java/toolkit/coderstory/CorePatchForR.java b/app/src/main/java/toolkit/coderstory/CorePatchForR.java index 9e34487..3801739 100644 --- a/app/src/main/java/toolkit/coderstory/CorePatchForR.java +++ b/app/src/main/java/toolkit/coderstory/CorePatchForR.java @@ -1,7 +1,6 @@ package toolkit.coderstory; -import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.AndroidAppHelper; import android.content.pm.ApplicationInfo; @@ -247,7 +246,7 @@ protected void beforeHookedMethod(MethodHookParam param) { findAndHookMethod("android.content.pm.ApplicationInfo", loadPackageParam.classLoader, "isPackageWhitelistedForHiddenApis", new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { - if (prefs.getBoolean("digestCreak", true)) { + if (prefs.getBoolean("digestCreak", true)) { ApplicationInfo info = (ApplicationInfo) param.thisObject; if ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0 || (info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) { @@ -313,9 +312,11 @@ protected void beforeHookedMethod(MethodHookParam param) throws Throwable { new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { - if (!prefs.getBoolean("digestCreak", true) || !prefs.getBoolean("sharedUser", false)) return; + if (!prefs.getBoolean("digestCreak", true) || !prefs.getBoolean("sharedUser", false)) + return; var flags = (int) XposedHelpers.getObjectField(param.thisObject, "uidFlags"); - if ((flags & ApplicationInfo.FLAG_SYSTEM) != 0) return; // do not modify system's signature + if ((flags & ApplicationInfo.FLAG_SYSTEM) != 0) + return; // do not modify system's signature var toRemove = param.args[0]; // PackageSetting if (toRemove == null) return; var removed = false; // Is toRemove really needed to be removed @@ -354,9 +355,11 @@ protected void beforeHookedMethod(MethodHookParam param) throws Throwable { new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { - if (!prefs.getBoolean("digestCreak", true) || !prefs.getBoolean("sharedUser", false)) return; + if (!prefs.getBoolean("digestCreak", true) || !prefs.getBoolean("sharedUser", false)) + return; var flags = (int) XposedHelpers.getObjectField(param.thisObject, "uidFlags"); - if ((flags & ApplicationInfo.FLAG_SYSTEM) != 0) return; // do not modify system's signature + if ((flags & ApplicationInfo.FLAG_SYSTEM) != 0) + return; // do not modify system's signature var toAdd = param.args[0]; // PackageSetting if (toAdd == null) return; var added = false; @@ -419,7 +422,7 @@ public void initZygote(StartupParam startupParam) { @Override protected void beforeHookedMethod(MethodHookParam param) throws Throwable { if (prefs.getBoolean("enhancedMode", false)) { - param.args[3] = Boolean.FALSE; + param.args[3] = Boolean.FALSE; } } }); diff --git a/app/src/main/java/toolkit/coderstory/CorePatchForT.java b/app/src/main/java/toolkit/coderstory/CorePatchForT.java index 837e000..70023ad 100644 --- a/app/src/main/java/toolkit/coderstory/CorePatchForT.java +++ b/app/src/main/java/toolkit/coderstory/CorePatchForT.java @@ -1,7 +1,6 @@ package toolkit.coderstory; import android.content.pm.Signature; -import android.util.Log; import java.io.PrintWriter; import java.lang.reflect.InvocationTargetException; diff --git a/app/src/main/java/toolkit/coderstory/ReturnConstant.java b/app/src/main/java/toolkit/coderstory/ReturnConstant.java index 0a3d3ec..ace1d78 100644 --- a/app/src/main/java/toolkit/coderstory/ReturnConstant.java +++ b/app/src/main/java/toolkit/coderstory/ReturnConstant.java @@ -1,10 +1,7 @@ package toolkit.coderstory; -import java.util.concurrent.atomic.AtomicLong; - import de.robv.android.xposed.XC_MethodHook; import de.robv.android.xposed.XSharedPreferences; -import de.robv.android.xposed.XposedBridge; public class ReturnConstant extends XC_MethodHook { private final XSharedPreferences prefs; diff --git a/app/src/main/java/toolkit/coderstory/SettingsActivity.java b/app/src/main/java/toolkit/coderstory/SettingsActivity.java index c15a7e4..407fcdb 100644 --- a/app/src/main/java/toolkit/coderstory/SettingsActivity.java +++ b/app/src/main/java/toolkit/coderstory/SettingsActivity.java @@ -5,17 +5,13 @@ import android.app.AlertDialog; import android.content.Context; import android.content.SharedPreferences; -import android.graphics.Color; import android.graphics.Insets; -import android.graphics.drawable.ColorDrawable; import android.os.Build; import android.os.Bundle; import android.preference.PreferenceFragment; import android.view.View; import android.view.ViewGroup; -import android.view.Window; import android.view.WindowInsets; -import android.view.WindowInsetsController; import com.coderstory.toolkit.R; diff --git a/app/src/main/java/toolkit/coderstory/XposedHelper.java b/app/src/main/java/toolkit/coderstory/XposedHelper.java index 260d20a..dd64893 100644 --- a/app/src/main/java/toolkit/coderstory/XposedHelper.java +++ b/app/src/main/java/toolkit/coderstory/XposedHelper.java @@ -4,8 +4,6 @@ import com.coderstory.toolkit.BuildConfig; -import java.util.Set; - import de.robv.android.xposed.XC_MethodHook; import de.robv.android.xposed.XposedBridge; import de.robv.android.xposed.XposedHelpers; @@ -23,6 +21,7 @@ public static void findAndHookMethod(String className, ClassLoader classLoader, XposedBridge.log("E/" + MainHook.TAG + " " + Log.getStackTraceString(e)); } } + public static void findAndHookMethod(Class clazz, String methodName, Object... parameterTypesAndCallback) { try { if (clazz != null) { @@ -33,6 +32,7 @@ public static void findAndHookMethod(Class clazz, String methodName, Object.. XposedBridge.log("E/" + MainHook.TAG + " " + Log.getStackTraceString(e)); } } + public static void hookAllMethods(String className, ClassLoader classLoader, String methodName, XC_MethodHook callback) { try { Class packageParser = findClass(className, classLoader);