Tunnez votre BSOD (Blue Screen Of Death)

30 avril 2009 – 20:58

Tous les développeurs de drivers et les reversers kernel vous le diront : quand on touche au noyau Windows d’un peu trop près, on a le droit à un écran bleu de la mort, ou BSOD (Blue Screen Of Death). C’est justement parce que je me suis pris un tas de BSOD que j’ai commencé à en avoir marre de voir des écrans bleus partout et à me poser des questions existentielles du genre : pourquoi l’é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’un octet en mémoire kernel. Matériel requis : une deuxième machine (une VM fait très bien l’affaire), Windbg, IDA PRO (facultatif) et quelques neurones.

Le pourquoi du BSOD

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’assez sérieux s’est produit au niveau du noyau. Cela peut être aussi bien matériel que logiciel (parfois les deux). Par exemple : un driver tente d’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’erreur Windows (ou une segmentation fault sous Linux). En mode noyau, l’équivalent est le BSOD sous Windows (Kernel Panic sous Linux) et vous n’avez pas d’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 :

* (unsigned int *) 0 = 0;

Ce code essaye d’écrire 0 à l’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’autres manières de provoquer un écran bleu, mais cela dépasse le cadre de cet article.

Reversing de la routine du BSOD

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’aperçois que la VM s’est arrêtée dans la routine KeBugCheckEx. J’ouvre ntoskrnl.exe avec IDA PRO et désassemble cette routine.

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

Cette fonction est très courte, elle ne fait qu’empiler des paramètres et appeler la routine KeBugCheck2. Cette dernière est par contre très longue. Comme mon but n’est pas de la reverser intégralement mais juste isoler la partie qui m’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’avoir lieu.

Couleur de fond

A peu près au milieu de la fonction, on tombe sur ce code :

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)

Apparemment, on a affaire à des appels de fonction gérant l’affichage. On commence par faire un reset de l’affichage, puis on appelle la routine InvbSolidColorFill. Les paramètres qui lui sont passés sont les suivants (je rappelle que l’ordre est inversé, selon la convention d’appel utilisé) :

  • deux arguments égaux à 0 (si on cherche un peu plus haut on trouve un xor esi,esi donc esi = 0)
  • 0x27F = 639
  • 0x1DF = 479
  • 4

Etant astucieux, on se rend compte que 639 = 640 – 1 et 479 = 480 – 1. Hors, 640 x 480 correspond exactement à la résolution de l’é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’é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’écran bleu est bleu ! :)

Si on désassemble la fonction InvbSolidColorFill, on se rend compte qu’assez rapidement, celle-ci appelle la fonction VidSolidColorFill. Il s’agit d’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’ai préféré jeter un coup d’oeil aux sources de ReactOS, qui est nettement plus lisible. Pour ceux qui ne connaissent pas, il s’agit d’un projet visant à recoder Windows en Open Source. Voici le code de la fonction dans ReactOS (qui doit être très similaire dans Windows) :

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 <= Bottom; y++)
	{

            for (x = Left; x <= Right; x++)
            {
                //
                // Draw the pixel
                //
                VidpSetPixel(x, y, Color);
            }
	}
}

Comme vous pouvez le voir, on peut difficilement faire plus simple. Une fonction chargée de fixer la couleur d’un pixel est appelée dans une double boucle, ce qui a pour effet de remplir l’écran.

Couleur du texte

Revenons au code de KeBugCheck2. Juste après le code qui remplit l’écran, on a ceci :

push    0Fh
call    _InbvSetTextColor@4 ; InbvSetTextColor(x)

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 ?

Patching à chaud pour changer les couleurs

Que diriez vous de personnaliser la couleur du BSOD sur votre système ? Je n’ai personnellement jamais aimé le bleu, je préfère le rouge (plus sexy pour un message d’erreur !). Quand au texte, du jaune devrait faire l’affaire…

