修正本地模式模式並解決 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:
NkBe 2026-02-10 15:30:34 +08:00
parent 3aa1e26343
commit 59dc353e0e
No known key found for this signature in database
GPG Key ID: 9FACEE0DB6DF678E
5 changed files with 132 additions and 66 deletions

View File

@ -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 { *; }

View File

@ -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>

View File

@ -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
}

View File

@ -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;

View File

@ -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) {