DexPatcher: corrigir APKs do Android usando Java

O DexPatcher permite que os desenvolvedores corrijam APKs usando Java. Isso tem várias vantagens e usar o DexPatcher é muito mais fácil do que a abordagem clássica do Smali.

Você provavelmente já viu ou instalou aplicativos modificados, seja um discador corrigido para sua resolução ou uma versão personalizada do WhatsApp com recursos adicionais. Mas como os desenvolvedores fazem isso? Muitas vezes, o código-fonte dos aplicativos nem está disponível, então como tudo funciona? Veremos isso primeiro, depois daremos uma olhada em uma nova ferramenta que visa tornar o processo muito mais fácil e, finalmente, compará-la-emos com o popular framework Xposed para ver como eles diferem.

Você deve ter ouvido falar sobre como os APKs geralmente são modificados: os desenvolvedores se conectam ao matriz, comece a ver tudo em Smali e ganhe a habilidade de modificar as coisas usando o poder do Fonte. Uma chamada telefônica é suficiente para tirá-los de lá quando isso for feito, quando eles estarão prontos para compartilhar os APKs novinhos em folha.

Falando sério… Vamos começar do início. Se você não está familiarizado com a modificação de aplicativos Android, deve estar se perguntando o que é smali. Os desenvolvedores geralmente usam a linguagem de programação Java para codificar aplicativos Android. Um programa (o compilador) então “traduz” esse código para outro formato adequado ao seu dispositivo, resultando em arquivos .dex contidos no pacote do aplicativo (ou APK).

Nesse ponto, você não poderá mais acessar o código-fonte original (a menos que você seja o desenvolvedor ou o aplicativo seja de código aberto). Porém, o que você tem é o APK, já que é o que está instalado no seu dispositivo. A partir dele, você pode obter os arquivos dex (geralmente classes.dex) e tentar traduzi-los de volta para um formato que você possa entender. É aí que entra o smali, como uma tradução mais legível, mas fiel. Você pode dar um passo adiante e traduzi-lo de volta para Java, embora esse processo não seja fiel o suficiente - você obterá uma resultado compreensível, mas é provável que você não consiga traduzi-lo ao contrário novamente, pois alguns detalhes serão perdidos pelo caminho. Em outras palavras, qualquer modificação que você fizer será em vão, já que você não poderá transformá-lo novamente em APK para instalá-lo em seu dispositivo… pelo menos não sem muito esforço.

smali/baksmali é na verdade um montador/dissimulador para o formato dex - é o que significa literalmente em islandês. No entanto, geralmente nos referimos ao formato que o smali entende quando dizemos 'Smali' (pense nisso como instruções definir cada pequeno detalhe, mesmo que não seja tudo necessário para nós, humanos - é, portanto, mais detalhado do que Java). Observe também que a explicação acima é um pouco simplificada, mas deve ser uma analogia próxima e ao mesmo tempo fácil de entender.

O que um desenvolvedor precisaria fazer para modificar um aplicativo (sem acesso à fonte), então? O processo é mais ou menos o seguinte:

  1. Obtenha o APK (da web ou do dispositivo).
  2. Use algo como apktool para descompilar o APK para Smali. (apktool faz uso de smali/baksmali, mas torna muito mais fácil descompilar e reconstruir APKs e também cuida da decodificação de recursos como arquivos XML.)
  3. Extraia classes.dex do APK e use dex2jar e, finalmente, um descompilador Java para obter código Java (incompleto, muitas vezes quebrado, mas principalmente compreensível). (Isso é opcional, mas pode ser útil porque Smali é muito mais difícil de entender.)
  4. Identifique o que modificar.
  5. Na verdade, modifique-o editando o código Smali diretamente.
  6. Alternativamente, escreva a modificação em Java, compile-a, descompile-a novamente para Smali e copie o código Smali resultante.
  7. Quando tudo acabar, use apktool novamente para reconstruir o APK.
  8. Assine o APK (para verificar a identidade do autor; todos os pacotes devem ser assinados) e finalmente instalá-lo.

Escrever código Smali é bastante difícil e sujeito a erros. Pequenas alterações podem ser feitas no Smali, mas adicionar novos recursos é mais desafiador. Além disso, você não terá erros em tempo de compilação, portanto, mesmo erros de digitação só poderão ser detectados em tempo de execução. Expandir e compartilhar patches Smali também pode ser problemático, pois as diferenças tendem a ser muito específicas para uma versão específica do APK. Embora existam algumas ferramentas para facilitar partes do processo explicado acima (Estúdio Virtuoso Dez vem à mente), ainda pode ser cansativo.

DexPatcher por membro sênior do XDA Lanchonete visa corrigir esses problemas, tornando o processo mais simples e permitindo que os desenvolvedores evitem completamente lidar com o Smali. Em vez disso, os desenvolvedores podem escrever patches apenas em Java e fazer com que o DexPatcher cuide de todo o resto.

