Cet article est un complément de celui de Julien.
Partons d’un APK Crackme01.apk. Le fichier est disponible ici.
Voici à quoi ressemble le Crack Me :
Deux methodes existent pour reverser une application android :
- La première avec dex2jar et jd-gui, cette méthode permet d’obtenir quasiment le code original, facile à analyser du code java et simple d’utilisation.Cependant avec cette méthode on ne peut pas modifier le code java (.class), nos ressources sont présentes sous la forme numérique uniquement dans les findviewbyid, par exemple.
- La seconde avec smali(assembleur) et baksmali(desassembleur), on retrouve ces outils dans apktool (d pour desassembler, b pour assembler).Lorsque l’on désassemble on obtient un code source smali, et on peut le réassembler. Une bible pour le code smali est présente ici : http://pallergabor.uw.hu/androidblog/dalvik_opcodes.html
Le but de ce crackme est de trouver le mot de passe pour la méthode 1.
Dans la seconde méthode, on ajoute un message rapide (Toasts) « Simple, non ? »
Methode 1 : Dex2jar et JD-GUI
On décompresse notre archive ZIP :
$ unzip Crackme01.apk Archive: Crackme01.apk inflating: res/layout/activity_crack_me01.xml inflating: res/menu/activity_crack_me01.xml inflating: AndroidManifest.xml extracting: resources.arsc extracting: res/drawable-hdpi/ic_launcher.png extracting: res/drawable-ldpi/ic_launcher.png extracting: res/drawable-mdpi/ic_launcher.png extracting: res/drawable-xhdpi/ic_launcher.png inflating: classes.dex inflating: META-INF/MANIFEST.MF inflating: META-INF/CERT.SF inflating: META-INF/CERT.RSA |
Avant de continuer, on est obligé de vous parler de la machine Dalvik. Cette machine dalvik est une machine virtuelle utilisée par Android. Elle a été développée par Google pour ne pas utiliser la JVM. La différence notable avec la JVM est la gestion du registre. Pour la JVM, la gestion est basée sur la pile contrairement à la machine Dalvik qui est illimitée. Cette machine utilise un fichier .dex qui est le regroupement de l’ensemble des classes JAVA compilées.
Obtenons un jar de notre fichier .dex :
$ dex2jar classes.dex
dex2jar classes.dex -> classes-dex2jar.jar
Utilisons JD-GUI, afin de voir nos fichiers .class qui sont présents dans l’archive .JAR
jd-gui classes-dex2jar.jar
Dans le fichier Crackme01.class on peut voir la ligne suivante :
final EditText localEditText = (EditText)findViewById(2131165185);
Cette ligne fait référence a un id, qui a pour intitulé motDePasse comme on peut le voir dans le fichier R.class :
public static final int motDePasse = 2131165185;
C’est l’EditText qui est utilisé pour saisir notre mot de passe.
On retourne dans le fichier Crackme01.class :
if ((localEditText.getText().toString() != null) && (localEditText.getText().toString().equals("oversimple"))) localToast.setText(2130968580); |
On voit que l’on compare notre EditText avec une chaîne de caractère « oversimple ». Cette chaine de caractère « oversimple » est notre mot de passe.
Méthode 2 : Smali & backsmali
Pour la deuxieme méthode avec smali et backsmali, on utilisera apktool qui contient un ensemble d’outils. De plus, celui ci est simple d’utilisation alors pourquoi s’en priver ?
On décompile notre APK.
$ apktool d Crackme01.apk reverseCrackme01/ I: Baksmaling... I: Loading resource table... I: Loaded. I: Decoding AndroidManifest.xml with resources... I: Loading resource table from file: /home/pierre/apktool/framework/1.apk I: Loaded. I: Regular manifest package... I: Decoding file-resources... I: Decoding values */* XMLs... I: Done. I: Copying assets and libs... |
On obtient un répertoire reverseCrackme01 :
$ cd reverseCrackme01/ $ ls AndroidManifest.xml apktool.yml res smali |
La premiere chose a regarder est le Manifest :
$ cat AndroidManifest.xml <!--?xml version="1.0" encoding="utf-8"?--> |
On peut voir que notre main est le fichier CrackMe01 car celui-ci possède les intents nécessaires (MAIN et LAUNCHER)
Regardons maintenant les fichiers avec le code source smali :
$ ls smali/fr/oversimple/crackme01/ BuildConfig.smali CrackMe01$1.smali CrackMe01.smali R$attr.smali R$drawable.smali R$id.smali R$layout.smali R$menu.smali R$string.smali R$style.smali R.smali |
Si on regarde dans le fichier CrackMe01\$1.smali, on peut voir la méthode onClick. Cette méthode correspond à un clic sur le bouton « Valider ». Voici un bref aperçu du code smali :
invoke-interface {v0}, Landroid/text/Editable;->toString()Ljava/lang/String; move-result-object v0 const-string v1, "oversimple" invoke-virtual {v0, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z move-result v0 |
Ce bout de code permet de comparer notre mot de passe « oversimple » avec notre EditText(dans le but de simplifier, on affiche uniquement le retour du toString)
Et le test de comparaison final :
if-eqz v0, :cond_0 .line 29 iget-object v0, p0, Lfr/oversimple/crackme01/CrackMe01$1;->val$toast:Landroid/widget/Toast; const v1, 0x7f040004 invoke-virtual {v0, v1}, Landroid/widget/Toast;->setText(I)V .line 33 :goto_0 iget-object v0, p0, Lfr/oversimple/crackme01/CrackMe01$1;->val$toast:Landroid/widget/Toast; invoke-virtual {v0}, Landroid/widget/Toast;->show()V .line 34 return-void .line 31 :cond_0 iget-object v0, p0, Lfr/oversimple/crackme01/CrackMe01$1;->val$toast:Landroid/widget/Toast; const v1, 0x7f040005 invoke-virtual {v0, v1}, Landroid/widget/Toast;->setText(I)V goto :goto_0 |
Si l’instruction if-eqz est égale à 0 alors nous allons à la condition cond_0. sinon on continue la suite donc .line 29.
Pour information, les valeurs qui nous intéressent se trouvent dans le fichier R$string.smali :
.field public static final ok:I = 0x7f040004
.field public static final ko:I = 0x7f040005
Le but de la deuxième méthode n’étant pas forcement de voir le mot de passe mais d’ajouter un message au démarrage de l’application.
Dans le fichier CrackMe01.smali, ligne 37 et après nous avons :
const-string v4, "" const/4 v5, 0x1 invoke-static {v3, v4, v5}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast; move-result-object v1 |
Nous souhaitons juste ajouter notre message, donc à la place de « » mettons : « Simple, non ? » et appelons la méthode afin de le rendre visible :
invoke-virtual {v1}, Landroid/widget/Toast;->show()V
Ce qui donne :
const-string v4, "Simple, non ?" const/4 v5, 0x1 invoke-static {v3, v4, v5}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast; move-result-object v1 invoke-virtual {v1}, Landroid/widget/Toast;->show()V |
On reconstruit l’application avec apktool b, il faut se placer dans le répertoire racineCrackme01 (ou ce trouve le fichier apktool.yml)
$ apktool b I: Checking whether sources has changed... I: Smaling... I: Checking whether resources has changed... I: Building resources... I: Building apk file... |
Un nouveau repertoire apparait dist/
On crée une clé afin de signer notre application :
keytool -genkey -v -keystore oversimple.keystore -alias oversimple -keyalg RSA -validity 25000
Une fois la clé créée, on signe notre application :
jarsigner -digestalg SHA1 -sigalg MD5withRSA -verbose -keystore oversimple.keystore dist/Crackme01.apk oversimple
Notre apk est signée de source inconnu.
On envoie notre apk :
adb install dist/Crackme01.apk 978 KB/s (184902 bytes in 0.184s) pkg: /data/local/tmp/Crackme01.apk Success |
Un article suivant présentera les commandes utiles de ADB.
Résultat au démarrage de notre APK :
Conclusion
Pour cette introduction au reverse d’application Android, rien de bien difficile, plusieurs articles en tête, notammennt un article sur les commandes adb et les pouvoirs de cette commande 😉
Sources
dex2jar : http://code.google.com/p/dex2jar/
jd-gui : http://java.decompiler.free.fr/?q=jdgui
apktool : http://code.google.com/p/android-apktool/
It’s oversimple isn’t it?
Bonjour,
Il existe des solutions de chiffrement d’une application Android telle que celle proposée par APK Protect : apkprotect.com.
Vous en pensez quoi ?
Est-ce réellement une protection indiscutable ?
Bonjour Laurent,
Pour répondre à votre question de manière générique, il n’y aucune technique pour éviter un reverse complet de l’application. Vous ne pouvez pas protéger votre application contre les modifications. Et n’importe quelle protection mise en place, peut être modifiée/désactivée.
L’application peut être chiffrée mais il faudra forcement un mécanisme pour déchiffrer l’application au lancement de celle-ci.
Il y a donc aucune protection indiscutable pour protéger votre application, seules des routines de protection peuvent permettre à un attaquant de passer plus de temps à analyser. C’est le même problème que pour les binaires des autres plateformes. Par exemple, vous avez des routines de protection qui permettent à l’attaquant d’avoir des problèmes pour le reverse de l’application, mais l’attaquant finit toujours par y arriver. Il en est de même pour la protection des données dans les binaires.
L’application cliente ne doit surtout pas être considérée comme sécurisée, la partie critique doit être gérée coté serveur ou sur un Secure Element.
En espérant avoir répondu à votre question,
Pierre.