DexPatcher: patch APK Android utilizzando Java

DexPatcher consente agli sviluppatori di applicare patch agli APK utilizzando Java. Ciò presenta numerosi vantaggi e l'utilizzo di DexPatcher è molto più semplice rispetto al classico approccio Smali.

Probabilmente hai visto o installato applicazioni modificate, che si tratti di un dialer con patch per la tua risoluzione o di una versione personalizzata di WhatsApp con funzionalità aggiunte. Ma come fanno gli sviluppatori a farlo? La maggior parte delle volte il codice sorgente delle applicazioni non è nemmeno disponibile, quindi come funziona? Lo vedremo prima, poi daremo un'occhiata a un nuovo strumento che mira a rendere il processo molto più semplice e infine lo confronteremo con il popolare framework Xposed per vedere in cosa differiscono.

Potresti aver sentito parlare di come vengono solitamente modificati gli APK: gli sviluppatori si collegano al file matrice, inizia a vedere tutto in Smali e acquisisci la capacità di modificare le cose usando il potere del Fonte. Una volta fatto, basta una telefonata per tirarli fuori e a quel punto saranno pronti per condividere i nuovi scintillanti APK.

Più seriamente... Cominciamo dall'inizio. Se non hai familiarità con il modding delle applicazioni Android, ti starai chiedendo cos'è smali. Gli sviluppatori di solito utilizzano il linguaggio di programmazione Java per codificare le app Android. Un programma (il compilatore) poi "traduce" quel codice in un altro formato adatto al tuo dispositivo, risultando in file .dex contenuti nel pacchetto dell'applicazione (o APK).

A quel punto, non potrai più accedere al codice sorgente originale (a meno che tu non sia lo sviluppatore o l'applicazione sia open source). Quello che hai però è l'APK, dato che è quello installato sul tuo dispositivo. Da esso, puoi ottenere i file dex (solitamente classi.dex) e quindi provare a tradurli in un formato che puoi comprendere. È qui che entra in gioco smali, come traduzione più leggibile ma fedele. Puoi fare un ulteriore passo avanti e tradurlo nuovamente in Java, anche se questo processo non è abbastanza fedele: otterrai un file risultato comprensibile, ma è probabile che non sarai in grado di tradurlo nuovamente al contrario poiché alcuni dettagli andranno persi lungo la strada. In altre parole, qualsiasi modifica tu faccia sarà vana poiché non potrai trasformarlo nuovamente in APK per installarlo sul tuo dispositivo... almeno non senza molto sforzo.

smali/baksmali è in realtà un assemblatore/dissimbler per il formato dex -- questo è ciò che significa letteralmente in islandese. Tuttavia, di solito ci riferiamo al formato che smali comprende quando diciamo 'Smali' (consideralo come un'istruzione definendo ogni piccolo dettaglio, anche se non è tutto necessario a noi umani, è quindi più dettagliato di Giava). Tieni inoltre presente che la spiegazione di cui sopra è un po' semplificata, ma dovrebbe essere un'analogia stretta pur essendo facile da capire.

Cosa dovrebbe fare uno sviluppatore per modificare un'app (senza accesso alla fonte), quindi? Il procedimento è più o meno il seguente:

  1. Ottieni l'APK (dal web o dal dispositivo).
  2. Usa qualcosa di simile apktool per decompilare l'APK in Smali. (apktool fa uso di smali/baksmali, ma rende molto più semplice la decompilazione e la ricostruzione degli APK e si occupa anche di decodificare risorse come i file XML.)
  3. Estrai classi.dex dall'APK, quindi utilizza dex2jar e infine un decompilatore Java per ottenere codice Java (incompleto, spesso rotto, ma per lo più comprensibile). (Questo è facoltativo, ma può essere utile poiché Smali è molto più difficile da capire.)
  4. Identificare cosa modificare.
  5. In realtà modificalo modificando direttamente il codice SMALI.
  6. In alternativa, scrivi la modifica in Java, compilala, decompilala nuovamente in Smali, quindi copia il codice Smali risultante.
  7. Una volta finito tutto, usa apktool di nuovo per ricostruire l'APK.
  8. Firma l'APK (per verificare l'identità dell'autore; tutti i pacchetti devono essere firmati) e infine installarlo.

Scrivere il codice Smali è piuttosto difficile e soggetto a errori. È possibile apportare modifiche più piccole in Smali, ma aggiungere nuove funzionalità è più impegnativo. Inoltre, non si verificheranno errori in fase di compilazione, quindi anche gli errori di battitura potrebbero essere rilevati solo in fase di esecuzione. Anche l'espansione e la condivisione delle patch Smali può essere problematica, poiché le differenze tendono ad essere molto specifiche per una particolare versione dell'APK. Sebbene esistano alcuni strumenti per rendere più semplici parti del processo spiegato sopra (Virtuoso Ten Studio mi viene in mente), può ancora diventare noioso.

DexPatcher del membro senior di XDA Lançon mira a risolvere questi problemi, rendendo il processo più semplice e consentendo agli sviluppatori di evitare completamente di avere a che fare con Smali. Invece, gli sviluppatori possono scrivere patch solo in Java e lasciare che DexPatcher gestisca tutto il resto.

Questo ha il vantaggio principale di avere file di patch facilmente leggibili e gestibili. Anche l'applicazione di patch agli APK diventa in generale più conveniente. Tra poco vedremo un esempio completo su come utilizzare DexPatcher, ma ecco prima una rapida panoramica di ciò che offre:

  • Fonte aperta.
  • Multipiattaforma: dovrebbe funzionare su Linux, Mac e Windows.
  • File patch: le modifiche apportate sono contenute in file patch Java che puoi condividere in modo indipendente.
  • Java: non è SMALI.

Ottieni anche il vantaggio del controllo degli errori in fase di compilazione, quindi i bug vengono visualizzati nelle prime fasi del ciclo di sviluppo. Il Java compilato fornisce il consueto controllo del tempo di compilazione (con accesso ai simboli APK originali) e DexPatcher applica compatibilità del sorgente e della patch durante l'applicazione della patch, fornendo informazioni utili e dando avvisi quando sembra che tu stia facendo qualcosa legale ma sospetto.

In aggiunta a ciò, DexPatcher viene fornito con un set di script di supporto (disponibili solo su Linux, sebbene possano essere trasferiti anche su altre piattaforme). Questi si occupano di impostare lo spazio di lavoro, estrarre le classi e le risorse dell'APK di destinazione, decompilare le classi in Java (il Decompilatore Java CFR viene utilizzato per quest'ultimo) e infine creare e firmare l'APK con patch una volta terminato.

Diamo un'occhiata ad un esempio (su Linux):

Installa gli script 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.

Configura gli script DexPatcher

Aprire dxp.config nel tuo editor di testo preferito e assicurati di modificare le variabili necessarie per adattarle al tuo sistema. Devi solo modificare la riga seguente in modo che punti al percorso di installazione dell'SDK Android:

dxp_android_sdk_dir=(~/android/sdk)

(DexPatcher sceglierà automaticamente la versione della piattaforma più alta disponibile. Inoltre, puoi anche modificare altre opzioni di configurazione per fare in modo che utilizzi le tue versioni di alcuni strumenti invece delle impostazioni predefinite in bundle.)

Per facilità di accesso, possiamo aggiungere il file dexpatch directory al nostro SENTIERO, o anche collegare simbolicamente il diverso dxp-* script in una posizione già presente nel tuo file SENTIERO, ad esempio ~/bin:

export PATH=$PWD:$PATH

Modificare un'applicazione

Per questo esempio utilizzeremo un'applicazione semplice e open source. Naturalmente, in questo caso particolare sarebbe possibile applicare direttamente una patch al codice sorgente, ma non è affatto divertente!

Prenderemo l'applicazione "Get ID" di basil2style, un'applicazione che ti mostra alcuni dettagli sul tuo dispositivo. Il nostro obiettivo è modificare il pulsante "Copia" per "ID dispositivo" e condividere invece questo ID:

  • Per prima cosa scarichiamo l'APK che andremo a modificare: Ottieni un documento d'identità.
  • Decompilare l'applicazione.
  • Crea la chiave di firma che utilizzeremo successivamente per firmare l'APK.

Possiamo fare tutto anche tramite shell, utilizzando gli script helper:

$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".

Noterai alcune directory diverse lì dentro:

  • decodificare: qui troverai le risorse e Smali, così come decodificate da apktool.
  • src: Directory vuota. Qui è dove posizioneremo i nostri file di patch.
  • src-cfr: qui è dove cfr decompilato l'app (insieme agli errori). Un buon posto in cui cercare per decidere cosa cambiare (potresti anche aver bisogno di risorse e dei loro ID dalla directory di decodifica sopra, ma non per questo esempio particolare).
  • src-cfr-nodecode: come sopra, ma contenente solo stub vuoti (nessun codice, solo scheletri). Puoi usare questi file come base per la tua patch, come vedremo tra poco.

Come accennato in precedenza, vogliamo modificare il pulsante "Copia" dell'ID dispositivo per condividere invece il testo dell'ID. Se diamo un'occhiata al codice sorgente, noteremo che il pulsante Copia ID dispositivo (dispositivo_copia) al clic l'evento viene gestito dalla classe anonima in src-cfr/makeinfo/com/getid/MainActivity.java. Anche se potremmo modificarlo qui, di solito è meglio trovare un modo alternativo per farlo poiché le classi anonime hanno nomi numerici (NomeClasseMain$AlcuniNumero, per esempio. Attività principale$ 3) che potrebbe cambiare in modo imprevedibile tra le versioni.

Invece, registreremo la nostra classe per l'evento modificando il file Attività principale classe. Innanzitutto, copiamo la versione "scheletro" da src-cfr-nocode/makeinfo/com/getid/MainActivity.java A src/makeinfo/com/getid/MainActivity.java (ricordati che src è dove vivrà la nostra patch). (Se preferisci, puoi anche copiare la versione con il codice completo, è puramente una questione di gusti.)

Ora possiamo modificarlo come segue:

  • Aggiungi l'importazione necessaria per l'annotazione DexPatcher:
importlanchon.dexpatcher.annotation.*;
  • Aggiungi un tag per indicare che stiamo modificando la classe. Impostiamo anche l'azione predefinita per i membri della classe patch su IGNORARE, il che significa che i membri sono lì per essere referenziati dal nostro codice durante la compilazione Java, ma verranno ignorati da 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{

  • Inoltre, aggiungi corpi vuoti al costruttore e onCreate, così come tutti gli altri metodi che intendiamo utilizzare (ricorda che verranno ignorati quando la nostra patch verrà effettivamente applicata: li stiamo semplicemente aggiungendo in modo da poterli fare riferimento qui se necessario). Puoi anche semplicemente aggiungere il file nativo parola chiave invece.
  • Possiamo già creare la patch a questo punto, se sei curioso:
    $ dxp-make # Output: `patched.apk`.
    Abbastanza semplice, vero? Ma andiamo avanti: non abbiamo ancora finito.
  • Modifichiamo onCreate ora per iniziare il proprio OnClickListener in modo da poter condividere l'ID del dispositivo invece di copiarlo negli appunti:
    // 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();}}}
  • Sembra che ora abbiamo finito! La patch completa dovrebbe apparire così Questo. Ora possiamo creare l'APK con patch e installarlo:
    $ dxp-make$ adb install patched.apk
  • Diamo un'occhiata al risultato:

(Grazie a Lanchon per l'aiuto con il codice di esempio!)

Xposed è immensamente popolare e per una buona ragione: rende la creazione, la condivisione e l'installazione di mod molto più semplice sia per gli sviluppatori che per gli utenti. Ci sono alcune differenze tra DexPatcher e Xposed che potrebbero far sì che alcuni preferiscano l'uno rispetto all'altro:

  1. Xposed fa la sua magia agganciando i metodi in fase di esecuzione e consentendo agli sviluppatori di fare qualcosa prima, dopo o al posto di qualsiasi metodo. DexPatcher, d'altra parte, modifica tutto prima del runtime e produce un APK modificato autonomo -- è ancora possibile eseguire il codice prima, dopo o al posto dei metodi e in realtà ne hai qualcosa in più libertà.
  2. Produrre un APK autonomo significa che non dipende da alcun framework esterno. Ciò significa anche che non è richiesto root per modificare le app utente.
  3. Poiché hai creato un nuovo APK con DexPatcher, verrà firmato in modo diverso. Ciò significa che gli utenti non possono ricevere aggiornamenti ufficiali dall'autore originale e potrebbero causare alcuni problemi con app come Google Apps se le firme vengono controllate.
  4. Il codice sorgente sia dei moduli che delle patch DexPatcher può essere facilmente distribuito e modificato. Condividono anche molte somiglianze se acquisisci familiarità con ciascuno.

Abbiamo parlato abbastanza di DexPatcher. Adesso è il tuo turno di provarci, quindi vai al Discussione del forum DexPatcher per iniziare subito!