[core] Workaround for binder: allow blocking

This commit is contained in:
LoveSy 2021-02-16 15:49:59 +08:00 committed by tehcneko
parent 0ec8959235
commit 2890aec54f
14 changed files with 145 additions and 219 deletions

View File

@ -39,9 +39,6 @@
#include "jni/native_api.h"
#include "service.h"
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-value"
namespace lspd {
namespace fs = std::filesystem;
@ -132,6 +129,7 @@ namespace lspd {
RegisterEdxpYahfa(env);
RegisterPendingHooks(env);
RegisterNativeAPI(env);
// TODO: Recover this
// variant_ = Variant(ConfigManager::GetInstance()->GetVariant());
// LOGI("LSP Variant: %d", variant_);
@ -193,15 +191,7 @@ namespace lspd {
}
void
Context::OnNativeForkSystemServerPre([[maybe_unused]] JNIEnv *env,
[[maybe_unused]] jclass clazz,
[[maybe_unused]] uid_t uid,
[[maybe_unused]] gid_t gid,
[[maybe_unused]] jintArray gids,
[[maybe_unused]] jint runtime_flags,
[[maybe_unused]] jobjectArray rlimits,
[[maybe_unused]] jlong permitted_capabilities,
[[maybe_unused]] jlong effective_capabilities) {
Context::OnNativeForkSystemServerPre(JNIEnv *env) {
Service::instance()->InitService(env);
PreLoadDex(ConfigManager::GetInjectDexPath());
skip_ = false;
@ -231,31 +221,24 @@ namespace lspd {
Service::instance()->HookBridge(*this, env);
}
void Context::OnNativeForkAndSpecializePre(JNIEnv *env, jclass clazz,
jint uid, jint gid,
jintArray gids,
jint runtime_flags,
jobjectArray rlimits,
jint mount_external,
jstring se_info,
void Context::OnNativeForkAndSpecializePre(JNIEnv *env,
jint uid,
jstring nice_name,
jintArray fds_to_close,
jintArray fds_to_ignore,
jboolean is_child_zygote,
jstring instruction_set,
jstring app_data_dir) {
PreLoadDex(ConfigManager::GetInjectDexPath());
Service::instance()->InitService(env);
this->uid = uid;
const auto app_id = uid % PER_USER_RANGE;
nice_name_ = nice_name;
JUTFString process_name(env, nice_name);
skip_ = false;
if (!skip_ && !app_data_dir) {
LOGD("skip injecting into %d because it has no data dir", uid);
LOGD("skip injecting into %s because it has no data dir", process_name.get());
skip_ = true;
}
if (!skip_ && is_child_zygote) {
skip_ = true;
LOGD("skip injecting into %d because it's a child zygote", uid);
LOGD("skip injecting into %s because it's a child zygote", process_name.get());
}
if (!skip_ && ((app_id >= FIRST_ISOLATED_UID && app_id <= LAST_ISOLATED_UID) ||
@ -263,7 +246,7 @@ namespace lspd {
app_id <= LAST_APP_ZYGOTE_ISOLATED_UID) ||
app_id == SHARED_RELRO_UID)) {
skip_ = true;
LOGI("skip injecting into %d because it's isolated", uid);
LOGI("skip injecting into %s because it's isolated", process_name.get());
}
}
@ -289,5 +272,3 @@ namespace lspd {
}
}
}
#pragma clang diagnostic pop

View File

@ -56,21 +56,13 @@ namespace lspd {
return FindClassFromLoader(env, GetCurrentClassLoader(), className);
};
void OnNativeForkAndSpecializePre(JNIEnv *env, jclass clazz, jint uid, jint gid,
jintArray gids, jint runtime_flags, jobjectArray rlimits,
jint mount_external,
jstring se_info, jstring se_name, jintArray fds_to_close,
jintArray fds_to_ignore, jboolean is_child_zygote,
jstring instruction_set, jstring app_data_dir);
void OnNativeForkAndSpecializePre(JNIEnv *env, jint uid, jstring nice_name, jboolean is_child_zygote, jstring app_data_dir);
void OnNativeForkAndSpecializePost(JNIEnv *env);
void OnNativeForkSystemServerPost(JNIEnv *env, jint res);
void OnNativeForkSystemServerPre(JNIEnv *env, jclass clazz, uid_t uid, gid_t gid,
jintArray gids, jint runtime_flags, jobjectArray rlimits,
jlong permitted_capabilities,
jlong effective_capabilities);
void OnNativeForkSystemServerPre(JNIEnv *env);
private:
@ -82,7 +74,6 @@ namespace lspd {
JavaVM *vm_ = nullptr;
jclass class_linker_class_ = nullptr;
jmethodID post_fixup_static_mid_ = nullptr;
jint uid = -1;
bool skip_ = false;
std::vector<signed char> dex{};

View File

@ -37,64 +37,58 @@ namespace lspd {
ConfigManager::Init();
}
static int shouldSkipUid(int uid) {
static int shouldSkipUid(int) {
return 0;
}
static void nativeForkAndSpecializePre(JNIEnv *env, jclass clazz, jint *_uid, jint *gid,
jintArray *gids, jint *runtime_flags,
jobjectArray *rlimits, jint *mount_external,
jstring *se_info, jstring *nice_name,
jintArray *fds_to_close, jintArray *fds_to_ignore,
jboolean *start_child_zygote, jstring *instruction_set,
jstring *app_data_dir, jboolean *is_top_app, jobjectArray *pkg_data_info_list,
jobjectArray *whitelisted_data_info_list, jboolean *bind_mount_app_data_dirs,
jboolean *bind_mount_app_storage_dirs) {
Context::GetInstance()->OnNativeForkAndSpecializePre(env, clazz, *_uid, *gid, *gids,
*runtime_flags, *rlimits,
*mount_external, *se_info, *nice_name,
*fds_to_close,
*fds_to_ignore,
*start_child_zygote, *instruction_set,
static void nativeForkAndSpecializePre(JNIEnv *env, jclass, jint *_uid, jint *,
jintArray *, jint *,
jobjectArray *, jint *,
jstring *, jstring *nice_name,
jintArray *, jintArray *,
jboolean *start_child_zygote, jstring *,
jstring *app_data_dir, jboolean *,
jobjectArray *,
jobjectArray *,
jboolean *,
jboolean *) {
Context::GetInstance()->OnNativeForkAndSpecializePre(env, *_uid,
*nice_name,
*start_child_zygote,
*app_data_dir);
}
static void nativeForkAndSpecializePost(JNIEnv *env, jclass clazz, jint res) {
Context::GetInstance()->OnNativeForkAndSpecializePost(env);
static void nativeForkAndSpecializePost(JNIEnv *env, jclass, jint res) {
if (res == 0)
Context::GetInstance()->OnNativeForkAndSpecializePost(env);
}
static void nativeForkSystemServerPre(JNIEnv *env, jclass clazz, uid_t *uid, gid_t *gid,
jintArray *gids, jint *runtime_flags,
jobjectArray *rlimits, jlong *permitted_capabilities,
jlong *effective_capabilities) {
Context::GetInstance()->OnNativeForkSystemServerPre(env, clazz, *uid, *gid, *gids,
*runtime_flags, *rlimits,
*permitted_capabilities,
*effective_capabilities
);
static void nativeForkSystemServerPre(JNIEnv *env, jclass, uid_t *, gid_t *,
jintArray *, jint *,
jobjectArray *, jlong *,
jlong *) {
Context::GetInstance()->OnNativeForkSystemServerPre(env);
}
static void nativeForkSystemServerPost(JNIEnv *env, [[maybe_unused]] jclass clazz, jint res) {
static void nativeForkSystemServerPost(JNIEnv *env, jclass, jint res) {
Context::GetInstance()->OnNativeForkSystemServerPost(env, res);
}
/* method added in Android Q */
static void specializeAppProcessPre(JNIEnv *env, jclass clazz, jint *uid, jint *gid,
jintArray *gids, jint *runtime_flags, jobjectArray *rlimits,
jint *mount_external, jstring *se_info, jstring *nice_name,
jboolean *start_child_zygote, jstring *instruction_set,
jstring *app_data_dir, jboolean *is_top_app, jobjectArray *pkg_data_info_list,
jobjectArray *whitelisted_data_info_list, jboolean *bind_mount_app_data_dirs,
jboolean *bind_mount_app_storage_dirs) {
Context::GetInstance()->OnNativeForkAndSpecializePre(env, clazz, *uid, *gid, *gids,
*runtime_flags, *rlimits,
*mount_external, *se_info, *nice_name,
nullptr, nullptr,
*start_child_zygote, *instruction_set,
static void specializeAppProcessPre(JNIEnv *env, jclass, jint *uid, jint *,
jintArray *, jint *, jobjectArray *,
jint *, jstring *, jstring *nice_name,
jboolean *start_child_zygote, jstring *,
jstring *app_data_dir, jboolean *,
jobjectArray *,
jobjectArray *,
jboolean *,
jboolean *) {
Context::GetInstance()->OnNativeForkAndSpecializePre(env, *uid, *nice_name, *start_child_zygote,
*app_data_dir);
}
static void specializeAppProcessPost(JNIEnv *env, [[maybe_unused]] jclass clazz) {
static void specializeAppProcessPost(JNIEnv *env, jclass) {
Context::GetInstance()->OnNativeForkAndSpecializePost(env);
}
}
@ -110,7 +104,9 @@ RIRU_EXPORT void *init(void *arg) {
switch (step) {
case 1: {
auto core_max_api_version = *(int *) arg;
riru_api_version = core_max_api_version <= RIRU_MODULE_API_VERSION ? core_max_api_version : RIRU_MODULE_API_VERSION;
riru_api_version =
core_max_api_version <= RIRU_MODULE_API_VERSION ? core_max_api_version
: RIRU_MODULE_API_VERSION;
return &riru_api_version;
}
case 2: {

View File

@ -154,12 +154,9 @@ namespace lspd {
if (res) {
JNI_CallVoidMethod(env, reply, readExceptionMethod_);
service = JNI_CallObjectMethod(env, reply, readStrongBinderMethod_);
} else {
LOGD("no reply");
}
JNI_CallVoidMethod(env, data, recycleMethod_);
JNI_CallObjectMethod(env, reply, recycleMethod_);
JNI_CallVoidMethod(env, reply, recycleMethod_);
return service;
}

View File

@ -36,9 +36,7 @@ namespace lspd {
inline static std::unique_ptr<Service> instance_ = std::make_unique<Service>();
bool initialized_ = false;
Service() {
}
Service() = default;
static jboolean
call_boolean_method_va_replace(JNIEnv *env, jobject obj, jmethodID methodId, va_list args);

View File

@ -20,7 +20,6 @@
package io.github.lsposed.lspd.core.yahfa;
import io.github.lsposed.lspd.nativebridge.Heap;
import io.github.lsposed.lspd.nativebridge.Yahfa;
import io.github.lsposed.lspd.util.Utils;

View File

@ -1,39 +0,0 @@
/*
* This file is part of LSPosed.
*
* LSPosed is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LSPosed is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
*
* Copyright (C) 2020 EdXposed Contributors
* Copyright (C) 2021 LSPosed Contributors
*/
package io.github.lsposed.lspd.framework;
import io.github.lsposed.lspd.util.Utils;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.annotation.ApiSensitive;
import de.robv.android.xposed.annotation.Level;
@ApiSensitive(Level.LOW)
public class Zygote {
public static void allowFileAcrossFork(String path) {
try {
Class zygote = XposedHelpers.findClass("com.android.internal.os.Zygote", null);
XposedHelpers.callStaticMethod(zygote, "nativeAllowFileAcrossFork", path);
} catch (Throwable throwable) {
Utils.logE("error when allowFileAcrossFork", throwable);
}
}
}

View File

@ -1,25 +0,0 @@
/*
* This file is part of LSPosed.
*
* LSPosed is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LSPosed is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with LSPosed. If not, see <https://www.gnu.org/licenses/>.
*
* Copyright (C) 2020 EdXposed Contributors
* Copyright (C) 2021 LSPosed Contributors
*/
package io.github.lsposed.lspd.nativebridge;
public class Heap {
public static native int waitForGcToComplete();
}

View File

@ -13,6 +13,7 @@ import androidx.annotation.Nullable;
import java.lang.reflect.Field;
import java.util.Map;
import static hidden.HiddenApiBridge.Binder_allowBlocking;
import static io.github.lsposed.lspd.service.ServiceManager.TAG;
public class BridgeService {
@ -21,6 +22,7 @@ public class BridgeService {
private static final String SERVICE_NAME = "activity";
enum ACTION {
ACTION_UNKNOWN,
ACTION_SEND_BINDER,
ACTION_GET_BINDER,
}
@ -149,7 +151,7 @@ public class BridgeService {
serviceBinder.unlinkToDeath(LSPSERVICE_DEATH_RECIPIENT, 0);
}
serviceBinder = binder;
serviceBinder = Binder_allowBlocking(binder);
service = ILSPosedService.Stub.asInterface(serviceBinder);
try {
serviceBinder.linkToDeath(LSPSERVICE_DEATH_RECIPIENT, 0);
@ -171,7 +173,7 @@ public class BridgeService {
}
@SuppressWarnings({"unused", "RedundantSuppression"})
public static boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) {
public static boolean onTransact(@NonNull Parcel data, @Nullable Parcel reply, int flags) {
data.enforceInterface(DESCRIPTOR);
ACTION action = ACTION.values()[data.readInt()];
@ -209,13 +211,13 @@ public class BridgeService {
@SuppressWarnings({"unused", "RedundantSuppression"})
public static boolean execTransact(int code, long dataObj, long replyObj, int flags) {
Log.d(TAG, String.valueOf(code));
if (code != TRANSACTION_CODE) return false;
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;
}
@ -224,9 +226,8 @@ public class BridgeService {
try {
String descriptor = ParcelUtils.readInterfaceDescriptor(data);
data.setDataPosition(0);
if (descriptor.equals(DESCRIPTOR)) {
res = onTransact(code, data, reply, flags);
res = onTransact(data, reply, flags);
}
} catch (Exception e) {
if ((flags & IBinder.FLAG_ONEWAY) != 0) {

View File

@ -1,14 +1,17 @@
package io.github.lsposed.lspd.service;
import android.content.ContentValues;
import android.content.pm.PackageInfo;
import android.database.Cursor;
import android.database.sqlite.SQLiteQueryBuilder;
import android.database.sqlite.SQLiteStatement;
import android.database.sqlite.SQLiteDatabase;
import android.os.FileObserver;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.io.File;
@ -43,6 +46,12 @@ public class ConfigManager {
final private File verboseLogSwitch = new File(configPath, "verbose_log");
private boolean verboseLog = false;
final private String DEFAULT_MANAGER_PACKAGE_NAME = "io.github.lsposed.manager";
final private File managerPath = new File(configPath, "manager");
private String manager = null;
private int managerUid = -1;
final private File selinuxPath = new File("/sys/fs/selinux/enforce");
// only check on boot
final private boolean isPermissive;
@ -59,41 +68,77 @@ public class ConfigManager {
}
};
int readInt(File file, int defaultValue) {
private String readText(@NonNull File file) throws IOException {
return new String(Files.readAllBytes(file.toPath()));
}
private String readText(@NonNull File file, String defaultValue) {
try {
return Integer.parseInt(new String(Files.readAllBytes(file.toPath())));
if (!file.exists()) return defaultValue;
return readText(file);
} catch (IOException e) {
Log.e(TAG, Log.getStackTraceString(e));
}
return defaultValue;
}
private void writeText(@NonNull File file, String value) {
try {
Files.write(file.toPath(), value.getBytes(), StandardOpenOption.CREATE_NEW);
} catch (IOException e) {
Log.e(TAG, Log.getStackTraceString(e));
}
}
private int readInt(@NonNull File file, int defaultValue) {
try {
if (!file.exists()) return defaultValue;
return Integer.parseInt(readText(file));
} catch (IOException | NumberFormatException e) {
Log.e(TAG, Log.getStackTraceString(e));
}
return defaultValue;
}
void writeInt(File file, int value) {
try {
Files.write(file.toPath(), String.valueOf(value).getBytes(), StandardOpenOption.CREATE_NEW);
} catch (IOException e) {
Log.e(TAG, Log.getStackTraceString(e));
}
private void writeInt(@NonNull File file, int value) {
writeText(file, String.valueOf(value));
}
void updateConfig() {
private synchronized void updateConfig() {
resourceHook = resourceHookSwitch.exists();
variant = readInt(variantSwitch, -1);
verboseLog = readInt(verboseLogSwitch, 0) == 1;
updateManager();
}
SQLiteStatement createEnabledModulesTable = db.compileStatement("CREATE TABLE IF NOT EXISTS enabled_modules (" +
"mid int PRIMARY KEY AUTOINCREMENT," +
public synchronized void updateManager() {
try {
PackageInfo info = PackageService.getPackageInfo(readText(managerPath, DEFAULT_MANAGER_PACKAGE_NAME), 0, 0);
if (info != null) {
managerUid = info.applicationInfo.uid;
manager = info.packageName;
}
} catch (RemoteException ignored) {
}
}
public synchronized void updateManager(@NonNull String packageName) {
writeText(managerPath, packageName);
updateManager();
}
private final SQLiteStatement createEnabledModulesTable = db.compileStatement("CREATE TABLE IF NOT EXISTS enabled_modules (" +
"mid integer PRIMARY KEY AUTOINCREMENT," +
"package_name text NOT NULL UNIQUE," +
"apk_path text NOT NULL" +
");");
SQLiteStatement createScopeTable = db.compileStatement("CREATE TABLE IF NOT EXISTS scope (" +
"mid int," +
"uid int," +
private final SQLiteStatement createScopeTable = db.compileStatement("CREATE TABLE IF NOT EXISTS scope (" +
"mid integer," +
"uid integer," +
"PRIMARY KEY (mid, uid)" +
");");
ConcurrentHashMap<Integer, ArrayList<String>> modulesForUid;
private final ConcurrentHashMap<Integer, ArrayList<String>> modulesForUid = new ConcurrentHashMap<>();
static ConfigManager getInstance() {
if (instance == null)
@ -105,6 +150,7 @@ public class ConfigManager {
createTables();
updateConfig();
isPermissive = readInt(selinuxPath, 1) == 0;
configObserver.startWatching();
}
private void createTables() {
@ -113,6 +159,7 @@ public class ConfigManager {
}
private synchronized void cacheScopes() {
modulesForUid.clear();
SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
builder.setTables("scope INNER JOIN enabled_modules ON scope.mid = enabled_modules.mid");
Cursor cursor = builder.query(db, new String[]{"scope.uid", "enabled_modules.apk_path"},
@ -132,13 +179,13 @@ public class ConfigManager {
// This is called when a new process created, use the cached result
public List<String> getModulesPathForUid(int uid) {
return modulesForUid.getOrDefault(uid, null);
return isManager(uid) ? new ArrayList<>() : modulesForUid.getOrDefault(uid, null);
}
// This is called when a new process created, use the cached result
// The signature matches Riru's
public boolean shouldSkipUid(int uid) {
return !modulesForUid.containsKey(uid);
return !modulesForUid.containsKey(uid) && !isManager(uid);
}
// This should only be called by manager, so we don't need to cache it
@ -278,4 +325,12 @@ public class ConfigManager {
return null;
}
}
public boolean isManager(String package_name) {
return package_name.equals(manager);
}
public boolean isManager(int uid) {
return uid == managerUid;
}
}

View File

@ -46,7 +46,9 @@ public class LSPApplicationService extends ILSPApplicationService.Stub {
@Override
public IBinder requestManagerBinder() throws RemoteException {
ensureRegistered();
return ServiceManager.getManagerService();
if (ConfigManager.getInstance().isManager(Binder.getCallingUid()))
return ServiceManager.getManagerService();
return null;
}
public boolean hasRegister(int uid, int pid) {

View File

@ -1,7 +1,6 @@
package io.github.lsposed.lspd.service;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
import static io.github.lsposed.lspd.service.ServiceManager.TAG;
@ -28,14 +27,18 @@ public class LSPosedService extends ILSPosedService.Stub {
@Override
public ILSPApplicationService requestApplicationService(int uid, int pid) {
if (Binder.getCallingUid() != 1000) {
Log.w(TAG, "Someone else got my binder!?");
return null;
}
if (ConfigManager.getInstance().shouldSkipUid(uid)) {
Log.d(TAG, "Skipped uid " + uid);
return null;
}
if (ServiceManager.getApplicationService().hasRegister(uid, pid)) {
Log.d(TAG, "Skipped duplicated request for uid " + uid + " pid " + pid);
return null;
}
Log.d(TAG, "returned service");
return ServiceManager.getApplicationService();
}

View File

@ -1,52 +1,15 @@
package hidden;
//import android.annotation.NonNull;
//import android.app.ActivityThread;
//import android.content.pm.ApplicationInfo;
//import android.content.pm.PackageInfo;
//import android.content.pm.PackageManager;
//import android.os.UserHandle;
import android.annotation.NonNull;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.content.res.AssetManager;
import android.os.UserManager;
import java.util.List;
import android.os.Binder;
import android.os.IBinder;
public class HiddenApiBridge {
public static int AssetManager_addAssetPath(AssetManager am, String path) {
return am.addAssetPath(path);
}
public static List<UserInfo> UserManager_getUsers(UserManager um) {
return um.getUsers();
public static IBinder Binder_allowBlocking(IBinder binder) {
return Binder.allowBlocking(binder);
}
// public static ApplicationInfo PackageManager_getApplicationInfoAsUser(PackageManager packageManager, @NonNull String packageName, int flags, int userId) throws android.content.pm.PackageManager.NameNotFoundException {
// return packageManager.getApplicationInfoAsUser(packageName, flags, userId);
// }
//
// public static PackageInfo PackageManager_getPackageInfoAsUser(PackageManager packageManager, @NonNull String packageName, int flags, int userId) throws android.content.pm.PackageManager.NameNotFoundException {
// return packageManager.getPackageInfoAsUser(packageName, flags, userId);
// }
//
// public static List<ApplicationInfo> PackageManager_getInstalledApplicationsAsUser(PackageManager packageManager, int flags, int userId) {
// return packageManager.getInstalledApplicationsAsUser(flags, userId);
// }
//
public static List<PackageInfo> PackageManager_getInstalledPackagesAsUser(PackageManager packageManager, int flags, int userId) {
return packageManager.getInstalledPackagesAsUser(flags, userId);
}
//
// public static UserHandle createUserHandle(int userId) {
// return new UserHandle(userId);
// }
//
// public static ActivityThread ActivityThread_systemMain() {
// return ActivityThread.systemMain();
// }
//
// public static String PackageInfo_overlayTarget(PackageInfo packageInfo) {
// return packageInfo.overlayTarget;
// }
}

View File

@ -55,4 +55,8 @@ public class Binder implements IBinder {
int flags) throws RemoteException {
throw new RuntimeException("STUB");
}
public static IBinder allowBlocking(IBinder binder){
throw new RuntimeException("STUB");
}
}