Newsgroup: it.comp.lang.c++
Titolo: [OT] scherzi da programmatore

Ciao a tutti.
Questo post solo per fare riflettere come a volte le cose non sono come
sembrano... E i programmi non si comportano per come ci si aspetterebbe a
prima vista.
Sicuramente i più esperti di voi non avranno difficoltà a spiegare lo strano
comportamento del piccolissimo programmino che riporto più giù... Comunque
la mia intenzione è quella di fare presente ai nuovi della programmazione
come si possono realizzare cose un po' strane e in C++ assolutamente
standard, tenuta in buon conto una conoscenza di come la macchina
sottostante opera, e nel caso specifico conoscendo come avviene la gestione
dello spazio di memoria del processo.
Quanto sto per scrivere funziona su vari Unix commerciali e Linux su Intel
x86, ma dovrebbe funzionare anche con Windows. Ripeto: C++ standard, niente
Assembly e niente strane syscalls...
#include <iostream>
void fun(); // la definiamo dopo...
int main()
{
    int var = 1;
    fun();
    var = 0;
    std::cout << var << '\n';
}
Molti di voi si aspettano che lanciando questo banale programma si deve
assolutamente avere in output il valore '0' e una newline... Invece questo
programma stamperà '1' , '\n' e poi andrà naturalmente a buon fine.
Mai sottovalutare un 'innocua' funzione fun(), che non prende nessun
parametro e non restituisce nulla... Ecco il codice:
void fun()
{
    int *ret;
    ret = (int *)&ret + 2;
    *ret += 7;
}
Provare per credere... Se qualcuno fosse interessato a capire come questa
funzione altera l'atteso comportamento di tutto il resto del programma,
scriva due righe e sarò lieto di spiegare e forse di mostrare altri esempi.
Buon divertimento,
fabiomdf
--------------------------------
Inviato via 
http://arianna.libero.it/usenet/

 assolutamente standard
è un undefinite behavior.
 > Molti di voi si aspettano che lanciando questo banale programma si
 > deve assolutamente avere in output il valore '0' e una newline...
 > Invece questo programma stamperà '1' , '\n'
 > e poi andrà naturalmente a buon fine.
come no, forse stamperà "1\n" sulla tua macchina, col tuo compilatore,
nel tuo sistema operativo, nel tuo ambiente, facendo qualche altro 
milione di supposizioni.
-- 
sbi


Non è così:
La funzione fun() assegna un indirizzo preciso al puntatore "int *ret" ed in
maniera altrettanto calcolata incrementa di una precisa quantità il valore
presente alla memoria puntata da ret stesso.
Questi calcoli si possono fare anche sulla tua macchina, qualsiasi essa sia,
sul tuo sistema operativo e compilatore (e non c'è bisogno di altre
supposizioni a parte queste 3).
Anzi, per quanto riguarda la assegnazione dell'indirizzo a ret, non c'è
differenza neanche tra sistemi operativi, ma si deriva dalle cosiddette
convenzioni di chiamata delle funzioni in stile C.
Ciao,
fanbiomdf


 Non è così:
Invece, quello che dice sbirish è perfettamente corretto. Non c'è alcuna 
garanzia che il programma debba avere quell'output. Ad esempio, con il mio 
compilatore e con le mie solite opzioni di compilazione, ottengo un 
comportamento diverso.
Max

 [...]
Ti passo questo "estratto" della mia console:
--------------------------------------
bash-2.05b$ g++ -o prova prova.cc
bash-2.05b$ ./prova
1
bash-2.05b$ g++ -O2 -o prova prova.cc
bash-2.05b$ ./prova
0
bash-2.05b$ g++ -O3 -o prova prova.cc
bash-2.05b$ ./prova
0
Segmentation fault
bash-2.05b$
--------------------------------------
Come vedi non ho cambiato macchina (Athlon), non ho cambiato sistema
operativo (Gentoo Linux), non ho cambiato ambiente, non ho nemmeno spostato
il mouse...
"undefinite behavior" e' la risposta giusta, non mi stupirei se su qualche
architettura il tuo codice formattasse l'HD ;-)
Ciao!
-- 
Qual'e' il colmo per uno studente-lavoratore?
Avere un quadrato che si crede un rettangolo.

 supposizioni a parte queste 3).
ok stesso compilatore stessa macchina stesso os:
C:\DOCUME~1\ADMINI~1\Desktop\crap>g++.exe -D__DEBUG__ -c main.cpp -o
main.o -I"C:/devcpp/include/c++/3.3.1" 
-I"C:/devcpp/include/c++/3.3.1/mingw32"
-I"C:/devcpp/include/c++/3.3.1/backward" 
-I"C:/devcpp/lib/gcc-lib/mingw32/3.3.1/include"
-I"C:/devcpp/include"
C:\DOCUME~1\ADMINI~1\Desktop\crap>g++  -D__DEBUG__ main.o -o right.exe
C:\DOCUME~1\ADMINI~1\Desktop\crap>right.exe
1
C:\DOCUME~1\ADMINI~1\Desktop\crap>g++.exe -O3 -c main.cpp -o main.o 
-I"C:/devcpp/include/c++/3.3.1"
-I"C:/devcpp/include/c++/3.3.1/mingw32"
-I"C:/devcpp/include/c++/3.3.1/backward"
-I"C:/devcpp/lib/gcc-lib/mingw32/3.3.1/include"
-I"C:/devcpp/include"
C:\DOCUME~1\ADMINI~1\Desktop\crap>g++ main.o -o wrong.exe
C:\DOCUME~1\ADMINI~1\Desktop\crap>wrong.exe
0
C:\DOCUME~1\ADMINI~1\Desktop\crap>
ora dammi una spiegazione valida che confermi la TUA asserzione.
In questo ng tra l'altro si parla di standard, non del tuo compilatore 
sulla tua macchina nel tuo os nel tuo ambiente.
-- 
sbi

 convenzioni di chiamata delle funzioni in stile C.
Mmh, puoi spiegarti meglio?
Ciao, Enrico

 "undefinite behavior" e' la risposta giusta,
Forse "undefined behavior" sarebbe meglio. :-)
EMG
--
EMAG Software Homepage:     
http://www.emagsoftware.it
The EMG's ZX-Spectrum Page: 
http://www.emagsoftware.it/spectrum
The Best of Spectrum Games: 
http://www.emagsoftware.it/tbosg


Beh, per quello che ha fatto, forse e' meno professionale ma piu' 
espressivo dire che " va a culo..." :)
Hai fatto una cosa che rappresenta la 'sporcatura' di quello che si fa 
modificando gli indirizzi di ritorno di una funzione per modificare 
indirittamente il flusso del programma; lo studio di ogni buffer 
overflow parte da lì, ma ripeto, te hai preso semplicemente una 
locazione di memoria e hai fatto delle strane alchimie ( ti sposti di 
pochi byte in pratica.... ).
Come dire, undefined behaviour
Ciao ciao

 Forse "undefined behavior" sarebbe meglio. :-)
Eh beh... forse si ;-)


 Forse "undefined behavior" sarebbe meglio. :-)
mea culpa =)
-- 
sbi


