修正本地模式模式並解決 x86_64 的 bypass_svc 構建問題
manager / patch-loader: - 將 ConfigProvider 添加至管理器以調用已啟用模組列表,繞過安卓沙盒限制。 - 將 NeoLocalApplicationService 改寫為查詢 ConfigProvider,而非 SharedPreferences。 - 動態解析 APK 路徑透過 PackageManager 並改善錯誤記錄。 bypass_svc: - 修復 x86_64 編譯錯誤。 - 為 x86_64 提供 stub 實現。 - 將 g_is_hook_active 移至全域範圍以避免未定義符號。 - 保護 ARM64 特定的 syscall/seccomp 邏輯。
This commit is contained in:
parent
3aa1e26343
commit
59dc353e0e
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
-keep class com.beust.jcommander.** { *; }
|
-keep class com.beust.jcommander.** { *; }
|
||||||
-keep class org.lsposed.npatch.database.** { *; }
|
-keep class org.lsposed.npatch.database.** { *; }
|
||||||
|
-keep class org.lsposed.npatch.manager.ConfigProvider { *; }
|
||||||
-keep class org.lsposed.npatch.Patcher$Options { *; }
|
-keep class org.lsposed.npatch.Patcher$Options { *; }
|
||||||
-keep class org.lsposed.npatch.share.LSPConfig { *; }
|
-keep class org.lsposed.npatch.share.LSPConfig { *; }
|
||||||
-keep class org.lsposed.npatch.share.PatchConfig { *; }
|
-keep class org.lsposed.npatch.share.PatchConfig { *; }
|
||||||
|
|
|
||||||
|
|
@ -89,5 +89,12 @@
|
||||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
android:resource="@xml/file_paths" />
|
android:resource="@xml/file_paths" />
|
||||||
</provider>
|
</provider>
|
||||||
|
|
||||||
|
<provider
|
||||||
|
android:name=".manager.ConfigProvider"
|
||||||
|
android:authorities="org.lsposed.npatch.manager.provider.config"
|
||||||
|
android:exported="true"
|
||||||
|
android:enabled="true" />
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
package org.lsposed.npatch.manager
|
||||||
|
|
||||||
|
import android.content.ContentProvider
|
||||||
|
import android.content.ContentValues
|
||||||
|
import android.database.Cursor
|
||||||
|
import android.database.MatrixCursor
|
||||||
|
import android.net.Uri
|
||||||
|
import android.util.Log
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.lsposed.npatch.config.ConfigManager
|
||||||
|
|
||||||
|
class ConfigProvider : ContentProvider() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val AUTHORITY = "org.lsposed.npatch.manager.provider.config"
|
||||||
|
const val TAG = "ConfigProvider"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(): Boolean = true
|
||||||
|
|
||||||
|
override fun query(
|
||||||
|
uri: Uri,
|
||||||
|
projection: Array<out String>?,
|
||||||
|
selection: String?,
|
||||||
|
selectionArgs: Array<out String>?,
|
||||||
|
sortOrder: String?
|
||||||
|
): Cursor? {
|
||||||
|
val targetPackage = uri.getQueryParameter("package")
|
||||||
|
if (targetPackage.isNullOrEmpty()) return null
|
||||||
|
|
||||||
|
val modulesList = runBlocking {
|
||||||
|
try {
|
||||||
|
// 修正:直接使用 ConfigManager 來獲取該 APP 啟用的模組列表
|
||||||
|
ConfigManager.getModulesForApp(targetPackage).map { it.pkgName }
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Database query failed", e)
|
||||||
|
emptyList<String>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回 Cursor 給被修補的 APP
|
||||||
|
val cursor = MatrixCursor(arrayOf("packageName"))
|
||||||
|
modulesList.forEach { cursor.addRow(arrayOf(it)) }
|
||||||
|
return cursor
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getType(uri: Uri): String? = null
|
||||||
|
override fun insert(uri: Uri, values: ContentValues?): Uri? = null
|
||||||
|
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int = 0
|
||||||
|
override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array<out String>?): Int = 0
|
||||||
|
}
|
||||||
|
|
@ -1,12 +1,15 @@
|
||||||
package org.lsposed.npatch.service;
|
package org.lsposed.npatch.service;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.RemoteException;
|
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
|
import android.os.RemoteException;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import org.json.JSONArray;
|
|
||||||
import org.lsposed.npatch.util.ModuleLoader;
|
import org.lsposed.npatch.util.ModuleLoader;
|
||||||
import org.lsposed.lspd.models.Module;
|
import org.lsposed.lspd.models.Module;
|
||||||
import org.lsposed.lspd.service.ILSPApplicationService;
|
import org.lsposed.lspd.service.ILSPApplicationService;
|
||||||
|
|
@ -18,64 +21,55 @@ import java.util.List;
|
||||||
|
|
||||||
public class NeoLocalApplicationService extends ILSPApplicationService.Stub {
|
public class NeoLocalApplicationService extends ILSPApplicationService.Stub {
|
||||||
private static final String TAG = "NPatch";
|
private static final String TAG = "NPatch";
|
||||||
|
private static final String AUTHORITY = "org.lsposed.npatch.manager.provider.config";
|
||||||
|
private static final Uri PROVIDER_URI = Uri.parse("content://" + AUTHORITY + "/config");
|
||||||
|
|
||||||
private final List<Module> cachedModule;
|
private final List<Module> cachedModule;
|
||||||
|
|
||||||
public NeoLocalApplicationService(Context context) {
|
public NeoLocalApplicationService(Context context) {
|
||||||
cachedModule = Collections.synchronizedList(new ArrayList<>());
|
cachedModule = Collections.synchronizedList(new ArrayList<>());
|
||||||
loadModulesFromSharedPreferences(context);
|
loadModulesFromProvider(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadModulesFromSharedPreferences(Context context) {
|
private void loadModulesFromProvider(Context context) {
|
||||||
var shared = context.getSharedPreferences("npatch", Context.MODE_PRIVATE);
|
PackageManager pm = context.getPackageManager();
|
||||||
|
String myPackageName = context.getPackageName();
|
||||||
|
|
||||||
|
Uri queryUri = PROVIDER_URI.buildUpon()
|
||||||
|
.appendQueryParameter("package", myPackageName)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try (Cursor cursor = context.getContentResolver().query(queryUri, null, null, null, null)) {
|
||||||
|
if (cursor == null) {
|
||||||
|
Log.w(TAG, "NeoLocal: Cannot reach Manager Provider.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (cursor.moveToNext()) {
|
||||||
|
int colIndex = cursor.getColumnIndex("packageName");
|
||||||
|
if (colIndex != -1) {
|
||||||
|
loadSingleModule(pm, cursor.getString(colIndex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "NeoLocal: Provider query failed", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadSingleModule(PackageManager pm, String pkgName) {
|
||||||
try {
|
try {
|
||||||
var modulesJsonString = shared.getString("modules", "[]");
|
ApplicationInfo appInfo = pm.getApplicationInfo(pkgName, 0);
|
||||||
Log.i(TAG, "Loading modules from local SharedPreferences...");
|
Module m = new Module();
|
||||||
|
m.packageName = pkgName;
|
||||||
|
m.apkPath = appInfo.sourceDir;
|
||||||
|
|
||||||
if (modulesJsonString.equals("{}")) {
|
if (m.apkPath != null && new File(m.apkPath).exists()) {
|
||||||
modulesJsonString = "[]";
|
m.file = ModuleLoader.loadModule(m.apkPath);
|
||||||
}
|
cachedModule.add(m);
|
||||||
|
Log.i(TAG, "NeoLocal: Loaded module " + pkgName);
|
||||||
var mArr = new JSONArray(modulesJsonString);
|
|
||||||
if (mArr.length() > 0) {
|
|
||||||
Log.i(TAG, "Found " + mArr.length() + " modules.");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < mArr.length(); i++) {
|
|
||||||
var mObj = mArr.getJSONObject(i);
|
|
||||||
var m = new Module();
|
|
||||||
|
|
||||||
m.packageName = mObj.optString("packageName", null);
|
|
||||||
var apkPath = mObj.optString("path", null);
|
|
||||||
|
|
||||||
if (m.packageName == null) {
|
|
||||||
Log.w(TAG, "Module at index " + i + " has no package name, skipping.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果路徑為 null 或文件不存在,嘗試從 PackageManager 恢復
|
|
||||||
if (apkPath == null || !new File(apkPath).exists()) {
|
|
||||||
Log.w(TAG, "Module:" + m.packageName + " path not available, attempting reset.");
|
|
||||||
try {
|
|
||||||
var info = context.getPackageManager().getApplicationInfo(m.packageName, 0);
|
|
||||||
m.apkPath = info.sourceDir;
|
|
||||||
Log.i(TAG, "Module:" + m.packageName + " path reset to " + m.apkPath);
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(TAG, "Failed to get ApplicationInfo for module: " + m.packageName, e);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
m.apkPath = apkPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m.apkPath != null) {
|
|
||||||
m.file = ModuleLoader.loadModule(m.apkPath);
|
|
||||||
cachedModule.add(m);
|
|
||||||
} else {
|
|
||||||
Log.w(TAG, "Could not load module " + m.packageName + ": final path is null.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
Log.e(TAG, "Error loading modules from SharedPreferences.", e);
|
Log.e(TAG, "NeoLocal: Failed to load " + pkgName, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -90,15 +84,9 @@ public class NeoLocalApplicationService extends ILSPApplicationService.Stub {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getPrefsPath(String packageName) throws RemoteException {
|
public String getPrefsPath(String packageName) throws RemoteException { return "/data/data/" + packageName + "/shared_prefs/"; }
|
||||||
return "/data/data/" + packageName + "/shared_prefs/";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ParcelFileDescriptor requestInjectedManagerBinder(List<IBinder> binder) throws RemoteException {
|
public ParcelFileDescriptor requestInjectedManagerBinder(List<IBinder> binder) throws RemoteException { return null; }
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IBinder asBinder() {
|
public IBinder asBinder() {
|
||||||
return this;
|
return this;
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,11 @@
|
||||||
namespace lspd {
|
namespace lspd {
|
||||||
|
|
||||||
// --- 共用結構與變數 ---
|
// --- 共用結構與變數 ---
|
||||||
|
// 將此變數移出 #if 區塊,確保 IDE 和編譯器在任何架構下都能識別,避免未定義錯誤。
|
||||||
|
static bool g_is_hook_active = false;
|
||||||
|
|
||||||
|
#if defined(__aarch64__)
|
||||||
|
|
||||||
struct SyscallRequest {
|
struct SyscallRequest {
|
||||||
long sys_no;
|
long sys_no;
|
||||||
long args[6];
|
long args[6];
|
||||||
|
|
@ -31,7 +36,6 @@ namespace lspd {
|
||||||
};
|
};
|
||||||
|
|
||||||
static pthread_t g_trusted_thread;
|
static pthread_t g_trusted_thread;
|
||||||
static bool g_is_hook_active = false;
|
|
||||||
static std::queue<SyscallRequest*> g_request_queue;
|
static std::queue<SyscallRequest*> g_request_queue;
|
||||||
static std::mutex g_queue_mtx;
|
static std::mutex g_queue_mtx;
|
||||||
static std::condition_variable g_queue_cv;
|
static std::condition_variable g_queue_cv;
|
||||||
|
|
@ -53,8 +57,8 @@ namespace lspd {
|
||||||
if (req) {
|
if (req) {
|
||||||
// 執行真正的 syscall
|
// 執行真正的 syscall
|
||||||
req->result = syscall(req->sys_no,
|
req->result = syscall(req->sys_no,
|
||||||
req->args[0], req->args[1], req->args[2],
|
req->args[0], req->args[1], req->args[2],
|
||||||
req->args[3], req->args[4], req->args[5]);
|
req->args[3], req->args[4], req->args[5]);
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(req->mtx);
|
std::lock_guard<std::mutex> lock(req->mtx);
|
||||||
|
|
@ -74,7 +78,6 @@ namespace lspd {
|
||||||
SyscallRequest req;
|
SyscallRequest req;
|
||||||
|
|
||||||
// ARM64: 從 regs 讀取 (x8=sys_no, x0-x5=args)
|
// ARM64: 從 regs 讀取 (x8=sys_no, x0-x5=args)
|
||||||
// 直接存取 ARM64 特有的 regs 結構
|
|
||||||
req.sys_no = ctx->uc_mcontext.regs[8];
|
req.sys_no = ctx->uc_mcontext.regs[8];
|
||||||
for (int i = 0; i < 6; ++i) {
|
for (int i = 0; i < 6; ++i) {
|
||||||
req.args[i] = ctx->uc_mcontext.regs[i];
|
req.args[i] = ctx->uc_mcontext.regs[i];
|
||||||
|
|
@ -98,12 +101,17 @@ namespace lspd {
|
||||||
ctx->uc_mcontext.regs[0] = req.result;
|
ctx->uc_mcontext.regs[0] = req.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- JNI 接口層 ---
|
#endif // 結束 __aarch64__ 專用區塊
|
||||||
|
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// JNI 接口層 (處理架構差異)
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
LSP_DEF_NATIVE_METHOD(jboolean, SvcBypass, initSvcHook) {
|
LSP_DEF_NATIVE_METHOD(jboolean, SvcBypass, initSvcHook) {
|
||||||
// 如果已經激活,直接返回成功
|
|
||||||
if (g_is_hook_active) return JNI_TRUE;
|
if (g_is_hook_active) return JNI_TRUE;
|
||||||
|
|
||||||
|
#if defined(__aarch64__)
|
||||||
int ret = pthread_create(&g_trusted_thread, nullptr, trusted_thread_loop, nullptr);
|
int ret = pthread_create(&g_trusted_thread, nullptr, trusted_thread_loop, nullptr);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
LOGE("SvcBypass: Failed to create trusted thread");
|
LOGE("SvcBypass: Failed to create trusted thread");
|
||||||
|
|
@ -122,16 +130,22 @@ namespace lspd {
|
||||||
g_is_hook_active = true;
|
g_is_hook_active = true;
|
||||||
LOGI("SvcBypass: Initialized successfully (ARM64)");
|
LOGI("SvcBypass: Initialized successfully (ARM64)");
|
||||||
return JNI_TRUE;
|
return JNI_TRUE;
|
||||||
|
#else
|
||||||
|
// x86/x86_64: 僅標記為激活,但不做實際 Hook
|
||||||
|
g_is_hook_active = true;
|
||||||
|
LOGI("SvcBypass: Skipped on non-ARM64 architecture");
|
||||||
|
return JNI_TRUE;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
LSP_DEF_NATIVE_METHOD(void, SvcBypass, enableSvcRedirect,
|
LSP_DEF_NATIVE_METHOD(void, SvcBypass, enableSvcRedirect,
|
||||||
jstring path, jstring orig, jstring pkg) {
|
jstring path, jstring orig, jstring pkg) {
|
||||||
if (!g_is_hook_active) {
|
if (!g_is_hook_active) {
|
||||||
LOGW("SvcBypass: Hook not initialized.");
|
LOGW("SvcBypass: Hook not initialized.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ARM64 BPF 規則
|
#if defined(__aarch64__)
|
||||||
struct sock_filter filter[] = {
|
struct sock_filter filter[] = {
|
||||||
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, (offsetof(struct seccomp_data, nr))),
|
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, (offsetof(struct seccomp_data, nr))),
|
||||||
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, __NR_openat, 0, 1),
|
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, __NR_openat, 0, 1),
|
||||||
|
|
@ -154,6 +168,7 @@ namespace lspd {
|
||||||
} else {
|
} else {
|
||||||
LOGI("SvcBypass: Seccomp filter applied (ARM64)");
|
LOGI("SvcBypass: Seccomp filter applied (ARM64)");
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
LSP_DEF_NATIVE_METHOD(void, SvcBypass, disableSvcRedirect) {
|
LSP_DEF_NATIVE_METHOD(void, SvcBypass, disableSvcRedirect) {
|
||||||
|
|
@ -165,7 +180,11 @@ namespace lspd {
|
||||||
}
|
}
|
||||||
|
|
||||||
LSP_DEF_NATIVE_METHOD(jstring, SvcBypass, getDebugInfo) {
|
LSP_DEF_NATIVE_METHOD(jstring, SvcBypass, getDebugInfo) {
|
||||||
|
#if defined(__aarch64__)
|
||||||
return env->NewStringUTF("SvcBypass: Active (ARM64)");
|
return env->NewStringUTF("SvcBypass: Active (ARM64)");
|
||||||
|
#else
|
||||||
|
return env->NewStringUTF("SvcBypass: Stub (Non-ARM64)");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
LSP_DEF_NATIVE_METHOD(jint, SvcBypass, getCurrentPid) {
|
LSP_DEF_NATIVE_METHOD(jint, SvcBypass, getCurrentPid) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue