Differences

This shows you the differences between two versions of the page.

Link to this comparison view

iocla:laboratoare:laborator-09 [2019/11/26 08:21]
laurentiu.stefan97
— (current)
Line 1: Line 1:
-====== Laborator 09: Analiza statică și dinamică a programelor. GDB ====== 
- 
-În acest laborator vom studia modalitățile de analiză a programelor în scopul de a înțelege mai amănunțit modul lor de funcționare și pentru a găsi ușor cauzele unor probleme (debugging). Vom studia câteva utilitare de analiză, vom rezolva bug-uri, iar în final vom ști ce unelte și cum să le folosim pentru a dezvolta programe corecte și sigure. 
-Conținutul laboratorului alternează între secțiuni de tip tutorial, cu parcurgere pas cu pas și prezentarea soluției, și exerciții care trebuie să fie rezolvate. 
- 
-===== Analiza statică ===== 
- 
-Analiza statică a unui program constă în inspectarea diferitelor aspecte din fișierul obiect sau executabil. Analiza statică a 
-unui program nu implică si rularea acestuia. Aceasta presupune analiza codului imediat dupa ce s-a terminat partea de codare și 
-înainte de rularea testelor. 
- 
-Analiza statică poate fi efectuată de un program, în mod automat, prin parcurgerea codului și verificarea faptului că sursa 
-a fost scrisă în conformitate cu regulile specifice. Un exemplu clasic în acest sens este compilatorul care identifică 
-erorile lexicale, sintactice și, uneori, semantice dintr-un program. Remarcați faptul că programul nu este rulat atunci cand 
-compilatorul inspectează sursa. 
- 
-Analiza statică poate fi efectuată și manual atunci cand codul este revizuit ("code review"​) pentru a se asigura calitatea 
-si lizibilitatea codului. 
- 
-Avantajele analizei statice: 
-  * identifică zona exactă unde poate aparea o eroare 
-  * ușurează înțelegerea codului de către alți (posibili) cititori ai codului 
-  * nu este nevoie să fie rulat codul (avantaj în cazul în care rularea codului necesită resurse foarte mari) 
-  * identifică erori ce nu pot fi găsite printr-un alt tip de analiză: cod mort ("​unreachable code"​),​ variabile nefolosite, funcții neapelate etc. 
- 
-Dezavantajele analizei statice: 
- 
-  * consumă foarte mult timp dacă este realizată manual 
-  * nu poate detecta vulnerabilități apărute la runtime (spre exemplu "​segmentation fault"​) 
-  * ai nevoie de codul sursă pentru a putea realiza acest tip de analiză 
- 
-Câteva din programele utile pentru analiza statică pe care le vom folosi și în cadrul tutorialelor/​exercițiilor sunt: 
- 
-  * **nm** - utilitar folosit pentru inspectarea simbolurilor și secțiunilor din executabile;​ 
-  * **objdump** - program folosit pentru dezasamblarea (traducerea din cod-mașină în limbaj de asamblare) programelor binare; 
-  * **IDA** - o unealtă foarte puternică pentru dezasamblarea și inspectarea fișierelor obiect și executabile;​ 
-  * **coverity**,​ **clang-analyzer**,​ **cppcheck** - utilitare pentru identificare statică a problemelor de tip: memory-leak,​ buffer overflow, NULL pointer dereferences. 
- 
-===== Analiza dinamică ===== 
- 
-Spre deosebire de analiza statică, analiza dinamică constă în inspectarea unui program aflat în execuție. Practic, analiza dinamică se face la //​runtime//​. 
- 
-Avantajele analizei dinamice: 
- 
-  * identifică erorile apărute la runtime: segmentation fault, arithmetic exception etc. 
-  * oferă posibilitatea analizării programului chiar dacă nu avem acces la codul sursă 
-  * identifică vulnerabilități care ar fi putut fi fals negative în momentul analizei statice 
-  * permite validarea rezultatelor analizei statice 
-  * poate fi realizată pentru orice aplicație 
- 
-Dezavantajele analizei dinamice: 
- 
-  * este mai greu de localizat unde exact în cod are loc eroarea 
-  * programele ce automatizează analiza dinamică produc falsuri pozitive și falsuri negative 
-  * nu poate garanta acoperirea completă a tuturor cazurilor unui fișier sursă 
- 
-Unul dintre cele mai folosite programe pentru analiză dinamică este **gdb**. Acesta oferă o gamă largă de operații ce pot fi făcute, de la inspectarea memoriei, la schimbarea control flow-ului și până la modificarea registrelor de pe procesor, în timpul rulării unui program. 
- 
-===== Tutoriale și exerciții ===== 
-În cadrul exercițiilor vom folosi ​ [[http://​elf.cs.pub.ro/​asm/​res/​laboratoare/​lab-09-tasks.zip|arhiva de laborator]]. Descărcați arhiva, decomprimați-o și accesați directorul aferent. 
- 
-Deși folosirea unui mediu grafic pentru programare poate părea mai atractivă, de multe ori folosirea liniei de comandă oferă mai multă putere și control asupra a ceea ce vrem să facem. În plus, folosirea utilitarelor din linia de comandă ​ în scripturi poate facilita automatizarea unor task-uri, lucru care ne va face viața mai ușoară în nenumărate cazuri. 
- 
-În cadrul acestui laborator, vom folosi utilitare în linia de comandă atât pentru asamblarea și link-editarea fișierelor sursă, cât și pentru analiza statică și dinamică a programelor obținute din parcurgerea tutorialelor și a exercițiilor. 
- 
- 
-=== [0.5p] 1. Tutorial: Asamblarea din linia de comandă === 
- 
-Deschideți fișierul ''​hello-world.asm''​ din directorul ''​1-2-tutorial''​. Pentru a asambla fișierul ''​hello-world.asm'',​ vom folosi utilitarul ''​nasm''​ (program care este folosit în spate și de către SASM). 
- 
-<​code>​ 
-nasm -g -f elf32 hello-world.asm -o hello-world.o 
-</​code>​ 
- 
-<​note>​ 
-''​-g''​ spune asamblorului să adauge simboluri de debug în fișierul obiect rezultat 
- 
-''​-f''​ menționează formatul executabilului (în cazul nostru ''​elf32''​) 
-</​note>​ 
- 
-Pentru a verifica "​corectitudinea"​ asamblării,​ haideți să dezasamblăm fișierul ''​hello-world.o''​ folosind utilitarul ''​objdump'',​ astfel: 
-<code asm> 
-objdump -M intel -d hello-world.o 
-</​code>​ 
- 
-<​note>​ 
-''​-M intel''​ spune dezasamblorului să folosească sintaxa Intel și nu pe cea AT&T 
-</​note>​ 
- 
-Putem observa similaritatea dintre codul inițial și codul dezasamblat,​ mai puțin la instrucțiunea ''​call''​ asociată funcției printf, unde adresa pare greșită. Acest fapt se întâmplă din cauza faptului că fișierul obiect obținut nu "​știe"​ cine este printf. Acest lucru se va afla la pasul de link-editare,​ iar adresa va fi modificată la cea corespunzătoare. Observați faptul că apelul la funcția ''​print_message''​ s-a realizat în mod normal deoarece label-ul acesteia se află în zona de text a fișierului obiect, și deci este cunoscută. 
- 
-=== [0.5p] 2. Tutorial: Link-editarea unui fișier obiect === 
- 
-Link-editarea unuia sau mai multor fișiere obiect constă în rezolvarea tuturor simbolurilor externe și crearea unui singur fișier executabil din toate fișiere primite la intrare. 
- 
-Pentru link-editare vom folosi ''​gcc''​. De asemenea și gcc este folosit de SASM pentru link-editarea fișierelor obiect. 
- 
-<​code>​ 
-gcc -g -m32 hello-world.o -o hello-world 
-</​code>​ 
- 
- 
-<​note>​ 
-''​-g''​ este folosit cu același scop ca la nasm, de a introduce simboluri de debug în executabil 
- 
-''​-m32''​ specifică arhitectura pentru care executabilul este generat (în cazul nostru, arhitectură pe 32 biți) 
-</​note>​ 
- 
-Acum puteți rula executabilul pentru a vedea că toți pașii au funcționat. Pentru lansarea în execuție din linie de comandă folosiți construcția 
- 
-<​code>​ 
-./​hello-world 
-</​code>​ 
- 
-Ar trebui să vi se afișeze pe ecran ''​Hello,​ World''​. 
- 
- 
- 
- 
-==== Tutoriale și exerciții analiză statică ==== 
- 
- 
- 
- 
-=== [1p] 3. Tutorial objdump === 
-Prin dezasamblarea unui fișier binar (executabil,​ cod de tip obiect, bibliotecă partajată...) obținem echivalentul în limbaj de asamblare al codului (eventual, scris în C) de la care a pornit totul. În funcție de formatul fișierului binar, codul în assembly va fi structurat într-un mod specific. În cadrul laboratoarelor lucrăm în general cu binare în formatul ELF (nu ne vom intersecta cu executabile de tipul Mach O sau IEEE-695, dar și acestea au la bază idei asemănătoare). În urma dezasamblării pentru acest tip de format vom obține un ansamblu de secțiuni, fiecare corespunzând anumitor date din codul sursă; secțiunile esențiale sunt următoarele:​ 
-  * .data - date inițializate de tip read-write 
-  * .bss - date neinițializate de tip read-write 
-  * .rodata - date read-only (const) 
-  * .text - instrucțiuni executabile 
-  * .init și .fini - secțiuni pe care probabil nu le veți întâlni în fișierele obiect, dar ele sunt legate de etapele de inițializare/​terminare a proceselor pornite din executabile ELF. Cu alte cuvinte, sistemul face astfel încât codul din aceste secțiuni să fie executat înaintea funcției main, respectiv în urma terminării cu success a execuției. 
-Zonele de memorie pentru stivă și heap vor fi, desigur, alocate la runtime și le vom putea vizualiza doar în cadrul analizei dinamice. 
-  
-Pentru acest tutorial avem la dispoziție fișierul de tip obiect ''​objd_tutorial.o''​ și codul sursă corespunzător acestuia, ambele aflate în directorul ''​3-objdump-tutorial''​. Urmăriți codul din fișierul .c și dezasamblați codul obiect folosind comanda: 
- 
-<​code>​ 
-objdump -D -M intel <​file_name>​ 
-</​code>​ 
- 
-<​note>​ 
-''​-D''​ determină dezasamblarea conținutului tuturor secțiunilor,​ nu doar al celor ce conțin instrucțiuni 
- 
-''​-M intel''​ spune dezasamblorului să folosească sintaxa Intel, nu pe cea AT&T 
-</​note>​ 
- 
-Se poate observa că porțiunile de cod în assembly sunt precedate de două coloane cu informații suplimentare:​ 
-<​code>​ 
-   ​0:​ 55 ​                  ​ push ​  rbp 
-   1: 48 89 e5             ​ mov ​   rbp,rsp 
-   4: f3 0f 11 45 fc       ​ movss ​ DWORD PTR [rbp-0x4],​xmm0 
-</​code>​ 
-Prima coloană reprezintă offsetul în octeți față de începutul secțiunii, iar cea de-a doua, codul mașină corespunzător fiecărei instrucțiuni,​ afișat în baza 16.  
-Identificați variabilele și funcțiile din fișierul .c în secțiunile corespunzătoare din outputul comenzii objdump. Se poate observa prezența unei funcții (helper_function) ce nu se regăsește în fișierul sursă, aceasta deoarece după etapa de preprocesare conținutul header-elor incluse în fișierul .c devine parte din acesta. 
- 
-Având în vedere dimensiunea tipurilor de date, ce dimensiune au secțiunile .bss și .data? Declarați una sau mai multe variabile în codul sursă astfel încât dimensiunea secțiunii .bss să crească cu 4 octeți. După modificarea codului, regenerați fișierul obiect. 
- 
-<​note>​ 
-Pentru generarea unui binar .o, puteți folosi comanda ''​gcc objd_tutorial.c -c''​ 
-</​note> ​ 
- 
-<​note>​ 
-Verificați corectitudinea folosind comanda ''​size objd_tutorial.o''​ 
-</​note>​ 
- 
-Decomentați cele 2 linii din fișierul .c, regenerați și dezasamblați fișierul obiect. Observați apelul funcției f. În ce regiștri sunt puși parametrii? Dar în cazul în care ar fi fost compilat cu opțiunea -m32? În ce secțiune ar trebui să apară variabila global_var3?​ Am putea determina motivul pentru care nu se întâmplă ceea ce ne-am aștepta adăugând opțiunea -t comenzii objdump. Totuși, există o alternativă ce prezintă acest tip de informație într-un mod mai user-friendly - comanda ''​nm''​. 
- 
-=== [1.5p] 4. nm === 
- 
-În cadrul diverselor etape de compilare, dar și în etapa de linkare, entităților dintr-un program le sunt asociate anumite metadate, fiind îndeosebi de interes adresele la care acestea - funcții, variabile - pot fi găsite. În acest context, funcțiile și variabilele sunt cunoscute drept simboluri, iar informațiile corespunzătoare sunt păstrate în structuri de date asociative denumite tabele de simboluri. Aici intervine comanda ''​nm'',​ care ne va oferi tot ce avem nevoie în materie de simboluri. ​ 
-Outputul comenzii este alcătuit din trei coloane: valoarea simbolului, tipul și numele. Există multe tipuri de simboluri, mare parte dintre acestea corespunzând secțiunii din care datele fac parte; enumerăm aici două dintre tipurile de simboluri (puteți citi mai multe în pagina de ''​man''​ a comenzii): 
-  * U - simbol nedefinit; cu alte cuvinte, el este utilizat în cadrul sursei curente, dar nu este declarat aici. Acest lucru este posibil în C datorită cuvântului cheie ''​extern'';​ locația exactă va fi determinată în urma etapei de linkare sau chiar la runtime 
-  * C - simbol comun; identifică variabilele neinițializate,​ nestatice; în cazul în care aceeași variabilă este declarată și inițializată într-un alt fișier sursă folosit la generarea executabilului final, această definiție va fi cea luată în considerare la nivelul modulului; în acest caz, variabila se va comporta ca un simbol nedefinit (valabil în C; în C++ acest tip de simbol va fi plasat în zona de date neinițializate (.BSS), iar link-editarea binarelor din acest scenariu va genera o eroare) 
- 
-În directorul ''​4-nm''​ aveți la dispoziție două fișiere tip obiect. Încercați să generați un executabil plecând de la fișierul source1.o 
- 
-<​note>​ 
-Pentru link-editare (generarea unui executabil dintr-un număr de fișiere obiect) folosim: ''​gcc file1.o [file2.o] [filen.o] -lm -o nume_executabil'' ​ 
-  * opținea -lm are rolul de a încărca biblioteca ce conține funcții matematice 
-</​note>​ 
- 
-Apare o problemă; codul sursă încearcă să folosească o funcție și o variabilă ale căror definiții nu le cunoaște. Folosiți comanda ''​nm source1.o''​ pentru a observa tipul simbolurilor corespunzătoare variabilei, respectiv funcției ce cauzează erori. Folosiți comanda ''​nm source2.o''​ pentru a vedea dacă cel de-al doilea binar conține simbolurile de care avem nevoie și, cel mai important, dacă acestea sunt ''​defined''​. 
-Reîncercați să generați un executabil, de data aceasta făcând linkarea ambelor fișiere obiect. 
- 
-Descoperim că, deși variabila ''​outsider_var''​ este definită în binarul source2.o, ea nu este vizibilă în afara fișierului în care a fost declarată. 
- 
-<​note>​ 
-Majuscula identifică (pentru majoritatea tipurilor de simboluri afișate de nm) entitățile globale, ce pot fi accesate și din alte fișiere. Minuscula (litera d în cazul nostru) identifică entitățile statice, vizibile doar în cadrul fișierului în care a fost definită. 
-</​note>​ 
- 
-Rezolvați erorile generate la link-editare creând un fișier sursă adițional în care veți defini variabila și funcția pe care source1.o nu le poate găsi; din acest fișier .c veți genera un fișier obiect și îl veți adăuga, alături de celelalte două, ca parametru al comenzii de link-editare. Considerați că funcția nu are parametri, iar valoarea de return este de tipul char; de asemenea, variabila outsider_var va avea tipul int. 
- 
-Folosind nm pentru executabilul obținut, veți observa că multe din simbolurile nedefinite în cadrul fișierelor obiect au căpătat noi tipuri; mai exact, toate variabilele și funcțiile definite de noi; funcțiile din biblioteci standard (printf, sqrt) și-au păstrat însă titulatura, ceea ce este normal când avem de-a face cu biblioteci dinamice - locațiile de memorie vor putea fi stabilite doar la runtime. 
- 
-Totuși, cât de importante sunt simbolurile?​ Folosiți comanda ''​strip -s''​ pentru a șterge toate simbolurile prezente în executabilul final. Rulați executabilul. Procedați în același mod pentru binarul source1.o. Refaceți linkarea celor 3 fișiere obiect și rulați scriptul. Evident, linkarea în absența simbolurilor este imposibilă. 
- 
- 
- 
-=== [2p] 5. Exercițiu objdump === 
-În directorul ''​5-objdump''​ găsiți un executabil care primește ca dată de input o valoare numerică n reținută pe un octet, scopul executabilului fiind acela de a calcula valoarea lui 2^(2 * n). Așa cum veți descoperi însă în urma dezasamblării,​ scopul nu este atins întotdeauna,​ întrucât codul ce stă la baza binarului conține o eroare. Folosiți objdump pentru inspectarea codului în limbaj de asamblare, descoperiți eroarea și găsiți o valoare de input pentru care se obține un rezultat corect. 
-  
-<​note>​ 
-Secțiunile din cadrul funcției main care se ocupă de citirea și afișarea datelor sunt irelevante în cadrul acestui exercițiu. De interes este doar codul delimitat prin flag-urile ''​BEGINNING_AREA_OF_INTEREST:''​ și ''​END_AREA_OF_INTEREST:'';​ de asemenea, valoarea afișată de funcție va fi preluată din registrul ''​eax''​ 
-</​note>​ 
-=== [0.5p] 6. Cppcheck === 
-[[http://​cppcheck.sourceforge.net/​|Cppcheck]] este un exemplu de utilitar open-source de analiză statică folosit pentru a detecta potențiale probleme neraportate de compilator. Spre deosebire de //objdump// care permite analiza direct pe fișierul executabil, //​cppcheck//​ analizează fișierele sursă. Acest utilitar nu detectează erorile de sintaxă, ci mai degrabă problemele cauzate de comportamente detefinite și construcții periculoase. 
- 
-Tipuri de probleme detectate de //​cppcheck//:​ 
-  * Dead pointers 
-  * Division by zero 
-  * Buffer and integer overflow 
-  * Uninitialized data 
-  * Null pointers dereferences 
-  * Memory leaks 
- 
-//​Cppcheck//​ oferă și o interfață web minimală pentru realizarea unui demo, disponibila [[http://​cppcheck.sourceforge.net/​demo/​|aici]]. Urmăriți exemplele din demo și rulați //​cppcheck//​ pe ele. Identificați sursa fiecărei erori din rezultat. 
- 
- 
-Următoarea funcție ar trebui să detecteze dacă un procesor este //little// sau //big endian//, însă ea nu arată tot timpul adevărul. Corectați funcția pornind de la //​cppcheck//​. 
-<code c> 
-void check_endianess(void) 
-{ 
-  int a; 
-  char *p = (char *)&a; 
- 
-  if (*p) 
-    printf("​Little endian\n"​);​ 
-  else 
-    printf("​Big endian\n"​);​ 
-} 
-</​code>​ 
- 
- 
- 
- 
-==== Tutoriale și exerciții analiză dinamică ==== 
- 
- 
-=== [3p] 7. Tutorial: GDB === 
- 
-GDB este o unealtă foarte utilă pentru analiza dinamică a programelor. Acesta este folosit foarte des pentru găsirea cauzelor care duc la erori într-un program. În continuare vă vom prezenta câteva dintre comenzile cele mai importante. 
- 
-Primul pas este să urmăriți și să înțelegeți codul din ''​7-8-gdb/​gdb-tutorial.asm''​. Pe scurt, programul primește un parametru ''​index''​ și citește de la tastatură o linie. Programul afișează doar un caracter, mai exact al ''​index''​-lea caracter din șirul dat la intrare. 
- 
-<note warning> 
-Când rulați în GDB să dați acel parametru ca argument comenzii ''​run''​ sau ''​start''​ vedeți mai jos. Altfel, rularea nu va fi corespunzătoare. 
-</​note>​ 
- 
-După ce ați citit codul sursă, asamblați și link-editați fișierul. După ce ați obținut executabilul ''​gdb-tutorial''​ (sau ce nume i-ați dat), vom porni GDB-ul cu acel fișier: 
- 
-<​code>​ 
-gdb ./​gdb-tutorial 
-</​code>​ 
- 
-<note tip> 
-Recomandăm folosirea extensiei [[https://​github.com/​longld/​peda|PEDA (Python Exploit Development Assistance) pentru GDB]]. Pentru a putea să o folosiţi, verificați faptul că fişierul ''​~/​.gdbinit''​ conţine linia ''​source ~/​peda/​peda.py''​. Dacă fişierul nu există sau nu conţine acea linie, rulaţi în terminal comanda: 
-<​code>​ 
-echo '​source ~/​peda/​peda.py'​ >> ~/.gdbinit 
-</​code>​ 
- 
-Dacă lucrați pe alt sistem decât cele din sala de laborator, va trebui să clonați în prealabil repository-ul PEDA, așa cum este indicat în [[https://​github.com/​longld/​peda#​installation|README-ul proiectului]]:​ 
-<​code>​ 
-git clone https://​github.com/​longld/​peda.git ~/peda 
-</​code>​ 
-</​note>​ 
- 
-După ce ați pornit programul gdb, toată interacțiunea cu acesta se face prin prompt-ul de gdb. 
- 
-== Lansarea în execuție a programului == 
- 
-Pentru a lansa programul urmărit în execuție există două comenzi disponibile:​ 
-  * **run** - această comandă va lansa în execuție programul 
-  * **start** - spre deosebire de ''​run'',​ această comandă va începe execuția programului,​ însă se va opri imediat după intrarea în main 
- 
-Aceste două comenzi mai pot fi folosite în două feluri: 
- 
-  - <​code>​start 1 2 3 4</​code>​ 
-  - <​code>​start < file.in</​code>​ 
- 
-<note tip> 
-În locul comenzii ''​start''​ puteți folosi mai sus comanda ''​run''​. 
-</​note>​ 
- 
-Utilizarea aceasta este similară cu execuția programului direct din linia de comandă (fără GDB), prima variantă însemnând că se trimit 4 parametri (1, 2, 3 și 4) programului,​ iar a doua, că ''​file.in''​ se redirectează ca intrare standard pentru program. 
- 
-Lansați programul în execuție folosind comanda GDB ''​run''​. Ce observați? Rulați din nou programul, de data aceasta dând comenzii run parametrul corespunzător. 
- 
-GDB se blochează la citirile de la input. Haideți să corectăm asta folosind un fișier de intrare. Creați un fișier (spre exemplu text.in) în directorul cu executabilul care să conțină textul "ana are mere". Porniți din nou GDB și lansați în execuție programul cu parametrul de intrare ''​11''​ și cu fișierul ''​text.in'',​ redirectat. 
- 
-Ce observați? Programul își termină execuția cu succes. Deoarece nu a existat niciun breakpoint setat în program, programul nu s-a oprit din execuție decât când a terminat treaba. 
- 
-În cazul pornirii programului,​ puteți folosi instrucțiunea ''​start''​ care va opri execuția după intrarea în main. 
- 
-== Breakpoints == 
- 
-Elementul esențial al GDB-ului este //​breakpoint-ul//​. Practic, un //​breakpoint//​ setat la o anumită instrucțiune face ca execuția programului să se oprească de fiecare dată când se ajunge la acest punct. 
- 
-Adăugarea unui breakpoint se face cu construcția 
- 
-<​code>​break [location]</​code>​ 
- 
-, unde ''​location''​ poate fi numele unei funcții sau o adresă din zona .text. În cazul cel din urmă, adresa trebuie să fie precedată de ''​*''​ (star). Exemplu: ''​break *0x004013af''​. 
- 
-Pentru continuarea programului după eventuala sa oprire într-un breakpoint, puteți folosi comanda ''​continue''​. 
- 
-Un alt lucru interesant în GDB este comanda ''​commands'',​ care poate asocia unui breakpoint un bloc de comenzi GDB ce vor fi executate la fiecare oprire în breakpoint-ul respectiv. 
- 
-Exemplu: 
-<​code>​ 
-(gdb) break *0x004013af 
-Breakpoint <n> at 0x4013af 
-(gdb) commands <n> 
-Type commands for breakpoint(s) <n>, one per line. 
-End with a line saying just "​end"​ 
-> print $eax 
-> x/i $eip 
-> end 
-</​code>​ 
- 
-Pentru a nu rămâne blocat în breakpoint (spre exemplu dacă scrieți un script de gdb), puteți adăuga în blocul de instrucțiuni și comanda ''​continue''​. 
- 
-Haideți să adăugăm un breakpoint la label-ul ''​ok''​. Dacă dăm ''​continue'',​ vom observa că programul s-a oprit în breakpoint-ul tocmai creat. 
-<​note>​ 
-Variaţii:​\\ ​ 
-**break label** - breakpoint la labelul **label**\\ ​ 
-**break *(label + <​offset>​)** - breakpoint la **label + offset**\\ ​ 
-</​note>​ 
- 
-== Parcurgerea instrucțiunilor == 
- 
-Atunci când execuția programului este oprită (de exemplu la un breakpoint),​ putem da comenzi care continuă execuția "pas cu pas". Pentru a face asta, cel mai des sunt folosite două comenzi: 
- 
-  * **stepi** - care practic trimite o instrucțiune spre execuție și după execuția acesteia întoarce control-ul la debugger (programul se oprește) 
-  * **nexti** - comandă similară cu ''​stepi'',​ însă dacă instrucțiunea curentă este un apel de funcție, debugger-ul nu va intra în funcție (va chema funcția și se va opri la următoarea instrucțiune după ''​call''​) 
- 
-Dacă emitem comanda ''​stepi'',​ putem observa că se afișează instruction pointer-ul instrucțiunii următoare dupa cea la care am făcut break (prima de la label-ul ''​ok''​). 
- 
-== Dezasamblarea programului == 
- 
-Pentru a dezasambla o porțiune de executabil, se poate folosi comanda ''​disassemble''​ din GDB. Dacă aceasta nu primește niciun parametru, va afișa dezasamblarea funcției curente din cadrul execuției. 
- 
-<​note>​ 
-Default, sintaxa folosită de GDB la dezasamblare este cea "​AT&​T"​. Pentru a folosi sintaxa cunoscută vouă (sintaxa intel), executați în GDB comanda ''​set disassembly-flavor intel''​. 
-</​note>​ 
- 
-În cadrul exemplului nostru, dacă cerem dezasamblarea funcției curente (folosind ''​disassemble''​ fără parametri) putem observa că ne aflăm la label-ul ''​ok''​. Observație:​ GDB iterpretează label-ul ''​ok''​ ca o funcție din cauza codului inițial, care este scris în limbaj de asamblare. 
- 
-Pentru a vedea mai clar efectul ''​stepi''/''​nexti''​ putem rula commanda ''​disassemble''​ înainte și după stepping. 
- 
-<​note>​ 
-Dacă ați intrat într-o funcție lungă și nu vreți să dați de ''​nexti''​ de foarte multe ori, vă recomandăm instrucțiunea GDB ''​finish'',​ care "​termină"​ o funcție. **Atenție** la funcțiile recursive. 
-</​note>​ 
-<​note>​ 
-**disassemble label, +<​length>​** - afişează <​length>​ bytes de cod dezasamblat începând de la labelul **label**. 
-</​note>​ 
- 
-== Inspectarea memoriei și a registrelor == 
- 
-Pentru a afișa diferite valori accesibile GDB-ului se folosește comanda ''​print''​. De exemplu, pentru a afișa ​ valoarea unui registru (de exemplu eax), vom folosi construcția ''​print $eax''​. 
- 
-Pentru inspectarea memoriei se folosește comanda ''​x''​ (examine). Modul de folosire al acestei comenzi este următorul: 
-<​code>​ 
-x/nfu address 
-</​code>​ 
-, unde: 
-  * ''​n''​ este numărul de elemente afișate 
-  * ''​f''​ este formatul de afișare (x pentru hexa, d pentru zecimal, s pentru șir de caractere și i pentru instrucțiuni) 
-  * ''​u''​ este dimensiunea unui element (b pentru 1 octet, h pentru 2, w pentru 4 și g pentru 8 octeți) 
- 
-De exemplu, o funcționalitate similară cu ''​disassemble''​ o putem obține și folosind ''​x''​ unde formatul este instrucțiune. Astfel, putem afișa, de exemplu, 10 instrucțiuni începând de la instrucțiunea curentă cu construcția ''​x/​10i $eip''​. 
- 
-=== [1p] 8. Afișarea unor informații la fiecare trecere printr-un breakpoint === 
- 
-Folosind executabilul creat la exercițiul anterior (''​gdb-tutorial.asm''​),​ trebuie să setați un breakpoint la intrare în bucla din program (când se mută în subregistrul ''​al''​ un caracter din șirul input). În plus, trebuie să adăugați o serie de comenzi astfel încât la fiecare intrare în buclă, GDB să afișeze valoarea subregistrului ''​al''​ și valoarea counter-ului (în cazul nostru ''​ecx''​). 
-<note tip> 
-**Hint:** folosiți comanda ''​commands''​. 
-</​note>​ 
- 
-=== [2p] 9. Bonus: Depanarea unui Segfault === 
- 
-Pornind de la executabilul ''​segfault'',​ aflat in directorul ''​9-solve-segfault'',​ rulat sub gdb, analizați atât backtrace-ul cât și pas cu pas codul pentru a identifica cauza care duce la Segmentation Fault. 
- 
-<​note>​ 
-Codul ce se ocupă cu printarea - delimitat în mod explicit în cod - nu este relevant pentru rezolvarea exercițiului. Totuși ar putea fi de interes faptul că registrul ebx este cel din care se preiau datele pentru printare 
-</​note>​ 
-<note tip>​Puteți să vă folosiți și de [[https://​linux.die.net/​man/​1/​objdump|objdump]] pentru a vedea exact conținutul secțiunii ''​.data''​ din cadrul binarului ''​segfault''</​note>​ 
-<note hint>Cu ce este diferit ''​objdump -d''​ de ''​objdump -D''?</​note>  ​ 
  
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0