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 16-bits Assembleur Discussion :

[.com avec TASM] Incompréhension au désassemblage


Sujet :

x86 16-bits Assembleur

  1. #1
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Décembre 2010
    Messages
    6
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 6
    Points : 0
    Points
    0
    Par défaut [.com avec TASM] Incompréhension au désassemblage
    Bonjour !

    Je suis débutant en assembleur (j'ai commencé hier, avec un passif de programmation en C) et j'aurais plusieurs questions. Une réponse même partielle, j'apprécierais beaucoup !!

    Je programme avec TASM. Après avoir réussi plusieurs programmes faciles je me suis intéressé à ce qu'il se passait réellement quand le programme était exécuté. Ainsi j'ai compilé et exécuté, puis désassemblé un "hello world".

    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
    .386
     
    unique segment use16
     
    assume cs:unique, ds:unique
    org 100h
     
    debut:
    mov ah, 09h
    mov dx, offset message
    int 21h
     
    message db 'Hello World$'
     
    unique ends
    end debut
    Et j'obtiens au désassemblage avec Hackman

    Hackman Disassembler

    Starting from offset 00000000
    Ending at offset 00000012
    Virtual address 00000000
    Signed/Unsigned arithmetic Unsigned


    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
    *******************************************************
    
    Adress          Hex           Command     Flags
    
    00000000	B409		MOV	ah,0x09	
    00000002	BA0701CD21	MOV	edx,0x21CD0107	
    00000007	48		DEC	eax	
    00000008	65		GS:		
    00000009	6C		INSB	esi,dx	
    0000000A	6C		INSB	esi,dx	
    0000000B	6F		OUTSD	esi	
    0000000C	20576F		AND	[edi+0x6F],dl	
    0000000F	726C		JB	[0x7D]	
    00000011	64		FS:		
    00000012	2400		AND	al,0x00	
    
    *******************************************************
    Une foule de questions sur un si petit exemple:

    -JB et GS sont-ils des marqueurs de segmentation de la mémoire ? Si oui, comment celà se fait-il ? Je suis en .com il ne devrait y avoir qu'un seul segment non ? J'ai lu que JB et GS sont des registres de données dans un programme multi-segments or ils ne sont pas utilisés comme des registres. Ils sont interdits de manipulation par le programme ? Réservés à l'OS ? (comme tout ce qui touche à la segmentation)

    -En supposant qu'il s'agisse bien de "marqueurs" de segmentation pour permettre au processeur de s'y retrouver, j'aimerais bien plus d'informations à ce sujet évidemment. Pourquoi on les utilise, qui les manipule, les opérations qu'on peut ou ne peut pas faire avec etc... Si vous avez une bonne page web (ou un site plus globalement) susceptible de m'éclairer ça je suis preneur car je n'ai rien trouvé pour le moment.

    -Si j'ai raison... De l'adresse 7 à F, celà correspond à la fonction 0x02 de l'interruption 21 ? Il s'agit d'une série d'instructions que je ne semble pas avoir programmées et qui s'achèvent par un branchement (?) donc je suppose, je suppose...

    -Si j'ai bien calculé le JB en F branche en A (il fait décaller ip de -4).
    1) Le calcul est-il correct ?
    2) Pourquoi ce saut ? (si c'est lié à l'int 21 ou pas)

    -A quoi sert ce "And" sans branchement derrière, à l'adresse 12 ?

    J'ai l'impression que mes questions sont ridicules, mais j'ai l'impression aussi qu'en assembleur il est enfin permis de comprendre ce qu'on fait et pourquoi on le fait, car le langage est poussé et il a de nombreux vétérans qui l'emploient depuis longtemps (donc le comprennent). J'espère que je serai pas déçu :p

    Merci d'avance en tout cas =)

  2. #2
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 368
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 368
    Points : 23 622
    Points
    23 622
    Par défaut
    Bonjour,

    Citation Envoyé par Eluncy Voir le message
    J'ai l'impression que mes questions sont ridicules, mais j'ai l'impression aussi qu'en assembleur il est enfin permis de comprendre ce qu'on fait et pourquoi on le fait, car le langage est poussé et il a de nombreux vétérans qui l'emploient depuis longtemps (donc le comprennent). J'espère que je serai pas déçu :p
    Ces questions ne sont pas ridicules et le présent problème est dû à un détail technique assez subtil.

    Tu as tout-à-fait raison de chercher à « descendre jusqu'en bas ». C'est généralement ce qui motive les programmeurs en assembleur. L'âge d'or de la discipline était entre 1980 et 1995, lorsque les ordinateurs personnels étaient monotâches et aux ressources limitées, et que la variété des machines existantes était nettement plus diversifiée. Il était aussi rentable que facile (tout choses comparables par ailleurs) d'aller directement parler à son processeur et aux ports d'entrée/sortie. Aujourd'hui, c'est devenu plus abstrait, et il est plus difficile de cohabiter avec les systèmes d'exploitation de haut niveau, mais le fonctionnement reste rigoureusement le même.

    Ainsi j'ai compilé et exécuté, puis désassemblé un "hello world".
    Et j'obtiens au désassemblage avec Hackman

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    Adress          Hex           Command     Flags
    
    00000000	B409		MOV	ah,0x09	
    00000002	BA0701CD21	MOV	edx,0x21CD0107	
    00000007	48		DEC	eax	
    00000008	65		GS:		
    00000009	6C		INSB	esi,dx	
    0000000A	6C		INSB	esi,dx	
    0000000B	6F		OUTSD	esi	
    0000000C	20576F		AND	[edi+0x6F],dl	
    0000000F	726C		JB	[0x7D]	
    00000011	64		FS:		
    00000012	2400		AND	al,0x00
    Une foule de questions sur un si petit exemple:

    -JB et GS sont-ils des marqueurs de segmentation de la mémoire ? Si oui, comment celà se fait-il ? Je suis en .com il ne devrait y avoir qu'un seul segment non ? J'ai lu que JB et GS sont des registres de données dans un programme multi-segments or ils ne sont pas utilisés comme des registres. Ils sont interdits de manipulation par le programme ? Réservés à l'OS ? (comme tout ce qui touche à la segmentation)
    — GS est un registre de segment, au même titre que CS et DS ;
    — « JB » est une instruction qui veut dire « Jump if Below ». Le saut a lieu si le résultat de la dernière comparaison montre que l'opérande examinée, considérée ici comme non signée, est inférieure à la valeur de référence.

    La vraie question est plutôt : « comment se fait-il que tu obtiennes ces instructions alors que tu ne les a jamais écrites, pas plus que INSB et tout ce qui suit ». La réponse se trouve dans leurs codes-opérations :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    00000007	48		DEC	eax	
    00000008	65		GS:		
    00000009	6C		INSB	esi,dx	
    0000000A	6C		INSB	esi,dx	
    0000000B	6F		OUTSD	esi	
    0000000C	20576F		AND	[edi+0x6F],dl	
    0000000F	726C		JB	[0x7D]	
    00000011	64		FS:		
    00000012	2400		AND	al,0x00
    Soit 48, 65, 5C, 6C, 6F, 20, 57, 5F, 72, 6C, 64, 24 et 00, lequels correspondent en fait aux codes A.S.C.I.I. des caractères H, e, l, l, o, « », W, o, r, l, d et du zéro final. :-) Comme ton programme est un *.com, que code et données sont mélangés, et qu'il n'y a aucun marqueur interne dans ton programme, ton désassembleur ne peut pas faire la différence entre les deux.

    Maintenant, où est donc passé ton propre code ? Il devrait ressembler à ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    00000000 B409             mov     ah,09h
    00000002 BA0701           mov     dx,0107h  <---- Adresse du message
    00000005 CD21             int     21h
    En regardant bien, tu t'aperçois qu'ils sont bien ici :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    00000000 B409		MOV	ah,0x09	
    00000002 BA0701CD21	MOV	edx,0x21CD0107
    Alors, que s'est-il passé ?
    Tu as compilé ton programme en 16 bits mais, en l'absence d'indications, ton désassembleur a considéré qu'il travaillait en 64 bits, soit un écart de plus de 20 ans. :-)

    le code-opération « BA » signifie « MOV DX,xxxx ». Lorsque l'on est passé en 32 bits, les instructions homologues ont été préfixées par « 66 ». Ainsi « 66 BA » signifie « MOV EDX,xxxxxxxx ». Le mode 32 bits reste donc implicitement compatible avec le 16. Par contre, en 64 bits, le fonctionnement est inversé sur ce point. Le code opération « BA » correspond par défaut au registre 32 bits « EDX » et « 66 BA » à « DX ».

    Ton désassembleur a donc pris ton « MOV DX » pour un « MOV EDX » et a inclus dans son opérande les codes opérations qui suivaient.

  3. #3
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Décembre 2010
    Messages
    6
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 6
    Points : 0
    Points
    0
    Par défaut
    Hé ben !! Il m'a bien fallu une heure pour comprendre ce schmilblick mais ça sonne comme une révélation !!!! J'ai changé les paramètres du désassembleur en Intelx86 16 bits et j'obtiens en effet quelque chose de beaucoup plus plausible !

    Hackman Disassembler

    Dump Information
    File C:\Users\Fkeed\Downloads\Tasm\PROG.COM
    Starting from offset 00000000
    Ending at offset 00000012
    Virtual address 00000000
    Signed/Unsigned arithmetic Unsigned

    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
     
    ***************************************
    00000000	B409	MOV	ah,0x09	8086
    00000002	BA0701	MOV	dx,0x0107	8086
    00000005	CD21	INT	0x21	8086
    00000007	48	DEC	ax	386
    00000008	65	GS:		8086
    00000009	6C	INSB	si,dx	186
    0000000A	6C	INSB	si,dx	186
    0000000B	6F	OUTSD	si	386
    0000000C	20576F	AND	[bx+0x6F],dl	8086
    0000000F	726C	JB	[0x7D]	8086
    00000011	64	FS:		8086
    00000012	2400	AND	al,0x00	8086
    ***************************************
    Si j'ai bien compris

    00 mov ah, 09h
    02 mov dx, 0107h

    Comme on est en .com, cela revient à mettre dans dx l'adresse n°7 après le PSP (100h+7)

    05 int 21h
    L'interruption lit la chaîne qui est jusque l'adresse 12

    C'est énorme !!! En fait les adresses 7 à 12 sont uniquement des données et du coup ça fait un bug dans la matrice quand on cherche à afficher les commandes associées... Ce ne sont pas des instructions ^^ Moi j'étais parti loin alors qu'en fait c'est simplissime !!

    Ca me donne plein d'idées ça !!
    Par contre, y a-t-il moyen de connaître en quel jeu d'instruction un exécutable/com a été compilé ?

  4. #4
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 368
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 368
    Points : 23 622
    Points
    23 622
    Par défaut
    C'est énorme !!! En fait les adresses 7 à 12 sont uniquement des données et du coup ça fait un bug dans la matrice quand on cherche à afficher les commandes associées... Ce ne sont pas des instructions ^^ Moi j'étais parti loin alors qu'en fait c'est simplissime !!

    Ca me donne plein d'idées ça !!
    Ça a donné pas mal d'idées à beaucoup de monde également. Mais il faut aussi remarquer que même si tu avais été en 32 bits (tu l'aurais systématiquement été il y a peu), tu aurais retrouvé ton code quand même, et aurait probablement été à même de comprendre que la suite était ta chaîne de caractères.

    Et justement, il existe pas mal d'astuces assembleur du style de celle qui a involontairement eu lieu ici : faire un MOV EDX,xxxxxxxx voire MOV RDX,xxxxxxxxxxxxxxxx permet en un ou deux octets de masquer les quatre ou huit suivants. Du coup, on se servait quelques fois de cela pour faire un JMP déguisé, par dessus ces quelques octets qui pouvait quand même representer de une à huit instructions.

    Par contre, y a-t-il moyen de connaître en quel jeu d'instruction un exécutable/com a été compilé ?
    Un exécutable *.EXE au format COFF, MZ, NE, ou PE certainement (et encore, il faudrait que je vérifie au cas par cas). Idem pour un ELF sous Unix, car tous ces formats embarquent un champ qui précise la nature du code. Avec un *.COM ou n'importe quel autre format n'embarquant pas ces informations, tu ne peux pas le déduire de façon certaine.

  5. #5
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Décembre 2010
    Messages
    6
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 6
    Points : 0
    Points
    0
    Par défaut
    Et justement, il existe pas mal d'astuces assembleur du style de celle qui a involontairement eu lieu ici : faire un MOV EDX,xxxxxxxx voire MOV RDX,xxxxxxxxxxxxxxxx permet en un ou deux octets de masquer les quatre ou huit suivants. Du coup, on se servait quelques fois de cela pour faire un JMP déguisé, par dessus ces quelques octets qui pouvait quand même representer de une à huit instructions.
    Mais ici c'était uniquement du au fait que j'avais mal configuré le désassembleur, comment ça serait sinon ? Je rentre des caractères ascii en hexa (qui coincident avec des opcodes) directement en mémoire avec MOV ? Je ne pense pas que le compilateur apprécierait si je faisais du

    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
    .386
    
    unique segment use16
    
    assume cs:unique, ds:unique
    org 100h
    
    debut:
    mov ax, 48h ; 48h équivaut à  "DEC Ax" ou à 'H'
    mov [17h], ax 
    
    ; Tout à l'heure mon programme s'arrêtait à 0x12, donc en théorie si je
    ;rajoute deux mov sur deux registres de 16 bits la fin du programme se trouve
    ;à l'adresse 0x12+0x4 = 0x16 et je peux donc écrire en 0x17 (en plus 
    ;c'est un com on s'en fout si c'est le bordel)
    
    mov ah, 09h
    mov dx, offset message
    int 21h
    
    message db 'Hello World$'
    
    unique ends
    end debut
    En effet TASM m'insulte gentiment: "Illegal immediate".
    Alors comment je dois m'y prendre pour fixer des caractères ascii à un endroit de la mémoire ? Je ne peux pas le déterminer avant ?


    Sinon j'ai une autre question en dehors de ça: j'ai trouvé un mini-tutoriel sur les checksum, mais ils utilisent l'instruction LODSB pour compter les opcodes. Là encore j'ai eu beau y passer pas mal de temps je ne comprends pas. Comment marche-t-elle ?

    Merci beaucoup pour les réponses déjà apportées sinon c'est très gentil =)

  6. #6
    Membre régulier
    Profil pro
    Inscrit en
    Novembre 2010
    Messages
    59
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2010
    Messages : 59
    Points : 88
    Points
    88
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    mov [17h], ax     ; Immediate mode illegal
    Il faut préciser le segment de mémoire auquel appartient la cellule de mémoire pointée par l'offset 17h :

Discussions similaires

  1. erreur avec tasm et c++-builder 4
    Par Arthur59 dans le forum C++Builder
    Réponses: 1
    Dernier message: 11/11/2006, 17h57
  2. Peut on utiliser un objet com avec eclipse
    Par MoiAussi dans le forum Eclipse Java
    Réponses: 2
    Dernier message: 22/09/2006, 15h47
  3. Pb port Com avec fonction SetCommTimeouts
    Par mbianchi dans le forum Windows
    Réponses: 3
    Dernier message: 19/05/2006, 17h32
  4. [16 bits] Compilation avec TASM
    Par SaladinDev dans le forum Assembleur
    Réponses: 9
    Dernier message: 24/10/2005, 17h35

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