Add ManagerResolver

This commit is contained in:
Nullptr 2021-09-05 13:33:01 +08:00 committed by LoveSy
parent cdf341b856
commit 919e44de56
10 changed files with 125 additions and 96 deletions

View File

@ -84,4 +84,5 @@ dependencies {
implementation project(path: ':hiddenapi-bridge')
compileOnly project(":hiddenapi-stubs")
implementation project(':share')
implementation project(':imanager')
}

View File

@ -8,21 +8,19 @@ import android.app.LoadedApk;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.os.SharedMemory;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
import android.util.Log;
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.share.Constants;
@ -33,9 +31,7 @@ import org.lsposed.lspd.models.PreLoadedApk;
import org.lsposed.lspd.nativebridge.SigBypass;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@ -44,15 +40,13 @@ import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.channels.Channels;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.zip.ZipFile;
import de.robv.android.xposed.XC_MethodHook;
@ -67,11 +61,14 @@ import hidden.HiddenApiBridge;
public class LSPApplication extends ApplicationServiceClient {
private static final String ORIGINAL_APPLICATION_NAME_ASSET_PATH = "original_application_name.ini";
private static final String ORIGINAL_SIGNATURE_ASSET_PATH = "original_signature_info.ini";
private static final String USE_MANAGER_CONTROL_PATH = "use_manager.ini";
private static final String TAG = "LSPatch";
private static boolean useManager;
private static String originalApplicationName = null;
private static String originalSignature = null;
private static Application sOriginalApplication = null;
private static ManagerResolver managerResolver = null;
private static ClassLoader appClassLoader;
private static Object activityThread;
@ -99,9 +96,16 @@ public class LSPApplication extends ApplicationServiceClient {
return;
}
useManager = Boolean.parseBoolean(Objects.requireNonNull(FileUtils.readTextFromAssets(context, USE_MANAGER_CONTROL_PATH)));
originalApplicationName = FileUtils.readTextFromAssets(context, ORIGINAL_APPLICATION_NAME_ASSET_PATH);
originalSignature = FileUtils.readTextFromAssets(context, ORIGINAL_SIGNATURE_ASSET_PATH);
if (useManager) try {
managerResolver = new ManagerResolver(context);
} catch (RemoteException e) {
Log.e(TAG, "Failed to instantiate manager resolver", e);
}
XLog.d(TAG, "original application class " + originalApplicationName);
XLog.d(TAG, "original signature info " + originalSignature);
@ -169,98 +173,40 @@ public class LSPApplication extends ApplicationServiceClient {
}
// TODO: set module config
public static void loadModules(Context context) {
var configFile = new File(context.getExternalFilesDir(null), "lspatch.json");
JSONObject moduleConfigs = new JSONObject();
try (var is = new FileInputStream(configFile)) {
moduleConfigs = new JSONObject(FileUtils.readTextFromInputStream(is));
} catch (Throwable ignored) {
}
var modules = moduleConfigs.optJSONArray("modules");
if (modules == null) {
modules = new JSONArray();
if (useManager) {
try {
moduleConfigs.put("modules", modules);
} catch (Throwable ignored) {
LSPApplication.modules.addAll(managerResolver.getModules());
} catch (NullPointerException | RemoteException e) {
Log.e(TAG, "Failed to get modules from manager", e);
}
}
HashSet<String> embedded_modules = new HashSet<>();
HashSet<String> disabled_modules = new HashSet<>();
try {
for (var name : context.getAssets().list("modules")) {
String packageName = name.substring(0, name.length() - 4);
String modulePath = context.getCacheDir() + "/lspatch/" + packageName + "/";
String cacheApkPath;
try (ZipFile sourceFile = new ZipFile(context.getApplicationInfo().sourceDir)) {
cacheApkPath = modulePath + sourceFile.getEntry("assets/modules/" + name).getCrc();
}
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("modules/" + name)) {
Files.copy(is, Paths.get(cacheApkPath));
} else {
try {
for (var name : context.getAssets().list("modules")) {
String packageName = name.substring(0, name.length() - 4);
String modulePath = context.getCacheDir() + "/lspatch/" + packageName + "/";
String cacheApkPath;
try (ZipFile sourceFile = new ZipFile(context.getApplicationInfo().sourceDir)) {
cacheApkPath = modulePath + sourceFile.getEntry("assets/modules/" + name).getCrc();
}
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("modules/" + name)) {
Files.copy(is, Paths.get(cacheApkPath));
}
}
var module = new Module();
module.apkPath = cacheApkPath;
module.packageName = packageName;
module.file = loadModule(context, cacheApkPath);
modules.add(module);
}
embedded_modules.add(packageName);
var module = new Module();
module.apkPath = cacheApkPath;
module.packageName = packageName;
LSPApplication.modules.add(module);
}
} catch (Throwable ignored) {
}
for (int i = 0; i < modules.length(); ++i) {
var module = modules.optJSONObject(i);
var name = module.optString("name");
var enabled = module.optBoolean("enabled", true);
var useEmbed = module.optBoolean("use_embed", false);
if (name.isEmpty()) continue;
if (!enabled) disabled_modules.add(name);
if (embedded_modules.contains(name) && !useEmbed) embedded_modules.remove(name);
}
for (PackageInfo pkg : context.getPackageManager().getInstalledPackages(PackageManager.GET_META_DATA)) {
ApplicationInfo app = pkg.applicationInfo;
if (!app.enabled) {
continue;
}
if (app.metaData != null && app.metaData.containsKey("xposedminversion") && !embedded_modules.contains(app.packageName)) {
var module = new Module();
module.apkPath = app.publicSourceDir;
module.packageName = app.packageName;
LSPApplication.modules.add(module);
}
}
final var new_modules = new JSONArray();
LSPApplication.modules.forEach(m -> {
try {
m.file = loadModule(context, m.apkPath);
var module = new JSONObject();
module.put("name", m.packageName);
module.put("enabled", !disabled_modules.contains(m.packageName));
module.put("use_embed", embedded_modules.contains(m.packageName));
module.put("path", m.apkPath);
new_modules.put(module);
} catch (Throwable ignored) {
}
});
try {
moduleConfigs.put("modules", new_modules);
} catch (Throwable ignored) {
}
try (var is = new ByteArrayInputStream(moduleConfigs.toString(4).getBytes(StandardCharsets.UTF_8))) {
Files.copy(is, configFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
} catch (Throwable ignored) {
}
for (var module : disabled_modules) {
LSPApplication.modules.remove(module);
}
}

View File

@ -0,0 +1,35 @@
package org.lsposed.lspatch.loader;
import android.content.ContentResolver;
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import android.os.RemoteException;
import org.lsposed.lspatch.manager.IManagerService;
import org.lsposed.lspd.models.Module;
import java.util.List;
public class ManagerResolver extends ContentResolver {
private static final String MANAGER_PACKAGE_NAME = "org.lsposed.lspatch";
private static final Uri BINDER_URI = Uri.parse("content://" + MANAGER_PACKAGE_NAME + "/binder");
private final IManagerService service;
public ManagerResolver(Context context) throws RemoteException {
super(context);
try {
Bundle back = call(BINDER_URI, "getBinder", null, null);
service = IManagerService.Stub.asInterface(back.getBinder("binder"));
} catch (Throwable t) {
var e = new RemoteException("Failed to get manager binder");
e.addSuppressed(t);
throw e;
}
}
public List<Module> getModules() throws RemoteException {
return service.getModules();
}
}

2
core

@ -1 +1 @@
Subproject commit a2e57ddfe9477933e6540ce9dad4c1dac1991d32
Subproject commit f955755ac40134f1b0a858f4e7df3b4a4eec78a8

1
imanager/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/build

26
imanager/build.gradle Normal file
View File

@ -0,0 +1,26 @@
plugins {
id 'com.android.library'
}
android {
compileSdk 31
defaultConfig {
minSdk 27
targetSdk 31
}
buildTypes {
release {
minifyEnabled true
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation project(path: ':lspcore')
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="org.lsposed.lspatch.manager" />

View File

@ -0,0 +1,7 @@
package org.lsposed.lspatch.manager;
import org.lsposed.lspd.models.Module;
interface IManagerService {
List<Module> getModules();
}

View File

@ -78,6 +78,9 @@ public class LSPatch {
@Parameter(names = {"--v3"}, arity = 1, description = "Sign with v3 signature")
private boolean v3 = true;
@Parameter(names = {"--manager"}, arity = 1, description = "Whether use manager (Cannot be true when has module embedded)")
private boolean useManager = false;
@Parameter(names = {"-v", "--verbose"}, description = "Verbose output")
private boolean verbose = false;
@ -90,6 +93,7 @@ public class LSPatch {
private static final String APP_COMPONENT_FACTORY_ASSET_PATH = "assets/original_app_component_factory.ini";
private static final String APPLICATION_NAME_ASSET_PATH = "assets/original_application_name.ini";
private static final String SIGNATURE_INFO_ASSET_PATH = "assets/original_signature_info.ini";
private static final String USE_MANAGER_CONTROL_PATH = "assets/use_manager.ini";
private static final String ORIGINAL_APK_ASSET_PATH = "assets/origin_apk.bin";
private static final String ANDROID_MANIFEST_XML = "AndroidManifest.xml";
private static final HashSet<String> ARCHES = new HashSet<>(Arrays.asList(
@ -135,6 +139,11 @@ public class LSPatch {
return;
}
if (!modules.isEmpty() && useManager) {
jCommander.usage();
return;
}
for (var apk : apkPaths) {
File srcApkFile = new File(apk).getAbsoluteFile();
@ -274,8 +283,9 @@ public class LSPatch {
// save lspatch config to asset..
try (var is = new ByteArrayInputStream("42".getBytes(StandardCharsets.UTF_8))) {
dstZFile.add("assets/" + Constants.CONFIG_NAME_SIGBYPASSLV + sigbypassLevel, is);
} catch (Throwable e) {
throw new PatchError("Error when saving signature bypass level", e);
}
try (var is = new ByteArrayInputStream(Boolean.toString(useManager).getBytes(StandardCharsets.UTF_8))){
dstZFile.add(USE_MANAGER_CONTROL_PATH, is);
}
Set<String> apkArchs = new HashSet<>();

View File

@ -15,3 +15,4 @@ include ':axmlprinter'
include ':share'
include ':appstub'
include ':apkzlib'
include ':imanager'