Après avoir vu l'analyse statique des applications mobiles en début de mois, penchons nous maintenant sur l’analyse dynamique, une méthode utilisée pour analyser le fonctionnement d’une partie d’un programme en pleine exécution. Ce type d’étude fournit des informations précieuses sur le paquet exécuté car nous pouvons observer les interactions qu’il génère au sein de son environnement d’exécution. Il convient dans ce contexte de délimiter deux zones d’exécution : l'environnement proche : zone dalvik [nom couramment donné à l'espace virtuel où une application mobile s'exécute au sein d'Android] et élément interne de l'application. Et aussi l’environnement distant (environnement Android hors zone dalvik et les parties tierces).
Dans le cas de l’étude d’une application mobile, ce type d’analyse permet de contourner les problèmes d’opacifications car il permet d’installer des mécanismes d’écoutes sur des éléments précis de l’application. L’analyste pourra récupérer un ensemble d’informations transitant dans le programme durant son fonctionnement. Toutefois, la difficulté résidera dans la localisation précise de la donnée ciblée. Comme nous le verrons plus tard, certains éléments peuvent être dupliqués ou opacifiés au sein du programme, et dans ce cadre il faudra déterminer le moment où celui-ci est décrypté en clair.
Voici une liste non exhaustive des types d’informations récoltables via cette méthode :
- Les transferts de données sur le réseau (requête http) ;
- Les écritures et les lectures de fichiers ;
- Les appels de fonctions ;
- Les arguments et les retours de fonctions.
Le mode opératoire de l’analyse
Pour la mettre en œuvre, un chercheur ou un analyste définira le type et la localisation précise de la cible. Par exemple, nous pourrons cibler les retours R d’une méthode M liée aux transferts de données issues d’un formulaire F contenu dans une activité A. Généralement effectué via un composant de type Array ou List ou Map, si l’application est écrite en Kotlin, l’analyste pourra procéder à l’écoute de ce composant en définissant une fonction d’écoute (hook) qui permettra de faire remonter la structure et le contenu dudit composant. De façon usuelle, nous stockons ensuite ces informations dans une base de données en vue d’en faire une analyse approfondie par la suite.
Contrairement à ce que nous pourrions penser, une écoute localisée ne suffit pas. Les analystes procèdent généralement à une série d’écoutes couvrant un champ d’investigation plus ou moins large. Cette multiplication permet de collecter et de comparer certaines valeurs entre elles afin d’avoir une vue précise d’un processus.
Reprenons notre précédent exemple pour illustrer ces derniers propos. Dans le cas où la méthode gère des informations sensibles (moyen de paiement, données d’authentification....) dans un but malicieux, un analyste averti ne cherchera pas à confirmer un résultat connu d’avance. Celui-ci essaiera plutôt d’en analyser le fonctionnement car le système malveillant fera tout pour rendre cette activité indétectable via divers moyens (cryptage, anonymisation, découpage...). Pour cette raison, il sera intéressant pour le praticien de procéder à un relevé précis de l’état des valeurs au fur et à mesure de leur traitement par le système afin d’identifier le mode opératoire mis en place dans l’application.
Nous comprenons alors que l’analyse dynamique présente une dimension exploratoire et nécessite une définition précise de ses objectifs.
Les biais de l’analyse dynamique
Ce type d’analyse, bien que robuste aux tentatives d'opacification, nécessite toutefois une définition fine de ses objectifs. Ce point est crucial car c’est ce qui explique qu’il est difficile de l’automatiser totalement. Bien que ce dernier point doit-être nuancé comme nous le verrons dans la partie suivante, il explique que son utilisation soit plus délicate.
Mais ce n’est pas la seule cause de cet état de fait. L'analyse dynamique nécessite aussi un degré d’expertise non négligeable de la part du praticien. Il est facile d’obtenir des données mais délicat de vérifier la provenance de celles-ci. Chaque donnée extraite par ce procédé doit être localisable dans les appels via une comparaison avec le graphe d’appel (callgraph). En cas de repacking (modification ou altération de l’origine d’un appel), certaines librairies systèmes tout à fait légitime, peuvent subir une modification afin d’y ajouter ou d'y altérer une fonctionnalité.
Arrêtons-nous deux minutes sur ce dernier point. Prenons l’exemple de la librairie prenant en charge les composants de type chaînes de caractères : java.Lang.String. Souvent utilisée dans les applications, celle-ci peut être détournée afin de doter le composant “String” d’un comportement, malicieux ou non, non prévu à son origine. Bien que cette librairie soit appelée de façon légitime par l’application, via une surcharge il est tout à fait possible pour un développeur d’en étendre les fonctionnalités. Ce revers de la médaille prend son origine dans le type de composant utilisé. Un string est “Objet” et donc doté d’une série d’attributs et de méthodes qui permettent d’assurer son fonctionnement. Arrivé là, si nous limitons l’analyse au relevé de la valeur du composant nous passons à côté d’une information cruciale, l’altération de son appel. Nous comprenons alors aisément que la mise en place d’un tel outil demandera à l’analyste non seulement de comprendre et de connaître les principaux types de composants présents dans l’application et le système d’exploitation mais aussi les mécanismes d’altérations possibles.
Un autre biais de ce type d’analyse réside dans la circonscription de son champ d’investigation. De fait, ce type d’analyse nécessitant l’exécution de l'application, il est délicat voire impossible de procéder à une écoute générale de tous les composants présents dans celle-ci.
A titre d’exemple, une application d’apprentissage des langues que j’utilise quotidiennement présente pas moins de 1528 classes et plus de 35000 méthodes. Une écoute globale serait instable et représenterait un flux de données conséquent. Il faut donc en restreindre le scope. L’analyse devra donc identifier au préalable les zones sur lesquelles elle doit se concentrer.
Enfin, les temps de traitement de ce type d’analyse sont longs et coûteux. Souvent, ceux-ci doivent être réitéré car ils sont susceptibles de s’arrêter brutalement notamment quand le praticien a recours à un générateur d’interactions comme « MonkeyRunner » présent dans la suite ADB de Google. Ce dernier point conforte l’idée d’un ciblage préalable à l'étude.
Le rôle de l’apprentissage automatique
Précédemment, nous avons mentionné le fait que ce type d’analyse était délicat à automatiser mais toutefois pas impossible. Si l’analyste procède avec une approche exploratoire (il n’a pas encore d’idée très précise de sa cible), alors l’automatisation sera complexe et l’analyse risque de passer à côté d’informations cruciales.
Toutefois, dans le cas où le praticien possède une idée précise de la cible, et ce quel que soit l’application analysée, alors une automatisation est possible. Ce cas de figure est notamment vrai lors de la conception d’un modèle de détection de programmes malveillants. Si le concepteur se focalise sur une famille ou une sous-famille de programme malveillants (botnet, rootkit...) alors il lui sera possible de procéder à une détection préalable de cette architecture dans l’application et d’en faire ensuite une analyse approfondie. Toutefois, pour faire cela l’analyste aura recours à l’apprentissage automatique via la création d’une base de données comportant des indicateurs pertinents vis-à-vis du type de programme malveillant audité afin d'entraîner l’algorithme qui procédera aux analyses. Les algorithmes sollicités sont variés (calcul des centres moyens, machine à support de vecteurs, arbre de décision...) tout comme les indicateurs (nature ou distribution des arguments, série d’appels, origine des appels...). Cette approche est notamment utilisée dans le cas d’une analyse hybride en combinant une analyse statique et dynamique.
Ce champ d’étude bien que récent dans le monde universitaire et entrepreneurial, présente des résultats probants. Toutefois, il n’existe pas de solution parfaite et les systèmes de détection sont régulièrement remis en cause et améliorés.
Nous voyons que l’analyse dynamique est plus robuste mais aussi plus délicate à mettre en œuvre. Celle-ci est automatisable dans des cas d’études précis mais nécessite alors un plus grand champ de connaissance et de savoir-faire pour être mise en œuvre. Toutefois, ce type d’analyse peut être optimisé dans certains cas en l’associant avec une analyse statique. Nous approfondirons ce point dans notre prochain article.
Commentaire