Format APK (Android PacKage)
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),
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)
SHA-1 des lignes du MANIFEST.MF correspondantes)
ressources.arsc
fichiers de ressources précompilées(images, fichiers XML, …)
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
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
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.
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.
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
NET ouverte dans Reflector ou ILSpy
Un parcours sommaire du code dans Java Decompiler ne révèlera cependant aucune zone intéressante
.
.
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
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
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
.
.
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
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
Vue « Graph » d’IDA
L‘instruction BEQ, « Branch if equal » est un saut conditionnel basé sur l’état des FLAGS settés par l’instruction CMP juste avant.
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
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
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
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.
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.