Pour des raisons de sécurité, je vous déconseille fortement d’éditer l’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’un simple reboot efface les modifications. D’autre part, comme on s’apprête à débugger Windows et à le faire planter, il faut opérer sur une deuxième machine. Munissez vous d’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 cet article d’0vercl0ck qui vous expliquera comment tout configurer comme il faut. Prêt ? C’est parti !

Une fois Windows démarré, freezez le avec Ctrl+Pause sous Windbg. Commençons par localiser ou se trouve l’appel à InvbSolidColorFill dans KeBugCheck2. Sous IDA, on effectue un petit calcul d’offset pour savoir ou se trouve le push 4 par rapport au début de KeBugCheck2. Chez moi, j’ai un offset de 0x60D. Dans Windbg, je tape donc u KeBugCheck2+0x60d. Cependant, ça ne tombe pas juste ; j’atterris après ce bout de code (facile de le constater avec IDA). C’est sans doute à cause du fait que j’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’instruction qui m’intéresse :

kd> 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)

Il suffit de patcher la valeur située à l’adresse 80533434 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) :

kd> eb 80533434 01

Vérifions cela en désassemblant à nouveau :

kd> 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)

Super, ça semble avoir marché. Faisons de même avec la couleur du texte !

kd> u KeBugCheck2+0x5fd
nt!KeBugCheck2+0x5fd:
80533447 6a0f            push    0Fh
80533449 e8d1a0ffff      call    nt!InbvSetTextColor (8052d51f)

Je veux du jaune, je remplace donc le 0x0F par un 0x0B soit 0b1011 (mélange clair de vert et rouge).

kd> eb 80533448 0B
kd> u KeBugCheck2+0x5fd
nt!KeBugCheck2+0x5fd:
80533447 6a0b            push    0Bh
80533449 e8d1a0ffff      call    nt!InbvSetTextColor (8052d51f)

Nikel. Il ne reste plus qu’à provoquer un écran bleu ! Pour ce faire à partir de Windbg, c’est très simple : il suffit de mettre eip à 0. On tape juste r eip = 0 et on débloque la machine avec la commande g. 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’opération de diagnostique de crash, et retapez g pour voir apparaître votre beau BSOD personnalisé. En ce qui me concerne, c’est plutôt un RSOD (Red Screen Of Death) !

Red Screen Of Death

Red Screen Of Death

C’est ti pas mignon tout ça ? Pour ceux qui par malheur verraient leur écran s’afficher puis disparaître à cause d’un reboot instantané, refaites la manip en n’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 « redémarrer automatiquement ».

Conclusion

J’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’il est également possible de modifier le texte affiché sur l’écran… Après, vous pouvez intégrer tout ça dans un driver qui se chargera d’effectuer le patching lui-même. C’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’ai entendu dire que certains avaient réussi à charger et afficher une image de bière lors d’un crash, si ça peut vous donner des idées… ;)

Références

  1. 6 réponses à “Tunnez votre BSOD (Blue Screen Of Death)”

  2. Délicieusement inutile… ;)

    Par Léo le 11 mai 2009

  3. Tu as plus simple pour déclencher un écran bleu manuellement qu’un driver foireux :
    http://www.pctools.com/guides/registry/detail/856/

    Par gojul le 30 mai 2009

  4. @Léo : non pas forcément. Les Linuxiens personnalisent leur GRUB, les Windowsiens leur BSOD :D

    Par HIK3 le 28 juillet 2009

  5. C’est vrai, je comprend. On voit le GRUB à chaque fois qu’on démare son PC sous linux. Après tout, le BSOD sous Windows, c’est à peu près la même chose… ;)

    Par Léo le 29 juillet 2009

  6. Il y bien plus simple pour mettre un BSOD dans sa vm, suffit de taper .crash dans windbg

    Par Edward le 20 novembre 2009

  7. Merci du tuyau, je connaissais pas :)

    Par Emilien Girault le 20 novembre 2009

Écrire un commentaire