. ____ _________ . _ __ |__ \ __ __ _ |____ ___| _ _____ __ ___ | | / _\ / _| | / \ | \| | / _||| | | ||_ _| / \ / __| | |_| _/ \ \ / | || || | \ \| || | | | | | | || | \ \ |___|\__/|__/| \ | || || |\ |__/ /| || |_| | | | | || |__/ / | | | \__/ |_| |_____/ | | \___/ |_| \__//___/ + |____/_/ | _ _ |_| . _ / _ | | | | _____ . | | \___|\ \_/\ |_ _| . . | | |_ ||_ | | | . + _ _ __ | |__ |_ ||_ |__| | __ _ _ ___ _ | \| | / \|____||__/ |__// _|_| / \ | \| | / \ | | + + | || || || || | || | ___ | || || || | || | | |\ || || || |\ || _ || ||_ || || || |\ || _ || |__ |_||_| \__/ |_||_||_| |_| \____/ \__/ |_||_||_| |_||____| + . . 0x00 -+- . ' . . ___________ /|___________________________________________ / \ | Pourquoi jouer à Call of Duty ou MineCraft quand | | tu peux apprendre des trucs trop b4d455 avec Denis Salem? | . \__________________________________________________________/__|\___ / \ | https://denissalem.tuxfamily.org | \__________________________________/ Frustré par l'éducation national, et le flou pédagogiques des internets je rédige cet eZine en solo pour la postérité (mais aussi un peu pour la bonne cause). Puisse ce release t'être utile, ô lecteur. ______________________________________________________________________________ | | | I N D E X | |______________________________________________________________________________| | | | 0x00 ASM : Ton premier programme ne sera pas un HelloWorld | \___________________________________________________________| ______________________________________________________ / \ | ASM : Ton premier programme ne sera pas un HelloWorld | \_ ___________________________________________________/ /' __0x00__ On va parler ici exclusivement d'assembleur linux pour processeur x86_64, architecture largement répendu sur la plupart des ordinateurs. Par ailleurs nous ne nous reposerons que sur la suite gcc lors des différentes étapes de la compilation. Commençons avec un code über simple. .text .global _start _start: movl $9,%ebx movl $1,%eax int $0x80 Et ouais morray. Rien qu'avec les six lignes ci-dessous, y à de quoi tenir un consortium. Alors un Hello world... Tu pense bien... Avant d'expliquer que fait chaque ligne nous allons assembler et exécuter tout ça. Par convention, l'extension d'un nom de fichier source est ".s". Copiez les six lignes de code ci-dessus dans un fichier vierge. Genre "demo1.s". Ensuite, dans un terminal: [l33t@nonagon asm]$ as demo1.s -o demo1.o [l33t@nonagon asm]$ ls demo* -l -rw-r--r-- 1 l33t l33t 696 30 mars 19:53 demo1.o -rw-r--r-- 1 l33t l33t 168 30 mars 19:53 demo1.s _______________________________________________________________________ | | | /!\ N'oublie pas de te reporter au manuel pour en savoir plus sur as. | |_______________________________________________________________________| Cette première étape consiste à produire des objets qui serons ensuite lié pour finalement obtenir un executable. L'option -o de as permet de spécifier le nom de fichier de sortie. [l33t@nonagon asm]$ ld -o demo1 demo1.o [l33t@nonagon asm]$ ls -l demo* -rwxr-xr-x 1 l33t l33T 664 30 mars 19:57 demo1 -rw-r--r-- 1 l33t l33t 696 30 mars 19:53 demo1.o -rw-r--r-- 1 l33t l33t 168 30 mars 19:53 demo1.s Nous obtenons finalement un fichier exécutable. Genre pour de vrai. Sans plus tarder, découvrons ce que fais notre exécutable.Un truc sensationnel,assurément. [l33t@nonagon asm]$ ./demo1 [l33t@nonagon asm]$ Bon. Et ben c'est pas folichons tout ça. Mais nous savons que tout programme retourne systématiquement une valeur entière. Généralement zéro, si le programme s'est terminé correctement. Sinon un code d'erreur qu'il est possible d'intercepter. Voyons ça. [l33t@nonagon asm]$ ./demo1 [l33t@nonagon asm]$ echo $? 9 [l33t@nonagon asm]$ Notre premier programme ne fais en fait strictement rien, sinon retourner 9. Il est interressant de noter que les instructions pour... ne rien faire constituent quand même un exécutable de 664 bytes. Un scandale sur lequel nous reviendrons plus tard. Notre programme ne produit pas d'erreurs et retourne sciemment un entier non nul. Examinons le code précédent et agrémentons le de quelque commentaires. Ceux sur une unique ligne commence par "//" et ceux qui en nécessitent plusieurs commence avec "/*" et se termine avec "*/". .text // Debut de la section ".text". .global _start /* On indique à as que le label "_start" doit être visible pour tous les autres objets. */ _start: /* Définition du label "_start". Toutes les instructions qui suivent constituent le corps du label jusqu'à la prochaine définition. */ movl $9,%ebx // On place les valeurs immédiates 9 movl $1,%eax // et 1 dans les registres eax et ebx int $0x80 /* Appel de l'interuption logiciel numéro 128 */ La directive ".text" indique le début d'une section du même nom. Cette section contient les instructions du programme et uniquement les instructions. Il n'est pas possible d'y stocker des valeurs ou d'y réserver de l'espace mémoire. La directive global indique que le label "_start" doit être accessible depuis les autres objets. On définit ensuite un label. Le nom d'un label peut-être constitué de n'importe quel caractère alpha-numérique ainsi que des caractères '_', '.' et '$'. La notion de label est très importante. Lors de l'exécution d'un programme, le processeur se déplace linéairement dans celui-ci. C'est à dire qu'il lit une instruction, l'exécute, passe à la suivante et ainsi de suite. Pour se faire le processeur utilise le registre RIP (EIP pour les architectures 32bits) qui repère l'adresse de l'instruction courante. Une fois l'exécution de cette instruction terminé RIP est automatiquement incrémenté. Dans la majorité des cas on souhaite controler le fil d'exécution, par exemple lorsque l'on veut faire une boucle, dans ces cas là on modifie manuellement RIP. Lors du processus de compilation, le label sera automatique convertie en une adresse qu'il sera alors possible de stocker dans RIP. La définition d'un label est donc important parce qu'elle permet de situer une portion de code pour y "sauter" quand cela est nécessaire. Dans notre cas le nom du label n'est pas choisit au hasard, c'est le nom par défaut du point d'entrée du programme. Ainsi la première instruction du programme sera movl $9, %ebx Le saut à la ligne marque la fin de l'instruction. Une instruction peut avoir une ou plusieur opérandes. Dans la syntax AT&T on place la source AVANT la destination. L'instruction movl sert à copier une valeur vers un emplacement qui peut être un registre on une zone mémoire. Ainsi dans notre exemple, on place successivement les valeurs immédiates 9 et 1, respectivement, dans les registres ebx et eax. Une valeur immédiate est indiqué par le caractère '$', tandis qu'un registre est préfixé par '%'. La dernière instruction int interrompt le programme et réalise un appel système, il s'agit de passer la main à une fonction du noyau, puis de reprendre le programme là où il en était. Pour faire simple l'opérande de l'instruction int * voir instruction handler / ISR _________________________________________________ / \ | Documentation de référence et sources à consulter | \_ ______________________________________________/ /' __0x__ Assembleur - http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html/ - http://sourceware.org/binutils/docs/as/ - http://lwn.net/Articles/531148/ - http://www.bravegnu.org/gnu-eprog/linker.html