IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

x86 32-bits / 64-bits Assembleur Discussion :

[Débutant] Inversion d'une chaîne


Sujet :

x86 32-bits / 64-bits Assembleur

  1. #41
    Invité
    Invité(e)
    Par défaut
    Hmm bizarrement c'est parce que j'adore m'arracher les cheveux (désolé en panne de synonymes le matin) pour un problème nain que je t'ai fait part de ça

    Sinon oui je ne te cache pas qu'au début, j'ai eu besoin de formations sur le net, mais après j'ai vite fait de passer à la pure création ^^

    Même si j'ai quelques formations de base qui persistent, faut avouer.

  2. #42
    Expert éminent Avatar de BufferBob
    Profil pro
    responsable R&D vidage de truites
    Inscrit en
    Novembre 2010
    Messages
    3 035
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : responsable R&D vidage de truites

    Informations forums :
    Inscription : Novembre 2010
    Messages : 3 035
    Points : 8 400
    Points
    8 400
    Par défaut
    Citation Envoyé par Abdoulito Voir le message
    l'utilisation de esi & edi (qui semble logique vus les noms de ces registres) était surtout là pour gagner en clarté c'est ça ?
    ça et c'est une question d'habitude aussi, des instructions comme movs{b,w,d,q} utilisent directement les registres ds:{,e,r}si et es:{,e,r}di (tout en incrémentant automatiquement {,e,r}cx), c'est donc plutôt une bonne habitude à prendre à mon sens, mais en l'occurrence ça pouvait fonctionner avec d'autres registres aussi bien effectivement

    quand on récupère la chaine de caractères, est-ce qu'une zone mémoire lui est allouée et le pointeur est stocké dans edx, ou alors la chaine est simplement mise dans le registre ?
    rien n'est automatique en assembleur, c'est à toi de prévoir de la place pour stocker la chaine lue via stdin, pour le reste il suffit d'aller regarder la page de manuel qui correspond au syscall, man 2 read qui donne le prototype ssize_t read(int fd, void *buf, size_t count);

    donc :
    • dans eax on met le numéro du syscall sys_read (3 pour x86)
    • dans ebx on met le descripteur fd (0 pour STDIN_FILENO)
    • dans ecx on met buf le pointeur (l'adresse) du début du buffer alloué
    • dans edx on met count le nombre d'octets qu'on veut lire


    donc de manière très simple, il suffit d'allouer de la place sur la pile et faire pointer ecx sur esp par exemple

    Code nasm : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    %define sys_exit     1
    %define sys_read     3
    %define sys_write    4
    %define stdin        0
    %define stdout       1
    %define sizebuf      16
     
    %macro syscall2 2
       mov eax, %1
       mov ebx, %2
       int 0x80
    %endmacro
     
    %macro syscall4 4
       mov eax, %1
       mov ebx, %2
       mov ecx, %3
       mov edx, %4
       int 0x80
    %endmacro
     
    section .text
    global _start
     
    _start:
       mov ebp, esp
       sub esp, sizebuf
     
       syscall4 sys_read, stdin, esp, sizebuf
       syscall4 sys_write, stdout, msg, sizemsg
       syscall4 sys_write, stdout, esp, sizebuf
       syscall2 sys_exit, 0
     
    section .data
       msg db 'vous avez ecrit : '
       sizemsg equ $ - msg

    Edit/PS: concernant la section .toto qui permet l'exécution de code malgré l'absence de droit SHF_EXECINSTR il semble que ce soit le comportement par défaut du noyau Linux, avec un patch grsec ou un bit NX activé à priori ça ne fonctionne plus

  3. #43
    Membre chevronné
    Avatar de Forthman
    Homme Profil pro
    conception mécanique
    Inscrit en
    Janvier 2005
    Messages
    702
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Tarn et Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : conception mécanique
    Secteur : Industrie

    Informations forums :
    Inscription : Janvier 2005
    Messages : 702
    Points : 1 905
    Points
    1 905
    Par défaut
    Citation Envoyé par Abdoulito Voir le message
    C'est encore moi !
    A ce sujet j'ai une question : quand on récupère la chaine de caractères, est-ce qu'une zone mémoire lui est allouée et le pointeur est stocké dans edx, ou alors la chaine est simplement mise dans le registre ?
    edx étant un registre 32 bits, si ta chaîne est codée en ASCII (8bits) tu ne pourras y stocker que 4 caractères.
    donc oui, edx sert de pointeur

  4. #44
    Invité
    Invité(e)
    Par défaut
    Citation Envoyé par Forthman Voir le message
    edx étant un registre 32 bits, si ta chaîne est codée en ASCII (8bits) tu ne pourras y stocker que 4 caractères.
    donc oui, edx sert de pointeur
    Il ne faut pas forcément voir cela comme ça, j'y vois plutôt comme un boost de déplacement de données, au lieu de traiter caractère par caractère, on en fait 4 à la fois

    Enfin pour ce cas là, comme le but c'est de faire du traitement par octets, ce n’est pas la méthode la plus naturelle, sauf que je pense que c'est plus opti en faisant ainsi, mais encore faut-il trouver un bon algo pour inverser les bytes, avec la rotation ça peut marcher comme la fait bufferbob.

    [Pub]
    Ou mieux encore avec des instructions avx et avec des registres avx (256-bits)
    D'ailleurs je pense même que l'on peut faire une seule inversion de tous les octets en une instruction en avx dans un reg. 256-bits.

    Et pour bientôt, des registres 512-bits (avx-512)
    [/Pub]
    Dernière modification par Invité ; 22/09/2015 à 13h44.

  5. #45
    Nouveau membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2015
    Messages
    14
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 28
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Août 2015
    Messages : 14
    Points : 26
    Points
    26
    Par défaut
    J'ai réussi à écrire mon programme de façon à prendre la chaîne tapée au clavier en entrée. C'était pas si compliqué mais ça m'a permis de progresser. J'ai repris l'idée d'une des solutions que vous m'avez données mais j'ai quand même pris la peine de réécrire le tout

    Mon code, qui interessera surement le prochain débutant à tomber sur ce topic :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    section .text
    global _start
    _start:
    mov	eax, 	3			 
    mov	ebx, 	0		 	
    mov     ecx, 	msg        	
    mov     edx, 	10		 	; longueur
    int	0x80                ; stdin
     
    mov	eax,	10
     
    cherche_fin:
     
    dec	eax
    lea     esi, [msg+eax]
    cmp     BYTE [esi], 0
    jz cherche_fin			;après ce passage esi pointe sur le dernier caractère (car msg est dans bss)
     
    add	eax,	1
    mov     edx,	eax			;on sauvegarde la taille dans edx
    mov     ebp, esp
    sub	esp,	eax		
    mov	edi,	esp			;réservation de len octets sur la pile
     
    boucle:
     
    mov     bl,		[esi]
    mov     [edi],	bl
    inc	edi
    dec     esi
    dec	eax
    cmp     eax, 	0
    jnz boucle
     
     
    mov     eax,	4		
    mov     ebx,	1	
    mov     ecx, 	esp
    int     80h					;stdout msg, edx est déja initialisé
    mov	eax,	1
    mov	ebx,	0
    int     80h					;quit
     
    section .bss
    msg	resb	10
    Ayant essayé avec des chaînes trop longues, j'ai même l'impression que c'est sécurisé, mais je me risquerais pas à l'affirmer. Si un expert peut m'en dire plus j'en serais ravi.

    Dans tous les cas merci de votre aide, encore une fois.

    EDIT : désolé pour les tabulations dans le code, l'afficheur du forum n'a pas l'air d'apprécier
    EDIT2 : là ça doit être bon

  6. #46
    Responsable Systèmes


    Homme Profil pro
    Gestion de parcs informatique
    Inscrit en
    Août 2011
    Messages
    17 434
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Gestion de parcs informatique
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Août 2011
    Messages : 17 434
    Points : 43 065
    Points
    43 065
    Par défaut
    oui, je ne me l'explique pas -encore ;p- d'ailleurs, car si .text est reconnue par le linker comme étant une section spéciale devant avoir des droits spécifiques il n'est est pas de même pour une section .toto ou .code, un objdump -h -j .text/.code permet de se rendre compte que les droits sur la plage mémoire ne sont pas les mêmes, notamment qu'une section nommée .code n'a pas de droits d'exécution, en toute logique le code ne devrait pas pouvoir s'exécuter
    Je ne comprends pas ce que tu dit là (mais c'est par manque de compétence).

    Je pense que les droits, il faut les regarder au niveau des program headers (objdump -x)

    J'ai essayé avec :

    nasm gueule. Il accepte .text ou .code pas pas autre chose.

    Pour le désassemblage avec gdb, ça vient pas du point d'entrée ? gdb attend main, mais mon point d'entrée c'est _start (option -nostdlib), on doit pouvoir le passer à gdb mais je sais pas faire.

    Abdoulito, c'est difficile de dire quelle méthode est la meilleure par rapport à une autre, je dirais que ça dépend beaucoup du contexte. Un code facilement relisible ne sera pas forcément optimal.

    Tu as par exemple l'instruction loop qui sert à faire une boucle en décrémentant le registre ecx
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    boucle:
    ....
    ...
    loop boucle
    Il n'est pas faut de l'utiliser mais on utilisera plutôt un décrémentation de ecx et un saut conditionnel (jcxz).

    Tout comme on ne fait pas :
    mais :
    pour mettre eax à 0, et bien qu'avec les processeurs récents, je suis pas sûr que ce soit significatif, donc si tu ne comprends pas xor eax,eax, utilises mov eax,0

    L'explication est que xor eax,eax va avoir le même résultat final, mais qu'il prendra beaucoup moins de temps à faire par le CPU que mov eax,0 (cela est du à sa logique interne).
    Ma page sur developpez.com : http://chrtophe.developpez.com/ (avec mes articles)
    Mon article sur le P2V, mon article sur le cloud
    Consultez nos FAQ : Windows, Linux, Virtualisation

  7. #47
    Invité
    Invité(e)
    Par défaut
    Finalement tu abandonnes l'idée de sauvegarder la phrase écrite à l'envers ?
    Parce que là elle n'est que dans la pile, donc tu vas perdre la phrase si tu remontes le contexte de la pile de ta fonction et que tu fais appelle à une autre fonction qui réalloue de la pile :/

    C'est un peu dommage, mais bon.

    "EDIT : désolé pour les tabulations dans le code, l'afficheur du forum n'a pas l'air d'apprécier"

    C'est pour cela que je n'accepte que des espaces quand je code ^^

    Et l'histoire du global _start, perso j'en ai pas besoin, j'ai juste à mettre le label d'entrée dans mon linker et ça roule ma poule
    Dernière modification par Invité ; 22/09/2015 à 21h47.

  8. #48
    Nouveau membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2015
    Messages
    14
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 28
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Août 2015
    Messages : 14
    Points : 26
    Points
    26
    Par défaut
    Je n'y avais pas pensé. Là ce n'est qu'un exercice donc osef un peu, surtout que ce ne sont que des changements minimes.

    Pour les optimisations, c'est surtout que j'ai pas l'habitude de les utiliser, je vais essayer de m'en souvenir en effet.

    Je passe en résolu !

  9. #49
    Expert éminent Avatar de BufferBob
    Profil pro
    responsable R&D vidage de truites
    Inscrit en
    Novembre 2010
    Messages
    3 035
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : responsable R&D vidage de truites

    Informations forums :
    Inscription : Novembre 2010
    Messages : 3 035
    Points : 8 400
    Points
    8 400
    Par défaut
    Citation Envoyé par Abdoulito Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    (...)
    cherche_fin:
       dec eax
       lea esi, [msg+eax]
       cmp BYTE [esi], 0
       jz cherche_fin
    (...)
    tu utilises une routine cherche_fin pour trouver la fin de ta chaine entrée sur stdin, tu as lu la manpage du syscall read ou toujours pas ? après ton read tu récupères dans eax le nombre d'octets lus, ça peut simplifier la vie non ?

    admettons que tu aies besoin d'une telle routine (ça n'est pas le cas ici), je te propose la solution suivante qui tire parti de l'instruction scas :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
       xor eax, eax
       xor ecx, ecx
       dec ecx
       mov edi, msg
       repnz scasb   ; à la fin edi pointe sur le dernier élément de la chaine (le zéro trouvé)
       neg ecx       ; (facultatif) besoin de la longueur directement ? ecx == edi - msg
    aller voir dans la doc de quoi il retourne pour scasb, repnz et pourquoi le comment arrive quand est laissé en exercice



    Citation Envoyé par chrtophe Voir le message
    Je ne comprends pas ce que tu dit là (...)
    Je pense que les droits, il faut les regarder au niveau des program headers (objdump -x) (...)
    J'ai essayé avec section .toto, nasm gueule. Il accepte .text ou .code pas pas autre chose.
    plus concis tu as objdump -h ou encore readelf -S comme je l'ai marqué dans un post précédent
    et effectivement, on voit bien une différence, on part sur ce code-ci section.tpl :
    Code nasm : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    section <NAME>
    global _start
     
    _start:
       mov eax, 4
       mov ebx, 1
       mov ecx, msg
       mov edx, len
       int 0x80
     
       mov eax, 1
       mov ebx, 0
       int 0x80
     
    section .data
       msg db 'hello, world !', 0xa
       len equ $ - msg
    de l'autre on se fait un script vroom.sh qui va tout faire pour nous et sans erreur possible, changer le nom de la section à la volée, compiler, linker, vérifier les headers et tenter d'exécuter le binaire si il est créé :
    Code bash : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    #!/bin/bash
    for i in .text .code .toto .saucisse
    do
       sed 's/<NAME>/'${i}'/' section.tpl > section_${i}.asm && # on modifie le nom de la section à la volette
       nasm -f elf section_${i}.asm &&                          # on compile
       ld section_${i}.o -o section_${i} &&                     # on link
       objdump -h section_${i} | awk '/  0 \./,/^   /' &&       # on check les attributs de la section concernée
       ./section_${i}                                           # on essaye d'exécuter ?
    done
    et on lance le tout :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    $ ./vroom.sh 
      0 .text         00000022  08048080  08048080  00000080  2**4
                      CONTENTS, ALLOC, LOAD, READONLY, CODE
    hello, world !
      0 .code         00000022  08048074  08048074  00000074  2**0
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
    hello, world !
      0 .toto         00000022  08048074  08048074  00000074  2**0
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
    hello, world !
      0 .saucisse     00000022  08048074  08048074  00000074  2**0
                      CONTENTS, ALLOC, LOAD, READONLY, DATA
    hello, world !
    donc chez moi ça marche, mais c'est une VM ubuntu un peu vieillissante/pas mise à jour, il se peut qu'entre temps certaines protections se soient un peu plus généralisées aussi, tu as testé sur quelle distribution ?



    Citation Envoyé par chrtophe Voir le message
    Pour le désassemblage avec gdb, ça vient pas du point d'entrée ? gdb attend main, mais mon point d'entrée c'est _start (option -nostdlib), on doit pouvoir le passer à gdb mais je sais pas faire.
    non, suffit de regarder l'output juste au dessus, le point d'entrée est le même pour tous les exécutables, gdb n'attend pas main (le point d'entrée de TOUS les programmes est _start, un programme C compilé avec gcc contient y compris ce label, qui ensuite renvoit vers __libc_start_main etc. jusqu'à finalement atterrir dans main, mais y'a pas mal de code qui déroule avant d'y arriver), un (gdb) disassemble _start suffit à désassembler... dans le cas d'une section .text uniquement

    en réalité, si on prend un autre code d'exemple, avec un read(0, esp, 1) par exemple (pour que le programme soit bloquant), on s'aperçoit en allant regarder dans /proc/<PID>/maps qu'en réalité la région mémoire honore bien un droit x (d'exécution), la question c'est d'où vient-il ? (non, pas dmc non..)

    selon toute vraissemblance il n'y a aucun intermédiaire entre l'invocation dans le shell ($ ./prog) et le noyau. c'est le kernel directement qui se charge de traiter le binaire ELF, décortiquer ses en-têtes, puis mmap-er les régions mémoire avec les bons droits etc. (ça doit tourner autour de linux_binfmt.c dans les sources du kernel, quelque chose comme ça)

    en cherchant un peu profondément sur le net (j'ai plus les liens désolé) on comprend qu'en fait à moins d'une protection spécifique, type patch noyau grsecurity, execshield, NX bit etc. le noyau ne s'embêtera pas la vie, en revanche le syscall ptrace() (utilisé par gdb) lui tient compte des droits sur les sections manifestement

    j'ai pas encore mis la main sur le code exact incriminé dans le noyau (le mmaping avec les droits d'exécution par défaut, soit sur toutes les sections, soit un droit d'exécution systématique sur la 1ère section ou la section pointée par l'entry-point etc.), à voir dans les jours qui viennent, si je trouve je notifierais ici

+ Répondre à la discussion
Cette discussion est résolue.
Page 3 sur 3 PremièrePremière 123

Discussions similaires

  1. Inversion d'une chaîne de caractères
    Par camoa dans le forum x86 16-bits
    Réponses: 7
    Dernier message: 31/10/2011, 13h56
  2. Réponses: 3
    Dernier message: 26/05/2010, 23h39
  3. Inversion d'une chaîne
    Par Maxence45 dans le forum Pascal
    Réponses: 36
    Dernier message: 14/03/2007, 23h58
  4. [Débutant] Comment sauvegarder une chaîne ?
    Par nmqm dans le forum Assembleur
    Réponses: 1
    Dernier message: 28/02/2006, 23h49

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo