Differences

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

Link to this comparison view

so:curs:exec-process [2020/03/29 18:10]
razvan.deaconescu created
so:curs:exec-process [2021/04/14 06:24] (current)
razvan.deaconescu [Apelarea unor funcționalități ascunse]
Line 1: Line 1:
 ====== Capitol 07 - Analiza executabilelor și proceselor ====== ====== Capitol 07 - Analiza executabilelor și proceselor ======
  
-  * [[TODO|Notițe de curs]]+  * [[https://​docs.google.com/​document/​d/​154mUxvHLsqigBTzR0R4SfJiiKNCw_0AQaB1F_lgNWYY/​edit?​usp=sharing|Notițe de curs]] 
 + 
 +  * Suport curs 
 +    * Reversing: Secrets of Reverse Engineering 
 +      * Chapter 2: Low-Level Software 
 + 
 +  * Resurse extra 
 +    * [[https://​nora.codes/​tutorial/​an-intro-to-x86_64-reverse-engineering/​|]] 
 +    * [[https://​nora.codes/​tutorial/​additional-exercises-in-reverse-engineering/​|Additional Exercises in Reverse Engineering]] 
 +    * [[https://​www.muppetlabs.com/​~breadbox/​software/​tiny/​teensy.html|A Whirlwind Tutorial on Creating Really Teensy ELF Executables for Linux]] 
 +    * [[https://​beginners.re/​|Reverse Engineering for Beginners]] 
 +    * [[https://​github.com/​onethawt/​reverseengineering-reading-list|Reverse Engineering Reading List]] 
 + 
 +  * Filmări  
 +    * 3CA: https://​web.microsoftstream.com/​video/​f0316c59-353b-4675-a5f4-d268b9d512ac 
 +    * 3CC curs 10, partea 1: https://​web.microsoftstream.com/​video/​5ffc701c-343d-4aad-8528-beb1bb5d11f0 
 +    * 3CC curs 10, partea a 2-a: https://​web.microsoftstream.com/​video/​9152c7c0-0d89-4a5a-a4c6-cab016038242 
 + 
 +  * Curs CA [[https://​www.slideshare.net/​alexandruradovici/​sisteme-de-operare-analiza-executabilelor-i-proceselor|slideshare]]
  
 ===== Demo-uri ===== ===== Demo-uri =====
Line 15: Line 33:
 Acum putem parcurge secțiunile cu demo-uri de mai jos. Acum putem parcurge secțiunile cu demo-uri de mai jos.
  
-==== Executabile fără libc ==== +==== Secțiuni și adrese în cadrul unui fișier executabil ​====
- +
-În mod obișt+
  
-Dorim să investigăm modul în care se alocă variabilele într-un ​executabil. Pentru aceasta accesăm subdirectorul ''​exec-vars/'';​ urmărim conținutul fișierului ''​exec-vars.c''​. În acest fișier definim/alocăm variabile ​în diverse forme: dinamic, static, globalVrem să vedem cum sunt acestea alocate ​în executabil ​și în proces.+Dorim să urmărim adresele secțiunilor și simbolurilor ​în cadrul unui fișier ​executabil ​de tip ELF (//​Executable and Linking Format//). Pentru aceasta accesăm subdirectorul ''​exec-addr/'';​ urmărim conținutul fișierului ''​exec-addr.c''​. În acest fișier definim ​variabile globale și afișăm adresele acestor ​variabile ​și a funcțiilor din modulVom observa că adresele variabilelor globale și a funcțiior ​sunt cunoscute de la link-time, ​în momentul link-editării ​și a obținerii executabilului.
  
 Compilăm programul folosind ''​make''​. Compilăm programul folosind ''​make''​.
  
-Investigăm zonele de memorie ​din executabil ​în care sunt salvate ​variabilele, folosind ​comanda<​code bash> +Pentru început investigăm simbolurile ​din executabil. Ne interesează ​variabilele ​globale și funcțiile așa că vom rula comanda ​de afișare a simbolurilor din care vom extrage liniile de interes:<code bash> 
-objdump -t addr-space | grep var+user@host:​$ ​objdump --syms exec-addr ​| grep '​\(exec_\| main\|simple_func\)'​ 
 +0000000000600da0 l     O .data 0000000000000004 ​             exec_static_int_global 
 +000000000040076b l     F .text 000000000000001a ​             simple_func 
 +0000000000600dc4 g     O .bss 0000000000000004 ​             exec_int_global_noinit 
 +0000000000600da4 g     O .data 0000000000000004 ​             exec_int_global 
 +00000000004008b8 g     O .rodata 0000000000000006 ​             exec_array_ro 
 +0000000000400785 g     F .text 000000000000009c ​             main
 </​code>​ </​code>​
  
-De ce apar doar unele variabile?+Prin rularea comenzii objdump de mai sus afișăm informații despre simboluri, în format pe coloane, astfel: 
 +  * În prima coloană sunt adresele simbolurilor. Aceste adrese se vor regăsi întocmai în proces (vom vedea în continuare). Adresele pot să difere în cazul obținerii executabilului pe alt sistem. 
 +  * A doua coloană este tipul simbolului. Simbolurile statice sunt marcate cu ''​l''​ (//local//) pentru că vor fi locale modulului. Celelalte sunt marcate cu ''​g''​ (//​global//​) și vor putea fi exportate în alte module. 
 +  * A patra coloană este secțiunea din executabil unde este alocat simbolul. Simbolul este alocat în modul încă de la compile-time. Secțiunile sunt după cum urmează: 
 +    * ''​.data'': ​variabile ​globale inițializate 
 +    * ''​.bss'':​ variabile globale neinițializate 
 +    * ''​.rodata'':​ variabile globale de tip read-only 
 +    * ''​.text'':​ zonă de cod/​instrucțiuni (pentru funcții) 
 +  * A cincea coloană este spațiul ocupat de simbol. Variabilele întregi ocupă ''​sizeof(int) = 4''​ octeți, șirul de caractere ocupă 6 octeți (incluzând NUL-terminatorul) iar funcțiile ocupă spațiul dat de codul acestora.
  
-<spoiler Răspuns> +Observăm că zona de date read-only ​(''​.rodata''​) este apropiată de zona de cod (''​.text''​ambele fiind zone care nu pot fi scrise.
-Pentru variabilele alocate dinamic ​(pe stivă, heapse alocă la runtime, adică în momentul în care procesul rulează. Executabilul ​nu are legătură cu datele alocate la runtime. +
-</​spoiler>​+
  
-Ce semnfică ''​l'' ​și ''​g'' ​în output-ul obținut?+Ca să verificăm faptul că adresele precizate în executabil se vor regăsi ​și în momentul rulării procesului, la run-time, rulăm executabilul:<​code bash> 
 +$ ./exec-addr  
 +Inside simple_func
  
-<spoiler Răspuns>​ +Run-time addresses are: 
-Arată că variabilele respective sunt simboluri globale (''​g'' ​la nivelul întregului modul) sau locale (''​l''​ - locale unei funcții). +&​exec_static_int_global:​ 0x600da0 
-</​spoiler>​+&​exec_int_global:​ 0x600da4 
 +&​exec_int_global_noinit:​ 0x600dc4 
 +&​exec_array_ro:​ 0x4008b8 
 +&​simple_func:​ 0x40076b 
 +&main: 0x400785
  
-Pentru a afișa conținutul zonei ''.rodata''​ folosind comanda<​code bash> +Run `pmap -p $(pidof exec-addr)' ​to show process map
-readelf -x .rodata addr-space+ 
 +     Press ENTER to continue ...
 </​code>​ </​code>​
-Secțiunea ''​.rodata''​ conține variabile read-only, în cazul de față literalul ''​rulz''​. Literalii și constantele se stocează în secțiunea ''​.rodata''​ a unui executabil. 
  
-==== Investigarea mapării folosind pmap ====+Observăm din rezultatul rulării că adresele de la run-time sunt aceleași cu cele din executabil.
  
-Vrem să vizualizăm spațiul virtual de adrese al unui proces. Pentru aceastaaccesăm subdirectorul ​''​pmap/''​; urmărim conținutul fișierului ''​pmap.c'';​ facem mapări (folosind apelul ''​mmap''​folosind diverse flag-uri.+După cum ni se indică la rularevom rula ''​pmap'' ​pentru a consulta spațiul virtual de adresă al procesului:<​code bash> 
 +$ pmap -p $(pidof exec-addr) 
 +13545: ​  ​./​exec-addr 
 +0000000000400000 ​     4K r-x-- /​home/​razvan/​school/​2011-2012/​so/​git-repos/​cursuri.git/​curs-07-demo/​exec-addr/​exec-addr 
 +0000000000600000 ​     4K rw--- /​home/​razvan/​school/​2011-2012/​so/​git-repos/​cursuri.git/​curs-07-demo/​exec-addr/​exec-addr 
 +00007fd884842000 ​  1664K r-x-- /​lib/​x86_64-linux-gnu/​libc-2.18.so 
 +00007fd8849e2000 ​  2044K ----- /​lib/​x86_64-linux-gnu/​libc-2.18.so 
 +00007fd884be1000 ​    16K r---- /​lib/​x86_64-linux-gnu/​libc-2.18.so 
 +00007fd884be5000 ​     8K rw--- /​lib/​x86_64-linux-gnu/​libc-2.18.so 
 +00007fd884be7000 ​    16K rw---   [ anon ] 
 +00007fd884beb000 ​   128K r-x-- /​lib/​x86_64-linux-gnu/​ld-2.18.so 
 +00007fd884dcf000 ​    12K rw---   [ anon ] 
 +00007fd884e06000 ​    16K rw---   [ anon ] 
 +00007fd884e0a000 ​     4K r---- /​lib/​x86_64-linux-gnu/​ld-2.18.so 
 +00007fd884e0b000 ​     4K rw--- /​lib/​x86_64-linux-gnu/​ld-2.18.so 
 +00007fd884e0c000 ​     4K rw---   [ anon ] 
 +00007fffb51d1000 ​   132K rw---   [ stack ] 
 +00007fffb51fe000 ​     8K r-x--   [ anon ] 
 +ffffffffff600000 ​     4K r-x--   [ anon ] 
 + ​total ​            ​4068K 
 +</​code>​
  
-Compilăm programul folosind ​''​make''​ și apoi îl rulăm:<​code ​bash+Legat de partea de executabil, observăm că avem două pagini (''​4K''​) mapate din executabilul ''​exec-addr''​. Una este readable/​executable (''​r-x''​) ​și începe de la adresa ''​0x400000'',​ iar alta este readable/​writable (''​rw-''​) și începe de la adresa ''​0x600000''​. În prima pagină sunt mapate secțiunile ''​.text''​ și ''​.rodata'',​ iar în cealaltă sunt mapate secțiunile ''​.data''​ și ''​.bss'';​ observăm acest lucru pe baza adreselor. 
-./pmap+ 
 +==== Executabile statice și dinamice ==== 
 + 
 +Pentru a obține un executabil, folosim linker-ul pentru a lega unul sau mai multe module obiect cu biblioteci. Legarea (//​linking//​) presupune: 
 +  * comasarea secțiunilor similare din modulele obiect și din bilioteci: de exemplu secțiunile de cod (''​.text''​) sunt comasate într-o singură secțiune 
 +  * atașarea de adrese de start fiecărei secțiuni și de adrese fiecărui simbol (//address binding//): fiecare simbol (adică fiecare variabilă, funcție) are acum o adresă unică în cadrul executabilului 
 +  * rezolvarea referințelor în cadrul secțiunilor comasate sau inter-secțiuni:​ o dată știute adresele simobolurilor în cadrul executabilului,​ se pot completa instrucțiunile de procesor (cod mașină) care foloseau aceste simboluri 
 + 
 +În modul simplu, legarea este statică (//static linking//) iar rezultatul este un executabil static. Un executabil static realizează complet toți pașii de mai sus și are în cadrul său toate sețiunile de care are nevoie și toate referințele rezolvate. 
 + 
 +O alternativă la legarea statică este legarea dinamică (//dynamic linking//) care are ca rezultat un executabil dinamic. Un executabil dinamic nu include toate secțiunile și menține referințe nerezolvate;​ rezolvarea acestora este amânată la momentul lansării în execuție a executabilului,​ numit și încărcare (//​loading//,​ //load time//), când se creează procesul corespunzător. 
 + 
 +Un executabil dinamic nu include în secțiunile sale părțile corespunzătoare din biblioteci. Aceste biblioteci sunt biblioteci dinamice și vor fi adăugate în spațiul virtual de adrese al procesului după încărcare (//​loading//​),​ fără a încărca executabilul. 
 + 
 +Astfel, un executabil dinamic va fi semnificativ mai mic decât un executabil static. Pe lângă dimensiunea redusă a executabilului,​ un alt avantaj este partajarea zonelor de cod din cadrul bibliotecilor dinamice cu alte procese. Întrucât aceste zone nu sunt modificate, sunt mapate în spațiul virtual al tuturor proceselor care le foloesc. De exemplu, în cazul bibliotecii standard C, folosită de toate procesele sistemului, zona sa de cod este prezentă o singură dată în memoria RAM și mapată în spațiul virtual de adrese al tuturor proceselor provenite din executabile dinamice. Un proces obținut dintr-un executabil static nu poate partaja nici o parte din zona sa cu alte procese obținute din executabile static, însemnând un consum mai mare de memorie. Bibliotecile dinamice, partajabile,​ poartă în Linux denumirea de ''​shared objects'';​ de aici și extensia fișierelor de tip bibliotecă dinamică ''​.so''​. 
 + 
 +Executabilele dinamice au dezavantajul unui timp mai mare de încărcare. La orice lansare în execuție a executabilului trebuie realizat procesul de rezolvare a referințelor,​ numit //dynamic binding//. Suplimentar,​ dacă un executabil dinamic este transferat pe un sistem unde nu este prezentă o bibliotecă dinamică de care are nevoie, sau este prezentă o versiune incompatibilă,​ acesta nu va rula. 
 + 
 +Sumarizând,​ executabilele statice și dinamice au următoarele avantaje: 
 +  * Executabilele dinamice ocupă mai puțin spațiu pe disc, iar procesele aferente ocupă mai puțină memorie fizică, partajând zonele read-only și read-execute (''​.text'',​ ''​.rodata''​) din bibilioteci cu alte procese obținute din executabile dinamice care folosesc aceleași biblioteci. 
 +  * Executabilele statice sunt portabile, pot fi transferate pe un alt sistem (cu aceeași arhitectură a sistemului pentru care a fost compilat binarul) fără a fi nevoie de prezența unor biblioteci sau biblioteci compatibile. Lansarea lor în execuție (//​loading//​) este mai rapidă, nefiind nevoie de rezolvarea referințelor (//dynamic binding//); toate referințele au fost rezolvate la link time. 
 + 
 +În subdirectorul ''​static-dynamic/''​ se găsește fișierul sursă ''​hello.c''​ și un fișier ''​Makefile''​ pe care îl folosim pentru a compila un executabil static (''​hello-static''​) și unul dinamic (''​hello-dynamic''​):​ 
 +<​code>​ 
 +$ ls 
 +hello.c  Makefile 
 + 
 +$ make 
 +cc -Wall -fno-PIC ​  -c -o hello.o hello.c 
 +cc -no-pie -static -o hello-static hello.o 
 +cc -no-pie -o hello-dynamic hello.o 
 + 
 +$ ls -F 
 +hello.c ​ hello-dynamic* ​ hello.o ​ hello-static* ​ Makefile
 </​code>​ </​code>​
  
-Într-o altă consolă urmărim schimbările din spațiul virtual de adrese al procesului creat folosind comanda<​code ​bash+Putem observa că executabilul static are dimensiunea mai mare decât cel dinamic (de circa 100 de ori mai mare) și că are mai multe simboluri:​ 
-watch -d pmap $(pidof pmap) +<​code>​ 
-</​code>​ Comanda de mai sus urmărește spațiul virtual de adrese. Ca să generăm schimbări în spațiul virtual de adrese, apăsăm ''​ENTER''​ în consola în care rulează programul pentru a continua pașii din cod.+$ ls -lh hello-* 
 +-rwxr-xr-x 1 student student 8.4K Mar 29 21:57 hello-dynamic 
 +-rwxr-xr-x 1 student student 826K Mar 29 21:57 hello-static
  
-Urmărim modificările care apar în urma diferitelor tipuri de mapare din cod. Observăm că se mapează câte o singură pagină; la fiecare operație de mapare spațiul total crește cu 4K, iar la fiecare operație de demapare spațiul total scade cu 4K. Observăm că în cazul mapării partajate permisiunile sunt ''​rw-s'';​ ''​s''​ înseamnă shared. Tot în cazul memoriei partajate apare mapat dispozitivul ''/​dev/​zero'',​ unul dintre modurile uzuale de a face mapare partajată.+$ nm hello-static | wc -l 
 +1664
  
-De ce unele biblioteci sunt mapate cu permisiuni de scriere?+$ nm hello-dynamic | wc -l 
 +38 
 +</​code>​
  
-<spoiler Răspuns>​ +Așa cum am discutatun executabil ​dinamic are referințe nerezolvate către biblioteci (în cazul de față biblioteca standard C), referințe ce vor fi rezolvate la //load time//. Executabilul static nu are referințe nerezolvate:​ 
-Bibliotecile au mai multe secțiunisimilar unui executabil, mapate cu permisiunile corespunzătoare. Secțiunea de cod/text este mapată cu permisiuni ​de citire și execuție (''​r-x''​), cea de date este mapată cu permisiuni de citire și scriere (''​rw-''​) iar cea de date read-only este mapată cu permisiuni doar de citire (''​r--''​)+<​code>​ 
-</​spoiler>​+$ nm hello-dynamic | grep ' ​' 
 +                 U fflush@@GLIBC_2.2.5 
 +                 U fgets@@GLIBC_2.2.5 
 +                 U __libc_start_main@@GLIBC_2.2.5 
 +                 U printf@@GLIBC_2.2.5 
 +                 U puts@@GLIBC_2.2.5 
 +                 U __stack_chk_fail@@GLIBC_2.4
  
-==== Alocarea de memorie virtuală ====+$ nm hello-static | grep ' U ' 
 +
 +</​code>​
  
-Vrem să urmărim modul în care se alocă memorie virtuală în spațiul virtual de adrese al unui proces. Pentru aceasta, accesăm subdirectorul ​''​allocation/''​; urmărim conținutul fișierului ''​allocation.c'';​ în cadrul fișierului se fac alocări de memorie virtuală folosind pe rând apelul ''​malloc''​ și apelul ''​mmap''​.+Utilitarul ​''​ldd'' ​afișează bibliotecile dinamice necesare pentru executabilele dinamice, iar pentru un executabil static nu afișează nimic: 
 +<​code>​ 
 +$ ldd hello-dynamic 
 +        linux-vdso.so.1 (0x00007ffcda18d000) 
 +        libc.so.6 => /​lib/​x86_64-linux-gnu/​libc.so.6 (0x00007f6dbc088000) 
 +        /​lib64/​ld-linux-x86-64.so.2 (0x00007f6dbc479000)
  
-Compilăm fișierul folosind comanda ''​make''​.+$ ldd hello-static 
 +        not a dynamic executable 
 +</​code>​
  
-Deschidem o consolă nouă și rulăm în cele două console astfel: +La rularea oricărui executabil (//​loading//​),​ acesta așteaptă să apăsăm ''​Enter''​ ca să continue. Acest lucru ne permite să investigăm spațiul de adresă pentru procesul proaspăt pornit folosind utilitarul ''​pmap''​ ca mai jos: 
-    * Într-o consolă rulăm executabilul aferent:<​code>​ +<​code>​ 
-./allocation+$ pmap $(pidof hello-dynamic) 
 +24227: ​  ./hello-dynamic 
 +0000000000400000 ​     4K r-x-- hello-dynamic 
 +0000000000600000 ​     4K r---- hello-dynamic 
 +0000000000601000 ​     4K rw--- hello-dynamic 
 +0000000001986000 ​   132K rw---   [ anon ] 
 +00007f556ba27000 ​  1948K r-x-- libc-2.27.so 
 +00007f556bc0e000 ​  2048K ----- libc-2.27.so 
 +00007f556be0e000 ​    16K r---- libc-2.27.so 
 +00007f556be12000 ​     8K rw--- libc-2.27.so 
 +00007f556be14000 ​    16K rw---   [ anon ] 
 +00007f556be18000 ​   156K r-x-- ld-2.27.so 
 +00007f556c01d000 ​     8K rw---   [ anon ] 
 +00007f556c03f000 ​     4K r---- ld-2.27.so 
 +00007f556c040000 ​     4K rw--- ld-2.27.so 
 +00007f556c041000 ​     4K rw---   [ anon ] 
 +00007ffeceb5d000 ​   132K rw---   [ stack ] 
 +00007ffeceba4000 ​    12K r----   [ anon ] 
 +00007ffeceba7000 ​     4K r-x--   [ anon ] 
 +ffffffffff600000 ​     4K --x--   [ anon ] 
 + ​total ​            ​4508K 
 + 
 +$ pmap $(pidof hello-static) 
 +24245: ​  ​./​hello-static 
 +0000000000400000 ​   728K r-x-- hello-static 
 +00000000006b6000 ​    24K rw--- hello-static 
 +00000000006bc000 ​     4K rw---   [ anon ] 
 +000000000091c000 ​   140K rw---   [ anon ] 
 +00007ffdb1a33000 ​   132K rw---   [ stack ] 
 +00007ffdb1b80000 ​    12K r----   [ anon ] 
 +00007ffdb1b83000 ​     4K r-x--   [ anon ] 
 +ffffffffff600000 ​     4K --x--   [ anon ] 
 + ​total ​            1048K
 </​code>​ </​code>​
-    * Într-o altă consolă vizualizăm dimensiunea ​spațiului fizic ocupat ​și a a spatiului ​virtual ​ocupat, folosind comanda<​code ​bash+ 
-watch -n 1 ps -o pid,​rss,​vsz,​cmd -p $(pidof allocation)+Observăm că în cazul executabilului dinamic, ​spațiul virtual de adrese cuprinde bibliotecile dinamice partajate (biblioteca standard C ''​libc-2.27.so'' ​și dynamic linker/​loader-ul ''​ld-2.27.so''​). Spațiul ​virtual ​de adrese al executabilului static este mai reduscuprinzând doar zonele specifice executabilului și zonele dinamice ale procesului (stivă, heap, mapări anonime), fără biblioteci. 
 + 
 +==== Executabile fără libc ==== 
 + 
 +<note important>​ 
 +Pentru acest demo trebuie să aveți instalat pe sistem asamblorul NASM. Pe un sistem Debian/​Ubuntu îl puteți instala ​folosind comanda
 +<​code>​ 
 +sudo apt install nasm
 </​code>​ </​code>​
 +</​note>​
 +
 +În mod obișnuit un executabil este obținut prin legarea unui sau a mai multor module obiect la biblioteca standard C (''​libc''​) și, la nevoie, alte biblioteci. Așa cum am prezentat mai sus, în cazul legării statice (//static linking//) linker-ul adaugă în executabil toate componentele necesare (cod, date) din cadrul bibliotecii statice; în cazul legării dinamice (//dynamic linking//) linker-ul adaugă referințe la simbolurile din bibliotecile dinamice, iar rezolvarea acestor simboluri și adăugarea componentelor se întămplă la încărcare (//load time//).
 +
 +Putem crea programe și folosi linker-ul să nu folosească deloc biblioteca standard C. Acest lucru este util pentru medii de lucru specializate (numite //free standing environments//​) în care nu avem acces la o bibliotecă standard C sau nu o putem folosi. Astfel de programe vor implementa singure funcționalități similare celor oferite de biblioteca standard C sau vor realiza direct apeluri de sistem.
 +
 +În directorul ''​no-libc/''​ avem un exemplu de astfel de program, care folosește direct apeluri de sistem pentru a afișa un mesaj la ieșirea standard. Programul este scris în limbaj de asamblare pentru arhictectura x86_64 în sintaxa AT&T folosind asamblorul GNU (''​gas''​ parte din GCC) în fișierul ''​hello.s''​ și în sintaxa Intel folosind asamblorul NASM în fișierul ''​hello.asm''​.
 +
 +În ambele fișiere (''​hello.s''​ și ''​hello.asm''​) invocăm apelul de sistem ''​write(1,​ "​Hello,​ world!\n",​ 14);''​. Pentru invocarea acestui apel de sistem, urmărim [[https://​blog.rchapman.org/​posts/​Linux_System_Call_Table_for_x86_64/​|convenția de apel de sistem specifică arhitecturii x86_64 pe Linux]]:
 +  * registrul ''​rax''​ reține numărul apelului de sistem (''​1''​ pentru apelul de sistem ''​sys_write''​)
 +  * registrul ''​rdi''​ reține primul argument (''​1''​ pentru descriptorul de fișier corespunzător ieșirii standard - //standard output//)
 +  * registrul ''​rsi''​ reține al doilea argument (adresa mesajului de afișat, adică adresa șirului ''​Hello,​ world!\n''​)
 +  * registrul ''​rdx''​ reține al treilea argument (''​14''​ pentru lungimea mesajului de afișat)
 +
 +Pentru încheierea execuției, invocăm apelul de sistem ''​exit(0)''​.
 +
 +Pentru a obține executabilele este nevoie de un pas de asamblare, care obține fișierul obiect corespunzător și un pas de legare care obține fișierul executabil. Realizăm acești pași prin intermediul comenzii ''​make'':​
 +
 +<​code>​
 +$ ls
 +hello.asm ​ hello.s ​ Makefile
  
-Observați cum crește dimensiunea spațiului fizic și a spatiului virtual în cazul folosirii ''​malloc''​ și **doar** a spațiului virtual folosind ''​mmap''​.+$ make 
 +nasm -f elf64 -o hello-nasm.o hello.asm 
 +cc -nostdlib -no-pie -Wl,​--entry=main -Wl,​--build-id=none ​ hello-nasm.o ​  -o hello-nasm 
 +gcc -c -o hello-gas.o hello.s 
 +cc -nostdlib -no-pie -Wl,​--entry=main -Wl,​--build-id=none ​ hello-gas.o ​  -o hello-gas
  
-Pentru a explica acest comportament,​ rulăm executabilul prin ''​strace'':<​code>​ +$ ls 
-strace ​./allocation+hello.asm  hello-gas ​ hello-gas.o ​ hello-nasm ​ hello-nasm.o ​ hello.s ​ Makefile
 </​code>​ </​code>​
  
-Observăm că în cazul funcției de bibliotecă ​''​malloc'' ​se realizează apelul de sistem ​''​brk''​, în timp ce în cazul funcției de bibliotecă ​''​mmap'' ​se realizează apelul de sistem ​''​mmap''​. Apelul de sistem ''​mmap''​ alocă **doar** memorie virtuală.+În urma rulării comenzii am obținut fișierele obiect ​''​hello-nasm.o'' ​și ''​hello-gas.o'' ​și fișierele executabile ​''​hello-nasm'' ​și ''​hello-gas''​.
  
-Observăm de asemenea, că se realizează un număr redus de apeluri de sistem ''​brk''​ raportat la cele ''​1024''​ de apeluri de bibliotecă ''​malloc''​. Un apel ''​brk''​ alocă un pool mai mare de memorie care va fi apoi folosit ​la apeluri viitoare ''​malloc'';​ realizează o prealocare.+Rulăm cele două executabile care vor afișa mesajul ​''​Hello, world!''​ la ieșirea standard:
  
-Pentru a vedea comportamentului funcției de bibliotecă ''​malloc'',​ actualizăm codul astfel încât ''​malloc''​ să aloce, la fel ca ''​mmap''​ calupuri de ''​1MB''​ de memorie. Adică bucla ''​for''​ aferentă să arate așa:<​code ​c+<​code>​ 
- for (cnt = 0; cnt < NUM_ROUNDS; cnt++) { +./hello-nasm 
- puts("​Using malloc to allocate 1024 sets of 1024 bytes."); +Helloworld! 
- p = malloc(1024*1024);​ +$ ./hello-gas 
- DIE(p == NULL"​malloc"​);​ +Hello, world!
- sleep(2); +
- }+
 </​code>​ </​code>​
  
-Compilăm noua sursă folosind comanda ''​make''​. La fel ca mai devreme rulăm executabilul într-o consolă și comanda ​de vizualizare în altă consolă. Observăm că acum atât funcția de bibliotecă ''​malloc''​cât și funcția de bibliotecă ​''​mmap''​ alocă **doar** memorie virtuală.+Putem observa diferențe între aceste executabile (obținute fără suport de bibliotecă standard C) și cele de la demo-ul anteriorobținute cu suport ​de bibliotecă ​standard C.
  
-Folosim în continuare ''​strace'' ​pentru a investiga:<​code ​bash+Fișierele sunt mai mici, nu conțin elemente de setup și cleanup din biblioteca standard C, au doar miminul necesar ​pentru a funcționaau minimul de simboluri, minimul de secțiuni, minimul de cod, așa cum putem observa rulând comenzile de mai jos: 
-strace ./​allocation+ 
 +<​code>​ 
 +$ ls -l hello-gas hello-nasm 
 +$ nm hello-gas 
 +$ nm hello-nasm 
 +$ objdump -d hello-gas 
 +$ objdump -d -M intel hello-nasm 
 +$ readelf -S hello-gas 
 +$ readelf -S hello-nasm
 </​code>​ </​code>​
  
-Observăm că acum și funcția de bibliotecă ''​malloc'' ​folosește în spate tot apelul de sistem ''​mmap''​. Acesta alocă doar memorie virtuală de unde și comportamentulDe la valoare dată, alocarea cu ''​malloc''​ folosește apelul de sistem ''​mmap''​.+Putem obține aceste executabile fără să apelăm comanda ​''​gcc'' ​cu un număr complicat ​de parametriPutem invoca direct linker-ul folosind comenzile:​ 
 +<​code>​ 
 +ld -hello-gas --entry=main hello-gas.
 +ld -o hello-nasm --entry=main hello-nasm.o 
 +</​code>​
  
-<spoiler Despre apelul malloc>​ +Nu în ultimul rând, putem reduce dimensiunea executabilelor eliminând tabela ​de simboluri cu ajutorul comenzii ​''​strip'':​ 
-Valoarea de la care funcția ​de bibliotecă ​''​malloc'' ​folosește ''​mmap''​ este definită de ''​MMAP_THRESHOLD'',​ în mod implicit configurat la ''​128KB''​. Detalii se găsesc în [[http://​man7.org/​linux/​man-pages/​man3/​malloc.3.html#​NOTES|pagina de manual a malloc]]:<​code>​ +<​code>​ 
-       Normally, malloc() allocates memory from the heap, and adjusts the +$ strip hello-nasm 
-       size of the heap as required, using sbrk(2). ​ When allocating blocks +$ strip hello-gas
-       of memory larger than MMAP_THRESHOLD bytes, the glibc malloc() +
-       ​implementation allocates the memory as a private anonymous mapping +
-       using mmap(2). ​ MMAP_THRESHOLD is 128 kB by default, but is +
-       ​adjustable using mallopt(3). ​ Allocations performed using mmap(2) are +
-       ​unaffected by the RLIMIT_DATA resource limit (see getrlimit(2)).+
 </​code>​ </​code>​
-</​spoiler>​ 
  
-==== Paginare la cerere (demand paging) ====+<note tip> 
 +Vedeți [[https://​www.muppetlabs.com/​~breadbox/​software/​tiny/​teensy.html|aici]] un articol despre cum puteți obține un executabil cât mai mic pe un sistem Linux. 
 +</​note>​
  
-Vrem să urmărim modul în care se alocă pagini de memorie fizică la cerere, proces care se numește ''​(on) demand paging''​. Pentru aceasta, accesăm subdirectorul ''​demand-paging/'';​ urmărim conținutul fișierului ''​demand-paging.c'';​ în cadrul fișierului alocăm memorie virtuală folosind ''​mmap''​ șapoi accesăm primul octet al fiecărei pagini alocate.+==== Apelarea unor funcționalități ascunse ====
  
-Compilăcodul sursă folosind comanda ''​make''​.+În mod obișnuit, un program execută fluxul de apel dat de codul său și de intrarea venită de la utilizator sau alte procese sau alte forme de I/O. În anumite situații putem dori să declanșăm mai repede un apel al unei funcționalități prezente în executabil, dar care nu se găsește pe fluxul de apel curent.
  
-Avem nevoie ​de două console+La nivel de provocare (//​challenge//​) în lumea competițiilor de tip Capture the Flag (CTF), sunt situații în care un program conține funcționalități ascunse; adică sunt funcții și variabile prezente în program dar care nu sunt apelate sau folosite explicit. Un astfel de exemplu este în subdirectorul ''​hidden/''​. 
-  * Într-consolă rulăm executabilul ​aferent:<​code>​ + 
-./demand-paging+În subdirectorul ''​hidden/''​ se găsește executabilul ''​hidden''​ obținut din compilarea și legarea fișierelor ''​hidden.c''​ și ''​reader.c''​. Obiectivul nostru este să afișăm mesajul ''​success''​ cu ajutorul acestui program, în orice mod posibil. 
 + 
 +Pentru început, observăm că acest program se apelează funcția ''​hidden_function()''​. Numai că apelul se realizează astfel încât depinde de valori aleatoare. În plus valorile variabilelor ''​test''​ și ''​success_message''​ nu ajută la obiectivul nostru: afișarea mesajului ''​success''​. Identificăm următoarele limitări ale programului,​ limitări peste care trebuie să trecem
 +  * variabila ''​test''​ are valoare predefinită care nu este modificată în program 
 +  * variabila ''​success_message''​ are o valoare predefinită care nu este modificată în program 
 +  * al doilea apel al funcției ''​hidden_function()''​ depinde de două valori aleatoare, generate la rulare (//run time//) 
 + 
 +=== Primul apel de funcție: analiză statică === 
 + 
 +Pentru început ne propunem să realizăm primul apel al funcției ''​hidden_function()'',​ adică apelul ''​hidden_function(100)''​ apel ce nu depinde de valori aleatoare generate la rulare. Putem să realizăm acest apel folosind doar analiză statică (pe executabil) și modificând ​executabilul. Adică urmărim: 
 +  * Să suprascriem valoarea variabilei ''​test''​ cu valoarea ''​100'',​ argumentul apelului ''​hidden_function()''​. În felul acesta vom trece de condiția ''​if''​ din cadrul funcției ''​hidden_function()''​ (linia ''​11''​ din fișierul ''​hidden.c''​). 
 +  * Să suprascriem valoarea variabilei ''​success_message''​ cu valoarea ''​success\0''​. în felul acesta apelul funcției ''​puts()''​ din cadrul funcției ''​hidden_function()''​ (linia ''​12''​ din fișierul ''​hidden.c''​). 
 + 
 +Pentru a realiza aceste suprascrieri trebuie să știm la ce offset în cadrul fișierului executabil se găsesc cele două valori. Pentru a determina acest offset, vom face pașii: 
 +  - Aflăm ce adresă le este asociată (//address binding//) celor două variabile (''​test''​ și ''​success_message''​). Fie aceste adrese ''​test_address''​ și ''​success_message_address''​. 
 +  - Aflăm ce adresă are asociată zona ''​.data''​ în care se găsesc cele două variabile. Fie această adresă ''​data_address''​. 
 +  - Aflăm la ce offset în cadrul fișierului executabil se găsește zona ''​.data''​. Fie acest offset ''​data_offset''​. 
 +  - Calculăm offset-ul celor două variabile în cadrul fișierului:​ ''​test_offset = test_address - data_address + data_offset'',​ ''​success_message_offset = success_message_address - data_address + data_offset''​. 
 + 
 +Pasul ''​1''​ îl putem realiza folosind utilitarul ''​nm''​ sau utiltarul ''​readelf'':​ 
 +<​code>​ 
 +$ nm hidden | grep ' test$' | awk -F '[ \t]+' '​{print $1;​}'​ 
 +0000000000601080 
 + 
 +$ readelf -s hidden | grep ' test$' | awk -F '[ \t]+' '​{print $3;​}'​ 
 +0000000000601080
 </​code>​ </​code>​
-  * Într-o altă consolă vizualizăm dimensiunea spațiului fizic ocupat ​și a a spatiului virtual ocupat, ​folosind ​comanda:<​code ​bash+ 
-watch -n 1 ps -o pid,​rss,​vsz,​cmd -p $(pidof demand-paging)+Pasul ''​2'' ​și pasul ''​3''​ îi realizăm ​folosind ​utilitarul ''​readelf''​: 
 +<​code>​ 
 +$ readelf ​-S hidden | grep ' .data ' | awk -F '[ \t]+' '​{print ​$5;}' 
 +0000000000601060 
 + 
 +$ readelf ​-S hidden | grep ' .data ' | awk -F '[ \t]+' '​{print $6;​}'​ 
 +00001060
 </​code>​ </​code>​
  
-Observăm cum crește dimensiunea spațiului spatiului virtual (coloana ​''​VSZ''​) fără a crește dimensiunea spațiului fizic în prima parte. Se face alocare de memorie virtuală, fără paginare - adică fără alocare de spațiu fizic aferent. În partea a doua, observați cum crește dimensiunea spațiului fizic (coloana ''​RSS''​) în a doua parte (fără a crește dimensiunea spațiului virtual). Aceasta este //(on) demand paging//, cu alocarea paginilor fizice necesare la nevoie.+Pasul ''​4'' ​îl realizăm prin operații aritmetice simple.
  
-Ca să detaliem, urmărim page fault-urile realizate pe parcursul rulării programului. Folosimla fel, două console+O dată aflat offset-ul putem folosi utilitarul ''​dd''​ pentru a suprascrie părți din fișiercu template-ul
-  * Într-o consolă rulăm executabilul aferent:<​code>​ +<​code>​ 
-./demand-paging+$ echo -en "<​value>"​ | dd seek=<​offset>​ of=hidden bs=1 conv=notrunc
 </​code>​ </​code>​
-  * Într-o altă consolă vizualizăm numărul de page fault-uri generate de program (''​min_flt'' ​este coloana de interes, ​''​maj_flt'' ​este pentru interacțiuni ​cu discul -- swapping):<​code ​bash+ 
-watch -n 1 ps -o pid,​min_flt,​maj_flt,​cmd -p $(pidof demand-paging)+Acești pași sunt realizați în scriptul ​''​rewrite_exec.sh''​. În urma rulării acestui script obținem fișierul ​''​hidden_copy''​ cu modificările realizate pentru a afișa mesajul ''​success''​: 
 +<​code>​ 
 +./​rewrite_exec.sh 
 +test_offset:​ 0x1080 
 +success_message_offset:​ 0x10a0 
 +Generated updated executable in hidden_copy. 
 + 
 +$ ./​hidden_copy 
 +success 
 +Give number: ^C
 </​code>​ </​code>​
-Observați cum nu există page fault-uri în prima pare a rulării programului,​ în momentul în care facem mapări de memorie. Dar apar page fault-uri în a doua parte a rulării programului. 
  
-Câte page fault-uri sunt generate la o "​trecere prin chunk"?​ De ce?+În acest moment avem realizat cu succes primul apel al funcției ''​hidden_function()''​ în urmă căruia este apelat mesajul ''​success''​.
  
-<spoiler Răspuns+<note tip
-Se generează 256 page fault-uri. Asta se întâmplă pentru ca la fiecare "​trecere prin chunk" se accesează 256 pagini, fiecare pagină având câte 4KB, pentru un total de 1MBUn page fault înseamnă alocarea (on demand) a unei pagini fizice+Pentru mod programatic și avansat de analiză și modificare ​de executabile putem folosi [[https://​lief.quarkslab.com/​|LIEF]] sau [[http://​docs.pwntools.com/​en/​stable/​elf/​elf.html|Modulul ''​pwnlib.elf.elf''​ din pwntools]]
-</spoiler>+</note>
  
-==== Page fault-uri la fork (copy-on-write) ====+=== Al doilea apel de funcție: analiză dinamică ​===
  
-Vrem să urmărim realizarea page fault-urilor în urma unui apel ''​fork''​; page fault-urilor vor fi cauzate de mecanismul de copy-on-write în momentul în care unul dintre cele două procese (copil sau părinte) scrie în zona respectivă. Pentru ​aceastaaccesăm subdirectorul ''​fork-faults'';​ urmărim conținutul fișierului ''​fork-faults.c''​. În cadrul fișierului ''​fork-faults.c''​ se execută următorii pași: +Folosind doar analiză statică (lucru pe executabil) am realizat primul ​apel reușit al funcției ​''​hidden_function()''​. Urmărim să realizăm și al doilea apel. Pentru ​al doilea apel depindem de informații aleatoareror valoarea este știută doar la rulare (//run time//). De aceea avem nevoie ​de analiză dinamică, adică investigația folosind un debugger, ​în cazul nostru GDB.
-  - se alocă memorie virtuală folosind ''​mmap''​ +
-  - se alocă memorie fizică pentru paginile de mai sus, folosind ​//(on) demand paging// prin accesarea primului octet al fiecărei pagini +
-  - se creează un proces nou +
-  - procesul copil citește valoarea din prima jumătate a numărului ​de pagini (**doar** citește) +
-  - procesul copil scrie o valoare în cadrul fiecărei pagini din a două jumătate +
-  - procesul părinte scrie o valoare ​în toate paginile+
  
-Ca să urmărim ce se întâmplă, compilăfișierul ​folosind ​''​make''​. ​Apoi rulăm programul obținut:<​code bash> +Pentru a apela corespunzător a doua oară funcția ''​hidden_function()''​trebuie ca cele două condiții ''​if''​ (liniile ''​11''​ și ''​26''​ din fișierul ''​hello.c''​) să fie satisfăcutePentru aceasta trebuie: 
-./​fork-faults +  * să aflăm valorile ''​r1''​ și ''​r2''​ generate aleator 
-</​code>​ Folosim ​''​ENTER'' ​pentru a continua programul, dar după rularea ​''​pidstat'' ​(mai jos).+  * să transmitem la intrarea standard în variabila ''​in''​ valoarea variabilei ''​r1''​ ca să satisfacem condiția ''​if''​ de la linia ''​26''​ 
 +  * să suprascriem valoarea variabilei ​''​test'' ​cu valorea variabilei ''​r2''​ ca să satisfacem condiția ​''​if''​ de la linia ''​11''​
  
-Într-o altă consolă folosim utilitarul ​''​pidstat'' ​din pachetul ​''​sysstat'' ​care permite monitorizarea page fault-urilor unui proces ​(prin intermediul ​argumentului ​''​-r''​). Dacă nu există comanda ​''​pidstat'' ​trebuie ​să instalăm pachetul ​''​sysstat'' ​folosind comanda:<​code ​bash+Ne vom folosi de facilitățile GDB pentru acest lucru: 
-apt-get install sysstat+  * folosirea de breakpoint-uri și suspendarea execuției programului/​procesului într-un punct convenabil 
 +  * aflarea adreselor simbolurilor din cadrul programului 
 +  * investigarea conținutului memoriei programului/​procesului 
 +  * suprascrierea conțiinutului memoriei programului/​procesului 
 + 
 +Adică vom urma pașii: 
 +  ​Vom suspenda execuția programului după generarea celor două numere aleatoare ​''​r1'' ​și ''​r2''​
 +  - Vom afla valorile celor două numere. 
 +  - Vom afla adresa variabilei ''​test''​ în memorie. 
 +  - Vom suprascrie valoarea variabilei ''​test''​ cu valoarea variabilei ''​r2''​ ca să satisfacem condiția ''​if''​ de la linia ''​11''​. 
 +  ​Vom continua execuția programului și la citirea valorii de la intrarea standard ​(prin intermediul ​funcției ​''​read_uint()''​) ​vom transmite valoarea variabilei ​''​r1'' ​ca să satisfacem condiția ''​if''​ de la linia ''​26''​. 
 +  - Programul își va continua execuția și va apela corespunzător funcția ​''​hidden_function()'' ​care va apela a doua oară mesajul ''​success''​. 
 + 
 +Pașii sunt urmați în GDB în secvența de mai jos: 
 + 
 +<​code>​ 
 +$ gdb -q ./​hidden_copy 
 +Reading symbols from ./​hidden_copy...done. 
 +(gdb) start 
 +Temporary breakpoint 1 at 0x400702: file hidden.c, line 20. 
 +Starting program: /​home/​student/​so/​demo/​exec-process/​hidden/​hidden_copy 
 + 
 +Temporary breakpoint 1, main () at hidden.c:​20 
 +20              hidden_function(100);​ 
 +(gdb) set disassembly-flavor intel 
 +(gdb) disass 
 +Dump of assembler code for function main: 
 +   ​0x00000000004006fa <​+0>: ​    ​push ​  rbp 
 +   ​0x00000000004006fb <​+1>: ​    ​mov ​   rbp,rsp 
 +   ​0x00000000004006fe <​+4>: ​    ​sub ​   rsp,0x10 
 +=> 0x0000000000400702 <​+8>: ​    ​mov ​   edi,0x64 
 +   ​0x0000000000400707 <​+13>: ​   call   ​0x4006d7 <​hidden_function>​ 
 +   ​0x000000000040070c <​+18>: ​   mov    edi,0x0 
 +   ​0x0000000000400711 <​+23>: ​   call   ​0x4005d0 <​time@plt>​ 
 +   ​0x0000000000400716 <​+28>: ​   mov    edi,eax 
 +   ​0x0000000000400718 <​+30>: ​   call   ​0x4005a0 <​srand@plt>​ 
 +   ​0x000000000040071d <​+35>: ​   call   ​0x4005e0 <​rand@plt>​ 
 +   ​0x0000000000400722 <​+40>: ​   mov    DWORD PTR [rbp-0x4],​eax 
 +   ​0x0000000000400725 <​+43>: ​   call   ​0x4005e0 <​rand@plt>​ 
 +   ​0x000000000040072a <​+48>: ​   mov    DWORD PTR [rbp-0x8],​eax 
 +   ​0x000000000040072d <​+51>: ​   mov    edi,​0x400834 
 +   ​0x0000000000400732 <​+56>: ​   call   ​0x400753 <​read_uint>​ 
 +   ​0x0000000000400737 <​+61>: ​   mov    DWORD PTR [rbp-0xc],​eax 
 +   ​0x000000000040073a <​+64>: ​   mov    eax,DWORD PTR [rbp-0xc] 
 +   ​0x000000000040073d <​+67>: ​   cmp    eax,DWORD PTR [rbp-0x4] 
 +   ​0x0000000000400740 <​+70>: ​   jne    0x40074c <​main+82>​ 
 +   ​0x0000000000400742 <​+72>: ​   mov    eax,DWORD PTR [rbp-0x8] 
 +   ​0x0000000000400745 <​+75>: ​   mov    edi,eax 
 +   ​0x0000000000400747 <​+77>: ​   call   ​0x4006d7 <​hidden_function>​ 
 +   ​0x000000000040074c <​+82>: ​   mov    eax,0x0 
 +   ​0x0000000000400751 <​+87>: ​   leave 
 +   ​0x0000000000400752 <​+88>: ​   ret 
 +End of assembler dump. 
 +(gdb) b *0x0000000000400732 
 +Breakpoint 2 at 0x400732: file hidden.c, line 25. 
 +(gdb) c 
 +Continuing. 
 +success 
 + 
 +Breakpoint 2, 0x0000000000400732 in main () at hidden.c:​25 
 +25              in = read_uint("​Give number: "); 
 +(gdb) x/2wd $rbp-8 
 +0x7fffffffdc08:​ 1586380876 ​     1175232931 
 +(gdb) p test 
 +$1 = 100 
 +(gdb) p &test 
 +$2 = (unsigned int *) 0x601080 <​test>​ 
 +(gdb) set *0x601080=1586380876 
 +(gdb) p test 
 +$1 = 1586380876 
 +(gdb) c 
 +Continuing. 
 +Give number: 1175232931 
 +success 
 +[Inferior 1 (process 28011) exited normally] 
 +(gdb) q
 </​code>​ </​code>​
  
-Pentru ​rula ''​pidstat''​ și a urmări page fault-urile, folosim, pe a doua consolă, comanda<​code bash> +Comenzile GDB folosite în secvențde mai sus sunt: 
-pidstat ​--T ALL -p $(pidof fork-faults100 +  * ''​start''​: pentru a porni procesul ​și a crea un breakpoint la începutul funcției ''​main''​ 
-</​code>​ Comanda de mai sus afișează câte un mesaj la fiecare 5 secunde. În prima consolă apăsă''​ENTER''​ pentru ​a continua ​rularea ​și urmărim informațiile afișate de ''​pidstat'', ​apoi continuăm apăsarea ''​ENTER'' ​etc.+    * se apelează prima instanță a funcției ''​hidden_function'',​ se afișează ''​success''​ 
 +  * ''​set disassembly-flavor intel'':​ pentru a dezasambla în sintaxă Intel 
 +  * ''​disass'':​ pentru a dezasambla codulsă urmărim unde putem plasa un breakpoint 
 +  * ''​b *0x0000000000400732'':​ pentru a crea un breakpoint înainte de apelul funcției ''​read_uint'';​ adică *după* ce au fost generate în variabilele ''​r1''​ și ''​r2''​ 
 +    * variabilele ''​r1''​ și ''​r2''​ sunt reținute ​pe stivă respectiv la adresele ''​rbp-4''​ și ''​rbp-8'';​ observăm în secvențdezasamblată între adresele ''​0x40071d''​ și ''​0x40072a''​ 
 +  * ''​c'':​ pentru continuarea execuției până la breakpoint-ul de mai sus 
 +    * acum suspendat programul la breakpointputem citi valorile variabilelor ''​r1''​ și ''​r2''​ 
 +  * ''​x/​2wd $rbp-8'':​ pentru citirea a două (''​2''​) numere pe 32 de biți (''​w''​) și afișarea lor în format zecimal (''​d''​) de la adresa ''​rbp-8'',​ adică valorile de la ''​rbp-8'' ​(''​r2''​) și ''​rbp-4''​ (''​r1''​). Rezultă de aici că ''​r1''​ este ''​1175232931''​ și ''​r2''​ este ''​1586380876''​. 
 +  * ''​p test'':​ pentru afișarea valorii variabilei ''​test'',​ inițial ''​100''​ 
 +  * ''​p &​test'':​ pentru ​afișarea adresei variabilei ''​test'',​ adică ''​0x601080''​ 
 +  * ''​set *0x601080=1586380876'':​ pentru modificarea valorii de la adresa ''​0x601080''​ (adică a variabilei ''​test''​) în valoarea variabilei ''​r2''​ (''​1586380876''​) 
 +  * ''​p test'':​ pentru verificarea noii valori a variabilei ''​test'';​ se confirmă că am modificat valoarea la 1586380876 
 +  * ''​c''​pentru ​continuarea execuției programului 
 +    * rulează funcția ''​read_uint''​ care solicită furnizarea unui număr ce va fi comparat cu ''​r1'';​ furnizăm valoarea lui ''​r1''​ adică ''​1175232931''​ 
 +    * programul continuă ​rularea, apelează funcția ''​hidden_function'', ​afișează a doua oară ''​success''​ 
 +  * ''​q'':​ pentru încheierea sesiunii curente și închiderea GDB 
 + 
 +=== Alte trucuri === 
 + 
 +Pașii ​de mai sus pot fi simplificați prin folosirea în GDB a comenzii ​''​call'' ​care permite apelul direct al unei funcții. Astfella începutul sesiunii GDB a programului,​ putem declanșa apelul direct al funcției ''​hidden_function''​ ca mai jos: 
 + 
 +<​code>​ 
 +(gdb) start 
 +Temporary breakpoint 1 at 0x400702: file hidden.c, line 20. 
 +Starting program: /​home/​student/​so/​demo/​exec-process/​hidden/​hidden_copy  
 + 
 +Temporary breakpoint 1, main () at hidden.c:​20 
 +20              hidden_function(100);​ 
 +(gdb) call hidden_function(100) 
 +success 
 +</​code>​ 
 + 
 +În felul acesta putem apela orice funcție din executabil fără a fi nevoie să facem operații mai complicate de investigare și modificare a memoriei. 
 + 
 +Ba mai mult, putem apela direct funcția ​''​puts''​
 +<​code>​ 
 +(gdb) call puts("​success"​) 
 +success 
 +$1 = 8 
 +</​code>​ 
 + 
 +<note tip> 
 +GDB nu are o interfață mai puțin prietenoasă și lipsuri pe partea de exploiting și reverse engineeringPentru acestă, vă recomandăm să augmentați GDB cu proiecte dedicate pentru acest lucru: 
 +  * [[http://​pwndbg.re/​|pwndbg]] 
 +  * [[https://​gef.readthedocs.io/​|GEF]] 
 +  * [[https://​github.com/​longld/​peda|PEDA]] 
 + 
 +PEDA este o versiune mai puțin menținută curentă, dar cu o istorie mai lungă în comunitatea de securitatea. pwndbg și GEF sunt proiecte mai recente, cu funcționalități mai multe și comunități active de dezvoltare. 
 + 
 +Cele trei proiecte pot fi configurate să fie prezente simultan (dar folosite alternativ) în GDB, așa cum este descris [[https://​infosecwriteups.com/​pwndbg-gef-peda-one-for-all-and-all-for-one-714d71bf36b8|aici]] (și configurat automat cu [[https://​github.com/​apogiatzis/​gdb-peda-pwndbg-gef|acest repository]]). 
 +</​note>​
  
-Urmărim, în outputul comenzii ''​pidstat'',​ evoluția numărului de page fault-uri pentru cele două procese: părinte și copil. Page fault-urile care apar în cazul unui copy-on-write în procesul copil vor fi vizibile ulterior și în procesul părinte. Observăm că procesul copil generează page fault-uri doar pe jumătate din pagini (cele în care scrie), iar procesul părinte generează page fault-uri pe toate paginile. Asta pentru că un proces creează o copie a paginilor inițiale, dar lasă acele pagini ''​read-only'',​ iar alt proces primește page fault dar doar schimbă permisiunile din read-only în read-write. 
so/curs/exec-process.1585494608.txt.gz · Last modified: 2020/03/29 18:10 by razvan.deaconescu
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