new parasitic manager
This commit is contained in:
parent
9125b4b2f1
commit
41eb1c63ad
|
|
@ -34,7 +34,12 @@ import io.github.libxposed.api.annotations.XposedHooker;
|
||||||
@XposedHooker
|
@XposedHooker
|
||||||
public class HandleSystemServerProcessHooker implements XposedInterface.Hooker {
|
public class HandleSystemServerProcessHooker implements XposedInterface.Hooker {
|
||||||
|
|
||||||
|
public interface Callback {
|
||||||
|
void onSystemServerLoaded(ClassLoader classLoader);
|
||||||
|
}
|
||||||
|
|
||||||
public static volatile ClassLoader systemServerCL;
|
public static volatile ClassLoader systemServerCL;
|
||||||
|
public static volatile Callback callback = null;
|
||||||
|
|
||||||
@SuppressLint("PrivateApi")
|
@SuppressLint("PrivateApi")
|
||||||
@AfterInvocation
|
@AfterInvocation
|
||||||
|
|
@ -47,6 +52,7 @@ public class HandleSystemServerProcessHooker implements XposedInterface.Hooker {
|
||||||
PrebuiltMethodsDeopter.deoptSystemServerMethods(systemServerCL);
|
PrebuiltMethodsDeopter.deoptSystemServerMethods(systemServerCL);
|
||||||
var clazz = Class.forName("com.android.server.SystemServer", false, systemServerCL);
|
var clazz = Class.forName("com.android.server.SystemServer", false, systemServerCL);
|
||||||
LSPosedHelper.hookAllMethods(StartBootstrapServicesHooker.class, clazz, "startBootstrapServices");
|
LSPosedHelper.hookAllMethods(StartBootstrapServicesHooker.class, clazz, "startBootstrapServices");
|
||||||
|
if (callback != null) callback.onSystemServerLoaded(systemServerCL);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
Hookers.logE("error when hooking systemMain", t);
|
Hookers.logE("error when hooking systemMain", t);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -206,9 +206,11 @@ public class ConfigManager {
|
||||||
Log.e(TAG, "skip injecting into android because sepolicy was not loaded properly");
|
Log.e(TAG, "skip injecting into android because sepolicy was not loaded properly");
|
||||||
return true; // skip
|
return true; // skip
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
try (Cursor cursor = db.query("scope INNER JOIN modules ON scope.mid = modules.mid", new String[]{"modules.mid"}, "app_pkg_name=? AND enabled=1", new String[]{"system"}, null, null, null)) {
|
try (Cursor cursor = db.query("scope INNER JOIN modules ON scope.mid = modules.mid", new String[]{"modules.mid"}, "app_pkg_name=? AND enabled=1", new String[]{"system"}, null, null, null)) {
|
||||||
return cursor == null || !cursor.moveToNext();
|
return cursor == null || !cursor.moveToNext();
|
||||||
}
|
}*/
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("BlockedPrivateApi")
|
@SuppressLint("BlockedPrivateApi")
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ package org.lsposed.lspd.service;
|
||||||
|
|
||||||
import static android.content.Context.BIND_AUTO_CREATE;
|
import static android.content.Context.BIND_AUTO_CREATE;
|
||||||
import static org.lsposed.lspd.service.ServiceManager.TAG;
|
import static org.lsposed.lspd.service.ServiceManager.TAG;
|
||||||
import static org.lsposed.lspd.service.ServiceManager.getExecutorService;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.IServiceConnection;
|
import android.app.IServiceConnection;
|
||||||
|
|
@ -59,7 +58,6 @@ import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import hidden.HiddenApiBridge;
|
import hidden.HiddenApiBridge;
|
||||||
|
|
@ -67,10 +65,9 @@ import io.github.libxposed.service.IXposedService;
|
||||||
import rikka.parcelablelist.ParcelableListSlice;
|
import rikka.parcelablelist.ParcelableListSlice;
|
||||||
|
|
||||||
public class LSPManagerService extends ILSPManagerService.Stub {
|
public class LSPManagerService extends ILSPManagerService.Stub {
|
||||||
// this maybe useful when obtaining the manager binder
|
|
||||||
private static String RANDOM_UUID = null;
|
|
||||||
|
|
||||||
private static Intent managerIntent = null;
|
private static Intent managerIntent = null;
|
||||||
|
private boolean enabled = true;
|
||||||
|
|
||||||
public class ManagerGuard implements IBinder.DeathRecipient {
|
public class ManagerGuard implements IBinder.DeathRecipient {
|
||||||
private final @NonNull
|
private final @NonNull
|
||||||
|
|
@ -163,7 +160,11 @@ public class LSPManagerService extends ILSPManagerService.Stub {
|
||||||
if (intent == null) return;
|
if (intent == null) return;
|
||||||
intent = new Intent(intent);
|
intent = new Intent(intent);
|
||||||
intent.setData(withData);
|
intent.setData(withData);
|
||||||
ServiceManager.getManagerService().preStartManager(BuildConfig.MANAGER_INJECTED_PKG_NAME, intent, true);
|
try {
|
||||||
|
ActivityManagerService.startActivityAsUserWithFeature("android", null, intent, intent.getType(), null, null, 0, 0, null, null, 0);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.e(TAG, "failed to open manager");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("WrongConstant")
|
@SuppressLint("WrongConstant")
|
||||||
|
|
@ -188,11 +189,6 @@ public class LSPManagerService extends ILSPManagerService.Stub {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ManagerGuard guardSnapshot() {
|
|
||||||
var snapshot = guard;
|
|
||||||
return snapshot != null && snapshot.isAlive() ? snapshot : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ensureWebViewPermission(File f) {
|
private void ensureWebViewPermission(File f) {
|
||||||
if (!f.exists()) return;
|
if (!f.exists()) return;
|
||||||
SELinux.setFileContext(f.getAbsolutePath(), "u:object_r:magisk_file:s0");
|
SELinux.setFileContext(f.getAbsolutePath(), "u:object_r:magisk_file:s0");
|
||||||
|
|
@ -222,136 +218,46 @@ public class LSPManagerService extends ILSPManagerService.Stub {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// To start injected manager, we should take care about conflict
|
synchronized boolean preStartManager() {
|
||||||
// with the target app since we won't inject into it
|
pendingManager = true;
|
||||||
// if we are not going to display manager.
|
managerPid = -1;
|
||||||
// Thus, when someone launching manager, we should no matter
|
|
||||||
// stop any process of the target app
|
|
||||||
// Ideally we should call force stop package here,
|
|
||||||
// however it's not feasible because it will cause deadlock
|
|
||||||
// Thus we will cancel the launch of the activity
|
|
||||||
// and manually start activity with force stopping
|
|
||||||
// However, the intent we got here is not complete since
|
|
||||||
// there's no extras. We cannot do the same thing
|
|
||||||
// where starting the target app while the manager is
|
|
||||||
// still running.
|
|
||||||
// We instead let the manager to restart the activity.
|
|
||||||
synchronized boolean preStartManager(String pkgName, Intent intent, boolean doResume) {
|
|
||||||
// first, check if it's our target app, if not continue the start
|
|
||||||
if (BuildConfig.MANAGER_INJECTED_PKG_NAME.equals(pkgName)) {
|
|
||||||
Log.d(TAG, "starting target app of parasitic manager");
|
|
||||||
// check if it's launching our manager
|
|
||||||
if (intent.getCategories() != null &&
|
|
||||||
intent.getCategories().contains("org.lsposed.manager.LAUNCH_MANAGER")) {
|
|
||||||
Log.d(TAG, "requesting launch of manager");
|
|
||||||
// a new launch for the manager
|
|
||||||
// check if there's one running
|
|
||||||
// or it's run by ourselves after force stopping
|
|
||||||
var snapshot = guardSnapshot();
|
|
||||||
if ((RANDOM_UUID != null && intent.getCategories().contains(RANDOM_UUID)) ||
|
|
||||||
(snapshot != null && snapshot.isAlive() && snapshot.uid == BuildConfig.MANAGER_INJECTED_UID)) {
|
|
||||||
Log.d(TAG, "manager is still running or is on its way");
|
|
||||||
// there's one running parasitic manager
|
|
||||||
// or it's run by ourself after killing, resume it
|
|
||||||
if (doResume) {
|
|
||||||
// if doResume is true, we help do the resumption
|
|
||||||
try {
|
|
||||||
ActivityManagerService.startActivityAsUserWithFeature("android", null, intent, intent.getType(), null, null, 0, 0, null, null, 0);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Log.w(TAG, "resume manager", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} else if (pendingManager) {
|
|
||||||
// Check the flag in case new launch comes before finishing
|
|
||||||
// the previous one to avoid racing.
|
|
||||||
Log.d(TAG, "manager is still on its way when new launch comes, skipping");
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
// new parasitic manager launch, set the flag and kill
|
|
||||||
// old processes
|
|
||||||
// we do it by cancelling the launch (return false)
|
|
||||||
// and start activity in a new thread
|
|
||||||
pendingManager = true;
|
|
||||||
getExecutorService().submit(() -> {
|
|
||||||
ensureWebViewPermission();
|
|
||||||
stopAndStartActivity(pkgName, intent, true);
|
|
||||||
});
|
|
||||||
Log.d(TAG, "requested to launch manager");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (pendingManager) {
|
|
||||||
// there's still parasitic manager, cancel a normal launch until
|
|
||||||
// the parasitic manager is launch
|
|
||||||
Log.d(TAG, "previous request is not yet done");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// this is a normal launch of the target app
|
|
||||||
// send it to the manager and let it to restart the package
|
|
||||||
// if the manager is running
|
|
||||||
// or normally restart without injecting
|
|
||||||
Log.d(TAG, "launching the target app normally");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized void stopAndStartActivity(String packageName, Intent intent, boolean addUUID) {
|
|
||||||
try {
|
|
||||||
ActivityManagerService.forceStopPackage(packageName, 0);
|
|
||||||
Log.d(TAG, "stopped old package");
|
|
||||||
if (addUUID) {
|
|
||||||
intent = new Intent(intent);
|
|
||||||
RANDOM_UUID = UUID.randomUUID().toString();
|
|
||||||
intent.addCategory(RANDOM_UUID);
|
|
||||||
}
|
|
||||||
ActivityManagerService.startActivityAsUserWithFeature("android", null, intent, intent.getType(), null, null, 0, 0, null, null, 0);
|
|
||||||
Log.d(TAG, "relaunching");
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
Log.e(TAG, "stop and start activity", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// return true to inject manager
|
// return true to inject manager
|
||||||
synchronized boolean shouldStartManager(int pid, int uid, String processName) {
|
synchronized boolean shouldStartManager(int pid, int uid, String processName) {
|
||||||
if (uid != BuildConfig.MANAGER_INJECTED_UID || !BuildConfig.MANAGER_INJECTED_PKG_NAME.equals(processName) || !pendingManager)
|
if (!enabled || uid != BuildConfig.MANAGER_INJECTED_UID || !BuildConfig.DEFAULT_MANAGER_PACKAGE_NAME.equals(processName) || !pendingManager)
|
||||||
return false;
|
return false;
|
||||||
// pending parasitic manager launch it processes
|
|
||||||
// now we have its pid so we allow it to be killed
|
|
||||||
// and thus reset the pending flag and mark its pid
|
|
||||||
pendingManager = false;
|
pendingManager = false;
|
||||||
managerPid = pid;
|
managerPid = pid;
|
||||||
Log.d(TAG, "starting injected manager: pid = " + pid + " uid = " + uid + " processName = " + processName);
|
Log.d(TAG, "starting injected manager: pid = " + pid + " uid = " + uid + " processName = " + processName);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
synchronized boolean setEnabled(boolean newValue) {
|
||||||
|
enabled = newValue;
|
||||||
|
Log.i(TAG, "manager enabled = " + enabled);
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
|
||||||
// return true to send manager binder
|
// return true to send manager binder
|
||||||
synchronized boolean postStartManager(int pid, int uid) {
|
boolean postStartManager(int pid, int uid) {
|
||||||
if (pid == managerPid && uid == BuildConfig.MANAGER_INJECTED_UID) {
|
return enabled && uid == BuildConfig.MANAGER_INJECTED_UID && pid == managerPid;
|
||||||
RANDOM_UUID = null;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NonNull
|
public @NonNull
|
||||||
IBinder obtainManagerBinder(@NonNull IBinder heartbeat, int pid, int uid) {
|
IBinder obtainManagerBinder(@NonNull IBinder heartbeat, int pid, int uid) {
|
||||||
new ManagerGuard(heartbeat, pid, uid);
|
new ManagerGuard(heartbeat, pid, uid);
|
||||||
if (postStartManager(pid, uid)) {
|
if (uid == BuildConfig.MANAGER_INJECTED_UID)
|
||||||
managerPid = 0;
|
ensureWebViewPermission();
|
||||||
}
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isRunningManager(int pid, int uid) {
|
public boolean isRunningManager(int pid, int uid) {
|
||||||
var snapshotPid = managerPid;
|
return false;
|
||||||
var snapshotGuard = guardSnapshot();
|
|
||||||
return (pid == snapshotPid && uid == BuildConfig.MANAGER_INJECTED_UID) || (snapshotGuard != null && snapshotGuard.pid == pid && snapshotGuard.uid == uid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void onSystemServerDied() {
|
void onSystemServerDied() {
|
||||||
pendingManager = false;
|
|
||||||
managerPid = 0;
|
|
||||||
guard = null;
|
guard = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -564,8 +470,6 @@ public class LSPManagerService extends ILSPManagerService.Stub {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void restartFor(Intent intent) throws RemoteException {
|
public void restartFor(Intent intent) throws RemoteException {
|
||||||
forceStopPackage(BuildConfig.MANAGER_INJECTED_PKG_NAME, 0);
|
|
||||||
stopAndStartActivity(BuildConfig.MANAGER_INJECTED_PKG_NAME, intent, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -221,12 +221,6 @@ public class LSPosedService extends ILSPosedService.Stub {
|
||||||
|
|
||||||
private void dispatchBootCompleted(Intent intent) {
|
private void dispatchBootCompleted(Intent intent) {
|
||||||
bootCompleted = true;
|
bootCompleted = true;
|
||||||
try {
|
|
||||||
var am = ActivityManagerService.getActivityManager();
|
|
||||||
if (am != null) am.setActivityController(null, false);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
Log.e(TAG, "setActivityController", e);
|
|
||||||
}
|
|
||||||
var configManager = ConfigManager.getInstance();
|
var configManager = ConfigManager.getInstance();
|
||||||
if (configManager.enableStatusNotification()) {
|
if (configManager.enableStatusNotification()) {
|
||||||
LSPNotificationManager.notifyStatusNotification();
|
LSPNotificationManager.notifyStatusNotification();
|
||||||
|
|
@ -463,8 +457,12 @@ public class LSPosedService extends ILSPosedService.Stub {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean preStartManager(String pkgName, Intent intent) {
|
public boolean preStartManager() {
|
||||||
Log.d(TAG, "checking manager intent");
|
return ServiceManager.getManagerService().preStartManager();
|
||||||
return ServiceManager.getManagerService().preStartManager(pkgName, intent, false);
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setManagerEnabled(boolean enabled) throws RemoteException {
|
||||||
|
return ServiceManager.getManagerService().setEnabled(enabled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,15 +24,18 @@ import android.os.Process;
|
||||||
|
|
||||||
import org.lsposed.lspd.service.ILSPApplicationService;
|
import org.lsposed.lspd.service.ILSPApplicationService;
|
||||||
import org.lsposed.lspd.util.ParasiticManagerHooker;
|
import org.lsposed.lspd.util.ParasiticManagerHooker;
|
||||||
|
import org.lsposed.lspd.util.ParasiticManagerSystemHooker;
|
||||||
import org.lsposed.lspd.util.Utils;
|
import org.lsposed.lspd.util.Utils;
|
||||||
import org.lsposed.lspd.BuildConfig;
|
import org.lsposed.lspd.BuildConfig;
|
||||||
|
|
||||||
public class Main {
|
public class Main {
|
||||||
|
|
||||||
public static void forkCommon(boolean isSystem, String niceName, String appDir, IBinder binder) {
|
public static void forkCommon(boolean isSystem, String niceName, String appDir, IBinder binder) {
|
||||||
|
if (isSystem) {
|
||||||
|
ParasiticManagerSystemHooker.start();
|
||||||
|
}
|
||||||
Startup.initXposed(isSystem, niceName, appDir, ILSPApplicationService.Stub.asInterface(binder));
|
Startup.initXposed(isSystem, niceName, appDir, ILSPApplicationService.Stub.asInterface(binder));
|
||||||
if ((niceName.equals(BuildConfig.MANAGER_INJECTED_PKG_NAME) || niceName.equals(BuildConfig.DEFAULT_MANAGER_PACKAGE_NAME))
|
if (niceName.equals(BuildConfig.DEFAULT_MANAGER_PACKAGE_NAME) && ParasiticManagerHooker.start()) {
|
||||||
&& ParasiticManagerHooker.start()) {
|
|
||||||
Utils.logI("Loaded manager, skipping next steps");
|
Utils.logI("Loaded manager, skipping next steps");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,260 +0,0 @@
|
||||||
package org.lsposed.lspd.service;
|
|
||||||
|
|
||||||
import static org.lsposed.lspd.service.BridgeService.TAG;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.app.ActivityThread;
|
|
||||||
import android.app.IActivityController;
|
|
||||||
import android.app.IActivityManager;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Binder;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.IBinder;
|
|
||||||
import android.os.Parcel;
|
|
||||||
import android.os.ResultReceiver;
|
|
||||||
import android.os.ServiceManager;
|
|
||||||
import android.os.ShellCallback;
|
|
||||||
import android.os.ShellCommand;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import org.lsposed.lspd.BuildConfig;
|
|
||||||
|
|
||||||
import java.io.FileDescriptor;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.PrintWriter;
|
|
||||||
import java.lang.reflect.Constructor;
|
|
||||||
import java.lang.reflect.InvocationHandler;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.lang.reflect.Proxy;
|
|
||||||
|
|
||||||
public class ActivityController extends IActivityController.Stub {
|
|
||||||
private static Constructor<?> myActivityControllerConstructor = null;
|
|
||||||
private static Method myActivityControllerRunner = null;
|
|
||||||
private static boolean inited = false;
|
|
||||||
private static int fdSize = -1;
|
|
||||||
|
|
||||||
private static IActivityController controller = null;
|
|
||||||
|
|
||||||
static private ActivityController instance;
|
|
||||||
|
|
||||||
static {
|
|
||||||
try {
|
|
||||||
Context ctx = ActivityThread.currentActivityThread().getSystemContext();
|
|
||||||
var systemClassLoader = ctx.getClassLoader();
|
|
||||||
@SuppressLint("PrivateApi") var myActivityControllerClass = Class.forName("com.android.server.am.ActivityManagerShellCommand$MyActivityController", false, systemClassLoader);
|
|
||||||
try {
|
|
||||||
myActivityControllerConstructor = myActivityControllerClass.getDeclaredConstructor(IActivityManager.class, PrintWriter.class, InputStream.class,
|
|
||||||
String.class, boolean.class);
|
|
||||||
} catch (NoSuchMethodException e1) {
|
|
||||||
try {
|
|
||||||
myActivityControllerConstructor = myActivityControllerClass.getDeclaredConstructor(IActivityManager.class, PrintWriter.class, InputStream.class,
|
|
||||||
String.class, boolean.class, boolean.class, String.class, boolean.class);
|
|
||||||
} catch (NoSuchMethodException e2) {
|
|
||||||
myActivityControllerConstructor = myActivityControllerClass.getDeclaredConstructor(IActivityManager.class, PrintWriter.class, InputStream.class,
|
|
||||||
String.class, boolean.class, boolean.class, String.class, boolean.class, boolean.class);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
myActivityControllerConstructor.setAccessible(true);
|
|
||||||
myActivityControllerRunner = myActivityControllerClass.getDeclaredMethod("run");
|
|
||||||
myActivityControllerRunner.setAccessible(true);
|
|
||||||
var tmp = Parcel.obtain();
|
|
||||||
tmp.writeFileDescriptor(FileDescriptor.in);
|
|
||||||
fdSize = tmp.dataPosition();
|
|
||||||
tmp.recycle();
|
|
||||||
inited = true;
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Log.e(TAG, "Failed to init ActivityController", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ActivityController() {
|
|
||||||
instance = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
static private @NonNull
|
|
||||||
ActivityController getInstance() {
|
|
||||||
if (instance == null) new ActivityController();
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
static boolean replaceShellCommand(IBinder am, Parcel data, Parcel reply) {
|
|
||||||
if (!inited) return false;
|
|
||||||
try {
|
|
||||||
data.setDataPosition(fdSize * 3);
|
|
||||||
String[] args = data.createStringArray();
|
|
||||||
|
|
||||||
if (args.length > 0 && "monitor".equals(args[0])) {
|
|
||||||
data.setDataPosition(0);
|
|
||||||
try (var in = data.readFileDescriptor();
|
|
||||||
var out = data.readFileDescriptor();
|
|
||||||
var err = data.readFileDescriptor()) {
|
|
||||||
data.createStringArray();
|
|
||||||
ShellCallback shellCallback = ShellCallback.CREATOR.createFromParcel(data);
|
|
||||||
ResultReceiver resultReceiver = ResultReceiver.CREATOR.createFromParcel(data);
|
|
||||||
new ShellCommand() {
|
|
||||||
@Override
|
|
||||||
public int onCommand(String cmd) {
|
|
||||||
final PrintWriter pw = getOutPrintWriter();
|
|
||||||
String opt;
|
|
||||||
String gdbPort = null;
|
|
||||||
boolean monkey = false;
|
|
||||||
boolean simpleMode = false;
|
|
||||||
String target = null;
|
|
||||||
boolean alwaysContinue = false;
|
|
||||||
boolean alwaysKill = false;
|
|
||||||
while ((opt = getNextOption()) != null) {
|
|
||||||
if (opt.equals("--gdb")) {
|
|
||||||
gdbPort = getNextArgRequired();
|
|
||||||
} else if (opt.equals("-m")) {
|
|
||||||
monkey = true;
|
|
||||||
} else if (myActivityControllerConstructor.getParameterCount() == 8) {
|
|
||||||
switch (opt) {
|
|
||||||
case "-p":
|
|
||||||
target = getNextArgRequired();
|
|
||||||
break;
|
|
||||||
case "-s":
|
|
||||||
simpleMode = true;
|
|
||||||
break;
|
|
||||||
case "-c":
|
|
||||||
alwaysContinue = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (myActivityControllerConstructor.getParameterCount() > 8) {
|
|
||||||
switch (opt) {
|
|
||||||
case "-k":
|
|
||||||
alwaysKill = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
getErrPrintWriter().println("Error: Unknown option: " + opt);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return replaceMyControllerActivity(pw, getRawInputStream(), gdbPort, monkey, simpleMode, target, alwaysContinue, alwaysKill);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onHelp() {
|
|
||||||
|
|
||||||
}
|
|
||||||
}.exec((Binder) am, in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(), args, shellCallback, resultReceiver);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Log.e(TAG, "replace shell command", e);
|
|
||||||
} finally {
|
|
||||||
if (reply != null) reply.writeNoException();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
data.setDataPosition(0);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static boolean replaceActivityController(Parcel data) {
|
|
||||||
if (!inited) return false;
|
|
||||||
try {
|
|
||||||
var position = data.dataPosition();
|
|
||||||
var controller = replaceActivityController(IActivityController.Stub.asInterface(data.readStrongBinder()));
|
|
||||||
var b = data.readInt();
|
|
||||||
data.setDataSize(position);
|
|
||||||
data.setDataPosition(position);
|
|
||||||
data.writeStrongInterface(controller);
|
|
||||||
data.writeInt(b);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Log.e(TAG, "replace activity controller", e);
|
|
||||||
} finally {
|
|
||||||
data.setDataPosition(0);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static private int replaceMyControllerActivity(PrintWriter pw, InputStream stream, String gdbPort, boolean monkey, boolean simpleMode, String target, boolean alwaysContinue, boolean alwaysKill) {
|
|
||||||
try {
|
|
||||||
InvocationHandler handler = (proxy, method, args1) -> {
|
|
||||||
if (method.getName().equals("setActivityController")) {
|
|
||||||
try {
|
|
||||||
args1[0] = replaceActivityController((IActivityController) args1[0]);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Log.e(TAG, "replace activity controller", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return method.invoke(ServiceManager.getService("activity"), args1);
|
|
||||||
};
|
|
||||||
var amProxy = Proxy.newProxyInstance(BridgeService.class.getClassLoader(),
|
|
||||||
new Class[]{myActivityControllerConstructor.getParameterTypes()[0]}, handler);
|
|
||||||
Object ctrl;
|
|
||||||
if (myActivityControllerConstructor.getParameterCount() == 5) {
|
|
||||||
ctrl = myActivityControllerConstructor.newInstance(amProxy, pw, stream, gdbPort, monkey);
|
|
||||||
} else if (myActivityControllerConstructor.getParameterCount() == 8){
|
|
||||||
ctrl = myActivityControllerConstructor.newInstance(amProxy, pw, stream, gdbPort, monkey, simpleMode, target, alwaysContinue);
|
|
||||||
} else {
|
|
||||||
ctrl = myActivityControllerConstructor.newInstance(amProxy, pw, stream, gdbPort, monkey, simpleMode, target, alwaysContinue, alwaysKill);
|
|
||||||
}
|
|
||||||
myActivityControllerRunner.invoke(ctrl);
|
|
||||||
return 0;
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Log.e(TAG, "run monitor", e);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static private IActivityController replaceActivityController(IActivityController controller) {
|
|
||||||
Log.d(TAG, "android.app.IActivityManager.setActivityController is called");
|
|
||||||
ActivityController.controller = controller;
|
|
||||||
return getInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean activityStarting(Intent intent, String pkg) {
|
|
||||||
Log.d(TAG, "activity from " + pkg + " with " + intent + " with extras " + intent.getExtras() + " is starting");
|
|
||||||
var snapshot = BridgeService.getService();
|
|
||||||
if (snapshot != null && BuildConfig.MANAGER_INJECTED_PKG_NAME.equals(pkg)) {
|
|
||||||
try {
|
|
||||||
return snapshot.preStartManager(pkg, intent);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Log.e(TAG, "request manager", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return controller == null || controller.activityStarting(intent, pkg);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean activityResuming(String pkg) {
|
|
||||||
return controller == null || controller.activityResuming(pkg);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean appCrashed(String processName, int pid, String shortMsg, String longMsg, long timeMillis, String stackTrace) {
|
|
||||||
return controller == null || controller.appCrashed(processName, pid, shortMsg, longMsg, timeMillis, stackTrace);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int appEarlyNotResponding(String processName, int pid, String annotation) {
|
|
||||||
return controller == null ? 0 : controller.appNotResponding(processName, pid, annotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int appNotResponding(String processName, int pid, String processStats) {
|
|
||||||
return controller == null ? 0 : controller.appNotResponding(processName, pid, processStats);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int systemNotResponding(String msg) {
|
|
||||||
return controller == null ? -1 : controller.systemNotResponding(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean moveTaskToFront(String pkg, int task, int flags, Bundle options) {
|
|
||||||
return controller == null || controller.moveTaskToFront(pkg, task, flags, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IBinder asBinder() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -47,6 +47,7 @@ public class BridgeService {
|
||||||
ACTION_UNKNOWN,
|
ACTION_UNKNOWN,
|
||||||
ACTION_SEND_BINDER,
|
ACTION_SEND_BINDER,
|
||||||
ACTION_GET_BINDER,
|
ACTION_GET_BINDER,
|
||||||
|
ACTION_ENABLE_MANAGER,
|
||||||
}
|
}
|
||||||
|
|
||||||
// for client
|
// for client
|
||||||
|
|
@ -149,6 +150,15 @@ public class BridgeService {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
case ACTION_ENABLE_MANAGER: {
|
||||||
|
var uid = Binder.getCallingUid();
|
||||||
|
if ((uid == 0 || uid == 2000 || uid == 1000) && service != null) {
|
||||||
|
var result = service.setManagerEnabled(data.readInt() == 1);
|
||||||
|
if (reply != null) reply.writeInt(result ? 1 : 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
Log.e(TAG, "onTransact", e);
|
Log.e(TAG, "onTransact", e);
|
||||||
|
|
@ -156,52 +166,6 @@ public class BridgeService {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
public static boolean replaceShellCommand(IBinder obj, int code, long dataObj, long replyObj, int flags) {
|
|
||||||
Parcel data = ParcelUtils.fromNativePointer(dataObj);
|
|
||||||
Parcel reply = ParcelUtils.fromNativePointer(replyObj);
|
|
||||||
|
|
||||||
if (data == null || reply == null) {
|
|
||||||
Log.w(TAG, "Got transaction with null data or reply");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
String descriptor = obj.getInterfaceDescriptor();
|
|
||||||
if (!"android.app.IActivityManager".equals(descriptor) &&
|
|
||||||
!"com.sonymobile.hookservice.HookActivityService".equals(descriptor)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return ActivityController.replaceShellCommand(obj, data, reply);
|
|
||||||
} catch (Throwable e) {
|
|
||||||
Log.e(TAG, "replace shell command", e);
|
|
||||||
return false;
|
|
||||||
} finally {
|
|
||||||
data.setDataPosition(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
public static boolean replaceActivityController(IBinder obj, int code, long dataObj, long replyObj, int flags) {
|
|
||||||
Parcel data = ParcelUtils.fromNativePointer(dataObj);
|
|
||||||
Parcel reply = ParcelUtils.fromNativePointer(replyObj);
|
|
||||||
|
|
||||||
if (data == null || reply == null) {
|
|
||||||
Log.w(TAG, "Got transaction with null data or reply");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (!ParcelUtils.safeEnforceInterface(data, "android.app.IActivityManager") &&
|
|
||||||
!ParcelUtils.safeEnforceInterface(data, "com.sonymobile.hookservice.HookActivityService")) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return ActivityController.replaceActivityController(data);
|
|
||||||
} finally {
|
|
||||||
data.setDataPosition(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public static boolean execTransact(IBinder obj, int code, long dataObj, long replyObj, int flags) {
|
public static boolean execTransact(IBinder obj, int code, long dataObj, long replyObj, int flags) {
|
||||||
if (code != TRANSACTION_CODE) return false;
|
if (code != TRANSACTION_CODE) return false;
|
||||||
|
|
|
||||||
|
|
@ -17,15 +17,12 @@ import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.PersistableBundle;
|
import android.os.PersistableBundle;
|
||||||
import android.os.Process;
|
|
||||||
import android.os.RemoteException;
|
|
||||||
import android.util.AndroidRuntimeException;
|
import android.util.AndroidRuntimeException;
|
||||||
import android.util.ArrayMap;
|
import android.util.ArrayMap;
|
||||||
import android.webkit.WebViewDelegate;
|
import android.webkit.WebViewDelegate;
|
||||||
import android.webkit.WebViewFactory;
|
import android.webkit.WebViewFactory;
|
||||||
import android.webkit.WebViewFactoryProvider;
|
import android.webkit.WebViewFactoryProvider;
|
||||||
|
|
||||||
import org.lsposed.lspd.BuildConfig;
|
|
||||||
import org.lsposed.lspd.ILSPManagerService;
|
import org.lsposed.lspd.ILSPManagerService;
|
||||||
|
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
|
|
@ -157,7 +154,6 @@ public class ParasiticManagerHooker {
|
||||||
}
|
}
|
||||||
if (param.args[i] instanceof Intent) {
|
if (param.args[i] instanceof Intent) {
|
||||||
var intent = (Intent) param.args[i];
|
var intent = (Intent) param.args[i];
|
||||||
checkIntent(managerService, intent);
|
|
||||||
intent.setComponent(new ComponentName(intent.getComponent().getPackageName(), "org.lsposed.manager.ui.activity.MainActivity"));
|
intent.setComponent(new ComponentName(intent.getComponent().getPackageName(), "org.lsposed.manager.ui.activity.MainActivity"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -257,16 +253,6 @@ public class ParasiticManagerHooker {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
XposedHelpers.findAndHookMethod(ActivityThread.class, "deliverNewIntents", activityClientRecordClass, List.class, new XC_MethodHook() {
|
|
||||||
@Override
|
|
||||||
protected void beforeHookedMethod(MethodHookParam param) {
|
|
||||||
if (param.args[1] == null) return;
|
|
||||||
for (var intent : (List<?>) param.args[1]) {
|
|
||||||
checkIntent(managerService, (Intent) intent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
XposedHelpers.findAndHookMethod(WebViewFactory.class, "getProvider", new XC_MethodReplacement() {
|
XposedHelpers.findAndHookMethod(WebViewFactory.class, "getProvider", new XC_MethodReplacement() {
|
||||||
@Override
|
@Override
|
||||||
protected Object replaceHookedMethod(MethodHookParam param) {
|
protected Object replaceHookedMethod(MethodHookParam param) {
|
||||||
|
|
@ -323,21 +309,6 @@ public class ParasiticManagerHooker {
|
||||||
XposedHelpers.findAndHookMethod(ActivityThread.class, "performDestroyActivity", IBinder.class, boolean.class, int.class, boolean.class, stateHooker);
|
XposedHelpers.findAndHookMethod(ActivityThread.class, "performDestroyActivity", IBinder.class, boolean.class, int.class, boolean.class, stateHooker);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void checkIntent(ILSPManagerService managerService, Intent intent) {
|
|
||||||
if (managerService == null) return;
|
|
||||||
if (Process.myUid() != BuildConfig.MANAGER_INJECTED_UID) return;
|
|
||||||
if (intent.getCategories() == null || !intent.getCategories().contains("org.lsposed.manager.LAUNCH_MANAGER")) {
|
|
||||||
Hookers.logD("Launching the original app, restarting");
|
|
||||||
try {
|
|
||||||
managerService.restartFor(intent);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
Hookers.logE("restart failed", e);
|
|
||||||
} finally {
|
|
||||||
Process.killProcess(Process.myPid());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static public boolean start() {
|
static public boolean start() {
|
||||||
List<IBinder> binder = new ArrayList<>(1);
|
List<IBinder> binder = new ArrayList<>(1);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
package org.lsposed.lspd.util;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.app.ProfilerInfo;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.ActivityInfo;
|
||||||
|
import android.content.pm.ResolveInfo;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.lsposed.lspd.hooker.HandleSystemServerProcessHooker;
|
||||||
|
import org.lsposed.lspd.impl.LSPosedHelper;
|
||||||
|
import org.lsposed.lspd.service.BridgeService;
|
||||||
|
|
||||||
|
import io.github.libxposed.api.XposedInterface;
|
||||||
|
import io.github.libxposed.api.annotations.AfterInvocation;
|
||||||
|
import io.github.libxposed.api.annotations.XposedHooker;
|
||||||
|
|
||||||
|
public class ParasiticManagerSystemHooker implements HandleSystemServerProcessHooker.Callback {
|
||||||
|
public static void start() {
|
||||||
|
HandleSystemServerProcessHooker.callback = new ParasiticManagerSystemHooker();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*@XposedHooker
|
||||||
|
private static class Hooker2 implements XposedInterface.Hooker {
|
||||||
|
@BeforeInvocation
|
||||||
|
public static void beforeHookedMethod(XposedInterface.BeforeHookCallback callback) throws Throwable {
|
||||||
|
Log.d("LSPosed", "checking new activity");
|
||||||
|
var self = callback.getThisObject();
|
||||||
|
if (self == null) return;
|
||||||
|
var request = XposedHelpers.getObjectField(self, "mRequest");
|
||||||
|
Log.d("LSPosed", "start activity intent=" + XposedHelpers.getObjectField(request, "intent") + " ai=" + XposedHelpers.getObjectField(request, "activityInfo"), new Throwable());
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
@XposedHooker
|
||||||
|
private static class Hooker implements XposedInterface.Hooker {
|
||||||
|
@AfterInvocation
|
||||||
|
public static void afterHookedMethod(XposedInterface.AfterHookCallback callback) throws Throwable {
|
||||||
|
Log.d("LSPosed", "checking new activity");
|
||||||
|
var intent = (Intent) callback.getArgs()[0];
|
||||||
|
Log.d("LSPosed", "intent=" + intent);
|
||||||
|
if (intent == null) return;
|
||||||
|
// TODO: keep sync with LSPManagerService getManagerIntent
|
||||||
|
if (!intent.hasCategory("org.lsposed.manager.LAUNCH_MANAGER")) return;
|
||||||
|
var aInfo = (ActivityInfo) callback.getResult();
|
||||||
|
if (aInfo == null || !"com.android.shell".equals(aInfo.packageName)) return;
|
||||||
|
aInfo.processName = "org.lsposed.manager";
|
||||||
|
aInfo.theme = android.R.style.Theme_Material_Light_NoActionBar;
|
||||||
|
aInfo.flags = aInfo.flags & ~(ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS | ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS);
|
||||||
|
BridgeService.getService().preStartManager();
|
||||||
|
Log.d("LSPosed", "replaced activity");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("PrivateApi")
|
||||||
|
@Override
|
||||||
|
public void onSystemServerLoaded(ClassLoader classLoader) {
|
||||||
|
try {
|
||||||
|
Class<?> supervisorClass;
|
||||||
|
try {
|
||||||
|
supervisorClass = Class.forName("com.android.server.wm.ActivityTaskSupervisor", false, classLoader);
|
||||||
|
} catch (ClassNotFoundException ignore) {
|
||||||
|
supervisorClass = Class.forName("com.android.server.wm.ActivityStackSupervisor", false, classLoader);
|
||||||
|
}
|
||||||
|
LSPosedHelper.hookMethod(Hooker.class, supervisorClass, "resolveActivity", Intent.class, ResolveInfo.class, int.class, ProfilerInfo.class);
|
||||||
|
/*
|
||||||
|
for (var method: Class.forName("com.android.server.wm.ActivityStarter", false, classLoader).getDeclaredMethods()) {
|
||||||
|
if ("execute".equals(method.getName()))
|
||||||
|
HookBridge.deoptimizeMethod(method);
|
||||||
|
}
|
||||||
|
LSPosedHelper.hookAllMethods(Hooker2.class, Class.forName("com.android.server.wm.ActivityStarter", false, classLoader), "execute");*/
|
||||||
|
Log.d("LSPosed", "hooked activity starter");
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Log.e("LSPosed", "onSystemServerLoaded: ", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -63,6 +63,7 @@ namespace lspd {
|
||||||
nice_name,
|
nice_name,
|
||||||
*start_child_zygote,
|
*start_child_zygote,
|
||||||
*_app_data_dir);
|
*_app_data_dir);
|
||||||
|
*_nice_name = nice_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nativeForkAndSpecializePost(JNIEnv *env, jclass, jint res) {
|
void nativeForkAndSpecializePost(JNIEnv *env, jclass, jint res) {
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ static_assert(FS_IOC_SETFLAGS == LP_SELECT(0x40046602, 0x40086602));
|
||||||
|
|
||||||
namespace lspd {
|
namespace lspd {
|
||||||
extern int *allowUnload;
|
extern int *allowUnload;
|
||||||
|
jboolean is_parasitic_manager = JNI_FALSE;
|
||||||
|
|
||||||
constexpr int FIRST_ISOLATED_UID = 99000;
|
constexpr int FIRST_ISOLATED_UID = 99000;
|
||||||
constexpr int LAST_ISOLATED_UID = 99999;
|
constexpr int LAST_ISOLATED_UID = 99999;
|
||||||
|
|
@ -114,52 +115,55 @@ namespace lspd {
|
||||||
close(dex_fd);
|
close(dex_fd);
|
||||||
instance->HookBridge(*this, env);
|
instance->HookBridge(*this, env);
|
||||||
|
|
||||||
if (application_binder) {
|
// always inject into system server
|
||||||
lsplant::InitInfo initInfo{
|
lsplant::InitInfo initInfo{
|
||||||
.inline_hooker = [](auto t, auto r) {
|
.inline_hooker = [](auto t, auto r) {
|
||||||
void* bk = nullptr;
|
void* bk = nullptr;
|
||||||
return HookFunction(t, r, &bk) == 0 ? bk : nullptr;
|
return HookFunction(t, r, &bk) == 0 ? bk : nullptr;
|
||||||
},
|
},
|
||||||
.inline_unhooker = [](auto t) {
|
.inline_unhooker = [](auto t) {
|
||||||
return UnhookFunction(t) == 0 ;
|
return UnhookFunction(t) == 0 ;
|
||||||
},
|
},
|
||||||
.art_symbol_resolver = [](auto symbol) {
|
.art_symbol_resolver = [](auto symbol) {
|
||||||
return GetArt()->getSymbAddress(symbol);
|
return GetArt()->getSymbAddress(symbol);
|
||||||
},
|
},
|
||||||
.art_symbol_prefix_resolver = [](auto symbol) {
|
.art_symbol_prefix_resolver = [](auto symbol) {
|
||||||
return GetArt()->getSymbPrefixFirstAddress(symbol);
|
return GetArt()->getSymbPrefixFirstAddress(symbol);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
InitArtHooker(env, initInfo);
|
InitArtHooker(env, initInfo);
|
||||||
InitHooks(env);
|
InitHooks(env);
|
||||||
SetupEntryClass(env);
|
SetupEntryClass(env);
|
||||||
FindAndCall(env, "forkCommon",
|
FindAndCall(env, "forkCommon",
|
||||||
"(ZLjava/lang/String;Ljava/lang/String;Landroid/os/IBinder;)V",
|
"(ZLjava/lang/String;Ljava/lang/String;Landroid/os/IBinder;)V",
|
||||||
JNI_TRUE, JNI_NewStringUTF(env, "system"), nullptr, application_binder);
|
JNI_TRUE, JNI_NewStringUTF(env, "system"), nullptr, application_binder, is_parasitic_manager);
|
||||||
GetArt(true);
|
GetArt(true);
|
||||||
} else {
|
|
||||||
LOGI("skipped system server");
|
|
||||||
GetArt(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MagiskLoader::OnNativeForkAndSpecializePre(JNIEnv *env,
|
void MagiskLoader::OnNativeForkAndSpecializePre(JNIEnv *env,
|
||||||
jint uid,
|
jint uid,
|
||||||
jintArray &gids,
|
jintArray &gids,
|
||||||
jstring nice_name,
|
jstring &nice_name,
|
||||||
jboolean is_child_zygote,
|
jboolean is_child_zygote,
|
||||||
jstring app_data_dir) {
|
jstring app_data_dir) {
|
||||||
|
jboolean is_manager = JNI_FALSE;
|
||||||
if (uid == kAidInjected) {
|
if (uid == kAidInjected) {
|
||||||
int array_size = gids ? env->GetArrayLength(gids) : 0;
|
const JUTFString name(env, nice_name);
|
||||||
auto region = std::make_unique<jint[]>(array_size + 1);
|
if (name.get() == "org.lsposed.manager"sv) {
|
||||||
auto *new_gids = env->NewIntArray(array_size + 1);
|
int array_size = gids ? env->GetArrayLength(gids) : 0;
|
||||||
if (gids) env->GetIntArrayRegion(gids, 0, array_size, region.get());
|
auto region = std::make_unique<jint[]>(array_size + 1);
|
||||||
region.get()[array_size] = kAidInet;
|
auto *new_gids = env->NewIntArray(array_size + 1);
|
||||||
env->SetIntArrayRegion(new_gids, 0, array_size + 1, region.get());
|
if (gids) env->GetIntArrayRegion(gids, 0, array_size, region.get());
|
||||||
if (gids) env->SetIntArrayRegion(gids, 0, 1, region.get() + array_size);
|
region.get()[array_size] = kAidInet;
|
||||||
gids = new_gids;
|
env->SetIntArrayRegion(new_gids, 0, array_size + 1, region.get());
|
||||||
|
if (gids) env->SetIntArrayRegion(gids, 0, 1, region.get() + array_size);
|
||||||
|
gids = new_gids;
|
||||||
|
nice_name = JNI_NewStringUTF(env, "com.android.shell").release();
|
||||||
|
is_manager = JNI_TRUE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
is_parasitic_manager = is_manager;
|
||||||
Service::instance()->InitService(env);
|
Service::instance()->InitService(env);
|
||||||
const auto app_id = uid % PER_USER_RANGE;
|
const auto app_id = uid % PER_USER_RANGE;
|
||||||
JUTFString process_name(env, nice_name);
|
JUTFString process_name(env, nice_name);
|
||||||
|
|
@ -187,6 +191,7 @@ namespace lspd {
|
||||||
MagiskLoader::OnNativeForkAndSpecializePost(JNIEnv *env, jstring nice_name, jstring app_dir) {
|
MagiskLoader::OnNativeForkAndSpecializePost(JNIEnv *env, jstring nice_name, jstring app_dir) {
|
||||||
const JUTFString process_name(env, nice_name);
|
const JUTFString process_name(env, nice_name);
|
||||||
auto *instance = Service::instance();
|
auto *instance = Service::instance();
|
||||||
|
if (is_parasitic_manager) nice_name = JNI_NewStringUTF(env, "org.lsposed.manager").release();
|
||||||
auto binder = skip_ ? ScopedLocalRef<jobject>{env, nullptr}
|
auto binder = skip_ ? ScopedLocalRef<jobject>{env, nullptr}
|
||||||
: instance->RequestBinder(env, nice_name);
|
: instance->RequestBinder(env, nice_name);
|
||||||
if (binder) {
|
if (binder) {
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ namespace lspd {
|
||||||
return static_cast<MagiskLoader*>(instance_.get());
|
return static_cast<MagiskLoader*>(instance_.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnNativeForkAndSpecializePre(JNIEnv *env, jint uid, jintArray &gids, jstring nice_name,
|
void OnNativeForkAndSpecializePre(JNIEnv *env, jint uid, jintArray &gids, jstring &nice_name,
|
||||||
jboolean is_child_zygote, jstring app_data_dir);
|
jboolean is_child_zygote, jstring app_data_dir);
|
||||||
|
|
||||||
void OnNativeForkAndSpecializePost(JNIEnv *env, jstring nice_name, jstring app_dir);
|
void OnNativeForkAndSpecializePost(JNIEnv *env, jstring nice_name, jstring app_dir);
|
||||||
|
|
|
||||||
|
|
@ -95,25 +95,6 @@ namespace lspd {
|
||||||
instance()->exec_transact_replace_methodID_,
|
instance()->exec_transact_replace_methodID_,
|
||||||
obj, code, data_obj, reply_obj, flags);
|
obj, code, data_obj, reply_obj, flags);
|
||||||
return true;
|
return true;
|
||||||
} else if (SET_ACTIVITY_CONTROLLER_CODE != -1 &&
|
|
||||||
code == SET_ACTIVITY_CONTROLLER_CODE) [[unlikely]] {
|
|
||||||
va_copy(copy, args);
|
|
||||||
if (instance()->replace_activity_controller_methodID_) {
|
|
||||||
*res = JNI_CallStaticBooleanMethod(env, instance()->bridge_service_class_,
|
|
||||||
instance()->replace_activity_controller_methodID_,
|
|
||||||
obj, code, data_obj, reply_obj, flags);
|
|
||||||
}
|
|
||||||
va_end(copy);
|
|
||||||
// fallback the backup
|
|
||||||
} else if (code == (('_' << 24) | ('C' << 16) | ('M' << 8) | 'D')) {
|
|
||||||
va_copy(copy, args);
|
|
||||||
if (instance()->replace_shell_command_methodID_) {
|
|
||||||
*res = JNI_CallStaticBooleanMethod(env, instance()->bridge_service_class_,
|
|
||||||
instance()->replace_shell_command_methodID_,
|
|
||||||
obj, code, data_obj, reply_obj, flags);
|
|
||||||
}
|
|
||||||
va_end(copy);
|
|
||||||
return *res;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -247,21 +228,6 @@ namespace lspd {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
replace_activity_controller_methodID_ = JNI_GetStaticMethodID(env, bridge_service_class_,
|
|
||||||
"replaceActivityController",
|
|
||||||
hooker_sig);
|
|
||||||
if (!replace_activity_controller_methodID_) {
|
|
||||||
LOGE("replaceActivityShell class not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
replace_shell_command_methodID_ = JNI_GetStaticMethodID(env, bridge_service_class_,
|
|
||||||
"replaceShellCommand",
|
|
||||||
hooker_sig);
|
|
||||||
if (!replace_shell_command_methodID_) {
|
|
||||||
LOGE("replaceShellCommand class not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
auto binder_class = JNI_FindClass(env, "android/os/Binder");
|
auto binder_class = JNI_FindClass(env, "android/os/Binder");
|
||||||
exec_transact_backup_methodID_ = JNI_GetMethodID(env, binder_class, "execTransact",
|
exec_transact_backup_methodID_ = JNI_GetMethodID(env, binder_class, "execTransact",
|
||||||
"(IJJI)Z");
|
"(IJJI)Z");
|
||||||
|
|
|
||||||
|
|
@ -107,8 +107,6 @@ namespace lspd {
|
||||||
|
|
||||||
jclass bridge_service_class_ = nullptr;
|
jclass bridge_service_class_ = nullptr;
|
||||||
jmethodID exec_transact_replace_methodID_ = nullptr;
|
jmethodID exec_transact_replace_methodID_ = nullptr;
|
||||||
jmethodID replace_activity_controller_methodID_ = nullptr;
|
|
||||||
jmethodID replace_shell_command_methodID_ = nullptr;
|
|
||||||
|
|
||||||
jclass binder_class_ = nullptr;
|
jclass binder_class_ = nullptr;
|
||||||
jmethodID binder_ctor_ = nullptr;
|
jmethodID binder_ctor_ = nullptr;
|
||||||
|
|
|
||||||
|
|
@ -7,5 +7,7 @@ interface ILSPosedService {
|
||||||
|
|
||||||
oneway void dispatchSystemServerContext(in IBinder activityThread, in IBinder activityToken, String api);
|
oneway void dispatchSystemServerContext(in IBinder activityThread, in IBinder activityToken, String api);
|
||||||
|
|
||||||
boolean preStartManager(String pkgName, in Intent intent);
|
boolean preStartManager();
|
||||||
|
|
||||||
|
boolean setManagerEnabled(boolean enabled);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue