<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Segmentation fault &#187; Windows</title>
	<atom:link href="http://www.segmentationfault.fr/tag/windows/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.segmentationfault.fr</link>
	<description>Projets d’un consultant en sécurité informatique</description>
	<lastBuildDate>Fri, 15 Feb 2019 08:02:10 +0000</lastBuildDate>
	<language>fr-FR</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.4.2</generator>
		<item>
		<title>Bypassing SEHOP on Windows 7</title>
		<link>http://www.segmentationfault.fr/securite-informatique/bypassing-sehop-on-windows-7/</link>
		<comments>http://www.segmentationfault.fr/securite-informatique/bypassing-sehop-on-windows-7/#comments</comments>
		<pubDate>Mon, 21 Dec 2009 12:59:28 +0000</pubDate>
		<dc:creator>Emilien Girault</dc:creator>
				<category><![CDATA[Reverse Engineering]]></category>
		<category><![CDATA[Sécurité informatique]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[hacking]]></category>
		<category><![CDATA[seven]]></category>

		<guid isPermaLink="false">http://www.segmentationfault.fr/?p=740</guid>
		<description><![CDATA[La protection SEHOP introduite dans Windows Vista et 2008 permet de protéger les applications contre les exploitations de buffer-overflows classiques. Celles-ci consistent en général à écraser non seulement une adresse de retour, mais aussi la structure SEH gérant les exceptions provoquées par l&#8217;application, dans le but de rediriger le flux d&#8217;exécution vers un shellcode. La [...]]]></description>
			<content:encoded><![CDATA[<p>La protection SEHOP <a href="http://blogs.technet.com/srd/archive/2009/02/02/preventing-the-exploitation-of-seh-overwrites-with-sehop.aspx">introduite</a> dans Windows Vista et 2008 permet de protéger les applications contre les exploitations de buffer-overflows classiques. Celles-ci consistent en général à écraser non seulement une adresse de retour, mais aussi la structure SEH gérant les exceptions provoquées par l&rsquo;application, dans le but de rediriger le flux d&rsquo;exécution vers un shellcode. La protection SEHOP empêche ce type d&rsquo;exploitation en parcourant au préalable la chaîne des structures SEH et en s&rsquo;assurant qu&rsquo;elle soit valide. Jusqu&rsquo;à maintenant, elle était considérée par beaucoup comme inviolable. Microsoft ont d&rsquo;ailleurs décidé de l&rsquo;activer par défaut dans Windows 2008, ainsi que dans Windows Vista et 7 sous forme de fix.<span id="more-740"></span></p>
<p>Or, deux collègues de <a href="http://www.sysdream.com/">Sysdream</a>, Stéfan Leberre (<a href="http://www.ghostsinthestack.org">Heurs</a>) et Damien Cauquil (<a href="http://www.virtualabs.fr">Virtualabs</a>), viennent de trouver une méthode permettant de la contourner sous certaines conditions. Leur article ainsi que leur Proof Of Concept est disponible sur le site de Sysdream :</p>
<ul>
<li><a href="http://www.sysdream.com/articles/sehop_en.pdf">L&rsquo;article Bypassing SEHOP</a></li>
<li><a href="http://www.sysdream.com/SEHOP.zip">Proof Of Concept fonctionnant sous Windows 7</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.segmentationfault.fr/securite-informatique/bypassing-sehop-on-windows-7/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Problème de multiboot Vista &#8211; XP</title>
		<link>http://www.segmentationfault.fr/windows/probleme-multiboot-vista-xp/</link>
		<comments>http://www.segmentationfault.fr/windows/probleme-multiboot-vista-xp/#comments</comments>
		<pubDate>Wed, 24 Jun 2009 22:35:56 +0000</pubDate>
		<dc:creator>Emilien Girault</dc:creator>
				<category><![CDATA[Windows]]></category>

		<guid isPermaLink="false">http://www.segmentationfault.fr/?p=674</guid>
		<description><![CDATA[Je viens tout juste de me pencher sur un problème de multiboot avec mon ordi portable. Pour résumer grossièrement la situation, j&#8217;avais un triple boot fonctionnel entre Windows Vista, Windows XP et Ubuntu, et un beau jour je me suis décidé de supprimer la partition contenant Vista. Résultat : impossible de booter sur XP ; [...]]]></description>
			<content:encoded><![CDATA[<p>Je viens tout juste de me pencher sur un problème de multiboot avec mon ordi portable. Pour résumer grossièrement la situation, j&rsquo;avais un triple boot fonctionnel entre Windows Vista, Windows XP et Ubuntu, et un beau jour je me suis décidé de supprimer la partition contenant Vista. Résultat : impossible de booter sur XP ; Grub me renvoie une erreur &laquo;&nbsp;Invalid device requested&nbsp;&raquo;. Après quelques bidouilles, d&rsquo;autres messages d&rsquo;erreurs font leur apparition, comme &laquo;&nbsp;NTLDR is missing&nbsp;&raquo; ou &laquo;&nbsp;Invalid partition table&nbsp;&raquo;. Finalement, après deux soirées d&rsquo;acharnement, je parviens à refaire démarrer mon Windows XP. Je poste cet article dans l&rsquo;espoir qu&rsquo;il vienne en aide à ceux qui seront confronté à un problème similaire.<span id="more-674"></span></p>
<h3>Le décor</h3>
<p>Il y a environ 2 ans, j&rsquo;achète un laptop. Vista est préinstallé dessus, et la machine n&rsquo;a qu&rsquo;1 Go de RAM et un AMD Turion x2 pas très véloce. Bref, je ne vous fais pas de dessin : Vista est sans surprise extrèmement lent. Je décide donc d&rsquo;installer un XP. Pour éviter les problèmes avec un éventuel retour SAV de la machine, je préfère garder Vista ; je réduis donc sa partition à 20 Go et installe XP sur le reste. Et je me dis que je l&rsquo;utiliserai sans doute occasionnelement&#8230;</p>
<p>Lors de l&rsquo;installation, XP détecte Vista, et s&rsquo;installe dans une partition étendue qu&rsquo;il crée automatiquement. Pourquoi une étendue et pas une principale ? Seul Microsoft le sait&#8230; Une fois XP installé, celui-ci a viré le boot loader de Vista. Il faut donc que j&rsquo;utilise Vista Boot Pro afin de mettre en place un dual boot Vista &#8211; XP. Je vous passe les détails, ce n&rsquo;est pas le but de cet article.</p>
<p>Enfin, je me décide à installer une Ubuntu. Je rétrécis donc XP et installe Ubuntu dans des partitions à la suite de celle contenant XP. Grub s&rsquo;installe sur le MBR et me permet alors de mettre en place mon triple boot. En fait, Grub détecte les deux Windows comme une seul entrée. Au boot, il me propose le choix entre Linux et les Windows. Si je choisis Windows, le boot loader de Vista prend la relève et me donne le choix entre booter Vista ou XP. Ouf !</p>
<p>Pour résumer, mon disque dur est donc partitionné comme suit :</p>
<ul>
<li>Une partition primaire contenant Vista</li>
<li>Une partition étendue avec :
<ul>
<li>XP</li>
<li>Linux</li>
</ul>
</li>
</ul>
<h3>La revanche de Vista</h3>
<p>Cette config a très bien marché pendant près de 2 ans. Jusqu&rsquo;au jour ou je me rend compte qu&rsquo;en fait, je n&rsquo;ai jamais utilisé Vista, alors qu&rsquo;il m&rsquo;utilise tout de même 20 Go sur mon disque. Comme la garantie du laptop est de toutes façon arrivée à terme, je me dis que je ne risque rien à le supprimer. Ni une ni deux ; je supprime la partition avec GParted, sous Linux. Je reboote. Et là, c&rsquo;est le drame. XP ne boote plus, j&rsquo;obtiens le message &laquo;&nbsp;<strong>Invalid device requested</strong>&laquo;&nbsp;. Il faut croire que Vista a emporté XP dans sa tombe&#8230;</p>
<h3>Le combat</h3>
<p>En cherchant un peu sur le Web, je m&rsquo;aperçois que cette erreur viendrait de Grub. Je vérifie l&rsquo;entrée correspondant à XP (en tapant &laquo;&nbsp;e&nbsp;&raquo; au menu de Grub lors du boot), qui est toujours valide. En effet, XP étant au début de la partition étendue, Grub la nomme (hd0,4) (équivalent de /dev/sda5). D&rsquo;où vient l&rsquo;erreur ?</p>
<p>Je continue à chercher, et je m&rsquo;aperçois que Windows XP n&rsquo;a apparemment pas été prévu pour pouvoir booter sur une partition étendue. La question que je me pose alors est : dans ce cas, pourquoi bootait-il lorsque Vista était encore là ? En me documentent sur le multiboot Vista &#8211; XP, j&rsquo;apprends que lorsqu&rsquo;un tel multiboot est mis en place, c&rsquo;est Vista qui se charge de booter XP. Ce qui expliquerait donc pourquoi XP ne boote plus désormais&#8230;</p>
<p>La solution se dessine : il faudrait que je &laquo;&nbsp;sorte&nbsp;&raquo; ma partition XP de ma partition étendue, pour en faire une primaire. Peut-être que de cette façon, XP sera capable de booter tout seul comme un grand. Sous Linux, j&rsquo;utilise donc GParted pour copier ma partition XP en dehors de la partition étendue (à la place de l&rsquo;ancien Vista). Après pas mal de temps, l&rsquo;opération se termine avec succès. Tant qu&rsquo;à faire, je marque la partition comme bootable. Je reboote, et demande à Grub de booter désormais sur (hd0,0), c&rsquo;est à dire la nouvelle partition. Cette fois-ci, nouvelle erreur : &laquo;&nbsp;<strong>NTLDR is missing</strong>&laquo;&nbsp;.</p>
<p>NTLDR est le boot loader de XP, et il semble introuvable. Je glane encore quelques infos sur le Web. Il semblerait qu&rsquo;il soit possible de remédier à ce problème avec la console de récupération du CD de XP, en utilisant l&rsquo;utilitaire fixboot. Je le fais, mais cela n&rsquo;améliore rien. Je recommence, en rajoutant cette fois-ci un coup de fixmbr. Là, c&rsquo;est encore pire : Grub ne démarre même plus, car Windows a écrasé le MBR (logique), mais le processus de boot va encore moins loin car j&rsquo;obtiens une erreur &laquo;&nbsp;<strong>Invalid partition table</strong>&laquo;&nbsp;. De mieux en mieux&#8230;</p>
<p>Je commence par réinstaller Grub, en utilisant un live CD d&rsquo;Ubuntu et l&rsquo;utilitaire grub-install, qui répare le MBR. Je peux alors booter à nouveau sous Linux. Je monte ma partition Windows défectueuse et liste les fichiers situés à la racine. Dans /dev/sda1 (C:), je ne vois aucun fichier ntldr. Ni de boot.ini, ni de ntdetect.com&#8230; Pourtant, ces fichiers sont vitaux au démarage de Windows ! J&rsquo;en conclus donc que Vista avait du déplacer ces fichiers dans sa partition (vu qu&rsquo;il boote XP lui-même), et qu&rsquo;ils ont été effacés avec le reste de la partition. Heureusement, j&rsquo;ai un autre XP qui fonctionne sur une autre machine. J&rsquo;utilise donc une clé USB pour copier les fichiers ntldr, boot.ini et ntdetect.com sur ma partiton XP defectueuse. Au passage, je vérifie que le boot.ini est correct. Je repasse ensuite un coup de fixboot avec la console de récupération XP. Je reboote, et&#8230;</p>
<h3>Le bout du tunnel</h3>
<p>Yeah, XP démarre ! Après un check des disques, tout semble fonctionner&#8230; Du moins pendant quelques secondes. Rapidement, plusieurs services se mettent à planter. En regardant de plus près, je commence à comprendre pourquoi : les lettres des lecteurs ont changé. En effet, lorsque j&rsquo;avais installé XP pour la 1ère fois, il s&rsquo;était attribué la lettre F, car il était sur la partition étendue. Comme cette fois-ci je boote sur une partition primaire (copie de l&rsquo;ancienne), et que Windows attribue &laquo;&nbsp;bêtement&nbsp;&raquo; les lettres dans l&rsquo;ordre des partitions sur le disque, cette partition se retrouve nommée C. Seul problème : tous les programmes, raccourcis et la base de registre pointent encore vers F&#8230;</p>
<p>La solution est donc de renommer les lecteurs, en inversant C et F. Cependant, l&rsquo;utilitaire de gestion des disques Windows refuse, car la partition C est une partition système. Quasiment désespéré, je reparcours le Web en recherche d&rsquo;une méthode miracle pour renommer de force cette partition&#8230; Et je finis par en trouver une, que <a href="http://www.svmlemag.fr/pratique/0727/modifier_la_lettre_d_unite_du_disque_de_demarrage">voici </a>!</p>
<p>Pour résumer, il s&rsquo;agit d&rsquo;inverser deux clés dans la base de registre. Elles se trouvent dans HKEY_LOCAL_MACHINESYSTEMMountedDevices. Dans mon cas, je dois inverser DosDevicesC: et DosDevicesF:.  Je le fais, et je reboote&#8230;</p>
<p>Ca marche ! Au reboote, F: est bien ma partition système primaire, et C: est devenu l&rsquo;ancienne partition. Mon XP est désormais stable, il pète de nouveau la forme&#8230; Je supprime C et à reboote une dernière fois pour m&rsquo;assurer que tout roule, et c&rsquo;est le cas. Encore une victoire de canard <img src='http://www.segmentationfault.fr/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<h3>Morales</h3>
<p>Les leçons à tirer de cette histoire sont :</p>
<ul>
<li>Méfiez vous comme la peste du multiboot Vista &#8211; XP, surtout quand vous supprimez Vista.</li>
<li>N&rsquo;installez pas Windows dans une partition étendue.</li>
<li>Ayez toujours un CD de Windows XP sous la main, car la console de restauration est bien pratique.</li>
<li>Même chose concernant les live CD de Linux : GParted est un outil gratuit incontournable qui pourra vous sauver la mise même si tous vos OS sont inutilisables. En plus, vous pourrez aller sur Internet avec et chercher de la doc&#8230;</li>
<li>Sauvegardez votre ntldr, boot.ini et ntdetect.com pour les avoir à disposition au cas où (ils ne font que quelques ko).</li>
<li>Changer la lettre d&rsquo;une partition système est possible&#8230; mais ne changera pas les références gardées en dur par tous vos programmes. Ne le faites que si la lettre de votre partition a changée toute seule !</li>
</ul>
<p>Sur ce, j&rsquo;espère avoir aidé du monde !</p>
]]></content:encoded>
			<wfw:commentRss>http://www.segmentationfault.fr/windows/probleme-multiboot-vista-xp/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Conférence NDH 2009 sur GoRing0</title>
		<link>http://www.segmentationfault.fr/projets/conference-ndh-2009-goring0/</link>
		<comments>http://www.segmentationfault.fr/projets/conference-ndh-2009-goring0/#comments</comments>
		<pubDate>Wed, 13 May 2009 22:50:50 +0000</pubDate>
		<dc:creator>Emilien Girault</dc:creator>
				<category><![CDATA[Nuit du hack]]></category>
		<category><![CDATA[Projets]]></category>
		<category><![CDATA[Publications]]></category>
		<category><![CDATA[Sécurité informatique]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[hacking]]></category>
		<category><![CDATA[kernel]]></category>
		<category><![CDATA[ring 0]]></category>
		<category><![CDATA[rootkit]]></category>
		<category><![CDATA[XeeK]]></category>

		<guid isPermaLink="false">http://www.segmentationfault.fr/?p=641</guid>
		<description><![CDATA[Je me suis récemment lancé sur un nouveau projet perso, nommé GoRing0. Ce projet est parti d&#8217;une question existentielle que je me suis posée concernant les processus, ou plutôt les threads. Sous Linux et Windows, ceux-ci s&#8217;exécutent normalement en ring 3 (mode processeur userland), alors que les drivers s&#8217;exécutent en ring 0 (kernelland).  Ma question [...]]]></description>
			<content:encoded><![CDATA[<p>Je me suis récemment lancé sur un nouveau projet perso, nommé GoRing0. Ce projet est parti d&rsquo;une question existentielle que je me suis posée concernant les processus, ou plutôt les threads. Sous Linux et Windows, ceux-ci s&rsquo;exécutent normalement en ring 3 (mode processeur <em>userland</em>), alors que les drivers s&rsquo;exécutent en ring 0 (<em>kernelland</em>).  Ma question était la suivante : est-ce techniquement possible de passer un thread du ring 3 vers le ring 0 ? Attention, je ne parle pas de faire un appel à un code driver depuis un programme ring 3 par le biais d&rsquo;une interruption ou d&rsquo;un IOCTL, mais bien de passer une le code du thread lui-même en ring 0. Étant convaincu que la réponse à cette question est affirmative, je me lance dans la conception d&rsquo;une preuve de concept : GoRing0.<span id="more-641"></span></p>
<h3>GoRing0</h3>
<p>GoRing0 est donc un rootkit qui a pour objectif de faire passer un thread en ring 0, et de le rebasculer accessoirement en ring 3 lorsqu&rsquo;il le souhaite. Pour cela, celui-ci devra bien évidemment charger un driver, afin de s&rsquo;élever les droits et de permettre une telle commutation. La partie utilisateur du rootkit pourrait se présenter sous la forme d&rsquo;une librairie exportant deux fonctions principales : GoRing0() et GoRing3(), permettant le basculement en ring 0 et ring 3 respectivement. N&rsquo;importe quel programme pourrait alors faire appel à ces fonctions et s&rsquo;élever les droits à volonté. Je développe pour le moment sous Windows, mais idéalement, GoRing0 devrait être capable de fonctionner aussi bien sous sous Linux, la manipulation qu&rsquo;il effectue étant surtout liée à l&rsquo;architecture x86.</p>
<p>Le principe de fonctionnement de GoRing0 repose sur les interruptions. Les fonctions GoRing0() et GoRing3() déclenchent une interruption spécifique qui est hookée par le driver et qui se charge de modifier la valeur de CS empilée automatiquement par le processeur. En effet il se trouve que le ring courant du processeur (le CPL) est encodé dans les 2 premiers bits de CS ainsi que dans le descripteur de segment (dans la GDT) auquel il correspond. Plus intéressant encore, Windows définit deux valeurs spéciales de CS : une en userland (0x1b), et une en kernelland (0&#215;8). Il suffit alors au handler d&rsquo;interruption de substituer la valeur empilée de CS par la valeur kernelland pour passer le thread en ring 0 au retour d&rsquo;interruption.</p>
<p>Pour le moment, j&rsquo;ai un prototype fonctionnel stable : j&rsquo;arrive à passer un thread en ring 0 et à le rebasculer en ring 3. Pour m&rsquo;en assurer, je peux afficher la mémoire kernel depuis ce thread (adresses supérieures à 0&#215;80000000 sous Windows). Cependant, lorsqu&rsquo;un thread est en ring 0, il est impossible d&rsquo;appeler des API Windows car celles-ci vérifient apparamment si le thread qui les appelle vient bien du ring 3. De même, il n&rsquo;est pas possible d&rsquo;appeler des fonctions du kernel depuis le thread même en ring 0, à moins d&rsquo;en connaître l&rsquo;adresse ou de la résoudre à l&rsquo;exécution.</p>
<h3>Nouvelle conférence à la Nuit Du Hack</h3>
<p>Enfin, j&rsquo;en viens à la véritable news de ce post&#8230; Au départ, je devais présenter une conférence sur XeeK et une autre sur InjecSO. Etant donné qu&rsquo;InjecSO est sorti depuis un moment maintenant (avec la doc sur ce blog), que je me suis lancé dans ce nouveau projet, et que cette année il n&rsquo;y a pour l&rsquo;instant aucune conférence orientée kernel, j&rsquo;ai pensé intéressant de remplacer la conférence sur InjecSO par une sur GoRing0. Je compte y présenter le projet et par la même occasion de présenter rapidement l&rsquo;architecture x86 et le développement en mode kernel aux débutants en la matière. Ceux pour qui les concepts d&rsquo;interruption, segments et autre GDT sont du chinois (autrement dit ceux qui n&rsquo;ont pas compris le paragraphe technique ci-dessus&#8230;) sont les bienvenus, je compte justement expliquer tout cela ! Ca sera de plus l&rsquo;occasion de releaser officiellement GoRing0 et de faire une petite démo. Sur ce, à la NDH !</p>
]]></content:encoded>
			<wfw:commentRss>http://www.segmentationfault.fr/projets/conference-ndh-2009-goring0/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Tunnez votre BSOD (Blue Screen Of Death)</title>
		<link>http://www.segmentationfault.fr/securite-informatique/tunnez-votre-bsod-blue-screen-of-death/</link>
		<comments>http://www.segmentationfault.fr/securite-informatique/tunnez-votre-bsod-blue-screen-of-death/#comments</comments>
		<pubDate>Thu, 30 Apr 2009 18:58:35 +0000</pubDate>
		<dc:creator>Emilien Girault</dc:creator>
				<category><![CDATA[Reverse Engineering]]></category>
		<category><![CDATA[Sécurité informatique]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[ring 0]]></category>

		<guid isPermaLink="false">http://www.segmentationfault.fr/?p=631</guid>
		<description><![CDATA[Tous les développeurs de drivers et les reversers kernel vous le diront : quand on touche au noyau Windows d&#8217;un peu trop près, on a le droit à un écran bleu de la mort, ou BSOD (Blue Screen Of Death). C&#8217;est justement parce que je me suis pris un tas de BSOD que j&#8217;ai commencé [...]]]></description>
			<content:encoded><![CDATA[<p>Tous les développeurs de drivers et les reversers kernel vous le diront : quand on touche au noyau Windows d&rsquo;un peu trop près, on a le droit à un écran bleu de la mort, ou BSOD (Blue Screen Of Death). C&rsquo;est justement parce que je me suis pris un tas de BSOD que j&rsquo;ai commencé à en avoir marre de voir des écrans bleus partout et à me poser des questions existentielles du genre : pourquoi l&rsquo;écran bleu est-il bleu ? Est-ce possible de changer cela ? Oui, et je vais montrer dans cet article comment faire en ne modifiant qu&rsquo;un octet en mémoire kernel. Matériel requis : une deuxième machine (une VM fait très bien l&rsquo;affaire), Windbg, IDA PRO (facultatif) et quelques neurones.<span id="more-631"></span></p>
<h3>Le pourquoi du BSOD</h3>
<p>Avant de mettre les mains dans le cambouis, il est intéressant de comprendre pourquoi Windows affiche un écran bleu. Cet écran maléfique est affiché lorsque quelque chose d&rsquo;assez sérieux s&rsquo;est produit au niveau du noyau. Cela peut être aussi bien matériel que logiciel (parfois les deux). Par exemple : un driver tente d&rsquo;accéder à une zone mémoire non existante ou protégée. Si vous essayez de faire cela avec un programme en en mode utilisateur, celui-ci crashera en affichant un message d&rsquo;erreur Windows (ou une <em>segmentation fault</em> sous Linux). En mode noyau, l&rsquo;équivalent est le BSOD sous Windows (<em>Kernel Panic</em> sous Linux) et vous n&rsquo;avez pas d&rsquo;autre choix que de rebooter votre bécane. Eh oui, on ne plaisante pas avec le kernel. Voici un exemple de driver qui provoquera un écran bleu à coup sûr :</p>
<pre>* (unsigned int *) 0 = 0;</pre>
<p>Ce code essaye d&rsquo;écrire 0 à l&rsquo;adresse mémoire virtuelle 0, qui est invalide. Par conséquent, le processeur déclenchera un défaut de page, ce qui provoque un écran bleu puisque son descripteur est invalide. Il y a plein d&rsquo;autres manières de provoquer un écran bleu, mais cela dépasse le cadre de cet article.</p>
<h3>Reversing de la routine du BSOD</h3>
<p>Le code de la routine affichant le BSOD se situe dans le kernel Windows, dans ntoskrnl.exe (ou ntkrnlpa.exe si vous avez la PAE). Après avoir débuggé une machine virtuelle et provoqué un écran bleu volontairement grâce au code donné ci-dessus, je m&rsquo;aperçois que la VM s&rsquo;est arrêtée dans la routine <code>KeBugCheckEx</code>. J&rsquo;ouvre ntoskrnl.exe avec IDA PRO et désassemble cette routine.</p>
<pre>public _KeBugCheckEx@20
_KeBugCheckEx@20 proc near

BugCheckCode= dword ptr  8
BugCheckParameter1= dword ptr  0Ch
BugCheckParameter2= dword ptr  10h
BugCheckParameter3= dword ptr  14h
BugCheckParameter4= dword ptr  18h

mov     edi, edi
push    ebp
mov     ebp, esp
push    0
push    [ebp+BugCheckParameter4]
push    [ebp+BugCheckParameter3]
push    [ebp+BugCheckParameter2]
push    [ebp+BugCheckParameter1]
push    [ebp+BugCheckCode]
call    _KeBugCheck2@24 ; KeBugCheck2(x,x,x,x,x,x)
pop     ebp
retn    14h
_KeBugCheckEx@20 endp</pre>
<p>Cette fonction est très courte, elle ne fait qu&rsquo;empiler des paramètres et appeler la routine <code>KeBugCheck2</code>. Cette dernière est par contre très longue. Comme mon but n&rsquo;est pas de la reverser intégralement mais juste isoler la partie qui m&rsquo;intéresse, je parcours rapidement son graphe avec IDA. Vu le nom des fonctions appelées, cette routine récupère des informations auprès des drivers sur le crash qui vient d&rsquo;avoir lieu.</p>
<h3>Couleur de fond</h3>
<p>A peu près au milieu de la fonction, on tombe sur ce code :</p>
<pre>call    _InbvAcquireDisplayOwnership@0 ; InbvAcquireDisplayOwnership()
call    _InbvResetDisplay@0 ; InbvResetDisplay()
push    4
push    1DFh
mov     ebx, 27Fh
push    ebx
push    esi
push    esi
call    _InbvSolidColorFill@20 ; InbvSolidColorFill(x,x,x,x,x)</pre>
<p>Apparemment, on a affaire à des appels de fonction gérant l&rsquo;affichage. On commence par faire un <em>reset </em>de l&rsquo;affichage, puis on appelle la routine <code>InvbSolidColorFill</code>. Les paramètres qui lui sont passés sont les suivants (je rappelle que l&rsquo;ordre est inversé, selon la convention d&rsquo;appel utilisé) :</p>
<ul>
<li>deux arguments égaux à 0 (si on cherche un peu plus haut on trouve un <code>xor esi,esi</code> donc esi = 0)</li>
<li>0x27F = 639</li>
<li>0x1DF = 479</li>
<li>4</li>
</ul>
<p>Etant astucieux, on se rend compte que 639 = 640 &#8211; 1 et 479 = 480 &#8211; 1. Hors, 640 x 480 correspond exactement à la résolution de l&rsquo;écran bleu. Avec les deux paramètres 0 qui précèdent, ces valeurs correspondent aux bornes (gauche, haute, droite, et basse) de la zone de l&rsquo;écran à remplir. La couleur de remplissage est précisée par le dernier paramètre, qui vaut 4. En cherchant un peu sur Google, on se rend compte que le code couleur utilisé par cette fonction est sur 4 bits : IBGR soit Intensity, Blue, Green and Red. 4 en décimal équivaut à 0100 en binaire, soit 1 pour le bleu et 0 pour le reste. Voilà donc pourquoi l&rsquo;écran bleu est bleu ! <img src='http://www.segmentationfault.fr/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Si on désassemble la fonction <code>InvbSolidColorFill</code>, on se rend compte qu&rsquo;assez rapidement, celle-ci appelle la fonction <code>VidSolidColorFill</code>. Il s&rsquo;agit d&rsquo;une fonction importée par ntoskrnl.exe depuis bootvid.dll, le module chargé de la vidéo lors du boot. Son code est relativement peu clair, aussi j&rsquo;ai préféré jeter un coup d&rsquo;oeil aux sources de ReactOS, qui est nettement plus lisible. Pour ceux qui ne connaissent pas, il s&rsquo;agit d&rsquo;un projet visant à recoder Windows en Open Source. Voici le code de la fonction dans ReactOS (qui doit être très similaire dans Windows) :</p>
<pre>VOID NTAPI
VidSolidColorFill(IN ULONG Left, IN ULONG Top,
                  IN ULONG Right, IN ULONG Bottom,
                  IN UCHAR Color)
{
    int y, x;

	for (y = Top; y &lt;= Bottom; y++)
	{

            for (x = Left; x &lt;= Right; x++)
            {
                //
                // Draw the pixel
                //
                VidpSetPixel(x, y, Color);
            }
	}
}</pre>
<p>Comme vous pouvez le voir, on peut difficilement faire plus simple. Une fonction chargée de fixer la couleur d&rsquo;un pixel est appelée dans une double boucle, ce qui a pour effet de remplir l&rsquo;écran.</p>
<h3>Couleur du texte</h3>
<p>Revenons au code de <code>KeBugCheck2</code>. Juste après le code qui remplit l&rsquo;écran, on a ceci :</p>
<pre>push    0Fh
call    _InbvSetTextColor@4 ; InbvSetTextColor(x)</pre>
<p>Vu le nom de la fonction, on peut se douter que celle-ci fixe la couleur du texte du BSOD. 0xF = 0b1111 = blanc. Que demander de plus ?</p>
<h3>Patching à chaud pour changer les couleurs</h3>
<p>Que diriez vous de personnaliser la couleur du BSOD sur votre système ? Je n&rsquo;ai personnellement jamais aimé le bleu, je préfère le rouge (plus sexy pour un message d&rsquo;erreur !). Quand au texte, du jaune devrait faire l&rsquo;affaire&#8230;</p>
<p>Pour des raisons de sécurité, je vous déconseille fortement d&rsquo;éditer l&rsquo;exécutable de votre noyau (ntoskrnl.exe) car une erreur peut être vraiment fatale. Je préfère nettement opérer en mémoire, afin qu&rsquo;un simple reboot efface les modifications. D&rsquo;autre part, comme on s&rsquo;apprête à débugger Windows et à le faire planter, il faut opérer sur une deuxième machine. Munissez vous d&rsquo;une VM (VirtualBox pour ma part) que vous bootez en mode debug (flag /debug du boot.ini) avec Windbg lancée en parallèle sur votre machine. Pour ceux qui débutent, je vous conseille de lire <a href="http://0vercl0k.blogspot.com/2008/02/first-steps-into-ring0.html">cet article</a> d&rsquo;0vercl0ck qui vous expliquera comment tout configurer comme il faut. Prêt ? C&rsquo;est parti !</p>
<p>Une fois Windows démarré, freezez le avec Ctrl+Pause sous Windbg. Commençons par localiser ou se trouve l&rsquo;appel à <code>InvbSolidColorFill</code> dans <code>KeBugCheck2</code>. Sous IDA, on effectue un petit calcul d&rsquo;offset pour savoir ou se trouve le <code>push 4</code> par rapport au début de <code>KeBugCheck2</code>. Chez moi, j&rsquo;ai un offset de 0x60D. Dans Windbg, je tape donc <code>u KeBugCheck2+0x60d</code>. Cependant, ça ne tombe pas juste ; j&rsquo;atterris après ce bout de code (facile de le constater avec IDA). C&rsquo;est sans doute à cause du fait que j&rsquo;ai des mises à jours différentes sur ma machine et dans ma VM. Je remonte donc petit à petit et je finir par arriver sur l&rsquo;instruction qui m&rsquo;intéresse :</p>
<pre>kd&gt; u KeBugCheck2+0x5e4
nt!KeBugCheck2+0x5e4:
8053342e e8da9fffff      call    nt!InbvResetDisplay (8052d40d)
80533433 6a04            push    4
80533435 68df010000      push    1DFh
8053343a bb7f020000      mov     ebx,27Fh
8053343f 53              push    ebx
80533440 56              push    esi
80533441 56              push    esi
80533442 e84aa0ffff      call    nt!InbvSolidColorFill (8052d491)</pre>
<p>Il suffit de patcher la valeur située à l&rsquo;adresse <code>80533434</code> pour changer la couleur du BSOD ! Pour ma part, je choisis le rouge, soit 0b0001 = 1. Je tape donc ceci dans Windbg (eb = edit byte) :</p>
<pre>kd&gt; eb 80533434 01</pre>
<p>Vérifions cela en désassemblant à nouveau :</p>
<pre>kd&gt; u KeBugCheck2+0x5e4
nt!KeBugCheck2+0x5e4:
8053342e e8da9fffff      call    nt!InbvResetDisplay (8052d40d)
80533433 6a01            push    1
80533435 68df010000      push    1DFh
8053343a bb7f020000      mov     ebx,27Fh
8053343f 53              push    ebx
80533440 56              push    esi
80533441 56              push    esi
80533442 e84aa0ffff      call    nt!InbvSolidColorFill (8052d491)</pre>
<p>Super, ça semble avoir marché. Faisons de même avec la couleur du texte !</p>
<pre>kd&gt; u KeBugCheck2+0x5fd
nt!KeBugCheck2+0x5fd:
80533447 6a0f            push    0Fh
80533449 e8d1a0ffff      call    nt!InbvSetTextColor (8052d51f)</pre>
<p>Je veux du jaune, je remplace donc le 0x0F par un 0x0B soit 0b1011 (mélange clair de vert et rouge).</p>
<pre>kd&gt; eb 80533448 0B
kd&gt; u KeBugCheck2+0x5fd
nt!KeBugCheck2+0x5fd:
80533447 6a0b            push    0Bh
80533449 e8d1a0ffff      call    nt!InbvSetTextColor (8052d51f)</pre>
<p>Nikel. Il ne reste plus qu&rsquo;à provoquer un écran bleu ! Pour ce faire à partir de Windbg, c&rsquo;est très simple : il suffit de mettre eip à 0. On tape juste <code>r eip = 0</code> et on débloque la machine avec la commande <code>g</code>. Windows crashe instantanément et Windbg reprend la main. Attendez quelques secondes que les symboles se rechargent, appuyez sur Ctrl+Pause pour arrêter l&rsquo;opération de diagnostique de crash, et retapez <code>g</code> pour voir apparaître votre beau BSOD personnalisé. En ce qui me concerne, c&rsquo;est plutôt un RSOD (Red Screen Of Death) !</p>
<div id="attachment_635" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.segmentationfault.fr/wp-content/uploads/2009/04/rsod.png"><img class="size-medium wp-image-635" title="RSOD" src="http://www.segmentationfault.fr/wp-content/uploads/2009/04/rsod-300x225.png" alt="Red Screen Of Death" width="300" height="225" /></a><p class="wp-caption-text">Red Screen Of Death</p></div>
<p>C&rsquo;est ti pas mignon tout ça ? Pour ceux qui par malheur verraient leur écran s&rsquo;afficher puis disparaître à cause d&rsquo;un reboot instantané, refaites la manip en n&rsquo;oubliant pas au préalable de désactiver les reboots sur crash système en faisant un clic droit sur le poste de travail, propriétés, avancé, paramètres de démarrage et récupération, puis décochez la case &laquo;&nbsp;redémarrer automatiquement&nbsp;&raquo;.</p>
<h3>Conclusion</h3>
<p>J&rsquo;espère que cet article vous aura plu et que vous allez dès à présent tuner votre BSOD pour rendre jaloux vos amis. Pour ceux qui voudraient aller plus loin, sachez qu&rsquo;il est également possible de modifier le texte affiché sur l&rsquo;écran&#8230; Après, vous pouvez intégrer tout ça dans un driver qui se chargera d&rsquo;effectuer le patching lui-même. C&rsquo;est ce que fait le programme BSOD Hack (cf en bas). Et si vous en avez le courage, vous pouvez même faire un petit hook inline (detour patching) afin de rediriger un crash vers votre propre routine. J&rsquo;ai entendu dire que certains avaient réussi à charger et afficher une image de bière lors d&rsquo;un crash, si ça peut vous donner des idées&#8230; <img src='http://www.segmentationfault.fr/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<h3>Références</h3>
<ul>
<li><a href="http://www.rohitab.com/discuss/index.php?showtopic=23767">BSOD Hack</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.segmentationfault.fr/securite-informatique/tunnez-votre-bsod-blue-screen-of-death/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Problèmes liés aux interruptions</title>
		<link>http://www.segmentationfault.fr/securite-informatique/problemes-lies-aux-interruptions/</link>
		<comments>http://www.segmentationfault.fr/securite-informatique/problemes-lies-aux-interruptions/#comments</comments>
		<pubDate>Tue, 21 Apr 2009 18:52:31 +0000</pubDate>
		<dc:creator>Emilien Girault</dc:creator>
				<category><![CDATA[Développement]]></category>
		<category><![CDATA[Reverse Engineering]]></category>
		<category><![CDATA[Sécurité informatique]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[drivers]]></category>
		<category><![CDATA[hacking]]></category>
		<category><![CDATA[kernel]]></category>
		<category><![CDATA[ring 0]]></category>
		<category><![CDATA[x86]]></category>

		<guid isPermaLink="false">http://www.segmentationfault.fr/?p=609</guid>
		<description><![CDATA[Dans le cadre de mon stage, je m&#8217;intéresse au fonctionnement des interruptions de l&#8217;architecture x86. Il s&#8217;agit d&#8217;un mécanisme complexe mais extrêmement important pour comprendre comment un système d&#8217;exploitation arrive à fonctionner. Si j&#8217;écris ce post, c&#8217;est parce que je me suis pris la tête sur des problèmes qui y sont liés&#8230;  Je viens d&#8217;en [...]]]></description>
			<content:encoded><![CDATA[<p>Dans le cadre de mon stage, je m&rsquo;intéresse au fonctionnement des interruptions de l&rsquo;architecture x86. Il s&rsquo;agit d&rsquo;un mécanisme complexe mais extrêmement important pour comprendre comment un système d&rsquo;exploitation arrive à fonctionner. Si j&rsquo;écris ce post, c&rsquo;est parce que je me suis pris la tête sur des problèmes qui y sont liés&#8230;  Je viens d&rsquo;en trouver les causes, et je pense que cela pourra en intéresser plus d&rsquo;un.<span id="more-609"></span></p>
<h3>Généralités sur les interruptions</h3>
<p>Une interruption est un événement particulier qui peuvent se produire lors de l&rsquo;exécution d&rsquo;un programme. A chaque type d&rsquo;interruption est associé un numéro appelé <em>vecteur d&rsquo;interruption</em>, connu du ou des processeur(s). Pour simplifier, les interruptions peuvent provenir de deux sources : le logiciel et le matériel. Les interruptions matérielles sont générées par les périphériques (clavier, souris, cartes, timers, disques durs&#8230;) et sont transmises au processeur par l&rsquo;APIC (<em>Advanced Programmable Interrupt Controller</em>).  Les interruptions logicielles peuvent être quant à elles déclenchées volontairement par une instruction (<em>int</em>) ou de façon accidentelle par une erreur arithmétique, logique, de protection, etc. Dans ce cas, on parle souvent d&rsquo;<em>exception</em> et de <em>fautes</em>.</p>
<p>Généralement, les interruptions (aussi bien matérielles que logicielles) sont susceptibles de se déclencher à n&rsquo;importe quel moment dans l&rsquo;exécution d&rsquo;un programme, et chaque processeur doit savoir comment traiter chacune d&rsquo;entre elles. A chaque vecteur d&rsquo;interruption on associe alors un <em>handler d&rsquo;interruption</em>, qui est une fonction qui sera appelée automatiquement quand l&rsquo;interruption sera générée.  On place ces fonctions dans une table, l&rsquo;IDT, pour <em>Interrupt Descriptor Table</em>. Il est important de noter qu&rsquo;il y a une IDT par processeur, afin que chacun puisse avoir si besoin un comportement différent pour chaque interruption.</p>
<p>Il existe deux types d&rsquo;interruptions matérielles, les masquables (les IRQ) et les non masquables (la NMI et la SMI) qu&rsquo;on ne traitera pas ici en raison de leur grande particularité. Chaque interruption matérielle possède une priorité, qui est gérée par l&rsquo;APIC. Pour faire simple, ce composant est relié aux périphériques par des lignes d&rsquo;IRQ (Interrupt Requests) et se charge de faire le médiateur entre tous ces périphériques et le ou les processeur(s). Son rôle est en gros de transformer ces IRQ en demandes d&rsquo;interruptions avec le vecteur associé.  Comme plusieurs IRQ peuvent intervenir en même temps, l&rsquo;APIC définit des priorités afin de transmettre les plus urgentes d&rsquo;abord.</p>
<p>Dans certains cas, le processeur peut ne pas vouloir être dérangé par une interruption. C&rsquo;est le cas par exemple lorsqu&rsquo;il est en train de modifier des structures globales en mémoire. Il a alors la possibilité d&rsquo;ignorer les interruptions qu&rsquo;il va recevoir de deux manières. La première, que je ne détaillerai pas, est de ne masquer que celles dont la priorité est inférieure à un certain seuil (appelé IRQL sous Windows) en communiquant avec l&rsquo;APIC. La deuxième est de masquer la totalité des interruptions et c&rsquo;est celle que nous allons détailler.</p>
<h3>Subtilité sur le masquage</h3>
<p>C&rsquo;est le registre EFLAGS  et plus précisément son bit IF (bit 9) qui contrôle si les interruptions masquables sont activées ou non. Ce bit est modifiable par l&rsquo;intermédiaire des instructions <code>cli</code> pour masquage et <code>sti</code> pour démasquage, ou bien en utilisant <em>pushf</em> / <em>popf</em> associés à des masques.</p>
<p>Attention, le masquage des interruptions n&rsquo;affecte que les interruptions matérielles ! Les interruptions logicielles (ainsi que les interruptions NMI et SMI, très particulières) ne sont pas affectées par ces opérations. Autrement dit, vous aurez beau baisser l&rsquo;IRQL ou faire un <code>cli</code>, une exception due à une division par zéro ou à un défaut de page pourra toujours survenir.</p>
<h3>Problème n°1 : DbgPrint(), c&rsquo;est le mal</h3>
<p>J&rsquo;ai fait tous mes tests en machine virtuelle. Comme le développement de driver n&rsquo;est jamais sûr à 100% et que je voulais tout de même avoir quelque chose de fonctionnel, j&rsquo;ai réalisé un petit script qui charge le driver et le décharge 100 fois de suite, en lançant en plus des programmes divers pour solliciter l&rsquo;OS un maximum. Le tout étant de prouver que le driver n&rsquo;explose pas à la première interruption, bien entendu.</p>
<p>Dans mon cas, je devais hooker l&rsquo;interruption n°14 correspondant au défaut de page (<em>page fault</em>), en insérant du code avant de rappeler le handler par défaut de Windows. Je rappelle que cette exception (logicielle) se produit quand une page virtuelle n&rsquo;est mappée à aucune adresse physique (soit parce qu&rsquo;elle a été swappée sur disque, soit parce qu&rsquo;elle n&rsquo;existe tout simplement pas). Cela se produit quand on tente d&rsquo;accéder à une page dont le descripteur PTE a son premier bit (bit P) à 0.</p>
<p>J&rsquo;ai donc commencé par concevoir un handler minimal qui ne faisait rien à part empiler tous les registres, les dépiler puis appeler le handler de Windows, <code>KiTrap0E</code>. Jusqu&rsquo;ici, tout fonctionne, sauf que je n&rsquo;ai pas vraiment de preuve que ma routine est appelée (vu que je n&rsquo;ai aucune trace).</p>
<p>Je me décide donc à insérer des <code>DbgPrint()</code> dans le code de la routine. Et là, c&rsquo;est le drame : j&rsquo;obtiens des traces certes, mais j&rsquo;en obtiens tellement que la VM freeze ou affiche un écran bleu au bout de quelques secondes. Il faut croire que les défauts de page sont très nombreux sous Windows&#8230; Mais pourquoi ça plante maintenant et pas avant ? L&rsquo;explication est liée au visualisateur de traces (j&rsquo;utilise DebugView). Lorsque <code>DbgPrint()</code> est appelé, le message est récupéré par le noyau puis par DebugView. Je ne sais pas exactement pourquoi, mais apparemment quand il y a beaucoup de messages de récupérés, DebugView commence à provoquer des défauts de page (probablement à cause du fait qu&rsquo;il se fait swapper par l&rsquo;OS). Comme ces défauts de pages sont à leur tour catchés par mon handler, ils entraînent d&rsquo;autres appels à <code>DbgPrint()</code>&#8230; Bref, une belle boucle qui finit par exploser tôt ou tard.</p>
<p>Au final, je renonce donc  à afficher des traces, vu que cela perturbe plus le système qu&rsquo;autre chose. En plus j&rsquo;ai ma preuve que mon handler est appelé, vu que ça plante <img src='http://www.segmentationfault.fr/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<h3>Problème n°2 : sti, c&rsquo;est aussi le mal</h3>
<p>Le réel but de mon handler est de modifier une structure assez cruciale du noyau sous certaines conditions. Afin d&rsquo;éviter que cette modification entraîne des problèmes de concurrence, je décide de masquer les interruptions avant le traitement (<code>cli</code>), et de les démasquer ensuite à l&rsquo;aide de  et <code>sti</code>. Je lance le driver. Pas de crash. Je commence à crier victoire, et au moment ou je lance mon script de déchargement, j&rsquo;ai le droit à un écran bleu. Je regarde le code de déchargement, il ne fait absolument rien de méchant. Je relance, idem. Je finis par comprendre que ce n&rsquo;est pas le déchargement du driver qui foire, mais juste le fait de lancer un programme, car cela provoque un défaut de page&#8230; C&rsquo;est donc bien mon handler qui pose problème, mais où ?</p>
<p>Je supprime ma modification de variable globale, ça plante toujours. Pourtant je n&rsquo;ai plus que <code>cli</code> et <code>sti</code> dans mon handler (en plus de l&rsquo;appel au handler par défaut). Je retire <code>sti</code> et je constate que ça marche ! C&rsquo;est donc cette instruction qui est responsable du plantage du driver&#8230; mais pourquoi ? En fait, il se trouve que lors des appels au handler de défaut de page, les interruptions sont <em>déjà masquées</em>. C&rsquo;est dû au fait que l&rsquo;interruption correspondant au défaut de page possède un descripteur dans l&rsquo;IDT qui précise que l&rsquo;IF doit être mis à 0 lors de l&rsquo;appel du handler (c&rsquo;est une <em>interrupt gate</em>, selon Intel). Ainsi, lors de l&rsquo;appel au handler, l&rsquo;instruction <code>cli</code> ne fait absolument rien (le bit IF d&rsquo;EFLAGS est déjà à 0)&#8230; Le problème, c&rsquo;est que <code>sti</code> le passe à 1, ce qui autorise les interuptions ! Si jamais le défaut de page s&rsquo;est produit dans une zone critique de l&rsquo;OS et qu&rsquo;une interruption survient précisément à ce moment, le kernel se vautre. Cela ne se produit peut-être pas à tous les coups, mais croyez moi, ça arrive (faites le test pour vous en convaincre).</p>
<p>Comment patcher cela ? Ne pas masquer les interruptions dans un handler de défaut de page, puisqu&rsquo;elles le sont déjà. La solution, c&rsquo;est tout simplement de ne rien faire <img src='http://www.segmentationfault.fr/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<h3>Conclusion</h3>
<p>Morale de l&rsquo;histoire : utiliser <code>DbgPrint()</code> ou <code>sti</code> dans un handler de défaut de page est une mauvaise idée. Pour déboguer, préférez utiliser des variables globales plutôt que des fonctions à effet de bords incontrôlables. Et ne cherchez pas à masquer (et surtout à démasquer) les interruptions qui le sont déjà . J&rsquo;espère que ce post vous aura épargné un long moment de galère. En tout cas, j&rsquo;aurais aimé en lire un du même type plus tôt !</p>
]]></content:encoded>
			<wfw:commentRss>http://www.segmentationfault.fr/securite-informatique/problemes-lies-aux-interruptions/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Plongeon dans les appels systèmes Windows</title>
		<link>http://www.segmentationfault.fr/reversing/plongeon-dans-les-appels-systemes-windows/</link>
		<comments>http://www.segmentationfault.fr/reversing/plongeon-dans-les-appels-systemes-windows/#comments</comments>
		<pubDate>Sat, 28 Mar 2009 10:53:26 +0000</pubDate>
		<dc:creator>Emilien Girault</dc:creator>
				<category><![CDATA[Reverse Engineering]]></category>
		<category><![CDATA[Windows]]></category>
		<category><![CDATA[ring 0]]></category>

		<guid isPermaLink="false">http://www.segmentationfault.fr/?p=593</guid>
		<description><![CDATA[Mon stage m&#8217;a donné l&#8217;occasion d&#8217;analyser en détails comment un appel système est réalisé sous Windows. Si vous vous demandez comment un programme utilisateur (ring 3) fait pour appeler une fonction s&#8217;exécutant en mode noyau (ring 0), alors cet article est pour vous. J&#8217;y explique toutes les étapes de la chaîne en partant du début [...]]]></description>
			<content:encoded><![CDATA[<p>Mon stage m&rsquo;a donné l&rsquo;occasion d&rsquo;analyser en détails comment un appel système est réalisé sous Windows. Si vous vous demandez comment un programme utilisateur (ring 3) fait pour appeler une fonction s&rsquo;exécutant en mode noyau (ring 0), alors cet article est pour vous. J&rsquo;y explique toutes les étapes de la chaîne en partant du début (l&rsquo;appel de fonction dans un programme quelconque) pour arriver au code de l&rsquo;API en mode noyau. Pour lire cet article, des bases d&rsquo;assembleur sont indispensables.<span id="more-593"></span></p>
<h3>Privilèges des processeurs x86</h3>
<p>Avant d&rsquo;aborder le sujet, il est nécessaire de bien comprendre une subtilité des processeurs x86 : les niveaux de privilèges. Un processeur est censé exécuter du code machine produit par un compilateur / assembleur. Le code qui s&rsquo;exécute à un instant t a accès à certains privilèges qui dépendent du niveau de privilège dans lequel se trouve le processeur. Ce niveau, aussi appelé <em>ring</em>, ou <em>anneau</em>, est un entier codé sur deux bits. Il peut donc prendre 4 valeurs : 0, 1, 2, et 3. Plus ce nombre est petit, plus les privilèges sont élevés ; plus il est grand, plus ils sont restreints. Le ring 0 est appelé <em>mode superviseur</em>, ou <em>kernelland</em>,et c&rsquo;est sous ce mode que tournent 99% des noyaux d&rsquo;OS (Windows et Linux en font partie). Les programmes utilisateurs s&rsquo;exécutent quant à eux en ring 3, appelé <em>mode utilisateur</em> ou <em>userland</em>.</p>
<p>Le niveau de privilège courant du processeur est appelé CPL (Current Privilege Level). En interne, le CPL est stocké dans les deux premiers bits des registres CS et SS. La règle générale est la suivante : il n&rsquo;est pas possible d&rsquo;exécuter des instructions nécessitant un niveau de privilège inférieur au CPL. De même, il n&rsquo;est pas directement possible de demander au processeur de changer le CPL vers un niveau inférieur. On peut donc se demander : comment est-ce possible d&rsquo;appeler une routine du noyau depuis un code utilisateur ? C&rsquo;est là tout l&rsquo;objet de cet article&#8230;</p>
<h3>Un programme d&rsquo;exemple</h3>
<p>Afin d&rsquo;analyser comment se déroule un appel système, nous allons commencer par coder un programme d&rsquo;exemple en C.</p>
<pre>#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;windows.h&gt;

int main(int argc, char *argv[])
{
    HANDLE handle;
    WIN32_FIND_DATA findData;

    handle = FindFirstFile(".", &amp;findData);

    printf("Handle = %dn", handle);

    getchar();

    return 0;
}</pre>
<p>Ce programme appelle l&rsquo;API Windows <code>FindFirstFile</code>. Cette fonction prend en paramètre un nom de dossier et retourne un handle vers le premier fichier de ce dossier. Je suis d&rsquo;accord : ce programme ne sert pas à grand chose, à part appeler une API Windows, mais c&rsquo;est justement le but.</p>
<h3>Tracing en userland</h3>
<p>Pour information, j&rsquo;ai réalisé tous ces tests sur un Windows XP SP3 français, avec Dev-C++ et GCC 3.4.2. Une fois le programme compilé, on peut utiliser un débogeur pour suivre l&rsquo;exécution du programme. Lançons OllyDbg et observons le <code>main()</code> du programme :</p>
<pre>00401290  /$ 55             PUSH EBP
00401291  |. 89E5           MOV EBP,ESP
00401293  |. 81EC 78010000  SUB ESP,178
00401299  |. 83E4 F0        AND ESP,FFFFFFF0
0040129C  |. B8 00000000    MOV EAX,0
004012A1  |. 83C0 0F        ADD EAX,0F
004012A4  |. 83C0 0F        ADD EAX,0F
004012A7  |. C1E8 04        SHR EAX,4
004012AA  |. C1E0 04        SHL EAX,4
004012AD  |. 8985 A4FEFFFF  MOV DWORD PTR SS:[EBP-15C],EAX
004012B3  |. 8B85 A4FEFFFF  MOV EAX,DWORD PTR SS:[EBP-15C]
004012B9  |. E8 82040000    CALL syscalls.00401740
004012BE  |. E8 1D010000    CALL syscalls.004013E0
004012C3  |. 8D85 A8FEFFFF  LEA EAX,DWORD PTR SS:[EBP-158]           ; |
004012C9  |. 894424 04      MOV DWORD PTR SS:[ESP+4],EAX             ; |
004012CD  |. C70424 0030400&gt;MOV DWORD PTR SS:[ESP],syscalls.00403000 ; |ASCII "fg"
004012D4  |. E8 E7050000    CALL &lt;JMP.&amp;KERNEL32.FindFirstFileA&gt;      ; FindFirstFileA
004012D9  |. 83EC 08        SUB ESP,8
004012DC  |. 8945 F4        MOV DWORD PTR SS:[EBP-C],EAX             ; |
004012DF  |. 8B45 F4        MOV EAX,DWORD PTR SS:[EBP-C]             ; |
004012E2  |. 894424 04      MOV DWORD PTR SS:[ESP+4],EAX             ; |
004012E6  |. C70424 0330400&gt;MOV DWORD PTR SS:[ESP],syscalls.00403003 ; |ASCII "Handle = %d"
004012ED  |. E8 4E050000    CALL &lt;JMP.&amp;msvcrt.printf&gt;                ; printf
004012F2  |. E8 39050000    CALL &lt;JMP.&amp;msvcrt.getchar&gt;               ; [getchar
004012F7  |. B8 00000000    MOV EAX,0
004012FC  |. C9             LEAVE
004012FD  . C3             RETN</pre>
<p>Je rappelle que nous sommes à présent dans un programme utilisateur, donc en userland. Il est facile de le vérifier en regardant la valeur de CS. Chez moi, c'est 0x2B, soit 0b101011. Les deux premiers bits (poids faible) sont bien 0b11, correspondant au ring 3.</p>
<p>L'appel à <code>FindFirstFile</code> se trouve en <code>004012D4</code>, après que les paramètres aient été mis sur la pile. Après avoir placé un breakpoint sur le call (F2), et lancé l'exécution (F9), le programme s'arrête dessus. On fait un <em>step in</em> (F7) pour suivre le call.</p>
<pre>004018C0   $-FF25 B8504000  JMP DWORD PTR DS:[&lt;&amp;KERNEL32.FindFirstFileA&gt;]</pre>
<p>Nous nous situons à présent dans une zone appelée <em>trampoline</em>. Cette zone fait référence à l&rsquo;IAT (Import Address Table) de l&rsquo;exécutable, qui contient les adresses des fonctions importées. Pour faire le parallèle avec Linux, le trampoline est l&rsquo;équivalent de la section .plt, et l&rsquo;IAT joue le même rôle que la section .got. Pour plus d&rsquo;informations sur ces sections je vous conseille de lire <a href="http://www.segmentationfault.fr/linux/role-plt-got-ld-so/">cet article</a>.</p>
<p>Le jump fait référence à un pointeur situé en 004050B8, qui contient l&rsquo;adresse de la fonction <code>FindFirstFileA</code>. On rappuie sur F7 pour suivre l&rsquo;appel. On arrive alors dans la section .text de kernel32.dll, qui a été chargée à l&rsquo;exécution.</p>
<pre>7C813869 &gt; 8BFF             MOV EDI,EDI
7C81386B   55               PUSH EBP
7C81386C   8BEC             MOV EBP,ESP
7C81386E   81EC 6C020000    SUB ESP,26C
...
7C813894   56               PUSH ESI
7C813895   56               PUSH ESI
7C813896   56               PUSH ESI
7C813897   8D8D ACFDFFFF    LEA ECX,DWORD PTR SS:[EBP-254]
7C81389D   51               PUSH ECX
7C81389E   56               PUSH ESI
7C81389F   FF70 04          PUSH DWORD PTR DS:[EAX+4]
7C8138A2   E8 66B2FFFF      CALL kernel32.FindFirstFileExW</pre>
<p>On s&rsquo;aperçoit que cette fonction en appelle une autre, <code>FindFirstFileExW</code>, toujours dans kernel32. Pourquoi ? Simplement parce que la plupart des fonctions internes de Windows utilisent un encodage Unicode, et non pas ASCII. Dans la convention Windows, les fonctions se terminant par un A gèrent l&rsquo;ASCII, et celles en W gèrent l&rsquo;Unicode. En interne, les fonctions ASCII convertissent les paramètres passés en Unicode, et appèlent les fonctions Unicode correspondantes. C&rsquo;est le call que nous venons de voir. Suivons le.</p>
<pre>7C80EB0D &gt; 8BFF             MOV EDI,EDI
7C80EB0F   55               PUSH EBP
7C80EB10   8BEC             MOV EBP,ESP
7C80EB12   81EC CC020000    SUB ESP,2CC
...
7C80EC66   57               PUSH EDI
7C80EC67   8D85 90FDFFFF    LEA EAX,DWORD PTR SS:[EBP-270]
7C80EC6D   89B5 40FDFFFF    MOV DWORD PTR SS:[EBP-2C0],ESI
7C80EC73   8B35 1410807C    MOV ESI,DWORD PTR DS:[&lt;&amp;ntdll.NtOpenFile&gt;] ; ZwOpenFile
7C80EC79   50               PUSH EAX
7C80EC7A   C785 34FDFFFF 18&gt;MOV DWORD PTR SS:[EBP-2CC],18
7C80EC84   FFD6             CALL ESI
...
7C80ED46   FFB5 90FDFFFF    PUSH DWORD PTR SS:[EBP-270]
7C80ED4C   FF15 2812807C    CALL DWORD PTR DS:[&lt;&amp;ntdll.NtQueryDirectoryFile&gt;]</pre>
<p>On voit que cette fonction réalise plusieurs appels (ici je n&rsquo;en n&rsquo;ai affiché que 2 mais il y en a d&rsquo;autres) dans ntdll.dll, une autre DLL chargée. Continuons notre traçing en explorant l&rsquo;appel à <code>ZwOpenFile</code>.</p>
<pre>7C91D580 &gt; B8 74000000      MOV EAX,74
7C91D585   BA 0003FE7F      MOV EDX,7FFE0300
7C91D58A   FF12             CALL DWORD PTR DS:[EDX]  ; <code>ntdll.KiFastSystemCall</code>
7C91D58C   C2 1800          RETN 18</pre>
<h3>Transition vers le mode noyau</h3>
<p>Nous sommes à présent dans ntdll.dll, dans la fonction <code>ZwOpenFile</code> exportée par la dll. Comme vous le voyez, la fonction est très courte. La première instruction place 0&#215;74 dans EAX, qui correspond au numéro de la fonction du noyau qui va être appelée. On a ensuite un appel à une fonction nommée <code>KiFastSystemCall</code>. C&rsquo;est elle qui va réaliser le passage en mode noyau, à l&rsquo;aide des instructions suivantes :</p>
<pre>7C91E4F0 &gt; 8BD4             MOV EDX,ESP
7C91E4F2   0F34             SYSENTER
7C91E4F4 &gt; C3               RETN</pre>
<p>C&rsquo;est précisément SYSENTER qui réalise la transition. Mais que fait donc cette instruction ? On ouvre le manuel Intel 2B au chapitre 4.1, instruction SYSENTER. On y apprend que cette instructionpermet d&rsquo;exécuter des appels systèmes. Historiquement, les appels systèmes étaient exécutés en utilisant les interruptions logicielles, avec l&rsquo;instruction INT 2E sous Windows (et INT 80 sous Linux). SYENTER étant plus rapide, elle a succédé à l&rsquo;ancienne méthode, qui reste toutefois disponible pour des raisons de compatibilité.</p>
<p>Contrairement aux interruptions, SYSENTER n&rsquo;utilise pas de table de pointeurs, mais des registres spéciaux du processeur, appelés MSR (pour Model Specific Register). Ces registres sont assez particuliers ; ils possèdent un numéro et sont accessibles en lecture et écriture via les instructions <code>rdmsr</code> et <code>wrmsr</code>. SYSENTER en utilise 3 :</p>
<ul>
<li>IA32_SYSENTER_CS (0&#215;174) correspond à la valeur à charger dans CS quand SYSENTER sera appelé</li>
<li>IA32_SYSENTER_ESP (0&#215;175) sera quant à lui chargé dans ESP</li>
<li>IA32_SYSENTER_EIP (0&#215;176) sera chargé dans EIP</li>
</ul>
<p>Ainsi, ces trois registres définissent tout ce qu&rsquo;il faut pour exécuter un bout de code en mode en mode noyau, puisque CS (et donc ses deux premiers bits, encodant le CPL) sera changé.</p>
<p>Ces registres ne sont pas lisibles en mode utilisateur ; il faut impérativement être en ring 0 pour les lire. Pour cela, utilisons Windbg, le débogueur noyau de Microsoft. Il se télécharge <a href="http://www.microsoft.com/whdc/devtools/debugging/installx86.mspx#a">ici</a>, vous trouverez également des informations pour la configuration des symboles sur <a href="http://www.microsoft.com/whdc/devtools/debugging/debugstart.mspx#a">cette page</a>.</p>
<p>Lançons Windbg en local kernel debugging (Cltrl K puis local). Dans l&rsquo;invite de commande, tapons :</p>
<pre>lkd&gt; rdmsr 174
msr[174] = 00000000`00000008
lkd&gt; rdmsr 176
msr[176] = 00000000`80541520</pre>
<p>Nous voyons donc que IA32_SYSENTER_CS = 0&#215;8 (donc avec un niveau de privilège à 0) et IA32_SYSENTER_EIP = 80541520. Au passage, on notera que cette adresse est supérieur à 80000000, c&rsquo;est à dire en mode noyau, puisque Windows divise l&rsquo;espace d&rsquo;adressage de tout processus en 2 : 2 Go pour l&rsquo;utilisateur (de 00000000 à 7FFFFFFF) et 2 Go pour le noyau (de 80000000 à FFFFFFFF). Regardons ce qui se trouve en 80541520 :</p>
<pre>lkd&gt; u 80541520 80541520+100     //desassemble les 100 premiers octets
nt!KiFastCallEntry:
80541520 b923000000      mov     ecx,23h
80541525 6a30            push    30h
80541527 0fa1            pop     fs
80541529 8ed9            mov     ds,cx
8054152b 8ec1            mov     es,cx
...
80541600 8b3f            mov     edi,dword ptr [edi]
80541602 8b1c87          mov     ebx,dword ptr [edi+eax*4]
80541605 2be1            sub     esp,ecx
80541607 c1e902          shr     ecx,2
8054160a 8bfc            mov     edi,esp
8054160c 3b3534215680    cmp     esi,dword ptr [nt!MmUserProbeAddress (80562134)]
80541612 0f83a8010000    jae     nt!KiSystemCallExit2+0x9f (805417c0)
80541618 f3a5            rep movs dword ptr es:[edi],dword ptr [esi]
8054161a ffd3            call    ebx</pre>
<p>On voit que la fonction s&rsquo;appelle <code>KiFastCallEntry</code> et se trouve dans le module nt qui correspond à ntoskrnl.exe, un des exécutables du noyau.</p>
<p>Après plusieurs vérifications de paramètres, la fonction <code>KiFastCallEntry</code> charge une table dans EDI (après les &laquo;&nbsp;&#8230;&nbsp;&raquo;). Il s&rsquo;agit de la SSDT (Service System Dispatch Table), une table très importante du noyau qui a un rôle similaire à la table d&rsquo;interruptions (IDT). Elle sert à dispatcher les appels systèmes vers la bonne fonction. Le symbole correspondant à la SSDT se nomme <code>KiServiceTable</code>, et est exporté par le noyau. Voici le début de son contenu :</p>
<pre>lkd&gt; dds KiServiceTable
80504450  805a4614 nt!NtAcceptConnectPort
80504454  805f0adc nt!NtAccessCheck
80504458  805f4312 nt!NtAccessCheckAndAuditAlarm
8050445c  805f0b0e nt!NtAccessCheckByType
80504460  805f434c nt!NtAccessCheckByTypeAndAuditAlarm</pre>
<p>Souvenez-vous : juste avant le SYSENTER, on a placé le numéro d&rsquo;appel système dans EAX. Ce registre va être utilisé ici afin de servir d&rsquo;index dans cette table et de trouver le pointeur de la fonction à appeler. L&rsquo;instruction suivante multiplie justement EAX par 4 (la taille d&rsquo;une entrée dans la table) et l&rsquo;ajoute à l&rsquo;adresse de début de la table contenu dans EDI. Le résultat est place dans EBX. Et quelques instructions plus loin, on trouve&#8230; un call EBX <img src='http://www.segmentationfault.fr/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Faisons le calcul nous même : nous connaissons le numéro d&rsquo;appel système (0&#215;74) :</p>
<pre>lkd&gt; dds KiServiceTable+0x74*4
80504620  8057a182 nt!NtOpenFile</pre>
<p>Bingo, voila la fonction <code>NtOpenFile</code> : c&rsquo;est elle qui sera exécutée lors du call EBX.</p>
<h3>Retour en userland</h3>
<p>Notre reversing peut s&rsquo;arrêter ici. Nous n&rsquo;allons pas désassembler cette fonction car ce n&rsquo;est pas le but de l&rsquo;article. Concernant le retour de l&rsquo;appel système, on peut, en désassemblant la suite de <code>KiFastCallEntry</code> , voir que la fonction <code>KiServiceExit</code> va être appelée. A son tour, elle appelle une des deux fonctions <code>KiSystemCallExit</code> ou <code>KiSystemCallExit</code>2. La première revient en mode utilisateur en utilisant l&rsquo;instruction IRET, et l&rsquo;autre le fait en utilisant SYSEXIT (c&rsquo;est celle là qui sera appelée dans notre cas). Cette instruction rebascule en userland et restaure EIP. On se retrouve alors dans <code>ZwOpenFile</code>, de ntdll.</p>
<h3>Conclusion</h3>
<p>Au travers de cet article, nous avons tracé l&rsquo;exécution d&rsquo;un programme utilisateur afin de comprendre comment il passe en mode noyau afin d&rsquo;exécuter des instructions privilégiées.  Nous avons vu comment fonctionne l&rsquo;instruction SYSENTER et le rôle de la SSDT.</p>
<p>On peut alors imaginer plusieurs hooks possibles exploitant ce schéma de fonctionnement. Au niveau utilisateur, on peut hooker l&rsquo;entrée de l&rsquo;IAT correspondant à la fonction à appeler. En mode noyau, il y a plusieurs possibilités. La première serait de remplacer l&rsquo;adresse de la fonction à appeler dans la SSDT par une autre fonction qui appelle l&rsquo;originale et effectue un filtrage en entrée et en sortie. Une autre serait de modifier carrément le registre IA32_SYSENTER_EIP en le faisant pointer sur une autre routine de traitement. L&rsquo;équivalent pour les vieilles versions de Windows utilisant les interruptions serait de modifier le registre IDTR contenant l&rsquo;adresse de la table des interruptions, ou bien de hooker l&rsquo;entrée 2E dans cette table. Enfin, une dernière solution, radicale mais fonctionnelle, serait de faire un hook inline de la fonction pointée, en remplaçant ses premiers octets par un call d&rsquo;une autre fonction. Comme vous le voyez, les possibilités de hook sont innombrables, les plus &laquo;&nbsp;profondes&nbsp;&raquo; étant bien souvent les plus indétectables&#8230;</p>
<h3>Références</h3>
<ul>
<li><a href="http://www.ivanlef0u.tuxfamily.org/?p=63">Ivanlef0u&rsquo;s Blog &#8211; SSDT Hooking Reinvented</a></li>
<li><a href="http://www.ivanlef0u.tuxfamily.org/?p=22">Ivanlef0u&rsquo;s Blog &#8211; SYSENTER, stepping into da ring0</a></li>
<li><a href="http://www.amazon.com/Rootkits-Subverting-Addison-Wesley-Software-Security/dp/0321294319">Rootkits &#8211; Subverting the Windows Kernel, Greg Hoglund et Jamie Butler<br />
</a></li>
<li><a href="http://download.intel.com/design/processor/manuals/253668.pdf">Intel 64 and IA-32 Architectures Software Developer’s Manual, Volume 2B</a>, Chapitre 4.1, Instruction SYSENTER</li>
<li><a href="http://download.intel.com/design/processor/manuals/253668.pdf">Intel 64 and IA-32 Architectures Software Developer’s Manual, Volume 3A</a>, Chapitre 4.5</li>
<li><a href="http://www.microsoft.com/whdc/devtools/debugging/installx86.mspx#a">Debugging Tools for Windows</a></li>
<li><a href="http://www.microsoft.com/whdc/devtools/debugging/debugstart.mspx#a">Debugging Tools and Symbols: Getting Started</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.segmentationfault.fr/reversing/plongeon-dans-les-appels-systemes-windows/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
	</channel>
</rss>
