Vous êtes sur la page 1sur 14

Rapport d’étude bibliographique

Encadrant : Jean-Yves Marion (équipe CARTE, LORIA)

Analyse morphologique en temps réel pour la


détection de malware
Aurélien Thierry

Rennes, janvier 2011


1 Introduction
La rapide démocratisation de l’informatique a transformé notre rapport à l’ordinateur et aux sys-
tèmes d’information. Les premiers tendent à regrouper une vaste quantité de données personnelles sur
leurs utilisateurs tandis que les systèmes d’information sont devenus une composante opérationnelle
et décisionnelle nécessaire à toute organisation moderne. Ces systèmes apparaissant alors comme des
cibles potentielles, des solutions ont été développées afin d’en protéger la confidentialité, l’intégrité et la
disponibilité.
La première ligne de défense consiste à filtrer les entrées du système (ports usb, interfaces réseau, etc.)
grâce à un pare-feu et à bloquer les menaces potentielles. Si cette défense échoue et qu’une ou plusieurs
parties du système sont compromises, avoir défini et appliqué une politique de sécurité constitue une
sécurité supplémentaire. En effet celle-ci restreint l’accès de chaque utilisateur, et donc des applications
qu’il utilise, aux ressources du système. Ce cloisonnement permet de ralentir et limiter la compromission
du système. Les détecteurs de logiciels malveillants, ou antivirus sont le dernier rempart dans cette
approche. Ils vont permettre la détection et l’élimination des menaces présentes sur la machine, afin de
stopper l’attaque.
La technique de détection la plus largement utilisée jusqu’à présent consiste à comparer un programme
analysé à l’ensemble d’une base de logiciels malveillants connus. Elle dispose de nombreux avantages :
elle est rapide et génère peu de faux positifs, c’est à dire de programmes légitimes faussement classés
comme malveillants. Néammoins, se basant uniquement sur des attaques connues, elle n’est pas adaptée
à la détection de nouvelles souches de programmes malveillants ni à la détection de techniques d’attaque
innovantes. De ce fait, jusqu’à ajout dans la base de signatures, certains nouveaux malware ne seront
pas détectés (faux négatif). Nous nous intéressons ici aux alternatives à cette approche par signature,
et particulièrement à l’analyse morphologique des programmes en utilisant le graphe de flot de contrôle
de celui-ci. Dans le but de détecter ou de stopper l’attaque avant l’infection de la machine, il serait
souhaitable d’avoir conscience de l’arrivée d’un programme malveillant dès sa présence sur une entrée du
système. L’objectif du stage est d’étudier la possibilité d’utiliser un détecteur morphologique au niveau
réseau, puis d’en réaliser une implémentation sur un pare-feu.
Cette bibliographie est organisée comme suit. Une taxonomie des logiciels malveillants selon leur
charge active et leur forme est proposée dans la partie 2. Puis, dans la partie 3 nous étudierons l’approche
par signature et ses limites, et proposerons différentes alternatives dont les analyses comportementales
et morphologique. Les aspects théoriques et pratiques de cette dernière approche seront détaillés dans la
partie 4.

2 Taxonomie des logiciels malveillants


Les travaux de Cohen [1] en 1986 et Adleman [2] en 1988 constituent les fondements de la virolo-
gie. Un virus, au sens de Cohen, peut être formalisé par un mot sur le ruban (zone mémoire) d’une
machine de Turing, qui se duplique ou se modifie sur ce même ruban lorsqu’il est activé. La notion de
réplication automatique est une caractéristique primordiale du virus. Adleman propose une notion plus
générale d’infection informatique. La réplication n’entre pas dans sa définition qui est alors élargie à
tout programme nuisible pour la machine ou l’utilisateur. Ses travaux ont été repris et complété par les
auteurs de [3] qui établissement un cadre formel et les démonstrations des théories d’Adleman. Malgré
un fondement théorique original solide, les approches fondamentales au problème des programmes mal-
veillants sont assez rares. A ma connaissance, moins d’une vingtaines de contributions ont été faites dans
ce domaine en près de 25 ans d’existence (depuis la thèse de Cohen [1]). Dans son livre, Filiol [4] propose
la définition 1 suivante pour une infection informatique ou malware.
Définition 1. Programme simple ou auto-reproducteur, à caractère offensif, s’installant dans un système
d’information, à l’insu du ou des utilisateurs, en vue de porter atteinte à la confidentialité, l’intégrité,
ou la disponibilité de ce système, ou susceptible d’incriminer à tort son possesseur ou l’utilisateur dans
la réalisation d’un crime ou d’un délit.
Dans un premier temps, nous effectuerons une classification des malware selon leur charge virale,
c’est à dire le mode d’action de l’attaque. Ensuite les différentes formes sous lesquelles les logiciels
malveillants se déclinent seront détaillées. Il est à noter que certains aspects de l’attaque virale comme

2
le mode d’infection ne seront pas détaillés ici, mais ils sont abordés dans la littérature, notamment dans
le livre de Szor [5].

2.1 Classification par comportement


Les principaux types de malware peuvent se distinguer par leur mode d’action général. Cette classi-
fication est largement inspirée des travaux de Szor [5] et Filiol [4].

Virus et vers. Le virus est le type de malware le plus ancien et dont l’étude théorique est la plus
fournie. A l’instar d’un virus en biologie, un virus informatique est un programme auto-reproducteur.
Cette propriété est indépendante du but du malware, qui peut être de différentes natures. Les modes de
réplication peuvent différer : le virus peut se copier lui-même, s’intégrer dans un autre fichier exécutable,
ou dans un fichier de données, selon ce qu’il vise. Avec l’avènement d’internet, les vers sont apparus. Ce
sont des virus qui se propagent par le réseau. Par leur mode de propagation rapide, les vers et les virus
ont été responsables des infections de grande envergure de ces dernières années (Conficker en 2008 avec
près de 9 millions de machines infectées par exemple).

Bombe logique. Une bombe logique est un malware qui attend un évènement (date, action...) pour
s’activer. Ces conditions peuvent reposer sur une opportunité (système vulnérable à l’attaque du virus) ou
sur la cible de l’attaque (une configuration réseau particulère). Le programme se comportera de manière
bénigne jusqu’à l’activation de cette “gachette”, et alors il exécutera sa charge virale.

Porte dérobée. Un malware de type porte dérobée ou backdoor est un programme permettant à un
attaquant de contrôler la machine à distance. En général, un port est ouvert assurant les communications
entre la machine infectée et l’attaquant, tandis qu’un programme de type console (shell sous linux par
exemple) exécute les commandes choisies par l’attaquant.

Enregistreur de frappe et logiciels espions Les enregisteurs de frappe, ou keylogger, capturent


les entrées de l’utilisateur (typiquement les frappes au clavier) et les enregistre afin de les transmettre
à un attaquant. Le but de ce genre de malware est souvent de récupérer des informations personnelles
(mots de passe, numéro de carte de crédit...) qu’il pourra ensuite utiliser à des fins frauduleuses. Un
logiciel espion, ou spyware, cherche également à collecter des informations personnelles, comme des mots
de passe ou des informations sur l’activité de la machine, en faisant souvent appel à un enregistreur de
frappe. Ces données peuvent également être utilisée pour générer de la publicité ciblée.

Rootkit. Cette dernière catégorie contient les malware modifiant la phase de démarrage de la machine.
De cette manière, les rootkits se placent entre la machine et le système d’exploitation en modifiant le
noyau. Ils peuvent alors par exemple intercepter les appels à la lecture physique des informations du disque
dur et se rendre indétectable par le système d’exploitation. Leur nature furtive les rend particulièrement
aptes à la récolte d’informations confidentielles.

2.2 Classification par forme du virus


Outre leur mode d’action, les malware se présentent sous différentes formes. Nous distinguerons ici
deux possibilités en opposant les programmes compilés des programmes interprêtés.

Malware compilés. Les malware compilés sont les plus fréquents. Leur analyse est rendue difficile
car ils sont non structurés. La compilation étant spécifique à chaque architecture, ils sont peu ou pas
portables d’un système d’exploitation à un autre.

Malware interprêtés. Les logiciels malveillants écrits en langage interprêté comme les langages de
script (bash sous linux, ou php par exemple) présentent l’avantage d’être portables sur différents envi-
ronnements d’exécution. Contrairement aux programmes compilés, les malware interprêtés contiennent
l’intégralité du code de haut niveau dans la structure de leur fichier. De ce fait ils sont plus simples à

3
analyser automatiquement comme manuellement, et donc plus facilement détectés.

Il est à noter que de nombreux malware combinent les deux approches en ayant par exemple une
partie fixe compilée servant à interprêter une seconde partie qui pourra différer selon les variantes du
programmes. Nous avons vu une partie des malware constituant une menace pour les systèmes ainsi que
deux des formes sous lesquelles ils apparaissent. Certaines des méthodes de détection prennent en compte
cette classification tandis que d’autres seront génériques.

3 Différentes méthodes de détection


Nous ne nous intéresserons ici qu’aux mécanismes de classement d’un fichier cherchant à déterminer
s’il est bénin ou malveillant. En particulier, les aspects d’implémentation du programme d’analyse, le
moment où le fichier est analysé (avant d’être modifié ou exécuté par exemple) comme les techniques
d’optimisations utilisées (pour la recherche de signature par exemple) ne seront pas évoqués. Différents
raisonnements ont été faits dans le domaine de la détection des logiciels malveillants et deux approches
générales s’opposent. La première consiste en la recherche systématique de fichiers connus par avance et
dont une signature a été extraite. Cette signature, comparée au fichier analysé, permet de déterminer
si celui-ci est malveillant ou non. Cette technique est bien rodée et reste la principale méthode utilisée
par les solutions commerciales. La seconde, encore principalement du domaine de la recherche, est une
approche par comportement. Elle s’attache particulièrement à l’observation des actions entreprises par
le programme analysé afin de déterminer s’il présente un risque ou non.

3.1 Analyse formelle du problème de détection


Le problème de détection des malware peut se formaliser bien qu’aucun travail d’abstraction ne fasse
encore l’unanimité. Nous présentons ici un aperçu de différents travaux dans ce domaine.
Les travaux fondateurs de Cohen [1] se basant sur la définition d’un virus comme programme auto-
reproducteur sur une machine de Turing aboutissent à un théorème fondateur. Celui-ci stipule que le
problème de détection est indécidable. C’est à dire qu’il n’existe pas de programme capable sans erreur
de décider si tel programme passé en entrée est malveillant. Et ce quelles que soient les connaissances à
priori sur des virus ou des programmes bénins connus. Par conséquence, aucune approche classique par
signature ne peut être complète.
L’approche d’Adleman [2] aboutit au résultat suivant dans le cas plus général des infections infor-
matiques. La détection de certains malware et de leurs variantes, est un problème Π2 complet, c’est à
dire soit non calculable, soit semi-calculable. Il s’agit de ce fait d’un problème plus difficile que celui de
l’arrêt d’un programme, qui est déjà indécidable.
Le problème de la détection, quelle que soit l’approche théorique réalisée est donc, formellement, très
difficile et souvent non calculable. Les approches pratiques ne peuvent donc pas être parfaites, même si
de nombreuses voies d’amélioration seront étudiées.

3.2 Approche par signature


Une signature est simplement une suite d’assertions sur les octets constituant le code à analyser.
Le processus de création des signatures nécessite une bonne connaissance du code analysé (binaire ou
code interprêté) et de ce fait a recours à des analystes humains. A partir d’un programme connu comme
malveillant, l’analyste tente d’extraire la plus petite suite d’octets caractérisant ce programme et éven-
tuellement ses variantes. Cette signature doit être suffisamment discriminante afin de ne pas classer
à tort un fichier sain comme malveillant. Une technique simple consiste à prendre une suite d’octets
consécutifs dans le fichier. Une base de signatures est remplie avec les signatures des différents malware
connus à détecter. Scanner un fichier revient à rechercher les différentes signatures au sein du fichier,
classant le fichier comme malveillant lorsqu’une correspondance a été trouvée. Dans son livre [5], Szor
établit un ensemble de variantes à cette signature standard dans le but d’atteindre les deux objectifs de
minimisation des faux-positifs et la détection de variantes.

4
Figure 1 – Extrait de programme et sa signature

90 : b0 3f mov $0x3f,%al
92 : 41 inc %ecx
93 : cd 80 int $0x80
95 : 89 fb mov %edi,%ebx
97 : 8d 4f 08 lea 0x8(%edi),%ecx
9a : 8d 57 1a lea 0x1a(%edi),%edx
9d : b0 0b mov $0xb,%al
9f : cd 80 int $0x80
a1 : e8 c3 ff ff ff call 69 <peer0+0x69>
a6 : 2f das
a7 : 62 69 6e bound %ebp,0x6e(%ecx)
aa : 2f das
ab : 73 68 jae 115 <peer0+0x35>
Signature : 08 8d 57 ** b0 ** cd 80 e8 c3

Utilisation de joker. En général, seules peu d’instructions sont déterminantes pour classer un pro-
gramme ; il peut aussi s’agir d’une suite d’instructions disséminées dans le fichier (par exemple toutes les
instructions modifiant la valeur d’un registre spécifique). Afin d’ajouter plus de souplesse au processus
de détection, la notion de joker a été introduite. Toute octet remplit la condition ajoutée par un joker,
et donc chaque signature définit un ensemble de chaînes la respectant. La figure 1 expose une partie
d’un programme en assembleur dont la signature contient des joker. La colonne de doite indique les ins-
tructions en assembleur telles qu’interprêtées lors de l’analyse du programme, celle du milieu les octets
(décomposés en code hexadécimal) constituant le fichier. Les chaînes 08 8d 57 ae b0 14 cd 80 e8 c3
comme 08 8d 57 43 b0 db cd 80 e8 c3, si présentes dans un fichier, conduiront à la détection du vi-
rus. L’utilisation de joker peut être généralisée en utilisant à la place une expression régulière comme
signature, permettant encore plus de souplesse.

Tolérance d’exceptions. Afin de détecter les variantes immédiates de certains virus, certaines signa-
tures autorisent le fichier à différer légèrement de la signature définie. Un nombre de différences est défini.
Par exemple, à partir de la signature à 6 octets 07 3d cd 20 e8 c3, si l’on autorise deux différences, les
chaînes 07 5c fd 20 e8 c3 comme 13 3d cd 20 6b c3 seront également reconnues.

Cette approche par signature présente des avantages en termes de vitesse de détection et induit peu
de faux positifs. De plus elle est indépendante du type de malware analysé. Nous verrons cependant par
la suite les limites de ce système.

3.3 Limites de l’approche par signature


L’analyse de produits du commerce faite dans [6] en 2004 montre l’utilisation massive faite de cette
approche ainsi que son manque de robustesse face à des techniques simples d’obfuscation. L’obfuscation
de code consiste à appliquer des transformations sur le code afin de le rendre suffisamment différent du
code original pour qu’il ne soit plus détecté par un antivirus reconnaissant l’original. Ces transformations
doivent cependant respecter l’exécution du programme initial. En particulier l’ensemble des sorties du
programme original doit être inclus dans celui du programme obfusqué.

Insertion de code inutile. Cela consiste à intercaler simplement dans le fichier à obfusquer des ins-
tructions supplémentaires sans modifier le comportement du programme initial. Ces ajouts peuvent être
faits dans un code de haut niveau comme dans un programme binaire, seule la syntaxe des instructions
ajoutées changeant.

5
Figure 2 – Évaluation de l’approche par signatures, exemples d’obfuscation

Réorganisation du code. Cette opération revient à modifier l’ordre physique du code sans modifier
son ordre d’exécution. En assembleur, l’ajout d’instructions de saut inconditionnel le permet.

Encapsulation du code. Il s’agit d’‘écrire les instructions sous une forme ne dévoilant pas leur sens.
Elle peut être réalisée à l’aide d’un encodage particulier (en hexadécimal par exemple), de compression
(au format ZIP par exemple), ou de chiffrement. Le cas des packers permettant le chiffrement du pro-
gramme n’est pas traité par les auteurs de [6]. Ces transformations modifient grandement la syntaxe
du programme sans en modifier les instructions qui seront exécutées au final. Souvent une partie du
programme sera dédiée à l’interprétation de la partie obfusquée.

