diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ffd80e2..ec2d453 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -60,7 +60,7 @@ jobs: uses: actions/setup-java@v5 with: java-version: '21' - distribution: 'adopt' + distribution: 'zulu' - name: 設定 Gradle uses: gradle/actions/setup-gradle@v5 @@ -76,7 +76,7 @@ jobs: - name: 設定 CMake uses: jwlawson/actions-setup-cmake@v2 with: - cmake-version: '4.0.3' + cmake-version: '4.1.1' - name: 移除 Android 的 cmake shell: bash diff --git a/build.gradle.kts b/build.gradle.kts index 9721b23..3e3370e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -108,7 +108,7 @@ fun Project.configureBaseExtension() { arguments += "-DEXTERNAL_ROOT=${File(rootDir.absolutePath, "core/external")}" arguments += "-DCORE_ROOT=${File(rootDir.absolutePath, "core/core/src/main/jni")}" - abiFilters("arm64-v8a", "armeabi-v7a", "x86", "x86_64") + abiFilters("arm64-v8a") val flags = arrayOf( "-Wall", "-Qunused-arguments", diff --git a/manager/proguard-rules-debug.pro b/manager/proguard-rules-debug.pro index 40c254f..bfc5205 100644 --- a/manager/proguard-rules-debug.pro +++ b/manager/proguard-rules-debug.pro @@ -3,6 +3,8 @@ -keep class org.lsposed.npatch.Patcher$Options { *; } -keep class org.lsposed.npatch.share.LSPConfig { *; } -keep class org.lsposed.npatch.share.PatchConfig { *; } +-keep class org.lsposed.lspd.nativebridge.** { *; } +-keep class org.lsposed.npatch.loader.SigBypass { *; } -keepclassmembers class org.lsposed.patch.NPatch { private ; } diff --git a/manager/proguard-rules.pro b/manager/proguard-rules.pro index 9781138..41a0a0d 100644 --- a/manager/proguard-rules.pro +++ b/manager/proguard-rules.pro @@ -19,6 +19,8 @@ -keep class org.lsposed.npatch.Patcher$Options { *; } -keep class org.lsposed.npatch.share.LSPConfig { *; } -keep class org.lsposed.npatch.share.PatchConfig { *; } +-keep class org.lsposed.lspd.nativebridge.** { *; } +-keep class org.lsposed.npatch.loader.SigBypass { *; } -dontwarn com.google.auto.value.AutoValue$Builder -dontwarn com.google.auto.value.AutoValue -dontwarn com.squareup.moshi.** diff --git a/manager/src/main/java/org/lsposed/npatch/ui/page/NewPatchScreen.kt b/manager/src/main/java/org/lsposed/npatch/ui/page/NewPatchScreen.kt index e1bdf6f..f8914cc 100644 --- a/manager/src/main/java/org/lsposed/npatch/ui/page/NewPatchScreen.kt +++ b/manager/src/main/java/org/lsposed/npatch/ui/page/NewPatchScreen.kt @@ -281,6 +281,7 @@ private fun sigBypassLvStr(level: Int) = when (level) { 0 -> stringResource(R.string.patch_sigbypasslv0) 1 -> stringResource(R.string.patch_sigbypasslv1) 2 -> stringResource(R.string.patch_sigbypasslv2) + 3 -> stringResource(R.string.patch_sigbypasslv3) else -> throw IllegalArgumentException("Invalid sigBypassLv: $level") } @@ -384,7 +385,7 @@ private fun PatchOptionsBody(modifier: Modifier, onAddEmbed: () -> Unit) { ) } ) { - repeat(3) { + repeat(4) { DropdownMenuItem( text = { Row(verticalAlignment = Alignment.CenterVertically) { diff --git a/manager/src/main/res/values-zh-rCN/strings.xml b/manager/src/main/res/values-zh-rCN/strings.xml index da4330c..74b57ad 100644 --- a/manager/src/main/res/values-zh-rCN/strings.xml +++ b/manager/src/main/res/values-zh-rCN/strings.xml @@ -64,6 +64,7 @@ lv0: 关闭 lv1: 绕过 PM lv2: 绕过 PM + openat (libc) + lv3: 绕过 PM + openat (libc) + SVC (仅 arm64) (测试版) 覆写版本号 将修补的 App 版本号重写为 1\n这将允许后续降级安装,并且通常来说这不会影响应用实际感知到的版本号 修补新包名 diff --git a/manager/src/main/res/values-zh-rTW/strings.xml b/manager/src/main/res/values-zh-rTW/strings.xml index 3768001..37e7c63 100644 --- a/manager/src/main/res/values-zh-rTW/strings.xml +++ b/manager/src/main/res/values-zh-rTW/strings.xml @@ -64,6 +64,7 @@ lv0: 關閉 lv1: 繞過 PM lv2: 繞過 PM + openat (libc) + lv3: 繞過 PM + openat (libc) + SVC (僅 arm64) (測試用) 覆蓋版本編號 將打包應用程式的版本編號改成 1\n允許以後降級安裝,一般來說,這不會影響應用程式實際感知的版本編號。 修補新套件名 diff --git a/manager/src/main/res/values/strings.xml b/manager/src/main/res/values/strings.xml index c0a6851..ae7f373 100644 --- a/manager/src/main/res/values/strings.xml +++ b/manager/src/main/res/values/strings.xml @@ -66,6 +66,7 @@ lv0: Off lv1: Bypass PM lv2: Bypass PM + openat (libc) + lv3: Bypass PM + openat(libc) + SVC (v8a only) (testing) Patch New PackageName Input a new package for app Override version code diff --git a/meta-loader/src/main/java/org/lsposed/npatch/metaloader/LSPAppComponentFactoryStub.java b/meta-loader/src/main/java/org/lsposed/npatch/metaloader/LSPAppComponentFactoryStub.java index 07eea30..3268d9c 100644 --- a/meta-loader/src/main/java/org/lsposed/npatch/metaloader/LSPAppComponentFactoryStub.java +++ b/meta-loader/src/main/java/org/lsposed/npatch/metaloader/LSPAppComponentFactoryStub.java @@ -45,9 +45,7 @@ public class LSPAppComponentFactoryStub extends AppComponentFactory { private static void bootstrap() { try { - archToLib.put("arm", "armeabi-v7a"); archToLib.put("arm64", "arm64-v8a"); - archToLib.put("x86", "x86"); archToLib.put("x86_64", "x86_64"); var cl = Objects.requireNonNull(LSPAppComponentFactoryStub.class.getClassLoader()); diff --git a/patch-loader/src/main/java/org/lsposed/lspd/nativebridge/SigBypass.java b/patch-loader/src/main/java/org/lsposed/lspd/nativebridge/SigBypass.java index 8fd4ccf..9e8a136 100644 --- a/patch-loader/src/main/java/org/lsposed/lspd/nativebridge/SigBypass.java +++ b/patch-loader/src/main/java/org/lsposed/lspd/nativebridge/SigBypass.java @@ -1,5 +1,6 @@ package org.lsposed.lspd.nativebridge; public class SigBypass { - public static native void enableOpenatHook(String origApkPath, String cacheApkPath); + public static native void enableOpenatHook(String patchedApkPath, String originalApkPath, String packageName); + public static native void disableOpenatHook(); } diff --git a/patch-loader/src/main/java/org/lsposed/lspd/nativebridge/SvcBypass.java b/patch-loader/src/main/java/org/lsposed/lspd/nativebridge/SvcBypass.java new file mode 100644 index 0000000..aa86542 --- /dev/null +++ b/patch-loader/src/main/java/org/lsposed/lspd/nativebridge/SvcBypass.java @@ -0,0 +1,31 @@ +package org.lsposed.lspd.nativebridge; + +public class SvcBypass { + // 核心功能方法 + public static native boolean initSvcHook(); + public static native void enableSvcRedirect(String path, String orig, String pkg); + public static native void disableSvcRedirect(); + + // 狀態檢查與除錯方法 + public static native boolean isSvcHookActive(); + public static native void logSvcHookStats(); + public static native String getDebugInfo(); + + // 進程與檔案描述符 (FD) 相關方法 + public static native int getCurrentPid(); + public static native int getInitialPid(); + public static native boolean isChildProcess(); + + public static native String checkFd(int fd); + public static native int dupFd(int fd); + public static native long getFdInode(int fd); + public static native boolean isSystemFile(int fd); + + // 系統 APK 與證書相關方法 + public static native int findSystemApkFd(String path); + public static native String[][] getSystemApkFds(); + public static native void refreshSystemFds(); + + public static native byte[] readCertificateFromFd(int fd); + public static native byte[] readCertificateFromPath(String path); +} diff --git a/patch-loader/src/main/java/org/lsposed/npatch/loader/SigBypass.java b/patch-loader/src/main/java/org/lsposed/npatch/loader/SigBypass.java index 2910455..d04ac51 100644 --- a/patch-loader/src/main/java/org/lsposed/npatch/loader/SigBypass.java +++ b/patch-loader/src/main/java/org/lsposed/npatch/loader/SigBypass.java @@ -15,15 +15,21 @@ import android.util.Log; import com.google.gson.JsonSyntaxException; -import org.lsposed.npatch.loader.util.XLog; -import org.lsposed.npatch.share.Constants; import org.json.JSONException; import org.json.JSONObject; +import org.lsposed.lspd.nativebridge.SvcBypass; +import org.lsposed.npatch.loader.util.XLog; +import org.lsposed.npatch.share.Constants; +import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.FileInputStream; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; +import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import de.robv.android.xposed.XC_MethodHook; @@ -34,6 +40,7 @@ public class SigBypass { private static final String TAG = "NPatch-SigBypass"; private static final Map signatures = new HashMap<>(); + private static String cachedOriginalApkPath; private static void replaceSignature(Context context, PackageInfo packageInfo) { boolean hasSignature = (packageInfo.signatures != null && packageInfo.signatures.length != 0) || packageInfo.signingInfo != null; @@ -145,17 +152,96 @@ public class SigBypass { } } + private static String extractOriginalApk(Context context) { + File cacheDir = new File(context.getCacheDir(), "npatch/origin"); + if (!cacheDir.exists()) cacheDir.mkdirs(); + + try (ZipFile sourceFile = new ZipFile(context.getPackageResourcePath())) { + ZipEntry entry = sourceFile.getEntry(ORIGINAL_APK_ASSET_PATH); + if (entry == null) { + Log.e(TAG, "Original APK not found in assets!"); + return null; + } + + File targetFile = new File(cacheDir, entry.getCrc() + ".apk"); + if (targetFile.exists() && targetFile.length() == entry.getSize()) { + return targetFile.getAbsolutePath(); + } + + try (InputStream is = sourceFile.getInputStream(entry); + FileOutputStream fos = new FileOutputStream(targetFile)) { + byte[] buffer = new byte[8192]; + int length; + while ((length = is.read(buffer)) > 0) { + fos.write(buffer, 0, length); + } + } + return targetFile.getAbsolutePath(); + } catch (IOException e) { + Log.e(TAG, "Failed to extract original APK", e); + return null; + } + } + + private static void hookJavaIO(String currentApkPath, String originalApkPath) { + XC_MethodHook redirectHook = new XC_MethodHook() { + @Override + protected void beforeHookedMethod(MethodHookParam param) { + if (param.args.length > 0) { + if (param.args[0] instanceof String) { + String path = (String) param.args[0]; + if (path.equals(currentApkPath)) { + param.args[0] = originalApkPath; + } + } else if (param.args[0] instanceof File) { + File file = (File) param.args[0]; + if (file.getPath().equals(currentApkPath)) { + param.args[0] = new File(originalApkPath); + } + } + } + } + }; + XposedBridge.hookAllConstructors(ZipFile.class, redirectHook); + try { + XposedBridge.hookAllConstructors(FileInputStream.class, redirectHook); + } catch (Throwable ignored) {} + } + static void doSigBypass(Context context, int sigBypassLevel) throws IOException { + // Level 1: Java PMS Hook if (sigBypassLevel >= Constants.SIGBYPASS_LV_PM) { hookPackageParser(context); proxyPackageInfoCreator(context); } if (sigBypassLevel >= Constants.SIGBYPASS_LV_PM_OPENAT) { - String cacheApkPath; - try (ZipFile sourceFile = new ZipFile(context.getPackageResourcePath())) { - cacheApkPath = context.getCacheDir() + "/npatch/origin/" + sourceFile.getEntry(ORIGINAL_APK_ASSET_PATH).getCrc() + ".apk"; + String currentApkPath = context.getPackageResourcePath(); + cachedOriginalApkPath = extractOriginalApk(context); + + if (cachedOriginalApkPath != null) { + // 1. Java Core stability + hookJavaIO(currentApkPath, cachedOriginalApkPath); + // 2. Native OpenAt Hook + org.lsposed.lspd.nativebridge.SigBypass.enableOpenatHook( + currentApkPath, + cachedOriginalApkPath, + context.getPackageName() + ); + + // Level 3: SVC (Seccomp) Hook + if (sigBypassLevel >= Constants.SIGBYPASS_LV_SVC) { + if (SvcBypass.initSvcHook()) { + SvcBypass.enableSvcRedirect( + currentApkPath, + cachedOriginalApkPath, + context.getPackageName() + ); + XLog.i(TAG, "SVC Hook enabled"); + } else { + XLog.w(TAG, "SVC Hook failed to init"); + } + } } - org.lsposed.lspd.nativebridge.SigBypass.enableOpenatHook(context.getPackageResourcePath(), cacheApkPath); } } } diff --git a/patch-loader/src/main/jni/CMakeLists.txt b/patch-loader/src/main/jni/CMakeLists.txt index 486d29e..d562a55 100644 --- a/patch-loader/src/main/jni/CMakeLists.txt +++ b/patch-loader/src/main/jni/CMakeLists.txt @@ -7,6 +7,8 @@ add_subdirectory(${CORE_ROOT} core) aux_source_directory(src SRC_LIST) aux_source_directory(src/jni SRC_LIST) set(SRC_LIST ${SRC_LIST} api/patch_main.cpp) +set(SRC_LIST ${SRC_LIST} src/jni/bypass_sig.cpp) +set(SRC_LIST ${SRC_LIST} src/jni/bypass_svc.cpp) add_library(${PROJECT_NAME} SHARED ${SRC_LIST}) @@ -25,4 +27,4 @@ if (DEFINED DEBUG_SYMBOLS_PATH) COMMAND ${CMAKE_STRIP} --strip-all $ COMMAND ${CMAKE_OBJCOPY} --add-gnu-debuglink ${DEBUG_SYMBOLS_PATH}/${ANDROID_ABI}/${PROJECT_NAME}.debug $) -endif() +endif() \ No newline at end of file diff --git a/patch-loader/src/main/jni/src/jni/bypass_sig.cpp b/patch-loader/src/main/jni/src/jni/bypass_sig.cpp index 644359b..783fb71 100644 --- a/patch-loader/src/main/jni/src/jni/bypass_sig.cpp +++ b/patch-loader/src/main/jni/src/jni/bypass_sig.cpp @@ -1,6 +1,6 @@ // // Created by VIP on 2021/4/25. -// Update by HSSkyBoy on 2025/9/11 +// Modified by HSSkyBoy on 2025/12/15 // #include "bypass_sig.h" @@ -13,17 +13,22 @@ #include "utils/hook_helper.hpp" #include "utils/jni_helper.hpp" #include +#include +#include +#include using lsplant::operator""_sym; namespace lspd { - std::string apkPath; - std::string redirectPath; + static std::string targetApkPath; + static std::string redirectApkPath; + static std::string currentPackageName; + static void *openat_backup = nullptr; inline static constexpr const char* kLibCName = "libc.so"; -// 修改回傳型別以匹配 kImg 的實際型別 + // 修改回傳型別以匹配 kImg 的實際型別 std::unique_ptr &GetC(bool release = false) { static auto kImg = std::make_unique(kLibCName); if (release) { @@ -33,38 +38,49 @@ namespace lspd { return kImg; } + // OpenAt Hook 邏輯 inline static auto __openat_ = "__openat"_sym.hook->*[](int fd, const char *pathname, int flag, int mode) static -> int { - if (pathname && strcmp(pathname, apkPath.c_str()) == 0) { - return backup(fd, redirectPath.c_str(), flag, mode); + if (pathname && !targetApkPath.empty() && strcmp(pathname, targetApkPath.c_str()) == 0) { + return backup(fd, redirectApkPath.c_str(), flag, mode); } return backup(fd, pathname, flag, mode); }; static bool HookOpenat(const lsplant::HookHandler &handler) { return handler(__openat_); } - LSP_DEF_NATIVE_METHOD(void, SigBypass, enableOpenatHook, jstring origApkPath, - jstring cacheApkPath) { - if (origApkPath == nullptr || cacheApkPath == nullptr) { - LOGE("Invalid arguments: original or cache path is null."); + LSP_DEF_NATIVE_METHOD(void, SigBypass, enableOpenatHook, + jstring jOrigApkPath, + jstring jCacheApkPath, + jstring jPkgName) { + + if (jOrigApkPath == nullptr || jCacheApkPath == nullptr) { + LOGE("Invalid arguments: paths cannot be null."); return; } - lsplant::JUTFString str1(env, origApkPath); - lsplant::JUTFString str2(env, cacheApkPath); + lsplant::JUTFString strOrig(env, jOrigApkPath); + lsplant::JUTFString strRedirect(env, jCacheApkPath); - apkPath = str1.get(); - redirectPath = str2.get(); + targetApkPath = strOrig.get(); + redirectApkPath = strRedirect.get(); + + if (jPkgName != nullptr) { + lsplant::JUTFString strPkg(env, jPkgName); + currentPackageName = strPkg.get(); + } + + LOGI("Enable OpenAt Hook: %s -> %s (Pkg: %s)", + targetApkPath.c_str(), redirectApkPath.c_str(), currentPackageName.c_str()); - LOGI("Attempting to hook __openat (libc). Original: %s, Redirect: %s", - apkPath.c_str(), redirectPath.c_str()); - auto r = HookOpenat(lsplant::InitInfo{ .inline_hooker = [](auto t, auto r) { void *bk = nullptr; - return HookInline(t, r, &bk) == 0 ? bk : nullptr; + int ret = HookInline(t, r, &bk); + if (ret == 0) openat_backup = bk; + return ret == 0 ? bk : nullptr; }, .art_symbol_resolver = [](auto symbol) { return GetC()->getSymbAddress(symbol); @@ -77,8 +93,17 @@ namespace lspd { GetC(true); } + LSP_DEF_NATIVE_METHOD(void, SigBypass, disableOpenatHook) { + LOGI("Disable OpenAt Hook requested"); + targetApkPath.clear(); + redirectApkPath.clear(); + } + + // 註冊 JNI 方法 static JNINativeMethod gMethods[] = { - LSP_NATIVE_METHOD(SigBypass, enableOpenatHook, "(Ljava/lang/String;Ljava/lang/String;)V")}; + LSP_NATIVE_METHOD(SigBypass, enableOpenatHook, "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"), + LSP_NATIVE_METHOD(SigBypass, disableOpenatHook, "()V") + }; void RegisterBypass(JNIEnv *env) { REGISTER_LSP_NATIVE_METHODS(SigBypass); } diff --git a/patch-loader/src/main/jni/src/jni/bypass_sig.h b/patch-loader/src/main/jni/src/jni/bypass_sig.h index 66b69ce..3d6ed72 100644 --- a/patch-loader/src/main/jni/src/jni/bypass_sig.h +++ b/patch-loader/src/main/jni/src/jni/bypass_sig.h @@ -1,5 +1,4 @@ #pragma once - #include namespace lspd { diff --git a/patch-loader/src/main/jni/src/jni/bypass_svc.cpp b/patch-loader/src/main/jni/src/jni/bypass_svc.cpp new file mode 100644 index 0000000..f159093 --- /dev/null +++ b/patch-loader/src/main/jni/src/jni/bypass_svc.cpp @@ -0,0 +1,255 @@ +#include "bypass_svc.h" +#include "logging.h" +#include "native_util.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace lspd { + + // --- 共用結構與變數 --- + struct SyscallRequest { + long sys_no; + long args[6]; + long result; + bool completed; + std::mutex mtx; + std::condition_variable cv; + }; + + static pthread_t g_trusted_thread; + static bool g_is_hook_active = false; + static std::queue g_request_queue; + static std::mutex g_queue_mtx; + static std::condition_variable g_queue_cv; + + // --- 核心功能實作 (ARM64 Only) --- + + // 1. 影子線程循環 + static void* trusted_thread_loop(void* arg) { + LOGD("SvcBypass: Trusted thread started (TID: %d)", gettid()); + while (true) { + SyscallRequest* req = nullptr; + { + std::unique_lock lock(g_queue_mtx); + g_queue_cv.wait(lock, [] { return !g_request_queue.empty(); }); + req = g_request_queue.front(); + g_request_queue.pop(); + } + + if (req) { + // 執行真正的 syscall + req->result = syscall(req->sys_no, + req->args[0], req->args[1], req->args[2], + req->args[3], req->args[4], req->args[5]); + + { + std::lock_guard lock(req->mtx); + req->completed = true; + } + req->cv.notify_one(); + } + } + return nullptr; + } + + // 2. SIGSYS 信號處理器 + static void sigsys_handler(int signo, siginfo_t* info, void* context) { + if (signo != SIGSYS) return; + + ucontext_t* ctx = (ucontext_t*)context; + SyscallRequest req; + + // ARM64: 從 regs 讀取 (x8=sys_no, x0-x5=args) + // 直接存取 ARM64 特有的 regs 結構 + req.sys_no = ctx->uc_mcontext.regs[8]; + for (int i = 0; i < 6; ++i) { + req.args[i] = ctx->uc_mcontext.regs[i]; + } + req.completed = false; + + LOGD("SvcBypass: Trapped syscall %ld (PID: %d)", req.sys_no, getpid()); + + { + std::lock_guard lock(g_queue_mtx); + g_request_queue.push(&req); + } + g_queue_cv.notify_one(); + + { + std::unique_lock lock(req.mtx); + req.cv.wait(lock, [&req] { return req.completed; }); + } + + // 將結果寫回 x0 + ctx->uc_mcontext.regs[0] = req.result; + } + + // --- JNI 接口層 --- + + LSP_DEF_NATIVE_METHOD(jboolean, SvcBypass, initSvcHook) { + // 如果已經激活,直接返回成功 + if (g_is_hook_active) return JNI_TRUE; + + int ret = pthread_create(&g_trusted_thread, nullptr, trusted_thread_loop, nullptr); + if (ret != 0) { + LOGE("SvcBypass: Failed to create trusted thread"); + return JNI_FALSE; + } + + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_sigaction = sigsys_handler; + sa.sa_flags = SA_SIGINFO | SA_NODEFER; + if (sigaction(SIGSYS, &sa, nullptr) < 0) { + LOGE("SvcBypass: Failed to register SIGSYS handler"); + return JNI_FALSE; + } + + g_is_hook_active = true; + LOGI("SvcBypass: Initialized successfully (ARM64)"); + return JNI_TRUE; + } + + LSP_DEF_NATIVE_METHOD(void, SvcBypass, enableSvcRedirect, + jstring path, jstring orig, jstring pkg) { + if (!g_is_hook_active) { + LOGW("SvcBypass: Hook not initialized."); + return; + } + + // ARM64 BPF 規則 + struct sock_filter filter[] = { + 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_STMT(BPF_RET + BPF_K, SECCOMP_RET_TRAP), + BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW), + }; + + struct sock_fprog prog = { + .len = (unsigned short)(sizeof(filter) / sizeof(filter[0])), + .filter = filter, + }; + + if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { + LOGE("SvcBypass: prctl(NO_NEW_PRIVS) failed"); + return; + } + + if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) { + LOGE("SvcBypass: prctl(SECCOMP) failed"); + } else { + LOGI("SvcBypass: Seccomp filter applied (ARM64)"); + } + } + + LSP_DEF_NATIVE_METHOD(void, SvcBypass, disableSvcRedirect) { + LOGW("SvcBypass: Cannot disable Seccomp filters once applied."); + } + + LSP_DEF_NATIVE_METHOD(jboolean, SvcBypass, isSvcHookActive) { + return g_is_hook_active ? JNI_TRUE : JNI_FALSE; + } + + LSP_DEF_NATIVE_METHOD(jstring, SvcBypass, getDebugInfo) { + return env->NewStringUTF("SvcBypass: Active (ARM64)"); + } + + LSP_DEF_NATIVE_METHOD(jint, SvcBypass, getCurrentPid) { + return getpid(); + } + + LSP_DEF_NATIVE_METHOD(jint, SvcBypass, getInitialPid) { + return getpid(); + } + + LSP_DEF_NATIVE_METHOD(void, SvcBypass, logSvcHookStats) { + } + + LSP_DEF_NATIVE_METHOD(jboolean, SvcBypass, isChildProcess) { + return JNI_FALSE; + } + + LSP_DEF_NATIVE_METHOD(jstring, SvcBypass, checkFd, jint fd) { + char path[512]; + char link[64]; + snprintf(link, sizeof(link), "/proc/self/fd/%d", fd); + ssize_t len = readlink(link, path, sizeof(path) - 1); + if (len != -1) { + path[len] = '\0'; + return env->NewStringUTF(path); + } + return nullptr; + } + + LSP_DEF_NATIVE_METHOD(jint, SvcBypass, dupFd, jint fd) { + return dup(fd); + } + + LSP_DEF_NATIVE_METHOD(jlong, SvcBypass, getFdInode, jint fd) { + struct stat st; + if (fstat(fd, &st) == 0) return (jlong)st.st_ino; + return -1; + } + + LSP_DEF_NATIVE_METHOD(jboolean, SvcBypass, isSystemFile, jint fd) { + return JNI_FALSE; + } + + LSP_DEF_NATIVE_METHOD(jint, SvcBypass, findSystemApkFd, jstring path) { + return -1; + } + + LSP_DEF_NATIVE_METHOD(jobjectArray, SvcBypass, getSystemApkFds) { + return nullptr; + } + + LSP_DEF_NATIVE_METHOD(void, SvcBypass, refreshSystemFds) { + } + + LSP_DEF_NATIVE_METHOD(jbyteArray, SvcBypass, readCertificateFromFd, jint fd) { + return nullptr; + } + + LSP_DEF_NATIVE_METHOD(jbyteArray, SvcBypass, readCertificateFromPath, jstring path) { + return nullptr; + } + + static JNINativeMethod gMethods[] = { + LSP_NATIVE_METHOD(SvcBypass, initSvcHook, "()Z"), + LSP_NATIVE_METHOD(SvcBypass, enableSvcRedirect, "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"), + LSP_NATIVE_METHOD(SvcBypass, disableSvcRedirect, "()V"), + LSP_NATIVE_METHOD(SvcBypass, isSvcHookActive, "()Z"), + LSP_NATIVE_METHOD(SvcBypass, logSvcHookStats, "()V"), + LSP_NATIVE_METHOD(SvcBypass, getDebugInfo, "()Ljava/lang/String;"), + LSP_NATIVE_METHOD(SvcBypass, getCurrentPid, "()I"), + LSP_NATIVE_METHOD(SvcBypass, getInitialPid, "()I"), + LSP_NATIVE_METHOD(SvcBypass, isChildProcess, "()Z"), + LSP_NATIVE_METHOD(SvcBypass, checkFd, "(I)Ljava/lang/String;"), + LSP_NATIVE_METHOD(SvcBypass, dupFd, "(I)I"), + LSP_NATIVE_METHOD(SvcBypass, getFdInode, "(I)J"), + LSP_NATIVE_METHOD(SvcBypass, isSystemFile, "(I)Z"), + LSP_NATIVE_METHOD(SvcBypass, findSystemApkFd, "(Ljava/lang/String;)I"), + LSP_NATIVE_METHOD(SvcBypass, getSystemApkFds, "()[[Ljava/lang/String;"), + LSP_NATIVE_METHOD(SvcBypass, refreshSystemFds, "()V"), + LSP_NATIVE_METHOD(SvcBypass, readCertificateFromFd, "(I)[B"), + LSP_NATIVE_METHOD(SvcBypass, readCertificateFromPath, "(Ljava/lang/String;)[B"), + }; + + void RegisterSvcBypass(JNIEnv *env) { + REGISTER_LSP_NATIVE_METHODS(SvcBypass); + } +} diff --git a/patch-loader/src/main/jni/src/jni/bypass_svc.h b/patch-loader/src/main/jni/src/jni/bypass_svc.h new file mode 100644 index 0000000..b481b5a --- /dev/null +++ b/patch-loader/src/main/jni/src/jni/bypass_svc.h @@ -0,0 +1,8 @@ +#pragma once +#include + +namespace lspd { + + void RegisterSvcBypass(JNIEnv* env); + +} // namespace lspd diff --git a/patch-loader/src/main/jni/src/patch_loader.cpp b/patch-loader/src/main/jni/src/patch_loader.cpp index e5ebbbc..99ee571 100644 --- a/patch-loader/src/main/jni/src/patch_loader.cpp +++ b/patch-loader/src/main/jni/src/patch_loader.cpp @@ -27,6 +27,7 @@ #include "art/runtime/oat_file_manager.h" #include "elf_util.h" #include "jni/bypass_sig.h" +#include "jni/bypass_svc.h" #include "native_util.h" #include "symbol_cache.h" #include "utils/jni_helper.hpp" @@ -78,6 +79,7 @@ namespace lspd { void PatchLoader::InitHooks(JNIEnv* env) { Context::InitHooks(env); RegisterBypass(env); + RegisterSvcBypass(env); } void PatchLoader::SetupEntryClass(JNIEnv* env) { diff --git a/patch/src/main/java/org/lsposed/patch/NPatch.java b/patch/src/main/java/org/lsposed/patch/NPatch.java index 1170ade..0ead926 100644 --- a/patch/src/main/java/org/lsposed/patch/NPatch.java +++ b/patch/src/main/java/org/lsposed/patch/NPatch.java @@ -313,6 +313,14 @@ public class NPatch { } catch (Throwable e) { throw new PatchError("Error when adding dex", e); } + if (sigbypassLevel >= Constants.SIGBYPASS_LV_PM_OPENAT) { + logger.i("Embedding original apk for SigBypass..."); + try (var is = new FileInputStream(srcApkFile)) { + dstZFile.add(Constants.ORIGINAL_APK_ASSET_PATH, is); + } catch (Throwable e) { + throw new PatchError("Error when embedding original apk", e); + } + } if (isInjectProvider){ try (var is = getClass().getClassLoader().getResourceAsStream("assets/mtprovider.dex")) { @@ -360,7 +368,16 @@ public class NPatch { if (!injectDex && name.startsWith("classes") && name.endsWith(".dex")) continue; if (name.equals("AndroidManifest.xml")) continue; if (name.startsWith("META-INF") && (name.endsWith(".SF") || name.endsWith(".MF") || name.endsWith(".RSA"))) continue; - srcZFile.addFileLink(name, name); + + try (InputStream is = entry.open()) { + if (name.endsWith(".so")) { + dstZFile.add(name, is, false); + } else { + dstZFile.add(name, is); + } + } catch (IOException e) { + throw new PatchError("Failed to copy entry: " + name, e); + } } dstZFile.realign(); diff --git a/share/java/src/main/java/org/lsposed/npatch/share/Constants.java b/share/java/src/main/java/org/lsposed/npatch/share/Constants.java index dacf4b1..55546cd 100644 --- a/share/java/src/main/java/org/lsposed/npatch/share/Constants.java +++ b/share/java/src/main/java/org/lsposed/npatch/share/Constants.java @@ -17,5 +17,5 @@ public class Constants { final static public int SIGBYPASS_LV_DISABLE = 0; final static public int SIGBYPASS_LV_PM = 1; final static public int SIGBYPASS_LV_PM_OPENAT = 2; - final static public int SIGBYPASS_LV_MAX = 3; + final static public int SIGBYPASS_LV_SVC = 3; } diff --git a/share/java/src/template/java/org.lsposed.lspatch.share/LSPConfig.java b/share/java/src/template/java/org.lsposed.lspatch.share/LSPConfig.java index dc9c971..4258277 100644 --- a/share/java/src/template/java/org.lsposed.lspatch.share/LSPConfig.java +++ b/share/java/src/template/java/org.lsposed.lspatch.share/LSPConfig.java @@ -9,6 +9,7 @@ public class LSPConfig { public String VERSION_NAME; public int CORE_VERSION_CODE; public String CORE_VERSION_NAME; + public int sigBypassLevel; private LSPConfig() { }