Ciao.
Bene, sei quello che si è avvicinato di più alla spiegazione corretta e
prendo spunto dal tuo reply per rispondere anche a tutti gli altri
(purtroppo non ho il tempo di rispondere individualmente ad ogni singolo
post, ma cercherò di essere esaustivo per tutti).
Ricordo che siamo OT, per tutti quelli che non sono interessati...
Come hai detto, ho modificato l'indirizzo al quale la funzione "fun()"
restituisce il controllo al "main()", in modo da restituire il controllo
all'istruzione "std::cout<<var<<'\n';" di fatto saltando l'esecuzione di
"var=0;".
Non capisco che cosa intendi dire con "sporcatura", ma forse è meglio
riparlarne alla fine di questo post...
Spiegherò più avanti che non c'è bisogno di fortuna nel manipolare questo
indirizzo ma solo di un po' di conoscenza di come funziona la gestione dello
stack del processo e un minimo di Assembly (più che altro per altri esempi
che posterò più avanti, se qualcuno è interessato, ma sarebbe utile una
piccola conoscenza dello stesso fin da subito). Dimenticavo che serve anche
sapere fare le addizioni... scherzo, ovviamente.
Per iniziare bisogna ricordare che esiste la cosidetta "convenzione delle
chiamate a funzione in stile C" che detta la gestione di basso livello delle
chiamate di funzione sotto molteplici aspetti, ma a noi interessano al
momento solo alcune cosine di base...
Ripeto che quanto sto per spiegare vale per qualsiasi programma in C/C++ su
qualsiasi sistema operativo ed entro certi limiti e con le dovute
considerazioni oer qualsiasi processore che sa cosa è uno stack
(praticamente tutti). Fate la prova voi stessi sul vostro ambiente, dopo
avere seguito le istruzioni che sto per scrivere... Dico che vale per tutti
gli ambienti perchè altrimenti un programma Standard C++ scritto per una
piattaforma non potrebbe essere compilato ed eseguito sulle altre.
Quando una funzione viene richiamata, il processo deve sapere da dove
ricominciare l'esecuzione del flusso chiamante. Per far sì che questa
informazione sia disponibile al rientro dalla funzione chiamata (diciamo
dalla nostra fun()), l'indirizzo della istruzione immediatamente successiva
a "fun();" è registrato sullo stack in modo automatico dalla istruzione
Assembly "call _fun". Alla locazione immediatamente successiva sullo stack
la funzione registra il valore contenuto sul registro %ebp di cui ora non ci
occuperemo.
Alla locazione ancora successiva viene fatto spazio per la nostra variabile
locale "int *ret". Con un po' di aritmetica dei puntatori abbiamo concluso
che l'indirizzo di prosecuzione nel main() si trova ad 8 bytes di distanza
(4 bytes, ovvero 32 bits per ogni locazione) dalla posizione della variabile
locale sullo stack. Quindi se vogliamo manipolare questo valore prendiamo
l'indirizzo di "ret" e aggiungiamo 8 bytes con "ret=(int *)&ret+2;".
Attenzione: aggiungiamo e non sottraiamo perchè lo stack su X86 cresce verso
le locazioni di memoria più basse, chiaro?
A questo punto non ci rimane che aggiungere all'indirizzo di memoria così
ottenuto un numero di bytes calcolati in modo da saltare l'istruzione
"var=0;" e andare direttamente alla stampa di "var" con il suo vecchio e
ancora attuale valore di "1".
Questa è la parte più complessa, bisogna sapere quanti bytes è lunga
l'istruzione "var=0;" in modo da sommarli a "*ret". Abbiamo due soluzioni.
La prima è conoscere così bene l'Assembly della nostra macchina da sapere
che questa istruzione occupa 7 bytes di memoria, ma mi sembra impraticabile
nella maggiore parte dei casi.
La seconda è praticabile da chiunque sappia utilizzare un disassemblatore
come quello embedded in GDB, o qualsiasi altro di vostra preferenza. Non è
difficile, almeno per riuscire nel nostro intento, seguitemi...
Prima di tutto compilate il programma con un semplice "g++ jump.c -o jump
-Wall", dopo avere scritto un numero qualsiasi come incremento di "*ret", ad
esempio scrivete "*ret+=6;". ho scritto "6" a caso ma tenendo conto del
fatto che l'istruzione che vogliamo saltare dovrebbe essere lunga tra i 5 e
gli 8 bytes, comunque questo numero non è importante.
Dopo la compilazione eseguite "gdb" o quello che preferite. Riporto la
sessione solo in parte, perchè il mio debuger si trova su un altra macchina
non collegata e devo copiare a mano. Aggiungerò dei commenti in stile C++
che ovviamente non sono presenti sull'output di "gdb".
myself@myhost /hack # gdb jump
...[cut]...
(gdb) disass main
Dump of assembler code for function main:
0x08048734 <main+0> push %ebp // la solita routine di entrata...
0x08048735 <main+1> mov %esp, %ebp // andate oltre...
...[cut]...
0x08048757 <main+35> call 0x804878e <_fun> // ecco la chiamata "fun();" come
potete vedere dentro <> vi fa sapere che si trova a 35 bytes dall'inizio del
main()
0x0804875c <main+40> movl $0x0, 0xfffffffc(%ebp) // questa è "var=0",
vogliamo che non venga eseguita, così guardando all'offset della prossima
istruzione ricaviamo il numero con cui incrementare "*ret"
0x08048763 <main+47> mov 0xfffffffc(%ebp), %eax // bene! avete visto? siamo
a main+47, quindi la precedente istruzione è lunga 7 bytes. Ora sappiamo che
dobbiamo ricompilare il programma con la riga "*ret+=6;" modificata in
"*ret+=7;". chiaro, no?
...[cut]...
End of Assembler dump.
(gdb) q
Spero sia tutto chiaro e spero di non avere fatto troppi errori. Purtroppo
mentre scrivevo questo post, ed ero già quasi alla fine, l'ho cancellato per
errore e il tempo che volevo dedicare si è accorciato parecchio per doverlo
riscrivere.
Spero di avere dimostrato che non si tratta di fortuna nel manipolare lo
stack a caso, ma che invece ci sono dietro calcoli ben precisi. Come dicevi
questa è la base per comprendere come fare l'exploit di un programma
attraverso la manipolazione sello stack, magari con un buffer overflow, e
non c'è niente di lasciato al caso.
Spero ancora di non avervi annoiato troppo.
Saluti,


 Ricordo che siamo OT, per tutti quelli che non sono interessati...
