Community寫作與編輯github.com

Marukon/skill-upgrade-libxposed-102

將傳統 Xposed 模組升級至現代 LibXposed API 102 的完整指南

skill-upgrade-libxposed-102 是什麼?

此技能提供了一套標準化的流程,用於將基於 XposedBridge API 54-93 的傳統 Xposed 模組遷移至現代 LibXposed API 102。它詳細說明了必要的構建腳本、元數據與清單檔案配置,以及將 Hook 邏輯重構為基於攔截器的現代模式的步驟。該技能支援 LSPosed 1.9.3+、Vector 等現代框架,並強調了熱重載、性能優化(如針對 Android 14+ 的 StackWalker 惰性遍歷)以及常見陷阱的排查。遵守此規範可確保模組在現代 Xposed 框架上獲得最佳相容性、效能與可維護性。

相容平台~Claude Code~Codex CLI~Cursor
npx skills add Marukon/skill-upgrade-libxposed-102

Installed? Explore more 寫作與編輯 skills: steipete/notion, langchain-ai/langchain, bytedance/podcast-generation · View all 6 →

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

Xposed 模組升級至 LibXposed API 102 完全指南

本文檔總結了將傳統 Xposed 模組(基於 XposedBridge API 54-93)升級至現代 LibXposed API 102(適用於 LSPosed 1.9.3+、Vector 等現代框架)的標準流程、程式碼範式、元資料規範與效能最佳化策略。

後續對任何其他模組執行升級指示時,可直接參照本規範執行。


一、核心變更對比

特性 / 維度傳統 Xposed (API 54-93)現代 LibXposed (API 102)
依賴座標de.robv.android.xposed:api:82 (或 93)io.github.libxposed:api:102.0.0
入口基底類別/介面IXposedHookLoadPackage, IXposedHookZygoteInit繼承 io.github.libxposed.api.XposedModule
入口宣告assets/xposed_initMETA-INF/xposed/java_init.list
模組屬性配置AndroidManifest.xml 中的 <meta-data>META-INF/xposed/module.prop
作用域宣告xposedscope resource arrayMETA-INF/xposed/scope.list
Hook 程式設計模型XC_MethodHook (before / after) 回呼類 OkHttp 的鏈式攔截器 Hooker (chain.proceed())
熱重載 (Hot Reload)不支援(需重啟目標應用甚至系統)原生支援(覆寫 onHotReloading / onHotReloaded)
Zygote 注入支援 initZygote 全域注入禁止 Zygote 注入,僅注入到作用域目標程序
混淆/R8 支援需嚴格保留 Hook 方法名與類別名原生支援 R8 混淆,宣告適配規則即可

二、標準遷移四步法

第一步:建構與依賴遷移 (build.gradle)

  1. 更新依賴項: 在模組的 build.gradle(如 app/build.gradle 或 hook/build.gradle)中替換依賴:

    dependencies {
        // 移除: compileOnly 'de.robv.android.xposed:api:82'
        // 引入現代 LibXposed 102 API
        compileOnly 'io.github.libxposed:api:102.0.0'
        compileOnly 'androidx.annotation:annotation:1.5.0'
    }
    
  2. Java 編譯相容性:

    android {
        compileOptions {
            sourceCompatibility JavaVersion.VERSION_11
            targetCompatibility JavaVersion.VERSION_11
        }
    }
    
  3. 配置 ProGuard / R8 規則 (proguard-rules.pro):

    -dontwarn io.github.libxposed.annotation.**
    -adaptresourcefilecontents META-INF/xposed/java_init.list
    -keep,allowoptimization,allowobfuscation public class * extends io.github.libxposed.api.XposedModule {
        public <init>();
    }
    

第二步:現代元資料檔案配置 (META-INF/xposed/)

在模組源碼目錄 src/main/resources/META-INF/xposed/ 下建立以下檔案:

1. module.prop(模組元資料)

minApiVersion=102
targetApiVersion=102
autoHotReload=true
exceptionMode=protective
  • minApiVersion: 最低支援的 API 版本,設為 102。
  • targetApiVersion: 目標 API 版本,設為 102。
  • autoHotReload: 設為 true 表示模組更新安裝時自動觸發熱重載。
  • exceptionMode: protective(預設保護模式,Hook 拋異常不影響宿主崩潰)或 passthrough。

2. java_init.list(入口類別宣告)

單行宣告模組入口全限定類別名:

com.example.myhook.MainHook

3. scope.list(推薦作用域)

每行一個目標包名,宣告模組預設需要注入的應用:

com.android.chrome
com.microsoft.emmx
com.example.targetapp

第三步:清單檔案配置 (AndroidManifest.xml)

更新模組的 AndroidManifest.xml:

<application ...>
    <!-- 傳統元資料保持相容,關鍵將 xposedminversion 設為 102 -->
    <meta-data
        android:name="xposedmodule"
        android:value="true" />
    <meta-data
        android:name="xposeddescription"
        android:value="模組功能描述" />
    <meta-data
        android:name="xposedminversion"
        android:value="102" />
    <meta-data
        android:name="xposedscope"
        android:resource="@array/xposed_scope" />
</application>

第四步:Hook 邏輯重構 (XposedModule)

1. 基礎模板與生命週期

package com.example.myhook;

import android.util.Log;
import androidx.annotation.NonNull;
import io.github.libxposed.api.XposedInterface;
import io.github.libxposed.api.XposedModule;

public class MainHook extends XposedModule {

    private static final String TAG = "MyHookModule";
    private volatile boolean mHooked = false;

    public MainHook() {
        super();
    }

    /**
     * 宿主應用 ClassLoader 就緒且即將建立 Application 時觸發
     */
    @Override
    public void onPackageReady(@NonNull PackageReadyParam param) {
        if (mHooked) return;
        synchronized (this) {
            if (mHooked) return;
            mHooked = true;
            initHooks(param.getPackageName(), param.getClassLoader());
        }
    }

