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

Assembleur Discussion :

Ingénierie inverse (désassembler un programme)


Sujet :

Assembleur

  1. #1
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Août 2004
    Messages
    4
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Août 2004
    Messages : 4
    Points : 1
    Points
    1
    Par défaut Ingénierie inverse (désassembler un programme)
    Bonjour,

    Je débute avec l'assembleur. Mon objectif est de pouvoir lire aisément l'assembleur mais d'un point de vue ingénierie inverse. J'ai compilé en C, avec GCC sous Fedora Core, un programme très simple que voici:

    Désassemblé avec GDB, j'obtiens:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    (gdb) disas main
    Dump of assembler code for function main:
    0x0804833c <main+0>:    push   %ebp
    0x0804833d <main+1>:    mov    %esp,%ebp
    0x0804833f <main+3>:    sub    $0x8,%esp
    0x08048342 <main+6>:    and    $0xfffffff0,%esp
    0x08048345 <main+9>:    mov    $0x0,%eax
    0x0804834a <main+14>:   sub    %eax,%esp
    0x0804834c <main+16>:   leave
    0x0804834d <main+17>:   ret
    End of assembler dump.
    (gdb)
    Et déjà je me questionne sur ce qui se passe. J'ai exécuté ce programme en obtenant les informations sur l'état des registres ainsi que de la pile d'exécution:

    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
    46
    47
    48
    49
    50
    51
    52
    Breakpoint 1, 0x08048342 in main () at example.c:3
    3       {
    (gdb) info registers
    eax            0x0      0
    ecx            0xfeedae1c       -17977828
    edx            0xfeedae14       -17977836
    ebx            0x650ffc 6623228
    esp            0xfeedad80       0xfeedad80
    ebp            0xfeedad88       0xfeedad88
    esi            0x1      1
    edi            0x6530fc 6631676
    eip            0x8048342        0x8048342
    eflags         0x200282 2097794
    cs             0x73     115
    ss             0x7b     123
    ds             0x7b     123
    es             0x7b     123
    fs             0x0      0
    gs             0x33     51
    (gdb) info frame
    Stack level 0, frame at 0xfeedad90:
    eip = 0x8048342 in main (example.c:3); saved eip 0x54ead4 source language c. Arglist at 0xfeedad88, args: Locals at 0xfeedad88, Previous frame's sp is 0xfeedad90 Saved registers:
      ebp at 0xfeedad88, eip at 0xfeedad8c
    (gdb) n
    0x0054ead4 in __libc_start_main () from /lib/tls/libc.so.6
    (gdb) info registers
    eax            0x0      0
    ecx            0xfeedae1c       -17977828
    edx            0xfeedae14       -17977836
    ebx            0x650ffc 6623228
    esp            0xfeedad90       0xfeedad90
    ebp            0xfeedade8       0xfeedade8
    esi            0x1      1
    edi            0x6530fc 6631676
    eip            0x54ead4 0x54ead4
    eflags         0x200382 2098050
    cs             0x73     115
    ss             0x7b     123
    ds             0x7b     123
    es             0x7b     123
    fs             0x0      0
    gs             0x33     51
    (gdb) info frame
    Stack level 0, frame at 0xfeedadf0:
    eip = 0x54ead4 in __libc_start_main; saved eip 0x80482ad
    called by frame at 0x0
    Arglist at 0xfeedade8, args:
    Locals at 0xfeedade8, Previous frame's sp is 0xfeedadf0
    Saved registers:
      ebx at 0xfeedaddc, ebp at 0xfeedade8, esi at 0xfeedade0, edi at 
    0xfeedade4,
      eip at 0xfeedadec
    J'ai compilé ce programme sur une plateforme Intel P4.

    Est-ce que quelqu'un pourrait me dire ce qui se passe sur la pile? J'essaie de suivre le cheminement du ESP et du EBP mais je ne crois pas y arriver.
    En fait j'essaie de reconstituer chronologiquement l'étas de tous les registres et de la pile mais l'info que j'ai obtenue avec gdb me cause des maux de tête!!!

    Merci.

    N.B. J'aurai bien d'autres questions dans un futur rapproché

    Merci.



    Balises [code] ajoutées par Hdd34. Merci d'y penser à l'avenir

  2. #2
    Rédacteur
    Avatar de Neitsa
    Homme Profil pro
    Chercheur sécurité informatique
    Inscrit en
    Octobre 2003
    Messages
    1 041
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Chercheur sécurité informatique

    Informations forums :
    Inscription : Octobre 2003
    Messages : 1 041
    Points : 1 956
    Points
    1 956
    Par défaut
    Bonjour,

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    0x0804833c <main+0>: push %ebp
    0x0804833d <main+1>: mov %esp,%ebp
    1) EBP (Base pointer) est poussé sur la pile
    2) ESP (Stack pointer) est mis dans le registre EBP (syntaxe AT&T il me semble)

    => Ces deux lignes montre une entrée de procédure (ici MAIN)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    0x0804833f <main+3>: sub $0x8,%esp
    On soustrait 8 à ESP, de ce fait on réserve deux DWORD ( 4 octets*2) sur la pile. Généralement pour les variables locales (ici il n'y en a pas mais le compilo choisi quand même d'en réserver).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    0x08048342 <main+6>: and $0xfffffff0,%esp
    Opération 'ET' qui ne garde PAS les 4 derniers bits d'ESP (exemple:
    ESP= 12345678
    AND $0xFFFFFFF0
    ESP=12345670

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    0x08048345 <main+9>: mov $0x0,%eax
    EAX = 0

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    0x0804834a <main+14>: sub %eax,%esp
    soustrait 0 à ESP, donc ESP reste le même

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    0x0804834c <main+16>: leave
    La pile est nettoyée de ses variables locales ce qui ici (et seulement dans ce cas) correspond à:

    ADD $0x8,%esp ; on enleve les 2 DWORD réservé en début de procédure.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    0x0804834d <main+17>: ret
    On RETourne à la procédure appelante. Comme ici le programme n'en dispose pas, on retourne dans le système.

    J'espère que cela clarifie un peu la chose. Si tu as d'autres questions n'hésite pas !

    P.S: n'oublie pas les balises de code, c'est plus facile à lire !

    Amicalement, Neitsa.

  3. #3
    Membre du Club
    Inscrit en
    Juin 2004
    Messages
    39
    Détails du profil
    Informations forums :
    Inscription : Juin 2004
    Messages : 39
    Points : 46
    Points
    46
    Par défaut
    bjr

    pour te former à l'assembleur inverse, (dessassemblage),

    essayes des programmes courts (< à 1Ko) en assembleur
    dans un premier temps

    en les connaissants c'est plus simple de "voir" au dessassemblage ce qui se passe.

    après recommence avec des courts programmes en C comme tu l'a fait avec celui ci.


    c'est plus facile de commencer comme ça.

  4. #4
    Membre expérimenté

    Inscrit en
    Mai 2002
    Messages
    720
    Détails du profil
    Informations forums :
    Inscription : Mai 2002
    Messages : 720
    Points : 1 594
    Points
    1 594
    Par défaut
    Apparament, le numero de MISC de ce mois (Et qui n'est donc peut etre plus en kiosque) a un article sur le reverse engeneering... Je ne sais pas ce qu'il vaut, je ne l'ai pas lu, mais c'etait pour l'info ... A voir peut etre

    Smortex

    Les FAQ Assembleur - Linux
    In The Beginning Was The Command Line Neal Stephenson

  5. #5
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Août 2004
    Messages
    4
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Août 2004
    Messages : 4
    Points : 1
    Points
    1
    Par défaut
    Neitsa,

    Merci de tes explications, elles sont claires. Je te reporte aux suivantes:

    Citation Envoyé par Neitsa
    Bonjour,


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    0x08048342 <main+6>: and $0xfffffff0,%esp
    Opération 'ET' qui ne garde PAS les 4 derniers bits d'ESP (exemple:
    ESP= 12345678
    AND $0xFFFFFFF0
    ESP=12345670
    En fait, quelle est l'utilité pour le compilateur d'enlever les 4 dernier bits du ESP?

    Merci

    \slackspace/

  6. #6
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Août 2004
    Messages
    4
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Août 2004
    Messages : 4
    Points : 1
    Points
    1
    Par défaut Autoréponse et commentaires bienvenus
    Bon, j'évolue tant bien que mal dans l'univers de l'assembleur (intel p4). J'ai fais quelques expérimentations et j'en ai déduit certaines chose. Cependant, d'autres demeurent tout de même obscure. Je requiers vos commentaires afin d'éclairer ma lanterne. Débutons.

    Voici un petit programme en c:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    int function (int a, int b, int c)
    {
    	return a+b+c;
    }
    int main (int argc, char ** argv)
    {
    	int add;
    	add = function (1,2,3);
    	return 0;
    }
    Ce petit programme fut compilé sous la plateforme Mandrake 10.0 avec gcc. Voici le code désassemblé:

    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
    disassembly-flavor intel
     
    push 	ebp
    mov 	ebp, esp
    sub 	esp, 0x8
    and 	esp, oxfffffff0
    mov 	eax, 0x0
    sub 	esp, eax
    sub 	esp, 0x4
    push 	0x3
    push	0x2
    push	0x1
    call 	0x804832c <function>
     
    	push	ebp
    	mov	ebp, esp
    	mov 	eax, DWORD PTR [ebp+12]
    	mov 	eax, DWORD PTR [ebp+8]
    	mov 	eax, DWORD PTR [ebp+16]
    	pop ebp
    	ret
     
    add 	esp, 0x10
    mov	DWORD PTR [ebp-4], eax
    mov	eax, 0x0
    leave
    ret
    Voici donc ce que je comprend en examinant le code désassemblé ligne par ligne (vous pouvez suivre l'évolution du esp et du ebp avec le graphique suivant):




    1) Après push ebp et mov ebp, esp, on se reporte à la ligne pointée par esp1
    2) Après sub esp, 0x8, on se reporte à la ligne pointée par esp3
    3) and esp, 0xfffffff0 est utilisée pour aligner le esp au début de l'emplacement mémoire de la pile
    4) mov eax, 0x0 et sub esp, eax n'ont aucun effet notable
    5) sub esp, 0x4 c'est pour réserver un emplacement mémoire pour la variable locale au main int add
    6) push 0x3, push 0x2, push 0x1 c'est pour envoyer les paramètres de la fontion sur la pile. On se reporte à la ligne pointée par esp5.
    7) call function: le eip est stocké sur la pile (voir esp6) et on entre dans le code de la fonction
    8) push ebp, mov ebp, esp, on se reporte à la ligne pointée par esp7
    9) mov eax, DWORD PTR [ebp+12]: on envoie la valeur 2 dans eax (voir la ligne pointée par ebp2, à laquelle on ajoute un offset de 12
    10 et 11) on additionne les autres paramètres, le résultat est dans eax
    12) pop ebp: on renvoie ebp1 dans le registre ebp (on retourne dans le frame du main). On se reporte à la ligne pointée par esp8.
    13) ret: on dépile eip et on continue à l'instruction du main suivant call. On se reporte à la ligne pointée par esp9.
    14) add esp, 0x10 : on nettoie la pile des paramètres de la fonction. On se reporte à la ligne pointée par esp10.
    15) mov DWORD PTR [ebp-4], eax: on envoie le résultat de l'addition i.e. 6 à la ligne pointée par "résultat addition"
    16) mov eax, 0x0: on remet le registre eax à 0
    17) leave: on nettoie la pile de la variable locale int add et des 8 bytes réservés par le compilateur au début du main
    18) ret: on dépile eip et on continue....

    C'est bien beau tout cela mais j'ai des question qui demeurent existentielles:

    Q1: À quoi sert les 8 bytes réservés par le compilateur au début du main? Est-ce pour argc et argv?
    Q2: Pourquoi la valeur de retour de la fonction ne va pas se copier dans l'emplacement de la pile réservé à cet effet i.e. lorsque l'on a fait sub esp, 0x4? En d'autres mots, pourquoi on a l'instruction mov DWORD PTR [ebp-4], eax au lieu de mov DWORD PTR [ebp-12]?

    Merci de votre temps.

    slackspace

  7. #7
    Rédacteur
    Avatar de Neitsa
    Homme Profil pro
    Chercheur sécurité informatique
    Inscrit en
    Octobre 2003
    Messages
    1 041
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Chercheur sécurité informatique

    Informations forums :
    Inscription : Octobre 2003
    Messages : 1 041
    Points : 1 956
    Points
    1 956
    Par défaut
    Bonjour,

    Permière chose que je ne comprend pas:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    int function (int a, int b, int c)
    {
       return a+b+c;
    }
    or le code désassemblé que tu nous fait voir ne montre d'aucun signe d'addition (les movs ne font qu'effacer la valeur précédente) !?

    Q1: À quoi sert les 8 bytes réservés par le compilateur au début du main? Est-ce pour argc et argv?
    Je ne peux pas être affirmatif, mais il semblerait que ce soit ca...

    pour la question 2 c'est effectivment étrange, mais je pense que ce genre de retour est dépendant du système. Une fois le RET final effectué on se retrouve dans le système...

    Peut être faudrait il essayé d'afficher le résultat de l'addition pour voir comment le sytème s'occupe de la valeur de retour de l'addition ce qui serait nettement plus facile pour voir ce qui arrive à cette dernière.

    P.S: j'ai du mal à saisir si les valeurs sont décimales ou héxa (décimales je pense). Un retour à EBP-12, donc EBP-0xC, serait plus juste. Etrange que ton désassembleur mélange Dec et Hex... (pas très lisible et prête à confusion...)

    Amicalement, Neitsa.

  8. #8
    Nouveau Candidat au Club
    Profil pro
    Inscrit en
    Août 2004
    Messages
    4
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations forums :
    Inscription : Août 2004
    Messages : 4
    Points : 1
    Points
    1
    Par défaut erratum
    Effectivement, il y a une erreur dans le code que j'ai donné en ce qui a trait à l'addition des variables a,b et c: voici le code corrigé:

    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
    c code
     
    int function (int a, int b, int c)
    {
    	retutn a+b+c;
    }
     
    int main (int argc, char ** argv)
    {
    	int add;
    	add = function (1,2,3);
    	return 0;
    }
     
     
    disassembly-flavor intel
     
    push 	ebp
    mov 	ebp, esp
    sub 	esp, 0x8
    and 	esp, oxfffffff0
    mov 	eax, 0x0
    sub 	esp, eax
    sub 	esp, 0x4
    push 	0x3
    push	0x2
    push	0x1
    call 	0x804832c <function>
     
    	push	ebp
    	mov	ebp, esp
    	mov 	eax, DWORD PTR [ebp+12]
    	add 	eax, DWORD PTR [ebp+8]
    	add 	eax, DWORD PTR [ebp+16]
    	pop ebp
    	ret
     
    add 	esp, 0x10
    mov	DWORD PTR [ebp-4], eax
    mov	eax, 0x0
    leave
    ret
    Merci, je vais faire encore d'autres tests.

Discussions similaires

  1. Degate démocratise l'ingénierie inversée du Hardware
    Par Idelways dans le forum Hardware
    Réponses: 6
    Dernier message: 08/08/2011, 13h45
  2. Degate démocratise l'ingénierie inversée du Hardware
    Par Idelways dans le forum Actualités
    Réponses: 0
    Dernier message: 05/08/2011, 12h35
  3. Ingénierie Inverse Via ODBC
    Par kfmystik dans le forum Autres SGBD
    Réponses: 1
    Dernier message: 11/08/2008, 11h25
  4. Postgres et l'ingénierie inversée
    Par Empty_body dans le forum PostgreSQL
    Réponses: 1
    Dernier message: 14/12/2006, 15h13
  5. logiciel d'ingénierie inverse
    Par dahan dans le forum SQL Procédural
    Réponses: 2
    Dernier message: 05/10/2006, 11h31

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