<?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; x86</title>
	<atom:link href="http://www.segmentationfault.fr/tag/x86/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>RDTSC hooking sous Linux : théorie et pratique</title>
		<link>http://www.segmentationfault.fr/securite-informatique/rdtsc-hooking-linux/</link>
		<comments>http://www.segmentationfault.fr/securite-informatique/rdtsc-hooking-linux/#comments</comments>
		<pubDate>Tue, 14 Jul 2009 12:30:35 +0000</pubDate>
		<dc:creator>Emilien Girault</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Reverse Engineering]]></category>
		<category><![CDATA[Sécurité informatique]]></category>
		<category><![CDATA[hacking]]></category>
		<category><![CDATA[ring 0]]></category>
		<category><![CDATA[x86]]></category>

		<guid isPermaLink="false">http://www.segmentationfault.fr/?p=688</guid>
		<description><![CDATA[L&#8217;architecture x86 possède des subtilités parfois méconnues de beaucoup de développeurs. En effet, il existe une instruction assez spéciale, RDTSC, qui renvoie le nombre de cycles d&#8217;horloge depuis le démarrage du processeur. En 2007, un chercheur d&#8217;IBM présente au Black Hat une technique de hook basée sur cette instruction. En effet, il se trouve qu&#8217;il [...]]]></description>
			<content:encoded><![CDATA[<p>L&rsquo;architecture x86 possède des subtilités parfois méconnues de beaucoup de développeurs. En effet, il existe une instruction assez spéciale, RDTSC, qui renvoie le nombre de cycles d&rsquo;horloge depuis le démarrage du processeur. En 2007, un chercheur d&rsquo;IBM <a href="https://www.blackhat.com/presentations/bh-usa-07/Yason/Whitepaper/bh-usa-07-yason-WP.pdf">présente </a>au Black Hat une technique de hook basée sur cette instruction. En effet, il se trouve qu&rsquo;il existe un flag dans le registre de contrôle CR4 permettant de désactiver cette instruction en ring 3, et de déclencher une exception #GP (int 13) lors de son appel. Via un hook de l&rsquo;IDT par un driver codé maison, il devient donc possible de détourner les appels ring 3 à RDTSC, de filtrer les résultats et imaginer toutes sortes de choses. D&rsquo;autant plus que RDTSC est couramment utilisée dans des application ayant trait à la sécurité, comme les méthodes d&rsquo;anti-debugging ou de génération de nombres aléatoires&#8230;</p>
<p><span id="more-688"></span></p>
<p>Pour cet article, on se propose d&rsquo;écrire un driver qui effectuera ce hook et qui détournera RDTSC afin de rendre les valeurs 11223344 et 55667788 respectivement dans EAX et EDX lorsqu&rsquo;on l&rsquo;appelle. Je présente en premier lieu la théorie nécessaire pour l&rsquo;attaque, puis décris comment l&rsquo;implémenter sous Linux. Enfin, je détaille une difficulté majeure à laquelle on peut faire face sur les distributions récentes telles qu&rsquo;ArchLinux : le flag TIF_NOTSC.</p>
<h3>L&rsquo;instruction RDTSC et le flag TSD</h3>
<p>RDTSC signifie &laquo;&nbsp;ReaD TimeStamp Counter&nbsp;&raquo;, autrement dit elle permet de lire le compteur de temps du processeur, incrémenté à chaque cycle d&rsquo;horloge. Ce compteur n&rsquo;est autre que le MSR IA32_TIME_STAMP_COUNTER (cf <a href="http://www.intel.com/products/processor/manuals/">manuel 3B d&rsquo;Intel</a>, section 18.11). Celui-ci fait 64 bits et est retourné dans EDX et EAX lors de l&rsquo;appel à RDTSC. Les applications classiques s&rsquo;en servent généralement pour :</p>
<ul>
<li>Effectuer des mesures de performance (benchmark) sans passer par les fonctions du noyau</li>
<li>Générer des nombres pseudo-aléatoires, à cause du caractère à priori non prévisible de ce compteur (surtout des bits de poids faible)</li>
<li>Détecter des débogueur en mesurant des deltas entre deux instructions fixes ; si un débogueur est présent et qu&rsquo;un breakpoint a été posé (ou que le mode step-by-step a été utilisé), le temps écoulé sera beaucoup plus long donc il est facile à l&rsquo;application de quitter.</li>
</ul>
<p>Cependant, ce n&rsquo;est pas exactement comme cela qu&rsquo;est décrit l&rsquo;instruction RDTSC dans le <a href="http://www.intel.com/products/processor/manuals/">manuel 2B d&rsquo;Intel</a>. En effet, on peut y lire le pseudo-code suivant :</p>
<pre>IF (CR4.TSD = 0) or (CPL = 0) or (CR0.PE = 0)
THEN EDX:EAX ? TimeStampCounter;
ELSE (* CR4.TSD = 1 and (CPL = 1, 2, or 3) and CR0.PE = 1 *)
#GP(0);
FI;</pre>
<p>On y apprend que le registre CR4 possède un flag TSD qui, s&rsquo;il est activé, provoquerait une exception lorsque RDTSC est appelé dans un ring supérieur à 0 (mode protégé). Cette exception est la General Protection Fault, notée #GP et définie à l&rsquo;index 13 dans l&rsquo;IDT (table des interruptions). L&rsquo;OS traite cette exception par une routine du noyau qui n&rsquo;est généralement pas prévue pour gérer ce cas, donc on aura par défaut droit à un crash du programme ayant appelé RDTSC. Sous Linux, cela se traduit par l&rsquo;envoi d&rsquo;un signal SIGSEGV au processus, causant une segmentation fault.</p>
<h3>IDT hooking</h3>
<p>Pour hooker RDTSC, il faut donc dans un premier temps mettre à 1 le flag TSD (bit 2) de CR4 pour déclencher une #GP. Mais ce que nous voulons, c&rsquo;est appeler notre fonction et non celle du noyau lors de l&rsquo;exception. Il va donc falloir patcher l&rsquo;IDT en remplaçant l&rsquo;adresse du handler 13 par le notre ; autrement dit, faire du IDT hooking.</p>
<p>L&rsquo;IDT recense des descripteurs explicités à la section 5.11 du manuel 3A d&rsquo;Intel. Les descripteurs suivant plus ou moins le même format :</p>
<div id="attachment_690" class="wp-caption aligncenter" style="width: 352px"><a href="http://www.segmentationfault.fr/wp-content/uploads/2009/07/int_desc.PNG"><img class="size-full wp-image-690" title="Interrupt Descriptor" src="http://www.segmentationfault.fr/wp-content/uploads/2009/07/int_desc.PNG" alt="Descripteur d'interruption" width="342" height="124" /></a><p class="wp-caption-text">Descripteur d&#39;interruption</p></div>
<p>Comme d&rsquo;habitude dans la doc Intel, le schéma se lit de bas en haut et de droite à gauche (little endian, quand tu nous tiens&#8230;). A la mode des autres descripteurs propres à l&rsquo;architecture x86, on constate que le champ Offset est découpé en deux parties : poids forts et poids faibles. C&rsquo;est ce champ qui pointe vers le handler à exécuter lors de l&rsquo;exception. Il suffit de remplacer sa valeur par l&rsquo;adresse d&rsquo;une de nos fonctions, et nous pourrons alors détourner le flux d&rsquo;exécution lors d&rsquo;un appel ring 3 à RDTSC.</p>
<h3>Trouver l&rsquo;IDT</h3>
<p>Pour pouvoir faire un hook de l&rsquo;IDT, il faut d&rsquo;abord savoir la trouver. En fait, il est nécessaire de préciser que pour les processeurs multi-coeur, il n&rsquo;y a pas une seule IDT mais plusieurs :  une par cœur. Il est donc en théorie nécessaire de hooker toutes les IDT pour éviter les problèmes. Pour connaître l&rsquo;IDT référencée par un cœur, il suffit d&rsquo;utiliser l&rsquo;instruction SIDT sur ce cœur. Cette instruction est accessible en ring 3 ; <a href="http://nibbles.tuxfamily.org/?p=372">voici un code</a> qui l&rsquo;illustre. Cependant, si vous utilisez Linux dans une machine virtuelle telle que VirtualBox, il se peut que vous rencontriez des problèmes en fonction de vos options de virtualisation. En effet, l&rsquo;instruction SIDT n&rsquo;est pas toujours bien émulée par l&rsquo;hyperviseur et il se peut que la valeur qu&rsquo;elle retourne soit erronée. Préférez-donc la solution suivante si vous tenez à faire vos tests dans une VM.</p>
<p>Même si un procceseur peut avoir plusieurs IDT, Linux n&rsquo;en utilise qu&rsquo;une car chaque cœur référence la même. Celle-ci est définie dans le noyau par le symbole <code>idt_table</code>. Pour connaître son adresse, tentez :</p>
<pre>grep idt_table /proc/kallsyms</pre>
<p>Le premier champ retourné est l&rsquo;addresse de l&rsquo;IDT. Si cela ne vous renvoie rien, il vous faudra à la place utiliser le fichier /boot/Symbol.map, généré à la compilation du noyau. Il se peut que son nom soit quelque peu différent ; par exemple sous Ubuntu il suit le format /boot/System.map-$(uname -r) alors que sous ArchLinux il s&rsquo;appelle /boot/System.map26.</p>
<h3>Conception du handler</h3>
<p>Une bonne conception du nouveau handler d&rsquo;interruption est cruciale pour éviter de rendre instable tout le système. En effet, #GP est utilisée non seulement pour RDTSC mais aussi à chaque fois qu&rsquo;un check de privilèges échoue (pour une bonne ou une mauvaise raison) dans l&rsquo;OS, autrement dit un sacré paquet de fois&#8230; Autant dire qu&rsquo;il est préférable de laisser l&rsquo;OS gérer ces cas là tout seul.</p>
<p>Pour cela, il va falloir filtrer dans un premier temps les #GP dues à RDTSC et celles dues à une autre instruction. Détecter l&rsquo;instruction fautive est facile vu que l&rsquo;EIP a été empilé ; il suffit de le regarder, d&rsquo;examiner ce qu&rsquo;il pointe et de comparer cette valeur à l&rsquo;opcode de RDTSC : 0F 31, soit 0x310F en mot de 16 bits little endian. Si cela ne correspond pas, on saute sur le handler de base de l&rsquo;OS pour ne pas tout crasher.</p>
<p>Ce n&rsquo;est pas tout : les programmes ring 3 de l&rsquo;OS aussi utilisent RDTSC. Si nous leur rendons des valeurs comme 0&#215;11223344, ils risquent d&rsquo;avoir un comportement plutôt imprévisible, surtout s&rsquo;ils s&rsquo;en servent comme base de temps. J&rsquo;ai d&rsquo;ailleurs testé sous Linux ; Cron a segfaulté instantanément et la machine est devenue inutilisable en quelques secondes.</p>
<p>Bref, il faut se débrouiller pour rendre la bonne valeur à ces programmes. La solution est d&rsquo;émuler RDTSC dans le driver, et de transmettre les résultats dans EAX et EDX au ring 3. Mais comment savoir quand retourner les bonnes et valeurs et les fakes ? La solution la plus simple qui m&rsquo;est venue à l&rsquo;esprit est d&rsquo;utiliser le PIDs du processus courant, en supposant que l&rsquo;on connaisse le PID à hooker. Pour transmettre au driver le PID du processus en question, on peut utiliser des IOCTLs, justement prévues pour la communication ring 3 &#8211; ring 0. Une fois que l&rsquo;on a le PID, il suffit de consulter le PID courant et on peut savoir si on doit émuler RDTSC ou forger les valeurs.</p>
<h3>Récupération du PID courant</h3>
<p>Nous avons quasiment tout ce qu&rsquo;il faut pour implémenter cette attaque sous Linux. La seule chose qu&rsquo;il nous manque, c&rsquo;est un moyen dé récupérer le PID du processus courant lorsque l&rsquo;on est dans un handler d&rsquo;interruption. Après lecture en diagonale du chapitre 7 d&rsquo;<a href="http://books.google.fr/books?id=h0lltXyJ8aIC&amp;dq=understanding+linux+kernel&amp;printsec=frontcover&amp;source=bn&amp;hl=fr&amp;ei=RopbSsO8A4z0nQOdwfXdCQ&amp;sa=X&amp;oi=book_result&amp;ct=result&amp;resnum=4">Understanding The Linux Kernel 3rd edition</a>, on constate qu&rsquo;il existe une macro nommée <code>current</code> qui permet de récupérer un pointeur vers le descripteur de processus courant. Après avoir testé cette macro, je me suis rendu compte qu&rsquo;elle ne marche en fait pas dans le contexte d&rsquo;un handler d&rsquo;interruption. Il faut utiliser à la place la fonction <code>current_thread_info()</code> qui marche à tous les coups. A partir de là, récupérer le PID est très simple, via l&rsquo;expression suivante : <code>current_thread_info()-&gt;task-&gt;pid</code>.</p>
<h3>Implémentation 1</h3>
<p>Nous pouvons maintenant implémenter l&rsquo;attaque. Je l&rsquo;ai réalisé sans problèmes particulier sur une Ubuntu 9.04 avec un noyau 2.6.28, sur processeur AMD dualcore. Les sources sont disponibles plus bas ; voici les points principaux.</p>
<pre>//Typedefs
typedef unsigned char u_int8;
typedef unsigned short u_int16;
typedef unsigned int u_int32;
typedef unsigned long long int u_int64;

/**
 * An IDT entry. Cf Intel SDM 3A
 */
typedef struct {
 u_int16 low_offset;
 u_int16 selector;
 u_int8 unused_lo;
 u_int8 segment_type:4;
 u_int8 system_segment_flag:1;
 u_int8 DPL:2;
 u_int8 P:1;
 u_int16 hi_offset;
} __attribute__((packed)) IDTENTRY_ST, *P_IDTENTRY_ST;</pre>
<p>Dans un premier temps, on déclare la structure d&rsquo;un descripteur d&rsquo;interruption. On fera particulièrement attention à bien spécifier <code>__attribute__((packed))</code> pour spécifier au compilateur de ne pas faire de padding entre les champs. La fonction effectuant le hook est ci-après :</p>
<pre>//Interrupt handlers
u_int32 old_int_handler, new_int_handler2;

void HookOneIDT (P_IDTENTRY_ST _p_IDT, u_int32 _interrupt_number,
                 u_int32* _old_address, u_int32 _new_address)
{
 asm("cli\n\t");

 *_old_address =  ((_p_IDT[_interrupt_number].hi_offset &lt;&lt; 16)
                | (_p_IDT[_interrupt_number].low_offset));
 _p_IDT[_interrupt_number].hi_offset = (_new_address &gt;&gt; 16) &amp; 0xFFFF;
 _p_IDT[_interrupt_number].low_offset = (_new_address &amp; 0xFFFF);

 asm("sti\n\t");
}</pre>
<p>Rien de particulier ici, à part une désactivation temporaires des interruptions. D&rsquo;ailleurs, pour être plus rigoureux, il aurait fallu les désactiver sur tous les cœurs, mais comme cette fonction sera appelée avec <code>interrupt_number = 13</code>, qui n&rsquo;est de toutes façon pas masquable, il n&rsquo;y a pas de risque.</p>
<p>Le nouveau handler d&rsquo;interruption est codé à part dans un fichier assembleur. Il s&rsquo;agit en fait d&rsquo;un squelette qui sauvegarde le contexte et appelle une fonction C, pour des raisons de commodité :</p>
<pre>.globl interrupt_handler

//The interrupt handler.
//This function must be naked. Since it's not possible with gcc on x86 platforms, we put it in a separate asm file.
interrupt_handler: 

 //Save registers
 pusha
 pushf

 //Call our hook function and  the parameter
 //Since convention call of my_func_handler is fastcall, parameter has to be in %ecx
 mov %esp, %ecx
 call my_func_handler

 //Check the return value
 cmp $1, %eax

 //If 1, throw the exception away
 je  my_exit

 //Otherwise, restore registers
 popf
 popa

 //Jump to the original handler
 jmpl * old_int_handler

my_exit:

 //Restore registers
 popf
 popa

 //Pop interrupt error code
 add $4, %esp

 //Return from interrupt
 iret</pre>
<p>La fonction appelée, <code>my_func_handler</code>, doit déterminer la nature de l&rsquo;exception et la traiter si besoin en détournant RDTSC. On utilise son code de retour pour savoir si l&rsquo;on repasse la main au handler par défaut de Linux, ou si on se contente de retourner en userland.</p>
<pre>//Opcode for RDTSC : 0F 31 =&gt; 31 OF in little endian
#define RDTSC_OPCODE 0x310F

//Size of RDTSC instruction
#define RDTSC_SIZE   2

/**
 * Interrupt stack structures
 */
typedef struct
{
 u_int32 edi;
 u_int32 esi;
 u_int32 ebp;
 u_int32 esp;
 u_int32 ebx;
 u_int32 edx;
 u_int32 ecx;
 u_int32 eax;
} PUSHA_ST, *P_PUSHA_ST;

typedef struct
{
 u_int32 error_code;  // !! Check Intel Manuals to see if the error code is present or not
 u_int32 eip;
 u_int32 cs;
 u_int32 eflags;
 u_int32 esp;
 u_int32 sp;
} INT_STACK_HARD_ST, P_INT_STACK_HARD_ST;

typedef struct
{
 u_int32              eflags;

 PUSHA_ST             pusha_st;
 INT_STACK_HARD_ST    int_stack_hard_st;

} MY_INT_STACK_ST, *P_MY_INT_STACK_ST;

/**
 * Return current PID
 */
unsigned int GetCurrentPID (void)
{
 // !!! The 'current' macro doesn't work in interrupt context !
 // !!! We have to use current_thread_info()-&gt;task instead
 return current_thread_info()-&gt;task-&gt;pid;
}

/**
 * Function called by the interrupt handler.
 *  !! WARNING !! Don't call printk() inside, or the kernel will freeze !
 *
 * @param stack pointer to the stack
 * @return 0 if this is a normal #GP exception,
 * 1 if it is due to our RDTSC hook
 */
u_int32  __attribute__((__fastcall__))
         my_func_handler (P_MY_INT_STACK_ST stack)
{
 //nb_interrupts++;+
 asm volatile("lock incl nb_interrupts\n\t");

 //Detect if the instruction that triggered the exception is RDTSC
 if(* (u_int16*) stack-&gt;int_stack_hard_st.eip == (u_int16) RDTSC_OPCODE)
 {
 //Check who is executing RDTSC
 if(GetCurrentPID() == pid_to_hook)
 {
 //Change EAX and EDX with magic values
 stack-&gt;pusha_st.eax = 0x11223344;
 stack-&gt;pusha_st.edx = 0x55667788;
 }
 else
 {
 //Perform a normal call to RDTSC
 RDTSC_ST rdtsc;
 RDTSC(&amp;rdtsc);

 stack-&gt;pusha_st.eax = rdtsc.eax;
 stack-&gt;pusha_st.edx = rdtsc.edx;
 }

 //Increment EIP
 stack-&gt;int_stack_hard_st.eip += RDTSC_SIZE;

 return 1;
 }
 else
 {
 return 0;
 }

}</pre>
<p>Il y a plusieurs détails qui ont leur importance. D&rsquo;une part,on définit des structures correspondant à l&rsquo;état de la pile lors de l&rsquo;appel à cette fonction. Cela inclut les registres généraux pushés par PUSHA ainsi que les valeurs pushés automatiquement par le processeur. Il faut faire attention à bien inverser leur ordre relativement aux specifications d&rsquo;Intel, vu que la pile croît des addresses hautes vers les basses. On récupère l&rsquo;EIP empilé, on déréférence ce pointeur et on compare le mot de 16 bits avec l&rsquo;opcode de RDTSC renversé (vu qu&rsquo;il se trouve en mémoire, donc en little-endian). On émule RDTSC su besoin, et on n&rsquo;oublie pas d&rsquo;incrémenter EIP afin de sauter par dessus l&rsquo;instruction lors du retour. On notera que le debug de cette fonction n&rsquo;est pas trivial, car il est impossible d&rsquo;utiliser des fonctions comme <code>printk()</code> à l&rsquo;intérieur.</p>
<p>Voici désormais la partie relative aux IOCTLs. Je n&rsquo;ai pas détaillé cette partie précédemment car elle fait plutôt partie d&rsquo;un choix d&rsquo;implémentation.</p>
<pre>#include &lt;linux/ioctl.h&gt;

//The device name in /proc/devices
#define DEVICE_NAME        "rdtsc_exploit"

//The name of the device file in /dev
#define DEVICE_FILE_NAME   "/dev/rdtsc_exploit"

//IOCTL command codes
#define IOCTL_SET_PID    _IOWR(0, 0, unsigned int)

//Device major and minor numbers
static dev_t g_device_num;

//Count the number of hooked interrupts
extern volatile unsigned int nb_interrupts;

//The file_operation structure, to link the device
//to the appropriate handlers
static struct file_operations g_fops = {
 .owner   = THIS_MODULE,
 .ioctl   = my_ioctl,
};

//Char device structure
static struct cdev g_device;</pre>
<p>Sous Linux, pour pouvoir communiquer avec un module en utilisant des IOCTLs, il faut créer un périphérique virtuel en mode caractère (char device) et lui assigner un handler l&rsquo;ioctl. Ce device possèdera un numéro majeur dynamiquement alloué par le noyau. Pour le numéro mineur, nous choisissons simplement 0. Une fois ces ressources allouées, nous enregistrons le device ce qui a pour effet de le faire apparaître dans /proc/devices. Tout ce procédé est fort bien décrit aux chapitres 3 et 6 de <a href="http://lwn.net/Kernel/LDD3/">Linux Device Drivers, 3rd edition</a>, livre libre que je vous conseille vivement.</p>
<pre>/**
 * Create the device
 */
int create_device (void)
{
 //Allocate the device major and minor
 if(alloc_chrdev_region(&amp;g_device_num, 0, 1, DEVICE_NAME))
 {
 printk(KERN_INFO "ERROR: alloc_chrdev_region FAILED\n");
 return -1;
 }

 //Initialise the device
 cdev_init(&amp;g_device, &amp;g_fops);

 //Fill in some fields (optional)
 g_device.owner = THIS_MODULE;
 g_device.ops = &amp;g_fops;

 //Register the device into the kernel
 if(cdev_add(&amp;g_device, g_device_num, 1))
 {
 printk(KERN_INFO "ERROR: cdev_add FAILED\n");
 return -1;
 }

 printk(KERN_INFO "Device registrated successfully - name = %s, "
                  "major = %d, minor = %d\n", DEVICE_NAME,
                  MAJOR(g_device_num), MINOR(g_device_num));

 return 0;
}

/**
 * Delete the device
 */
void delete_device (void)
{
 //Unregister the device
 cdev_del(&amp;g_device);

 //Unregister the device number
 unregister_chrdev_region(g_device_num, 1);
}</pre>
<p>Ces deux fonctions réalisent la création et la suppression du device.</p>
<p>Pour manipuler le flag TSD de CR4, on cree les fonctions suivantes :</p>
<pre>//Flag of CR4 that disable RDTSC in userland
#define FLAG_DISABLE_USER_RDTSC 0x4

/**
 * Get CR4 value
 */
u_int32 GetCR4 (void)
{
 u_int32 res = 0;

 asm volatile (
 "push %%eax\t\n"
 "mov %%cr4, %%eax\t\n"
 "mov %%eax, %0\t\n"
 "pop %%eax\t\n"
 : "=m"(res));

 return res;
}

/**
 * Set CR4 value
 */
void SetCR4 (u_int32 _new_cr4)
{
 asm volatile(
 "push %%eax\t\n"
 "mov %0, %%eax\t\n"
 "mov %%eax, %%cr4\t\n"
 "pop %%eax\t\n"
 : : "m" (_new_cr4));
}

/**
 * Enable userland calls to RDTSC
 */
void EnableUserRDTSC (void)
{
 SetCR4(GetCR4() &amp; ~FLAG_DISABLE_USER_RDTSC);
}

/**
 * Disable userland calls to RDTSC
 */
void DisableUserRDTSC (void)
{
 SetCR4(GetCR4() | FLAG_DISABLE_USER_RDTSC);
}</pre>
<p>On notera au passage la syntaxe assez inhabituelle de l&rsquo;assembleur inline de GCC, notemment les doubles % nécessaires puisque l&rsquo;on utilise des références (%0), ainsi que les \n\t en fin de ligne. Et bien entendu, les arguments inversés par rapport à la syntaxe officielle d&rsquo;Intel.</p>
<p>Lors du chargement du driver, il suffira de hooker l&rsquo;IDT et de positionner le flag CR4.TSD. Cependant, cette dernière opération doit être faite sur tous les coeurs. On utilisera donc la macro <code>on_each_cpu()</code>.</p>
<pre>//Hook the General Protection Fault handler (0x0D)
#define INTERRUPT_VECTOR_TO_HOOK 0x0D

#include &lt;linux/module.h&gt;  /* Needed by all modules */
#include &lt;linux/kernel.h&gt;  /* Needed for KERN_ALERT */
#include &lt;linux/init.h&gt;     // Needed for the macros

#include "../include/defines.h"
#include "hook.h"
#include "device.h"

static int module_load(void)
{
 Hook();
 create_device();

 //Must return 0, otherwise the module is not loaded
 return 0;
}

static void module_unload(void)
{
 delete_device();
 UnHook();
}  

module_init(module_load);
module_exit(module_unload);

/**
 * Hook
 */
void Hook ()
{
 //Get the IDT address (all CPUS use the same)
 P_IDTENTRY_ST pIDT = GetIDTSoft();

 printk(KERN_INFO "interrupt_handler = %08x\n", (u_int32) interrupt_handler);

 //Hook interrupt handler
 HookOneIDT(pIDT, INTERRUPT_VECTOR_TO_HOOK,
            &amp;old_int_handler, (u_int32) interrupt_handler);

 //Hook RDTSC
 on_each_cpu(DisableUserRDTSC, 0, 0);
}

/**
 * Unhook
 */
void UnHook ()
{
 //Unhook RDTSC
 on_each_cpu(EnableUserRDTSC, 0, 0);

 //Unhook interrupt handler
 HookOneIDT(GetIDTSoft(), INTERRUPT_VECTOR_TO_HOOK,
            &amp;new_int_handler2, old_int_handler);
}</pre>
<p>Dans mon prototype, je récupère l&rsquo;adresse de l&rsquo;IDT en userland dans le Makefile&#8230;</p>
<pre>IDT_ADDRESS = "0x`grep idt_table /boot/System.map-2.6.28-11-generic
               | cut -d ' ' -f 1`"</pre>
<p>&#8230; que je passe en paramètre à GCC lors de la compilation avec le flag -D. Le module la récupère comme une constante pré-processeur :</p>
<pre>/**
 * Get a pointer to the IDT - the soft way.
 * Works perfectly in VMs, but we either have to hardcode the IDT offset,
 * or read it from userland ('grep idt_table /proc/kallsyms'
 * or 'grep idt_table /boot/System.map').
 */
P_IDTENTRY_ST GetIDTSoft (void)
{
 P_IDTENTRY_ST pIDT = 0;

 pIDT = (P_IDTENTRY_ST) IDT_ADDRESS;

 return pIDT;
}</pre>
<p>En userland, il faudra transmettre le PID à hooker au device, ce qui se fait par le code suivant :</p>
<pre>#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;unistd.h&gt;

#include &lt;fcntl.h&gt;
#include &lt;sys/ioctl.h&gt;

#include "defines.h"

int main(int ac, char **av)
{

 //int i;
 int fd;
 int pid_to_hook;

 if(ac != 2)
 {
 printf("Usage: set_pid pid\n");
 printf("Set the pid to hook.\n\n");
 exit(0);
 }

 pid_to_hook = atoi(av[1]);

 if(pid_to_hook &lt;= 0)
 {
 fprintf(stderr, "Error, pid must be &gt; 0.\n");
 exit(1);
 }

 //Open the device in order to communicate with the driver
 fd = open(DEVICE_FILE_NAME, O_RDONLY);

 if(fd == -1)
 {
 printf("Error: %s does not exist!\n", DEVICE_FILE_NAME);
 exit(1);
 }

 //Send IOCTLs to the driver to set the pid do hook
 if(ioctl(fd, IOCTL_SET_PID, pid_to_hook))
 {
 fprintf(stderr, "Error setting the pid.\n");
 }
 else
 {
 printf("pid set successfully.\n");
 }

 //Close the device
 close(fd);

}</pre>
<p>Enfin, la routine de traitement de l&rsquo;IOCTL qui sert à récupérer le PID dans le module est relativement simple :</p>
<pre>/**
 * IOCTL handler
 */
int my_ioctl (struct inode * _inode, struct file * _file,
              unsigned int _ioctl_num, unsigned long _ioctl_param)
{
 struct task_struct *task;

 switch(_ioctl_num)
 {
 //Set the PID
 case (IOCTL_SET_PID):

 pid_to_hook = (unsigned int) _ioctl_param;

 break;

 default:
 printk(KERN_INFO "rdtsc_exploit: ERROR: Unsupported ioctl code: "
                  "%08x.\n", _ioctl_num);
 }
 return 0;
}</pre>
<h3>Test</h3>
<p>Après avoir compilé le tout, on charge le module :</p>
<pre># insmod module/rootkit.ko
# grep rdtsc_exploit /proc/devices
250 rdtsc_exploit
# mknod /dev/rdtsc_exploit c 250 0</pre>
<p>On lance le programme exécutant RDTSC dans un shell à part :</p>
<pre>$ exe/rdtsc/rdtsc
Press &lt;Enter&gt; to call rdtsc. Press q to quit.

RDTSC result (edx : eax) = (0000126d : 8c1cc9a2)

RDTSC result (edx : eax) = (0000126d : a38e75be)</pre>
<p>Puis on envoie son PID au module avec :</p>
<pre># exe/set_pid/set_pid $(pidof rdtsc)
pid set successfully.</pre>
<p>Et on revient au terminal précédent, en constatant que le hook fonctionne bien :</p>
<pre>RDTSC result (edx : eax) = (55667788 : 11223344)

RDTSC result (edx : eax) = (55667788 : 11223344)

RDTSC result (edx : eax) = (55667788 : 11223344)</pre>
<p>On n&rsquo;oubliera pas de décharger le module avec :</p>
<pre># rmmod rootkit
# rm /dev/rdtsc_exploit</pre>
<h3>Problème avec ArchLinux</h3>
<p>En testant l&rsquo;implémentation précédente avec deux distributions ArchLinux de noyaux 2.6.29 et 2.6.30, j&rsquo;ai constaté qu&rsquo;ell ne marchait tout simplement pas. En faisant plusieurs tests, je constate que le handler de #GP est bien hooké, mais RDTSC ne l&rsquo;est pas du tout car le programme de test affiche toujours des valeurs normales. J&rsquo;affiche la valeur de CR4.TSD à plusieurs reprises, et je vois que de temps en temps, il repasse à 0, ce qui expliquerait pourquoi RDTSC n&rsquo;est pas détournée.</p>
<p>Après plusieurs recherches, je tombe sur <a href="http://blog.cr0.org/2009/05/time-stamp-counter-disabling-oddities.html">ce blog</a>, qui pointe du doigt quelques bizarreries du noyau Linux concernant justement le flag TSD. Apparemment, il serait possible de l&rsquo;activer ou non pour certains processus seulement. Il s&rsquo;agit du Thread Information Flag TIF_NOTSC définit dans le fichier arch/x86/include/asm/thread_info.h du noyau. Ce flag est plus ou moins l&rsquo;équivalent du flag TSD, mais dans le contexte de chaque processus. Il est possible de le définir avec l&rsquo;appel système <a href="http://www.kernel.org/doc/man-pages/online/pages/man2/prctl.2.html">prctl</a> en utilisant l&rsquo;option PR_SET_TSC. La valeur PR_TSC_ENABLE revient à positionner TSD = 0, tandis que PR_TSC_SIGSEGV est équivalent à TSD = 1.</p>
<p>Ces flags existent déjà dans les noyaux 2.6.28 d&rsquo;Ubuntu 9.04 ; je n&rsquo;ai pas encore bien saisi pourquoi ceuxi-ci sont effectivement appliqués sur ArchLinux. Le blog cité précédemment parle de l&rsquo;option de configuration CONFIG_SECCOMP du noyau, présente sur ArchLinux, mais visiblement désactivée ia le flag TIF_SECCOMP qui vaut 0 pour tous les processus. Je vais continuer mes recherches de ce côté&#8230; Si toutefois vous avez des explications, je suis preveur <img src='http://www.segmentationfault.fr/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> .</p>
<h3>Implémentation 2</h3>
<p>En attendant, il reste tout de même effectuer le hook de RDTSC. Il suffit de positionner le flag TIF_NOTSC du processus en question à PR_TSC_SIGSEGV. Cela peut se faire en appelant prctl, mais cette technique n&rsquo;est pas vraiment convenable car un hook se doit d&rsquo;être extérieur au processus. La technique consiste donc à émuler le fonctionnement de cet appel système au sein de notre module. Il nous suffit de parcourir la liste chainée des processus, d&rsquo;isoler celui qui a le bon PID, et à positionner son flag. Cela revient à modifier la fonction <code>my_ioctl()</code> comme ceci :</p>
<pre>/**
 * IOCTL handler
 */
int my_ioctl (struct inode * _inode, struct file * _file,
              unsigned int _ioctl_num, unsigned long _ioctl_param)
{
   struct task_struct *task;

   switch(_ioctl_num)
   {
      //Set the PID
      case (IOCTL_SET_PID):

         pid_to_hook = (unsigned int) _ioctl_param;
         printk(KERN_INFO "rdtsc_exploit: pid_to_hook = "
                          "%d.\n", pid_to_hook);

         for_each_process(task) {
            if(task-&gt;pid == pid_to_hook){
               test_and_set_ti_thread_flag(task_thread_info(task), TIF_NOTSC);
               printk("TIF_NOTSC set for process %d\n", task-&gt;pid);
            }
         }

      break;

      default:
         printk(KERN_INFO "rdtsc_exploit: ERROR: Unsupported ioctl code: "
                          "%08x.\n", _ioctl_num);
   }
   return 0;
}</pre>
<p>La macro <code>for_each_process()</code> définie dans linux/sched.h permet d&rsquo;itérer très simplement sur les threads du système. On utilise la fonction <code>test_and_set_ti_thread_flag()</code> afin de positionner le flag TIF_NOTSC du thread en question. On notera qu&rsquo;il n&rsquo;y a même plus besoin de modifier à la main CR4 à l&rsquo;initialisation.</p>
<h3>Sources</h3>
<p><a href="http://www.segmentationfault.fr/wp-content/uploads/2009/07/rdtsc_exploit.tar.gz">Téléchargez les sources</a></p>
<p>Les sources incluent l&rsquo;implémentation 2, sachant que celle-ci fonctionne aussi bien sur les deux distributions que j&rsquo;ai testées (Ubuntu et ArchLinux). Les lignes spécifiques à la 1ère implémentation sont commentées, donc vous pouvez toujours jouer avec et voir le résultat que vous obtenez.</p>
<p>Pour compiler, invoquez simplement <code>make</code> à la racine de rdtsc_exploit. Si jamais cela ne compile pas, éditez le fichier module/Makefile, et indiquez le bon chemin vers votre fichier /boot/System.map. Vérifiez également que le fichier module/handler.S a bien un S majuscule concernant son extension.</p>
<p>Les fichiers fournis sont organisés comme ceci :</p>
<ul>
<li>module/ contient les sources du module</li>
<li>exe/ contient deux sources d&rsquo;exécutables :
<ul>
<li>rdtsc : programme de test exécutant RDTSC à chaque appui sur une touche. Il contient aussi un fichier de test du noyau,  disable-tsc-test.c, que j&rsquo;ai jugé intéressant de garder pour des tests. A compiler séparément.</li>
<li>set_pid : programme prenant en paramètre le PID de rdtsc et l&rsquo;envoyant au module par ioctl</li>
</ul>
</li>
<li>scripts/ contient trois scripts permettant d&rsquo;automatiser le chargement du module et la création du device. load_hook.sh et unload_hook.sh appellent en réalité load.sh, capable de charger/décharger un module et créer/détruire son device.</li>
</ul>
<h3>Applications</h3>
<p>Pour terminer, voici quelques possibilités offertes par le hook de RDTSC :</p>
<ul>
<li><strong>Empoisonnement des générateurs de nombres pseudo-aléatoires</strong> : Certaines applications utilisent RDTSC comme source d&rsquo;aléa, pour générer des valeurs pseudo-aléatoires qui peuvent par exemple être utilisées pour la génération de clé de chiffrement. En forçant à RDTSC à renvoyer des valeurs bien précise, on peut injecter des valeurs bien précises dans l&rsquo;algorithme de génération et pouvoir prédire plus facilement son résultat.</li>
<li><strong>Anti-anti-debuging</strong> : Comme dit au premier paragraphe, une technique d&rsquo;anti-debug consiste à utiliser RDTSC pour estimer le temps passé entre deux instructions et le comparer à une valeur seuil. Une technique d&rsquo;anti-anti-debug peut donc être de hooker RDTSC et de retourner des valeurs plausibles à l&rsquo;application, en masquant le fait que celle-ci est en train de se faire déboguer. C&rsquo;est précisément ce que fait le plugin <a href="http://www.openrce.org/downloads/details/241/Olly_Advanced">Olly Advanced</a> d&rsquo;OllyDbg.</li>
<li><strong>Communication offusquée entre une application et un driver</strong> : Puisqu&rsquo;avec cette technique RDTSC est exécutée en ring 3 et provoque une exception #GP en ring 0, c&rsquo;est un moyen de donner la main à un driver afin qu&rsquo;il effectue des opérations &laquo;&nbsp;ni vu ni connu&nbsp;&raquo;, dans le sens ou il n&rsquo;y a aucun appel explicite vers fonction noyau dans l&rsquo;application ring 3.</li>
</ul>
<h3>Conclusion</h3>
<p>Cette technique n&rsquo;est pas nouvelle, mais encore assez peu connue (enfin sans doute pas des reversers :p). Cependant, elle peut se révéler très intéressantes dans de multiples occasions. Si je devais donner un conseil, ce serait d&rsquo;éviter de l&rsquo;utiliser en ring 3, pour deux raisons principales de sécurité :</p>
<ul>
<li>Il existe des générateurs aléatoires reconnus comme fiables, il est donc préférable de les utiliser plutôt que de se faire son propre algorithme.</li>
<li>L&rsquo;OS fournit généralement des appels systèmes permettant d&rsquo;appeler RDTSC en ring 0 et de retourner sa valeur (cf <code>NtQueryPerformanceCounter()</code> sous Windows). Comme l&rsquo;appel est en ring 0, la méthode de hook décrite précédemment ne marche plus.</li>
</ul>
<h3>Références</h3>
<ul>
<li><a href="https://www.blackhat.com/presentations/bh-usa-07/Yason/Whitepaper/bh-usa-07-yason-WP.pdf">The Art of Unpacking</a>, Marc Vincent Yason, BlackHat 2007</li>
<li><a href="http://www.intel.com/products/processor/manuals/">Intel Software Devloper&rsquo;s Manuals</a> 2B, 3A</li>
<li><a href="http://books.google.fr/books?id=h0lltXyJ8aIC&amp;dq=understanding+linux+kernel&amp;printsec=frontcover&amp;source=bn&amp;hl=fr&amp;ei=RopbSsO8A4z0nQOdwfXdCQ&amp;sa=X&amp;oi=book_result&amp;ct=result&amp;resnum=4">Understanding the Linux kernel, 3rd edition</a>, <span>Daniel Pierre Bovet, Marco Cesati, </span>O&rsquo;Reilly</li>
<li><a href="http://nibbles.tuxfamily.org/?p=372">Nibbles &#8211; SMP : IDT et GDT</a>, j0rn</li>
<li><a href="http://lwn.net/Kernel/LDD3/">Linux Device Drivers, 3rd edition</a>, Jonathan Corbet, Alessandro Rubini, Greg Kroah-Hartman, O&rsquo;Reilly</li>
<li><a href="http://blog.cr0.org/2009/05/time-stamp-counter-disabling-oddities.html">CR0&prime;s blog : Time-stamp counter disabling oddities in the Linux kernel</a></li>
<li><a href="http://www.kernel.org/doc/man-pages/online/pages/man2/prctl.2.html">Man prctl</a></li>
</ul>
<div id="_mcePaste" style="overflow: hidden; position: absolute; left: -10000px; top: 11920px; width: 1px; height: 1px;">
<pre><strong><strong>PR_SET_TSC</strong></strong></pre>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.segmentationfault.fr/securite-informatique/rdtsc-hooking-linux/feed/</wfw:commentRss>
		<slash:comments>4</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>
	</channel>
</rss>
