Qualche tempo fa scrissi due righe su come utilizzare l’adb per andarsi a leggere le cartelle private di un’applicazione Android installata sul proprio dispositivo, cosa molto utile per esempio quando si vuole leggere i dati contenuti in un database sqlite.
Quest’oggi invece mi sono tolto la curiosità di provare a tirarmi giù dal telefono varie applicazioni – scritte nei modi più disparati – e “strucinare” un minimo al loro interno giusto per capire quali sono gli strumenti di sviluppo più sicuri.
File apk partendo da un’app installata su un telefono Android
Come già detto nell’altro articolo l’adb nient’altro è che un bridge tra dispositivo e macchina di sviluppo, e lo si può trovare nel percorso android-sdk/platform-tools/adb
, dove “android-sdk” è il percorso di installazione dell’sdk Android (la posizione dipende da cosa si utilizza per sviluppare: Android Studio, Xamarin, Titanium, Ionic, …).
Utilizzando l’adb si può scaricare sulla propria macchina l’apk dell’applicazione che ci interessa con questi comandi:
adb shell pm list packages
adb shell pm path com.boh.nonso
adb pull /data/app/com.boh.nonso-2.apk path
Il primo elenca tutte le applicazioni installate, con il secondo si ottiene il path di quella che ci interessa e con il terzo la si scarica.
Listato con tutte le applicazioni installate sul dispositivo
“Esplorazione” dell’apk
Qui si va sul difficile… è sufficiente rinominare il file sostituendo l’estensione “apk” con “zip” e scompattare lo zip.
-
-
Contenuto di un’app sviluppata con Xamarin Android
-
-
Contenuto di un’app sviluppata con Titanium
-
-
Contenuto dell’app di Wind, sviluppata con IBM Worklight
-
-
Contenuto dell’app di Babbel, sviluppata nativamente
-
-
Contenuto di un’app scritta con Ionic/Cordova
Studio dei sorgenti dell’applicazione
La fase più complessa principalmente perché l’applicazione potrebbe essere stata sviluppata nei modi più disparati.
Uno strumento utile è sicuramente il Dex2Jar, che come dice il nome permette di convertire il/i file .dex in dei .jar.
Quest’attrezzo però non va molto d’accordo con l’osx, e per farlo funzionare potrebbe essere necessario lavorare un po’ di martello e Stackoverflow, per esempio togliendo la cartella dove si trova il dex2jar dalla quarantena con xattr -rd com.apple.quarantine path
.
Una volta dati i permessi di esecuzione allo script d2j-dex2jar.sh
basterà eseguirlo e andare a prendere il file generato all’interno della directory del’applicativo:
sh d2j-dex2jar.sh -f apk_path
I file .dex
Qualunque sia lo strumento utilizzato per sviluppare l’applicazione almeno un file .dex c’è sempre, ma non è detto che il codice contenuto al suo interno sia significativo. Se infatti l’applicazione non è stata scritta nativamente in Java (o c++) il .dex serve solo come container/ponte per l’applicazione vera e propria, che potrebbe essere stata sviluppata in Javascript, C#, …
Un file .dex c’è ma non è detto che sia da solo; questo accade quando l’applicazione è stata compilata utilizzando il MultiDex per superare l’infame limite dei 65536 metodi della piattaforma Android.
Decompilazione dei .jar
Una volta ottenuti i jar, ovvero degli archivi contenenti i file .class (classi Java compilate), decompilarli per ottenere il codice Java originario è un gioco da ragazzi, ed è sufficiente aprirli con un decompilatore Java come Java Decompiler.
-
-
Jar dell’app di Babbel, sviluppata nativamente e offuscata
-
-
Jar dell’app di Couchsurfing, sviluppata nativamente ma non offuscata
-
-
Jar dell’app di Wind, scritta con IBM Worklight/Cordova
-
-
Jar di un’app scritta con Titanium
-
-
Jar di un’app scritta con Xamarin Android
-
-
Jar di un’app scritta con Ionic/Cordova
Senza aver studiato i vari casi nel dettaglio devo dire che tra tutti il sistema apparentemente più difficile da crackare sembra quello di Titanium, che minimizza e cripta il codice javascript originario inserendolo in delle HashMap contenute all’interno del codice java autogenerato dall’sdk di Titanium.
Applicazione scritta in Xamarin, decompilazione delle DLL
Se l’applicazione è stata sviluppata con Xamarin, ovvero scritta in C#, il codice contenuto nel file jar non è molto significativo; all’interno dell’apk però ci sono le dll che possono essere decompilate facilmente con un decompilatore .NET come ILSpy.
Libreria scritta con Xamarin e decompilata con ILSpy
Il processo di decompilazione potrebbe non essere perfetto, e infatti alcuni dettagli si perdono. Per esempio ILSpy non sembra (ancora) in grado di riconoscere le stringhe ottenute con un
nameof
sulle proprietà di un oggetto, o le stringhe scritte con sintassi
$"{code}"
(che vengono tradotte con delle
string.format
), ma per lo più si tratta di zucchero sintattico introdotto nelle ultime versioni del .NET mentre il grosso viene interpretato esattamente così com’era stato scritto originariamente.
Su reverse engineering ed offuscamento
La reverse engineering non è mai vista di buon occhio e, salvo rari casi come lo studio di sistemi esistenti al fine di garantirne l’interoperabilità con altri, è più o meno proibita.
Di certo quelli descritti sommariamente qui sopra sono esercizi molto utili a fini didattici e che in alcuni casi possono aiutarci a scovare bug in strumenti che utilizziamo tutti i giorni, quindi conoscere un po’ queste tematiche è cosa buona e giusta. Inoltre è bene sapere che il nostro lavoro potrebbe essere studiato con grande facilità da malintenzionati e/o concorrenti al fine di danneggiarci o copiare il nostro lavoro, ed è qui che entrano in gioco tutta una serie di buone pratiche su cosa includere o no all’interno della nostra applicazione.
L’offuscamento del codice è argomento controverso senza risposte precise, e in rete si possono trovare moltissime discussioni sull’argomento e vari strumenti per ciascun linguaggio.
In generale a mio avviso non conviene farci affidamento se è vostra intenzione proteggere delle password o rendere incomprensibile il codice di applicazioni molto famose per evitare che dei cracker trovino eventuali vulnerabilità, perché non esiste offuscamento che regga contro il tempo e la pazienza di pirati informatici degni di questo nome.
Al contrario potreste aver sviluppato un’applicazione che deve funzionare offline e al cui interno ci sono soluzioni avanzate e che vi danno un vantaggio competitivo su eventuali concorrenti; in questo caso può aver senso sviluppare tutte le “logiche di calcolo” in una libreria a parte e offuscare solo quella in modo da renderne più difficile lo studio e l’utilizzo da parte di malintenzionati… che poi magari uno sta a pagare per un offuscatore, a rallentare tutta la catena di deploy, a rischiare di inserire bug dovuti all’offuscamento, e il repository con il codice è accessibile via web da molti utenti con password deboli, o ancora più comune si pagano gli sviluppatori come degli operai di basso livello e li si lascia partire con la conoscenza o peggio con i sorgenti, ma questa è un’altra storia.