Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion core/src/main/java/org/lsposed/lspd/core/Startup.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import android.app.LoadedApk;
import android.content.pm.ApplicationInfo;
import android.content.res.CompatibilityInfo;
import android.os.IBinder;

import com.android.internal.os.ZygoteInit;

Expand All @@ -34,6 +35,7 @@
import org.lsposed.lspd.hooker.LoadedApkCtorHooker;
import org.lsposed.lspd.hooker.LoadedApkCreateCLHooker;
import org.lsposed.lspd.hooker.OpenDexFileHooker;
import org.lsposed.lspd.hooker.StartBootstrapServicesHooker;
import org.lsposed.lspd.impl.LSPosedContext;
import org.lsposed.lspd.impl.LSPosedHelper;
import org.lsposed.lspd.service.ILSPApplicationService;
Expand Down Expand Up @@ -63,14 +65,31 @@ private static void startBootstrapHook(boolean isSystem) {
LSPosedHelper.hookAllMethods(AttachHooker.class, ActivityThread.class, "attach");
}

public static void bootstrapXposed() {
public static void bootstrapXposed(boolean systemServerStarted) {
// Initialize the Xposed framework
try {
startBootstrapHook(XposedInit.startsSystemServer);
XposedInit.loadLegacyModules();
} catch (Throwable t) {
Utils.logE("error during Xposed initialization", t);
}

if (systemServerStarted) {
Utils.logD("Manually triggering system_server module load for late injection");

IBinder activityService = android.os.ServiceManager.getService("activity");
if (activityService == null) {
Utils.logE("Activity service not found! Cannot get SystemServer ClassLoader.");
return;
}

// Maintain state consistency for the rest of the Vector framework
HandleSystemServerProcessHooker.systemServerCL = activityService.getClass().getClassLoader();
HandleSystemServerProcessHooker.after();
StartBootstrapServicesHooker.before();

Utils.logI("Late system_server injection successfully completed.");
}
}

public static void initXposed(boolean isSystem, String processName, String appDir, ILSPApplicationService service) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,17 @@ public interface Callback {
void onSystemServerLoaded(ClassLoader classLoader);
}

public static volatile ClassLoader systemServerCL;
public static volatile ClassLoader systemServerCL = null;
public static volatile Callback callback = null;

@SuppressLint("PrivateApi")
public static void after() {
Hookers.logD("ZygoteInit#handleSystemServerProcess() starts");
try {
// get system_server classLoader
systemServerCL = Thread.currentThread().getContextClassLoader();
if (systemServerCL == null) {
// get system_server classLoader
systemServerCL = Thread.currentThread().getContextClassLoader();
}
// deopt methods in SYSTEMSERVERCLASSPATH
PrebuiltMethodsDeopter.deoptSystemServerMethods(systemServerCL);
var clazz = Class.forName("com.android.server.SystemServer", false, systemServerCL);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@

public class LSPSystemServerService extends ILSPSystemServerService.Stub implements IBinder.DeathRecipient {

public static final String PROXY_SERVICE_NAME = "serial";

private final String proxyServiceName;
private IBinder originService = null;
private int requested;

Expand All @@ -42,12 +41,13 @@ public boolean systemServerRequested() {
}

public void putBinderForSystemServer() {
android.os.ServiceManager.addService(PROXY_SERVICE_NAME, this);
android.os.ServiceManager.addService(proxyServiceName, this);
binderDied();
}

public LSPSystemServerService(int maxRetry) {
Log.d(TAG, "LSPSystemServerService::LSPSystemServerService");
public LSPSystemServerService(int maxRetry, String serviceName) {
Log.d(TAG, "LSPSystemServerService::LSPSystemServerService with proxy " + serviceName);
proxyServiceName = serviceName;
requested = -maxRetry;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
// Registers a callback when system is registering an authentic "serial" service
Expand All @@ -56,7 +56,7 @@ public LSPSystemServerService(int maxRetry) {
@Override
public void onRegistration(String name, IBinder binder) {
Log.d(TAG, "LSPSystemServerService::LSPSystemServerService onRegistration: " + name + " " + binder);
if (name.equals(PROXY_SERVICE_NAME) && binder != null && binder != LSPSystemServerService.this) {
if (name.equals(proxyServiceName) && binder != null && binder != LSPSystemServerService.this) {
Log.d(TAG, "Register " + name + " " + binder);
originService = binder;
LSPSystemServerService.this.linkToDeath();
Expand All @@ -69,7 +69,7 @@ public IBinder asBinder() {
}
};
try {
getSystemServiceManager().registerForNotifications(PROXY_SERVICE_NAME, serviceCallback);
getSystemServiceManager().registerForNotifications(proxyServiceName, serviceCallback);
} catch (Throwable e) {
Log.e(TAG, "unregister: ", e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,11 @@ public void dispatchSystemServerContext(IBinder appThread, IBinder activityToken
registerOpenManagerReceiver();
registerModuleScopeReceiver();
registerUidObserver();

if (ServiceManager.isLateInject) {
Log.i(TAG, "System already booted during late injection. Manually triggering boot completed.");
dispatchBootCompleted(null);
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ public class ServiceManager {
private static LogcatService logcatService = null;
private static Dex2OatService dex2OatService = null;

public static boolean isLateInject = false;
public static String proxyServiceName = "serial";

private static final ExecutorService executorService = Executors.newSingleThreadExecutor();

@RequiresApi(Build.VERSION_CODES.Q)
Expand Down Expand Up @@ -104,9 +107,13 @@ public static void start(String[] args) {
systemServerMaxRetry = Integer.parseInt(arg.substring(arg.lastIndexOf('=') + 1));
} catch (Throwable ignored) {
}
} else if (arg.equals("--late-inject")) {
isLateInject = true;
proxyServiceName = "serial_vector";
}
}
Log.i(TAG, "starting server...");

Log.i(TAG, "Vector daemon started: lateInject: " + isLateInject);
Log.i(TAG, String.format("version %s (%d)", BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE));

Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
Expand Down Expand Up @@ -136,7 +143,7 @@ public static void start(String[] args) {
mainService = new LSPosedService();
applicationService = new LSPApplicationService();
managerService = new LSPManagerService();
systemServerService = new LSPSystemServerService(systemServerMaxRetry);
systemServerService = new LSPSystemServerService(systemServerMaxRetry, proxyServiceName);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
dex2OatService = new Dex2OatService();
dex2OatService.start();
Expand Down
2 changes: 1 addition & 1 deletion zygisk/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
-keepclasseswithmembers class org.matrix.vector.core.Main {
public static void forkCommon(boolean, java.lang.String, java.lang.String, android.os.IBinder);
public static void forkCommon(boolean, boolean, java.lang.String, java.lang.String, android.os.IBinder);
}
-keepclasseswithmembers,includedescriptorclasses class * {
native <methods>;
Expand Down
4 changes: 3 additions & 1 deletion zygisk/src/main/cpp/include/ipc_bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,11 @@ class IPCBridge {
/**
* @brief Requests the system_server's dedicated Binder from the host service.
* @param env JNI environment pointer.
* @param bridgeServiceName rendezvous point used by the system_server
* @return A ScopedLocalRef to the Binder object, or nullptr on failure.
*/
lsplant::ScopedLocalRef<jobject> RequestSystemServerBinder(JNIEnv *env);
lsplant::ScopedLocalRef<jobject> RequestSystemServerBinder(JNIEnv *env,
std::string bridgeServiceName);

/**
* @brief Asks the system_server binder for the application manager binder.
Expand Down
13 changes: 6 additions & 7 deletions zygisk/src/main/cpp/ipc_bridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,6 @@ class BinderCaller {
// The name of the system service we use as a rendezvous point to find our manager service.
// Using "activity" is a common technique as it's always available.
constexpr auto kBridgeServiceName = "activity"sv;
// A different rendezvous point used only by the system_server.
constexpr auto kSystemServerBridgeServiceName = "serial"sv;

// Transaction codes for specific actions.
constexpr jint kBridgeTransactionCode = ('_' << 24) | ('V' << 16) | ('E' << 8) | 'C';
Expand Down Expand Up @@ -299,14 +297,14 @@ lsplant::ScopedLocalRef<jobject> IPCBridge::RequestAppBinder(JNIEnv *env, jstrin
return result_binder;
}

lsplant::ScopedLocalRef<jobject> IPCBridge::RequestSystemServerBinder(JNIEnv *env) {
lsplant::ScopedLocalRef<jobject> IPCBridge::RequestSystemServerBinder(
JNIEnv *env, std::string bridgeServiceName) {
if (!initialized_) {
LOGE("RequestSystemServerBinder failed: IPCBridge not initialized.");
return {env, nullptr};
}

auto service_name =
lsplant::ScopedLocalRef(env, env->NewStringUTF(kSystemServerBridgeServiceName.data()));
auto service_name = lsplant::ScopedLocalRef(env, env->NewStringUTF(bridgeServiceName.data()));
lsplant::ScopedLocalRef<jobject> binder = {env, nullptr};

// The system_server might start its services slightly after Zygisk injects us.
Expand All @@ -315,11 +313,12 @@ lsplant::ScopedLocalRef<jobject> IPCBridge::RequestSystemServerBinder(JNIEnv *en
binder = lsplant::JNI_CallStaticObjectMethod(env, service_manager_class_,
get_service_method_, service_name.get());
if (binder) {
LOGI("Got system server binder on attempt {}.", i + 1);
LOGI("Got system server binder via {} on attempt {}.", bridgeServiceName.data(), i + 1);
return binder;
}
if (i < 2) {
LOGW("Failed to get system server binder, will retry in 1 second...");
LOGW("Failed to get system server binder via {}, will retry in 1 second...",
bridgeServiceName.data());
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
Expand Down
21 changes: 15 additions & 6 deletions zygisk/src/main/cpp/module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ const char *const kHostPackageName = INJECTED_PACKAGE_NAME;
const char *const kManagePackageName = MANAGER_PACKAGE_NAME;
constexpr uid_t GID_INET = 3003; // Android's Internet group ID.

enum RuntimeFlags : uint32_t {
// Flags defined by NeoZygisk
LATE_INJECT = 1 << 30,
};

// A simply ConfigBridge implemnetation holding obfuscation maps in memory
using obfuscation_map_t = std::map<std::string, std::string>;
class ConfigImpl : public ConfigBridge {
Expand Down Expand Up @@ -338,9 +343,9 @@ void VectorModule::postAppSpecialize(const zygisk::AppSpecializeArgs *args) {
this->SetupEntryClass(env_);

// Hand off control to the Java side of the framework.
this->FindAndCall(env_, "forkCommon",
"(ZLjava/lang/String;Ljava/lang/String;Landroid/os/IBinder;)V", JNI_FALSE,
args->nice_name, args->app_data_dir, binder.get(), is_manager_app_);
this->FindAndCall(
env_, "forkCommon", "(ZZLjava/lang/String;Ljava/lang/String;Landroid/os/IBinder;)V",
JNI_FALSE, JNI_FALSE, args->nice_name, args->app_data_dir, binder.get(), is_manager_app_);

LOGV("Injected Vector framework into '{}'.", nice_name_str.get());
SetAllowUnload(false); // We are injected, PREVENT module unloading.
Expand Down Expand Up @@ -385,7 +390,10 @@ void VectorModule::postServerSpecialize(const zygisk::ServerSpecializeArgs *args

// --- Framework Injection for System Server ---
auto &ipc_bridge = IPCBridge::GetInstance();
auto system_binder = ipc_bridge.RequestSystemServerBinder(env_);
std::string bridgeServiceName = "serial";
bool is_late_inject = (args->runtime_flags & RuntimeFlags::LATE_INJECT) != 0;
if (is_late_inject) bridgeServiceName = "serial_vector";
auto system_binder = ipc_bridge.RequestSystemServerBinder(env_, bridgeServiceName);
if (!system_binder) {
LOGE("Failed to get system server IPC binder. Aborting injection.");
SetAllowUnload(true); // Allow unload on failure.
Expand Down Expand Up @@ -423,8 +431,9 @@ void VectorModule::postServerSpecialize(const zygisk::ServerSpecializeArgs *args

auto system_name = lsplant::ScopedLocalRef(env_, env_->NewStringUTF("system"));
this->FindAndCall(env_, "forkCommon",
"(ZLjava/lang/String;Ljava/lang/String;Landroid/os/IBinder;)V", JNI_TRUE,
system_name.get(), nullptr, manager_binder.get(), is_manager_app_);
"(ZZLjava/lang/String;Ljava/lang/String;Landroid/os/IBinder;)V", JNI_TRUE,
is_late_inject, system_name.get(), nullptr, manager_binder.get(),
is_manager_app_);

LOGI("Injected Vector framework into system_server.");
SetAllowUnload(false); // We are injected, PREVENT module unloading.
Expand Down
11 changes: 9 additions & 2 deletions zygisk/src/main/kotlin/org/matrix/vector/core/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,19 @@ object Main {
* Shared initialization logic for both System Server and Application processes.
*
* @param isSystem True if this is the system_server process.
* @param isLateInject True if Zygisk APIs are not invoked via hooks
* @param niceName The process name (e.g., package name or "system").
* @param appDir The application's data directory.
* @param binder The Binder token associated with the application service.
*/
@JvmStatic
fun forkCommon(isSystem: Boolean, niceName: String, appDir: String?, binder: IBinder) {
fun forkCommon(
isSystem: Boolean,
isLateInject: Boolean,
niceName: String,
appDir: String?,
binder: IBinder,
) {
// Initialize system-specific resolution hooks if in system_server
if (isSystem) {
ParasiticManagerSystemHooker.start()
Expand All @@ -46,6 +53,6 @@ object Main {

// Standard Xposed module loading for third-party apps
Utils.logI("Loading Vector/Xposed for $niceName (UID: ${Process.myUid()})")
Startup.bootstrapXposed()
Startup.bootstrapXposed(isSystem && isLateInject)
}
}
Loading