diff --git a/patch-loader/src/main/java/org/lsposed/lspatch/loader/LSPApplication.java b/patch-loader/src/main/java/org/lsposed/lspatch/loader/LSPApplication.java index 9b9aa52..eeb0591 100644 --- a/patch-loader/src/main/java/org/lsposed/lspatch/loader/LSPApplication.java +++ b/patch-loader/src/main/java/org/lsposed/lspatch/loader/LSPApplication.java @@ -6,40 +6,46 @@ import static org.lsposed.lspatch.share.Constants.ORIGINAL_APK_ASSET_PATH; import android.app.ActivityThread; import android.app.LoadedApk; import android.content.Context; +import android.content.SharedPreferences; import android.content.pm.ApplicationInfo; import android.content.res.CompatibilityInfo; import android.os.Build; import android.os.RemoteException; -import android.system.Os; import android.util.Log; +import com.google.gson.Gson; + +import org.json.JSONArray; +import org.json.JSONObject; import org.lsposed.lspatch.loader.util.FileUtils; import org.lsposed.lspatch.loader.util.XLog; import org.lsposed.lspatch.service.LocalApplicationService; import org.lsposed.lspatch.service.RemoteApplicationService; +import org.lsposed.lspatch.share.PatchConfig; import org.lsposed.lspd.core.Startup; +import org.lsposed.lspd.models.Module; import org.lsposed.lspd.service.ILSPApplicationService; -import org.json.JSONObject; import java.io.BufferedReader; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.lang.reflect.Array; import java.lang.reflect.Field; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.nio.file.attribute.PosixFilePermissions; import java.util.ArrayList; import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.function.BiConsumer; -import java.util.stream.Collectors; import java.util.zip.ZipFile; +import dalvik.system.DexFile; +import de.robv.android.xposed.XposedBridge; import de.robv.android.xposed.XposedHelpers; import hidden.HiddenApiBridge; @@ -57,7 +63,7 @@ public class LSPApplication { private static LoadedApk stubLoadedApk; private static LoadedApk appLoadedApk; - private static JSONObject config; + private static PatchConfig config; public static boolean isIsolated() { return (android.os.Process.myUid() % PER_USER_RANGE) >= FIRST_APP_ZYGOTE_ISOLATED_UID; @@ -77,29 +83,47 @@ public class LSPApplication { Log.d(TAG, "Initialize service client"); ILSPApplicationService service; - if (config.optBoolean("useManager")) { - service = new RemoteApplicationService(context); + if (config.useManager) { + try { + service = new RemoteApplicationService(context); + List m = service.getLegacyModulesList(); + JSONArray moduleArr = new JSONArray(); + for (Module module : m) { + JSONObject moduleObj = new JSONObject(); + moduleObj.put("path",module.apkPath); + moduleObj.put("packageName",module.packageName); + moduleArr.put(moduleObj); + } + SharedPreferences shared = context.getSharedPreferences("npatch", Context.MODE_PRIVATE); + shared.edit().putString("modules",moduleArr.toString()).commit(); + Log.e(TAG, "Success update module scope"); + }catch (Exception e){ + Log.e(TAG, "Failed to connect to manager, fallback to fixed local service"); + service = new LocalApplicationService(context); + } + } else { service = new LocalApplicationService(context); } - disableProfile(context); Startup.initXposed(false, ActivityThread.currentProcessName(), context.getApplicationInfo().dataDir, service); Startup.bootstrapXposed(); // WARN: Since it uses `XResource`, the following class should not be initialized // before forkPostCommon is invoke. Otherwise, you will get failure of XResources + Log.i(TAG, "Load modules"); LSPLoader.initModules(appLoadedApk); Log.i(TAG, "Modules initialized"); switchAllClassLoader(); - SigBypass.doSigBypass(context, config.optInt("sigBypassLevel")); + SigBypass.doSigBypass(context, config.sigBypassLevel); Log.i(TAG, "LSPatch bootstrap completed"); } private static Context createLoadedApkWithContext() { try { + var timeStart = System.currentTimeMillis(); var mBoundApplication = XposedHelpers.getObjectField(activityThread, "mBoundApplication"); stubLoadedApk = (LoadedApk) XposedHelpers.getObjectField(mBoundApplication, "info"); @@ -109,13 +133,13 @@ public class LSPApplication { try (var is = baseClassLoader.getResourceAsStream(CONFIG_ASSET_PATH)) { BufferedReader streamReader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)); - config = new JSONObject(streamReader.lines().collect(Collectors.joining())); - } catch (Throwable e) { - Log.e(TAG, "Failed to parse config file", e); + config = new Gson().fromJson(streamReader, PatchConfig.class); + } catch (IOException e) { + Log.e(TAG, "Failed to load config file"); return null; } - Log.i(TAG, "Use manager: " + config.optBoolean("useManager")); - Log.i(TAG, "Signature bypass level: " + config.optInt("sigBypassLevel")); + Log.i(TAG, "Use manager: " + config.useManager); + Log.i(TAG, "Signature bypass level: " + config.sigBypassLevel); Path originPath = Paths.get(appInfo.dataDir, "cache/lspatch/origin/"); Path cacheApkPath; @@ -125,9 +149,7 @@ public class LSPApplication { appInfo.sourceDir = cacheApkPath.toString(); appInfo.publicSourceDir = cacheApkPath.toString(); - if (config.has("appComponentFactory")) { - appInfo.appComponentFactory = config.optString("appComponentFactory"); - } + appInfo.appComponentFactory = config.appComponentFactory; if (!Files.exists(cacheApkPath)) { Log.i(TAG, "Extract original apk"); @@ -137,10 +159,13 @@ public class LSPApplication { Files.copy(is, cacheApkPath); } } + cacheApkPath.toFile().setWritable(false); + var mPackages = (Map) XposedHelpers.getObjectField(activityThread, "mPackages"); mPackages.remove(appInfo.packageName); appLoadedApk = activityThread.getPackageInfoNoCheck(appInfo, compatInfo); + XposedHelpers.setObjectField(mBoundApplication, "info", appLoadedApk); var activityClientRecordClass = XposedHelpers.findClass("android.app.ActivityThread$ActivityClientRecord", ActivityThread.class.getClassLoader()); @@ -165,14 +190,16 @@ public class LSPApplication { Log.i(TAG, "hooked app initialized: " + appLoadedApk); var context = (Context) XposedHelpers.callStaticMethod(Class.forName("android.app.ContextImpl"), "createAppContext", activityThread, stubLoadedApk); - if (config.has("appComponentFactory")) { + if (config.appComponentFactory != null) { try { - context.getClassLoader().loadClass(appInfo.appComponentFactory); - } catch (Throwable e) { // 捕捉更廣泛的類載入錯誤, 可能可以兼容部分加固如 360 - Log.w(TAG, "Original AppComponentFactory not found: " + appInfo.appComponentFactory, e); + context.getClassLoader().loadClass(config.appComponentFactory); + } catch (Throwable e) { + Log.w(TAG, "Original AppComponentFactory not found: " + config.appComponentFactory, e); appInfo.appComponentFactory = null; } } + Log.i(TAG,"createLoadedApkWithContext cost: " + (System.currentTimeMillis() - timeStart) + "ms"); + return context; } catch (Throwable e) { Log.e(TAG, "createLoadedApk", e); @@ -180,55 +207,6 @@ public class LSPApplication { } } - public static void disableProfile(Context context) { - final ArrayList codePaths = new ArrayList<>(); - var appInfo = context.getApplicationInfo(); - var pkgName = context.getPackageName(); - if (appInfo == null) return; - if ((appInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) { - codePaths.add(appInfo.sourceDir); - } - if (appInfo.splitSourceDirs != null) { - Collections.addAll(codePaths, appInfo.splitSourceDirs); - } - - if (codePaths.isEmpty()) { - // If there are no code paths there's no need to setup a profile file and register with - // the runtime, - return; - } - - var profileDir = HiddenApiBridge.Environment_getDataProfilesDePackageDirectory(appInfo.uid / PER_USER_RANGE, pkgName); - - var attrs = PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("r--------")); - - for (int i = codePaths.size() - 1; i >= 0; i--) { - String splitName = i == 0 ? null : appInfo.splitNames[i - 1]; - File curProfileFile = new File(profileDir, splitName == null ? "primary.prof" : splitName + ".split.prof").getAbsoluteFile(); - Log.d(TAG, "Processing " + curProfileFile.getAbsolutePath()); - try { - if (!curProfileFile.exists()) { - Files.createFile(curProfileFile.toPath(), attrs); - continue; - } - if (!curProfileFile.canWrite() && Files.size(curProfileFile.toPath()) == 0) { - Log.d(TAG, "Skip profile " + curProfileFile.getAbsolutePath()); - continue; - } - if (curProfileFile.exists() && !curProfileFile.delete()) { - try (var writer = new FileOutputStream(curProfileFile)) { - Log.d(TAG, "Failed to delete, try to clear content " + curProfileFile.getAbsolutePath()); - } catch (Throwable e) { - Log.e(TAG, "Failed to delete and clear profile file " + curProfileFile.getAbsolutePath(), e); - } - Os.chmod(curProfileFile.getAbsolutePath(), 00400); - } - } catch (Throwable e) { - Log.e(TAG, "Failed to disable profile file " + curProfileFile.getAbsolutePath(), e); - } - } - } - private static void switchAllClassLoader() { var fields = LoadedApk.class.getDeclaredFields(); for (Field field : fields) { diff --git a/patch-loader/src/main/java/org/lsposed/lspatch/service/LocalApplicationService.java b/patch-loader/src/main/java/org/lsposed/lspatch/service/LocalApplicationService.java index 7c74f37..adfcfd6 100644 --- a/patch-loader/src/main/java/org/lsposed/lspatch/service/LocalApplicationService.java +++ b/patch-loader/src/main/java/org/lsposed/lspatch/service/LocalApplicationService.java @@ -1,84 +1,75 @@ package org.lsposed.lspatch.service; import android.content.Context; +import android.content.SharedPreferences; +import android.content.pm.ApplicationInfo; import android.os.Environment; import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.util.Log; -import org.lsposed.lspatch.loader.util.FileUtils; -import org.lsposed.lspatch.share.Constants; +import org.json.JSONArray; +import org.json.JSONObject; import org.lsposed.lspatch.util.ModuleLoader; import org.lsposed.lspd.models.Module; import org.lsposed.lspd.service.ILSPApplicationService; import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; -import java.util.zip.ZipFile; public class LocalApplicationService extends ILSPApplicationService.Stub { - private static final String TAG = "NPatch"; - - private final List modules = new ArrayList<>(); - - public LocalApplicationService(Context context) { + private final List cachedModule; + public LocalApplicationService(Context context){ + SharedPreferences shared = context.getSharedPreferences("npatch", Context.MODE_PRIVATE); + cachedModule = new ArrayList<>(); try { - for (var name : context.getAssets().list("lspatch/modules")) { - String packageName = name.substring(0, name.length() - 4); - String modulePath = context.getCacheDir() + "/lspatch/" + packageName + "/"; - String cacheApkPath; - try (ZipFile sourceFile = new ZipFile(context.getPackageResourcePath())) { - cacheApkPath = modulePath + sourceFile.getEntry(Constants.EMBEDDED_MODULES_ASSET_PATH + name).getCrc() + ".apk"; - } - - if (!Files.exists(Paths.get(cacheApkPath))) { - Log.i(TAG, "Extract module apk: " + packageName); - FileUtils.deleteFolderIfExists(Paths.get(modulePath)); - Files.createDirectories(Paths.get(modulePath)); - try (var is = context.getAssets().open("lspatch/modules/" + name)) { - Files.copy(is, Paths.get(cacheApkPath)); + JSONArray mArr = new JSONArray(shared.getString("modules", "{}")); + Log.i(TAG,"use fixed local application service:"+shared.getString("modules", "{}")); + for (int i = 0; i < mArr.length(); i++) { + JSONObject mObj = mArr.getJSONObject(i); + Module m = new Module(); + String path = mObj.getString("path"); + String packageName = mObj.getString("packageName"); + m.apkPath = path; + m.packageName = packageName; + if (!new File(m.apkPath).exists()){ + Log.i("NPatch","Module:" + m.packageName + " path not available, reset."); + try { + ApplicationInfo info = context.getPackageManager().getApplicationInfo(m.packageName, 0); + m.apkPath = info.sourceDir; + Log.i("NPatch","Module:" + m.packageName + " path reset to " + m.apkPath); + }catch (Exception e){ + Log.e("NPatch",Log.getStackTraceString(e)); } } - - var module = new Module(); - module.apkPath = cacheApkPath; - module.packageName = packageName; - module.file = ModuleLoader.loadModule(cacheApkPath); - modules.add(module); + m.file = ModuleLoader.loadModule(m.apkPath); + cachedModule.add(m); } - } catch (IOException e) { - Log.e(TAG, "Error when initializing LocalApplicationServiceClient", e); + }catch (Exception e){ + Log.e(TAG,Log.getStackTraceString(e)); } } @Override - public boolean isLogMuted() throws RemoteException { - return false; + public List getLegacyModulesList() throws RemoteException { + return cachedModule; } @Override - public List getLegacyModulesList() { - return modules; - } - - @Override - public List getModulesList() { + public List getModulesList() throws RemoteException { return new ArrayList<>(); } @Override - public String getPrefsPath(String packageName) { + public String getPrefsPath(String packageName) throws RemoteException { return new File(Environment.getDataDirectory(), "data/" + packageName + "/shared_prefs/").getAbsolutePath(); } @Override - public ParcelFileDescriptor requestInjectedManagerBinder(List binder) { + public ParcelFileDescriptor requestInjectedManagerBinder(List binder) throws RemoteException { return null; } @@ -86,4 +77,9 @@ public class LocalApplicationService extends ILSPApplicationService.Stub { public IBinder asBinder() { return this; } + + @Override + public boolean isLogMuted() throws RemoteException { + return false; + } } diff --git a/patch-loader/src/main/java/org/lsposed/lspatch/service/RemoteApplicationService.java b/patch-loader/src/main/java/org/lsposed/lspatch/service/RemoteApplicationService.java index c6c1bd5..fd64f6a 100644 --- a/patch-loader/src/main/java/org/lsposed/lspatch/service/RemoteApplicationService.java +++ b/patch-loader/src/main/java/org/lsposed/lspatch/service/RemoteApplicationService.java @@ -14,7 +14,6 @@ import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.UserHandle; import android.util.Log; -import android.widget.Toast; import org.lsposed.lspatch.share.Constants; import org.lsposed.lspd.models.Module; @@ -48,7 +47,7 @@ public class RemoteApplicationService implements ILSPApplicationService { @Override public void onServiceConnected(ComponentName name, IBinder binder) { Log.i(TAG, "Manager binder received"); - service = Stub.asInterface(binder); + service = ILSPApplicationService.Stub.asInterface(binder); latch.countDown(); } @@ -76,7 +75,6 @@ public class RemoteApplicationService implements ILSPApplicationService { if (!success) throw new TimeoutException("Bind service timeout"); } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InterruptedException | TimeoutException e) { - Toast.makeText(context, "Unable to connect to Manager", Toast.LENGTH_SHORT).show(); var r = new RemoteException("Failed to get manager binder"); r.initCause(e); throw r;