Les auteurs ont ensuite généré, à partir de 8 virus connus, entre 31 et 5 592 variantes. Les antivirus
testés sont Norton Antivirus, Sophos Antivirus, et McAfee Virus Scan. Les versions évaluées sont les
dernières disponibles (Juillet 2004), avec leurs signatures de virus mises à jour. Leurs résultats sur
chacune des techniques d’obfuscation sont les suivants.
Les résultats obtenus présentés dans la figure 2 donnent le pourcentage de virus non détectés après
obfuscation pour chaque antivirus. Il est à noter qu’avec les deux méthodes simples d’obfuscation que
sont l’insertion de code et la réorganisation, le pourcentage de faux négatifs est très élevé. Le cas de la
réorganisation est particulièrement intéressant. En effet, les taux de faux négatif sont de 67%, 88%, et
58% alors que la transformation effectuée ne modifie ni les instructions ni leur ordre réel d’exécution.
Il apparaît donc clair que la méthode de détection par signature simple telle qu’elle est largement
pratiquée encore aujourd’hui n’est pas suffisante. Cet effet est d’autant plus gênant que ces modifications
du code peuvent être faites de manière automatique par un virus cherchant à se rendre furtif aux yeux
d’un antivirus. Le temps nécessaire à la génération et la diffusion de la signature de chaque nouvelle souche
laisse au virus une fenêtre temporelle suffisamment large pour se répandre. Le papier [6] datant de 2004
justifie la nécessité de développer de nouvelles méthodes de détection, et explique donc l’abondance de
recherches en ce sens ces dernières années.

3.4 Approche comportementale


Les méthodes de détection dites comportementales cherchent à caractériser les actions normales pour
un type de programme étudié. Deux approches sont donc possible. La première tente de modéliser
les comportements malveillants et mesure l’écart entre le programme évalué et les modèles connus. La
seconde au contraire consitste à définir un ensemble de profils correspondant à des programmes légitimes

6
(client mail, navigateur internet, etc.) à l’aide d’outils statistiques et détermine l’écart avec le programme
testé. Les modèles basés sur des profils d’applications légitimes sont complexes à mettre en oeuvre, par
la grande diversité d’applications de différentes natures sur un système. Ils sont de fait très sensibles
aux faux positifs et dépendant de l’environnement sur lequel ils sont déployés. C’est pour ces raisons
que nous ne détaillerons ici que la première approche. Néammoins, étant basée sur le comportement des
malware connus, elle n’est pas adaptée pour détecter des logiciels malveillants utilisant des techniques
innovantes. Le risque ici concerne le nombre possiblement grand de faux négatifs.
Les auteurs de [7] se sont employés à extraire la structure d’un détecteur comportemental, ainsi
que les différentes possibilités explorées jusqu’ici. Trois tâches consécutives sont nécessaires. La première
consiste à récupérer une base de malware et à en extraire les instructions ou les actions qu’ils réalisent.
La seconde transforme ces données en données intermédiaires, plus abstraites. Le dernier composant
contient l’algorithme de détection indiquant si tel programme analysé est malveillant.

3.4.1 Collecter des malware


La collection de malware utilisée peut provenir de deux sources. Ils peuvent être récupérés sur les
ordinateurs personnels de clients (un antivirus peut avoir une fonction d’envoi d’un fichier suspect pour
analyse), ou provenir de pots de miel. Un pot de miel ou honeypot est un système volontairement vulné-
rable, dans le but de subir des attaques susceptibles d’être par la suite analysées afin de mieux connaître
l’attaquant (ici les malware).

3.4.2 Extraction et analyse des actions d’un malware


Pour extraire les actions du programme étudié, il est possible de n’effectuer qu’une analyse statique,
mais on cherche souvent à obtenir plus d’informations. Dans la plupart des cas, le programme est exécuté,
soit sur une machine physique en enregistrant les appels système, soit sur une machine virtuelle simulant
un environnement réel. Ce deuxième cas est celui rencontré le plus souvent car il permet l’analyse du
malware sans risquer de compromettre la machine physique. L’analyse n’est pas toujours réaliste car
certains malware parviennent à détecter qu’ils sont exécutés sur une machine virtuelle et se comportent
alors différemment, rendant leur analyse inefficace [8] .
Les actions sont rendues moins spécifiques en les regroupant par catégories. Certaines opérations sont
plus sensibles que d’autres. On peut citer l’accès à des secteurs système (partition ou démarrage), ou des
références vers une zone mémoire réservée au noyau.

3.4.3 Algorithme de reconnaissance des malware


Le composant réalisant la détection peut implémenter différents alogorithmes d’évaluation de la me-
nace. Deux de ces alogorithmes sont présentés ci-dessous.

Algorithme par seuil. Une stratégie simple consiste à extraire des malware récoltés les actions les
plus susceptibles d’être malveillantes. Une coefficient est affecté à chaque action, et un score de mal-
veillance peut être calculé pour chaque programme à analyser en effectuant la somme des coefficients
associés à chaque action qu’il entreprend. Si ce score dépasse un certain seuil, le programme sera détecté
comme malveillant. Une étude comparative de logiciels légitimes peut être bénéfique pour éviter un taux
trop élevé de faux positifs car beaucoup d’actions sont fréquentes pour les malware comme pour les
programmes légitimes et donc ne doivent pas rentrer excessivement en ligne de compte.

