Android NDK – Reverse engineering d’une application ARM

Android NDK – Reverse engineering d’une application ARM

Les applications Android sont packagées au format APK (Android PacKage).

1) Généralités sur le format APK

Un package Android est, à l’instar d’un fichier JAR, une archive compressée contenant les éléments ci-dessous:

Format APK

Format APK

lib: fichiers développés avec le NDK et en langage natif pour une ou plusieurs architectures cibles (armeabi/, armeabi-v7a/, x86/, …)

res: fichiers de ressources multimédia

assets: fichiers de ressources multimédia non référencés par un ID

classes.dex:  compilation de fichiers développés avec le SDK contenant des fichiers en bytecode pour VM Dalvik d’Android. Cette VM est très similaire à une JVM classique mais son orientation « stackless » la rend plus optimisée pour les appareils mobiles.

AndroidManifest.xml: contenant les listes de fichiers, numéros de versions, permissions android du package.

META-INF/:  MANIFEST.MF(manifest de l’application), CERT.RSA(certificat de l’application), CERT.SF(listes de ressources et sommes SHA-1 des lignes du MANIFEST.MF correspondantes)

ressources.arsc: fichiers de ressources précompilées(images, fichiers XML, …)

 

1) Extraction du repository local

Les fichiers APK installés sous Android sont localisés dans /data/app (ou /system/app pour les applications intégrées au system).

$ ls -ld /data/app
drwxrwx–x system system 2014-09-19 16:51 app

Les droits –x indiquent que l’utilisateur non-system, dans notre cas non-ROOT, n’a pas le droit de lister le contenu du répertoire. En revanche, ayant le droit de lire les fichiers contenus dedans (sous réserve d’avoir les droits), il suffit de lister le nom complet d’un APK via le packet manager pour pouvoir l’extraire vers la sdcard pour analyse par exemple.

$ pm list packages -f |grep -i youtube
package:/data/app/com.google.android.youtube-2.apk=com.google.android.youtube

$ cp /data/app/com.google.android.youtube-2.apk /sdcard

 

 

2) Android NDK Native Development Kit

La facilité, la souplesse et la portabilité obtenue par le développement Java-like d’applications avec le SDK arrive avec une contrainte majeure: une perte de performance. En effet, de par sa nature, le bytecode Android à destination de la VM Dalvik, ou ART en passe de devenir le nouveau standard, souffre des mêmes problèmes de performances que tout logiciel d’émulation de code.

Pour pallier à ce problème, Android donne la possibilité aux développeurs ayant besoin de performance de développer certaines parties en C/C++ puis de les compiler directement dans l’architecture cible: ARM/MIPS/x86/… en utilisant le kits de développement natif: NDK.

Comme bien souvent, les développeurs de jeux-vidéos sont en tête de lice des demandeurs de performances et c’est pourquoi beaucoup de jeux sur le Play Store sont développés avec le NDK.

Nous avons donc choisi une application, remake pour Android d’un cèlèbre de jeu d’arcade des années 80-90 afin de regarder ce qu’il était possible de faire…

 

 

3) Analyse et PoC

La méthodologie d’analyse est très proche de celle utilisée pour les clients lourds Java à la différence près qu’une étape de conversion est nécessaire pour transformer le bytecode Dalvik vers du bytecode Java traditionnel.

En utilisant la suite d’outils dex2jar, il est ainsi possible de lire, convertir, dé-obfusquer ou même éditer le code Dalvik contenu dans le fichier classes.dex. 

dex2jar

dex2jar

 

Après cette étape, on obtient un fichier .jar que l’on pourra charger dans Java Decompiler ou autre afin de lire le code plus facilement et ainsi augmenter les chances de trouver de potentiels secrets ou faiblesses conceptuelles.