Isto tem a principal vantagem de ter arquivos de patch facilmente legíveis e gerenciáveis. A correção de APKs também se torna mais conveniente em geral. Veremos um exemplo completo de como usar o DexPatcher daqui a pouco, mas aqui está uma rápida visão geral do que ele oferece primeiro:

  • Código aberto.
  • Multiplataforma: deve rodar em Linux, Mac e Windows.
  • Arquivos de patch: as modificações feitas estão contidas em arquivos de patch Java que você pode compartilhar de forma independente.
  • Java: não é Smali.

Você também ganha a vantagem da verificação de erros em tempo de construção, para que os bugs apareçam no início do ciclo de desenvolvimento. O Java compilado fornece sua verificação usual do tempo de compilação (com acesso aos símbolos originais do APK) e o DexPatcher impõe compatibilidade de origem e patch ao aplicar patches, fornecendo informações úteis e avisando quando você parece estar fazendo algo legal, mas suspeito.

Além disso, o DexPatcher vem com um conjunto de scripts auxiliares (disponível apenas no Linux, embora também possam ser portados para outras plataformas). Eles se encarregam de configurar o espaço de trabalho, extrair as classes e recursos do APK alvo, descompilar as classes para Java (o Descompilador CFR Java é usado para o último) e, finalmente, criar e assinar o APK corrigido quando terminar.

Vejamos um exemplo (no Linux):

Instale os scripts DexPatcher

$# Make a directory where we can test stuff out and enter it. 

$ mkdir xda-test

$cd xda-test

$ git clone https://github.com/Lanchon/DexPatcher-scripts.git dexpatcher # Clone the DexPatcher helper scripts repo.

$cd dexpatcher

$ chmod +x dxp-* # Not necessary, but for clarity: we need to make sure the files we'll call later are executable.

Configurar os scripts do DexPatcher

Abrir dxp.config em seu editor de texto favorito e certifique-se de alterar as variáveis ​​necessárias para se adequar ao seu sistema. Você só precisa alterar a linha a seguir para apontar para o local de instalação do Android SDK:

dxp_android_sdk_dir=(~/android/sdk)

(O DexPatcher escolherá automaticamente a versão de plataforma mais alta disponível. Além disso, você também pode modificar outras opções de configuração para que usem suas próprias versões de algumas ferramentas em vez dos padrões incluídos.)

Para facilitar o acesso, podemos adicionar o despachante diretório para o nosso CAMINHO, ou mesmo vincular simbolicamente os diferentes dxp-* scripts para um local que já está em seu CAMINHO, como ~/bin:

export PATH=$PWD:$PATH

Modificar um aplicativo

Neste exemplo, usaremos um aplicativo simples e de código aberto. É claro que corrigir o código-fonte diretamente seria possível neste caso específico, mas isso não é nada divertido!

Usaremos o aplicativo "Get ID" da basil2style, um aplicativo que mostra alguns detalhes do seu dispositivo. Nosso objetivo é modificar o botão "Copiar" do "ID do dispositivo" e fazer com que ele compartilhe esse ID:

  • Primeiro, vamos baixar o APK que vamos modificar: Obter identificação.
  • Descompile o aplicativo.
  • Crie a chave de assinatura que usaremos posteriormente para assinar o APK.

Também podemos fazer tudo através do shell, usando os scripts auxiliares:

$cd dexpatcher # Go to our working directory. 

$ curl -O https://f-droid.org/repo/makeinfo.com.getid_1.apk # Download the APK.

$ dxp-setup-for-apk makeinfo.com.getid_1.apk # Unpack and decompile the APK.

$cd makeinfo.com.getid_1 # Go to the newly created directory where everything is unpacked/decompiled to.

$ dxp-create-keystore # Create the APK signing key. Press 6 times (or fill out the info), then "yes".

Você notará alguns diretórios diferentes lá:

  • decodificar: você encontrará os recursos e Smali aqui, conforme decodificado por apktool.
  • fonte: Diretório vazio. É aqui que colocaremos nossos arquivos de patch.
  • src-cfr: é aqui que cfr descompilei o aplicativo (junto com erros). Um bom local para decidir o que mudar (você também pode precisar de recursos e seus IDs do diretório de decodificação acima, mas não para este exemplo específico).
  • src-cfr-nodecode: igual ao acima, mas contendo apenas stubs vazios (sem código, apenas esqueletos). Você pode usar esses arquivos como base para seu patch, como veremos em breve.

Como mencionamos antes, queremos alterar o botão "Copiar" do ID do dispositivo para compartilhar o texto do ID. Se olharmos o código-fonte, notaremos que o botão Cópia de ID do dispositivo (cópia_do_dispositivo) onClick o evento é tratado pela classe anônima em src-cfr/makeinfo/com/getid/MainActivity.java. Embora possamos modificá-lo aqui, geralmente é melhor encontrar uma maneira alternativa de fazer isso, pois as classes anônimas têm nomes numéricos (MainClassName$SomeNumber, por exemplo. Atividade Principal$3) que pode mudar de forma imprevisível entre as versões.

Em vez disso, registraremos nossa própria classe para o evento, modificando o Atividade principal aula. Primeiro, vamos copiar a versão "esqueleto" de src-cfr-nocode/makeinfo/com/getid/MainActivity.java para src/makeinfo/com/getid/MainActivity.java (lembre-se disso fonte é onde nosso patch ficará). (Você também pode copiar a versão com o código completo se preferir, isso é apenas uma questão de gosto.)

Agora podemos editá-lo da seguinte maneira:

  • Adicione a importação necessária para a anotação do DexPatcher:
importlanchon.dexpatcher.annotation.*;
  • Adicione uma tag para indicar que estamos editando a classe. Também definimos a ação padrão para membros da classe patch como IGNORAR, o que significa que os membros estão lá para serem referenciados pelo nosso código durante a compilação Java, mas serão ignorados pelo DexPatcher.
@DexEdit(defaultAction=DexAction.IGNORE)

publicclassMainActivity

// The reference to ActionBarActivity will be satisfied by symbols

// extracted from the app when we build the patch.

extendsActionBarActivity{

  • Além disso, adicione corpos vazios ao construtor e onCreate bem como todos os outros métodos que planejamos usar (lembre-se de que eles serão ignorados quando nosso patch for realmente aplicado - estamos apenas adicionando-os para que possamos consultá-los aqui se precisarmos). Você também pode simplesmente adicionar o nativo palavra-chave em vez disso.
  • Já podemos construir o patch neste momento, se você estiver curioso:
    $ dxp-make # Output: `patched.apk`.
    Muito simples, certo? Vamos continuar, porém - ainda não terminamos.
  • Vamos editar onCreate agora para definir o próprio OnClickListener para que possamos compartilhar o ID do dispositivo em vez de copiá-lo para a área de transferência:
    // Rename the target method so that we can still call it (the original)// if needed.@DexEdit(target="onCreate")protectedvoidsource_onCreate(Bundlevar1){}// Add our new custom method.@Override@DexAddprotectedvoidonCreate(Bundlevar1){// Call the original method:source_onCreate(var1);// Replace the text and handler:device_copy.setText("Share");device_copy.setOnClickListener(newDeviceCopyOnClick());}// Note that we don't use an anonymous class to avoid nameclashing with// MainActivity$1, which already exists.// We also could've defined a nested MainActivity.Patch class and used// an anonymous class in MainActivity.Patch.onCreate(), and then called// MainActivity.Patch.onCreate() from MainActivity.onCreate().@DexAddclassDeviceCopyOnClickimplementsView.OnClickListener{@OverridepublicvoidonClick(Viewobject){if(MainActivity.this.val){Intentintent=newIntent(Intent.ACTION_SEND);intent.setType("text/plain");intent.putExtra(Intent.EXTRA_SUBJECT,"Device ID");intent.putExtra(Intent.EXTRA_TEXT,device.getText().toString());startActivity(Intent.createChooser(intent,"Share Device ID"));}else{Toast.makeText(MainActivity.this.getApplicationContext(),"Nothing to Share",0).show();}}}
  • Parece que terminamos agora! O patch completo deve se parecer com esse. Agora podemos construir o APK corrigido e instalá-lo:
    $ dxp-make$ adb install patched.apk
  • Vamos dar uma olhada no resultado:

(Obrigado ao Lanchon por ajudar com o código de exemplo!)

O Xposed é imensamente popular e por um bom motivo: torna a construção, o compartilhamento e a instalação de mods muito mais simples para desenvolvedores e usuários. Existem algumas diferenças entre DexPatcher e Xposed que podem fazer com que alguns prefiram um ao outro:

  1. O Xposed faz sua mágica conectando métodos em tempo de execução e permitindo que os desenvolvedores façam algo antes, depois ou em vez de qualquer método. O DexPatcher, por outro lado, modifica tudo antes do tempo de execução e produz um APK modificado e independente - ainda é possível executar código antes, depois ou em vez de métodos, e você realmente tem alguns recursos extras liberdade.
  2. Produzir um APK independente significa que ele não depende de nenhuma estrutura externa. Isso também significa que o root não é necessário para modificar os aplicativos do usuário.
  3. Como você criou um novo APK com o DexPatcher, ele será assinado de forma diferente. Isso significa que os usuários não podem receber atualizações oficiais do autor original e podem causar alguns problemas com aplicativos como o Google Apps se as assinaturas forem verificadas.
  4. O código-fonte dos módulos e dos patches do DexPatcher pode ser facilmente distribuído e modificado. Eles também compartilham muitas semelhanças se você se familiarizar um pouco com cada um.

Já conversamos o suficiente sobre o DexPatcher. Agora é a sua vez de tentar, então vá para o Tópico do fórum DexPatcher para começar imediatamente!