[CUT]
\o/
Per me è stato interessante,
anche se sono cose già fatte e rifatte (Credo un pò per tutti)
io no non ho mai messo in pratica però la teoria sul funzionamento la
conoscevo.
Sorge una domanda spontenea come devo scrivere il mio codice per
evitare questo ? .
Tempo avevo avevo letto in un articolo che il problema era originato non dal
codice sorgente ma
appunto dalla gestione dello stack...
_____________/ciao\______________

 [...] 
Tutto molto bello, ma allora mi spieghi perche' a me, compilando con
l'opzione "-O3", stampa "0" e poi esce con SegFault?


 
....credo perchè se prendiamo un programma che fa cose 'sporche' ( oddio 
non flammiamo, ma se ci mettiamo a girare per le locazioni di memoria 
cazzum canis qualcosa di sporco o di quantomeno 'normalmente inusuale' 
lo stiamo facendo ) allora probabilmente si esce fuori dal contesto 
delle cose ragionevolmente ottimizzabili ed il compilatore si trova ad 
ottimizzare il turco antico...
Rispondo qui a fabiomdf:
...intendo che hai sporcato il tutto perchè in questo ng presentare un 
codice del genere vuol dire nient'altro che far venire i brividi, io ho 
subito visto "smashin'da stack 4 fun'n'profit" ma lungi da me intendere 
che volessi fare una cosa [ senza spocchia per amor di Dio ] tipo 
"Indovinate cos'è !".
Ma soprattutto una domanda, anzi due, tralasciando quello che per me è 
interesse ma non so se sia oggettivamente tale:
1) perchè hai speso tutto questo tempo a scrivere sta cosa quando paper 
di questo tipo in giro ce ne sono a bizzeffe;
2) perchè in un ng sul C++ ???
Ti saluto !

 ha scritto nel messaggio
news:yb%vd.43827$C94....@tornado.fastwebnet.it...

Sta diventando troppo questo post.. (byte sprecati a mio avviso)
passo e chiudo.

 appunto dalla gestione dello stack...
Si tratta di 'buffer overflow', in rete trovi materiale a quintalate.
E cmq: il problema e' che spesso si scrive codice fatto male. 
Ovviamente, lo stack e' gestito (piu' o meno direttamente), dal codice.
-- 
Mat/tia Belletti - Graduate student @ 
cs.unibo.it
ICQ: 33292311    - email: 
mbel...@cs.unibo.it
IRC: RedGlow     - site(s): 
http://mbellett.web.cs.unibo.it/
Jabber: RedGlow  - Linux registered user 299762 @ machine 213003


Il problema rimane: in C++ standard, quello che hai scritto tu e' un 
undefined behaviour, senza se e ma. Che poi su varie architetture, sotto 
certe condizioni, faccia la cosa buffa che dici te... beh, non e' nulla 
di strano. Essendo un undefined behaviour, il programma poteva stampare 
'0', '1', 'hello world' o causare la fine del mondo.
Come anche Bruno ti ha detto: perche' parlare quindi qui di un fatto 
dipendente dall'architettura? Le convenzioni di chiamata C, per quanto 
abbiano questo nome, con lo standard C non c'entrano nulla (e tanto meno 
con il C++).



Infatti il contesto degli stessi buffer overflow è 'valido' soltanto in 
certe condizioni, su certi sistemi operativi e *sempre* in scenari nei 
quali il linguaggio utilizzato è il C, al massimo affiancato dai 
cosiddetti *glue script* come perl o puthon.
Saluti.

 evitare questo ? .
Uno dei metodi che vengono usati per gestire i b.o. è quello che viene
chiamato del "canarino", o almeno questo è quello + semplice da implementare
ma non è molto sicuro perchè esiste un metodo per poter far andare in b.o.
l'applicazione anche usando il "canarino".
In rete trovi tanti esempi su questo "tipo" di programmazione.
Ciao
Vinsanto

 l'opzione "-O3", stampa "0" e poi esce con SegFault?
Perchč l'offset da aggiungere a *ret che hai trovato disassemblando il
main() del programma compilato senza ottimizzazione č quasi sempre diverso
da quello che trovi disassemblando lo stesso programma compilato con diversi
flags di ottimazione.
Quindi se vuoi fare saltare il flusso del processo ad una qualsiasi
locazione devi prima disassemblare il codice compilato con gli stessi flags
che userai alla prossima compilazione.
In sintesi devi ricontrollare che l'istruzione che vuoi eseguire al ritorno
dalla funzione sia ancora 7 bytes piů avanti e probabilmente non lo č piů.
Ciao,


 2) perchè in un ng sul C++ ???
Perchè una consapevolezza di come funzionano queste cose può rendere le
nostre tecniche di programmazione più attente ai pericoli di un uso
patologico del codice e quindi il nostro prodotto C++ più affidabile e
robusto (e non credo sia cosa da poco).
Comunque, visto che a nessuno sembra interessare trattare questi argomenti,
mi asterrò dal procedere oltre.



[CUT]
A mio avviso è interessante come argomento (visto dal punto di vista
espresso nel tuo ultimo post)
Però ci sono da dire alcune cose:
1) questo è ng di c++ e non C e le tue sono tecniche basate prevalentemente
sul linguaggio c
2) secondo me hai sbagliato completamente il modo in cui ti sei proposto
(partendo dal topic)
    in questo ng si studiano soluzioni si cerca di aiutare chi a problemi
+o- complicati +o seri
    ma gli indovinelli :-)  poi che ci siano persone più o meno competenti è
un'altro discorso.
    Si parte dal fatto che i post sono per tutti e  possono leggere /
rispondere tutti.
3) Da parte mia c'è interesse a questo genere di argomenti il discorso
sicurezza penso debba essere sempre compreso
    fra le "spese" di un progetto
Il mio è un tono assolutamente tranquillo volevo commentare solo le tue
ultime "scritte" io le ho interpretate come se dicessi
"questi non hanno voglia di imparare cose se stanno per i fatti loro e non
vogliono condividere"
Buon lavoro a tutti :-) è Natale e siamo tutti più bravi...
Ciao :-)

 Ciao :-)
Buon lavoro anche a te e buon divertimento con il C++,


 Non č cosě:
Sulla mia (un Mac) stampa 0.
-- 
RiK0
Non credo negli schemi non vincenti.


