Fix module loading (#2418)

This commit is contained in:
LoveSy 2023-03-05 20:37:21 +08:00 committed by GitHub
parent 55afed974d
commit 516a4aa99d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 98 additions and 69 deletions

View File

@ -75,10 +75,10 @@ public abstract class XC_MethodHook extends XCallback {
* @param param Information about the method call.
* @throws Throwable Everything the callback throws is caught and logged.
*/
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
protected void beforeHookedMethod(MethodHookParam<?> param) throws Throwable {
}
public void callBeforeHookedMethod(MethodHookParam param) throws Throwable {
public void callBeforeHookedMethod(MethodHookParam<?> param) throws Throwable {
beforeHookedMethod(param);
}
@ -93,10 +93,10 @@ public abstract class XC_MethodHook extends XCallback {
* @param param Information about the method call.
* @throws Throwable Everything the callback throws is caught and logged.
*/
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
protected void afterHookedMethod(MethodHookParam<?> param) throws Throwable {
}
public void callAfterHookedMethod(MethodHookParam param) throws Throwable {
public void callAfterHookedMethod(MethodHookParam<?> param) throws Throwable {
afterHookedMethod(param);
}

View File

@ -50,6 +50,7 @@ import java.security.MessageDigest;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import de.robv.android.xposed.services.FileResult;
@ -160,29 +161,27 @@ public final class XSharedPreferences implements SharedPreferences {
*/
public XSharedPreferences(String packageName, String prefFileName) {
boolean newModule = false;
Set<String> modules = XposedInit.getLoadedModules();
for (String m : modules) {
if (m.contains("/" + packageName + "-")) {
boolean isModule = false;
int xposedminversion = -1;
boolean xposedsharedprefs = false;
try {
Map<String, Object> metaData = MetaDataReader.getMetaData(new File(m));
isModule = metaData.containsKey("xposedminversion");
if (isModule) {
Object minVersionRaw = metaData.get("xposedminversion");
if (minVersionRaw instanceof Integer) {
xposedminversion = (Integer) minVersionRaw;
} else if (minVersionRaw instanceof String) {
xposedminversion = MetaDataReader.extractIntPart((String) minVersionRaw);
}
xposedsharedprefs = metaData.containsKey("xposedsharedprefs");
var m = XposedInit.getLoadedModules().getOrDefault(packageName, Optional.empty());
if (m.isPresent()) {
boolean isModule = false;
int xposedminversion = -1;
boolean xposedsharedprefs = false;
try {
Map<String, Object> metaData = MetaDataReader.getMetaData(new File(m.get()));
isModule = metaData.containsKey("xposedminversion");
if (isModule) {
Object minVersionRaw = metaData.get("xposedminversion");
if (minVersionRaw instanceof Integer) {
xposedminversion = (Integer) minVersionRaw;
} else if (minVersionRaw instanceof String) {
xposedminversion = MetaDataReader.extractIntPart((String) minVersionRaw);
}
} catch (NumberFormatException | IOException e) {
Log.w(TAG, "Apk parser fails: " + e);
xposedsharedprefs = metaData.containsKey("xposedsharedprefs");
}
newModule = isModule && (xposedminversion > 92 || xposedsharedprefs);
} catch (NumberFormatException | IOException e) {
Log.w(TAG, "Apk parser fails: " + e);
}
newModule = isModule && (xposedminversion > 92 || xposedsharedprefs);
}
if (newModule) {
mFile = new File(serviceClient.getPrefsPath(packageName), prefFileName + ".xml");

View File

@ -29,6 +29,7 @@ import static de.robv.android.xposed.XposedHelpers.getObjectField;
import static de.robv.android.xposed.XposedHelpers.getParameterIndexByType;
import static de.robv.android.xposed.XposedHelpers.setStaticObjectField;
import android.app.ActivityThread;
import android.content.pm.ApplicationInfo;
import android.content.res.Resources;
import android.content.res.ResourcesImpl;
@ -50,9 +51,10 @@ import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicBoolean;
import de.robv.android.xposed.callbacks.XC_InitPackageResources;
@ -82,7 +84,7 @@ public final class XposedInit {
findAndHookMethod("android.app.ApplicationPackageManager", null, "getResourcesForApplication",
ApplicationInfo.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
protected void beforeHookedMethod(MethodHookParam<?> param) {
ApplicationInfo app = (ApplicationInfo) param.args[0];
XResources.setPackageNameForResDir(app.packageName,
app.uid == Process.myUid() ? app.sourceDir : app.publicSourceDir);
@ -117,7 +119,7 @@ public final class XposedInit {
final Class<?> classActivityRes = XposedHelpers.findClassIfExists("android.app.ResourcesManager$ActivityResource", classGTLR.getClassLoader());
var hooker = new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) {
protected void afterHookedMethod(MethodHookParam<?> param) {
// At least on OnePlus 5, the method has an additional parameter compared to AOSP.
Object activityToken = null;
try {
@ -162,7 +164,7 @@ public final class XposedInit {
findAndHookMethod(TypedArray.class, "obtain", Resources.class, int.class,
new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
protected void afterHookedMethod(MethodHookParam<?> param) throws Throwable {
if (param.getResult() instanceof XResources.XTypedArray) {
return;
}
@ -189,7 +191,7 @@ public final class XposedInit {
XResources.init(latestResKey);
}
private static XResources cloneToXResources(XC_MethodHook.MethodHookParam param, String resDir) {
private static XResources cloneToXResources(XC_MethodHook.MethodHookParam<?> param, String resDir) {
Object result = param.getResult();
if (result == null || result instanceof XResources) {
return null;
@ -213,21 +215,31 @@ public final class XposedInit {
return newRes;
}
private static final Set<String> loadedModules = new CopyOnWriteArraySet<>();
// only legacy modules have non-empty value
private static final Map<String, Optional<String>> loadedModules = new ConcurrentHashMap<>();
public static Set<String> getLoadedModules() {
public static Map<String, Optional<String>> getLoadedModules() {
return loadedModules;
}
public static void loadModules() {
public static void loadLegacyModules() {
var moduleList = serviceClient.getLegacyModulesList();
moduleList.forEach(module -> {
var apk = module.apkPath;
var name = module.packageName;
var file = module.file;
loadedModules.add(apk); // temporarily add it for XSharedPreference
loadedModules.put(name, Optional.of(apk)); // temporarily add it for XSharedPreference
if (!loadModule(name, apk, file)) {
loadedModules.remove(apk);
loadedModules.remove(name);
}
});
}
public static void loadModules(ActivityThread at) {
serviceClient.getModulesList().forEach(module -> {
loadedModules.put(module.packageName, Optional.empty());
if (!LSPosedContext.loadModule(at, module)) {
loadedModules.remove(module.packageName);
}
});
}

View File

@ -68,7 +68,7 @@ public class Startup {
// Initialize the Xposed framework
try {
startBootstrapHook(XposedInit.startsSystemServer);
XposedInit.loadModules();
XposedInit.loadLegacyModules();
} catch (Throwable t) {
Utils.logE("error during Xposed initialization", t);
}

View File

@ -6,16 +6,14 @@ import android.app.ActivityThread;
import org.lsposed.lspd.impl.LSPosedContext;
import java.util.Optional;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedInit;
public class AttachHooker extends XC_MethodHook {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
serviceClient.getModulesList().forEach(module -> {
if (LSPosedContext.loadModule((ActivityThread) param.thisObject, module)) {
XposedInit.getLoadedModules().add(module.packageName);
}
});
protected void afterHookedMethod(MethodHookParam<?> param) throws Throwable {
XposedInit.loadModules((ActivityThread) param.thisObject);
}
}

View File

@ -7,7 +7,7 @@ import de.robv.android.xposed.XposedBridge;
public class CrashDumpHooker extends XC_MethodHook {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
protected void beforeHookedMethod(MethodHookParam<?> param) {
try {
var e = (Throwable) param.args[0];
XposedBridge.log("Crash unexpectedly: " + Log.getStackTraceString(e));

View File

@ -33,7 +33,7 @@ public class HandleSystemServerProcessHooker extends XC_MethodHook {
public static volatile ClassLoader systemServerCL;
@Override
protected void afterHookedMethod(MethodHookParam param) {
protected void afterHookedMethod(MethodHookParam<?> param) {
Hookers.logD("ZygoteInit#handleSystemServerProcess() starts");
try {
// get system_server classLoader

View File

@ -20,12 +20,15 @@
package org.lsposed.lspd.hooker;
import android.app.ActivityThread;
import android.app.LoadedApk;
import android.content.res.XResources;
import android.util.Log;
import org.lsposed.lspd.util.Hookers;
import java.util.Optional;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.XposedInit;
@ -34,13 +37,14 @@ import de.robv.android.xposed.XposedInit;
public class LoadedApkCtorHooker extends XC_MethodHook {
@Override
protected void afterHookedMethod(MethodHookParam param) {
protected void afterHookedMethod(MethodHookParam<?> param) {
Hookers.logD("LoadedApk#<init> starts");
try {
LoadedApk loadedApk = (LoadedApk) param.thisObject;
String packageName = loadedApk.getPackageName();
if (XposedInit.getLoadedModules().contains(packageName)) {
boolean isFirstPackage = packageName != null && ActivityThread.currentProcessName() != null && packageName.equals(ActivityThread.currentPackageName());
if (!isFirstPackage && !XposedInit.getLoadedModules().getOrDefault(packageName, Optional.of("")).isPresent()) {
return;
}
Object mAppDir = XposedHelpers.getObjectField(loadedApk, "mAppDir");
@ -77,7 +81,7 @@ public class LoadedApkCtorHooker extends XC_MethodHook {
return;
}
new LoadedApkGetCLHooker(loadedApk);
new LoadedApkGetCLHooker(loadedApk, isFirstPackage);
} catch (Throwable t) {
Hookers.logE("error when hooking LoadedApk.<init>", t);
}

View File

@ -40,11 +40,13 @@ import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.Optional;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XC_MethodReplacement;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.XposedInit;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
import io.github.libxposed.api.XposedModuleInterface;
@ -67,13 +69,16 @@ public class LoadedApkGetCLHooker extends XC_MethodHook {
private final LoadedApk loadedApk;
private final Unhook unhook;
public LoadedApkGetCLHooker(LoadedApk loadedApk) {
private final boolean isFirstPackage;
public LoadedApkGetCLHooker(LoadedApk loadedApk, boolean isFirstPackage) {
this.loadedApk = loadedApk;
this.isFirstPackage = isFirstPackage;
unhook = XposedHelpers.findAndHookMethod(LoadedApk.class, "getClassLoader", this);
}
@Override
protected void afterHookedMethod(MethodHookParam param) {
protected void afterHookedMethod(MethodHookParam<?> param) {
LoadedApk loadedApk = (LoadedApk) param.thisObject;
if (loadedApk != this.loadedApk) {
@ -83,15 +88,8 @@ public class LoadedApkGetCLHooker extends XC_MethodHook {
try {
Hookers.logD("LoadedApk#getClassLoader starts");
String packageName = ActivityThread.currentPackageName();
String processName = ActivityThread.currentProcessName();
boolean isFirstPackage = packageName != null && processName != null && packageName.equals(loadedApk.getPackageName());
if (!isFirstPackage) {
packageName = loadedApk.getPackageName();
processName = AndroidAppHelper.currentProcessName();
} else if (packageName.equals("android")) {
packageName = "system";
}
final String processName = AndroidAppHelper.currentProcessName();
final String packageName = isFirstPackage && "android".equals(ActivityThread.currentPackageName()) ? "system" : loadedApk.getPackageName();
Object mAppDir = XposedHelpers.getObjectField(loadedApk, "mAppDir");
ClassLoader classLoader = (ClassLoader) param.getResult();
@ -109,7 +107,9 @@ public class LoadedApkGetCLHooker extends XC_MethodHook {
lpparam.appInfo = loadedApk.getApplicationInfo();
lpparam.isFirstApplication = isFirstPackage;
hookNewXSP(lpparam);
if (isFirstPackage && XposedInit.getLoadedModules().getOrDefault(packageName, Optional.empty()).isPresent()) {
hookNewXSP(lpparam);
}
Hookers.logD("Call handleLoadedPackage: packageName=" + lpparam.packageName + " processName=" + lpparam.processName + " isFirstPackage=" + isFirstPackage + " classLoader=" + lpparam.classLoader + " appInfo=" + lpparam.appInfo);
XC_LoadPackage.callAll(lpparam);

View File

@ -9,7 +9,7 @@ import de.robv.android.xposed.XposedHelpers;
public class OpenDexFileHooker extends XC_MethodHook {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
protected void afterHookedMethod(MethodHookParam<?> param) throws Throwable {
ClassLoader classLoader = null;
for (var arg : param.args) {
if (arg instanceof ClassLoader) {

View File

@ -36,7 +36,7 @@ import io.github.libxposed.api.XposedModuleInterface;
public class StartBootstrapServicesHooker extends XC_MethodHook {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
protected void beforeHookedMethod(MethodHookParam<?> param) {
logD("SystemServer#startBootstrapServices() starts");
try {

View File

@ -98,7 +98,7 @@ public class ConfigManager {
private boolean verboseLog = true;
private boolean dexObfuscate = false;
private boolean enableStatusNotification = true;
private String miscPath = null;
private Path miscPath = null;
private int managerUid = -1;
@ -280,16 +280,15 @@ public class ConfigManager {
// Don't migrate to ConfigFileManager, as XSharedPreferences will be restored soon
String string = (String) config.get("misc_path");
if (string == null) {
miscPath = "/data/misc/" + UUID.randomUUID().toString();
updateModulePrefs("lspd", 0, "config", "misc_path", miscPath);
miscPath = Paths.get("/data", "misc", UUID.randomUUID().toString());
updateModulePrefs("lspd", 0, "config", "misc_path", miscPath.toString());
} else {
miscPath = string;
miscPath = Paths.get(string);
}
try {
Path prefs = Paths.get(miscPath);
var perms = PosixFilePermissions.fromString("rwx--x--x");
Files.createDirectories(prefs, PosixFilePermissions.asFileAttribute(perms));
walkFileTree(prefs, f -> SELinux.setFileContext(f.toString(), "u:object_r:magisk_file:s0"));
Files.createDirectories(miscPath, PosixFilePermissions.asFileAttribute(perms));
walkFileTree(miscPath, f -> SELinux.setFileContext(f.toString(), "u:object_r:magisk_file:s0"));
} catch (IOException e) {
Log.e(TAG, Log.getStackTraceString(e));
}
@ -1083,9 +1082,26 @@ public class ConfigManager {
return managerUid != -1;
}
public String getPrefsPath(String fileName, int uid) {
public String getPrefsPath(String packageName, int uid) {
int userId = uid / PER_USER_RANGE;
return miscPath + "/prefs" + (userId == 0 ? "" : String.valueOf(userId)) + "/" + fileName;
var path = miscPath.resolve("prefs" + (userId == 0 ? "" : String.valueOf(userId))).resolve(packageName);
var module = cachedModule.getOrDefault(packageName, null);
if (module != null && module.appId == uid % PER_USER_RANGE) {
try {
var perms = PosixFilePermissions.fromString("rwx--x--x");
Files.createDirectories(path, PosixFilePermissions.asFileAttribute(perms));
walkFileTree(path, p -> {
try {
Os.chown(p.toString(), uid, uid);
} catch (ErrnoException e) {
Log.e(TAG, Log.getStackTraceString(e));
}
});
} catch (IOException e) {
Log.e(TAG, Log.getStackTraceString(e));
}
}
return path.toString();
}
// this is slow, avoid using it