<?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; Linux</title>
	<atom:link href="https://www.segmentationfault.fr/categories/linux/feed/" rel="self" type="application/rss+xml" />
	<link>https://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>https://www.segmentationfault.fr/securite-informatique/rdtsc-hooking-linux/</link>
		<comments>https://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='https://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>https://www.segmentationfault.fr/securite-informatique/rdtsc-hooking-linux/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>InjecSO : Injection de .so sous Linux</title>
		<link>https://www.segmentationfault.fr/projets/injecso-injection-de-so-sous-linux/</link>
		<comments>https://www.segmentationfault.fr/projets/injecso-injection-de-so-sous-linux/#comments</comments>
		<pubDate>Sat, 14 Feb 2009 20:37:52 +0000</pubDate>
		<dc:creator>Emilien Girault</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Projets]]></category>
		<category><![CDATA[Reverse Engineering]]></category>
		<category><![CDATA[Sécurité informatique]]></category>
		<category><![CDATA[hacking]]></category>

		<guid isPermaLink="false">http://www.segmentationfault.fr/?p=521</guid>
		<description><![CDATA[L&#8217;injection de librairie dynamique est une technique principalement orientée reverse-engineering qui consiste à introduire et exécuter du code dans un processus actif du système d&#8217;exploitation. Le code en question s&#8217;exécute alors dans le contexte du processus cible, et peut accéder aux mêmes ressources. Attention, je parle bien d&#8217;injection dynamique, c&#8217;est à dire qu&#8217;elle peut avoir [...]]]></description>
			<content:encoded><![CDATA[<p>L&rsquo;injection de librairie dynamique est une technique principalement orientée reverse-engineering qui consiste à introduire et exécuter du code dans un processus actif du système d&rsquo;exploitation. Le code en question s&rsquo;exécute alors dans le contexte du processus cible, et peut accéder aux mêmes ressources. Attention, je parle bien d&rsquo;injection dynamique, c&rsquo;est à dire qu&rsquo;elle peut avoir lieu n&rsquo;importe quand après le lancement du processus, et non pas juste à son lancement comme avec la technique utilisant <code>LD_PRELOAD</code> (qui en plus ne fonctionne pas avec les programmes setuid).</p>
<p>Après avoir lu de nombreux articles sur l&rsquo;injection de DLL sous Windows, j&rsquo;ai commencé à en avoir assez à ne pas trouver d&rsquo;équivalent sous Linux.  Le peu d&rsquo;information que j&rsquo;ai trouvé pour Linux datent d&rsquo;au moins 5 ans, et se révèlent non adaptables aux distributions récentes. C&rsquo;est pourquoi je me suis lancé dans l&rsquo;idée de réaliser ce genre d&rsquo;outil moi même. Ainsi, après m&rsquo;être heurté à plusieurs obstacles, j&rsquo;ai finalement réussi à développer un outil fonctionnel : <strong>InjecSO</strong>. <span id="more-521"></span></p>
<p>Cet outil marche très bien sur ma machine qui est une Ubuntu 8.10, avec un noyau 2.6.27, sans outil de protection de la mémoire comme PaX (la technique utilisée ne marchera pas si la pile n&rsquo;est pas exécutable), et avec une Libc de version 2.8.90. Je n&rsquo;ai pas eu l&rsquo;occasion de tester sur d&rsquo;autres machines, mais je pense qu&rsquo;il devrait fonctionner aussi bien, les deux seuls obstacles étant l&rsquo;implémentation de la librairie standard et les éventuels patches appliqués au noyau.</p>
<h3>InjecSO in a nutshell</h3>
<p>Voyons dans un premier temps comment utiliser InjecSO dans un cas simple.</p>
<h4>Téléchargement</h4>
<p><a href="http://www.segmentationfault.fr/wp-content/uploads/2009/02/injecso-1-0tar.gz">Télécharger InjecSO au format tar.gz</a></p>
<h4>Présentation rapide</h4>
<p>InjecSO se présente sous la forme de deux outils.</p>
<ul>
<li><code>injecso</code> est le cœur du programme utilisé pour l&rsquo;injection de code. Il s&rsquo;agit d&rsquo;un programme écrit en C qui prend 3 arguments : le pid du processus cible, le chemin absolu de la librairie à injecter, et l&rsquo;adresse de la fonction <code>__libc_dlopen_mode()</code> dans l&rsquo;espace mémoire du processus cible. Cette dernière est une fonction spéciale de la libc qui rend l&rsquo;injection possible et est décrite précisément dans la deuxième partie de l&rsquo;article. Localiser cette adresse précise dans le processus cible est faisable mais rébarbatif ; c&rsquo;est pourquoi j&rsquo;ai développé un deuxième outil pour se faciliter la vie.</li>
<li><code>injecso.sh</code> est un script Bash qui a justement pour but de calculer cette adresse de façon automatique. Il ne prend donc que deux paramètres : le pid et le nom de la librairie à injecter. Il appelle automatiquement le programme précédent en lui fournissant le paramètre manquant. De plus, le chemin de la librairie peut être relatif car l&rsquo;outil calcule le chemin absolu automatiquement.</li>
</ul>
<p>J&rsquo;entends déjà des remarques venir :  «Pourquoi ne pas avoir tout intégré dans un seul programme ?». La réponse est simple : cela aurait été faisable, mais m&rsquo;aurait nécessité beaucoup plus de temps pour au final arriver au même résultat. Le calcul de l&rsquo;adresse nécessite d&rsquo;analyser la mémoire du processus et le code de la libc, et il se trouve qu&rsquo;il existe déjà des outils qui font cela très bien sous Linux. Ainsi, <code>injecso.sh</code> ne fait qu&rsquo;exploiter ces ressources pour calculer rapidement l&rsquo;adresse de la fonction voulue. Je suis conscient que cela a ses avantages et ses inconvénients ; en particulier, le script nécessite que vous ayez certaines dépendances d&rsquo;installées, dont <code>readelf</code> et <code>perl</code> (pour parser la sortie produite par ces outils). Je ne pense pas que cela soit une exigence trop forte, puisque ces outils sont en général présents sur beaucoup de systèmes, et sont au pire facilement installables surtout si votre distribution comprend un système de paquets.</p>
<h4>Compiler le programme</h4>
<p>Décompressez l&rsquo;archive et utilisez le Makefile pour compiler l&rsquo;exécutable :</p>
<pre>$ tar xzvf injecso-1.0tar.gz
$ make</pre>
<h4>Exemples d&rsquo;utilisation</h4>
<p>L&rsquo;outil est capable d&rsquo;injecter n&rsquo;importe quelle librairie dans n&rsquo;importe quel type de processus, pour peu que vous ayez les droits suffisants (n&rsquo;espérez pas injecter du code dans un processus appartenant à root si vous ne l&rsquo;êtes pas vous-même). Comme exemple, prenons un processus simple tel que l&rsquo;éditeur de texte <code>vi</code> et une librairie dynamique qui affiche &laquo;&nbsp;Hello World!&nbsp;&raquo; nommée <code>libhelloworld.so</code>. Voici le code de <code>libhelloworld.c</code> :</p>
<pre>#include &lt;stdlib.h&gt;
#include &lt;stdio.h&gt;

void __attribute__ ((constructor)) hello_world(void);

void hello_world(void){
  printf("Hello World!n");
}</pre>
<p>La directive <code>__attribute__ ((constructor))</code> indique au compilateur qu&rsquo;il devra ajouter cette fonction à la liste des fonctions à appeler au chargement de la libairie. Si vous êtes familier du monde Windows, c&rsquo;est plus ou moins l&rsquo;équivalent de la directive <code>DllMain()</code>. Compilez le code avec :</p>
<pre>$ gcc libhelloworld.c -o libhelloworld.so -shared -fPIC</pre>
<p>Vous pouvez placer cette librairie ou bon vous semble, le plus simple étant de la mettre dans le même dossier qu&rsquo;InjectSO. Lancez <code>vi</code>, récupérez son pid avec la commande <code>pidof vi</code> et lancez l&rsquo;injection !</p>
<pre>$ ./injecso.sh $(pidof vi) ./libhelloworld.so
[+] Found __libc_dlopen_mode at 0xb7e44210
[+] Launching: injecso 9796 libhelloworld.so 0xb7e44210
[+] Attaching...
[+] Waiting for process...
[+] Copying shellcode to 0xbfa0b02e...
[+] Setting eip and esp...
[+] Detaching...</pre>
<p>Regardez à présent la fenête où vous avez lancez <code>vi</code>&#8230; Vous devriez voir un petit <code>Hello World!</code> en haut de la console <img src='https://www.segmentationfault.fr/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Vous pouvez aussi bien injecter votre librairie dans des plus gros processus comme par exemple Firefox — pour les applications graphiques, le message s&rsquo;affichera dans la console ayant l&rsquo;ancé le programme —, et complexifier votre librairie. Les possibilités n&rsquo;ont de limite que votre imagination : création d&rsquo;un client ou serveur, dump de la mémoire, log d&rsquo;événements, hooking de fonctions de la PLT&#8230; Notez que votre librairie peut à son tour appeler d&rsquo;autres librairies (dynamiques ou pas) sans aucun problème.</p>
<h4>Remarque concernant les threads</h4>
<p>Il est important de noter qu&rsquo;InjecSO ne crée aucun thread dans le processus cible. A la différence des outils d&rsquo;injection de code sous Windows qui effectuent un appel à <code>CreateRemoteThread()</code>, le code injecté est directement exécuté dans le contexte courant du processus, qui est sauvegardé avant l&rsquo;injection puis restauré. Si le code de votre librairie est gros ou effectue des opérations gourmandes en cycles CPU, le processus cible en sera ralenti. Cette remarque &lsquo;est particulièrement valable si votre librairie effectue des entrées/sorties disque ou réseau (si vous comptez coder un serveur&#8230;). C&rsquo;est pourquoi dans ces cas il est préférable de créer un nouveau thread, en utilisant par exemple la libairie <code>pthread</code>.</p>
<h3>Détails d&rsquo;implémentation</h3>
<p>Cette section décrit en détails comment InjecSO est implementé. Je commence par faire un tour d&rsquo;horizon des techniques utilisées, puis je détaille le code de l&rsquo;outil.</p>
<h4>ptrace() et dlopen()</h4>
<p>Il faut reconnaître que sous Linux, l&rsquo;attirail disponible pour injecter des librairies est très limité, en tout cas beaucoup plus que sous Windows. A vrai dire, il n&rsquo;y a tout simplement pas d&rsquo;appel de fonction tel que  <code>OpenProcess()</code> et <code>CreateRemoteThread()</code>, donc manipuler un processus devient beaucoup plus délicat. Le seul outil dont nous disposons est <code>ptrace()</code>.  Il s&rsquo;agit d&rsquo;un appel système qui est utilisé majoritairement pour le débogage de processus. Il est relativement simple à utiliser ; on commence par s&rsquo;attacher au processus à tracer, qui se bloque. On peut alors récupérer son état, ses registres et sa mémoire d&rsquo;un processus, et les modifier. Une fois les opérations de tracage terminées, on se détache, et le processus reprend son cours. Nous allons voir comment InjecSO utilise cet appel système par la suite ; en attendant je vous renvoie au manuel si vous voulez en savoir plus.</p>
<p>Pour charger une librairie dynamique sous Unix, on utilise la fonction <code>dlopen()</code> qui est plus ou moins l&rsquo;équivalent de <code>LoadLibraryA()</code> sous Windows. Cette fonction prend en paramètre le nom de la librairie à charger, ainsi qu&rsquo;un flag qui indique la manière dont doivent être résolus les symboles. Cela n&rsquo;a guère d&rsquo;importance pour notre application, aussi nous spécifierons arbitrairement que les symboles doivent tous être résolus au chargement.</p>
<h4>Un premier problème : dlopen()</h4>
<p>A première vue, <code>ptrace()</code> et <code>dlopen()</code> constituent de bonnes bases pour notre injection. Seulement, ce n&rsquo;est pas si simple : il se trouve que la fonction <code>dlopen()</code> n&rsquo;est pas une fonction standard, mais se situe dans une librairie séparée nommée <code>libdl</code>&#8230; qui n&rsquo;est pas toujours chargée par tous les processus. Autrement dit, un processus lambda ne possède pas forcément le moyen de charger une librairie dynamique, car la fonction qui permet de charger des librairies se trouve justement dans une librairie dynamique !</p>
<p>C&rsquo;est là qu&rsquo;on peut se dire «OK, mais alors comment fait un processus quand il veut charger une librairie ?». Réponse : c&rsquo;est le programmeur qui spécifie au moment de la compilation et de l&rsquo;édition des liens qu&rsquo;il vaut lier son programme avec <code>libdl</code> qui sera alors chargée à son lancement. Sauf que dans notre cas, nous ne sommes pas forcément le développeur du programme cible, et nous ne voulons de toute manière pas modifier le code du programme&#8230;</p>
<h4>La solution : __libc_dlopen_mode()</h4>
<p>En cherchant de la documentation sur les détails d&rsquo;implémentation de <code>dlopen()</code>, j&rsquo;ai finalement trouvé un papier datant de 2003 [1] qui explique que les fonctions de <code>libdl</code> sont pour la plupart des <em>stubs</em> qui appellent des fonctions qui se trouvent en réalité dans la libc. Rappelons que la libc est chargée dans quasiment tous les processus, donc cette découverte parraît très intéressante. Selon le papier, <code>dlopen()</code> appelle en fait <code>_dl_open()</code>. Après vérification, je me rend compte que ce n&rsquo;est pas/plus le cas, du moins sur ma machine. Mais il semblerait qu&rsquo;il y ait une fonction similaire avec un nom assez proche :  <code>__libc_dlopen_mode()</code>. Voici la mise en évidence en images :</p>
<pre>$ pidof bash
10712 9864 8911
$ cat /proc/10712/maps | grep libc
b7d61000-b7eb9000 r-xp 00000000 08:08 51577   /lib/tls/i686/cmov/libc-2.8.90.so
b7eb9000-b7ebb000 r--p 00158000 08:08 51577   /lib/tls/i686/cmov/libc-2.8.90.so
b7ebb000-b7ebc000 rw-p 0015a000 08:08 51577   /lib/tls/i686/cmov/libc-2.8.90.so
$ readelf -s -D /lib/tls/i686/cmov/libc-2.8.90.so | grep dlopen
 2188 744: 0011d210   156    FUNC GLOBAL DEFAULT  11 __libc_dlopen_mode
 2188 966: 0011d210   156    FUNC GLOBAL DEFAULT  11 __libc_dlopen_mode</pre>
<p>Dans un premier temps, on récupère les pid d&rsquo;un processus quelconque, ici Bash, on obtient le chemin complet de la libc (ici <code>/lib/tls/i686/cmov/libc-2.8.90.so</code>) et on utilise <code>readelf</code> pour afficher les symboles dynamiques de la librairie. Résultat : il y a bien une fonction qui a l&rsquo;air similaire à <code>dlopen()</code>.</p>
<p>Mais que fait cette fonction, et quel est son prototype ? Pour cela, le plus simple est de récupérer le code source de la librairie standard et de le parcourir. C&rsquo;est ce que j&rsquo;ai donc fait, et je suis finalement tombé sur cela :</p>
<pre>extern void *__libc_dlopen_mode  (__const char *__name, int __mode);</pre>
<p>Comparons cela au prototype original de <code>dlopen()</code> :</p>
<pre>void *dlopen(const char *filename, int flag);</pre>
<p>Hum&#8230; cela paraît très similaire, pour ne pas dire identique ! Je m&rsquo;empresse donc de coder un petit programme en C qui appelle cette fonction, et m&rsquo;aperçois alors que la libraire est bien chargée, comme avec <code>dlopen()</code> ! Super, nous pouvons donc nous contenter de cette fonction.</p>
<h4>Deuxième problème : randomization des adresses</h4>
<p>Ok, nous avons maintenant un nom de fonction pour charger la librairie. Cependant, pour pouvoir l&rsquo;appeler dans le processus cible, il nous faut son adresse. Comment l&rsquo;obtenir, sachant qu&rsquo;elle se trouve dans l&rsquo;espace mémoire du processus cible ? Nous savons que la fonction réside dans la libc ; pour déterminer son adresse nous pouvons utiliser le même programme que précédamment, <code>readelf</code>. Lors de notre dernière commande, cet outil nous a indiqué que la fonction se situe à l&rsquo;offset <code>0x0011d210</code> dans l&rsquo;image mémoire de la librairie. Comment obtenir l&rsquo;adresse globale à partir de cet offset ? Simplement en additionnant cet offset avec l&rsquo;adresse de base à laquelle est chargée la libc. Mais quelle est l&rsquo;adresse de base de la libc ? Observons le résultat de la commande <code>ldd</code> :</p>
<pre>$ ldd /bin/bash | grep libc
        libc.so.6 =&gt; /lib/tls/i686/cmov/libc.so.6 (0xb7ee4000)</pre>
<p>Nous utilisons cette commande sur Bash, et filtrons la sortie pour n&rsquo;afficher que ce qui nous intéresse. Notez que le chemin de la libc est ici différent de précédamment, mais cela n&rsquo;a pas d&rsquo;importance pour ce que je cherche à illustrer ici.  Ce qu&rsquo;il faut noter ici, c&rsquo;est que <code>ldd</code> nous affiche que la libc est chargée à l&rsquo;adresse <code>0xb7ee4000</code>. Mais il y a un léger hic&#8230; En effet, si on relance la même commande une deuxième fois&#8230;</p>
<pre>$ ldd /bin/bash | grep libc
        libc.so.6 =&gt; /lib/tls/i686/cmov/libc.so.6 (0xb7ef0000)</pre>
<p>Oups. L&rsquo;adresse a changée ! Pourquoi donc ? Parce que depuis relativement récamment (quelques années), Linux a introduit un système de randomization des adresses. Autrement dit, l&rsquo;adresse à laquelle une librairie est chargée n&rsquo;est pas constante et varie pour toutes les exécutions d&rsquo;un programme donné. Autant dire que cela ne va pas nous faciliter la tâche pour calculer l&rsquo;adresse de  <code>__libc_dlopen_mode()</code>&#8230;</p>
<h4>Solution : /proc/&lt;pid&gt;/maps</h4>
<p>Tout n&rsquo;est pas perdu. Linux possède un système de fichier virtuel, nommé <code>/proc/</code>, qui va nous permettre de nous en sortir. En effet, lorsqu&rsquo;un processus est créé, Linux crée un répertoire <code>/proc/&lt;pid&gt;/</code> (<code>&lt;pid</code>&gt; étant le nom du processus en question) contenant plein d&rsquo;informations. En particulier, le fichier <code>/proc/&lt;pid&gt;/maps</code> contient la liste de toutes les sections mappées mémoires dans l&rsquo;espace du processus. Voyons ce que nous pouvons obtenir&#8230;</p>
<pre>$ pidof bash
10712 9864 8911
$ cat /proc/10712/maps | grep libc
b7d61000-b7eb9000 r-xp 00000000 08:08 51577  /lib/tls/i686/cmov/libc-2.8.90.so
b7eb9000-b7ebb000 r--p 00158000 08:08 51577  /lib/tls/i686/cmov/libc-2.8.90.so
b7ebb000-b7ebc000 rw-p 0015a000 08:08 51577  /lib/tls/i686/cmov/libc-2.8.90.so</pre>
<p>On s&rsquo;aperçoit que la libc est mappée 3 fois dans l&rsquo;espace mémoire de Bash : une fois en lecture et exécution, une fois en lecture seule, et une fois en lecture et écriture. En ce qui nous concerne, nous souhaitons exécuter une fonction, donc nous avons intérêt à choisir la section exécutable. Son adresse est <code>0xb7d61000</code> ; ajoutons à cela l&rsquo;offset précédent (<code>0x0011d210</code>), et nous obtenons l&rsquo;adresse de <code>__libc_dlopen_mode()</code> ! Sa valeur n&rsquo;a pas d&rsquo;importance dans cet exemple car nous voulons juste un moyen de la calculer automatiquement pour n&rsquo;importe quel processus.</p>
<h3>Injecso.sh : calcul de l&rsquo;adresse</h3>
<p>Si nous rassemblons tout ce que nous venons de voir, nous savons désormais calculer l&rsquo;adresse de <code>__libc_dlopen_mode()</code> pour un processus donnée. Il ne reste plus qu&rsquo;à mettre tout cela dans un script ; c&rsquo;est le but d&rsquo;<code>injecso.sh</code>. Le script a volontairement été raccourci ici pour n&rsquo;afficher que les bouts intéressants.</p>
<pre>#!/bin/bash
# Param renaming
pid=$1
lib=$2</pre>
<p>Nous commençons par récupérer la ligne de <code>/proc/&lt;pid&gt;/maps</code> qui nous intéresse, et nous allons extraire d&rsquo;une part l&rsquo;adresse de base de la librairie, et son nom.</p>
<pre># Get the map of process and get the line
# that correspond to the executable section of libc
line=$(cat /proc/$pid/maps | grep "libcb" | grep r-x | head -n 1)</pre>
<pre># Extract the base address of that section and the name of the library
libc_baseaddr=$(echo "$line" | cut -d "-" -f1)                # first field
libc_name=$(echo "$line" | perl -n -e '/(S+$)/ &amp;&amp; print $1') # last field</pre>
<p>Nous utilisons ensuite <code>readelf </code>pour extraire l&rsquo;offset de <code>__libc_dlopen_mode()</code>.</p>
<pre># Use readelf to find the offset of __libc_dlopen_mode
dlopen_offset=$(readelf -s -D $libc_name | grep __libc_dlopen_mode |
  head -n 1 | perl -n -e '/^s*S+s+S+s+(S+)/ &amp;&amp; print $1') # 3rd field</pre>
<p>L&rsquo;adresse est obtenue en additionnant cet offset avec l&rsquo;adresse de base.</p>
<pre># Compute the actual addresses
dlopen_addr=$(expr $(printf "%d" 0x$libc_baseaddr) 
  + $(printf "%d" 0x$dlopen_offset))
dlopen_addr_hex=$(printf "0x%x" $dlopen_addr)

echo "[+] Found __libc_dlopen_mode at $dlopen_addr_hex"
echo "[+] Launching: injecso $pid $lib $dlopen_addr_hex"</pre>
<p>Il ne reste plus qu&rsquo;a passer les paramètres adéquats à notre injecteur.</p>
<pre># Launch InjecSO
$(dirname $0)/injecso $pid $(pwd)/$lib $dlopen_addr_hex</pre>
<p>Voila, nous venons de faire le tour du script. Plutôt simple, la difficulté majeure étant de parser la sortie des différents outils utilisés.</p>
<h4>Structure de l&rsquo;injection</h4>
<p>Bon, ce n&rsquo;est pas tout, mais nous n&rsquo;avons toujours pas vu comment se déroule l&rsquo;injection ! C&rsquo;est là que nous allons utiliser <code>ptrace()</code>&#8230; Comme je l&rsquo;ai dit précédemment, cet appel système permet de modifier la mémoire et les registres d&rsquo;un processus cible. Comment l&rsquo;utiliser à fin d&rsquo;injecter un appel à <code>__libc_dlopen_mode()</code> ? La solution la plus simple est d&rsquo;injecter un shellcode qui va réaliser l&rsquo;appel. Il y a plusieurs possibilités pour ce faire ; j&rsquo;ai choisi la plus simple c&rsquo;est à dire d&rsquo;injecter le shellcode dans la pile. Notez que cela requirt que la pile soit exécutable, ce qui n&rsquo;est pas toujours le cas.</p>
<p>De plus, il va nous falloir faire très attention car nous ne voulons pas crasher le processus cible. Comme notre shellcode sera susceptible de s&rsquo;exécuter n&rsquo;importe quand dans le processus, il doit rester &laquo;&nbsp;invisible&nbsp;&raquo;. Autrement dit, il faut que l&rsquo;état du processeur avant et après l&rsquo;exécution du shellcode soit &laquo;&nbsp;quasiment&nbsp;&raquo; le même. Pour cela, nous allons devoir sauvegarder les registres avant d&rsquo;exécuter la charge utile du shellcode, et les restaurer ensuite.</p>
<p>Là encore, il y a plusieurs solutions pour résoudre ce problème ; je vais détailler celle que j&rsquo;ai retenue pour injecSO. Voici un shema de la pile avant l&rsquo;appel au shellcode :</p>
<pre> |               |
 |               |
 |               |
 |               |
 |               |
 +---------------+
 |    donnees    | &lt;- esp
 |               |</pre>
<p>Dans ce schéma, les adresses croissent vers le bas, mais rappelez-vous que la pile croît en sens inverse. Au moment de l&rsquo;injection, nous allons effectuer les opérations suivantes en utilisant <code>ptrace()</code>:</p>
<ul>
<li>Sauvegarder l&rsquo;adresse de l&rsquo;instruction courante (contenue dans <code>eip</code>) sur la pile</li>
<li>Allouer le shellcode sur la pile</li>
<li>Faire pointer  <code>eip </code>et <code>esp</code> sur le shellcode</li>
</ul>
<p>Voici donc la pile après l&rsquo;injection :</p>
<pre> |               |
 +---------------+
 |   shellcode   | &lt;- eip, esp
 |               |
 |               |
 |               |
 +---------------+
 |  ancien eip   |
 +---------------+
 |    donnees    |
 |               |</pre>
<p>Pour que cela fonctionne, le shellcode devra avoir une structure particulière :</p>
<ul>
<li>Quelques <code>nops</code> afin de compenser un éventuel décalage dans les adresses.</li>
<li><code>pushal</code> afin de sauvegarder les registres sur la pile.</li>
<li>Charge utile du shellcode (en gros, <code>push</code> des arguments et <code>call</code><code> __libc_dlopen_mode</code> )</li>
<li><code>popal</code> pour restaurer les registres</li>
<li><code>addl $size, %esp</code> (<code>$size</code> étant la taille du shellcode) afin de repositionner <code>esp</code> sur l&rsquo;adresse de retour</li>
<li><code>ret</code> qui dépilera et restaurera l&rsquo;adresse de retour</li>
</ul>
<p>L&rsquo;injecteur va dans un premier temps générer le shellcode suivant ce modèle. Cependant, ce modèle est incomplet car le shellcode nécessite des paramètres qui ne seront connus qu&rsquo;à l&rsquo;exécution : l&rsquo;adresse de <code>__libc_dlopen_mode</code>, le nom de la librairie à injecter, ainsi que la taille du shellcode (comprenant le nom de la librairie). Ainsi, l&rsquo;injecteur va devoir compléter/patcher le shellcode à plusieurs endroits avant qu&rsquo;il soit fonctionnel.</p>
<h4>Le shellcode</h4>
<p>Voici le code du shellcode utilisé ; je pense que les commentaires sont assez explicites :</p>
<pre>  .text
  .globl shellcode_code
  .globl shellcode_code_end

shellcode_code:

  /* Some nops */
  nop
  nop
  nop
  nop
  nop
  nop
  nop

  /* Save all registers */
  pushal

  /* Get the name of the library into ebx */
  jmp       libname

call_dlopen:
  popl      %ebx

  /* 0x11111111 will be later replaced by
     the address of __lib_dlopen_mode */
  movl      $0x11111111, %eax
  pushl     $2      /* RTLD_NOW */
  pushl     %ebx    /* name of the library */
  call      *%eax   /* call __lib_dlopen_mode */

  /* Clean args on the stack */
  addl      $8, %esp

  /* Restore all registers */
  popal

  /* 0x12345678 will be later replaced by
     the size of the shellcode (+ delta) */
  addl      $0x12345678, %esp

  /* Return to where we were before */
  ret

libname:
  call call_dlopen
shellcode_code_end:
/*
 * End of shellcode
 * The string corresponding to the library name
 * will be put here later
 */</pre>
<p>Comme je le disais, le shellcode est encore incomplet. En particulier, il manque encore le nom de la librairie, qui sera ajouté à la fin. Pour récupérer son adresse relative, nous utilisons l&rsquo;astuce du <code>jmp/call</code>, assez célèbre. Concernant l&rsquo;adresse de  <code>__libc_dlopen_mode</code> et la taille du shellcode, nous laissons des offsets bidon pour le moment que nous allons patcher ensuite. Voila justement le début du code de l&rsquo;injecteur, qui a pour but d&rsquo;assembler/patcher le shellcode :</p>
<pre>int main(int argc, char **argv){
  char * shellcode;
  int shellcode_size, libname_size;
  char * ptr;
  int i;
  int pid;
  char * libname;
  int dlopen_addr;

  //Check parameters
  check_params(argc, argv, &amp;pid, &amp;libname, &amp;dlopen_addr);</pre>
<p>Ici, nous venons de récupérer les paramètres de l&rsquo;injecteur, c&rsquo;est à dire le pid, le nom de la librairie et l&rsquo;adresse de <code>__libc_dlopen_mode</code>. Je ne pense pas que cette fonction soit sufisamment intéressante et complexe pour être détaillée ici.</p>
<pre>  //Compute the size of the library name and deduce the shellcode size
  libname_size = strlen(libname);
  shellcode_size = (char *) shellcode_code_end - (char *) shellcode_code 
                   + libname_size + 1;

  //Allocate the shellcode buffer
  shellcode = malloc(shellcode_size);</pre>
<p>La copie du shellcode commence ici, puis le nom de la librairie y est ajouté.</p>
<pre>  //Copy the shellcode code into the buffer
  for(i = 0, ptr = (char *) shellcode_code;
      ptr != (char *) shellcode_code_end;
      ptr++, i++){
    shellcode[i] = *ptr;
  }

  //Copy the library name at the end of the shellcode
  for(ptr = libname; *ptr != 0; ptr++, i++){
    shellcode[i] = *ptr;
  }</pre>
<p>Maintenant, c&rsquo;est le moment de patcher le shellcode. Le début du shellcode ayant une taille fixe, nous connaissons précisément les offsets à patcher (il suffit d&rsquo;assembler le shellcode une première fois et de compter).</p>
<pre>  //Patch the shellcode by inserting the real address of __libc_dlopen_mode
  //(replace 0x11111111 by dlopen_addr)
  *((int *) &amp;(shellcode[12])) = dlopen_addr;

  //Patch the shellcode to include its own size
  *((int *) &amp;(shellcode[27])) = shellcode_size + DELTA_SHELLCODE_EIP_BAK;</pre>
<p>Il y a un petit détail dont je n&rsquo;ai pas parlé : l&rsquo;expérience montre que le shellcode inséré tel quel juste après l&rsquo;adresse de retour ne marche pas ; la fin du shellcode se retrouve écrasée pour une raison que je n&rsquo;ai pas encore bien comprise. Pour éviter ça, j&rsquo;ai introduit une petit décalage (delta) de quelques octets entre l&rsquo;adresse de retour et la fin du shellcode, afin de garantir qu&rsquo;elle ne sera pas touchée. Il faut juste prendre en compte ce décalage dans certains calculs, bref rien de très compliqué.</p>
<pre>  //Inject!
  inject(pid, shellcode, shellcode_size);</pre>
<p>Le shellcode est désormais prêt, place à l&rsquo;injection !</p>
<h4>L&rsquo;injection de code</h4>
<p>Voici enfin la routine qui effectue l&rsquo;injection.</p>
<pre>void inject(int pid, char * shellcode, int shellcode_size){

  long res;
  struct user_regs_struct regs;
  char * addr_shellcode;
  int i;

  //Attach to the process
  printf("[+] Attaching...n");
  res = ptrace(PTRACE_ATTACH, pid, NULL, NULL);
  if(res == -1){
    perror("Attaching");
  }</pre>
<p>Après s&rsquo;être attaché au processus, il faut impérativement l&rsquo;attendre, sans quoi il risque de ne pas être prêt.</p>
<pre>  //Wait for the process
  printf("[+] Waiting for process...n");
  waitpid(pid, NULL, 0);

  //Set option for interrupted syscalls
  res = ptrace(PTRACE_SETOPTIONS, pid, NULL, PTRACE_O_TRACESYSGOOD);
  if(res == -1){
    perror("Setting ptrace option");
  }</pre>
<p>Cette option n&rsquo;est pas obligatoire, mais est préférable dans le cas où le processus a été interompu en plein milieu d&rsquo;un appel système.</p>
<pre>  //Get the registers of the process
  res = ptrace(PTRACE_GETREGS, pid, NULL, &amp;regs);
  if(res == -1){
    perror("Getting registers");
  }</pre>
<p>Nous avons les registres du processus, nous pouvons maintenant les manipuler comme nous voulons. Nous commençons par calculer l&rsquo;adresse à laquelle le shellcode devra être chargé, puis nous sauvegardons l&rsquo;adresse de retour sur la pile</p>
<pre>  //Compute the address where the shellcode will be copied
  //We keep 4 bytes for old eip and a delta between this
  //and the end of the shellcode
  addr_shellcode = (char *) regs.esp - shellcode_size 
                    - DELTA_SHELLCODE_EIP_BAK - 4;

  //Save eip on the stack (esp orig - 4)
  res = ptrace(PTRACE_POKEDATA, pid, regs.esp-4, regs.eip);
  if(res == -1){
    perror("Saving eip");
  }</pre>
<p>Il faut maintenant copier le shellcode. Attention : lors des transferts de données, <code>ptrace()</code> copie les octets 4 par 4. Il faut donc faire attention lors de l&rsquo;itération, et ne pas oublier la fin du shellcode.</p>
<pre>  //Copy shellcode
  printf("[+] Copying shellcode to 0x%x...n", (int) addr_shellcode);
  for(i = 0; i &lt; shellcode_size/4; i++){
    res = ptrace(PTRACE_POKEDATA, pid, &amp;addr_shellcode[i*4], 
         (int) *((int*) &amp;shellcode[i*4])); //Copy 4 bytes each time
    if(res == -1){
      perror("Copying shellcode");
    }
  }
  if((shellcode_size % 4) != 0){
    res = ptrace(PTRACE_POKEDATA, pid, &amp;addr_shellcode[i*4], 
         (int) *((int*) &amp;shellcode[i*4])); //Copy the last 3- bytes if necessary
    if(res == -1){
      perror("Copying shellcode");
    }
  }</pre>
<p>Le shellcode a été copié, il ne reste plus qu&rsquo;à mettre à jour <code>esp</code> et <code>eip</code> en les faisant pointer sur le shellcode. En fait, nous décalons légèrement <code>eip</code> afin d&rsquo;être sûr de tomber au milieu des <code>nops</code>.</p>
<pre>  //Make eip and esp point to the shellcode
  printf("[+] Setting eip and esp...n");
  regs.eip = (int) addr_shellcode+2;
  regs.esp = (int) addr_shellcode;
  res = ptrace(PTRACE_SETREGS, pid, NULL, &amp;regs);
  if(res == -1){
    perror("Setting eip and esp");
  }</pre>
<p>Mission accomplie, plus qu&rsquo;à se détacher pour libérer le processus.</p>
<pre>  //Detach from the process
  printf("[+] Detaching...n");
  res = ptrace(PTRACE_DETACH, pid, NULL, NULL);
  if(res == -1){
    perror("Detaching");
  }</pre>
<p>Pour comprendre en détails les arguments de chaque appel à <code>ptrace()</code>, je vous conseille fortement de lire le manuel.</p>
<h3>Conclusion</h3>
<p>Ca y est, nous venons d&rsquo;arriver au bout de l&rsquo;injection. J&rsquo;espère que vous avez désormais une idée plus claire sur le fonctionnement général de l&rsquo;injection de code sous Linux. Cela m&rsquo;aura pris 3 jours pour arriver à un prototype fonctionnel, mais je ne suis vraiment pas déçu compte tenu du résultat. Si vous avez des remarques, n&rsquo;hésitez pas !</p>
<h3>Références</h3>
<ul>
<li>[1] <a href="http://www.hick.org/code/skape/papers/needle.txt">Linux X86 run-time process manipulation</a>, skape, Uninformed, 2003</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>https://www.segmentationfault.fr/projets/injecso-injection-de-so-sous-linux/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Reversing Linux : Comprendre le rôle des sections PLT et GOT dans l&#8217;édition de liens dynamique</title>
		<link>https://www.segmentationfault.fr/linux/role-plt-got-ld-so/</link>
		<comments>https://www.segmentationfault.fr/linux/role-plt-got-ld-so/#comments</comments>
		<pubDate>Fri, 25 Jul 2008 09:04:58 +0000</pubDate>
		<dc:creator>Emilien Girault</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Reverse Engineering]]></category>
		<category><![CDATA[hacking]]></category>

		<guid isPermaLink="false">http://www.segmentationfault.fr/?p=41</guid>
		<description><![CDATA[J&#8217;ai récemment eu la volonté de comprendre plus en détails comment Linux parvient à résoudre les symboles (tels que les fonctions) liés dynamiquement dans les programmes. Ne disposant pas d&#8217;Internet et n&#8217;ayant pas les sources du noyau sous la main, il a fallu faire avec les moyens du bord : un éditeur de texte, gcc, [...]]]></description>
			<content:encoded><![CDATA[<p>J&rsquo;ai récemment eu la volonté de comprendre plus en détails comment Linux parvient à résoudre les symboles (tels que les fonctions) liés dynamiquement dans les programmes. Ne disposant pas d&rsquo;Internet et n&rsquo;ayant pas les sources du noyau sous la main, il a fallu faire avec les moyens du bord : un éditeur de texte, gcc, gdb, et un peu de connaissance relative à l&rsquo;édition de liens en général. Je préfère donc préciser que cet article n&rsquo;a pas pour objectif d&rsquo;être exhaustif et de décrire le fonctionnement exact de l&rsquo;édition de liens dynamique et de la résolution des symboles sous Linux. Il se contente de présenter la démarche que j&rsquo;ai eu pour comprendre les choses à ma manière. En particulier, le rôle des sections PLT et GOT sera expliqué. Si le sujet vous intéresse, vous trouverez sans doute d&rsquo;autres papiers certainement plus détaillés.<span id="more-41"></span></p>
<h3>Rappels théoriques</h3>
<p>Tout d&rsquo;abord, il convient de faire quelques rappels sur la compilation et l&rsquo;édition de liens des binaires. Quand on compile un programme qui fait appel à des fonctions situées dans d&rsquo;autres bibliothèques (telles que la librairie standard), l&rsquo;édition de liens peut être faite de deux façons différentes. La première méthode, dite <em>statique</em>, consiste à intégrer à l&rsquo;exécutable toutes les librairies dont il a besoin pour fonctionner. A l&rsquo;exécution, tous les symboles sont donc résolus, et les appels sont immédiats. Si cette méthode a été la plus utilisée dans les versions anciennes des OS, elle est toutefois largement dépassée. En effet, il s&rsquo;agit d&rsquo;un gouffre à espace disque, puisqu&rsquo;elle oblige à dupliquer chaque librairie autant de fois qu&rsquo;il y a d&rsquo;exécutables qui l&rsquo;utilisent. Les exécutables générés sont donc volumineux, puisqu&rsquo;il suffit par exemple d&rsquo;un simple appel à <code>printf()</code> pour que toute la librairie standard du C soit intégrée à l&rsquo;exécutable !</p>
<p>Depuis les versions &laquo;&nbsp;récentes&nbsp;&raquo; de Linux, c&rsquo;est la deuxième méthode d&rsquo;édition de liens, dite <em>dynamique</em>, qui est utilisée par défaut. Avec cette méthode, chaque librairie est compilée une fois pour toute dans une librairie dynamique, ou <em>partagée</em> (<em>shared</em>) ayant l&rsquo;extension .so (équivalent des .dll sous Windows). Lorsque l&rsquo;on compile un programme qui y fait référence, on y insère juste le nom du symbole (fonction ou variable) dont il a besoin, ainsi que le nom de la librairie. C&rsquo;est à l&rsquo;exécution du programme que l&rsquo;<em>éditeur de liens dynamique</em> (ou <em>dynamic linker</em>), nommé <em>ld.so</em>, charge les libraires nécessaire et effectue la résolution des symboles manquants en temps réel. C&rsquo;est donc la vitesse d&rsquo;exécution qui s&rsquo;en retrouve pénalisée, même si nous verrons que cette perte est toutefois relative car compensée par un système de mise en cache des adresses.</p>
<p>Enfin, il convient de clarifier la notion de PIC, ou <em>Position Independant Code</em>. Un code exécutable est dit PIC s&rsquo;il peut être mappé à l&rsquo;importe quelle région mémoire tout en pouvant s&rsquo;exécuter convenablement. Dans de tels exécutables, aucune adresse absolue ne doit apparaître, puisque si l&rsquo;exécutable se retrouve translaté en mémoire, les adresses absolues ne seront plus valides. Dans Linux, les librairies dynamiques sont en PIC. C&rsquo;est le linker dynamique, ld.so, qui les charge en mémoire à l&rsquo;exécution, et leur place en mémoire peut varier d&rsquo;une exécution à une autre. Ainsi, l&rsquo;adresse des fonctions de la libraire standard, telles que <code>printf()</code>, changent de place à chaque exécution. Pourtant, un programme qui utilise <code>printf()</code> n&rsquo;est compilé qu&rsquo;une seule fois. Comment les processus arrivent-ils donc à s&rsquo;exécuter tout en prenant en compte cette variation d&rsquo;adresses ? C&rsquo;est là tout l&rsquo;objectif de cet article&#8230;</p>
<h3>Un programme de test</h3>
<p>Place à la pratique ! Dans la suite, je considérerais que nous somme sur une Ubuntu Hardy (noyau 2.6.24), avec gcc 4.2.3 et gdb 6.8. Nous allons utiliser le programme en C suivant :</p>
<pre>#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;

int main(){
  char c1[] = "chaine1";
  char c2[] = "chaine2";

  int res = strcmp(c1, c2);

  printf("c1 == c2 ? %dn", res);

  return 0;
}</pre>
<p>Pour illustrer ce que nous venons de voir, compilons-le avec les deux méthodes, statique et dynamique :</p>
<pre>$ gcc -o bin_str bin_str.c
$ gcc -static -o bin_str_static bin_str.c</pre>
<p>Notez au passage que gcc linke les exécutable dynamiquement par défaut, et que l&rsquo;option -static permet de forcer l&rsquo;édition de liens statique. A l&rsquo;exécution, les deux produisent exactement le même résultat :</p>
<pre>$ ./bin_str
c1 == c2 ? -1
$ ./bin_str_static
c1 == c2 ? -1</pre>
<p>Cependant, quand on compare la taille des exécutables&#8230;</p>
<pre>$ ls -lh bin_str bin_str_static
-rwxrwxrwx 1 root root 6,6K 2008-07-21 19:12 bin_str
-rwxrwxrwx 1 root root 545K 2008-07-21 19:12 bin_str_static</pre>
<p>On constate que le binaire lié statiquement (545K) est 80 fois plus volumineux que le binaire lié dynamiquement (qui ne pèse que 6.6K) ! En effet, c&rsquo;est toute la librairie standard qui a été incorporée au binaire durant l&rsquo;édition de liens.</p>
<p>Lançons-les avec gdb et désassemblons le main de chacun :</p>
<pre>$ gdb ./bin_str_static
(gdb) disas main
Dump of assembler code for function main:
...
0x0804823b &lt;main+75&gt;:   call   0x804dd70 &lt;strcmp&gt;
...
0x08048251 &lt;main+97&gt;:   call   0x8048c70 &lt;printf&gt;
...
(gdb) quit

$ gdb ./bin_str
(gdb) disas main
Dump of assembler code for function main:
...
0x0804844f &lt;main+75&gt;:   call   0x8048364 &lt;strcmp@plt&gt;
...
0x08048465 &lt;main+97&gt;:   call   0x8048344 &lt;printf@plt&gt;
...</pre>
<p>On constate que dans le binaire lié statiquement, <code>printf()</code> et <code>strcmp()</code> ont bien été intégrées dans la région .text de l&rsquo;exécutable. Dans le binaire lié dynamiquement, les deux appels n&rsquo;ont cependant pas lieu dans la .text, mais dans une région spécifique à l&rsquo;édition de lien dynamique : la .plt. La <em>PLT</em>, pour <em>Procedure Linkage Table</em>, est une table sertvant à faire le lien avec les fonctions situées dans des bibliothèques dynamiques.</p>
<h3>La PLT et la GOT</h3>
<p>Dans la suite, on ne travaillera évidemment qu&rsquo;avec le binaire lié dynamiquement. Listons les régions actuellement mapées dans l&rsquo;espace mémoire de l&rsquo;exécutable:</p>
<pre>(gdb) info files
...
0x080482e4 - 0x08048314 is .init
0x08048314 - 0x08048374 is .plt
0x08048380 - 0x0804852c is .text
0x0804852c - 0x08048548 is .fini
0x08048548 - 0x0804856f is .rodata
0x08048570 - 0x08048574 is .eh_frame
0x08049574 - 0x0804957c is .ctors
0x0804957c - 0x08049584 is .dtors
0x08049584 - 0x08049588 is .jcr
0x08049588 - 0x08049658 is .dynamic
0x08049658 - 0x0804965c is .got
0x0804965c - 0x0804967c is .got.plt
0x0804967c - 0x08049688 is .data
0x08049688 - 0x0804968c is .bss</pre>
<p>La région .plt est donc mappée entre les adresses 0&#215;08048314 et 0&#215;08048374. Il est important de noter que cette section se trouve à des adresses fixes. On vérifie au passage que les adresses appelées par les deux calls du main (0&#215;8048364  pour <code>strcmp</code> et 0&#215;8048344 pour <code>printf</code>) appartiennent bien à cette zone. Désassemblons à présent la région .plt :</p>
<pre>(gdb) disas 0x08048314 0x08048374
Dump of assembler code from 0x8048314 to 0x8048374:
0x08048314 &lt;_init+48&gt;:  pushl  0x8049660
0x0804831a &lt;_init+54&gt;:  jmp    *0x8049664
0x08048320 &lt;_init+60&gt;:  add    %al,(%eax)
0x08048322 &lt;_init+62&gt;:  add    %al,(%eax)
0x08048324 &lt;__gmon_start__@plt+0&gt;:      jmp    *0x8049668
0x0804832a &lt;__gmon_start__@plt+6&gt;:      push   $0x0
0x0804832f &lt;__gmon_start__@plt+11&gt;:     jmp    0x8048314 &lt;_init+48&gt;
0x08048334 &lt;__libc_start_main@plt+0&gt;:   jmp    *0x804966c
0x0804833a &lt;__libc_start_main@plt+6&gt;:   push   $0x8
0x0804833f &lt;__libc_start_main@plt+11&gt;:  jmp    0x8048314 &lt;_init+48&gt;
0x08048344 &lt;printf@plt+0&gt;:      jmp    *0x8049670
0x0804834a &lt;printf@plt+6&gt;:      push   $0x10
0x0804834f &lt;printf@plt+11&gt;:     jmp    0x8048314 &lt;_init+48&gt;
0x08048354 &lt;__stack_chk_fail@plt+0&gt;:    jmp    *0x8049674
0x0804835a &lt;__stack_chk_fail@plt+6&gt;:    push   $0x18
0x0804835f &lt;__stack_chk_fail@plt+11&gt;:   jmp    0x8048314 &lt;_init+48&gt;
0x08048364 &lt;strcmp@plt+0&gt;:      jmp    *0x8049678
0x0804836a &lt;strcmp@plt+6&gt;:      push   $0x20
0x0804836f &lt;strcmp@plt+11&gt;:     jmp    0x8048314 &lt;_init+48&gt;
End of assembler dump.</pre>
<p>Globalement, on constate que la section .plt est composée de plusieurs sous-sections de taille égale (0&#215;10 soit 16 octets), que nous appellerons <em>entrées</em>. L&rsquo;entrée 0 (composée des 4 premières instructions) est un peu spéciale ; nous y reviendrons plus tard. Les autres entrées, à compter de la 1 jusqu&rsquo;à la dernière, sont composées de 3 instructions qui suivent toujours le même schéma. Un premier <code>jmp</code> a lieu, dont l&rsquo;adresse de saut n&rsquo;est pas explicitement donnée dans l&rsquo;instruction, mais par l&rsquo;intermédiaire d&rsquo;un pointeur. Par exemple, dans le cas de l&rsquo;entrée 3 correspondant à <code>printf</code>, l&rsquo;adresse de saut se trouve dans les 4 octets pointés par l&rsquo;adresse 0&#215;8049670 qui fait donc office de pointeur. On remarque que la 2ème instruction de l&rsquo;entrée 0 comporte aussi un <code>jmp</code> avec pointeur. En regardant de plus près chaque pointeur, y compris celui de l&rsquo;entrée 0 (0&#215;8049664), on s&rsquo;aperçoit qu&rsquo;ils se trouvent tous les uns à la suite des autres. En effet, on a ici : 0&#215;8049664, 0&#215;8049668, 0x804966c, 0&#215;8049670, 0&#215;8049674, et 0&#215;8049678. Pour savoir où se trouvent ces pointeurs, il suffit de regarder à nouveau la liste des sections mappées en mémoire :</p>
<pre>(gdb) info files
...
0x0804957c - 0x08049584 is .dtors
0x08049584 - 0x08049588 is .jcr
0x08049588 - 0x08049658 is .dynamic
0x08049658 - 0x0804965c is .got  &lt;=====
0x0804965c - 0x0804967c is .got.plt</pre>
<p>Ces adresses se situent donc dans la section nommée .got.plt. Les régions nommées ici .got et .got.plt font en réalité partie d&rsquo;une table, que l&rsquo;on nomme la <em>GOT</em>, pour <em>Global Offset Table</em>. Le rôle de cette table sera explicité dans la suite.</p>
<p>Affichons maintenant son contenu. Étant donné que cette région contient des pointeurs, il s&rsquo;agit de données et non d&rsquo;instructions ; pour afficher son contenu il convient donc d&rsquo;utiliser l&rsquo;instruction <code>x</code> de gdb, et non pas <code>disas</code>. De plus, la commande précédente nous indique que la section fait 0&#215;20 = 32 octets de long, soit 8 pointeurs de 4 octets.</p>
<pre>(gdb) x/8x 0x0804965c
0x804965c &lt;_GLOBAL_OFFSET_TABLE_&gt;:      0x08049588      0x00000000
                                        0x00000000      0x0804832a
0x804966c &lt;_GLOBAL_OFFSET_TABLE_+16&gt;:   0x0804833a      0x0804834a
                                        0x0804835a      0x0804836a</pre>
<p>Je rappelle que pour le moment, le programme n&rsquo;a pas encore été lancé. Le contenu de cette section est fixe, du moins avant chaque exécution. Il est tout à fait possible de le voir en utilisant d&rsquo;autres outils tels que objdump.</p>
<p>Comme nous venons de le voir, les entrées de la PLT référencent des pointeurs situés dans la GOT. Pour comprendre le rôle de ces deux tables, regardons comment se déroule un appel à <code>strcmp()</code>. Cette fonction est située dans l&rsquo;entrée 3 de la PLT :</p>
<pre>0x08048364 &lt;strcmp@plt+0&gt;:      jmp    *0x8049678
0x0804836a &lt;strcmp@plt+6&gt;:      push   $0x20
0x0804836f &lt;strcmp@plt+11&gt;:     jmp    0x8048314 &lt;_init+48&gt;</pre>
<p>Le 1er jmp référence un pointeur (0&#215;8049678) situé dans la GOT. Voyons ce qu&rsquo;il contient :</p>
<pre>(gdb) x 0x8049678
0x8049678 &lt;_GLOBAL_OFFSET_TABLE_+28&gt;:   0x0804836a</pre>
<p>Sa valeur veut 0x0804834a, qui correspond&#8230; à une adresse de la PLT, et plus précisément à l&rsquo;adresse de l&rsquo;instruction juste après le <code>jmp</code>. En d&rsquo;autres termes, lorsque <code>strcmp()</code> sera appelée, on saute dans la PLT, et on exécute le 1er <code>jmp</code>, qui se contente de sauter sur l&rsquo;instruction suivante. Cela peut paraître inutile à première vue, mais nous verrons l&rsquo;astuce qui se cache derrière plus tard. Continuons le fil de l&rsquo;exécution : après ce 1er <code>jmp</code>, on rencontre un <code>push</code>, qui empile une certaine valeur, ici 0&#215;20. Enfin, on rencontre un <code>jmp</code>, et en examinant l&rsquo;adresse de saut on s&rsquo;aperçoit qu&rsquo;il nous emmène sur l&rsquo;entrée 0 de la PLT.</p>
<p>Une fois sur l&rsquo;entrée 0, on rencontre deux instructions (les deux suivantes ne sont pas vraiment des instructions, mais correspondent aux opcodes 0&#215;0000) :</p>
<pre>0x08048314 &lt;_init+48&gt;:  pushl  0x8049660
0x0804831a &lt;_init+54&gt;:  jmp    *0x8049664</pre>
<p>On commence par empiler une valeur, qui se révèle être l&rsquo;entrée 1 de la GOT. On effectue ensuite un <code>jmp</code> sur le contenu d&rsquo;un pointeur, se situant lui aussi dans la GOT mais à l&rsquo;entrée 2. Pour le moment, ce pointeur ne contient que des zéros, vu que l&rsquo;exécution n&rsquo;a pas encore commencée. Plaçons un breakpoint sur l&rsquo;appel à <code>strcmp()</code>, et examinons la valeur du pointeur.</p>
<pre>(gdb) b *0x0804844f         //Correspond au call   0x8048364 &lt;strcmp@plt&gt;
Breakpoint 1 at 0x804844f

(gdb) r
Starting program: /media/echange/Hacking/Reversing_Linux/plt_got/bin_str

Breakpoint 1, 0x0804844f in main ()
Current language:  auto; currently asm

(gdb) x 0x8049664
0x8049664 &lt;_GLOBAL_OFFSET_TABLE_+8&gt;:    0xb7f12c40</pre>
<p>On constate qu&rsquo;une fois le programme lancé, la valeur du pointeur a été modifiée. C&rsquo;est la procédure de lancement de l&rsquo;exécutable qui a en fait initialisé cette valeur. Où pointe-t-elle ?</p>
<pre>(gdb) info files
...
0x08049658 - 0x0804965c is .got
0x0804965c - 0x0804967c is .got.plt
0x0804967c - 0x08049688 is .data
0x08049688 - 0x0804968c is .bss
0xb7f000f4 - 0xb7f001b4 is .hash in /lib/ld-linux.so.2
0xb7f001b4 - 0xb7f00298 is .gnu.hash in /lib/ld-linux.so.2
0xb7f00298 - 0xb7f00468 is .dynsym in /lib/ld-linux.so.2
0xb7f00468 - 0xb7f005fc is .dynstr in /lib/ld-linux.so.2
0xb7f005fc - 0xb7f00636 is .gnu.version in /lib/ld-linux.so.2
0xb7f00638 - 0xb7f00700 is .gnu.version_d in /lib/ld-linux.so.2
0xb7f00700 - 0xb7f00760 is .rel.dyn in /lib/ld-linux.so.2
0xb7f00760 - 0xb7f00788 is .rel.plt in /lib/ld-linux.so.2
0xb7f00788 - 0xb7f007e8 is .plt in /lib/ld-linux.so.2
0xb7f007f0 - 0xb7f157af is .text in /lib/ld-linux.so.2  &lt;=====
0xb7f157b0 - 0xb7f158e1 is __libc_freeres_fn in /lib/ld-linux.so.2
0xb7f15900 - 0xb7f19320 is .rodata in /lib/ld-linux.so.2
0xb7f19320 - 0xb7f1941c is .eh_frame_hdr in /lib/ld-linux.so.2
0xb7f1941c - 0xb7f19850 is .eh_frame in /lib/ld-linux.so.2
0xb7f1acc0 - 0xb7f1af28 is .data.rel.ro in /lib/ld-linux.so.2
0xb7f1af28 - 0xb7f1afe0 is .dynamic in /lib/ld-linux.so.2
0xb7f1afe0 - 0xb7f1afe8 is .got in /lib/ld-linux.so.2
0xb7f1aff4 - 0xb7f1b014 is .got.plt in /lib/ld-linux.so.2
0xb7f1b020 - 0xb7f1b5b0 is .data in /lib/ld-linux.so.2
0xb7f1b5b0 - 0xb7f1b5b4 is __libc_subfreeres in /lib/ld-linux.so.2
0xb7f1b5b4 - 0xb7f1b668 is .bss in /lib/ld-linux.so.2
0xb7d9c174 - 0xb7d9c194 is .note.ABI-tag in /lib/tls/i686/cmov/libc.so.6
0xb7d9c194 - 0xb7d9fcbc is .gnu.hash in /lib/tls/i686/cmov/libc.so.6
0xb7d9fcbc - 0xb7da8a8c is .dynsym in /lib/tls/i686/cmov/libc.so.6
0xb7da8a8c - 0xb7dae274 is .dynstr in /lib/tls/i686/cmov/libc.so.6
...</pre>
<p>La région contenant cette adresse d&rsquo;est rien d&rsquo;autre que la .text du linker dynamique, ld.so ! On se trouve dans la fonction du linker permettant d&rsquo;appeler la véritable fonction <code>strcmp()</code>.</p>
<h3>La résolution des symboles</h3>
<p>Mais comment le linker sait-il qu&rsquo;il faut appeler <code>strcmp()</code> et pas une autre fonction ? Tout simplement grâce au push 0&#215;20 précédent : 0&#215;20 correspond en fait à un offset correspondant ici à <code>strcmp()</code>. Les instructions de la PLT l&rsquo;ont empilé afin de le passer en paramètre à la fonction du linker.</p>
<p>Désassemblons cette fonction :</p>
<pre>(gdb) disas 0xb7ff6c40 0xb7ff6c40+28
//Le +28 a été trouvé en tatonnant jusqu'à trouver un ret
Dump of assembler code from 0xb7f12c40 to 0xb7f12c5c:
0xb7f12c40:     push   %eax
0xb7f12c41:     push   %ecx
0xb7f12c42:     push   %edx
0xb7f12c43:     mov    0x10(%esp),%edx
0xb7f12c47:     mov    0xc(%esp),%eax
0xb7f12c4b:     call   0xb7f0d350
0xb7f12c50:     pop    %edx
0xb7f12c51:     mov    (%esp),%ecx
0xb7f12c54:     mov    %eax,(%esp)
0xb7f12c57:     mov    0x4(%esp),%eax
0xb7f12c5b:     ret    $0xc
End of assembler dump.</pre>
<p>Cette fonction est relativement courte ; elle ne fait en réalité qu&rsquo;appeler la véritable fonction de résolution des adresses. Lorsqu&rsquo;elle est appelée, le sommet de pile contient une adresse (correspondant à GOT[1], qui a été pushée à l&rsquo;entrée 0 de la PLT), et juste en dessous se trouve l&rsquo;index de <code>strcmp</code>, soit 0&#215;20. Vérifions en plaçant un breakpoint au début et en examinant la pile :</p>
<pre>(gdb) b *0xb7f12c40
Breakpoint 2 at 0xb7f12c40

(gdb) c
Continuing.

Breakpoint 2, 0xb7f12c40 in ?? () from /lib/ld-linux.so.2

(gdb) x/2x $esp
0xbfbf50b4:     0xb7f1b668      0x00000020</pre>
<p>Vérifions que le 1er pointeur correspond bien à l&rsquo;entrée 1 de la GOT :</p>
<pre>(gdb) x 0x8049660
0x8049660 &lt;_GLOBAL_OFFSET_TABLE_+4&gt;:    0xb7f1b668</pre>
<p>Bingo ! On a bien en sommet de pile l&rsquo;adresse contenue dans GOT[1] et juste en dessous l&rsquo;index de strcmp(), 0&#215;20.</p>
<p>Revenons au code de cette fonction.</p>
<pre>(gdb) disas 0xb7f12c40 0xb7f12c40+28
Dump of assembler code from 0xb7f12c40 to 0xb7f12c5c:
0xb7f12c40:     push   %eax
0xb7f12c41:     push   %ecx
0xb7f12c42:     push   %edx
0xb7f12c43:     mov    0x10(%esp),%edx
0xb7f12c47:     mov    0xc(%esp),%eax
0xb7f12c4b:     call   0xb7f0d350
0xb7f12c50:     pop    %edx
0xb7f12c51:     mov    (%esp),%ecx
0xb7f12c54:     mov    %eax,(%esp)
0xb7f12c57:     mov    0x4(%esp),%eax
0xb7f12c5b:     ret    $0xc
End of assembler dump.</pre>
<p>Elle commence par 3 push, permettant de sauvegarder des registres. Ainsi, nos deux valeurs en sommet de pile vont être décalées de 3*4 = 12 octets. Juste après ces 3 <code>push</code>, on a deux <code>mov</code>. Le premier place dans %edx une valeur située sur la pile à l&rsquo;offset 0&#215;10 soit 16 = 4 * 4 octets. Il s&rsquo;agit donc de l&rsquo;index de <code>strcmp()</code>, 0&#215;20. Le second place dans %eax la valeur suivante, soit celle de GOT[1]. Puis un appel de fonction a lieu.</p>
<p>On arrive alors dans une fonction relativement complexe, qui se situe toujours dans la section .text de ld.so. C&rsquo;est elle qui est chargée d&rsquo;effectuer la résolution des symbolesen recherchant dans les librairies. Si vous essayez de la désassembler, vous constaterez qu&rsquo;elle est relativement longue et complexe. Comme ici le but n&rsquo;est pas d&rsquo;être exhaustif, je ne la détaillerai pas. En plus, je n&rsquo;ai pas encore eu le courage de la reverser dans sesmoindres détails&#8230;</p>
<p>Continuons donc. Plaçons un breakpoint juste après le call de cette fonction, en 0xb7f12c50.</p>
<pre>(gdb) b *0xb7f12c50
Breakpoint 3 at 0xb7f12c50

(gdb) c
Continuing.

Breakpoint 3, 0xb7f12c50 in ?? () from /lib/ld-linux.so.2</pre>
<p>Les instructions suivantes manipulent des registres. Dans le cadre de cet article, seules deux instructions sont intressantes :</p>
<pre>0xb7f12c54:     mov    %eax,(%esp)
et
0xb7f12c5b:     ret    $0xc</pre>
<p>Le mov place la valeur de %eax en sommet de pile, tandis que le ret ordonne au CPU de continuer l&rsquo;exécution du code à l&rsquo;adresse située sur le sommet de pile. Autrement dit, juste après le call de la fonction de résolution des symboles, on saute sur l&rsquo;adresse contenue dans %eax ! Regardons ce que vaut ce registre&#8230;</p>
<pre>(gdb) info registers
eax            0xb7e0dd20       -1210000096
ecx            0x0      0
edx            0x8049678        134518392
ebx            0xb7ee6ff4       -1209110540
esp            0xbfbf50a8       0xbfbf50a8
ebp            0xbfbf50f8       0xbfbf50f8
esi            0xb7f1ace0       -1208898336
edi            0x0      0
eip            0xb7f12c50       0xb7f12c50
...</pre>
<p>Que peut bien représenter l&rsquo;adresse 0xb7e0dd20 ?</p>
<pre>(gdb) info files
...
0xb7d9c174 - 0xb7d9c194 is .note.ABI-tag in /lib/tls/i686/cmov/libc.so.6
0xb7d9c194 - 0xb7d9fcbc is .gnu.hash in /lib/tls/i686/cmov/libc.so.6
0xb7d9fcbc - 0xb7da8a8c is .dynsym in /lib/tls/i686/cmov/libc.so.6
0xb7da8a8c - 0xb7dae274 is .dynstr in /lib/tls/i686/cmov/libc.so.6
0xb7dae274 - 0xb7daf42e is .gnu.version in /lib/tls/i686/cmov/libc.so.6
0xb7daf430 - 0xb7daf730 is .gnu.version_d in /lib/tls/i686/cmov/libc.so.6
0xb7daf730 - 0xb7daf770 is .gnu.version_r in /lib/tls/i686/cmov/libc.so.6
0xb7daf770 - 0xb7db2140 is .rel.dyn in /lib/tls/i686/cmov/libc.so.6
0xb7db2140 - 0xb7db2188 is .rel.plt in /lib/tls/i686/cmov/libc.so.6
0xb7db2188 - 0xb7db2228 is .plt in /lib/tls/i686/cmov/libc.so.6
0xb7db2230 - 0xb7eb2d84 is .text in /lib/tls/i686/cmov/libc.so.6  &lt;=====
0xb7eb2d90 - 0xb7eb3de8 is __libc_freeres_fn in /lib/tls/i686/cmov/libc.so.6
0xb7eb3df0 - 0xb7eb4082 is __libc_thread_freeres_fn in /lib/tls/i686/cmov/libc.so.6
0xb7eb40a0 - 0xb7ecf090 is .rodata in /lib/tls/i686/cmov/libc.so.6
0xb7ecf090 - 0xb7ecf0a3 is .interp in /lib/tls/i686/cmov/libc.so.6
0xb7ecf0a4 - 0xb7ed1c90 is .eh_frame_hdr in /lib/tls/i686/cmov/libc.so.6
0xb7ed1c90 - 0xb7ee1544 is .eh_frame in /lib/tls/i686/cmov/libc.so.6
0xb7ee1544 - 0xb7ee19b0 is .gcc_except_table in /lib/tls/i686/cmov/libc.so.6
0xb7ee19b0 - 0xb7ee4d28 is .hash in /lib/tls/i686/cmov/libc.so.6
0xb7ee51ec - 0xb7ee51f4 is .tdata in /lib/tls/i686/cmov/libc.so.6
...</pre>
<p>Tiens, elle se trouve dans la .text&#8230; Par hasard, ce ne serait pas l&rsquo;adresse de <code>strcmp</code> ?</p>
<pre>(gdb) p strcmp
$1 = {&lt;text variable, no debug info&gt;} 0xb7e0dd20 &lt;strcmp&gt;</pre>
<p>Eh si ! Autrement dit, la fonction de résolution des symboles a résolu correctement <code>strcmp</code> et a placé son adresse dans %eax.</p>
<h3>Et la GOT dans tout ça ?</h3>
<p>Nous venons de voir le cheminement (d&rsquo;un point de vue assez haut niveau) d&rsquo;un appel de fonction situé dans une bibliothèque partagée. Comme on a pu le constater, chaque appel de fonction entraîne à priori une résolution de symbole, ce qui paraît fastidieux. Fort heureusement, par défaut, ld.so ne résoud pas un symbole à chaque fois qu&rsquo;on tente d&rsquo;y accéder, mais uniquement la 1ère fois. Par exemple, si vous avez 10 appels à strcmp() dans un programme, le 1er appel entraînera une résolution, et l&rsquo;adresse de strcmp() sera gardée en mémoire pour les 9 appels suivant. C&rsquo;est ce que l&rsquo;on appelle l&rsquo;<em>évaluation fainéante</em> : on ne fait que le minimum d&rsquo;opération, et juste à temps.</p>
<p>Où et comment les adresses des symboles sont-elles gardées en mémoire une fois résolues ? Réponse : dans la GOT ! Pour le comprendre, relançons le programme et plaçons un breakpoint dans l&rsquo;entrée de la PLT correspondant à <code>strcmp</code>.</p>
<pre>$ gdb ./bin_str

(gdb) disas 0x08048314 0x08048374   //Les adresses de la PLT, qui restent fixes
Dump of assembler code from 0x8048314 to 0x8048374:
0x08048314 &lt;_init+48&gt;:  pushl  0x8049660
0x0804831a &lt;_init+54&gt;:  jmp    *0x8049664
0x08048320 &lt;_init+60&gt;:  add    %al,(%eax)
0x08048322 &lt;_init+62&gt;:  add    %al,(%eax)
0x08048324 &lt;__gmon_start__@plt+0&gt;:      jmp    *0x8049668
0x0804832a &lt;__gmon_start__@plt+6&gt;:      push   $0x0
0x0804832f &lt;__gmon_start__@plt+11&gt;:     jmp    0x8048314 &lt;_init+48&gt;
0x08048334 &lt;__libc_start_main@plt+0&gt;:   jmp    *0x804966c
0x0804833a &lt;__libc_start_main@plt+6&gt;:   push   $0x8
0x0804833f &lt;__libc_start_main@plt+11&gt;:  jmp    0x8048314 &lt;_init+48&gt;
0x08048344 &lt;printf@plt+0&gt;:      jmp    *0x8049670
0x0804834a &lt;printf@plt+6&gt;:      push   $0x10
0x0804834f &lt;printf@plt+11&gt;:     jmp    0x8048314 &lt;_init+48&gt;
0x08048354 &lt;__stack_chk_fail@plt+0&gt;:    jmp    *0x8049674
0x0804835a &lt;__stack_chk_fail@plt+6&gt;:    push   $0x18
0x0804835f &lt;__stack_chk_fail@plt+11&gt;:   jmp    0x8048314 &lt;_init+48&gt;
0x08048364 &lt;strcmp@plt+0&gt;:      jmp    *0x8049678
0x0804836a &lt;strcmp@plt+6&gt;:      push   $0x20
0x0804836f &lt;strcmp@plt+11&gt;:     jmp    0x8048314 &lt;_init+48&gt;
End of assembler dump.

(gdb) b *0x08048364
Breakpoint 1 at 0x8048364

(gdb) r
Starting program: /media/echange/Hacking/Reversing_Linux/plt_got/bin_str

Breakpoint 1, 0x08048364 in strcmp@plt ()
Current language:  auto; currently asm

(gdb) x 0x8049678
0x8049678 &lt;_GLOBAL_OFFSET_TABLE_+28&gt;:   0x0804836a</pre>
<p>L&rsquo;entrée correspondante de la GOT contient toujours l&rsquo;adresse de l&rsquo;instruction suivante dans la PLT. Quel intérêt ? A ce moment, il faut noter que le symbole <code>strcmp</code> n&rsquo;est pas encore résolu, donc il est normal que cette entrée ne comporte aucune valeur intéressante. Plaçons un watchpoint sur cette entrée de la GOT afin de voir si elle change au fil du temps.</p>
<pre>(gdb) watch *0x8049678
Hardware watchpoint 2: *134518392

(gdb) c
Continuing.
Hardware watchpoint 2: *134518392

Old value = 134513514
New value = -1209639648
0xb7f6545d in ?? () from /lib/ld-linux.so.2</pre>
<p>Apparamment, la valeur de l&rsquo;entrée a changé ! Observons sa nouvelle valeur :</p>
<pre>(gdb) x 0x8049678
0x8049678 &lt;_GLOBAL_OFFSET_TABLE_+28&gt;:   0xb7e65d20

(gdb) p strcmp
$1 = {&lt;text variable, no debug info&gt;} 0xb7e65d20 &lt;strcmp&gt;</pre>
<p>Ainsi on s&rsquo;aperçoit qu&rsquo;elle correspond désormais à l&rsquo;adresse de <code>strcmp()</code>. Désormais, si le programme souhaîte faire d&rsquo;autres appels à <code>strcmp()</code>, il n&rsquo;aura plus à effectuer la résolution de symbole puisque le jmp situé dans l&rsquo;entrée de la PLT référence directement l&rsquo;adresse de <code>strcmp()</code> !</p>
<p>Quand cette valeur a-t-elle été écrite ? Pour cela, il suffit de regarder <code>%eip</code> et de voir dans quelle zone nous sommes.</p>
<pre>(gdb) info registers
eax            0xb7e65d20       -1209639648
ecx            0x0      0
edx            0x8049678        134518392
ebx            0xb7f72ff4       -1208537100
esp            0xbfae4f58       0xbfae4f58
ebp            0xbfae4f90       0xbfae4f90
esi            0xb7f56858       -1208653736
edi            0xb7f73668       -1208535448
eip            0xb7f6545d       0xb7f6545d
eflags         0x246    [ PF ZF IF ]
cs             0x73     115
ss             0x7b     123
ds             0x7b     123
es             0x7b     123
fs             0x0      0
gs             0x33     51

(gdb) disas $eip-3 $eip+20
Dump of assembler code from 0xb7f6545a to 0xb7f65471:
0xb7f6545a:     mov    %eax,(%edx,%ecx,1)
0xb7f6545d:     lea    -0xc(%ebp),%esp
0xb7f65460:     pop    %ebx
0xb7f65461:     pop    %esi
0xb7f65462:     pop    %edi
0xb7f65463:     pop    %ebp
0xb7f65464:     ret
0xb7f65465:     xor    %edx,%edx
0xb7f65467:     jmp    0xb7f653de
0xb7f6546c:     lea    -0x2477(%ebx),%eax
...</pre>
<p>En cherchant un peu, on remarque qu&rsquo;on se trouve juste à la fin de la fonction de résolution des symboles. L&rsquo;instruction responsable de l&rsquo;écriture de l&rsquo;adresse dans la GOT est le <code>mov</code> %eax,(%edx,%ecx,1). En inspectant les registres, on voit que cela correspond bien à l&rsquo;opération *0&#215;8049678 = 0xb7e65d20 (l&rsquo;adresse de <code>strcmp</code>).</p>
<p>On notera que cette méthode est comparable aux systèmes de caches des processeurs : l&rsquo;objectif est d&rsquo;accelérer les accès futurs aux fonctions en accédant une fois pour toute à une donnée et en la plaçant dans une zone d&rsquo;accès plus rapide. Pour information, il est tout à fait possible de désactiver ce système de mise en cache en utilisant des variables d&rsquo;environnement reconnues par ld.so. Par exemple, la variable d&rsquo;environnement <code>LD_BIND_NOT</code>, si elle est définie, permet ainsi de désactiver complétement l&rsquo;écriture des adresses dans la GOT, tandis que la variable <code>LD_BIND_NOW</code> indique à ld.so d&rsquo;effectuer toutes les résolutions dès le début, donc de remplir la GOT dès le lancement de l&rsquo;exécutable. Pour plus de renseignements, consultez le man de ld.so.</p>
<h3>Récapitulatif</h3>
<p>Le schéma suivant retrace le fil de l&rsquo;exécution lors d&rsquo;un appel à <code>strcmp()</code>, et récapitule les différents liens entre la PLT et la GOT.</p>
<h4>1er appel de strcmp : symbole non encore résolu</h4>
<pre>main:
...
call   0x8048364 &lt;strcmp@plt&gt; ----+
mov    %eax,-0x1c(%ebp)           |
...                               |
                                  |
                                  |
0x8048364 (Entrée de la PLT) : &lt;--+
jmp    *0x8049678  -----------------------&gt; 0x8049678 (Entrée de la GOT) :
push   $0x20  &lt;------------------------------ 0x0804836a
jmp    0x8048314 &lt;_init+48&gt; --------+
                                    |
                                    |
0x8048314 (Entrée 0 de la PLT) : &lt;--+
pushl  0x8049660
jmp    *0x8049664  -----------------------&gt; 0x8049664 (Header de la GOT) :
                                              0xb7f12c40 --+
                                                           |
                                                           |
0xb7f12c40 (.text de ld.so, résolution + appel) : &lt;--------+
push   %eax              // Sauvegarde de registres
push   %ecx
push   %edx
mov    0x10(%esp),%edx   // Récupération du code de la fonction strcmp (0x20)
mov    0xc(%esp),%eax
call   0xb7f4c350  -------&gt; Appel de la fonction de résolution des symboles
                            L'adresse du symbole (strcmp) est placé dans %eax.
                            L'entrée de la .got est patchée avec cette adresse.
... &lt;---------------------- Retour de la fonction
mov    %eax,(%esp)       // L'adresse de strcmp (0xb7e65d20) est empilée
...
ret    $0xc ------------------------------+ // On saute sur strcmp
                                          |
                                          |
0xb7e65d20 (strcmp, .text de la libc): &lt;--+
...
ret                      // Retour au main</pre>
<h4>2ème appel de strcmp : symbole déjà résolu</h4>
<pre>main:
...
call   0x8048364 &lt;strcmp@plt&gt;  ---+
mov    %eax,-0x1c(%ebp)           |
...                               |
                                  |
                                  |
0x8048364 (Entrée de la .plt) : &lt;-+
jmp    *0x8049678  ----------------&gt; 0x8049678 (Entrée de la GOT) :
                                       0xb7e65d20 --+ // Symbole résolu !
push   $0x20                                        | // Les instructions
jmp    0x8048314 &lt;_init+48&gt;                         | // suivantes ne sont pas
                                                    | // exécutées.
                                                    |
0xb7e65d20 (strcmp, .text de la libc): &lt;------------+
...
ret                      // Retour au main</pre>
<h3>Références</h3>
<p>Comme le précise l&rsquo;introduction, je n&rsquo;avais pas Internet quand j&rsquo;ai réalisé cet article ; je ne peux donc pas citer de page Web. Je me suis beaucoup aidé de la documentation off-line des programmes que j&rsquo;avais sous la main, à savoir :</p>
<ul>
<li>Le man de ld.so (man <code>ld.so</code>)</li>
<li>L&rsquo;aide de gdb (commande <code>help</code> de gdb)</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>https://www.segmentationfault.fr/linux/role-plt-got-ld-so/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		</item>
		<item>
		<title>Problèmes de locale dans Ubuntu</title>
		<link>https://www.segmentationfault.fr/linux/ubuntu/locale-ubuntu/</link>
		<comments>https://www.segmentationfault.fr/linux/ubuntu/locale-ubuntu/#comments</comments>
		<pubDate>Sat, 28 Jun 2008 16:34:01 +0000</pubDate>
		<dc:creator>Emilien Girault</dc:creator>
				<category><![CDATA[Ubuntu]]></category>
		<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://www.segmentationfault.fr/?p=34</guid>
		<description><![CDATA[Depuis que j&#8217;ai réinstallé Ubuntu Hardy sur mon laptop, j&#8217;ai été embêté par plusieurs problèmes liés aux locales. En effet, je suis en dual boot avec Windows, dont l&#8217;encodage est l&#8217;ISO8859-15, alors l&#8217;encodage par défaut d&#8217;Ubuntu est l&#8217;UTF-8. Cela pose donc des problèmes en ce qui concerne l&#8217;encodage des fichiers et de leurs noms. J&#8217;ai [...]]]></description>
			<content:encoded><![CDATA[<p>Depuis que j&rsquo;ai réinstallé Ubuntu Hardy sur mon laptop, j&rsquo;ai été embêté par plusieurs problèmes liés aux locales. En effet, je suis en dual boot avec Windows, dont l&rsquo;encodage est l&rsquo;ISO8859-15, alors l&rsquo;encodage par défaut d&rsquo;Ubuntu est l&rsquo;UTF-8. Cela pose donc des problèmes en ce qui concerne l&rsquo;encodage des fichiers et de leurs noms.<span id="more-34"></span></p>
<p>J&rsquo;ai donc galéré une première fois pour changer l&rsquo;encodage d&rsquo;Ubuntu, souhaitant le passer en ISO8859-15. Des tas de techniques existent, mais chez moi aucune n&rsquo;a marché jusqu&rsquo;à ce que je sois finalement tombé sur un <a href="http://blog.andrewbeacock.com/2007/01/how-to-change-your-default-locale-on.html" target="_blank">excellent post</a> qui explique la manipulation, que je me suis permis de le traduire :</p>
<p><strong>Changer les locales supportées</strong><br />
Éditez le fichier <code>/var/lib/locales/supported.d/local</code> (en root) en y ajoutant les lignes suivantes :</p>
<pre>fr_FR ISO-8859-1
fr_FR@euro ISO-8859-15</pre>
<p><strong>Regénérer les  locales</strong><br />
Lancez la commande suivante :</p>
<p><code>sudo dpkg-reconfigure locales</code></p>
<p><strong>Changer la locale par défaut</strong><br />
Éditez le fichier <code>/etc/environment</code> pour y ajouter :</p>
<pre><code>LANGUAGE="fr_FR:fr"
LANG="fr_FR@euro"</code></pre>
<p><strong>Rebooter !</strong></p>
<p>Cela permet ainsi de résoudre, dans un premier temps, les problèmes liés à l&rsquo;encodage par défaut du système. Si vous lancez la commande <code>locale charmap</code> dans un shell, vous devriez obtenir l&rsquo;affichage suivant :</p>
<pre>ISO-8859-15</pre>
<p>Dans un deuxième temps, j&rsquo;ai rencontré un deuxième problème : lorsque je connecte mon disque dur externe, il se monte, mais il est incapable de gérer les accents dans les noms de fichiers. Pour s&rsquo;en rendre compte, c&rsquo;est très simple : il suffit de tenter de créer un fichier avec un nom comportant un accent. Chez moi, konqueror m&rsquo;affichait une erreur&#8230;</p>
<p>D&rsquo;après mes recherches, ce problème serait dû à HAL, le module chargé de monter automatiquement les disques, car celui-ci monte par défaut tous les systèmes de fichiers&#8230; en UTF8 ! Pour résoudre ce problème, il m&rsquo;a fallu un peu de temps avant de trouver <a href="http://forum.ubuntu-fr.org/viewtopic.php?pid=1499339#p1499339" target="_blank">cette manip</a> :</p>
<p>Supprimer le lien symbolique <code>/sbin/mount.ntfs-3g</code> :</p>
<pre> sudo rm /sbin/mount.ntfs-3g</pre>
<p>Le remplacer (créer un fichier de même nom) avec ce contenu :</p>
<pre>#!/bin/bash
/bin/ntfs-3g $1 $2 -o locale=fr_FR@euro</pre>
<p>Le rendre exécutable : sudo <code>chmod +x /sbin/mount.ntfs-3g</code></p>
<p>Et voila ! Normalement, en rebranchant le disque dur externe, celui-ci est correctement monté et les accents sont supportés.</p>
<p>Merci aux deux auteurs de ces solutions !</p>
]]></content:encoded>
			<wfw:commentRss>https://www.segmentationfault.fr/linux/ubuntu/locale-ubuntu/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Drivers Wifi RT73 et Ubuntu Hardy, le retour</title>
		<link>https://www.segmentationfault.fr/linux/ubuntu/drivers-rt73ubuntu-hardy/</link>
		<comments>https://www.segmentationfault.fr/linux/ubuntu/drivers-rt73ubuntu-hardy/#comments</comments>
		<pubDate>Sun, 18 May 2008 21:57:10 +0000</pubDate>
		<dc:creator>Emilien Girault</dc:creator>
				<category><![CDATA[Ubuntu]]></category>
		<category><![CDATA[drivers]]></category>
		<category><![CDATA[ralink]]></category>
		<category><![CDATA[wifi]]></category>

		<guid isPermaLink="false">http://www.segmentationfault.fr/?p=28</guid>
		<description><![CDATA[Dans un précédent article, j&#8217;ai expliqué comment installer et utiliser le driver rt73 pour les cartes Wifi Ralink. Cela fonctionnait très bien, jusqu&#8217;à ce que la nouvelle monture d&#8217;Ubuntu, Hardy heron, n&#8217;arrive. En fait, cela est du au passage à un nouveau noyau (2.6.24-17). Voici donc la marche à suivre pour s&#8217;en sortir&#8230; Tout d&#8217;abord, [...]]]></description>
			<content:encoded><![CDATA[<p>Dans un <a href="http://www.segmentationfault.fr/systeme/drivers-wifi-rt73-injection-wpa-ubuntu/" target="_self">précédent article</a>, j&rsquo;ai expliqué comment installer et utiliser le driver rt73 pour les cartes Wifi Ralink. Cela fonctionnait très bien, jusqu&rsquo;à ce que la nouvelle monture d&rsquo;Ubuntu, Hardy heron, n&rsquo;arrive. En fait, cela est du au passage à un nouveau noyau (2.6.24-17). Voici donc la marche à suivre pour s&rsquo;en sortir&#8230;<span id="more-28"></span></p>
<p>Tout d&rsquo;abord, téléchargez les derniers drivers d&rsquo;Aircrack pour profiter de l&rsquo;injection, puis décompressez-les :</p>
<pre>wget http://rt2x00.serialmonkey.com/rt73-cvs-daily.tar.gz
tar -zxvf rt73-cvs-daily.tar.gz
cd rt73-cvs*/Module</pre>
<p>Si ce n&rsquo;est pas déjà fait, blacklistez les anciens modules qui peuvent empêcher rt73 de fonctionner en éditant /etc/modprobe.d/blacklist et en y ajoutant :</p>
<pre>blacklist rt73usb
blacklist rt2570
blacklist rt2500usb
blacklist rt2x00lib</pre>
<p>Au cas où, déchargez ces modulez éventuels :</p>
<pre>sudo modprobe -r rt73usb
sudo modprobe -r rt2570
sudo modprobe -r rt2500usb
sudo modprobe -r rt2x00lib</pre>
<p>Puis compilez et installez le module :</p>
<pre>make
sudo make install</pre>
<p>Si jamais vous obtenez un message du style :</p>
<pre>!!! WARNING: Module file much too big (&gt;1MB)
!!! Check your kernel settings or use 'strip'</pre>
<p>Alors lancez les commandes :</p>
<pre>make
strip -S rt73.ko
sudo make install</pre>
<p>Ensuite, copiez le module :</p>
<pre>sudo mkdir /lib/modules/$(uname -r)/extra
sudo cp rt73.ko /lib/modules/$(uname -r)/extra/rt73.ko</pre>
<p>Puis chargez-le :</p>
<pre>sudo depmod -ae
sudo modprobe rt73</pre>
<p>Normalement, vous devriez pouvoir activer votre interface :</p>
<pre>sudo ifconfig wlan0 up</pre>
<p>Notez que désormais l&rsquo;interface a un nom du style wlan* et non plus en rausb*.</p>
<p>Maintenant, votre connexion Wifi et Aircrack devraient fonctionner à nouveau. Vous devrez très certainement éditer votre /etc/network/interfaces pour remplacer les occurrences de rausb0 par wlan0.</p>
<p>Et voila ! Au besoin, référez-vous au README situé dans l&rsquo;archive des drivers, il est bien fourni et décrit les configurations classiques (WEP, WPA, WPA2) de la carte.</p>
<p>Sources :</p>
<ul>
<li><a href="http://forum.ubuntu-fr.org/viewtopic.php?pid=1747722#p1747722" target="_blank">Forum Ubuntu</a></li>
<li><a href="http://aircrack-ng.org/doku.php?id=rt73" target="_blank">Aircrack, drivers rt73</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>https://www.segmentationfault.fr/linux/ubuntu/drivers-rt73ubuntu-hardy/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>Loguer l&#8217;entrée et la sortie standard d&#8217;un programme sous Linux</title>
		<link>https://www.segmentationfault.fr/dev/loguer-stdin-stdout/</link>
		<comments>https://www.segmentationfault.fr/dev/loguer-stdin-stdout/#comments</comments>
		<pubDate>Sat, 05 Apr 2008 12:12:20 +0000</pubDate>
		<dc:creator>Emilien Girault</dc:creator>
				<category><![CDATA[Développement]]></category>
		<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://www.segmentationfault.fr/?p=25</guid>
		<description><![CDATA[Voici un petit script Bash qui permet de loguer très facilement l&#8217;entrée (stdin) et la sortie standard (stdout) d&#8217;un programme, et ce sans le modifier. #!/bin/bash LOG_STDIN=/path/du/log/stdin.log LOG_STDOUT=/path/du/log/stdout.log PROG="/path/du/prog avec parametres eventuels" tee $LOG_STDIN &#124; $PROG &#124; tee $LOG_STDOUT Comme vous pouvez le voir, ce script repose sur la commande tee qui permet de dupliquer [...]]]></description>
			<content:encoded><![CDATA[<p>Voici un petit script Bash qui permet de loguer très facilement l&rsquo;entrée (stdin) et la sortie standard (stdout) d&rsquo;un programme, et ce sans le modifier.</p>
<pre>#!/bin/bash

LOG_STDIN=/path/du/log/stdin.log
LOG_STDOUT=/path/du/log/stdout.log
PROG="/path/du/prog avec parametres eventuels"

tee $LOG_STDIN | $PROG | tee $LOG_STDOUT</pre>
<p>Comme vous pouvez le voir, ce script repose sur la commande tee qui permet de dupliquer un flux.Il suffit de lancer ce script à la place du programme que vous souhaitez espionner. Cette astuce pourra être utile aussi bien au développeur qu&rsquo;au <em>reverse-engineer</em>&#8230;</p>
<p>Pour la petite histoire, j&rsquo;ai développé ce petit script dans le cadre d&rsquo;un projet d&rsquo;intelligence artificielle. Le but est de réaliser un joueur artificiel de Jeu de Go. Notre équipe a choisis d&rsquo;utiliser le protocole <a href="http://www.segmentationfault.fr/wp-admin/www.lysator.liu.se/~gunnar/gtp" target="_blank">GTP</a> qui permet de faire communiquer des joueurs avec des interfaces graphiques. Ce script nous permet donc de déboguer plus facilement notre joueur car il permet de garder une trace des trames GTP échangées. Nous l&rsquo;avons également utiliser pour avoir une idée des commandes GTP échangées entre une interface comme <a href="http://qgo.sourceforge.net" target="_blank">qGo</a> et un joueur déjà existant tel que <a href="http://www.gnu.org/software/gnugo/gnugo.html" target="_blank">GNUGo</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://www.segmentationfault.fr/dev/loguer-stdin-stdout/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Utiliser update-manager -d avec un proxy</title>
		<link>https://www.segmentationfault.fr/linux/ubuntu/update-manager-d-et-proxy/</link>
		<comments>https://www.segmentationfault.fr/linux/ubuntu/update-manager-d-et-proxy/#comments</comments>
		<pubDate>Sat, 05 Apr 2008 12:31:41 +0000</pubDate>
		<dc:creator>Emilien Girault</dc:creator>
				<category><![CDATA[Ubuntu]]></category>
		<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://www.segmentationfault.fr/?p=24</guid>
		<description><![CDATA[La nouvelle version d&#8217;Ubuntu, Hardy Heron, sort dans une vingtaine de jours&#8230; Mais comme je suis imparient, j&#8217;ai souhaité tester la version instable. J&#8217;ai donc lançé la commande permettant de faire la mise à jour, que l&#8217;on peut trouver sur le Wiki d&#8217;Ubuntu-fr : sudo update-manager -d -c Le problème, c&#8217;est qu&#8217;update-manager indiquait qu&#8217;aucune mise [...]]]></description>
			<content:encoded><![CDATA[<p>La nouvelle version d&rsquo;Ubuntu, Hardy Heron, sort dans une vingtaine de jours&#8230; Mais comme je suis imparient, j&rsquo;ai souhaité tester la version instable. J&rsquo;ai donc lançé la commande permettant de faire la mise à jour, que l&rsquo;on peut trouver sur le <a href="http://doc.ubuntu-fr.org/migration_gutsy_hardy" target="_blank">Wiki d&rsquo;Ubuntu-fr</a> :</p>
<pre>sudo update-manager -d -c</pre>
<p>Le problème, c&rsquo;est qu&rsquo;update-manager indiquait qu&rsquo;aucune mise à jour n&rsquo;était disponible. Même en enlevant le paramètre -d, rien à faire. Le pire c&rsquo;est que je n&rsquo;obtenais aucun message d&rsquo;erreur.</p>
<p>Il faut aussi préciser que j&rsquo;étais contraint de passer par un proxy HTTP. Pour qu&rsquo;APT fonctionne, j&rsquo;avais inséré ceci dans mon /etc/apt/apt/conf :</p>
<pre>Acquire::http::Proxy "http://mon.proxy:8080/";</pre>
<p>De même, mon fichier /etc/wgetrc contenait la ligne :</p>
<pre>http_proxy = http://mon.proxy:8080/</pre>
<p>Ces deux directives permettent aux outils comme aptitude et apt-get de télécharger correctement les paquets en passant par le proxy. Jusqu&rsquo;ici, tout fonctionnait correctement, du moins avec ces deux outils.</p>
<p>Mais revenons à mon problème de départ : update-manager ne parvenait pas à trouver les mises à jour. J&rsquo;ai passé un certain temps à rechercher la solution, et j&rsquo;ai finalement trouvé la réponse sur le <a href="http://ubuntuforums.org/showthread.php?t=115829" target="_blank">forum officiel</a>.</p>
<p>L&rsquo;explication est assez bizarre. Il se trouve qu&rsquo;update-manager, lorsqu&rsquo;on l&rsquo;utilise avec l&rsquo;option -d (précisant de rechercher une mise à jour instable), ne tient pas du tout compte des deux fichiers de configuration précédents (apt.conf et wgetrc). C&rsquo;est donc parce qu&rsquo;il n&rsquo;arrivait pas à se connecter qu&rsquo;il indiquait qu&rsquo;aucune mise à jour n&rsquo;était disponible !</p>
<p>La solution est finalement très simple : il faut configurer le proxy dans Synapic ! Vu que je n&rsquo;utilise jamais ce logiciel, je ne l&rsquo;avais pas configuré&#8230; Je l&rsquo;ai donc lancé, configuré correctement, et relancé update-manager -d. Et là, la nouvelel mise à jour était disponible&#8230;</p>
<p>Moralité : Même si vous ne l&rsquo;utilisez pas, pensez à configurer le proxy de Synaptic, où vous risquez d&rsquo;avoir des surprises !</p>
<p>Pour information, j&rsquo;ai rajouté cette manip dans le <a href="http://doc.ubuntu-fr.org:81/migration_gutsy_hardy#sous_ubuntu_xubuntu_et_edubuntu" target="_blank">Wiki d&rsquo;Ubuntu-fr</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://www.segmentationfault.fr/linux/ubuntu/update-manager-d-et-proxy/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Injecteur de fautes pour application distribuée</title>
		<link>https://www.segmentationfault.fr/projets/injecteur-fautes-parallele/</link>
		<comments>https://www.segmentationfault.fr/projets/injecteur-fautes-parallele/#comments</comments>
		<pubDate>Mon, 24 Mar 2008 13:10:43 +0000</pubDate>
		<dc:creator>Emilien Girault</dc:creator>
				<category><![CDATA[Applications]]></category>
		<category><![CDATA[Développement]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Projets]]></category>
		<category><![CDATA[Sécurité informatique]]></category>
		<category><![CDATA[c++]]></category>
		<category><![CDATA[calcul distribué]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[projet]]></category>

		<guid isPermaLink="false">http://www.segmentationfault.fr/projets/injecteur-fautes-parallele/</guid>
		<description><![CDATA[Dans le cadre d&#8217;un projet scolaire, je travaille sur un projet concernant la sécurité des grilles de calcul. Rappelons qu&#8217;une grille de calcul désigne un ensemble d&#8217;ordinateurs interconnectés et qui ne sont pas nécessairement homogènes. Ce type d&#8217;architecture est utilisé majoritairement pour faire tourner des applications nécessitant une très grande puissance de calculs. Avec le [...]]]></description>
			<content:encoded><![CDATA[<p>Dans le cadre d&rsquo;un projet scolaire, je travaille sur un projet concernant la sécurité des grilles de calcul. Rappelons qu&rsquo;une grille de calcul désigne un ensemble d&rsquo;ordinateurs interconnectés et qui ne sont pas nécessairement homogènes. Ce type d&rsquo;architecture est utilisé majoritairement pour faire tourner des applications nécessitant une très grande puissance de calculs. Avec le temps, la taille de ces application tend à augmenter, donc les besoins en matière de sécurité également. Le but du projet sur lequel je travaille en ce moment est de concevoir un injecteur de fautes pour application distribuée. Cet injecteur doit permettre en quelque sorte de faire planter une application tournant en parallèle sur plusieurs machines, afin de mettre à l&rsquo;épreuve sa tolérance aux fautes. Ce billet présente rapidement l&rsquo;architecture de notre logiciel, qui est toujours en développement.</p>
<p>Pour concevoir une application parallèle, on utilise assez souvent la programmation par messages. Dans le cadre de notre projet, nous utilisons la bibliothèque <strong>LAM/MPI</strong> qui fournit toute une API permettant d&rsquo;envoyer des messages à des processus tournant sur des machines distantes. De plus, le but étant d&rsquo;injecter des fautes, nous avons choisit de provoquer ces fautes lors des envois et réceptions de messages. Ainsi nous pouvons facilement simuler aussi bien les pannes logicielles (déni de service) et matérielles (coupure de lien d&rsquo;un réseau, paquet perdu ou corrompu). Pour détourner les fonctions fournies par la bibliothèque LAM/MPI, nous utilisons la variable d&rsquo;environnement <strong>LD_PRELOAD</strong>. Cette variable des systèmes UNIX/Linux permet de charger dynamiquement une bibliothèque au lancement d&rsquo;une application. Le point intéressant est que cette bibliothèque peut redéfinir des fonctions qui existent déjà dans les autres bibliothèques, donc peut potentiellement les appeler tout en modifiant leur comportement.</p>
<p>LD_PRELOAD permet alors de modifier le comportement d&rsquo;une application tout en n&rsquo;ayant pas besoin de la recompiler ! Il est toutefois important de préciser que cette technique ne permet de détourner (<em>hooker</em>) que les fonctions définies dans des librairies dynamiques. C&rsquo;est un point crucial et qui nous a posé quelques problèmes. En effet nous utilisions au départ la librairie MPICH 1, et il se trouve que lorsqu&rsquo;une application est compilée avec, les appels MPI sont liés de manière statique. Un simple appel à la commande ldd permet de le voir. C&rsquo;est pourquoi nous avons choisis d&rsquo;utiliser LAM/MPI à la place.</p>
<p>Ainsi, nous avons développé une bibliothèque dynamique (fichier .so) dont le but est de venir d&rsquo;interposer entre l&rsquo;application parallèle et LAM/MPI. Cette bibliothèque (que nous avons nommé bibliothèque d&rsquo;interposition) redéfinit les fonctions MPI_Send() et MPI_Recv() en injectant des fautes comme des corruptions de données et des dénis de service. Les fautes ne sont pas générées de manière permanentes ; nous utilisons en plus un processus qui tourne en tâche de fond (démon) qui communique avec la librairie par l&rsquo;intermédiaire d&rsquo;un segment de mémoire partagé. Ce démon est en réalité un serveur utilisant CORBA. Son rôle est de rester en attente de requêtes et de dialoguer avec la librairie pour déclencher l&rsquo;injection de fautes. Il surveille également l&rsquo;application ainsi que le système par l&rsquo;intermédiaire de sondes logicielles afin de suivre en temps réel les valeurs de quelques variables, comme la charge CPU, l&rsquo;occupation mémoire, etc. Ces valeurs sont sauvegardées dans une base de données pour être retraitées plus tard par un module de statistiques.</p>
<p>Sur chaque machine tournent donc : une instance de l&rsquo;application distribuée, une instance de la bibliothèque d&rsquo;interposition, et une instance du démon. Mais en plus de tout cela, il faut être capable de déployer l&rsquo;application parallèle. Nous avons donc conçu un module dédié à cela, le simulateur. Il est contrôlable en ligne de commande ou via une interface graphique. Son but est de lancer l&rsquo;application parallèle, les démons et activer le détournement de fonction en exportant la variable LD_PRELOAD sur toutes les machines.</p>
<p>Ce projet, bien que relativement complexe, se révèle très intéressant. Cela nous a permis de découvrir et d&rsquo;utiliser des libraires et des technologies très utiles, comme CORBA, MPI, Boost, MySQL++. Nous utilisons également Flex et Bison pour la conception de l&rsquo;interpréteur de commandes du simulateur.</p>
<p>Au fut et à mesure du développement, je mettrai sans doute en ligne quelques billets exposant de façon plus détaillée la conception de certains des modules.</p>
]]></content:encoded>
			<wfw:commentRss>https://www.segmentationfault.fr/projets/injecteur-fautes-parallele/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Drivers Wifi Ralink rt73, injection et WPA sous Ubuntu</title>
		<link>https://www.segmentationfault.fr/linux/ubuntu/drivers-wifi-rt73-injection-wpa-ubuntu/</link>
		<comments>https://www.segmentationfault.fr/linux/ubuntu/drivers-wifi-rt73-injection-wpa-ubuntu/#comments</comments>
		<pubDate>Sun, 24 Feb 2008 09:33:52 +0000</pubDate>
		<dc:creator>Emilien Girault</dc:creator>
				<category><![CDATA[Ubuntu]]></category>
		<category><![CDATA[aircrack]]></category>
		<category><![CDATA[drivers]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[ralink]]></category>
		<category><![CDATA[wifi]]></category>

		<guid isPermaLink="false">http://www.segmentationfault.fr/systeme/drivers-wifi-rt73-injection-wpa-ubuntu/</guid>
		<description><![CDATA[Suite à une mise à jour critique du noyau de Linux (due à une faille de sécurité permettant de faire un local root), j&#8217;ai mis à jour mes machines, et donc installé la dernière mise à jour du noyau. Cela s&#8217;est passé sans problème sur mon ordinateur fixe, mais j&#8217;ai eu un petit soucis sur [...]]]></description>
			<content:encoded><![CDATA[<p>Suite à une mise à jour critique du noyau de Linux (due à une faille de sécurité permettant de faire un local root), j&rsquo;ai mis à jour mes machines, et donc installé la dernière mise à jour du noyau. Cela s&rsquo;est passé sans problème sur mon ordinateur fixe, mais j&rsquo;ai eu un petit soucis sur mon portable. En fait, ce portable possède une carte Wifi Ralink intégrée, et le driver de base fourni par Ubuntu est défectueux (on peut voir les réseaux Wifi, mais pas s&rsquo;y connecter). J&rsquo;avais donc utilisé les drivers fournis par Aircrack, qui permettent en plus de faire de l&rsquo;injection de packets <img src='https://www.segmentationfault.fr/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> . Et comme il faut compiler ce module, une réinstallation du noyau nécessite de le recompiler une fois de plus. Ce n&rsquo;est pas dur, mais il faut le savoir ! Après avoir expliqué comment (re)compiler le driver, je montrerai comment utiliser le cryptage WPA avec l&rsquo;interface Wifi.</p>
<p><span id="more-17"></span></p>
<h3>Compilation du driver</h3>
<p>Voici donc la marche à suivre pour compiler et utiliser ces drivers. Tout d&rsquo;abord, placez-vous en root dans le dossier /usr/src :</p>
<blockquote></blockquote>
<pre>sudo -s</pre>
<pre>cd /usr/src</pre>
<blockquote></blockquote>
<p>Assurez-vous d&rsquo;avoir les outils pour la compilation (GCC et compagnie). Au cas où, installez le paquet build-essential :</p>
<blockquote></blockquote>
<pre>aptitude install build-essential</pre>
<blockquote></blockquote>
<p>Ensuite, téléchargez le driver, et décompressez-le :</p>
<blockquote></blockquote>
<pre>wget http://homepages.tu-darmstadt.de/~p_larbig/wlan/rt73-k2wrlz-2.0.1.tar.bz2</pre>
<pre>tar jxvf rt73-k2wrlz-2.0.1.tar.bz2</pre>
<blockquote></blockquote>
<p>Placez-vous dans le dossier du driver, compilez-le et installez-le :</p>
<blockquote></blockquote>
<pre>cd rt73-k2wrlz-2.0.1/Module</pre>
<pre>make</pre>
<pre>make install</pre>
<blockquote></blockquote>
<p>Si jamais vous obtenez un message d&rsquo;erreur de ce type à la compilation (après &laquo;&nbsp;make&nbsp;&raquo;)  :</p>
<blockquote></blockquote>
<pre>!!! WARNING: Module file much too big (&gt;1MB)
!!! Check your kernel settings or use 'strip'</pre>
<blockquote></blockquote>
<p>Dans ce cas (et dans ce cas seulement), il faut utiliser l&rsquo;utilitaire &laquo;&nbsp;strip&nbsp;&raquo; et recompiler le driver :</p>
<blockquote></blockquote>
<pre>strip -S rt73.ko</pre>
<pre>make</pre>
<pre>make install</pre>
<blockquote></blockquote>
<p>Une fois que c&rsquo;est fait, il faut bannir (blacklister) les modules Wifi de base d&rsquo;Ubuntu en ajoutant ceci à la fin du fichier /etc/modprobe.d/blacklist, si ce n&rsquo;est pas déjà fait :</p>
<blockquote></blockquote>
<pre>blacklist rt73usb
blacklist rt2570
blacklist rt2500usb
blacklist rt2x00lib</pre>
<blockquote></blockquote>
<p>Pour être sur que l&rsquo;ancien driver ne provoque pas de conflit, déchargeons tous les modules susceptibles de causer des problèmes :</p>
<blockquote></blockquote>
<pre>modprobe -r rt73usb</pre>
<pre>modprobe -r rt2570</pre>
<pre>modprobe -r rt2500usb</pre>
<pre>modprobe -r rt2x00lib</pre>
<blockquote></blockquote>
<p>Testons maintenant le driver ! Allumez votre interface (si vous disposez d&rsquo;un bouton Wifi sur votre machine), chargez le driver et testez l&rsquo;interface :</p>
<blockquote></blockquote>
<pre>modprobe rt73</pre>
<pre>ifconfig rausb0 up</pre>
<blockquote></blockquote>
<p>Normalement, ça marche ! Au cas où ça ne serait pas le cas, une possibilité est de prendre une version antérieure du driver sur la page http://homepages.tu-darmstadt.de/~p_larbig/wlan/ (allez dans la section &laquo;&nbsp;<span style="font-weight: bold">RaLink RT73 USB Enhanced Driver</span>&laquo;&nbsp;) et réeffectuez les étapes précédentes (en adaptant le nom de l&rsquo;archive).</p>
<h3>Utiliser le WPA</h3>
<p>Pour ceux qui sont toujours connectés en WEP, permettez-moi de vous rappeler que cette méthode d&rsquo;encryption est depuis longtemps dépassée car trop facilement crackable ! Avec Aircrack et quelques connaissances, il faut environ 30min pour cracker une clé, et avec la dernière version (et un peut de chance) on peut même atteindre quelques minutes seulement. Si j&rsquo;ai le temps, je montrerai dans un prochain billet comment utiliser Aircrack pour casser une clé WEP. Mais en attendant, passez donc au WPA !</p>
<p>Pour activer le WPA sur l&rsquo;interface, ce n&rsquo;est pas bien compliqué. Tout se passe dans le fichier /etc/network/interfaces. Il faut que vous ajoutiez  des lignes de configuration basées sur ce modèle :</p>
<blockquote></blockquote>
<pre>auto rausb0
iface rausb0 inet dhcp
        pre-up ifconfig rausb0 up  # a enlever si ca marche pas
        pre-up iwconfig rausb0 essid Livebox-XXXX  # le nom du réseau
        pre-up iwconfig rausb0 mode managed
        pre-up iwpriv rausb0 set AuthMode=WPAPSK
        pre-up iwpriv rausb0 set EncrypType=TKIP
        pre-up iwpriv rausb0 set WPAPSK="XXXXXXXXXXXXXXXXXXXXXXXXXX" # la clé WPA</pre>
<blockquote></blockquote>
<p>Cette configuration est celle que j&rsquo;utilise pour me connecter sur ma livebox en WPA. Adaptez-la si besoin. Et assurez-vous que le WPA est activé sur la borne&#8230; Attention toutefois pour les Livebox Inventel : il faut absolument prendre l&rsquo;option &laquo;&nbsp;WPA seulement&nbsp;&raquo; et pas &laquo;&nbsp;WEP et WPA&nbsp;&raquo; dans le menu de configuration, sinon cela ne marche pas. Si votre borne Wifi nécessite l&rsquo;appui sur une touche pour permettre l&rsquo;association Wifi de nouveaux périphériques, appuyez dessus sinon vous ne pourrez pas vous connecter (c&rsquo;est le cas pour les Livebox)</p>
<p>Ensuite, redémarrez les interfaces réseau :</p>
<blockquote></blockquote>
<pre>/etc/init.d/networking restart</pre>
<blockquote></blockquote>
<p>Et si tout va bien, vous devriez obtenir une adresse IP et vous serez alors connectés ! Si jamais cela ne marche pas, c&rsquo;est probablement du à une erreur dans la clé, ou bien à un conflit avec une autre interface. En effet, n&rsquo;essayez pas de vous connecter à la fois en filaire et en Wifi, car au niveau des adresses IP attribuées par le DHCP, il risque d&rsquo;y avoir des problèmes (elles seront toutes les deux dans le même réseau). Essayez de désactiver votre interface réseau en tapant :</p>
<blockquote></blockquote>
<pre>ifconfig eth0 down</pre>
<blockquote></blockquote>
<p>Assurez-vous également qu&rsquo;il n&rsquo;y a pas de ligne du type &laquo;&nbsp;auto eth0&Prime; dans le fichier /etc/network/interfaces, et redémarrez une nouvelle fois les interfaces.</p>
<p>Si vous obtenez un message du style</p>
<blockquote></blockquote>
<pre>DHCPACK from 192.168.1.1
bound to 192.168.1.161 -- renewal in 32485 seconds.</pre>
<blockquote></blockquote>
<p>alors vous êtes connectés !</p>
<p>Attention, le module installé ne fonctionnera pas avec les outils graphiques du style NetworkManager. Enfin, n&rsquo;oubliez pas qu&rsquo;à chaque mise à jour de noyau, il faudra le recompiler (refaire un &laquo;&nbsp;make&nbsp;&raquo; et &laquo;&nbsp;make install&nbsp;&raquo; dans le dossier des sources du driver).</p>
<h3>Conclusion</h3>
<p>J&rsquo;espère que ce billet vous aura été utile, car trouver toute cette procédure m&rsquo;aura demandé un certain temps de recherche. Voici quelques pages qui m&rsquo;ont pas mal servi :</p>
<ul>
<li><a href="http://aircrack-ng.org/doku.php?id=rt73" target="_blank">La page des drivers rt73 sur Arcrack</a></li>
<li><a href="http://homepages.tu-darmstadt.de/~p_larbig/wlan/" target="_blank">Les drivers Ralink supportant l&rsquo;injection </a></li>
<li><a href="http://doc.ubuntu-fr.org/wpa#methode_alternative" target="_blank">La page WPA sur la doc d&rsquo;Ubuntu-fr</a> (la partie intéressante est à la fin, nommée &laquo;&nbsp;<strong>Méthode alternative</strong>&laquo;&nbsp;)</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>https://www.segmentationfault.fr/linux/ubuntu/drivers-wifi-rt73-injection-wpa-ubuntu/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Installation de Kubuntu 7.10 pour AMD 64</title>
		<link>https://www.segmentationfault.fr/linux/ubuntu/installation-de-kubuntu-710-pour-amd-64/</link>
		<comments>https://www.segmentationfault.fr/linux/ubuntu/installation-de-kubuntu-710-pour-amd-64/#comments</comments>
		<pubDate>Fri, 18 Jan 2008 18:53:05 +0000</pubDate>
		<dc:creator>Emilien Girault</dc:creator>
				<category><![CDATA[Ubuntu]]></category>
		<category><![CDATA[64bits]]></category>
		<category><![CDATA[Linux]]></category>

		<guid isPermaLink="false">http://www.segmentationfault.fr/systeme/ubuntu/installation-de-kubuntu-710-pour-amd-64/</guid>
		<description><![CDATA[J&#8217;ai migré vers Kubuntu depuis un peu plus d&#8217;un an et demi, à l&#8217;époque je disposais d&#8217;un processeur 32 bits et j&#8217;avais installé Dapper. J&#8217;ai effectué les mises à jour successives pour passer à Edgy, Feisty, puis Gusty. Mais bricoleur dans l&#8217;âme, j&#8217;ai entre temps changé pas mal de paramètres du système et je me [...]]]></description>
			<content:encoded><![CDATA[<p>J&rsquo;ai migré vers Kubuntu depuis un peu plus d&rsquo;un an et demi, à l&rsquo;époque je disposais d&rsquo;un processeur 32 bits et j&rsquo;avais installé Dapper. J&rsquo;ai effectué les mises à jour successives pour passer à Edgy, Feisty, puis Gusty. Mais bricoleur dans l&rsquo;âme, j&rsquo;ai entre temps changé pas mal de paramètres du système et je me suis lancé à plusieurs reprises dans l&rsquo;installation de Beryl, qui n&rsquo;était pas vraiment stable à l&rsquo;époque. Après l&rsquo;avoir désinstallé, réinstallé, redésinstallé, mis Compiz-Fusion à la place, et finalement désinstallé ce dernier, je dois dire que certaines «imperfections» (le mot est faible&#8230;) étaient apparues sur le système. Et comble de malchance, ma carte graphique a grillé et étant donné que c&rsquo;était de l&rsquo;AGP et que ce standard a quasiment disparu de nos jours, j&rsquo;ai du changer de carte mère, de processeur (un AMD64 3800+), de RAM, et bien entendu de carte graphique. J&rsquo;ai ainsi fait l&rsquo;acquisition d&rsquo;une Nvidia 8500 GT toute neuve&#8230; Et même trop neuve à l&rsquo;époque pour être supportée par les drivers des dépots Ubuntu. J&rsquo;ai donc installé les drivers béta du site officiel en les compilant, ce qui a partiellement mis le bazar dans le système de paquets Ubuntu&#8230; Et entre temps, étant donné que j&rsquo;avais changé de carte mère, Windows XP (en dual boot) refusait catégoriquement de démarrer.</p>
<p>Enfin bref, j&rsquo;ai décidé récemment d&rsquo;en finir avec ces problèmes et de revenir à une installation. J&rsquo;ai réinstallé XP et Kubuntu 7.10 proprement à partir des CD. En plus, j&rsquo;étais motivé pour tester l&rsquo;édition 64bits, alors c&rsquo;était le moment ou jamais. Cependant, comme tout installation, cela nécessite du temps, et rencontrer quelques soucis n&rsquo;est pas rare&#8230; J&rsquo;en profite donc pour détailler ici quelques problèmes que j&rsquo;ai eu et les solutions que j&rsquo;ai trouvées pour les résoudre.  J&rsquo;explique aussi comment restaurer les paramètres principaux de l&rsquo;ancienne distribution sur la nouvelle, en espérant que cela aide les gens qui hésitent encore à réinstaller leur OS ou qui ne savent pas comment configurer certains paramètres du système.<span id="more-13"></span></p>
<h3>Sauvegarde</h3>
<p>Avant tout, je commence par sauvegarder mes données personnelles sur un disque dur externe :</p>
<ul>
<li>ma partition /home/ avec tous mes fichiers et paramètres personnels</li>
<li>le dossier /etc/ contenant les fichiers de configuration</li>
<li>les bases de données MySQL, situées dans le dossier /var/mysql/lib/</li>
</ul>
<p>Ceci étant fait, je boote sur un cd live de Kubuntu Gutsy et je supprime la totalité de mes partitions avec gparted. Souhaitant installer un dual boot WinXP / Kubuntu, je commence naturellement à installer Windows étant donné que si je fais l&rsquo;inverse, celui-ci écrasera Grub&#8230; Une fois Windows installé (je vous passe les détails, ce n&rsquo;est pas le sujet du billet) sur la moitié de mon disque, je lance l&rsquo;<a href="http://ftp.oleane.net/ubuntu-cd/kubuntu/gutsy/kubuntu-7.10-alternate-amd64.iso">Alternate CD de Kubuntu 7.10 x64</a> et j&rsquo;installe le système. Tout se passe bien jusqu&rsquo;au redémarrage : là commence l&rsquo;aventure&#8230;</p>
<h3>Les problèmes après l&rsquo;installation</h3>
<p>Tout d&rsquo;abord, je constate que dès que Grub lance le noyau, mon écran devient noir et affiche «NO SIGNAL»&#8230; Visiblement, il y a un problème concernant la résolution de l&rsquo;écran. Deuxième souci : les disques durs se mettent à gratter et le PC semble figé. Par contre cette fois-ci je sais d&rsquo;où vient le coupable : fsck s&rsquo;est lancé au démarrage et à fait un scan des disques alors que je ne lui avais rien demandé. Toujours à l&rsquo;aveuglette (vu que l&rsquo;écran est noir) je quitte ce scan par Ctrl Alt Suppr. 30 secondes plus tard, l&rsquo;écran se rallume et le curseur de la souris apparaît ainsi que l&rsquo;écran de KDM, victoire ! Mais étant donné que j&rsquo;ai stoppé fsck, le noyau n&rsquo;a pas chargé les partitions (hormis la principale). Je retourne donc en mode console, avec Ctrl Alt F1. Je me logue, et je monte manuellement la partition /home avec :</p>
<blockquote>
<pre>sudo mount /home</pre>
</blockquote>
<p>Ensuite, je retourne en graphique (Ctrl Alt F7) et je me logue et KDE se charge sans problème. Il est désormais temps de configurer tout ça&#8230; après avoir résolu les deux soucis précédents bien sûr. Concernant l&rsquo;écran noir, après un peu de recherche, il semblerait que le paquet usplash (gérant le splashscreen du démarrage) soit buggé pour AMD64. Je désactive donc le splashscreen en éditant le fichier de conf de Grub :</p>
<blockquote>
<pre>sudo nano -w /boot/grub/menu.lst</pre>
</blockquote>
<p>Je retire simplement le mot  «splash» situé à la fin de la ligne concernant mon noyau.</p>
<p>Pour éviter que fsck ne se lance à chaque démarrage sur mon disque, j&rsquo;édite /etc/fstab et je remplace la ligne suivante</p>
<blockquote>
<pre>UUID=XXXX-XXXX  /media/sdb5     vfat    defaults,utf8,umask=007,gid=46 0       1</pre>
</blockquote>
<p>par :</p>
<blockquote>
<pre>UUID=XXXX-XXXX  /media/sdb5     vfat    iocharset=iso8859-15,</pre>
<pre>codepage=850,umask=000 0       0 # sur la meme ligne</pre>
</blockquote>
<p>Cette ligne concerne en fait mon disque dur Sata qui a la fâcheuse tendance à se faire checker par fsck à chaque fois que j&rsquo;installe Ubuntu ; je commence à avoir l&rsquo;habitude car cela m&rsquo;était arrivé quand j&rsquo;avais installé ce même OS cet été sur un autre PC. En fait, il suffisait de remplacer le dernier 1 par un 0, mais étant donné que j&rsquo;ai conservé mon ancien fstab, j&rsquo;ai carrément collé la ligne dans le fichier. Les options que j&rsquo;ai changé servent à ne pas avoir de problèmes pour accéder au disque en tant que simple utilisateur, et pour (normalement) ne pas avoir de soucis dans l&rsquo;encodage des noms de fichiers.</p>
<h3>Configuration générale</h3>
<p>Les deux problèmes étant résolus, passons à la configuration. Étant donné que je suis en chambre universitaire et que notre réseau utilise un proxy, il me faut rajouter le proxy dans les paramètres de wget :</p>
<blockquote>
<pre>sudo nano -w /etc/wgetrc</pre>
</blockquote>
<p>Il suffit de décommenter les deux lignes correspondant au proxy en mettant le bon host et port :</p>
<blockquote>
<pre>#http_proxy = http://proxy.yoyodyne.com:18023/

#ftp_proxy = http://proxy.yoyodyne.com:18023/</pre>
</blockquote>
<p>Ensuite, je constate que KDE a détecté ma carte graphique, je clique donc sur l&rsquo;icône en bas à droite ce qui ouvre le gestionnaire de pilotes propriétaires (restricted-manager-kde) et j&rsquo;active le pilote. On me demande de redémarrer mais je n&rsquo;ai pas encore fini alors je refuse.</p>
<p>Je continue la configuration en éditant le fichier des sources d&rsquo;APT :</p>
<blockquote>
<pre>sudo nano -w /etc/apt/sources.list</pre>
</blockquote>
<p>Je commente la ligne correspondant au CD qui n&rsquo;a pas été commentée, j&rsquo;en profite pour décommenter tous les dépots, je quitte et je met la liste des paquets à jour avec le classique</p>
<blockquote>
<pre>sudo aptitude update</pre>
</blockquote>
<h3>Firefox</h3>
<p>Maintenant qu&rsquo;APT est configuré, j&rsquo;installe quelques programmes de base comme Firefox, Thunderbird, et VLC :</p>
<blockquote>
<pre>sudo aptitude install firefox thunderbird vlc</pre>
</blockquote>
<p>Je lance Firefox, je configure le proxy et en surfant je me rappelle que Flash n&rsquo;est plus installé. Après quelques recherches et tentatives je trouve que pour AMD64 il faut prendre une autre version du paquet et l&rsquo;installer manuellement :</p>
<blockquote>
<pre>wget http://launchpadlibrarian.net/10804892/flashplugin-nonfree_9.0.115.0ubuntu2_amd64.deb

sudo dpkg -i flashplugin-nonfree_9.0.115.0ubuntu2_amd64.deb</pre>
</blockquote>
<p>Je redémarre Firefox, et vérifie que Flash est bien installé en tapant «about:plugins» dans la barre d&rsquo;URL. Ensuite, je me rends compte que les polices Microsoft ne sont pas installées sur mon système ; je lance donc :</p>
<blockquote>
<pre>sudo aptitude install msttcorefonts</pre>
</blockquote>
<p>Je continue en installant mes extensions favorites, à savoir :</p>
<ul>
<li>Adblock Plus, pour bloquer la pub</li>
<li>Adblock Filterset.G Updater, pour mettre à jour les filtres d&rsquo;Adblock</li>
<li>All-in-one Sidebar, pour avoir une barre latérale sympa avec tout de regroupé dedans</li>
<li>Tab Mix Plus, qui rajoute plein d&rsquo;options indispensables pour a gestion des onglets</li>
<li>Urlparams, permettant de manipuler les paramètres GET et POST des pages Web (pour le débug d&rsquo;application Web)</li>
<li>Web developer, une barre d&rsquo;outils très utile, toujours pour développeur Web</li>
<li>Firebug, un outil d&rsquo;une puissance remarquable pour le développement et surtout le débogage (CSS, JavaScript&#8230;)</li>
</ul>
<p>Pour terminer la configuration de Firefox, je copie les fichiers bookmarks.html, signons2.txt et key3.db situé dans mon dossier de profil firefox de l&rsquo;ancien /home que j&rsquo;ai sauvegardé (rappelons que le dossier de profil de Firefox a un nom du style ~/.mozilla/firefox/XXXXXXXX.default). En relançant Firefox, je me retrouve alors avec tous mes mots de passes enregistrés, et tous mes marques pages. Pratique&#8230;</p>
<h3>Le son</h3>
<p>Soudainement j&rsquo;ai envie d&rsquo;écouter de la musique, je lance donc un MP3 dans Amarok. Immédiatement, l&rsquo;application me signale que le MP3 n&rsquo;est pas lisible par défaut et me propose de télécharger les paquets pour le support ; je ne vais pas dire non ! L&rsquo;installation est automatique, il n&rsquo;y a rien à faire. Concernant es paramètres d&rsquo;ALSA et de disposition des hauts parleurs de mon 5.1, je copie le .asoundrc de mon ancien /home. Ainsi, j&rsquo;ai directement du son sur tous mes hauts parleurs : un vrai bonheur&#8230; J&rsquo;utilise ensuite la commande «alsamixer» pour régler le volume de chaque haut parleur, et je sauvegarde les paramètres avec :</p>
<blockquote>
<pre>sudo alsactl store</pre>
</blockquote>
<h3>Le réseau</h3>
<p>Après cela je me décide à restaurer le réseau que j&rsquo;ai établi dans ma chambre universitaire. Je commence par vérifier que mes 2 interfaces réseau ont bien été détectées et que leur ordre ne risque pas de changer dans la suite (cela m&rsquo;est déjà arrivé&#8230;) :</p>
<blockquote>
<pre>sudo nano -w /etc/udev/rules.d/70-persistent-net.rules</pre>
</blockquote>
<p>Et j&rsquo;ai bien fait puisque je constate que les 2 interfaces ont été inversées par rapport à ma préférence ; en effet j&rsquo;aime mieux que le réseau de l&rsquo;intranet soit attribué à eth0, et avoir l&rsquo;interface eth1 reliée à mon PC portable situé dans ma chambre. Je modifie donc les deux lignes du style</p>
<blockquote>
<pre>SUBSYSTEM=="net", DRIVERS=="?*", ATTRS{address}=="XX:XX:XX:XX:XX", NAME="eth0"</pre>
</blockquote>
<p>avec l&rsquo;adresse mac de la carte réseau à la place des X.</p>
<p>Ceci étant fait, je configure /etc/network/interfaces pour avoir un fichier qui ressemble à cela :</p>
<blockquote>
<pre># Reseau general

auto eth0

iface eth0 inet dhcp</pre>
<pre></pre>
<pre># Reseau interne

auto eth1

iface eth1 inet static

        address 192.168.0.1

        netmask 255.255.255.0</pre>
</blockquote>
<p>Et je relance les interfaces avec :</p>
<blockquote>
<pre>sudo /etc/init.d/networking restart</pre>
</blockquote>
<h3>Bilan</h3>
<p>Voilà, je pense que j&rsquo;ai fait le tour de toutes les modifications que j&rsquo;ai apportées. Après un redémarrage, tout semble correct, mise à part le fait que je n&rsquo;ai plus de splashscreen au démarrage. Je vais essayer de me documenter sur ce point car j&rsquo;avoue que cela me manque <img src='https://www.segmentationfault.fr/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Sinon, j&rsquo;ai toujours un problème de locale : je voudrais que mon système soit de base en ISO8859-15 alors qu&rsquo;il est de base en UTF-8. C&rsquo;est particulièrement gênant pour les noms de fichiers et même pour leur contenu, surtout quand on doit éditer des fichiers qui ne sont pas encodés de la même façon&#8230;</p>
<p>Si je devais faire un bilan de cette installation, je dirais que je suis assez satisfait. En installant la version 64bits, je m&rsquo;attendais à rencontrer des problèmes, et à part le problème du splash et l&rsquo;installation de Flash non conventionnelle, je n&rsquo;ai rencontré aucun soucis propre à cette architecture. Je pense que c&rsquo;est notamment grâce au fait que les paquets Ubuntu ont eu le temps de mûrir, car l&rsquo;installation de certains paquets en 64bits il y a plusieurs mois était autrement plus complexe.</p>
<p>Comparé à mon ancienne version 32bits, je constate un gain appréciable en performances. Après, je ne sais pas si cela vient du 64bits ou simplement du fait que l&rsquo;installation est propre et pas une upgrade d&rsquo;upgrade bidouillée dans tous les coins. En tout cas je sens la différence, et même si j&rsquo;étais sceptique sur le 64bit au début, je pense que je vais adopter cette version.</p>
<p>Je me rends également compte à quel point restaurer les paramètres d&rsquo;un tel système est simple : quelques lignes de commande, deux ou trois fichiers à copier ou à éditer, et on gagne un temps fou en évitant de tout reconfigurer. Attention toutefois de ne pas tomber dans l&rsquo;extrème et de restaurer brutalement tout son /etc ou son /home ! Cela pourrait poser des problèmes de compatibilité ; de plus si vous aviez des bugs dûs à des problèmes de configuration dans votre ancienne distribution, ils seront propagés dans la nouvelle&#8230; Par contre, concevoir un petit script Bash peut être une solution afin d&rsquo;automatiser ce qui peut l&rsquo;être, comme la copie de certains fichiers, et l&rsquo;installation des paquets principaux.</p>
<p>J&rsquo;espère que la marche à suivre aura aidé ceux qui se sont posés les mêmes questions que moi concernant la réinstallation d&rsquo;une distribution. Un dernier conseil : surtout, n&rsquo;oubliez pas de faire un backup total de vos données personnelles (le /home, le /etc et les éventuelles bases de données dans /var/mysql/lib) avant. Si vous avez des choses à ajouter, les commentaires sont là pour ça ! Bon surf <img src='https://www.segmentationfault.fr/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>https://www.segmentationfault.fr/linux/ubuntu/installation-de-kubuntu-710-pour-amd-64/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
