Fix module loading (#2418)
This commit is contained in:
parent
55afed974d
commit
516a4aa99d
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue