From b23589209898d0f52834773dce23986bf0b73cca Mon Sep 17 00:00:00 2001 From: NekoInverter <42698724+NekoInverter@users.noreply.github.com> Date: Sun, 5 Apr 2020 19:05:39 +0800 Subject: [PATCH] Enhance enhance! --- app/src/main/assets/xposed_init | 1 + .../edxposed/manager/SettingsActivity.java | 73 +++++++ .../receivers/PackageChangeReceiver.java | 3 +- .../edxposed/manager/xposed/Enhancement.java | 202 +++++++++++++++++- app/src/main/res/values-zh-rCN/strings.xml | 10 +- app/src/main/res/values/strings.xml | 10 +- app/src/main/res/xml/prefs.xml | 23 ++ 7 files changed, 311 insertions(+), 11 deletions(-) create mode 100644 app/src/main/assets/xposed_init diff --git a/app/src/main/assets/xposed_init b/app/src/main/assets/xposed_init new file mode 100644 index 00000000..dbac97d2 --- /dev/null +++ b/app/src/main/assets/xposed_init @@ -0,0 +1 @@ +org.meowcat.edxposed.manager.xposed.Enhancement \ No newline at end of file diff --git a/app/src/main/java/org/meowcat/edxposed/manager/SettingsActivity.java b/app/src/main/java/org/meowcat/edxposed/manager/SettingsActivity.java index b8fbd060..7b485960 100644 --- a/app/src/main/java/org/meowcat/edxposed/manager/SettingsActivity.java +++ b/app/src/main/java/org/meowcat/edxposed/manager/SettingsActivity.java @@ -26,6 +26,8 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.takisoft.preferencex.PreferenceFragmentCompat; import com.topjohnwu.superuser.Shell; +import org.meowcat.edxposed.manager.adapters.AppHelper; +import org.meowcat.edxposed.manager.adapters.BlackListAdapter; import org.meowcat.edxposed.manager.databinding.ActivitySettingsBinding; import org.meowcat.edxposed.manager.util.RepoLoader; import org.meowcat.edxposed.manager.widget.IntegerListPreference; @@ -34,6 +36,7 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; +import java.util.Objects; public class SettingsActivity extends BaseActivity { private static final String KEY_PREFIX = SettingsActivity.class.getName() + '.'; @@ -111,6 +114,8 @@ public class SettingsActivity extends BaseActivity { @SuppressWarnings({"ResultOfMethodCallIgnored", "deprecation"}) public static class SettingsFragment extends PreferenceFragmentCompat { + private static final File pretendXposedInstallerFlag = new File(XposedApp.BASE_DIR + "conf/pretend_xposed_installer"); + private static final File hideEdXposedManagerFlag = new File(XposedApp.BASE_DIR + "conf/hide_edxposed_manager"); static final File disableResourcesFlag = new File(XposedApp.BASE_DIR + "conf/disable_resources"); static final File dynamicModulesFlag = new File(XposedApp.BASE_DIR + "conf/dynamicmodules"); static final File deoptBootFlag = new File(XposedApp.BASE_DIR + "conf/deoptbootimage"); @@ -486,6 +491,74 @@ public class SettingsActivity extends BaseActivity { return true; }); updatePreference(!md2.isChecked()); + + Preference enhancement_status = findPreference("enhancement_status"); + Objects.requireNonNull(enhancement_status).setSummary(StatusInstallerFragment.isEnhancementEnabled() ? R.string.settings_summary_enhancement_enabled : R.string.settings_summary_enhancement); + SwitchPreferenceCompat prefPretendXposedInstaller = findPreference("pretend_xposed_installer"); + + Objects.requireNonNull(prefPretendXposedInstaller).setChecked(pretendXposedInstallerFlag.exists()); + prefPretendXposedInstaller.setOnPreferenceChangeListener((preference, newValue) -> { + boolean enabled = (Boolean) newValue; + if (enabled) { + new BlackListAdapter(getContext(), AppHelper.isWhiteListMode(), null).generateCheckedList(); + FileOutputStream fos = null; + try { + fos = new FileOutputStream(pretendXposedInstallerFlag.getPath()); + setFilePermissionsFromMode(pretendXposedInstallerFlag.getPath()); + } catch (FileNotFoundException e) { + Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_SHORT).show(); + } finally { + if (fos != null) { + try { + fos.close(); + } catch (IOException e) { + Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_SHORT).show(); + try { + pretendXposedInstallerFlag.createNewFile(); + } catch (IOException e1) { + Toast.makeText(getActivity(), e1.getMessage(), Toast.LENGTH_SHORT).show(); + } + } + } + } + } else { + pretendXposedInstallerFlag.delete(); + } + return (enabled == pretendXposedInstallerFlag.exists()); + }); + + SwitchPreferenceCompat prefHideEdXposedManager = findPreference("hide_edxposed_manager"); + Objects.requireNonNull(prefHideEdXposedManager).setChecked(hideEdXposedManagerFlag.exists()); + prefHideEdXposedManager.setOnPreferenceChangeListener((preference, newValue) -> { + boolean enabled = (Boolean) newValue; + if (enabled) { + new BlackListAdapter(getContext(), AppHelper.isWhiteListMode(), null).generateCheckedList(); + FileOutputStream fos = null; + try { + fos = new FileOutputStream(hideEdXposedManagerFlag.getPath()); + setFilePermissionsFromMode(hideEdXposedManagerFlag.getPath()); + } catch (FileNotFoundException e) { + Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_SHORT).show(); + } finally { + if (fos != null) { + try { + fos.close(); + } catch (IOException e) { + Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_SHORT).show(); + try { + hideEdXposedManagerFlag.createNewFile(); + } catch (IOException e1) { + Toast.makeText(getActivity(), e1.getMessage(), Toast.LENGTH_SHORT).show(); + } + } + } + } + } else { + hideEdXposedManagerFlag.delete(); + } + return (enabled == hideEdXposedManagerFlag.exists()); + }); + } } diff --git a/app/src/main/java/org/meowcat/edxposed/manager/receivers/PackageChangeReceiver.java b/app/src/main/java/org/meowcat/edxposed/manager/receivers/PackageChangeReceiver.java index ddbf37d0..9c7a3623 100644 --- a/app/src/main/java/org/meowcat/edxposed/manager/receivers/PackageChangeReceiver.java +++ b/app/src/main/java/org/meowcat/edxposed/manager/receivers/PackageChangeReceiver.java @@ -51,6 +51,7 @@ public class PackageChangeReceiver extends BroadcastReceiver { moduleUtil = getModuleUtilInstance(); + moduleUtil.updateModulesList(false); InstalledModule module = ModuleUtil.getInstance().reloadSingleModule(packageName); if (module == null || intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED)) { @@ -58,13 +59,11 @@ public class PackageChangeReceiver extends BroadcastReceiver { // Xposed mod if (moduleUtil.isModuleEnabled(packageName)) { moduleUtil.setModuleEnabled(packageName, false); - moduleUtil.updateModulesList(false); } return; } if (moduleUtil.isModuleEnabled(packageName)) { - moduleUtil.updateModulesList(false); NotificationUtil.showModulesUpdatedNotification(); } else { NotificationUtil.showNotActivatedNotification(packageName, module.getAppName()); diff --git a/app/src/main/java/org/meowcat/edxposed/manager/xposed/Enhancement.java b/app/src/main/java/org/meowcat/edxposed/manager/xposed/Enhancement.java index ddadff78..f58f92f9 100644 --- a/app/src/main/java/org/meowcat/edxposed/manager/xposed/Enhancement.java +++ b/app/src/main/java/org/meowcat/edxposed/manager/xposed/Enhancement.java @@ -1,6 +1,19 @@ -package org.meowcat.edxposed.manager.xposed; +package org.meowcat.edxposed.manager.xposed;import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.os.Binder; import android.os.Build; +import androidx.annotation.Keep; + +import org.meowcat.edxposed.manager.StatusInstallerFragment; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + import de.robv.android.xposed.IXposedHookLoadPackage; import de.robv.android.xposed.XC_MethodHook; import de.robv.android.xposed.XC_MethodReplacement; @@ -8,12 +21,45 @@ import de.robv.android.xposed.XposedBridge; import de.robv.android.xposed.XposedHelpers; import de.robv.android.xposed.callbacks.XC_LoadPackage; +import static de.robv.android.xposed.XposedHelpers.findAndHookMethod; import static org.meowcat.edxposed.manager.BuildConfig.APPLICATION_ID; +@Keep public class Enhancement implements IXposedHookLoadPackage { + private static final String mPretendXposedInstallerFlag = "pretend_xposed_installer"; + private static final String mHideEdXposedManagerFlag = "hide_edxposed_manager"; + private static final String LEGACY_INSTALLER = "de.robv.android.xposed.installer"; + private static List modulesList = null; + + private static boolean getFlagState(int user, String flag) { + return new File(String.format("/data/user_de/%s/%s/conf/%s", user, APPLICATION_ID, flag)).exists(); + } + + private static List getModulesList(int user) { + if (modulesList != null) { + return modulesList; + } + final File listFile = new File(String.format("/data/user_de/%s/%s/conf/enabled_modules.list", user, APPLICATION_ID)); + List list = new ArrayList<>(); + try { + FileReader fileReader = new FileReader(listFile); + BufferedReader bufferedReader = new BufferedReader(fileReader); + String str; + while ((str = bufferedReader.readLine()) != null) { + list.add(str); + } + bufferedReader.close(); + fileReader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + modulesList = list; + return list; + } + private static void hookAllMethods(String className, ClassLoader classLoader, String methodName, XC_MethodHook callback) { try { Class hookClass = XposedHelpers.findClassIfExists(className, classLoader); @@ -27,24 +73,165 @@ public class Enhancement implements IXposedHookLoadPackage { @Override public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) { if (lpparam.packageName.equals("android")) { - // Hook PM to pretend to have legacy Xposed Installer installed + // android.app.ApplicationPackageManager.getInstalledApplicationsAsUser(int flag, int userId) + findAndHookMethod("android.app.ApplicationPackageManager", lpparam.classLoader, "getInstalledApplicationsAsUser", int.class, int.class, new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) { + if (param.args != null && param.args[0] != null) { + final int userId = (int) param.args[1]; + + boolean isXposedModule = false; + final String[] packages = + (String[]) XposedHelpers.callMethod(param.thisObject, "getPackagesForUid", Binder.getCallingUid()); + for (String packageName : packages) { + if (packageName.equals(APPLICATION_ID)) { + return; + } + if (getModulesList(userId).contains(packageName)) { + isXposedModule = true; + break; + } + } + + @SuppressWarnings("unchecked") List applicationInfoList = (List) param.getResult(); + if (isXposedModule) { + if (getFlagState(userId, mPretendXposedInstallerFlag)) { + for (ApplicationInfo applicationInfo : applicationInfoList) { + if (applicationInfo.packageName.equals(APPLICATION_ID)) { + applicationInfo.packageName = LEGACY_INSTALLER; + applicationInfoList.add(applicationInfo); + break; + } + } + } + } else { + if (getFlagState(userId, mHideEdXposedManagerFlag)) { + for (ApplicationInfo applicationInfo : applicationInfoList) { + if (applicationInfo.packageName.equals(APPLICATION_ID)) { + applicationInfoList.remove(applicationInfo); + break; + } + } + } + } + param.setResult(applicationInfoList); + } + } + }); + // android.app.ApplicationPackageManager.getInstalledPackagesAsUser(int flag, int userId) + findAndHookMethod("android.app.ApplicationPackageManager", lpparam.classLoader, "getInstalledPackagesAsUser", int.class, int.class, new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) { + if (param.args != null && param.args[0] != null) { + final int userId = (int) param.args[1]; + + boolean isXposedModule = false; + final String[] packages = + (String[]) XposedHelpers.callMethod(param.thisObject, "getPackagesForUid", Binder.getCallingUid()); + for (String packageName : packages) { + if (packageName.equals(APPLICATION_ID)) { + return; + } + if (getModulesList(userId).contains(packageName)) { + isXposedModule = true; + break; + } + } + + @SuppressWarnings("unchecked") List packageInfoList = (List) param.getResult(); + if (isXposedModule) { + if (getFlagState(userId, mPretendXposedInstallerFlag)) { + for (PackageInfo packageInfo : packageInfoList) { + if (packageInfo.packageName.equals(APPLICATION_ID)) { + packageInfo.packageName = LEGACY_INSTALLER; + packageInfoList.add(packageInfo); + break; + } + } + } + } else { + if (getFlagState(userId, mHideEdXposedManagerFlag)) { + for (PackageInfo packageInfo : packageInfoList) { + if (packageInfo.packageName.equals(APPLICATION_ID)) { + packageInfoList.remove(packageInfo); + break; + } + } + } + } + param.setResult(packageInfoList); + } + } + }); + // com.android.server.pm.PackageManagerService.getApplicationInfo(String packageName, int flag, int userId) hookAllMethods("com.android.server.pm.PackageManagerService", lpparam.classLoader, "getApplicationInfo", new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) { if (param.args != null && param.args[0] != null) { - if (param.args[0].equals(LEGACY_INSTALLER)) { - param.args[0] = APPLICATION_ID; + final int userId = (int) param.args[2]; + + boolean isXposedModule = false; + final String[] packages = + (String[]) XposedHelpers.callMethod(param.thisObject, "getPackagesForUid", Binder.getCallingUid()); + for (String packageName : packages) { + if (packageName.equals(APPLICATION_ID)) { + return; + } + if (getModulesList(userId).contains(packageName)) { + isXposedModule = true; + break; + } + } + + if (isXposedModule) { + if (getFlagState(userId, mPretendXposedInstallerFlag)) { + if (param.args[0].equals(LEGACY_INSTALLER)) { + param.args[0] = APPLICATION_ID; + } + } + } else { + if (getFlagState(userId, mHideEdXposedManagerFlag)) { + if (param.args[0].equals(APPLICATION_ID)) { + param.setResult(null); + } + } } } } }); + // com.android.server.pm.PackageManagerService.getPackageInfo(String packageName, int flag, int userId) hookAllMethods("com.android.server.pm.PackageManagerService", lpparam.classLoader, "getPackageInfo", new XC_MethodHook() { @Override protected void beforeHookedMethod(MethodHookParam param) { if (param.args != null && param.args[0] != null) { - if (param.args[0].equals(LEGACY_INSTALLER)) { - param.args[0] = APPLICATION_ID; + final int userId = (int) param.args[2]; + + boolean isXposedModule = false; + final String[] packages = + (String[]) XposedHelpers.callMethod(param.thisObject, "getPackagesForUid", Binder.getCallingUid()); + for (String packageName : packages) { + if (packageName.equals(APPLICATION_ID)) { + return; + } + if (getModulesList(userId).contains(packageName)) { + isXposedModule = true; + break; + } + } + + if (isXposedModule) { + if (getFlagState(userId, mPretendXposedInstallerFlag)) { + if (param.args[0].equals(LEGACY_INSTALLER)) { + param.args[0] = APPLICATION_ID; + } + } + } else { + if (getFlagState(userId, mHideEdXposedManagerFlag)) { + if (param.args[0].equals(APPLICATION_ID)) { + param.setResult(null); + } + } } } } @@ -84,7 +271,8 @@ public class Enhancement implements IXposedHookLoadPackage { } } else if (lpparam.packageName.equals(APPLICATION_ID)) { // Make sure Xposed work - XposedHelpers.findAndHookMethod("org.meowcat.edxposed.manager.StatusInstallerFragment", lpparam.classLoader, "isEnhancementEnabled", XC_MethodReplacement.returnConstant(true)); + XposedHelpers.findAndHookMethod(StatusInstallerFragment.class.getName(), lpparam.classLoader, "isEnhancementEnabled", XC_MethodReplacement.returnConstant(true)); + // XposedHelpers.findAndHookMethod(StatusInstallerFragment.class.getName(), lpparam.classLoader, "isSELinuxEnforced", XC_MethodReplacement.returnConstant(SELinuxHelper.isSELinuxEnforced())); } } diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index af363c78..06c56619 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -302,6 +302,14 @@ 着色应用栏 主题 惨白设计 - 启用 EdXposed 的增强功能:\n判断 Xposed 是否正常工作\n移除针对 EdXposed 的后台限制\n假装安装了 Xposed Installer + 启用 EdXposed 的增强功能:\n - 判断 Xposed 是否正常工作\n - 移除针对 EdXposed 的后台限制\n - 假装安装了 Xposed Installer\n - 隐藏 EdXposed Manager 增强模式已激活 + 增强模式 + 增强模式状态 + 未激活\n你可以在「模块」中启用增强模块 + 已激活 + Xposed Installer 伪装 + 假装已安装 Xposed Installer 来使一些过时但有用的模块正常工作\n注:开启此功能可能会被某些软件(如 RootBeer)检测到 EdXposed + 隐藏 EdXposed Manager + 防止软件检测到 EdXposed Manager\n注:模块可能无法正常打开 Manager 界面 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8d0ba9ba..113c79e2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -333,7 +333,15 @@ Colorized action bar Theme Material Design 2 - Enable EdXposed enhancements:\nRemove background restrictions on EdXposed\nDetermine if Xposed is working properly\nPretend to have Xposed Installer installed + Enable EdXposed enhancements:\nRemove background restrictions on EdXposed\nDetermine if Xposed is working properly\nPretend to have Xposed Installer installed\nHide EdXposed Manager Enhancement mode + Enhancement mode + Enhancement mode status + Disabled\nYou can enable enhancement mode in the module + Enabled + Pretend to have Xposed Installer installed + Pretend that Xposed Installer is installed to make some outdated but useful modules work\nWARNING: Some software (eg. RootBeer) may detect that EdXposed when this function is turned on + Hide EdXposed Manager + Prevent the software from detecting EdXposed Manager\nWARNING: Modules may not be able to start the Manager UI properly Version %d diff --git a/app/src/main/res/xml/prefs.xml b/app/src/main/res/xml/prefs.xml index dd3fcd06..097007aa 100644 --- a/app/src/main/res/xml/prefs.xml +++ b/app/src/main/res/xml/prefs.xml @@ -189,4 +189,27 @@ app:iconSpaceReserved="false" /> + + + + + + + + + + \ No newline at end of file