CommunityEscrita e Ediçãogithub.com

Marukon/skill-upgrade-libxposed-102

Habilidade do agente para atualizar módulos Xposed tradicionais para a API LibXposed 102.

O que é skill-upgrade-libxposed-102?

Este guia abrangente descreve o processo de migração de módulos Xposed tradicionais (baseados na API XposedBridge 54-93) para a moderna API LibXposed 102, compatível com estruturas como LSPosed 1.9.3+ e Vector. A habilidade cobre desde a atualização de dependências e configuração de metadados até a refatoração do código de hook e otimização de desempenho para chamadas de alta frequência. É projetada para desenvolvedores que precisam modernizar seus módulos Xposed, garantindo compatibilidade com as versões mais recentes das estruturas Xposed. A habilidade fornece um roteiro passo a passo, incluindo a configuração correta de `module.prop`, `java_init.list` e `scope.list`, além de padrões de código detalhados para usar a API de interceptação encadeada da LibXposed. Ela também aborda otimizações de desempenho cruciais, como o uso de `StackWalker` para evitar a criação cara de stack traces em chamadas de método frequentes, e destaca armadilhas comuns da migração, como a remoção de arquivos legados.

Funciona com~Claude Code~Codex CLI~Cursor
npx skills add Marukon/skill-upgrade-libxposed-102

Installed? Explore more Escrita e Edição skills: steipete/notion, langchain-ai/langchain, bytedance/podcast-generation · View all 6 →

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Guia Completo de Atualização de Módulo Xposed para LibXposed API 102

Este documento resume o processo padrão, paradigmas de código, especificações de metadados e estratégias de otimização de desempenho para atualizar módulos Xposed tradicionais (baseados na API XposedBridge 54-93) para a moderna LibXposed API 102 (compatível com LSPosed 1.9.3+, Vector e outras estruturas modernas).

Qualquer instrução futura para atualizar outros módulos deve referir-se diretamente a esta especificação.


1. Comparação de Mudanças Principais

Característica / DimensãoXposed Tradicional (API 54-93)LibXposed Moderno (API 102)
Coordenada de Dependênciade.robv.android.xposed:api:82 (ou 93)io.github.libxposed:api:102.0.0
Classe/Interface de EntradaIXposedHookLoadPackage, IXposedHookZygoteInitHerda io.github.libxposed.api.XposedModule
Declaração de Entradaassets/xposed_initMETA-INF/xposed/java_init.list
Configuração de Propriedades do Módulo<meta-data> no AndroidManifest.xmlMETA-INF/xposed/module.prop
Declaração de EscopoRecurso de array xposedscopeMETA-INF/xposed/scope.list
Modelo de Programação de HookCallbacks XC_MethodHook (before / after)Interceptador encadeado estilo OkHttp Hooker (chain.proceed())
Recarga a Quente (Hot Reload)Não suportado (reinicialização do aplicativo alvo ou do sistema necessária)Suportado nativamente (sobrescrever onHotReloading / onHotReloaded)
Injeção no ZygoteSuporta injeção global initZygoteInjeção no Zygote proibida, injeta apenas em processos alvo no escopo
Suporte a Ofuscação/R8Necessário preservar rigorosamente nomes de métodos e classes de hookSuporta ofuscação R8 nativamente, apenas declarar regras de adaptação

2. Método Padrão de Migração em Quatro Etapas

Primeira Etapa: Migração de Build e Dependência (build.gradle)

  1. Atualizar Dependências: Substitua a dependência no build.gradle do módulo (ex.: app/build.gradle ou hook/build.gradle):

    dependencies {
        // Remover: compileOnly 'de.robv.android.xposed:api:82'
        // Introduzir a API moderna LibXposed 102
        compileOnly 'io.github.libxposed:api:102.0.0'
        compileOnly 'androidx.annotation:annotation:1.5.0'
    }
    
  2. Compatibilidade de Compilação Java:

    android {
        compileOptions {
            sourceCompatibility JavaVersion.VERSION_11
            targetCompatibility JavaVersion.VERSION_11
        }
    }
    
  3. Configurar Regras 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>();
    }
    

Segunda Etapa: Configuração de Arquivos de Metadados Modernos (META-INF/xposed/)

Crie os seguintes arquivos no diretório de origem do módulo src/main/resources/META-INF/xposed/:

1. module.prop (Metadados do Módulo)

minApiVersion=102
targetApiVersion=102
autoHotReload=true
exceptionMode=protective
  • minApiVersion: Versão mínima suportada da API, defina como 102.
  • targetApiVersion: Versão alvo da API, defina como 102.
  • autoHotReload: Defina como true para acionar automaticamente a recarga a quente quando o módulo for atualizado/instalado.
  • exceptionMode: protective (modo de proteção padrão, exceções de Hook não causam travamento do host) ou passthrough.

2. java_init.list (Declaração da Classe de Entrada)

Declare o nome de classe totalmente qualificado da entrada do módulo em uma única linha:

com.example.myhook.MainHook

3. scope.list (Escopo Recomendado)

Um nome de pacote alvo por linha, declarando os aplicativos nos quais o módulo deve ser injetado por padrão:

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

Terceira Etapa: Configuração do Arquivo de Manifesto (AndroidManifest.xml)

Atualize o AndroidManifest.xml do módulo:

<application ...>
    <!-- Manter metadados tradicionais para compatibilidade, chave é definir xposedminversion como 102 -->
    <meta-data
        android:name="xposedmodule"
        android:value="true" />
    <meta-data
        android:name="xposeddescription"
        android:value="Descrição da função do módulo" />
    <meta-data
        android:name="xposedminversion"
        android:value="102" />
    <meta-data
        android:name="xposedscope"
        android:resource="@array/xposed_scope" />
</application>

Quarta Etapa: Refatoração da Lógica de Hook (XposedModule)

1. Modelo Básico e Ciclo de Vida

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();
    }

    /**
     * Acionado quando o ClassLoader do aplicativo host está pronto e o Application está prestes a ser criado
     */
    @Override
    public void onPackageReady(@NonNull PackageReadyParam param) {
        if (mHooked) return;
        synchronized (this) {
            if (mHooked) return;
            mHooked = true;
            initHooks(param.getPackageName(), param.getClassLoader());
        }
    }

    /**
     * Callback de preparação para recarga a quente (executado no código da versão antiga)
     * Retorne true para permitir que a estrutura substitua pela nova versão
     */
    @Override
    public boolean onHotReloading(@NonNull HotReloadingParam param) {
        return true;
    }

    /**
     * Callback de conclusão de recarga a quente (executado no código da nova versão)
     */
    @Override
    public void onHotReloaded(@NonNull HotReloadedParam param) {
        // Desfazer todos os hooks registrados pela versão antiga
        param.getOldHookHandles().forEach(XposedInterface.HookHandle::unhook);
        synchronized (this) {
            mHooked = false;
            initHooks("hot-reload", getClass().getClassLoader());
        }
    }

    private void initHooks(String packageName, ClassLoader classLoader) {
        // Executar lógica de hook específica
    }
}

2. Sintaxe de Hook Central - Comparação

Operação AlvoEscrita Xposed TradicionalEscrita Moderna LibXposed 102
Encontrar e Hookar MétodoXposedHelpers.findAndHookMethod("NomeClasse", cl, "metodo", argTypes..., callback)Method m = Clazz.class.getDeclaredMethod("metodo", argTypes...);hook(m).intercept(chain -> { ... });
Executar Método OriginalExecutado diretamente por padrão; chamar param.getResult()Object result = chain.proceed();
Modificar Parâmetros e Chamarparam.args[0] = novoVal;Object result = chain.proceed(new Object[]{ novoVal, ... });
Interceptar e Substituir Valor de Retornoparam.setResult(valorCustomizado);Retornar valor customizado diretamente (sem chamar chain.proceed()):hook(m).intercept(chain -> valorCustomizado);
Obter Parâmetros de Entradaparam.args[i]chain.getArg(i) ou chain.getArgs()
Obter Instância thisparam.thisObjectchain.getThisObject()
Hookar ConstrutorfindAndHookConstructor(...)Constructor<?> ctor = ...;hook(ctor).intercept(chain -> ...);
Hookar Bloco Static InitializerAPI padrão não suportada tradicionalmentehookClassInitializer(TargetClass.class).intercept(...)

3. Cenários de Hook de Alta Frequência e Padrões de Otimização de Desempenho Profundo

Regra de Ouro para Otimização de Chamadas de Alta Frequência (ex.: isEnabled, renderização de View, despacho de toque):

  1. Caminho Rápido com Sobrecarga Zero (Sondagem Direta): Se o método é um método de consulta de estado (ex.: isEnabled(), isLoaded()), primeiro chame a leitura de campo nativo leve chain.proceed(). Se o estado real for falso, retorne falso imediatamente, pulando toda a lógica subsequente e verificações de pilha.

  2. Android 14+ (API 34+) Usar StackWalker para Iteração Preguiçosa: O tradicional new Throwable().getStackTrace() causa expansão profunda da pilha de threads (Native Unwind) e centenas de alocações de string, facilmente causando queda de quadros devido ao GC. Use java.lang.StackWalker do Java 9 / Android 14+ para escanear camada por camada sob demanda, com zero alocação de pilha completa.

  3. Localização de Pilha Dinâmica para Android < 34 (Evitar Endereços Fixos): Execute new Throwable().getStackTrace() apenas uma vez, itere para baixo para encontrar a classe do sistema onde o método alvo reside (ex.: AccessibilityManager), o quadro de pilha imediatamente seguinte é o verdadeiro chamador. Nunca codifique [3] ou [4], garantindo operação estável entre versões do LSPosed.

Código de Exemplo de Otimização de Desempenho:

// Verificar StackWalker do Android 14+
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;
}

// Exemplo de Hook de Método de Alta Frequência
Method isEnabled = AccessibilityManager.class.getDeclaredMethod("isEnabled");
hook(isEnabled).intercept(chain -> {
    Object real = chain.proceed();
    if (Boolean.FALSE.equals(real)) {
        return false; // Sistema já está desligado, retorno direto com sobrecarga zero
    }
    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;
        }));
    }
}

4. Armadilhas Comuns de Atualização e Lista de Verificação

  1. assets/xposed_init ainda precisa ser mantido?
    • A API moderna 102 lê apenas META-INF/xposed/java_init.list. Se assets/xposed_init for mantido, estruturas antigas podem tentar usar reflexão tradicional para carregá-lo e gerar erros. Recomenda-se removê-lo diretamente ou mantê-lo vazio.
  2. compileSdk e Plataforma Android SDK:
    • Recomenda-se atualizar compileSdk e targetSdk para Android 33 ou 34 para usar interfaces modernas e recursos de linguagem Java 11/17 sem problemas.
  3. Compatibilidade do Gradle:
    • Se o ambiente de build usar JDK 17/21, certifique-se de atualizar o Gradle Wrapper para 7.4+ (recomendado 7.5.1 ou 8.x) e o AGP para 7.3+.
  4. Verificar Reconhecimento do Módulo:
    • Use uma ferramenta de descompressão para verificar o diretório raiz do APK final. Ele deve conter:
      • META-INF/xposed/module.prop
      • META-INF/xposed/java_init.list
      • META-INF/xposed/scope.list

Habilidades Relacionadas