    /**
     * 熱重載準備回呼(執行在舊版本程式碼中)
     * 傳回 true 允許框架替換為新版本
     */
    @Override
    public boolean onHotReloading(@NonNull HotReloadingParam param) {
        return true;
    }

    /**
     * 熱重載完成回呼(執行在新版本程式碼中)
     */
    @Override
    public void onHotReloaded(@NonNull HotReloadedParam param) {
        // 解除舊版本註冊的所有 Hook
        param.getOldHookHandles().forEach(XposedInterface.HookHandle::unhook);
        synchronized (this) {
            mHooked = false;
            initHooks("hot-reload", getClass().getClassLoader());
        }
    }

    private void initHooks(String packageName, ClassLoader classLoader) {
        // 執行具體 Hook 邏輯
    }
}

2. 核心 Hook 語法對照

目標操作傳統 Xposed 寫法LibXposed 102 現代寫法
查詢並 Hook 方法XposedHelpers.findAndHookMethod("類別名", cl, "方法", argTypes..., callback)Method m = Clazz.class.getDeclaredMethod("方法", argTypes...);hook(m).intercept(chain -> { ... });
執行原方法預設直接執行;呼叫 param.getResult()Object result = chain.proceed();
修改參數並呼叫param.args[0] = newVal;Object result = chain.proceed(new Object[]{ newVal, ... });
攔截並替換回傳值param.setResult(customVal);直接回傳自訂值(不呼叫 chain.proceed()):hook(m).intercept(chain -> customVal);
取得入參param.args[i]chain.getArg(i) 或 chain.getArgs()
取得 this 實例param.thisObjectchain.getThisObject()
Hook 建構函數findAndHookConstructor(...)Constructor<?> ctor = ...;hook(ctor).intercept(chain -> ...);
Hook 靜態程式碼區塊傳統不支援標準 APIhookClassInitializer(TargetClass.class).intercept(...)

三、高頻 Hook 場景與效能深度最佳化模式

針對高頻呼叫(如 isEnabled、View 繪製、觸摸派發)的黃金最佳化法則:

  1. 零開銷快速路徑(嗅探直通): 若方法是狀態查詢方法(如 isEnabled()、isLoaded()),先呼叫輕量級的原生欄位讀取 chain.proceed()。如果真實狀態本身即為假,直接回傳假,跳過所有後續邏輯與堆疊檢查。

  2. Android 14+ (API 34+) 採用 StackWalker 惰性遍歷: 傳統 new Throwable().getStackTrace() 會導致全執行緒堆疊深度展開(Native Unwind)和幾百個字串分配,極易引起 GC 掉幀。 使用 Java 9 / Android 14+ 的 java.lang.StackWalker,按需逐層掃描,零全堆疊分配。

  3. Android < 34 動態堆疊定位(杜絕硬編碼): 僅單次執行 new Throwable().getStackTrace(),向下遍歷查詢目標方法所在的系統類別(如 AccessibilityManager),緊隨其後的堆疊幀即為真實呼叫者。絕不硬編碼 [3] 或 [4],保證跨 LSPosed 版本穩定執行。

效能最佳化示範程式碼:

// 檢查 Android 14+ StackWalker
private static final boolean HAS_STACK_WALKER;
static {
    boolean has;
    try {
        Class.forName("java.lang.StackWalker");
        has = (Build.VERSION.SDK_INT >= 34);
    } catch (Throwable t) {
        has = false;
    }
    HAS_STACK_WALKER = has;
}

// 高頻方法 Hook 示範
Method isEnabled = AccessibilityManager.class.getDeclaredMethod("isEnabled");
hook(isEnabled).intercept(chain -> {
    Object real = chain.proceed();
    if (Boolean.FALSE.equals(real)) {
        return false; // 系統本就是關的,零開銷直通回傳
    }
    return isCallerWhitelisted() ? real : false;
});

private static boolean isCallerWhitelisted() {
    if (HAS_STACK_WALKER) {
        try {
            return Api34StackWalker.check();
        } catch (Throwable ignored) {}
    }
    return checkLegacy();
}

@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
private static final class Api34StackWalker {
    private static final StackWalker WALKER = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);

    static boolean check() {
        return Boolean.TRUE.equals(WALKER.walk(frames -> {
            boolean foundTarget = false;
            var it = frames.iterator();
            while (it.hasNext()) {
                var f = it.next();
                if ("android.view.accessibility.AccessibilityManager".equals(f.getClassName())) {
                    foundTarget = true;
                } else if (foundTarget) {
                    return isPackageAllowed(f.getClassName());
                }
            }
            return false;
        }));
    }
}

四、升級常見陷阱與排查清單

  1. assets/xposed_init 是否仍需保留?
    • 現代 API 102 僅讀取 META-INF/xposed/java_init.list。如果保留 assets/xposed_init,舊版框架可能嘗試使用傳統反射載入它而報錯,推薦直接移除或保持為空。
  2. compileSdk 與 Android SDK 平台:
    • 建議將 compileSdk 與 targetSdk 升級至 Android 33 或 34,以便順利使用現代介面和 Java 11/17 語言特性。
  3. Gradle 相容性:
    • 如果建構環境使用的是 JDK 17 / 21,必須確保 Gradle Wrapper 升級至 7.4+(推薦 7.5.1 或 8.x),AGP 升級至 7.3+。
  4. 驗證模組識別:
    • 使用解壓工具檢視最終 APK 根目錄,必須包含:
      • META-INF/xposed/module.prop
      • META-INF/xposed/java_init.list
      • META-INF/xposed/scope.list

相關技能