Use service instead of content provider to send binder

This commit is contained in:
Nullptr 2022-11-08 02:53:56 +08:00
parent cb0c24deb4
commit 54cf7eed35
No known key found for this signature in database
5 changed files with 101 additions and 90 deletions

View File

@ -26,12 +26,9 @@
</intent-filter>
</activity>
<provider
android:name=".manager.ModuleProvider"
android:authorities="org.lsposed.lspatch.provider"
android:enabled="true"
android:exported="true"
tools:ignore="ExportedContentProvider" />
<service
android:name=".manager.ModuleService"
android:exported="true" />
<provider
android:name="rikka.shizuku.ShizukuProvider"

View File

@ -1,52 +0,0 @@
package org.lsposed.lspatch.manager
import android.content.ContentProvider
import android.content.ContentValues
import android.database.Cursor
import android.net.Uri
import android.os.Binder
import android.os.Bundle
import android.util.Log
import org.lsposed.lspatch.lspApp
class ModuleProvider : ContentProvider() {
companion object {
private const val TAG = "ModuleProvider"
}
override fun onCreate(): Boolean {
return false
}
override fun call(method: String, arg: String?, extras: Bundle?): Bundle {
val app = lspApp.packageManager.getNameForUid(Binder.getCallingUid())
Log.d(TAG, "$app requests ModuleProvider")
return when (method) {
"getBinder" -> Bundle().apply {
putBinder("binder", ManagerService.asBinder())
}
else -> throw IllegalArgumentException("Invalid method name")
}
}
override fun query(p0: Uri, p1: Array<out String>?, p2: String?, p3: Array<out String>?, p4: String?): Cursor? {
return null
}
override fun getType(p0: Uri): String? {
return null
}
override fun insert(p0: Uri, p1: ContentValues?): Uri? {
return null
}
override fun delete(p0: Uri, p1: String?, p2: Array<out String>?): Int {
return 0
}
override fun update(p0: Uri, p1: ContentValues?, p2: String?, p3: Array<out String>?): Int {
return 0
}
}

View File

@ -0,0 +1,20 @@
package org.lsposed.lspatch.manager
import android.app.Service
import android.content.Intent
import android.os.IBinder
import android.util.Log
class ModuleService : Service() {
companion object {
private const val TAG = "ModuleService"
}
override fun onBind(intent: Intent): IBinder? {
val packageName = intent.getStringExtra("packageName") ?: return null
// TODO: Authentication
Log.i(TAG, "$packageName requests binder")
return ManagerService.asBinder()
}
}

View File

@ -9,6 +9,7 @@ import android.content.Context;
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;
@ -63,7 +64,7 @@ public class LSPApplication {
return (android.os.Process.myUid() % PER_USER_RANGE) >= FIRST_APP_ZYGOTE_ISOLATED_UID;
}
public static void onLoad() {
public static void onLoad() throws RemoteException, IOException {
if (isIsolated()) {
XLog.d(TAG, "Skip isolated process");
return;
@ -75,30 +76,27 @@ public class LSPApplication {
return;
}
try {
Log.d(TAG, "Initialize service client");
ILSPApplicationService service;
if (config.useManager) {
service = new RemoteApplicationService(context);
} else {
service = new LocalApplicationService(context);
}
disableProfile(context);
Startup.initXposed(false, ActivityThread.currentProcessName(), service);
Log.i(TAG, "Bootstrap Xposed");
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.sigBypassLevel);
} catch (Throwable e) {
throw new RuntimeException("Do hook", e);
Log.d(TAG, "Initialize service client");
ILSPApplicationService service;
if (config.useManager) {
service = new RemoteApplicationService(context);
} else {
service = new LocalApplicationService(context);
}
disableProfile(context);
Startup.initXposed(false, ActivityThread.currentProcessName(), service);
Log.i(TAG, "Bootstrap Xposed");
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.sigBypassLevel);
Log.i(TAG, "LSPatch bootstrap completed");
}

View File

@ -1,34 +1,82 @@
package org.lsposed.lspatch.service;
import static org.lsposed.lspatch.share.Constants.MANAGER_PACKAGE_NAME;
import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Context;
import android.net.Uri;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
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;
import org.lsposed.lspd.service.ILSPApplicationService;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class RemoteApplicationService implements ILSPApplicationService {
private static final Uri PROVIDER = Uri.parse("content://" + MANAGER_PACKAGE_NAME + ".provider");
private static final String TAG = "LSPatch";
private static final String MODULE_SERVICE = Constants.MANAGER_PACKAGE_NAME + ".manager.ModuleService";
private final ILSPApplicationService service;
private ILSPApplicationService service;
@SuppressLint("DiscouragedPrivateApi")
public RemoteApplicationService(Context context) throws RemoteException {
try {
Bundle back = context.getContentResolver().call(PROVIDER, "getBinder", null, null);
service = ILSPApplicationService.Stub.asInterface(back.getBinder("binder"));
} catch (IllegalArgumentException | NullPointerException e) {
var intent = new Intent()
.setComponent(new ComponentName(Constants.MANAGER_PACKAGE_NAME, MODULE_SERVICE))
.putExtra("packageName", context.getPackageName());
// TODO: Authentication
var latch = new CountDownLatch(1);
var conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
Log.i(TAG, "Manager binder received");
latch.countDown();
service = Stub.asInterface(binder);
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e(TAG, "Manager service died");
service = null;
}
};
Log.i(TAG, "Request manager binder");
context.startService(intent);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
context.bindService(intent, Context.BIND_AUTO_CREATE, Executors.newSingleThreadExecutor(), conn);
} else {
var handlerThread = new HandlerThread("RemoteApplicationService");
handlerThread.start();
var handler = new Handler(handlerThread.getLooper());
var contextImplClass = context.getClass();
var getUserMethod = contextImplClass.getMethod("getUser");
var bindServiceAsUserMethod = contextImplClass.getDeclaredMethod(
"bindServiceAsUser", Intent.class, ServiceConnection.class, int.class, Handler.class, UserHandle.class);
var userHandle = (UserHandle) getUserMethod.invoke(context);
bindServiceAsUserMethod.invoke(context, intent, conn, Context.BIND_AUTO_CREATE, handler, userHandle);
}
boolean success = latch.await(5, TimeUnit.SECONDS);
if (!success) throw new TimeoutException("Bind service timeout");
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InterruptedException | TimeoutException e) {
Toast.makeText(context, "Manager died", Toast.LENGTH_SHORT).show();
var r = new RemoteException("Failed to get manager binder");
r.initCause(e);