Extração de DEX de APKs Protegidos com Legu e 360 Jiagu

Introdução

A ofuscação via Proguard já não oferece proteção suficiente contra engenharia reversa. Por isso, serviços de empacotamento (packers) como Tencent Legu e 360 Jiagu surgiram para dificultar a análise de código. No entanto, técnicas de unpacking permitem recuperar as classes DEX originais mesmo após esse processo de bilndagem.

Quando tentamos descompilar um APK processado por essas ferramentas usando dex2jar, o resultado é incompleto — estruturas de classes ofuscadas ou vazias, evidenciando que o código executável foi movido para outra camada de proteção.

Ferramentas Necessárias

Ferramenta Função Obtenção
FDex2 Módulo Xposed para extração de DEX em memória Repositórios de segurança móvel
VirtualXposed Ambiente virtualizado com suporte a módulos Xposed sem root vxposed.com

Procedimento de Extração

Preparação do ambiente:

  1. Instale o VirtualXposed no dispositivo
  2. Dentro do ambiente virtual, instale o módulo FDex2
  3. Ative o módulo nas configurações do Xposed e reinicie o ambiente virtual

Configuração do alvo:

  1. Instale o aplicativo protegido dentro do VirtualXposed
  2. Abra o FDex2 e selecione o pacote-alvo na lista

Execução e captura:

  1. Inicie o aplicativo protegido pelo FDex2
  2. O módulo interceptará o carregamento das classes e extrairá os bytes DEX
  3. Os arquivos serão salvos em: /data/data/[pacote]/[pacote]_[tamanho].dex

Recuperação dos arquivos:

Em dispositivos com acesso root:

adb shell su -c "cp /data/data/[pacote.alvo]/[pacote.alvo]_*.dex /sdcard/"
adb pull /sdcard/[pacote.alvo]_*.dex ./extraidos/

Em dispositivos sem root, utilize o gerenciador de arquivos integrado ao VirtualXposed (Configurações → Avançado → Gerenciador de arquivos) e compartilhe via Bluetooth ou nuvem.

Análise do Resultado

Converta os DEX extraídos para JAR:

d2j-dex2jar.sh extraidos/classes_[identificador].dex -o analise.jar

A aálise com jadx ou jd-gui revelará o código fonte original, demonstrando que a proteção foi neutralizada.

Mecanismo do FDex2

O núcleo da técnica reside no hook do carregamento de classes. Abaixo, uma reimplementação conceitual que ilustra o princípio:

package br.security.unpacker;

import de.robv.android.xposed.*;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
import java.io.*;
import java.lang.reflect.*;

public class DexDumper implements IXposedHookLoadPackage {
    
    private Class<?> dexClazz;
    private Method extractBytes;
    private Method obtainDex;
    private String targetPackage;
    
    @Override
    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam params) {
        loadConfiguration();
        if (!params.packageName.equals(targetPackage)) return;
        
        initializeReflection();
        interceptClassLoader(params);
    }
    
    private void loadConfiguration() {
        XSharedPreferences prefs = new XSharedPreferences(
            "br.security.unpacker.config", "targets");
        prefs.makeWorldReadable();
        targetPackage = prefs.getString("pkg", null);
    }
    
    private void initializeReflection() {
        try {
            dexClazz = Class.forName("com.android.dex.Dex");
            extractBytes = dexClazz.getDeclaredMethod("getBytes");
            
            Class<?> classClazz = Class.forName("java.lang.Class");
            obtainDex = classClazz.getDeclaredMethod("getDex");
        } catch (Exception ex) {
            XposedBridge.log("Falha na reflexão: " + ex);
        }
    }
    
    private void interceptClassLoader(XC_LoadPackage.LoadPackageParam params) {
        XposedHelpers.findAndHookMethod(
            "java.lang.ClassLoader", 
            params.classLoader,
            "loadClass",
            String.class,
            boolean.class,
            new XC_MethodHook() {
                @Override
                protected void afterHookedMethod(MethodHookParam mp) {
                    try {
                        Class<?> loaded = (Class<?>) mp.getResult();
                        if (loaded == null) return;
                        
                        Object dexInstance = obtainDex.invoke(loaded);
                        byte[] rawDex = (byte[]) extractBytes.invoke(dexInstance);
                        
                        if (rawDex == null) return;
                        
                        String filename = String.format(
                            "/data/data/%s/dump_%d.dex",
                            targetPackage, 
                            rawDex.length);
                        
                        persistBytes(rawDex, filename);
                        
                    } catch (Exception ex) {
                        XposedBridge.log("Erro no hook: " + ex);
                    }
                }
            });
    }
    
    private void persistBytes(byte[] data, String path) {
        File destination = new File(path);
        if (destination.exists()) return;
        
        try (OutputStream stream = new FileOutputStream(destination)) {
            stream.write(data);
            XposedBridge.log("DEX salvo: " + path);
        } catch (IOException io) {
            XposedBridge.log("Falha ao escrever: " + io);
        }
    }
}

A estratégia explora o momento exato em que o ClassLoader materializa a classe, interceptando a estrutura DEX antes que ela seja executada pela máquina virtual.

Tags: android-security xposed-framework dex-extraction reverse-engineering apk-unpacking

Publicado em 9-8 03:32