Algorithme par automate. Cette approche s’intéresse plus à des séquences d’actions. Des profils
malveillants vont être établis et un langage leur est associé. Pour permettre la détection de ces profils,
des automates sont utilisés. Un programme sera considéré comme malveillant si, partant d’un état initial
d’un des automates, la séquence des actions qu’il entreprend le fait arriver dans un état final de cet
automate. Ce méthode tend à considérer un malware comme effectuant une suite d’actions, malveillantes
ou non, qui, effectuées séquentiellement, ont un comportement malveillant.

7
3.4.4 Résultats
Certains travaux montrent l’efficacité de l’approche comportementale. Pour la détection d’une ca-
tégorie particulière de logiels espions par exemple [9] . Les auteurs se sont intéressés aux programmes
s’intégrant dans un navigateur internet (Internet Explorer sous Windows) et offrant des fonctionnalités
supplémentaires (barres d’outils intégrés, etc.). Certains de ces programmes tiers sont en fait des logiciels
destinés à collecter des informations de navigation des utilisateurs pour leur proposer de la publicité ci-
blée. Ils ont caractérisé un logiciel espion par l’observation des sites visités et de leur contenu via Internet
Explorer, suivie de l’appel à des API Windows destinées à enregistrer (dans un fichier) ou envoyer (à un
serveur distant) ces informations. Afin de détecter ces comportements, ils ont combiné analyse statique
et analyse dynamique. Dans ce cas, et sur 51 fichiers étudiés (dont 33 sont malveillants), leur approche
a un taux de détection optimal (100% des 33 malware sont détectés) et un taux de faux positifs faible
(deux logiciels bénins sont classés comme malveillants).
Il apparaît ici que l’analyse des programmes en se basant sur leur comportement plutôt que sur des
signatures est prometteuse et donne déjà des résultats probants dans certains cas. Dans le paragraphe
suivant, nous aborderons une approche originale, par analyse morphologique des malware. Cette approche
repose sur un nouveau type de signatures, par le graphe de flot de contrôle. Ce graphe est construit à
partir des instructions exécutées par le malware.

4 Analyse morphologique : de la création du CFG à sa détection


L’analyse morphologique des virus se base sur la reconnaissance de graphes de flot de contrôle (control
flow graph, ou CFG) de virus connus. En ce sens il s’agit d’une approche par signatures. Dans cette partie,
nous allons définir le champ d’application de cette étude, les graphes de de contrôle de flots, les aspects
théoriques et pratiques de leur détection, et discuter les résultats déjà obtenus avec cette méthode. Nous
verrons alors en quoi cette approche se place entre une approche standard par signature et une approche
comportementale.

4.1 Cadre de l’étude


L’analyse se réduira ici aux programmes non structurés tels les exécutables en code assembleur x86.
La principale difficulté dans leur analyse réside dans l’existence d’instructions de saut dynamiques (ins-
truction jmp par exemple). On supposera les programmes non obfusqués à l’aide d’un packer, c’est à
dire que leur code (en assembleur) est accessible tel qu’il sera effectivement exécuté. Un graphe de flot
de contrôle est un graphe orienté dans lequel les nœuds sont les adresses des instructions et les arcs
représentent les différents chemins que l’exécution peut suivre.

4.2 Construction du CFG


L’étude faite dans [10] introduit un langage modélisant l’assembleur x86. Il différentie principalement
deux types d’instructions. D’une part les instructions de flot ont une influence sur le graphe de flot et
sont au nombre de 4 (dans un jeu d’instructions x86 réel, il y a un grand nombre de variante au saut jcc
mais elles ont toutes la même influence sur le CFG). D’autre part les instructions purement séquentielles
n’ont pas d’influence sur le graphe de flots. De ce fait leur présentation ne cherche pas à être exhaustive.
Dans ce modèle, un programme est simplement une suite d’instructions.

Adresses N
Offset Z
Registres R
Expressions Z|N|R|[N]|[R]
Instructions de flot If ::= jmp E | call | ret| jcc Z
Instructions séquentielles Id ::= mov E E | comp E E | ...
Programmes P ::= Id | If | P; P
Chaque instruction se caractérise par son nombre de successeurs.
– Les instructions séquentielles ont unique successeur, il s’agit de l’adresse de l’instruction suivante.
– L’instruction de saut inconditionnel jmp a un unique successeur : l’adresse de destination du saut.

8
– L’instruction de saut conditionnel jcc a deux successeurs : l’adresse du saut dans le cas où la
condition est vérifiée, et celle de l’instruction suivante dans le cas contraire.
– L’instruction call a deux successeurs : l’adresse de la fonction à appeler et l’adresse de retour (celle
de l’instruction qui suit).
– L’instruction end n’a pas de succeseur. C’est ici que le programme (ou la fonction) s’arrête.

La figure 3(a) présente ces cinq cas et leurs influences sur le CFG selon l’instruction in à la ligne
n. Lorsqu’un paramêtre e d’une instruction définit la cible d’un saut dynamique, il peut être évalué à
l’aide d’une méthode d’analyse statique à k = |e| afin de connaître si possible la ligne (et l’instruction
correspondante) cible du saut. Dans de nombreux cas, une telle analyse n’est pas faite ou n’est pas
probante, on note alors |e| =⊥ et on considère la cible du saut comme inconnue.

inst
inst

in : instruction Concaténation
séquentielle inst
des instructions
n+1 séquentielles inst

A
jmp
A
in : jmp e
|e| = k
k
jmp
Réalignement
call des sauts A
inconditionnels
in : call e A
|e| = k
n+1 k

jcc
jcc
in : jcc x Fusion des
jcc
|e| = k sauts
jcc
n+1 k conditionnels
jcc A B
B A
in : end end

(a) Construction du CFG (b) Techniques de normalisation

Figure 3 – Construction et normalisation du graphe de flot de contrôle

4.2.1 Normalisation : protection contre l’obfuscation


Une fois un premier graphe réalisé, il est utile d’en normaliser le résultat afin que les suites d’ins-
tructions ayant le même comportement aient la même représentation. Ces transformations rendent le
CFG moins dépendant de l’agencement des instructions dans le code, et donc fournissent une protection
contre des techniques habituelles d’obfuscation. Les opérations effectuées sont les suivantes.
– Concaténer les instructions séquentielles consécutives en un seul bloc.
– Réaligner le code en supprimant les sauts inconditionnels.
– Fusionner des sauts conditionnels consécutifs.
La figure 3(b) présente la réécriture du graphe effectuée sur le CFG. La figure 4 présente un exemple
de programme en assembleur (à gauche), sa représentation initiale en graphe de flot de contrôle (au
centre), et le CFG réduit (à droite). Les seules instructions ayant une influence sur le graphe de flot sont
aux lignes 1, 6, 7 et 9. L’instruction jne est une variante du saut conditionnel jcc. La seule réduction
possible consiste à regrouper les instructions séquentielles 3, 4 et 5.

9
Figure 4 – Un programme avec son CFG initial, et le CFG réduit

0 0

1 1
0 : cmp eax 0
1 : jne +7 2 2
2 : mov ecx eax
3 : dec ecx 3
4 : mul eax ecx
8 8
5 : cmp ecx 1
4 3, 4, 5
6 : jne -3
7 : jmp +2 5
8 : inc ecx
9 : ret 6 6

7 7

9 9

Figure 5 – Exemple de code avec instruction inutile (ligne 11)

1 assign x := x mod 3
2 assign x := 10(x+1)
3 cgoto(x)
10 stop
11 goto 2
20 stop
30 stop

4.2.2 Raffinement par analyse statique


Le graphe de flot peut être dérivé directement du code binaire dans des cas simples. Cela dit, très
souvent, et d’autant plus si des méthodes d’obfuscation sont employées, il devient nécessaire d’avoir une
connaissance à priori sur le flot d’exécution du code. Pour cela, les approches effectuées dans [10] et [11]
préconisent le recours à l’analyse statique. Le but de cette analyse est d’identifier les instructions qui ne
sont pas accessibles, de simplifier certaines suites d’instructions dans le but d’évaluer les conditions de
transition de flot de contrôle pour supprimer les branches inutiles, et au final d’approximer au mieux le
comportement réel du code avant de construire le graphe de flot.

Une approche classique s’intéresse aux valeurs des variables tout au long du programme. Soit le pro-
gramme de la figure 5, une approche par valeurs ne s’intéressant qu’aux bornes considèrera l’instruction
de la ligne 11 comme exécutable et le CFG en prendra compte car après la ligne 1, x ∈ [0, 2] et après
la ligne 2, x ∈ [10, 30]. Cela dit, compte tenu des valeurs possibles de x, cette instruction n’est pas
atteignable. L’approche faite dans [12] est par raffinement successif. Dans une première approximation,
les valeurs possibles de x ne sont pas connues, et donc l’analyse n’est pas suffisante pour déterminer si
l’instruction de la ligne 11 est utile ou non. Les valeurs de x seront donc recherchées, en remontant à

10
partir de l’instruction de la ligne 11 via les possibles instructions qui la précédent. De cette manière, il
sera déterminé que x ∈ {10, 20, 30} et donc que cette instruction est inutile. Le graphe de flot de contrôle
est donc simplifié.
Bien que ce ne soit pas l’objet de cette étude, il est ainsi à noter que la précision du graphe de flot de
contrôle dépend grandement de la qualité de l’analyse statique effectuée au préalable. Nous chercherons
par la suite à comparer un programme analysé à l’ensemble de la base de malware connus, dont le CFG
a été extrait.

4.3 Reconnaissance d’un CFG


Afin de construire une base de signatures par graphe de flot de contrôle et de pouvoir y reconnaître
un malware analysé, les auteurs de [10] proposent d’utiliser des automates d’arbres. Les CFG seront donc
au préalable tranformés en arbres, et un automate d’arbres sera utilisé pour l’ensemble de la base de
signatures. Cet automate d’arbres permettra la reconnaissance des malware.

4.3.1 Des CFG aux arbres


Un chemin est un mot sur {1, 2}∗ , ε est le chemin vide. Soit l’ordre < défini sur {1, 2}∗ comme suit
pour ρ, τ ∈ {1, 2}∗ et i ∈ {1, 2} :
– ρ1 < ρ2
– ρ < ρi
– ρ < τ ⇒ ρρ0 < τ τ 0 .
Un domaine d’arbre est un ensemble d ⊂ {1, 2}∗ tel que, pour tout ρ ∈ {1, 2}∗ et i ∈ {1, 2}, on ait
ρi ∈ d ⇒ ρ ∈ d.
Soit l’ensemble de symboles F = {inst, jmp, call, jcc, ret} ∪ {1, 2}∗ . On considère un arbre sur cet
ensemble de symboles comme étant une paire t = (d(t), t̂) avec d(t) un domaine d’arbre et t̂ une fonction
de d(t) dans F. Il est à noter qu’avec cette définition, un arbre a des nœuds de deux types. Un nœud
peut être une instruction prise en compte par le CFG ou un chemin, ou pointeur, vers un autre nœud de
l’arbre.
˙ l’ensemble des nœuds internes de l’arbre t. d(t)
Soit d(t) ˙ = {ρ ∈ d(t), t̂(ρ) ∈ {inst, jmp, call, jcc, ret}}.
Il s’agit des nœuds dont le label est une instruction.
˙
Définition 2. Un arbre t est bien formé si quels que soient les chemins ρ, τ ∈ d(t), (t̂(τ ) = ρ) ⇒ (ρ ∈ d(t)
et ρ ≤ τ ).