Scusami, evidentemente non sono stato molto chiaro...
Prima di tutto ho specificato che si applica a tutti gli Intel X86 ( dal
80386 fino all'ultimissimo PentiumIV ), mentre gli altri processori hanno
probabilmente un modo diverso di gestire stack e %eip (instruction pointer)
e di sicuro hanno una lunghezza in bytes delle istruzioni diversa dagli
Intel.
In secondo luogo non puoi prendere il codice così come è, compilarlo e
aspettarti che faccia quanto ho spiegato se non calcoli il corretto offset
della istruzione alla quale vuoi saltare. Devi prima disassemblarlo come ho
spiegato, oppure conoscere già da te stesso la dimensione delle istruzioni,
e calcolare l'offset che è molto probabile che non sia '7' come nel mio
caso.
Ripeto che su X86 il "ret = (int *)&ret + 2" è sempre correto, qualsiasi sia
il sistema operativo, il compilatore e le ottimizzazioni dello stesso;
invece l'offset può variare e quindi bisogna calcolare, disassemblando, il
valore da sommare a "*ret", come nel caso di "*ret+=7;". Questo '7' è
l'offset che si deve calcolare sulla tua macchina (x86) e col tuo
compilatore. Ti consiglio di rileggerti il post dove si spiega come
calcolare l'offset con l'ausilio di gdb.
Comunque l'esempio che ho riportato è molto banale ed e ormai è sfruttato
più che altro a fini didattici per introdurre meccanismi molto più raffinati
e complessi... Anche se a suo tempo, e con le modifiche del caso, è servito
a molti programmatori per introdurre delle back-doors in progetti molto
ampi, dove due operazioni su un puntatore possono passare inosservate
soprattutto se nascoste dentro una innocua funzione che non prende parametri
(ma potrebbe) e non restituisce nulla (come ad esempio una routine di stampa
in cui il codice malevolo è attivato a condizione che una qualche variabile
globale abbia un certo valore).
Saluti,
fabiomdf


 globale abbia un certo valore).
Interessante, non ci avevo pensato...


 Intel.
In particolare %eip non esiste (non serve, abbiamo 32 registri
generici), lo stack (se ricordo bene) 'cresce all'inverso rispetto x86'
e le istruzioni sono a lunghezza fissa 32 bit.
Detto questo insisto. Non devi venire a spiegarmi *perche`* non
funziona. E` ovvio che non funzioni, io sto solo dicendo che *non*
funziona.
Avere sotto mano un PPC e` spesso comodo quando si deve lavorare a basso
livello scrivendo pero` codice 'standard'. Per dirne una e` una macchina
abbastanza comune che lavora in big-endian, e quindi pone un comodo test
per vedere se la serializzazione di un oggetto e` fatta come si deve
(ovvero in modo che la libreria si comporti in modo consistente su
architetture differenti).
E` inoltre abbastanza facile mostrare cosa si intende quando si dice che
reinterpret_cast spesso non e` portabile e cosi` via.
Questo e` il senso del mio post. Un esempio. Solo un esempio.


 l'opzione "-O3", stampa "0" e poi esce con SegFault?
Per quello che mi č dato sapere, il compilatore quando vede il livello di ottimizzazione 
decide se conviene creare una funzione vera e propria (come di solito accade) oppure se 
inserire direttamente nel main il codice della funzione (fun() in questo caso).
In questo modo non esiste nessun indirizzo di ritorno perchč il record di attivazione della 
funzione fun() non viene proprio creato! Cosě penso che si motiva il segmentation fault
Saluti


Ciao.
Si. E' corretto. Anche il mio compilatore (gcc-3.4.1) non crea la funzione
fun() se il flag -O3 è attivato. Si può vedere disassemblando il main().
Comunque è sufficente una funzione appena più complessa (come nei casi reali
di exploits) per ridurre le possibilità di essere messa inline praticamente
a zero.


