Dans cet ultime volet nous nous attaquerons à la conception d'un serveur socket. Oulaaaa.. chôôôoo.. OUI ! MAIS ! C'est l'assurance d'un poil brillant pour toute l'année! Alors, partant?.. Coquin(e) va :)
Le code si dessous s'acquitte d'une tache simple: accepter une connexion, lire les données qu'on lui envoie et renvoyer un anagramme dès que l'utilisateur appuie sur la touche entrée. Si l'utilisateur ne saisis rien et appuie sur la touche entrée, l'application se termine.
1 section .bss 2 buffer: resb 256 3 4 section .text 5 global main 6 extern strfry 7 extern strlen 8 extern fprintf 9 extern puts 10 extern socket 11 extern bind 12 extern dup2 13 extern accept 14 extern listen 15 extern read 16 extern write 17 extern close 18 extern perror 19 20 section .rodata 21 KERN: equ 0x80 22 STDIN: equ 0 23 STDOUT: equ 1 24 STDERR: equ 2 25 AF_INET: equ 2 26 SOCK_STREAM: equ 1 27 IPPROTO_TCP: equ 6 28 INADDR_ANY: equ 0 29 SOCKADDRIN_SIZE: equ 16 30 PORT: equ 47269 31 32 section .data 33 str_perror: db "Perror " 34 35 align 4 36 sockaddr_in: 37 sin_family: dw 0 38 sin_port: dw 0 39 sin_addr: dd 0 40 sin_zero: db 0,0,0,0,0,0,0,0 41 42 sock_listen: dd 0 43 sock_accept: dd 0 44 socklen: dd 0 45 length: dd 0 46 47 48 main: 49 push dword 0 50 push dword SOCK_STREAM 51 push dword AF_INET 52 call socket 53 add esp, 12 54 55 cmp dword eax, 0 56 jnz asm_perror 57 58 mov [sock_listen], eax 59 60 mov word [sin_family], AF_INET 61 mov dword [sin_addr], INADDR_ANY 62 mov word [sin_port], PORT 63 64 push dword SOCKADDRIN_SIZE 65 push sockaddr_in 66 push dword [sock_listen] 67 call bind 68 add esp, 12 69 70 cmp dword eax, 0 71 jnz asm_perror 72 73 push dword 1 74 push dword [sock_listen] 75 call listen 76 add esp, 8 77 78 cmp dword eax, 0 79 jnz asm_perror 80 81 mov dword [socklen], SOCKADDRIN_SIZE 82 push socklen 83 push sockaddr_in 84 push dword [sock_listen] 85 call accept 86 add esp, 12 87 88 cmp dword eax, 0 89 jnz asm_perror 90 91 mov [sock_accept], eax 92 93 read_lbl: 94 push dword 256 95 push buffer 96 push dword [sock_accept] 97 call read 98 add esp, 12 99 100 cmp dword eax, 3 101 jnz asm_perror 102 103 mov [length], eax 104 105 ; removing \r\n 106 mov dword ebx, [length] 107 mov byte [buffer+ebx-2],0x00 108 mov byte [buffer+ebx-1],0x00 109 110 push buffer 111 call strfry 112 add esp, 4 113 114 ; adding \n 115 mov byte [buffer+ebx-1],0x0a 116 117 push dword [length] 118 push buffer 119 push dword [sock_accept] 120 call write 121 add esp, 12 122 123 cmp dword eax, 0 124 jnz asm_perror 125 126 jmp read_lbl 127 128 close_sock_accept: 129 push dword [sock_accept] 130 call close 131 add esp,4 132 133 close_sock_listen: 134 push dword [sock_listen] 135 call close 136 add esp, 4 137 138 exit: 139 xor eax, eax 140 inc eax 141 xor ebx, ebx 142 int 80h 143 144 asm_perror: 145 push str_perror 146 call perror 147 add esp, 4 148 cmp dword [sock_accept],0 149 jnz close_sock_accept 150 cmp dword [sock_listen],0 151 jnz close_sock_listen 152 jmp exit
152 lignes, ça reste correct, sachant qu'il y a une gestion minimale des erreurs avec perror. Oui, car on aurait pu se la jouer crado hein..
Si vous lisez ces lignes, je suppose que vous avez lu les deux premières parties, et vais donc me contenter d'expliquer les parties pas ou peu étudiées dans les parties précédentes.
Vous êtes aussi censés savoir comment fonctionne un serveur socket.
Ligne 1: Section .bss, là où on alloue de la mémoire.
Ligne 2: Ici on réservere 256 octets (bytes), et comme nous savons tous, un char = 1 octet sur nos architextures x86, et comme vous vous en doutez cette lignes équivaut à un “buffer[256];” en C.
Ligne 4: La section .text où on déclare la fonction principale et les fonctions externes. Genre ici on déclare toutes les fonctions de la libC que l'on va utiliser dans notre programme. On pourrait très bien utiliser des fonctions d'autres librairies, mais pour ce faire il faudra l'indiquer à gcc avec un truc genre -lqqc..
Ligne 20: La section .rodata où l'on déclare toutes nos constantes, l'équivalent d'un “#define BLAH 654”..
Ligne 32: La section data où l'on déclare nos variables. Vous remarquerez ici la déclaration de la fameuse “struct sockaddr_in”. En fait ici on ne fait que déclarer une variable nommée sockaddr_in, qui va pointer sur sin_family. Ouais c'est la même zone mémoire, c'est ce qu'il faut voir. Pour connaitre le nombre d'octets à réserver pour chaque membre de la structure, référez vous aux headers de la libc.
Ligne 49 à 53: Ce qu'il faut retenir ici c'est que pour passer des argument à une fonction suffit de les empiler sur la stack dans l'ordre inverse de la déclaration. Genre ici pour socket(..) on va push en premier le dernier argument, puis le second, puis le premier.
Ensuite on call socket qu'on a déclaré dans la section .text, puis on enlève les arguments de la stack (comme 4*3=12 on enlève 12 octets).
Ligne 55-56: Ici on ne fait que vérifier le retour de socket. Si le résultat n'est pas supérieur à 0 alors on appelle perror.
Dans les lignes qui suivent on remplit notre pseudo structure, puis on appelle bind.
Je pense que la suite du code se passe de commentaires, si vous avez compris les principes de base, ce n'est qu'une répétition de push/call/add/cmp hein…
Ligne 93: Boucle principale du programme où on attend que l'utilisateur envoie du texte sur la socket.
On vérifie que la chaine fasse plus de 3 caractères, sinon ça voudrait dire que l'utilisateur veut quitter le programme. CF explications du début.
Comme on suppose que l'utilisateur utilise telnet, on enlève \r\n de la fin de la chaine pour pas qu'strfry les prenne en compte.
Ensuite la fin de programme est on ne peut plus claire, enfin je l'espère.
Pour compiler tout ça :
nasm -felf sockstrfry.asm gcc sockstrfry.o -o sockstrfry
Il y a quelques oublis volontaires, qui ne vous empêcherons pas de faire tourner le programme. J'espère que vous saurez les apercevoir.
A présent vous êtes censés pouvoir voler de vos propres ailes et faire des programmes utiles en assembleur.
Pour terminer voici tous les liens utiles que j'ai sur l'ASM. Si ces turotiels vous ont plus, un petit mail me ferait plaisir, et dans le cas contraire un mail expliquant ce qui vous a déplu m'aidera pour mes prochaines rédactions.
Certains sont mieux que d'autres, mais tous m'ont été utiles à un moment donné ou à un autre. J'espère qu'ils vous permettront d'approfondir vos connaissance.
J'espère enfin vous avoir donné envie d'aller plus loin.
http://www.monkey.org/openbsd/archive/misc/0105/msg01202.html http://inferno.cs.univ-paris8.fr/~am/tutorial/os/nasmC.html http://asm.sourceforge.net/resources.html#tutorials http://docs.cs.up.ac.za/programming/asm/derick_tut/ http://www.alrj.org/docs/asm/linux-asm.html http://docs.cs.up.ac.za/programming/asm/derick_tut/syscalls.html http://www.iprezo.org/index.php?page=asm http://rs1.szif.hu/~tomcat/win32/apj3.txt http://jeanfrancoisdelnero.free.fr/hard.htm http://www.leto.net/writing/nasm.txt http://milw0rm.org/papers/46 http://download.savannah.gnu.org/releases/pgubook/ http://sourceware.org/binutils/docs-2.17/as/index.html http://database.sarang.net/study/linux/asm/linux-asm.txt http://www.coder-studio.com/index.php?page=tutoriaux_aff&code=asm_4 http://mdbui2.ift.ulaval.ca/17583_E2007/Labs/Instructions_Chaines_caracteres.htm http://www.developpez.net/forums/showthread.php?t=378804 http://cui.unige.ch/DI/cours/1840/cours/ http://www.dil.univ-mrs.fr/~jfp/tp_lex_yacc2005/yacc/docAs/Assembleur/index.html http://www.ai.univ-paris8.fr/~n/gpi/GPIas32bits/GPIas32bits.html http://pluton.up.univ-mrs.fr/eremy/Ens/Info1.Archi/asm.html http://www.games-creators.org/wiki/Tutorial_7_:_Les_instructions_asm_les_plus_courantes_et_le_corps_d'un_programme_FASM_pour_les_librairies http://www.technocage.com/~ray/notespage.jsp?pageName=nasmexamples http://www.kernelthread.com/programming/miscellaneous/asm/ http://www.csee.umbc.edu/~plusquel/310/nasm/ http://pdos.csail.mit.edu/6.828/2004/readings/i386/toc.htm http://asm.sourceforge.net/articles/ http://www.alrj.org/docs/asm/linux-asm.html http://www.pouet.free.fr/docs/boso/tutorial07.html http://flyers.next-touch.com/data/homework/article/Nasm_fr.htm
— jfg 2007/09/21 00:37