La définition 2 impose, pour un arbre bien formé, que tout chemin pointe vers un nœud interne, et
interdit que le nœud vers lequel il pointe soit un précédesseur, empêchant les définitions récursives. Il
est à noter que dans les cas pratiques étudiés dans [10], tous les graphes de flot de contrôle peuvent être
réécrits sous forme d’un arbre bien formé.
Une fois cette transformation d’un CFG à un arbre effectué, il va être possible de définir un automate
d’arbres reconnaissant le langage constitué des malware connus, et donc de mener à leur détection.

4.3.2 Automates d’arbres


La théorie des automates d’arbres est largement abordée dans [13] et les résultats suivants en sont
extraits.
Définition 3. Un automate d’arbres fini est un tuple A = (Q, F, Qf , ∆), où Q est un ensemble fini
d’états, F un ensemble de symboles, Qf ⊂ Q un ensemble d’états finaux et ∆ un ensemble fini de règles
de transition de la forme a(q1 ...qi ) → q avec a ∈ F ayant i successeurs, et q, q1 , ..., qi ∈ Q.
Une transformation de l’arbre est faite selon l’automate. Dans un premier temps chaque feuille est
transformée en un état, selon les transitions définies, puis ces transitions sont utilisées pour remonter
dans l’arbre au fur et à mesure des transformations effectuées. Si à l’issue de ces transformations l’arbre
est dans un état final de l’automate, on dit que l’automate reconnaît l’arbre. Pour un arbre t ayant
|t| nœuds, la transformation peut être effectuée avec une complexité temporelle linéaire, soit O(|t|). La
figure 6 présente un exemple d’automate d’arbres en définissant le tuple (Q, F, Qf , ∆). Cet automate ne
reconnaît qu’un arbre bien fondé particulier. Celui-ci est présenté dans la figure 7 où il est dans son état

11
(1) 21 → q1
Q = {q1 , q2 , q3 , q4 , q5 } (2) ret → q2
Qf = {q5 } (3) jmp(q1 ) → q3
F = {inst, jmp, ret} ∪{1, 2}∗ (4) inst(q2 ) → q4
(a) États et symboles de l’automate (5) inst(q3 , q4 ) → q5
(b) Règles de transition

Figure 6 – Automate d’arbre reconnaissant un unique arbre

Figure 7 – Arbre bien formé et transformations pour sa reconnaissance par l’automate

inst inst inst inst inst


q5
(1) (2) (3) (4) (5)
jmp inst jmp inst jmp inst q3 inst q3 q4

21 ret q1 ret q1 q2 q2

initial puis subit les transformations définies par les transitions de l’automate afin de le réduire en un
seul état, état final de l’automate.
Les automates d’abres ont des propriétés utiles pour notre analyse. Il est possible de construire un
automate qui reconnaît exactement tous les arbres d’un ensemble fini d’arbres. Cette construction ayant
une complexité temporelle linéaire, en O(n) où n est la somme du nombre de nœuds de chaque arbre
de l’ensemble. Il est également possible de fusionner deux automates A et A0 pour obtenir un automate
reconnaissant exactement l’union des arbres reconnus par chacun des deux automates originaux. Ceci
peut être effectué avec une complexité de O(|A0 |) où |A0 | est le nombre de règles de transition de A0 .
Le résultat le plus intéressant est le théorème 1 sur les automates d’arbres minimaux. Pour chaque
langage reconnaissable d’arbres, il existe un unique automate minimal en nombre d’états reconnaissant
ce langage. Cet automate est donc la meilleure représentation de la base des malware, donnés sous forme
d’arbres bien formés.

Théorème 1. Pour tout automate d’arbres A qui reconnaît un langage d’arbres L, on peut déterminer
en temps quadratique, soit O(|A|2 ), un automate d’arbres Amin qui est minimal et reconnaît L.
La possiblité de déterminer cet automate minimal en un temps raisonnable assure la faisabilité de
cette approche. Reste à voir les résultats obtenus par cette méthode de détection avec les implémentations
réalisées.

4.4 Implémentations et résultats


Les résultats de l’approche présentée précédemment [10] sont abordés dans cette partie. Un ensemble
de 10156 malware ainsi que 2653 programmes légitimes récoltés juste après installation de Windows
Vista constituent les références testées ici. Afin de valider l’approche par CFG, le nombre de faux positifs
sera étudié ici. On remarque que 5% des programmes malveillants analysés ne sont pas des exécutables
valides et donc ne seront pas étudiés. La part de malware dont le graphe de flot de contrôle contient plus
de 15 nœuds atteint 65%. Les 30% restant ont entre 1 et 15 nœuds dans leur CFG. Prendre en compte
ces malware risque d’augmenter le nombre de faux positifs car leur graphe ne sera pas caractéristique
du malware et pourra être retrouvé dans un programme sain. Le nombre de nœuds minimal requis pour
prendre en compte un malware dans la base utilisée dans cette analyse est un facteur important de la
variation des faux positifs.
Á partir de chaque graphe de flot de contrôle obtenu pour un malware, un arbre a été généré comme
défini précédemment. La construction d’un automate d’arbres reconnaissant la base de données des
malware a pu être effectuée en 25 minutes tandis que la minimisation de cet automate a pris plusieurs

