l'arte dell'hacking - jon erickson

1037

Upload: marco-luzzara

Post on 16-Dec-2015

200 views

Category:

Documents


38 download

DESCRIPTION

an easy and an excellent way to learn hacking

TRANSCRIPT

  • L'ARTE DELL'HACKING

    Volume 1

    Jon Erickson

  • Apogeo s.r.l. - socio unicoGiangiacomo Feltrinelli Editore s.r.l.

    ISBN edizione cartacea:978-88-50-32873-4

    Copyright (C) 2008 by Jon Erickson.Title of English-language original:Hacking: the Art of Exploitation, 2ndEdition, ISBN 978-1-59327-144-2.Italian-language edition copyright (C) byApogeo s.r.l. All rights reserved.

  • Questo testo tratto dal volume L'artedell'hacking - seconda edizione, Apogeo2008.

    Il presente file pu essere usatoesclusivamente per finalit di caratterepersonale. Tutti i contenuti sono protettidalla Legge sul diritto d'autore. Nomi emarchi citati nel testo sono generalmentedepositati o registrati dalle rispettivecase produttrici.

    L'edizione cartacea in vendita nellemigliori librerie.

    ~

    Seguici su Twitter @apogeonline

  • Indice dellopera

    Volume 1PrefazioneIntroduzioneCapitolo 1 Lidea dihackingCapitolo 2 Programmazione0x210 Che cos la programmazione?0x220 Pseudocodice0x230 Strutture di controllo0x240 Altri concetti fondamentali diprogrammazione

  • 0x250 Iniziamo a sporcarci le mani0x260 Torniamo alle basi0x270 Segmentazione della memoria0x280 Costruire sulle fondamenta

    Capitolo 3 Exploit0x310 Tecniche di exploit generalizzate0x320 Buffer overflow0x330 Esperimenti con la shell BASH0x340 Overflow in altri segmenti0x350 Stringhe di formato

    Capitolo 4 Strutture direte0x410 Il modello OSI0x420 Socket0x430 I livelli inferiori0x440 Sniffing di rete

  • Riferimenti

    Volume 2PrefazioneIntroduzioneCapitolo 1 Attacchi di rete0x110 DoS (Denial of Service)0x120 Dirottamento TCP/IP0x130 Scansione di porte0x140 Qualche hack in pratica

    Capitolo 2 Shellcode0x210 Assembly e C0x220 Il percorso dello shellcode0x230 Shellcode che avvia una shell0x240 Shellcode per il binding di porte

  • 0x250 Shellcode di connect-back

    Capitolo 3 Contromisure0x310 Contromisure che rilevano gliattacchi0x320 Daemon di sistema0x330 Strumenti del mestiere0x340 File di log0x350 Trascurare lovvio0x360 Camuffamento avanzato0x370 Linfrastruttura completa0x380 Contrabbando del payload0x390 Restrizioni per i buffer0x3a0 Rafforzare le contromisure0x3b0 Stack non eseguibile0x3c0 Spazio nello stack a generazionecasuale

    Capitolo 4 Crittologia

  • 0x410 Teoria dellinformazione0x420 Tempo di esecuzione di unalgoritmo0x430 Cifratura simmetrica0x440 Cifratura asimmetrica0x450 Sistemi di cifratura ibridi0x460 Cracking delle password0x470 Cifratura su reti wireless 802.11b0x480 Attacchi WEP

    Riferimenti

  • Prefazione

    Attenzione Questo libro uno dei duevolumi realizzati a partire dal testo di JonErickson Hacking - The Art of Exploitation(2nd Edition) pubblicato in lingua inglesedalleditore No Starch Press ed edito laprima volta in Italia da Apogeo nel mese difebbraio 2008 con il titolo Lartedellhacking - seconda edizione. Il testooriginale contava 456 pagine nel formatodella collana Guida completa (17 x 24 cm).Larte delhacking volume 1 e 2ripropongono il testo completo, senza taglio modifiche. Gli unici cambiamenti sonostati fatti da un punto di vista tipografico,per adattare il contenuto al tagliotascabile della collana Pocket.

    Capire le tecniche di hacking

  • spesso complesso, perch richiedeconoscenze ampie e approfondite. Moltitesti dedicati allhacking sono oscuri econfusi proprio perch ci sono dellelacune nella formazione di base. Inquesto libro si rende pi accessibile ilmondo dellhacking presentando ilquadro completo delle competenzenecessarie: dalla programmazione alcodice macchina e alla realizzazione diexploit.

    Inoltre il codice sorgente riportatonel libro scaricabile gratuitamenteallindirizzohttp://www.nostarch.com/download/booksrc.zipun utile supporto per la realizzazione diexploit per seguire meglio gli esempi

  • presentati nel testo e fare delle provepratiche lungo il percorso.

    Piano delloperaVolume 1Capitolo 1 Lidea dihackingGli hacker, programmatori creativi:chiarimenti sul nome e sulle originidellhacking.

    Capitolo 2 ProgrammazioneFondamenti della programmazione in C;scrittura delle prime righe di codice;analisi del codice sorgente di tre

  • semplici giochi dazzardo per impararea gestire casualit e permessimultiutente.

    Capitolo 3 ExploitGli exploit, ovvero come sfruttare unafalla di un programma: tecnichegeneralizzate; buffer overflow;esperimenti con la shell BASH,overflow in altri segmenti, stringhe diformato.

    Capitolo 4 Strutture direteIntroduzione alle strutture di rete: ilmodello OSI, i socket e lo sniffing didati.

    Volume 2

  • Capitolo 1 Attacchi di reteGli attachi DoS, dirottamenti TCP/IP,scansione di porte e alcuni esempi sucome sfruttare le vulnerabilit deiprogrammi di rete.

    Capitolo 2 ShellcodeSfruttare lo shellcode per avere uncontrollo assoluto sul programmaattaccato e ampliare cos le potenzialitdegli exploit, oltre a sviluppare capacitcon luso del linguaggio assembly.

    Capitolo 3 ContromisureCome difendersi (cercare di individuaregli attacchi e difendere la vulnerabilitgrazie allazione dei daemon eallanalisi dei file di log) e come

  • aggirare le difese (creare exploit chenon lascino tracce).

    Capitolo 4 CrittologiaCome comunicare in segreto tramitemessaggi cifrati e come decifrare talicomunicazioni: crittografia ecrittoanalisi.

    RingraziamentiDesidero ringraziare Bill Pollock e tuttolo staff di No Starch Press per aver resopossibile la realizzazione di questo libroe per avermi consentito di applicare unalto grado di controllo creativo nelprocesso di produzione. Voglio inoltreringraziare i miei amici Seth Benson eAaron Adams per la rilettura e la

  • correzione delle bozze, Jack Mathesonper laiuto nellorganizzazione deicontenuti, il dott. Seidel per avermantenuto sempre vivo in me linteresseper linformatica, i miei genitori peravermi acquistato il primo CommodoreVIC-20 e la comunit degli hacker per lospirito di innovazione e la creativit chehanno prodotto le tecniche descritte inquesto libro.

  • Introduzione

    Attenzione Nelledizione originale il testoche segue era parte della conclusione. Inquesta edizione, ritenendo le ideeproposte utili a chi si avvicina alletematiche affrontate, si deciso diutilizzarlo per introdurre entrambi ivolumi.

    Lhacking un argomento spessofrainteso, e i media amano enfatizzarnegli aspetti, il che peggiora le cose. Itentativi di cambiare la terminologia nonhanno portato ad alcunch: occorrecambiare la mentalit. Gli hacker sonosemplicemente persone con spirito diinnovazione e conoscenza approfonditadella tecnologia. Non sono

  • necessariamente criminali, anche se,poich il crimine talvolta rende, cisaranno sempre dei criminali anche tragli hacker. Non c nulla di male nellaconoscenza in dote a un hacker,nonostante le sue potenzialiapplicazioni.

    Che piaccia o meno, esistono dellevulnerabilit in software e reti da cuidipende il funzionamento dellinterosistema mondiale. semplicemente unrisultato inevitabile delleccezionalevelocit di sviluppo del software.Spesso il software nuovo riscuotesuccesso anche se presenta dellevulnerabilit. Il successo significadenaro, e questo attrae criminali che

  • imparano a sfruttare tali vulnerabilitper ottenere proventi finanziari.Sembrerebbe una spirale senza fine, mafortunatamente non tutte le persone chetrovano le vulnerabilit nel softwaresono criminali che pensano solo alprofitto. Si tratta per lo pi di hacker,ognuno spinto dalle proprie motivazioni;per alcuni la curiosit, per altri ancora il piacere della sfida, altri sono pagatiper farlo e parecchi sono, in effetti,criminali. Tuttavia la maggior parte diqueste persone non hanno intentimalevoli, ma anzi, spesso aiutano iproduttori a correggere i loro software.Senza gli hacker, le vulnerabilit e glierrori presenti nel softwarerimarrebbero occulti.

  • Sfortunatamente il sistema legislativo lento e piuttosto ignorante riguardo latecnologia. Spesso vengono promulgateleggi draconiane e sono comminatesentenze eccessive per spaventare lepersone. Questa una tattica infantile: iltentativo di scoraggiare gli hackerdallesplorare e cercare vulnerabilitnon porter a nulla. Convincere tutti cheil re indossa nuovi abiti non cambia larealt che il re nudo. Le vulnerabilitnascoste rimangono l dove si trovano,in attesa che una persona pi malevoladi un hacker normale le scopra.

    Il pericolo delle vulnerabilitpresenti nel software che possonoessere sfruttate per qualunque fine. I

  • worm diffusi su Internet sonorelativamente benigni, rispetto ai tantotemuti scenari terroristici. Tentare dilimitare gli hacker con la legge puaumentare le probabilit che si avverinoi peggiori scenari, perch si lasciano pivulnerabilit a disposizione di chi nonha rispetto per la legge e vuole davverocausare danni.

    Alcuni potrebbero sostenere che senon esistessero gli hacker non visarebbe motivo di porre rimedio allevulnerabilit occulte. un punto divista, ma personalmente preferisco ilprogresso alla stagnazione. Gli hackergiocano un ruolo molto importante nellacoevoluzione della tecnologia. Senza di

  • essi non vi sarebbe grande impulso almiglioramento della sicurezzainformatica. Inoltre, finch saranno postedomande sul perch e il come, glihacker esisteranno sempre. Un mondosenza hacker sarebbe un mondo privo dicuriosit e spirito di innovazione.

    Lintento di questo libro quello dispiegare alcune tecniche di base perhacking e forse anche di dare unideadello spirito che lo pervade. Latecnologia sempre in mutamento edespansione, perci ci saranno semprenuovi hack. Ci saranno sempre nuovevulnerabilit nel software, ambiguitnelle specifiche di protocollo e unamiriade di altri problemi.

  • Le conoscenze fornite in questo librosono soltanto un punto di partenza.Spetta a voi ampliarle continuando ariflettere sul funzionamento delle cose,sulle possibilit esistenti e pensando adaspetti di cui gli sviluppatori softwarenon hanno tenuto conto. Spetta a voitrarre il meglio da queste scoperte eapplicare le nuove conoscenze nel modoche riterrete pi opportuno.

    Linformazione in s non uncrimine.

  • Capitolo 1Lidea di hacking

    Lidea di hacking potrebbe evocareimmagini di vandalismo elettronico,spionaggio, capelli tinti e piercing. Lamaggior parte delle persone associalhacking alla violazione della legge esuppone che chiunque eserciti taleattivit sia un criminale. In effetti cisono certamente persone che usanotecniche di hacking per violare la legge,ma questo non hacking; anzi, hackingsignifica seguire la legge, non violarla.Lessenza dellhacking sta nel trovareapplicazioni inattese o neglette di leggio propriet di una data situazione, che

  • consentano di risolvere un problema,qualunque esso sia, in modi nuovi efantasiosi.

    Il seguente problema matematicoillustra lessenza dellhacking:

    Usate ciascuno dei numeri 1, 3, 4 e 6esattamente una volta con i quattro operatorimatematici di base (addizione, sottrazione,moltiplicazione e divisione) per ottenerecome risultato 24. Ogni numero deve essereusato una e una sola volta, e potete definirelordine delle operazioni; per esempio, 3 *(4 + 6) + 1 = 31 valido, ma errato, perchil risultato non 24.

    Le regole di questo problema sonoben definite e semplici, ma la rispostane elude molte. Come la soluzione a

  • questo problema, le soluzioni trovatemediante hacking seguono le regole delsistema, ma le usano in modicontrointuitivi. questo che conferisceagli hacker il loro vantaggio,consentendo loro di risolvere problemiin modi inimmaginabili per chi si limitaal pensiero e alle metodologieconvenzionali.

    Fin dalle origini dei computer glihacker hanno sempre risolto problemi inmaniera creativa. Verso la fine deglianni 50 il club di appassionati dimodellini ferroviari del MIT ricevetteuna donazione di varie apparecchiature,per lo pi vecchi sistemi telefonici. Imembri del club usarono tali apparecchi

  • per mettere insieme un sistemacomplesso che consentiva a pioperatori di controllare diverse partidella pista collegandosi telefonicamentecon la sezione appropriata. Essichiamarono hacking questa nuova efantasiosa applicazione diapparecchiature telefoniche, e oggi molticonsiderano i membri di quel club iprimi hacker. Il gruppo pass aprogrammare schede perforate e nastriper i primi computer come lIBM 704 eil TX-0. Mentre altri si accontentavanodi scrivere programmi che risolvessero iproblemi, i primi hacker eranoossessionati dallidea di scrivereprogrammi che risolvessero i problemibene. Un nuovo programma in grado di

  • ottenere lo stesso risultato di unprogramma esistente, ma utilizzandomeno schede perforate, era consideratomigliore, anche se assolveva lo stessocompito. La differenza chiave stava nelmodo in cui il programma otteneva irisultati: con eleganza.

    La capacit di ridurre il numero dischede perforate necessarie per unprogramma rivelava una padronanza delcomputer che evidenziava un carattereartistico. Un tavolo intarsiato pusostenere un vaso esattamente come unacassetta della frutta, ma lo fa in manieramolto pi elegante e raffinata. I primihacker dimostrarono che i problemitecnici possono avere soluzioni

  • artistiche e cos trasformarono laprogrammazione da una mera attivittecnica in una forma darte.

    Come molte altre forme darte,lhacking fu unattivit spessoincompresa. I pochi che la capironoformarono una subcultura informale cherimase intensamente concentratasullapprendimento e la padronanzadella propria arte. Essi ritenevano che leinformazioni dovessero essere libere equalsiasi intralcio sulla via della libertdoveva essere eluso. Tra gli ostacoli vierano le persone dotate di autorit, laburocrazia dei corsi scolastici e ladiscriminazione. In un mare di studentiche pensavano soltanto a guadagnarsi il

  • diploma, questo gruppo clandestino dihacker ignor gli obiettivi convenzionaliper perseguire la conoscenza in s. Talespinta ad apprendere ed esplorarecontinuamente vie nuove trasceseperfino i limiti convenzionali tracciatidalla discriminazione, come apparveevidente quando il club di appassionatidi modellini ferroviari del MIT accettladesione di un ragazzo di 12 anni,Peter Deutsch, nel momento in cui eglimise in luce la sua conoscenza del TX-0e il suo desiderio di apprendere. Et,razza, genere, aspetto, titolo accademicoe stato sociale non erano criterifondamentali per giudicare il valore diun altro, e questo non per desiderio dieguaglianza, ma per volont di far

  • progredire larte emergentedellhacking.

    I primi hacker trovavano splendoreed eleganza nella matematica enellelettronica, tradizionalmenteconsiderate aride. Vedevano laprogrammazione come una forma diespressione artistica e il computer comeuno strumento di tale arte. Il lorodesiderio di sezionare e comprenderenon intendeva demistificare esperimentiartistici, era semplicemente un modo perraggiungere un maggiore apprezzamentodi questi ultimi. Questi valori orientatialla conoscenza fondarono ci che fu poichiamata letica hacker:lapprezzamento della logica come

  • forma darte e la promozione del flussolibero delle informazioni, sorvolando iconfini e i vincoli tradizionali per ilsemplice scopo di comprendere meglioil mondo. Non si tratta di una tendenzaculturale nuova: i Pitagorici dellanticaGrecia avevano unetica e unasubcultura simili, bench nondisponessero di computer. Essivedevano la bellezza nella matematica escoprirono molti concetti fondamentalinella geometria. Quella sete diconoscenza e i suoi benefici effetticollaterali tracciano un filo storico checollega i Pitagorici ad Ada Lovelace, adAlan Turing, fino agli hacker del club diappassionati di modellini ferroviari delMIT. Hacker moderni come Richard

  • Stallman e Steve Wozniak hanno ripresoleredit dellhacking, portando a noimoderni sistemi operativi, linguaggi diprogrammazione, personal computer emolte altre tecnologie che usiamo ognigiorno.

    Come distinguere gli hacker buoniche ci portano le meraviglie delprogresso tecnologico dagli hackermaligni che rubano i numeri delle cartedi credito? Per tale scopo fu coniato iltermine cracker. Alla stampa si disseche i cracker erano i cattivi, mentre glihacker erano i buoni. Gli hackerrispettavano i principi delletica hacker,mentre i cracker erano interessatisoltanto a violare la legge e a fare soldi

  • in fretta. I cracker erano consideratimolto meno talentuosi dei migliorihacker, poich si limitavano a usarestrumenti e script scritti da questi, senzacomprendere come funzionassero.Cracker doveva essere il termine usatoper indicare chiunque facesse qualcosadi eticamente non corretto con uncomputer: piratare software odeturpare siti web, e tutto ci, cosapeggiore, senza comprendere fino infondo ci che facevano. Oggi, tuttavia,pochissimi usano questo termine.

    Linsuccesso del termine cracker sideve forse alletimologia confusa:cracker in origine indicava chi violava idiritti del software (in gergo crackava il

  • software) ed eseguiva il reverseengineering di schemi di protezionedalla copia. Lattuale scarsa popolaritdel termine potrebbe anche esseredovuta semplicemente allambiguitdelle due nuove definizioni, cheindicano persone dedite ad attivitillecite con il computer, o persone chesono in realt hacker di scarsa capacit.Pochi giornalisti della stampa tecnicaaccettano di usare termini con cui i lorolettori non hanno familiarit. Percontrasto, moltissime personeconoscono lalone di mistero e laltolivello di capacit associati al terminehacker, perci, per un giornalista, ladecisione di usare il termine hacker lapi facile. Analogamente, alcuni talvolta

  • utilizzano il termine script kiddie perindicare i cracker, ma non ha lo stessofascino tenebroso di hacker. Ci sarsempre qualcuno intenzionato asostenere che vi una netta separazionetra hacker e cracker, ma personalmenteritengo che chiunque abbia lo spirito dahacker sia un hacker, nonostante tutte leleggi che possa eventualmente violare.

    Le leggi attuali che pongono vincolisulla crittografia e la ricerca nel campocontribuiscono a rendere ancora menodistinta la linea di confine tra hacker ecracker. Nel 2001 il professor EdwardFelten e il suo team di ricerca dellaPrinceton University stavano perpubblicare un articolo che discuteva i

  • punti deboli di vari schemi di watermarko filigrana digitale. Larticolorappresentava una risposta a una sfidaposta dallSDMI (Secure Digital MusicInitiative) nel cosiddetto SDMI PublicChallenge, che aveva incoraggiato ilpubblico a tentare di violare tali schemidi watermark. Tuttavia, prima che Feltene il suo team pubblicassero larticolo,furono minacciati dallSDMI Foundatione dalla RIAA (Recording IndustryAssociation of America). Il DCMA(Digital Millennium Copyright Act) del1998 rende illegale negli Stati Unitidiscutere o rendere pubbliche tecnologieche potrebbero essere usate per violarecontrolli di carattere industriale. Lastessa legge fu usata contro Dmitry

  • Sklyarov, un programmatore e hackerrusso che aveva scritto un software peraggirare un meccanismo di crittografiatroppo semplice inserito nel software diAdobe, presentando i suoi risultati a unconvegno di hacker tenutosi negli StatiUniti. LFBI fece irruzione e lo arrest,avviando una lunghissima battaglialegale. A termini di legge, lacomplessit dei controlli industriali nonconta: sarebbe tecnicamente illegaleeffettuare il reverse engineering operfino discutere un semplice giococome Pig Latin, se fosse usato comecontrollo industriale. Chi sono glihacker e chi i cracker, ora? Quando leleggi sembrano interferire con la libertdi espressione, le brave persone che

  • esprimono il loro pensiero diventanoimprovvisamente cattive? Personalmentecredo che lo spirito hacker trascenda leleggi del governo, invece di esseredefinito da esse.

    La fisica nucleare e la biochimicapossono essere usate per uccidere, etuttavia ci forniscono importantiprogressi scientifici e una medicinamoderna. La tecnologia in s non buona n cattiva: il giudizio morale valeper lapplicazione della conoscenza.Anche se lo volessimo, non potremmosopprimere la conoscenza di cometrasformare la materia in energia, ointerrompere il continuo progressotecnologico della societ. Allo stesso

  • modo, lo spirito hacker non pu maiessere fermato, n classificato osezionato facilmente. Gli hackerspingeranno sempre pi in l i limitidella conoscenza e del comportamentoaccettabile, obbligandoci a portaresempre pi avanti il nostro percorso diesplorazione.

    Parte di questo impulso genera unavantaggiosa coevoluzione dellasicurezza ottenuta attraverso lacompetizione tra hacker che attaccano ehacker che difendono. Esattamente comela veloce gazzella che si evolutaadattandosi per la continua minaccia delghepardo, e come il ghepardo che diventato ancora pi veloce cacciando

  • la gazzella, la competizione tra hackeroffre agli utenti di computer unasicurezza pi elevata, insieme a tecnichedi attacco pi complesse e sofisticate.Lintroduzione e i progressi dei sistemidi rilevamento delle intrusioni (IDS,Intrusion Detection System) un tipicoesempio di questo processo coevolutivo.Gli hacker impegnati in difesa creanosistemi IDS da aggiungere ai loroarsenali, mentre quelli in attaccosviluppano tecniche di evasione daquesti sistemi, che alla fine fannonascere prodotti IDS migliori e pi forti.Il risultato netto di questa interazione positivo, perch produce persone piabili, maggiore sicurezza, software pistabile, tecniche inventive di risoluzione

  • dei problemi, e perfino una nuovaeconomia.

    Questo libro stato scritto conlintento di insegnare lo spirito autenticodellhacking. Esamineremo varietecniche di hacker, passate e presenti,sezionandole per apprendere come eperch funzionano. In questo modopotrete acquisire una conoscenza praticae un apprezzamento in concretodellhacking, che potrebbe spingervi amigliorare tecniche esistenti o perfino ainventarne di nuove. Speriamo chequesto libro stimoler lhacker curiosoche in voi e vi porter a dare il vostrocontributo allarte dellhacking, aprescindere dal lato della barricata che

  • sceglierete.

  • Capitolo 2Programmazione

    Hacker un termine usato perindicare sia chi scrive codice, sia chirealizza exploit approfittando dellevulnerabilit nel codice. Bench questidue gruppi di hacker abbiano scopidiversi, entrambi utilizzano tecnichesimili per la risoluzione dei problemi oproblem solving. Poich la conoscenzadella programmazione utile per chirealizza gli exploit, e la conoscenzadelle tecniche per realizzare exploit utile a chi programma, molti hacker sidedicano a entrambe le attivit. Sitrovano interessanti hack sia nelle

  • tecniche utilizzate per scrivere codiceelegante, sia in quelle usate perbucare i programmi. Hackingsignifica semplicemente trovare unasoluzione intelligente e controintuitiva aun problema.

    Gli hack utilizzati negli exploit diprogrammi utilizzano solitamente leregole del computer per aggirare lasicurezza in modi che risultano del tuttoimprevisti. Quelli adottati nellaprogrammazione sono simili nel fatto diimpiegare le regole del computer inmodi nuovi e ricchi di inventiva, ma inquesto caso con lo scopo finale dimigliorare lefficienza o realizzarecodice sorgente meno pesante, non

  • necessariamente di compromettere lasicurezza. In realt, per svolgere undeterminato compito si possono scrivereinfiniti programmi, ma la maggior partedi queste soluzioni si rivela inutilmentemassiccia, complessa e approssimativa.Sono poche le soluzioni di dimensionicontenute, efficienti e pulite; iprogrammi che vantano queste qualitsono detti eleganti, e le soluzionibrillanti e ricche di inventiva chepermettono di raggiungere questo livellodi efficienza sono chiamate hack. Glihacker di entrambe le sponde del mondodella programmazione apprezzano sia labellezza di un codice elegante, sialingegno messo in luce dagli hack pibrillanti.

  • Nel mondo degli affari si d piimportanza alla capacit di sfornarecodice funzionale che a quella diottenere eleganza e hack brillanti.

    Data lincredibile crescitaesponenziale in termini di potenza dicalcolo e quantit di memoria,impiegare qualche ora in pi per creareuna porzione di codice leggermente piveloce e pi efficiente nelluso dellamemoria non ha molto senso, da unpunto di vista commerciale, quando sihanno a disposizione i modernicomputer che raggiungono frequenze dielaborazione dellordine dei gigahertz equantit di memoria dellordine deigigabyte. Inoltre, mentre le

  • ottimizzazioni a livello di tempi ememoria possono essere notate soltantodagli utenti pi avanzati, una nuovafunzionalit pu essere oggetto di unacampagna di marketing. Quando ci checonta davvero il denaro, passare iltempo a cercare soluzioni brillanti perottimizzare i programmi non ha senso.

    Chi apprezza davvero leleganza diun programma sono solo gli hacker:appassionati di informatica il cui scopoultimo non il profitto, ma spremerefino allimpossibile le potenzialit delloro vecchio Commodore 64, creatori diexploit che hanno la necessit direalizzare porzioni di codice minuscolee sorprendenti per potersi infilare in

  • sottili crepe della sicurezza, e chiunquesia in grado di apprezzare la ricerca e lasfida di trovare la soluzione migliorepossibile. Queste persone siappassionano alla programmazione eapprezzano davvero la bellezza di uncodice elegante o la creativit di un hackintelligente. Poich la conoscenza deimeccanismi della programmazione unprerequisito indispensabile per potercomprendere come realizzare exploit diprogrammi, la programmazione stessadiventa un punto di partenza naturale.

    0x210 Che cos laprogrammazione?

  • La programmazione un concettomolto naturale e intuitivo. Unprogramma non altro che una serie diistruzioni scritte in un determinatolinguaggio. I programmi si trovanoovunque, e perfino chi odia la tecnologiali utilizza quotidianamente. Leindicazioni stradali, le ricette di cucina,le partite di calcio, e anche il DNA sonotutti tipi di programmi. Un programmaper dare delle indicazioni stradalipotrebbe avere un aspetto simile aquesto:

    Parti dalla Main Street andando verso est. Continua sulla Main Street finch non vedi una chiesa sulla tua destra. Se la strada bloccata da lavori, gira a destra sulla Quindicesima, gira a sinistra sulla Pine Street, quindi

  • svolta a destra sulla Sedicesima. Altrimenti, puoi semplicemente proseguire e svoltare sulla Sedicesima. Continua sulla Sedicesima e svolta a sinistra su Destination Road. Percorri Destination Road per 5 chilometri e vedrai la casa sulla destra. L'indirizzo Destination Road 743.

    Chiunque conosca litaliano pucomprendere e seguire queste istruzioni,dato che sono in italiano. Non si pudavvero dire che siano uno sfoggio dieloquenza, ma ognuna di esse chiara esemplice da capire, almeno per chiconosce litaliano.

    Un computer per non in grado dicapire litaliano o unaltra lingua; pucapire solo il linguaggio macchina.

  • Perch un computer possa svolgere uncompito qualsiasi, le istruzioni devonoessere scritte nel suo linguaggio. Illinguaggio macchina, per, oscuro edifficile da usare: formato da semplicibit e byte, e varia da architettura adarchitettura. Per scrivere un programmain linguaggio macchina per unprocessore Intel x86, si dovrebbeindividuare il valore associato aciascuna istruzione, conoscere come levarie istruzioni interagiscono, e averecoscienza di una miriade di dettagli dibasso livello. Programmare in questomodo complicato e richiede grandeprecisione, non davvero intuitivo.

    Per poter superare la complessit

  • dello scrivere il codice macchinaoccorre un traduttore. Un assemblerrappresenta una forma di traduttore inlinguaggio macchina un programmache traduce il linguaggio assembly incodice comprensibile dalla macchina. Illinguaggio assembly meno cripticodel linguaggio macchina, perch utilizzadei nomi per le diverse istruzioni evariabili, anzich dei numeri. Tuttavia,il linguaggio assembly ancora lontanodallessere un linguaggio intuitivo. Inomi delle istruzioni sono moltoesoterici, e il linguaggio specificodelle varie architetture. Come illinguaggio macchina per i processoriIntel x86 diverso dal linguaggiomacchina per i processori Sparc,

  • lassembly x86 diverso dallassemblySparc. Qualsiasi programma scrittousando il linguaggio assembly per unadeterminata architettura di processorenon funzioner su unarchitetturadifferente. Se un programma scritto inlinguaggio assembly per x86, per poterfunzionare su unarchitettura Sparcdovr essere riscritto. Inoltre, per poterescrivere un programma efficiente inassembly, rimane necessario conosceremolti dettagli di basso livellodellarchitettura di processorecoinvolta.

    Questo tipo di problemi pu esserealleviato da un altro tipo di traduttore,chiamato compilatore, che converte un

  • linguaggio di alto livello in linguaggiomacchina. I linguaggi di alto livello sonomolto pi intuitivi rispetto al linguaggioassembly e possono essere tradotti inmolti tipi diversi di linguaggio macchinain base alle diverse architetture diprocessori. Ci significa che se unprogramma scritto in un linguaggio dialto livello sar sufficiente scriverlo unasola volta; la stessa porzione di codicepu essere compilata in linguaggiomacchina per svariate architetture. Il C,il C++ e il Fortran sono linguaggi di altolivello. Un programma realizzato in unlinguaggio di alto livello molto pileggibile e simile alla lingua umana diuno realizzato in assembly o inlinguaggio macchina, ma deve pur

  • sempre rispettare regole precise sulmodo in cui le istruzioni devono esserescritte, altrimenti il compilatore non sarin grado di comprenderlo.

    0x220 Pseudocodice

    I programmatori hanno adisposizione unulteriore forma dilinguaggio di programmazione chiamatapseudocodice. Lo pseudocodice semplicemente lingua disposta con unastruttura generale simile a quella di unlinguaggio di alto livello. Non vienecompreso dai compilatori, dagliassembler o da qualsiasi computer, marappresenta per un programmatore unmodo utile per disporre le istruzioni. Lo

  • pseudocodice non ben definito; ineffetti, la maggior parte deiprogrammatori scrive il propriopseudocodice in maniera leggermentediversa. una specie di anello mancantetra la lingua umana e i linguaggi diprogrammazione di alto livello come ilC. Lo pseudocodice pu rappresentareuna eccellente introduzione ad alcunicomuni concetti di programmazione.

    0x230 Strutture di controllo

    Senza le strutture di controllo, unprogramma sarebbe semplicemente unaserie di istruzioni eseguite in ordinesequenziale. Questo pu esseresufficiente per programmi molto

  • semplici, ma la maggior parte deiprogrammi, come le indicazioni stradalidellesempio riportato in precedenza,non sono cos semplici. Le indicazionistradali contenevano dichiarazioni comeContinua sulla Main Street finch nonvedi una chiesa sulla tua destra e Se lastrada bloccata dai lavori... Questeistruzioni sono note come strutture dicontrollo e modificano il flussodellesecuzione del programma da unsemplice ordine sequenziale a qualcosadi pi complesso e utile.

    0x231 If-Then-Else

    Nel caso delle indicazioni stradali,sulla Main Street potrebbero essere in

  • corso dei lavori. In questo caso, occorreuno speciale gruppo di istruzioni checonsenta di affrontare la situazione. Senon ci fosse alcuna interruzione,dovrebbe essere seguito il gruppo diistruzioni originario. Questi tipi disituazioni particolari possono essereaffrontati allinterno di un programmacon una delle pi naturali strutture dicontrollo: la struttura if-then-else. Ingenerale, il suo aspetto simile aquesto:

    If (condizione) then{ Gruppo di istruzioni da eseguire se la condizione soddisfatta;}Else{ Gruppo di istruzioni da eseguire

  • se la condizione non soddisfatta;}

    In questo libro si utilizza unopseudocodice simile al C, per cui ogniistruzione si chiuder con un punto evirgola, e i gruppi di istruzioni sarannocaratterizzati da parentesi graffe erientri. Lo pseudocodice che descrive lastruttura if-then-else per le indicazionistradali potrebbe avere un aspetto similea questo:

    Guida lungo la Main Street;If (la strada bloccata){ Svolta a destra sulla Quindicesima; Svolta a sinistra su Pine Street; Svolta a destra sulla Sedicesima;}Else

  • { Svolta a destra sulla Sedicesima;}

    Ciascuna istruzione si trova su unapropria riga e i vari gruppi di istruzionicondizionali sono delimitati da parentesigraffe e rientrati per una miglioreleggibilit. Nel linguaggio C e in moltialtri linguaggi di programmazione, laparola chiave then implicita, equindi viene tralasciata, come nelcodice precedente.

    Ovviamente, altri linguaggirichiedono la presenza della parolachiave then nella propria sintassi, peresempio il BASIC, il Fortran e anche ilPascal. Questi tipi di differenze

  • sintattiche nei linguaggi diprogrammazione sono solo superficiali;la struttura sottostante rimane lamedesima. Una volta compresi i concettiche questi linguaggi cercano ditrasmettere, imparare le differenzesintattiche diventa un compitoabbastanza banale. Poich nel seguitoverr utilizzato il linguaggio C, lopseudocodice impiegato in questo libroseguir una sintassi simile al C, maricordate che le forme assuntepotrebbero essere molto diverse.

    Unaltra regola comune della sintassilegata al C prevede che, quando ungruppo di istruzioni rinchiuse traparentesi graffe costituito da una sola

  • istruzione, le parentesi possono esseretralasciate. Per garantire una miglioreleggibilit, sempre consigliabilemantenere i rientri, ma ci non sintatticamente. Le indicazioni stradalipresentate in precedenza possono essereriscritte rispettando questa regola fino aottenere una porzione di pseudocodiceequivalente:

    Guida lungo la Main Street;If (la strada bloccata){ Svolta a destra sulla Quindicesima; Svolta a sinistra su Pine Street; Svolta a destra sulla Sedicesima;}Else Svolta a destra sulla Sedicesima;Questa regola relativa ai gruppi di istruzioni valida per tutte le

  • strutture di controllo menzionate in questo volume, e anch'essa pu esseredescritta mediante pseudocodice.If (un gruppo di istruzioni costituito da una sola istruzione) L'uso delle parentesi graffe per raggruppare le istruzioni facoltativo;Else{ L'uso delle parentesi graffe necessario; Dato che deve esserci un modo logico per raggruppare tali istruzioni;}

    Anche la descrizione di unadeterminata sintassi pu essere vistacome una specie di sempliceprogramma. Ci sono delle variazioni diif-thenelse, come le istruzioniselect/case, ma la logica di base

  • sempre la medesima: Se si verificaquesta cosa, fai questo, altrimenti faiquestaltro (che potrebbe prevedereulteriori istruzioni if-then).

    0x232 Cicli while/until

    Un altro concetto elementare dellaprogrammazione costituito dallastruttura di controllo while cherappresenta un tipo di ciclo. Unprogrammatore potrebbe volere eseguireuna serie di istruzioni per pi di unavolta. Un programma pu svolgerequesto tipo di funzione usando i cicli,ma ha bisogno di un gruppo di istruzioniche indicano quando interrompere ilciclo, altrimenti continuerebbe

  • allinfinito. Un ciclo while dice dieseguire ciclicamente un gruppo diistruzioni fintanto che (while) unacondizione vera. Un sempliceprogramma per un topo affamatopotrebbe avere un aspetto simile aquesto:

    While (hai fame){ Trova del cibo; Mangia;}

    Il gruppo di due istruzioni che seguelistruzione while sar ripetuto fintantoche (while) il topo ha fame. La quantitdi cibo che il topo in grado di trovarea ogni passaggio potrebbe variare dauna piccola briciola a unintera fetta di

  • pane. In maniera simile, il numero divolte che il gruppo di istruzionidellistruzione while viene eseguitocambia in relazione alla quantit di cibotrovata.

    Una variante del ciclo while rappresentata dal ciclo until, un tipo disintassi che si pu trovare nel linguaggioPerl (il C non la usa). Un ciclo until semplicemente un ciclo while conlistruzione condizionale invertita. Ilprogramma per il topo potrebbe essereriscritto con un ciclo until in questomodo:

    Until (non hai fame){ Trova del cibo; Mangia;

  • }Logicamente, ogni istruzione del tipountil pu essere tradotta in un ciclowhile. Le indicazioni stradalicontenevano listruzione Continua sullaMain Street finch non vedi una chiesasulla tua destra. Listruzione pu esseretrasformata con semplicit in un normaleciclo while invertendo la condizione.

    While (non c' una chiesa sulla tua destra) Guida lungo la Main Street;

    0x233 Cicli for

    Il ciclo for unaltra struttura dicontrollo. In genere essa viene utilizzata

  • quando un programmatore vuolesvolgere un ciclo per un determinatonumero di iterazioni. LindicazionePercorri Destination Road per 5chilometri potrebbe essere trasformatain un ciclo for come il seguente:

    For (5 volte) Guida per 1 chilometro;

    In verit, un ciclo for non che unciclo while con un contatore. La stessaistruzione pu essere scritta in questomodo:

    Porta il contatore a 0;While (il contatore inferiore a 5){ Guida per 1 chilometro; Aggiungi 1 al contatore;}

  • La sintassi dello pseudocodice C perun ciclo for rende il tutto ancora pievidente:

    For (i=0; i

  • costituisce un modo abbreviato per direAggiungi 1 al contatore i.

    Usando tutte le strutture di controllo,le indicazioni stradali date inprecedenza possono essere trasformatein pseudo codice di tipo C fino aottenere quanto segue:

    Inizia andando verso est sulla Main Street;While (non c' una chiesa sulla destra) Guida lungo la Main Street;If (la strada bloccata){ Svolta a destra sulla Quindicesima; Svolta a sinistra su Pine Street; Svolta a destra sulla Sedicesima;}Else Svolta a destra sulla Sedicesimat; Svolta a sinistra su Destination

  • Road;For (i=0; i
  • dovrebbe avere un aspetto davverosimile al codice C.

    0x241 Variabili

    Il contatore usato nel ciclo for ineffetti un tipo di variabile. Unavariabile pu essere vistasemplicemente come un oggetto checontiene dati che possono cambiare daqui il nome. Ci sono anche variabili chenon cambiano, e che vengonoopportunamente chiamate costanti.Tornando allesempio dellautomobile,la sua velocit potrebbe essere unavariabile, mentre il suo colore sarebbeuna costante. Nello pseudocodice, levariabili sono semplicemente concetti

  • astratti, ma in C (e in molti altrilinguaggi), prima di poterle utilizzare necessario dichiararle e attribuire loroun tipo. Questo perch un programma Calla fine dovr essere compilato in uneseguibile. Come una ricetta di cucinache elenca tutti gli ingredienti necessariprima di dare le istruzioni, ladichiarazione delle variabili consente disvolgere una serie di preparativi primadi entrare nel cuore del programma. Indefinitiva, tutte le variabili vengonosalvate in qualche porzione dellamemoria, e la loro dichiarazioneconsente al compilatore di organizzarequesta memoria in maniera piefficiente. Alla fine, comunque,nonostante tutte le dichiarazioni del tipo

  • di variabile, tutto si riduce un discorsodi memoria.

    Nel linguaggio C, a ciascunavariabile viene associato un tipo chedescrive il genere di informazioni che sidesiderano salvare nella variabilestessa. Alcuni di tipi pi comuni sonoint (integer: valori interi), float(floating point: valori a virgola mobile)e char (valori a carattere singolo). Levariabili vengono dichiaratesemplicemente anteponendo questeparole chiave ai loro nomi, comenellesempio seguente.

    int a, b;float k;char z;

  • Ora le variabili a e b sono definitecome interi, k pu accettare valori avirgola mobile, (come 3,14), e ci siaspetta che z contenga un valore di uncarattere, come A o w. Alle variabili puessere assegnato un valore al momentodella dichiarazione o in qualsiasimomento successivo, con loperatore =.

    int a = 13, b;float k;char z = 'A';

    k = 3.14;z = 'w';b = a + 5;

    Una volta eseguite queste istruzioni,la variabile a conterr il valore 13, kconterr il numero 3,14, z conterr ilcarattere w e b il valore 18, dato che 13

  • pi 5 fa 18. Le variabili sonosemplicemente un modo per ricordaredei valori; nel linguaggio C, per necessario dichiarare prima il tipo diciascuna variabile.

    0x242 Operatori aritmetici

    Listruzione b = a + 7 unesempio di un semplice operatorearitmetico. Nel linguaggio C i simboliseguenti vengono utilizzati per diverseoperazioni aritmetiche.

    Le prime quattro operazionidovrebbero avere un aspetto familiare.La divisione modulo potrebbe apparirecome un concetto nuovo, ma si tratta

  • solo di prendere il resto dopo unadivisione. Se a 13, allora 13 diviso 5fa 2, con il resto di 3, che significa chea % 5 = 3. Ancora, poich levariabili a e b sono degli interi,listruzione b = a / 5 avr comerisultato che b conterr il valore 2, datoche questa la parte intera del risultato.Per contenere il pi corretto valore di2,6 risultante dalloperazione, devonoessere usate le variabili a virgolamobile.

    Operazione Simbolo EsempioAddizione + b = a + 5Sottrazione - b = a - 5

  • Moltiplicazione * b = a * 5Divisione / b = a / 5

    Divisione modulo % b = a %5

    Per fare in modo che un programmausi questi concetti, dovete parlare la sualingua. Il linguaggio C mette adisposizione anche molte formeabbreviate per queste operazioniaritmetiche. Una di esse stata vista inprecedenza e viene comunemente usatanei cicli.

    Espressionecompleta

    Formaabbreviata Spiegazione

    Aggiunge 1

  • i = i + 1 i++ o ++i allavariabile.

    i = i - 1 i-- o --iSottrae 1dallavariabile.

    Queste espressioni abbreviatepossono essere combinate con altreoperazioni aritmetiche per produrreespressioni pi complesse. Qui diventaevidente la differenza tra i++ e ++i. Laprima espressione significa Incrementadi 1 il valore di i dopo avere calcolatoloperazione aritmetica, mentre laseconda espressione significaIncrementa di 1 il valore di i prima dicalcolare loperazione aritmetica.Lesempio seguente dovrebbe chiarire.

  • int a, b;a = 5;b = a++ * 6;

    Alla fine di questo gruppo diistruzioni, b conterr 30 e a conterr 6,dato che la forma abbreviata di b =a++ * 6; equivalente alle seguentiistruzioni:

    b = a * 6;a = a + 1;

    Tuttavia, se viene utilizzatalistruzione b = ++a * 6; lordinedella somma in a cambia, portandocome risultato il seguente gruppo diistruzioni equivalenti:

    a = a + 1;b = a * 6;

  • Poich lordine cambiato, in questocaso b conterr 36 e a conterr ancora6.

    Molto spesso nei programmi levariabili devono essere modificate sulposto. Per esempio, potrebbe esserenecessario aggiungere a una variabile unvalore arbitrario, come 12, e salvaresubito il risultato nella stessa variabile(per esempio, i = i + 12). unasituazione che si verifica abbastanzacomunemente, e anche per questi casiesiste una forma abbreviata.

    Espressionecompleta

    Formaabbreviata Spiegazione

  • i = i + 12 i+=12 Aggiunge unvalore allavariabile.

    i = i - 12 i-=12Sottrae unvalore dallavariabile.

    i = i * 12 i*=12Moltiplica unvalore per lavariabile

    i = i / 12 i/=12Divide unvalore per lavariabile.

    0x243 Operatori diconfronto

  • Le variabili sono utilizzate difrequente nelle istruzioni condizionalidelle strutture di controllo descritte inprecedenza. Queste istruzionicondizionali sono basate su una sorta diconfronto. Nel linguaggio C, questioperatori di confronto utilizzano unasintassi abbreviata che abbastanzacomune in molti linguaggi diprogrammazione.

    Condizione Simbolo EsempioMinore di < (a < b)Maggiore di > (a > b)Minore ouguale a

  • Maggiore ouguale a

    >= (a >= b)

    Uguale a == (a == b)Non uguale a != (a != b)

    La maggior parte di questi operatori facile da capire; notate comunquecome la forma abbreviata per uguale aimpieghi un doppio segno di uguale.Questa una distinzione importante,dato che il doppio segno di uguale vieneutilizzato per verificare lequivalenza,mentre il singolo segno di uguale vieneimpiegato per assegnare un valore a unadeterminata variabile. Listruzione a =7 significa Metti il valore 7 nellavariabile a, mentre a == 7 significaVerifica se la variabile a uguale a 7

  • (alcuni linguaggi di programmazione,come il Pascal, usano la notazione :=per lassegnamento delle variabili, inmodo da eliminare la confusionevisiva). Ancora, notate come un puntoesclamativo in genere abbia ilsignificato di negazione. Questosimbolo pu essere utilizzato da soloper invertire una qualsiasi espressione.

    !(a < b) equivalente a (a >= b)

    Questi operatori di confrontopossono anche essere concatenatiusando la forma abbreviata per OR eAND.

    Operatore

  • logico Simbolo Esempio

    OR || ((a < b) || (a< c))

    AND && ((a < b) && !(a < c))

    Listruzione di esempio formata dalledue condizioni legate dallOR logicodar come risultato vero se a minoredi b, OR se a minore di c.Similmente, listruzione di esempioformata dai due confronti legati conlAND logico dar il vero se a minoredi b AND a non minore di c. Questeistruzioni dovrebbero essereraggruppate tra parentesi e possonocontenere numerose varianti.

  • Molte cose possono essere ridotte avariabili, operatori di confronto estrutture di controllo. Tornandoallesempio del topo in cerca di cibo, lafame pu essere tradotta in una variabilebooleana vero/falso. Naturalmente, 1significa vero e 0 falso.

    While (affamato == 1){ Trova del cibo; Mangia il cibo;}

    Ecco unaltra forma abbreviata usatamolto spesso da hacker eprogrammatori. Il C in realt nonpresenta operatori booleani, per cuiqualsiasi valore diverso da zero vieneconsiderato vero, e una dichiarazione

  • viene considerata falsa se contiene 0. Ineffetti, gli operatori di confrontorestituiranno un valore 1 se il confronto vero e un valore 0 in caso contrario.Controllando se la varabile affamato uguale a 1 si otterr 1 se affamato uguale a 1 e 0 se affamato uguale a0. Poich il programma usa solo questidue casi, loperatore di controllo puessere del tutto escluso.

    While (affamato){ Trova del cibo; Mangia il cibo;}

    Un programma per topi pi evoluto econ pi input illustra come sia possibilecombinare operatori di confronto e

  • variabili.

    While ((affamato) && !(gatto_presente)){ Trova del cibo; If(!(il_cibo__in_una_trappola)) Mangia il cibo;}

    Questo esempio presume che ci sianoanche variabili che descrivono lapresenza di un gatto e la posizione delcibo, con un valore di 1 per vero e di 0per falso. Ricordate solo che qualsiasivalore diverso da 0 viene consideratovero e che il valore 0 viene consideratofalso.

    0x244 Funzioni

  • A volte si avr un gruppo diistruzioni di cui il programmatore sa cheavr bisogno pi volte. Queste istruzionipossono essere riunite in unsottoprogramma pi piccolo chiamatofunzione. In altri linguaggi, le funzionisono note come subroutine o procedure.Per esempio, lazione di svoltare conlauto in realt formata da molteistruzioni pi particolari: attivarelindicatore di direzione opportuno,rallentare, controllare la presenza ditraffico in senso contrario, ruotare ilvolante nella direzione appropriata ecos via. Le indicazioni stradalidellinizio del capitolo richiedonoalcune svolte; elencare ogni singolaistruzione per ciascuna svolta, per,

  • sarebbe noioso (e meno leggibile). possibile passare delle variabili comeargomenti di una funzione permodificare il modo in cui questultimaagisce. In questo caso, alla funzioneviene passata la direzione in cuisvoltare.

    Function Svolta(direzione_variabile){ Aziona l'indicatore di direzione direzione_variabile; Rallenta; Controlla il traffico in senso contrario; while(c' traffico contrario) { Stop; Controlla il traffico in senso contrario; } Gira il volante verso direzione_variabile;

  • while(la svolta non completa) { if(velocit < 5 kmh) Accelera; } Riporta il volante nella posizione originale; Spegni l'indicatore di direzione direzione_variabile;}

    Questa funzione descrive tutte leistruzioni necessarie per effettuare unasvolta. Quando un programma checonosce questa funzione ha bisogno disvoltare, pu semplicemente richiamarequesta funzione. Quando la funzioneviene richiamata, le istruzioni che sitrovano al suo interno vengono eseguitecon gli argomenti passati; poi, dopo lachiamata della funzione, lesecuzione

  • riprende dal punto del programma in cuisi trovava. A questa funzione di esempio possibile passare il parametro asinistra o a destra, e questo modificala direzione di svolta.

    Per default nel linguaggio C lefunzioni possono restituire un valore aun chiamante. Per quanti abbianofamiliarit con le funzioni dellamatematica, tutto ci ha un senso.Immaginate una funzione che calcola ilfattoriale di un numero: ovviamenterestituisce il risultato.

    Nel linguaggio C le funzioni nonvengono etichettate con una parolachiave funzione o function, mavengono semplicemente dichiarate con il

  • tipo di dati della variabile cherestituiscono. Questo formato moltosimile alla dichiarazione delle variabili.Se una funzione deve restituire un intero(come quella che calcola il fattoriale diun numero x), potrebbe avere un aspettosimile a questo:

    int factorial(int x){ int i; for(i=1; i < x; i++) x *= i; return x;}

    Questa funzione viene dichiaratacome int perch moltiplica ogni valoreda 1 a x e restituisce il risultato, che unintero. Listruzione return posta allafine della funzione passa il contenuto

  • della variabile x e termina la funzionestessa. Questa funzione fattoriale puquindi essere utilizzata come unavariabile di tipo intero nella parteprincipale di qualsiasi programma chene abbia conoscenza.

    int a=5, b;b = factorial(a);

    Al termine di questo breveprogramma, la variabile b conterr 120,dato che la funzione fattoriale sarrichiamata con argomento 5 e restituir120.

    Anche nel linguaggio C ilcompilatore deve essere a conoscenzadelle funzioni prima di poterle

  • utilizzare. Questo scopo pu essereraggiunto semplicemente scrivendo tuttala funzione prima di utilizzarla in unaparte successiva del programma, outilizzando i prototipi di funzione. Unprototipo di funzione semplicementeun modo per indicare al compilatore chedeve aspettarsi di trovare una funzionecon un dato nome, un certo tipo di datida restituire e un certo tipo di dati comeargomento. La funzione effettiva potrtrovarsi vicino alla fine del programma,ma pu essere utilizzata in qualsiasialtra parte di esso, dato che ilcompilatore ne gi a conoscenza. Unesempio di prototipo di funzione per lafunzione factorial() avrebbe unaspetto simile a questo:

  • int factorial(int);

    In genere i prototipi di funzione sitrovano vicino allinizio di unprogramma. Nel prototipo non cbisogno di definire nomi di variabili,dato che questo processo viene svoltonella funzione effettiva. Al compilatoreinteressa soltanto il nome della funzione,il tipo di dati che restituisce e il tipo didati dei suoi argomenti.

    Se una funzione non deve restituirealcun valore, deve essere dichiaratacome void, come nel caso dellafunzione svolta() utilizzata comeesempio in precedenza. La funzionesvolta(), tuttavia, non assicura tuttoquanto richiesto per il programma sulle

  • indicazioni stradali. Ogni svoltapresenta una direzione e il nome di unavia. Ci significa che una funzione persvoltare dovrebbe avere due variabili:la direzione in cui svoltare e il nomedella via sulla quale immettersi. Questocomplica la funzione di svoltare, datoche il nome della via deve essereindividuato prima di effettuare il cambiodi direzione. Di seguito riportato lopseudocodice con una sintassi simile alC di una versione pi completa dellafunzione di svolta.

    void svolta(direzione_variabile, nome_via_cercato){ Cerca un cartello stradale; nome_incrocio_attuale = leggi cartello stradale;

  • while(nome_incrocio_attuale != nome_via_cercato) { Cerca un altro cartello stradale; nome_incrocio_attuale = leggi cartello stradale; }

    Aziona l'indicatore di direzione direzione_variabile; Rallenta; Controlla in traffico in senso contrario; while(c' traffico contrario) { Stop; Controlla il traffico in senso contrario; } Gira il volante verso direzione_variabile; while(la svolta non completa) { if(velocit < 5 kmh) Accelera; }

  • Riporta il volante nella posizione originale; Spegni l'indicatore di direzione direzione_variabile;}

    Questa funzione comprende unasezione che ricerca lincrocio adattocontrollando i cartelli stradali, leggendoil nome della via su ogni cartello esalvandolo in una variabile denominatanome_incrocio_attuale.Continuer a cercare e leggere i cartellistradali fino a trovare la via desiderata;a quel punto, verranno eseguite leistruzioni di svolta rimanenti. Ora possibile modificare lo pseudocodicedelle indicazioni stradali in modo cheutilizzi questa funzione di svolta.

  • Inizia andando verso est sulla Main Street;while (non c' una chiesa sulla destra) Guida lungo la Main Street;if (la strada bloccata){ Svolta(destra, Quindicesima); Svolta(sinistra, Pine Street); Svolta(destra, Sedicesima);}else Svolta(destra, Sedicesima);Svolta(sinistra, Destination Road);for (i=0; i
  • codice compilabile. Dato che lopseudocodice non deve esserefunzionante, non necessario scrivereper intero tutte le funzioni, sufficienteannotare Qui va inserita un po di robacomplicata. In un linguaggio diprogrammazione come il C, per, lefunzioni vengono usate in modomassiccio. Gran parte dellutilit del Csi deve a raccolte di funzioni chiamatelibrerie.

    0x250 Iniziamo a sporcarcile mani

    Ora che la sintassi del C un po pifamiliare e sono stati spiegati alcuni

  • concetti fondamentali diprogrammazione, la programmazione inC non appare pi un ostacolo cos alto.Esistono compilatori C praticamente perogni sistema operativo e ogniarchitettura di processore, ma in questolibro utilizziamo solo Linux e unprocessore di tipo x86. Linux unsistema operativo libero e al quale tuttipossono avere accesso, e i processoribasati su x86 sono quelli pi diffusi alivello del consumatore finale. Poichlhacking richiede una buona dose diesperimenti, meglio che abbiate adisposizione anche un compilatore C.

    Iniziamo, allora. Il programmafirstprog.c un semplice codice C che

  • stampa Hello, world! per 10 volte.

    firstprog.c

    #include

    int main(){ int i; for(i=0; i < 10; i++) // Itera per 10 volte. { puts("Hello, world!\n"); // Pone la stringa in output. } return 0; // Indica al sistema operativo // che il programma uscito senza errori.}

    Lesecuzione principale di unprogramma C inizia con la funzione

  • main(). Qualsiasi testo preceduto dadue barre (//) un commento e vieneignorato dal compilatore.

    La prima riga potrebbe sembrarestrana, ma solo sintassi C che indica alcompilatore di aggiungere le intestazioniper una libreria di input/ output (I/O)standard denominata stdio. Questofile viene aggiunto al programma quandoquesto viene compilato. Il suo percorso /usr/include/ stdio.h, e il file definiscediverse costanti e prototipi di funzioniper le corrispondenti funzioni dellalibreria di I/O standard. Poich lafunzione main() usa la funzioneprintf() della libreria di I/Ostandard, necessario un prototipo di

  • funzione per printf() prima che essapossa essere utilizzata. Questo prototipodi funzione (insieme a molti altri) contenuto nel file di intestazione stdio.h.Gran parte della potenza del C derivadalle librerie e dallestensibilit. Laparte restante del codice dovrebbe avereun senso e un aspetto simile a quellodello pseudocodice incontrato inprecedenza. Potreste avere notato lapresenza di un gruppo di parentesi graffeche possono essere eliminate. Il risultatodi questo programma dovrebbe apparirepiuttosto chiaro, ma compiliamolo conGCC ed eseguiamolo, tanto per esseresicuri.

    GCC (GNU Compiler Collection)

  • un compilatore C libero che traduce il Cin linguaggio macchina comprensibileper un processore. La traduzione che siottiene un file binario eseguibile cheper default denominato a.out. Ilprogramma compilato fa quello che visareste aspettati?

    reader@hacking:~/booksrc $ gcc firstprog.creader@hacking:~/booksrc $ ls -l a.out-rwxr-xr-x 1 reader reader 6621 2007-09-06 22:16 a.outreader@hacking:~/booksrc $ ./a.outHello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!Hello, world!

  • Hello, world!reader@hacking:~/booksrc $

    0x251 Il quadro dinsieme

    Finora abbiamo presentato materialeche avreste potuto apprendere in unalezione elementare sullaprogrammazione: semplici, maessenziali. La maggior parte dellelezioni introduttive alla programmazioneinsegna semplicemente come leggere escrivere il C. Non vorrei esserefrainteso: conoscere il C utile ed sufficiente per fare di voi deiprogrammatori rispettabili, marappresenta solo una parte di un quadropi ampio. La maggior parte dei

  • programmatori impara il linguaggiodallinizio alla fine e non riesce mai adavere una visione globale. Gli hackerbasano il loro vantaggio sul fatto disapere come tutti i pezzi interagisconoallinterno di questo quadro pi ampio.Per vedere il quadro dinsieme delregno della programmazione, bastacapire che il codice C prevede di esserecompilato: non pu fare nulla finch nonviene compilato in un file binarioeseguibile. Pensare a un sorgente Ccome a un programma unfraintendimento comune che vienesfruttato quotidianamente dagli hacker.Le istruzioni binarie di a. out sonoscritte in linguaggio macchina, unlinguaggio elementare che pu essere

  • compreso dalla CPU. I compilatori sonoprogettati per tradurre il codice dellinguaggio C in linguaggio macchina peruna moltitudine di architetture diprocessori. In questo caso, il processoreappartiene a una famiglia che usalarchitettura x86. Ci sono anchearchitetture di processori Sparc (usatenelle workstation Sun) e PowerPC(usata nei Mac pre-Intel). Ogniarchitettura ha un diverso linguaggiomacchina, quindi il compilatore agiscecome intermediario, traducendo ilcodice C nel linguaggio macchina adattoallarchitettura prescelta.

    Finch il programma compilatofunziona, il programmatore medio si

  • preoccupa solo del codice sorgente. Maun hacker capisce che il programmacompilato quello che vieneeffettivamente eseguito. Con unamigliore comprensione delfunzionamento della CPU, un hacker pumanipolare i programmi che vengonoeseguiti su di essa. Abbiamo visto ilcodice sorgente per il primo programmae labbiamo compilato in un binarioeseguibile per larchitettura x86. Maqual laspetto di questo file binarioeseguibile? Gli strumenti di sviluppoGNU contengono un programmadenominato objdump che pu essereutilizzato per esaminare i file binaricompilati. Iniziamo osservando il codicemacchina nel quale stata tradotta la

  • funzione main().

    reader@hacking:~/booksrc $ objdump -D a.out | grep -A20 main.:08048374 :8048374: 55 push %ebp8048375: 89 e5 mov %esp,%ebp8048377: 83 ec 08 sub $0x8,%esp804837a: 83 e4 f0 and $0xfffffff0,%esp804837d: b8 00 00 00 00 mov $0x0,%eax8048382: 29 c4 sub %eax,%esp8048384: c7 45 fc 00 00 00 00 movl $0x0,0xfffffffc(%ebp)804838b: 83 7d fc 09 cmpl $0x9,0xfffffffc(%ebp)804838f: 7e 02 jle 8048393 8048391: eb 13 jmp 80483a6 8048393: c7 04 24 84 84 04 08

  • movl $0x8048484,(%esp)804839a: e8 01 ff ff ff call 80482a0 804839f: 8d 45 fc lea 0xfffffffc(%ebp),%eax80483a2: ff 00 incl (%eax)80483a4: eb e5 jmp 804838b 80483a6: c9 leave80483a7: c3 ret80483a8: 90 nop80483a9: 90 nop80483aa: 90 nopreader@hacking:~/booksrc $

    Il programma objdumpprodurrebbe troppe righe di output perpoterle esaminare in modo ragionevole,

  • quindi i risultati del programma sonopassati a grep con lopzione della rigadi comando per visualizzare le 20 righesuccessive allespressione regolaremain.:. Ogni byte rappresentato innotazione esadecimale, un sistema dinumerazione in base 16. Il sistema dinumerazione usato normalmente inbase 10, poich utilizza in tutto 10 cifre(da 0 a 9). Il sistema esadecimale usa icaratteri da 0 a 9 per rappresentare inumeri da 0 a 9, ma usa anche le lettereda A a F per rappresentare i valori da10 a 15. una notazione comoda perchun byte contiene 8 bit, ognuno dei qualipu essere vero o falso. Ci significache un byte ha 256 (28) valori possibili,quindi ogni byte pu essere descritto con

  • 2 cifre esadecimali.

    I numeri esadecimali nel codicemacchina iniziando da 0x8048374allestrema sinistra rappresentanoindirizzi di memoria. I bit delleistruzioni in linguaggio macchina devonoessere inseriti da qualche parte, e questoluogo si chiama memoria. La memorianon altro che un insieme di byte dispazio temporaneo numerati conaltrettanti indirizzi.

    Come una fila di case su una stessavia, ciascuna con il proprio indirizzo, lamemoria pu essere pensata come unafila di byte, ognuno con il proprioindirizzo di memoria. possibileaccedere a ciascun byte di memoria con

  • il suo indirizzo, e in questo caso la CPUaccede a questa parte di memoria perindividuare le istruzioni in linguaggiomacchina che costituiscono ilprogramma compilato. I processori Intelx86 pi vecchi utilizzano uno schema diindirizzamento a 32 bit, mentre quellipi recenti ne utilizzano uno a 64 bit. Iprocessori a 32 bit hanno 232 (o4.294.967.296) possibili indirizzi, chediventano 264 (1,84467441 1019) per iprocessori a 64 bit. Questi ultimipossono funzionare anche in unamodalit di compatibilit con i 32 bit, eci consente loro di eseguire codice a32 bit con rapidit.

    I byte esadecimali presenti nella

  • parte centrale del listato precedenteriportano le istruzioni in linguaggiomacchina per il processore x86.Ovviamente questi valori esadecimalisono solo delle rappresentazioni degli 1e degli 0 binari che la CPU pucomprendere. Ma dato che 0101010110001001111001011000001111101100111100001 qualcosa di utile solamente alprocessore, il codice macchina vienevisualizzato sotto forma di byteesadecimali e ogni istruzione vieneriportata su una propria riga, comequando si divide un paragrafo in frasi.

    Pensandoci bene, i byte esadecimalinon sono poi molto utili di per s: quiche entra in gioco il linguaggio

  • assembly. Questo linguaggio in realtnon che una raccolta di codicimnemonici per le corrispondentiistruzioni in linguaggio macchina.Listruzione ret decisamente picomprensibile e pi facile da ricordaredi 0xc3 o 11000011. A differenza delC o di altri linguaggi compilati, leistruzioni del linguaggio assembly hannouna relazione diretta uno a uno con leistruzioni del linguaggio macchinacorrispondenti. Ci significa che, datoche ogni architettura di processori haistruzioni di linguaggio macchinadifferenti, ciascuna possiede anche unlinguaggio assembly differente.Lassembly solo un modo che iprogrammatori utilizzano per

  • rappresentare le istruzioni in linguaggiomacchina che vengono inviate alprocessore. Il modo in cui questeistruzioni del linguaggio macchinavengono rappresentate semplicementeuna questione di preferenze e diconvenzioni. Bench in teoria chiunquepotrebbe creare una propria sintassi dilinguaggio assembly per x86, nellamaggior parte dei casi viene utilizzatouno di due tipi principali: la sintassiAT&T e la sintassi Intel. Lassemblyvisualizzato in precedenza adotta lasintassi AT&T, e questa utilizzata perdefault praticamente da tutti gli strumentidi disassemblaggio Linux. La sintassiAT&T risulta facilmente riconoscibileper la massiccia presenza dei simboli %

  • e $ come prefissi a qualsiasi cosa (dateancora unocchiata allesempioprecedente). Lo stesso codice puessere visualizzato in sintassi Intelinserendo unulteriore parametro da rigadi comando, -M intel, perobjdump, come nellesempio riportatodi seguito.

    reader@hacking:~/booksrc $ objdump -M intel -D a.out | grep -A20 main.:08048374 :8048374: 55 push ebp8048375: 89 e5 mov ebp,esp8048377: 83 ec 08 sub esp,0x8804837a: 83 e4 f0 and esp,0xfffffff0804837d: b8 00 00 00 00 mov eax,0x08048382: 29 c4

  • sub esp,eax8048384: c7 45 fc 00 00 00 00 mov DWORD PTR [ebp-4],0x0804838b: 83 7d fc 09 cmp DWORD PTR [ebp-4],0x9804838f: 7e 02 jle 8048393 8048391: eb 13 jmp 80483a6 8048393: c7 04 24 84 84 04 08 mov DWORD PTR [esp],0x8048484804839a: e8 01 ff ff ff call 80482a0 804839f: 8d 45 fc lea eax,[ebp-4]80483a2: ff 00 inc DWORD PTR [eax]80483a4: eb e5 jmp 804838b 80483a6: c9 leave80483a7: c3 ret80483a8: 90 nop80483a9: 90

  • nop80483aa: 90 nopreader@hacking:~/booksrc $

    Personalmente ritengo che la sintassiIntel sia pi leggibile e pi facile dacapire, perci ho scelto di utilizzarlasempre per gli scopi di questo libro.Indipendentemente dallarappresentazione in linguaggioassembly, i comandi che un processorecapisce sono piuttosto semplici. Questeistruzioni consistono di unoperazione ea volte di argomenti aggiuntivi chedescrivono la destinazione e/o loriginedelloperazione. Le operazioni spostanoil contenuto della memoria, svolgonosemplici calcoli matematici ointervengono sul processore per fargli

  • fare qualcosaltro. Alla fine, questo tutto quanto un processore in grado difare. Tuttavia, come con un alfabeto dilettere relativamente piccolo sono statiscritti milioni di libri, cos con ungruppo relativamente piccolo diistruzioni macchina si pu creare unnumero infinito di programmi.

    I processori hanno anche un propriogruppo di variabili speciali, chiamateregistri. La maggior parte delleistruzioni utilizza questi registri perleggere o scrivere dati, quindi lacomprensione dei registri di unprocessore essenziale per capire leistruzioni. Le dimensioni del quadrodinsieme continuano a crescere...

  • 0x252 Il processore x86

    La CPU 8086 fu il primo processorex86. Fu sviluppata e prodotta da Intel,che in seguito sforn processori piavanzati nella stessa famiglia: 80186,80286, 80386 e 80486. Se ricordate diavere sentito parlare di processori 386 e486 negli anni 80 e 90, si riferivanoproprio a questo.

    Il processore x86 ha molti registri,che somigliano a variabili interne per ilprocessore stesso. A questo punto sipotrebbe affrontare un discorso astrattosui registri, ma sempre meglio avereunesperienza diretta delle cose. Tra glistrumenti di sviluppo GNU esiste anche

  • un debugger denominato GDB. Idebugger sono utilizzati daiprogrammatori per seguire passo passoil funzionamento dei programmicompilati, esaminare gli spazi dimemoria dei programmi e visualizzare iregistri del processore. Unprogrammatore che non ha mai utilizzatoun debugger per indagare suimeccanismi pi interni di un programma come un medico del diciassettesimosecolo che non ha mai utilizzato unmicroscopio. Come un microscopio, undebugger consente a un hacker diosservare il mondo microscopico delcodice macchina, ma le potenzialit ditale strumento sono molto superiori a cia cui potrebbe far pensare questa

  • metafora. A differenza di unmicroscopio, un debugger pu esaminarelesecuzione da tutti i punti di vista,interromperla e modificarla in temporeale.

    Di seguito viene utilizzato GDB pervisualizzare lo stato dei registri delprocessore prima dellavvio delprogramma.

    reader@hacking:~/booksrc $ gdb -q ./a.outUsing host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".

    (gdb) break mainBreakpoint 1 at 0x804837a(gdb) runStarting program: /home/reader/booksrc/a.out

  • Breakpoint 1, 0x0804837a in main ()(gdb) info registerseax 0xbffff894 -1073743724ecx 0x48e0fe81 1222704769edx 0x1 1ebx 0xb7fd6ff4 -1208127500esp 0xbffff800 0xbffff800ebp 0xbffff808 0xbffff808esi 0xb8000ce0 -1207956256edi 0x0 0eip 0x804837a 0x804837a

    eflags 0x286 [ PF SF IF ]cs 0x73 115ss 0x7b 123ds 0x7b 123es 0x7b 123fs 0x0 0gs 0x33 51(gdb) quitThe program is running. Exit anyway? (y or n) yreader@hacking:~/booksrc $

  • Sulla funzione main() impostatoun breakpoint (punto di interruzione),per cui lesecuzione si interromperappena prima di quel punto. QuindiGDB avvia il programma, si ferma albreakpoint e visualizza tutti i registri delprocessore con il loro stato attuale.

    I primi quattro registri (EAX, ECX,EDX ed EBX) sono noti come registrigenerici. Sono chiamati accumulatore,contatore, dati e base, rispettivamente.Questi registri vengono utilizzati permolti scopi diversi, ma agisconoprincipalmente come variabilitemporanee per la CPU quando questasta eseguendo istruzioni macchina.

    Anche gli altri quattro registri (ESP,

  • EBP, ESI ed EDI) sono generici, ma avolte vengono definiti come puntatori eindici. Corrispondono rispettivamente apuntatore allo stack, puntatore base,indice di origine e indice didestinazione. I primi due vengonochiamati puntatori perchimmagazzinano indirizzi a 32 bit, chepuntano essenzialmente a posizioni dimemoria. Questi registri sono piuttostoimportanti per lesecuzione delprogramma e la gestione della memoria;ne riparleremo pi approfonditamente inseguito. Anche gli ultimi due registrisono tecnicamente dei puntatori chevengono comunemente utilizzati perpuntare allorigine e alla destinazionequando necessario leggere o scrivere

  • dei dati in memoria. Ci sono istruzioniload e store che usano questiregistri, ma per la maggior parte dei casiessi possono essere visti come sempliciregistri generici.

    Il registro EIP il puntatore diistruzione, che punta allistruzione cheil processore sta leggendo in un datomomento. Come un bambino che segnacon lindice ogni parola mentre legge, ilprocessore legge ciascuna istruzioneusando come indice il registro EIP.Ovviamente questo registro decisamente importante e sar utilizzatospesso nelle operazioni di debugging.Attualmente sta puntando a un indirizzodi memoria in 0x804838a.

  • Il registro EFLAGS consiste didiversi flag a bit che vengono utilizzatiper i confronti e le segmentazioni dellamemoria. La memoria effettiva vienesuddivisa in vari segmenti diversi, cheverranno esaminati in seguito, e questiregistri tengono traccia dellesuddivisioni. Per la maggior parte,questi registri possono essere ignorati,dato che raro dovervi accederedirettamente.

    0x253 Il linguaggioassembly

    Poich per questo libro vieneimpiegato il linguaggio assembly con

  • sintassi Intel, gli strumenti utilizzatidevono essere configurati per adottarequesta sintassi. In GDB possibileimpostare la sintassi per ildisassemblaggio su Intel semplicementeinserendo set disassemblyintel, o, in forma abbreviata, setdis intel. Questa impostazione puessere configurata come quella adottataa ogni avvio di GDB inserendo ilcomando nel file .gdbinit nella propriahome directory.

    reader@hacking:~/booksrc $ gdb -q(gdb) set dis intel(gdb) quitreader@hacking:~/booksrc $ echo "set dis intel" > ~/.gdbinitreader@hacking:~/booksrc $ cat ~/.gdbinitset dis intel

  • reader@hacking:~/booksrc $

    Ora che GDB stato configurato perutilizzare la sintassi Intel, si pu iniziarea descrivere tale sintassi. In genere leistruzioni di assembly nella sintassi Intelseguono questo stile:

    operazione ,

    I valori destinazione e originepotranno essere un registro, un indirizzodi memoria o un valore. Le operazioni ingenere sono associazioni mnemonicheintuitive: loperazione mov sposta unvalore dallorigine alla destinazione,sub sottre, inc incrementa, e cos via.Le istruzioni seguenti, per esempio,spostano il valore da ESP a EBP e

  • quindi sottraggono 8 da ESP (salvandoil risultato in ESP).

    8048375: 89 e5 mov ebp,esp8048377: 83 ec 08 sub esp,0x8

    Ci sono anche operazioni utilizzateper controllare il flusso di esecuzione.Loperazione cmp viene impiegata perconfrontare dei valori, e in praticamentetutte le operazioni che iniziano con j utilizzata per saltare (jump) a unadiversa parte del codice (in base alrisultato del confronto). Lesempioriportato di seguito confronta un valore a4 byte presente in EBP meno 4 con ilnumero 9. Listruzione successiva unaforma abbreviata che sta per jump if less

  • than or equal to (salta se minore ouguale a), riferita al risultato delprecedente confronto. Se quel valore minore o uguale a 9, lesecuzione saltaallistruzione presente in 0x8048393;in caso contrario, lesecuzione proseguecon listruzione seguente, con un saltoincondizionato. Se il valore non minore o uguale a 9, lesecuzione salta a0x80483a6.

    804838b: 83 7d fc 09 cmp DWORD PTR [ebp-4],0x9804838f: 7e 02 jle 8048393 8048391: eb 13 jmp 80483a6

    Questi esempi sono stati tratti dadisassemblaggi precedenti, e il debugger

  • stato configurato per utilizzare lasintassi Intel, quindi possibile usarloper seguire passo passo il primoprogramma a livello di istruzioniassembly.

    Il parametro -g pu essere utilizzatodal compilatore GCC per includereulteriori informazioni di debugging, checonsentiranno a GDB laccesso alcodice sorgente.

    reader@hacking:~/booksrc $ gcc -g firstprog.creader@hacking:~/booksrc $ ls -l a.out-rwxr-xr-x 1 matrix users 12919 Jul 4 17:29 a.outreader@hacking:~/booksrc $ gdb -q ./a.outUsing host libthread_db library "/lib/libthread_db.so.1".(gdb) list

  • 1 #include 23 int main()4 {5 int i;6 for(i=0; i < 10; i++)7 {8 printf("Hello, world!\n");9 }10 }(gdb) disassemble mainDump of assembler code for function main():0x08048384 : push ebp0x08048385 : mov ebp,esp0x08048387 : sub esp,0x80x0804838a : and esp,0xfffffff00x0804838d : mov eax,0x00x08048392 : sub esp,eax0x08048394 : mov DWORD PTR [ebp-4],0x00x0804839b : cmp DWORD PTR [ebp-4],0x90x0804839f : jle 0x80483a3

  • 0x080483a1 : jmp 0x80483b6

    0x080483a3 : mov DWORD PTR [esp],0x80484d40x080483aa : call 0x80482a8

    0x080483af : lea eax,[ebp-4]0x080483b2 : inc DWORD PTR [eax]0x080483b4 : jmp 0x804839b

    0x080483b6 : leave0x080483b7 : retEnd of assembler dump.(gdb) break mainBreakpoint 1 at 0x8048394: file firstprog.c, line 6.(gdb) runStarting program: /hacking/a.out

    Breakpoint 1, main() at firstprog.c:66 for(i=0; i < 10; i++)(gdb) info register eipeip 0x8048394

  • 0x8048394(gdb)

    Innanzitutto viene riportato il codicesorgente e viene visualizzata la funzionemain() disassemblata. Quindi vieneimpostato un breakpoint allinizio dimain(), e il programma vieneeseguito. Il breakpoint indicasemplicemente al debugger di arrestarelesecuzione del programma. Dato chequesto breakpoint stato impostatoallinizio della funzione main(),lesecuzione si fermer prima di attivareuna qualsiasi delle istruzioni presenti inmain(). Infine viene mostrato il valoredel registro EIP (il puntatore diistruzione).

  • Notate come lEIP contenga unindirizzo di memoria che punta aunistruzione nel codice disassemblatodella funzione main(), evidenziata ingrassetto. Le istruzioni prima di questa(in corsivo) costituiscono il cosiddettoprologo della funzione e vengonogenerate dal compilatore per impostarela memoria per il resto delle variabililocali della funzione main(). Il fattoche nel linguaggio C le variabilidebbano essere dichiarate si deve inparte alla necessit di agevolare lacostruzione di questa parte di codice. Ildebugger sa che questa porzione dicodice viene generata in automatico, ed abbastanza intelligente da passareoltre. In seguito torneremo sul prologo

  • della funzione; per ora, accettando ilconsiglio di GDB, passiamo avanti.

    Il debugger GDB offre un metododiretto per esaminare la memoria, con ilcomando x, che una forma abbreviatadi examine. La capacit di esaminare lamemoria una caratteristicafondamentale per qualsiasi hacker. Lamaggior parte dei successi degli hackerassomigliano molto ai trucchi di magia:sembrano fantastici e incredibili per chinon sa nulla riguardo a destrezza dimano e inganno. Sia nella magia chenellhacking, guardando nel posto giusto,il trucco risulta ovvio. Questo uno deimotivi per cui un buon mago non fa mailo stesso trucco due volte. Tuttavia, con

  • un debugger come GDB, ogni aspettodellesecuzione di un programma puessere attentamente esaminato, fermato,seguito passo passo e ripetuto per tuttele volte necessarie. Poich unprogramma in esecuzione in gran parte siriduce a un processore e a segmenti dimemoria, esaminare la memoriarappresenta il primo modo per dareunocchiata a quanto sta effettivamenteaccadendo.

    Il comando examine in GDB puesser utilizzato per esaminare undeterminato indirizzo di memoria inmolti modi diversi. Quando vieneutilizzato, questo comando richiede dueargomenti: la posizione di memoria da

  • esaminare e il modo in cui visualizzarla.Anche il formato di visualizzazione usauna forma abbreviata di una sola lettera,che pu essere fatta precedere da unnumero che esprime gli elementi daesaminare. Tra le lettere chedeterminano il formato vi sono:

    o Visualizza in ottale.x Visualizza in esadecimale.u Visualizza in decimale standard

    senza segno.t Visualizza in binario.

    Queste lettere possono essere usatecon il comando examine per esaminareun determinato indirizzo di memoria.

  • Nellesempio che segue viene utilizzatolindirizzo corrente del registro EIP.Spesso in GDB vengono utilizzati icomandi abbreviati, e anche inforegister eip pu essere abbreviatosemplicemente in i r eip.

    (gdb) i r eipeip 0x8048384 0x8048384

    (gdb) x/o 0x80483840x8048384 : 077042707(gdb) x/x $eip0x8048384 : 0x00fc45c7(gdb) x/u $eip0x8048384 : 16532935(gdb) x/t $eip0x8048384 : 00000000111111000100010111000111(gdb)

    La memoria a cui il registro EIP sta

  • puntando pu essere esaminatautilizzando lindirizzo contenutonellEIP. Il debugger consente direferenziare i registri direttamente, percui $eip equivalente al valore chelEIP contiene in quel momento. Ilvalore 077042707 in ottale equivale a0x00fc45c7 in esadecimale, cheequivale a 16532935 in decimale, chea sua volta equivale al binario00000000111111000100010111000111Un numero anteposto al formato delcomando examine consente di esaminarepi unit allindirizzo desiderato.

    (gdb) x/2x $eip0x8048384 : 0x00fc45c7 0x83000000(gdb) x/12x $eip0x8048384 : 0x00fc45c7

  • 0x83000000 0x7e09fc7d 0xc713eb020x8048394 : 0x84842404 0x01e80804 0x8dffffff 0x00fffc450x80483a4 : 0xc3c9e5eb 0x90909090 0x90909090 0x5de58955(gdb)

    La dimensione di default di unasingola unit ununit di 4 bytechiamata word o parola. La dimensionedelle unit di visualizzazione per ilcomando examine pu essere modificataaggiungendo una lettera dopo quella chespecifica il formato. Queste sono lelettere accettate per le dimensioni:

    b Un byte singolo.

    h Una halfword, cio due byte.

    w Una word, cio quattro byte.

  • g Un giant, cio otto byte.

    Qui si potrebbe creare una certaconfusione, perch a volte il termineword (parola) si riferisce anche avalori di due byte. In questo caso, unadouble word o DWORD si riferisce a unvalore di 4 byte. In questo libro, word eDWORD si riferiscono entrambi avalori di 4 byte. Se si parla di un valoredi 2 byte, si utilizza il termine short ohalfword. Riportiamo di seguito loutputdi GDB che visualizza la memoria condiversi formati.

    (gdb) x/8xb $eip0x8048384 : 0xc7 0x45 0xfc 0x00 x00 0x00 0x00 x83(gdb) x/8xh $eip0x8048384 : 0x45c7 0x00fc

  • 0x0000 0x8300 0xfc7d 0x7e09 0xeb02 0xc713(gdb) x/8xw $eip0x8048384 : 0x00fc45c7 0x83000000 0x7e09fc7d 0xc713eb020x8048394 : 0x84842404 0x01e80804 0x8dffffff 0x00fffc45(gdb)

    Osservando attentamente, potrestenotare qualcosa di strano nei dati appenapresentati. Il primo comando examinemostra i primi otto byte, e naturalmente icomandi examine che usano unit pigrandi visualizzano pi dati in totale. Ilprimo examine, per, mostra che i primidue byte sono 0xc7 e 0x45, ma seviene esaminata una halfword allostesso identico indirizzo di memoria, siha la visualizzazione di 0x45c7, con ibyte invertiti. Lo stesso effetto di

  • inversione dei byte si nota quando unaword di 4 byte viene visualizzata come0x00fc45c7, laddove se i primiquattro byte vengono mostrati byte perbyte, lordine che si ottiene 0xc7,0x45, 0xfc e 0x00.

    Questo avviene perch sulprocessore x86 i valori vengonomemorizzati nellordine dei byte little-endian, il che significa che il byte menosignificativo viene memorizzato perprimo. Per esempio, se quattro bytedevono essere interpretati come un unicovalore, dovranno essere utilizzati inordine inverso. Il debugger GDB abbastanza intelligente da conoscere ilmodo in cui i valori vengono

  • memorizzati, quindi, quando vieneesaminata una word o una halfword, perpotere visualizzare i valori corretti inesadecimale, i byte devono essereinvertiti. Una nuova occhiata a questivalori visualizzati sia come esadecimalisia come decimali senza segno potrebbeessere utile per dissipare qualsiasiconfusione.

    (gdb) x/4xb $eip0x8048384 : 0xc7 0x45 0xfc 0x00(gdb) x/4ub $eip0x8048384 : 199 69 252 0(gdb) x/1xw $eip0x8048384 : 0x00fc45c7(gdb) x/1uw $eip0x8048384 : 16532935(gdb) quitThe program is running. Exit anyway?

  • (y or n) yreader@hacking:~/booksrc $ bc -ql199*(256^3) + 69*(256^2) + 252*(256^1) + 0*(256^0)33432524800*(256^3) + 252*(256^2) + 69*(256^1) + 199*(256^0)16532935quitreader@hacking:~/booksrc $

    I primi quattro byte sono mostrati siain esadecimale sia nella notazionedecimale standard senza segno. Vieneutilizzato un programma di calcolo dallariga di comando denominato bc permostrare che, se i byte vengonointerpretati nellordine errato, ilrisultato sar un valore orribilmentesbagliato e pari a 3343252480.Lordine dei byte di una determinata

  • architettura un dettaglio importante daconoscere. Bench la maggior partedegli strumenti di debugging e deicompilatori gestisca lordine dei byte inmaniera automatica, alla fine vitroverete a manipolare la memoriadirettamente.

    Oltre a convertire lordine dei byte,il comando examine consente a GDB dieffettuare altre operazioni. Si gi vistocome GDB sia in grado didisassemblare le istruzioni in linguaggiomacchina in istruzioni assembly leggibilida un essere umano. Il comandoexamine accetta anche la lettera diformato i, forma abbreviata perinstruction, per visualizzare la memoria

  • sotto forma di istruzioni disassemblatein linguaggio assembly.

    reader@hacking:~/booksrc $ gdb -q ./a.outUsing host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".

    (gdb) break mainBreakpoint 1 at 0x8048384: file firstprog.c, line 6.(gdb) runStarting program: /home/reader/booksrc/a.out

    Breakpoint 1, main () at firstprog.c:66 for(i=0; i < 10; i++)(gdb) i r $eipeip 0x8048384 0x8048384

    (gdb) x/i $eip0x8048384 : mov DWORD PTR [ebp-4],0x0(gdb) x/3i $eip

  • 0x8048384 : mov DWORD PTR [ebp-4],0x00x804838b : cmp DWORD PTR [ebp-4],0x90x804838f : jle 0x8048393

    (gdb) x/7xb $eip0x8048384 : 0xc7 0x45 0xfc 0x00 0x00 0x00 0x00(gdb) x/i $eip0x8048384 : mov DWORD PTR [ebp-4],0x0(gdb)

    Nelloutput riportato in precedenza,il programma a.out viene eseguito inGDB, con un breakpoint impostato sumain(). Poich il registro EIP stapuntando a unarea di memoria checontiene istruzioni in linguaggiomacchina, il loro disassemblaggioavviene senza problemi.

  • Le operazioni svolte in precedenzaper objdump confermano che i settebyte a cui lEIP sta puntandocorrispondono a istruzioni in linguaggiomacchina per listruzione in assemblycorrispondente:

    8048384: c7 45 fc 00 00 00 00 mov DWORD PTR [ebp-4],0x0

    Questa istruzione sposta il valore 0nella memoria situata allindirizzomemorizzato nel registro EBP, meno 4.Questa la posizione in cui la variabilei del C registrata in memoria; i erastata dichiarata come un intero che usa 4byte di memoria sul processore x86. Inpratica questo comando azzera lavariabile i per il ciclo for. Se questa

  • memoria viene esaminata adesso, nonconterr altro che una specie di inutilespazzatura. La memoria in questaposizione pu essere esaminata in moltimodi diversi.

    (gdb) i r ebpebp 0xbffff808 0xbffff808(gdb) x/4xb $ebp - 40xbffff804: 0xc0 0x83 0x04 0x08(gdb) x/4xb 0xbffff8040xbffff804: 0xc0 0x83 0x04 0x08(gdb) print $ebp - 4$1 = (void *) 0xbffff804(gdb) x/4xb $10xbffff804: 0xc0 0x83 0x04 0x08(gdb) x/xw $10xbffff804: 0x080483c0(gdb)

  • Si vede che il registro EBP contienelindirizzo 0xbffff808, e listruzioneassembly andr a scrivere in un valorespostato di 4 in meno rispetto a esso,0xbffff804. Il comando examinepu esaminare questo indirizzo dimemoria direttamente o facendo ilcalcolo al momento. Anche il comandoprint pu essere utilizzato persvolgere semplici calcoli, ma il risultatoviene memorizzato nel debugger in unavariabile temporanea. Questa variabiledenominata $1 pu essere impiegata inseguito per accedere nuovamente a unadeterminata posizione di memoria.Ciascuno dei metodi illustrati inprecedenza otterr lo stesso scopo:visualizzare i quattro byte di spazzatura

  • presenti in memoria che saranno azzeratiquando listruzione corrente andr inesecuzione.

    Eseguiamo listruzione corrente conil comando nexti, forma abbreviataper next instruction. Il processorelegger listruzione presente nellEI P,la eseguir e far passare lEIPallistruzione successiva.

    (gdb) nexti0x0804838b 6 for(i=0; i < 10; i++)(gdb) x/4xb $10xbffff804: 0x00 0x00 0x00 0x00(gdb) x/dw $10xbffff804: 0(gdb) i r eipeip 0x804838b 0x804838b

  • (gdb) x/i $eip0x804838b : cmp DWORD PTR [ebp-4],0x9(gdb)

    Come previsto, il comandoprecedente azzera i quattro byte presentiin EBP meno 4, preparando la memoriaper la variabile i del C. Quindi lEIPpassa allistruzione successiva. Lepoche istruzioni seguenti hanno pisenso se esaminate insieme.

    (gdb) x/10i $eip0x804838b : cmp DWORD PTR [ebp-4],0x90x804838f : jle 0x8048393

    0x8048391 : jmp 0x80483a6

    0x8048393 : mov DWORD PTR [esp],0x80484840x804839a : call 0x80482a0

  • 0x804839f : lea eax,[ebp-4]0x80483a2 : inc DWORD PTR [eax]0x80483a4 : jmp 0x804838b

    0x80483a6 : leave0x80483a7 : ret(gdb)

    La prima istruzione, cmp, unistruzione compare, di confronto, checonfronta la memoria usata dallavariabile i del C con il valore 9.Listruzione successiva jle sta perjump if less than or equal to. Essautilizza il risultato del confrontoprecedente (memorizzato attualmente nelregistro EFLAGS) per fare in modo chelEIP passi a una diversa parte del

  • codice se la destinazione restituitadalloperazione di confronto precedente minore o uguale alla origine. In questocaso listruzione dice di spostarsiallindirizzo 0x8048393 se il valorepresente in memoria per la variabile idel C minore o uguale a 9. In casocontrario, lEIP continuer allistruzionesuccessiva, con un salto incondizionato.Listruzione far passare lEIPallindirizzo 0x80483a6. Queste trestrutture si combinano per creare unastruttura di controllo if-then-else: se i minore o uguale a 9, allora passaallistruzione allindirizzo0x8048393; altrimenti vaiallistruzione allindirizzo0x80483a6. Il primo indirizzo,

  • 0x8048393 (evidenziato in grassetto), semplicemente listruzione presentedopo listruzione di salto, e il secondoindirizzo, 0x80483a6 (evidenziato incorsivo), si trova alla fine dellafunzione.

    Poich sappiamo che nella posizionedi memoria confrontata con 9 presenteil valore 0, e