Ehm...
Vedo che in pochi se ne sono accorti, ma nel mio post c'era un po'
d'ironia...
Ringrazio chi mi ha risposto, ma piu' o meno avevo gia' un'idea della
ragione del "Segmentation Fault"...
Ho scritto che "non capivo" semplicemente perche' piu' sopra si affermava
che il codice in questione funziona per qualsiasi compilatore, qualsiasi
sistema operativo e (quasi) qualsiasi processore, negando l'evidenza dei
fatti, e cioe' che il codice e' un semplice "undefined behaviour" (stavolta
lo scrivo giusto :-) ) e che basta cambiare una direttiva di compilazione
perche' non funzioni piu'.
In seguito Fabio ha corretto un po' il tiro, dicendo che "il codice
funziona" significa che bisogna scegliere processore, sistema operativo,
compilatore (la sua versione e vari flag da usare in compilazione) e allora
disassemblando si riesce a trovare un valore magico (7 nel suo caso) e
fargli fare quello che si vuole che faccia...
Wow... beh non ne vedo tanto l'utilita' pero' su quest'ultima cosa sono
daccordo. 
Ad ogni modo e' quanto di piu' lontano dal C++ standard che si possa
pensare...
Chiudo qui.
Francesco.



Gli exploits del codice C++ sono una cosa molto seria. Stack Overflows,
Format Strings Overflows, Heap Overflows sono 'exploitati' (passatemi il
termine) di continuo sulle più comuni applicazioni lato server e client che
utilizziamo (ad esempio con le Shellcode's Injections). E il risultato non è
così tanto "undefined" per il povero amministratore di sistema. Di solito
l'intruso riesce ad ottenere una bella shell con tutti i permessi di
superutente, e cosa ci sia di "undefined" in questo non riesco proprio a
capirlo.
Ovviamente ogni exploit per funzionare deve essere calibrato sulla macchina
per la quale il processo sarà compilato. Se non fosse possibile fare questo
tipo di calcoli (e considerazioni) praticamente su qualsiasi sistema
operativo e processore non ci sarebbero gli exploits che popolano le liste
di sicurezza della nostra distribuzione preferita di GNU/Linux oppure non ci
sarebbero i continui aggiornamenti automatici di MS-Win a copertura delle
sempre più numerose falle all'apparato di "sicurezza" (se così si può dire
di Windows).
Per evitare di far cadere il sistema in mano a chiunque abbia la conoscenza
di come fare, e sempre più spesso anche in mano a chi questa conoscenza non
ce l'ha (script-kiddies) esistono delle vere e proprie tecniche di
"Auditing" del codice, sia sorgente che binario (si, proprio binario, per
chi volesse pensare che basta non far vedere il codice sorgente per
assicurarsi la tranquillità), che fanno uso di conoscenze e strumenti
disponibili a tutti i programmatori che capiscono cosa succede dietro le
quinte di un programma compilato.
Tramite questo "Auditing" del codice si scoprono le vulnerabilità del nostro
prezioso codice C++, in modo da preparare delle patches da inviare ai nostri
clienti. Queste patches sono proprio come quelle che la MS ci invia più o
meno quotidianamente, o che include nelle versioni più aggiornate dei
Service Packs. Questo lavoro è fatto in tutte le software house che hanno
sviluppato codice non banale e che viene fatto pagare a colpi di migliaia di
Euro a singola licenza (ma anche per l'open source). E chi fa questo lavoro?
Proprio i programmatori C++ più esperti, quelli che hanno un livello di
conoscenza profonda di come opera il linguaggio (implementazione), del
sistema operativo e del processore (questi due ultimi skills non devono
essere necessariamente ampi come il primo, ma almeno focalizzati su alcune
parti rilevanti al discorso).
Quindi la scoperta delle vulnerabilità e la creazione delle patches attiene
al programmatore C++. Presuppone ulteriori skills che vanno oltre la
capacità di scrivere codice C++, ma non prescinde da una completa e
approfondita conoscenza del linguaggio.
Il prodotto (la patch) di questa indagine è considerata così importante che
spesso alcuni programmi che si aggiornano in automatico con una connessione
ad Internet, propongono gli aggiornamenti di sicurezza in liste differenti
dagli aggiornamenti delle funzionalità chiedendo all'utente di installare
almeno le prime e al più presto possibile.