12
heures. La figure 8 présente les résultats obtenus. Le nombre de faux positifs et de malware non détectés

Taux de faux positifs et faux négatifs


Borne inférieure Faux positifs Faux négatifs 120.00%

1 100.00% 4.80%
2 83.78% 5.43% 100.00%
Faux négatifs
3 76.82% 16.43% Faux positifs
4 76.77% 16.66% 80.00%

5 57.98% 20.01%
60.00%
6 34.84% 21.50%
7 20.57% 23.34%
40.00%
9 12.06% 24.43%
10 2.17% 26.47%
20.00%
11 2.04% 27.78%
12 1.60% 29.35% 0.00%

13 0.71% 30.74% 1 2 3 4 5 6 7 9 10 11 12 13 15

Borne inférieure au nombre de noeuds


15 0.09% 36.52%

Figure 8 – Résultat : faux positifs et faux négatifs

est donné en fonction de la borne inférieur du nombre de nœuds présents dans le CFG pour prendre en
compte le malware. Comme attendu, le nombre de faux positifs diminue lorsqu’on augmente la borne
inférieure de la taille du CFG. Le nombre encore important de malware non détectés (faux négatifs)
pourra être amélioré en effectuant une meilleure analyse statique avant la construction du CFG afin que
celui-ci soit plus précis.
Les résultats de l’analyse morphologique vis-à-vis de la détection de variantes obfusquées d’un vi-
rus sont donnés dans [11]. Sur un large échantillon de programmes à analyser contenant 115 malware
connus, des signatures simples et par graphe de flot de contrôle ont été extraites. De nombreuses variantes
de ces souches sont présentes dans la base de programmes à analyser. Les programmes classés comme
malveillants par analyse morphologique sont dans 96,5% des cas confirmés par l’antivirus par signature
simple. Des 3.5% (soit 1971 programmes) restant, un échantillon de 50 a été sélectionné au hasard, puis
analysé à la main. Tous ces programmes se sont révêlés être des malware, variantes de ceux introduits
dans la base de signature. Il n’y a pas de faux négatifs avec cette approche dans le cas étudié [11].

Les quelques implémentations de détecteurs de malware par analyse morphologique donnent des
résultats convaincants. En effet elles génèrent peu de faux positifs, et peu de faux négatifs même dans le
cas de programmes obfusqués.

5 Conclusion
Dans ce rapport, nous avons défini et classé les malware dans différentes catégories. Il a ensuite été
question de la détection de ces programmes malveillants. Un état de l’art a été effectué et a exposé les
faiblesses de l’approche classique par signatures simples. Entre cette approche classique et une approche
purement comportementale, la méthode de détection par analyse morphologique semble prometteuse.
Celle-ci atteint en effet le double objectif de limitation du nombre de faux positifs tout en étant capable
de détecter différentes variantes d’un programme malveillant.
Le stage consistera à continuer les travaux réalisés au LORIA [10] afin d’implémenter le détecteur
morphologique déjà développé au niveau réseau, sur un pare-feu. Un effort d’optimisation, tant sur
les modèles choisis (réprésentation sous forme d’un graphe de flot de contrôle, utilisation d’automates
d’arbres, etc.) que sur les algorithmes utilisés, sera effectué afin que les performances de ce détecteur
soient satisfaisantes.

13
Références
[1] F. Cohen. Computer Viruses. PhD thesis, University of Southern California, 1986.
[2] L. Adleman. An abstract theory of computer viruses. In Advances in Cryptology, volume 403 of
Lecture Notes in Computer Science, pages 354–374. Springer, 1988.
[3] G. Bonfante, M. Kaczmarek, and J.Y. Marion. On abstract computer virology from a recursion
theoretic perspective. Journal in computer virology, 1(3) :45–54, 2006.
[4] É. Filiol. Les virus informatiques : théorie, pratique et applications. Springer, 2004.
[5] P. Szor. The Art of Computer Virus Defense and Research. Symantec Press, 2005.
[6] Mihai Christodorescu and Somesh Jha. Testing malware detectors. Proceedings of the International
Symposium on Software Testing and Analysis (ISSTA), July 2004.
[7] Grégoire Jacob, Hervé Debar, and Eric Filiol. Behavioral detection of malware : from a survey
towards an established taxonomy. Journal in Computer Virology, 4 :251–266, 2008.
[8] P. Ferrie. Attacks on more virtual machine emulators. Symantec Technology Exchange, 2007.
[9] E. Kirda, C. Kruegel, G. Banks, G. Vigna, and R. Kemmerer. Behavior-based spyware detection.
In Usenix Security Symposium, 2006.
[10] Guillaume Bonfante, Matthieu Kaczmarek, and Jean-Yves Marion. Architecture of a Morphological
Malware Detector. Journal in Computer Virology, 5 :263–270, 2009.
[11] D. Bruschi, L. Martignoni, and M. Monga. Detecting self-mutating malware using control-flow graph
matching. Detection of Intrusions and Malware & Vulnerability Assessment, pages 129–143, 2006.
[12] Sébastien Bardin and Philippe Herrmann and Franck Védrine. Refinement-based CFG Reconstruc-
tion from Unstructured Programs. Technical report, CEA, 2010.
[13] H. Comon, M. Dauchet, R. Gilleron, F. Jacquemard, D. Lugiez, C. Löding, S. Tison, and M. Tom-
masi. Tree automata techniques and applications. tata.gforge.inria.fr, 2008.

14

Vous aimerez peut-être aussi