Le niveau de lisibilité du code en résultant sera, à peu de choses près, le même que celui d’une application .NET ouverte dans Reflector ou ILSpy (cf article précédent https://www.nes.fr/securitylab/?p=1225)

Un parcours sommaire du code dans Java Decompiler ne révèlera cependant aucune zone intéressante.

Java Decompiler GUI (jd-gui)

Java Decompiler GUI (jd-gui)

 

Ce n’est pas très étonnant en considération du contenu du répertoire lib de l’APK analysé qui est trois fois plus volumineux que le fichiers .dex:

lib_dir

Contenu du répertoire ./lib

Il va donc falloir analyser en détail les fichiers ci-dessus pour espérer tomber sur des infos intéressantes.

$ file ./lib/armeabi/libgame_logic.so
ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically linked, stripped

Ces fichiers sont des fichiers objets ARM développés avec le NDK d’Android (Native Development Kit). C’est monnaie courante dans les jeux-vidéos Android, nature de l’application auditée.

Le principe de l’application, gratuite, est d’utiliser un modèle économique « pay-to-win » où, payer n’est pas obligatoire mais vivement suggéré pour pouvoir avancer dans le jeu.

ingame_marche

Options payantes

marche

Marché du jeu

 

Après un rapide parcours des différents exports des bibliothèques, on décide de concentrer notre analyse sur les fichiers libgame.so et libgame_logic.so.

ida_view_exports

 

Comble du luxe, IDA gère le mangling d’Android, ce qui permet de visualiser les prototypes des fonctions et méthodes C++ de manière plus lisible.

Afin de faire un POC, on regarde un peu plus en détail une fonction potentiellement sympathique  CDamageEmitter::updateStatus() avec l’idée de ne pas updater les damage infligés sur notre personnage.

Une rapide analyse permet de repérer un jump intéressant à l’offset 0X0011E540 de la section .text. qui semble JUMPER à la partie UPDATE des damage si aucun bonus d’invincibilité n’est en cours d’utilisation.

ida_graphview

Vue « Graph » d’IDA

Linstruction BEQ, « Branch if equal » est un saut conditionnel basé sur l’état des FLAGS settés par l’instruction CMP juste avant.

 

ida_textview

Vue texte

 

 

L’idée de base est de tout le temps contourner ce saut ce qui peut s’obtenir facilement en « effaçant » l’instruction du BEQ.

On procède de manière très classique: NOP de l’instruction du jump avec un éditeur héxadécimal, repackage de l’APK modifié et test du POC.

Néanmoins, après consultation de la documentation ARMv7, langage machine de notre plateforme de test (le Nexus 5), on découvre que, loin du confort des architectures Intel/AMD (CISC), il n’y a généralement pas d’instruction NOP à proprement parler dans celles à jeux d’instructions réduits (RISC)

On utilisera donc du « junk-code » afin de NOPper des zones mémoires, traditionnellement:

mov r0, r0       ; 0xe1a00000 little-endian

L’instruction à remplacer ne faisant que 2 octets, nous utiliserons la version 16 bits issue du jeu d’instruction THUMB:

mov r8, r8        ; 0x46C0 little-endian

Ce qui donne dans la vue héxadécimale au format « ASCII » assimilable à du big-endian:

dump_patch

Patch du binaire

 

En updatant la vue, IDA nous confirme que l’on ne s’est pas trompé: seule l’instruction BEQ a été modifiée. Il détecte même notre instruction junk et la remplace par un joli NOP plus lisible !

text_patch

Vue TEXT de la version modifiée

 

On push l’APK modifié via ADB (en vérifiant que l’on autorise bien l’installation d’APK non-vérifiés/non-signés dans les paramètres développeurs), on test le tout et là … Miracle ! plus de dégâts infligés par les ennemis ! En revanche, on peut toujours tomber dans des trous 🙂

 

4) Conclusion

En conclusion, on voit que les applications issues du NDK sont analysables de manière similaire aux applications standards faites avec le SDK: seules les outils sont différents.

De manière générale, toutes les vérifications côté client, incluant celles liées aux achats « in-app », sont contournables simplement en corrompant l’environnement (le téléphone de l’utilisateur) pour qu’il accepte les applications non-signées.

Une possible contre-mesure serait d’utiliser les différentes implémentations de TrustZone ARM pour exécuter l’application dans un environnement de confiance. reposant essentiellement sur des certificats cryptographiques protégés dans le hardware.

En effet, depuis l’apparition des extensions de sécurité TrustZone du jeu d’instruction ARM à partir d’ARMv6KZ, différentes implémentations de « Chain-of-Trust » sont apparues chez les constructeurs de SoC. Qualcomm s’appuie notamment dessus dans sa technologie  « SecureMSM » qui scinde l’environnement en deux: « secure-world » et « unsecure-world ». Les développeurs/éditeurs d’applications peuvent ainsi s’appuyer sur un mécanisme central de sécurité développé par des spécialistes pour protéger des données/codes sensibles d’accès / modifications / exécutions non autorisées.

Offrir une sécurité des moyens de paiement est aussi un arguments marketing phare de cette technologie. La zone EFS du téléphone, évoquées dans l’article sur l’IMEI, contient de nombreuses données sensible liées du téléphone (IMEI, restrictions opérateurs), et est stockée en zone Secure dans les téléphones gérant les instructions TrustZone. Les développeurs d’applications embarquant des fonctionnalités locales payantes telle que l’application analysée dans cet article, devraient notamment l’utiliser pour compliquer grandement l’obtention de fonctionnalités additionnelles par des moyens détournés.

 

GO