Archivio storico · Reverse engineering · Spiegazione dettagliata

«How to Crack» — Lezione 2: strumenti e trucchi del mestiere

Una guida alla lettura della seconda lezione di +ORC: come orientarsi in un programma sconosciuto, quali strumenti usava la comunità DOS e quali idee metodologiche restano valide.

In breve

La seconda lezione è un manuale di orientamento. Non risolve un singolo caso, ma insegna come non perdersi quando si analizza un programma sconosciuto: leggere la memoria, scegliere un debugger adatto, usare breakpoint mirati invece di procedere istruzione per istruzione, distinguere codice e dati, cercare i messaggi mostrati all’utente, osservare quali interruzioni vengono intercettate. Verso la fine mostra un esempio di protezione basata sulla risposta a una domanda del manuale.

La pagina spiega i concetti e il ragionamento. Non ricopia procedure operative per modificare un programma specifico e non incoraggia l’uso illecito delle tecniche descritte.

1. «Perso nei boschi di codice»

L’autore parte da un’esperienza reale: quando si interrompe un programma con un debugger, spesso ci si ritrova in un punto che non si riconosce, magari fuori dal programma stesso, in un driver o in un programma residente. Prima di analizzare occorre capire dove si è.

Gli strumenti utili devono mostrare, secondo il testo:

L’idea è quella di una mappa: senza sapere quali regioni appartengono al programma e quali al sistema, ogni osservazione può essere interpretata male.

Profiler e gerarchia delle chiamate

Prima di studiare in dettaglio il codice, il testo consiglia due strumenti di orientamento. Un profiler indica quali parti vengono davvero eseguite: se una funzione non partecipa al comportamento che interessa, è inutile analizzarla. Un grafico della gerarchia delle chiamate mostra quali funzioni ne richiamano altre, dando una visione d’insieme.

2. Il debugger come strumento principale

La lezione sostiene che il debugger è «l’unico strumento di cui si ha davvero bisogno». Deve poter operare con moduli residenti e interruzioni; per questo il semplice debug.com di DOS è giudicato troppo limitato per casi complessi. L’autore elenca alcuni debugger professionali dell’epoca.

Il testo aggiunge che molte copie dei programmi citati circolavano senza autorizzazione sul Web. Questo è un punto da separare dal valore storico: la presenza di copie diffuse non le rende lecite. Per una lettura didattica, interessa la categoria degli strumenti, non il suggerimento dell’autore di procurarsene copie senza autorizzazione.

La nota su SoftICE ha invece un interesse storico concreto: nasceva come debugger per DOS alla fine degli anni Ottanta, sviluppato da NuMega, e poi evolse verso Windows. Nota storica su SoftICE.

3. Il metodo «zen»: pensare prima di tracciare

Il tono è colorito, ma l’indicazione metodologica è chiara: quando si sospetta una protezione, non buttarsi subito in tecniche invasive. Se non si ha già un’idea, si rischia di perdere dettagli importanti e di restare sepolti da migliaia di istruzioni.

L’esecuzione a passo singolo è «costosa»: richiede tempo e produce troppi dettagli. Conviene invece procedere per approssimazioni successive:

  1. formulare un’ipotesi su quale parte del programma sia coinvolta;
  2. piazzare punti di arresto che riducano il problema;
  3. raccogliere una cronologia dell’esecuzione;
  4. usare il passo singolo soltanto quando si è vicini all’obiettivo.

Le domande da farsi prima di ogni breakpoint

Il testo ne propone tre: che cosa mi dirà questa interruzione?, che cosa dovrò sapere quando si fermerà?, il mio approccio è il più semplice e diretto? Sono domande valide per qualsiasi attività di debugging, anche su software proprio.

Le «briciole di pane»

Se non si registra una cronologia fin dall’inizio, può essere necessario ricominciare. È l’equivalente di annotare il percorso durante una passeggiata in un bosco.

4. Interruzioni software come punti di osservazione

Fermare il programma quando richiama certe interruzioni dà una vista d’insieme di ciò che sta facendo. Nel DOS, l’interruzione 21h raccoglie moltissimi servizi di sistema: file, memoria, tastiera, orologio. Altre interruzioni del BIOS gestiscono disco e tastiera.

Un breakpoint troppo generico fornisce rumore. Per questo il testo parla di breakpoint valutativi: ad esempio fermarsi su ogni chiamata della 21h, escludendo alcune funzioni frequenti e poco interessanti, come la lettura dell’ora o il controllo dello stato dell’ingresso. È un uso intelligente del filtro: non osservare tutto, ma tutto ciò che è informativo.

5. Funzioni, dati e «tentacoli»

Capire un programma significa spesso seguire una catena. Per comprendere una funzione bisogna capire quelle che chiama, poi quelle richiamate da queste, fino alle foglie. L’autore la chiama inseguire i «tentacoli». Per le funzioni esiste una lista implicita da seguire; per i dati no.

Per una variabile, occorre chiedersi quali funzioni la leggono e quali la scrivono. Si tratta dell’analisi dei riferimenti incrociati, che oggi i disassemblatori moderni producono in modo molto più comodo. Il testo cita strumenti dell’epoca, ma la logica resta la stessa.

6. Dove si trovano le chiamate di sistema

Se un programma è scritto in un linguaggio di alto livello, le chiamate al sistema passano di solito da funzioni di libreria comuni: tutte le chiamate della 21h partono dallo stesso punto. Se si individua quel punto, si può interrompere l’esecuzione al ritorno e risalire alla funzione chiamante.

L’autore propone anche un’euristica: una zona scritta in assembler dentro un programma altrimenti prodotto da un compilatore potrebbe essere stata aggiunta in un secondo tempo, e le protezioni vengono spesso inserite alla fine dello sviluppo. È una buona intuizione da usare come indizio, non come regola. Un’ottimizzazione prestazionale in assembler non è necessariamente una protezione.

7. Cercare i messaggi: stringhe ASCIIZ

Molti programmi mostrano un messaggio quando negano un’operazione. Il testo suggerisce di cercare le sequenze di caratteri leggibili nei file binari. Una stringa terminata da zero, come quelle usate dal C, è un punto di partenza utile. Non tutte le stringhe DOS sono però terminate da zero: alcuni servizi usano altri delimitatori o una lunghezza esplicita. Individuata una stringa, trovato dove viene richiamata, si risale al codice che decide di mostrarla.

La ricerca dipende da come il programma scrive sullo schermo: tramite servizi DOS, tramite il BIOS video oppure scrivendo direttamente nella memoria video. Nell’ultimo caso, un controllo sugli accessi alla zona corrispondente può individuare chi sta scrivendo il testo. Il concetto ricorda un problema più generale: partire da un effetto visibile e risalire alla causa.

8. Quando dati e codice sono mescolati

Alcune interruzioni vengono usate con dati inseriti subito dopo l’istruzione. Se il disassemblatore continua a leggere come codice ciò che in realtà sono dati, i risultati diventano senza senso. L’autore suggerisce di sospettare quando le istruzioni successive a una chiamata di interruzione non hanno logica e di esaminare come la routine modifica l’indirizzo di ritorno.

È un esempio di mescolanza di codice e dati: una sequenza che il disassemblatore tenta di interpretare come istruzioni può in realtà contenere parametri. Non va confusa automaticamente con codice auto-modificante o autoreferenziale.

9. I vettori agganciati

Confrontare i vettori di interruzione prima e dopo l’avvio di un programma rivela quali intercettazioni ha installato. Se, ad esempio, un programma aggancia la tastiera poco prima di una verifica, le zone sospette si riducono.

Il testo discute tre approcci: guardare le chiamate che leggono e scrivono i vettori, controllare direttamente le modifiche alla tabella e sospettare un gestore hardware quando una modifica avviene senza passare per i servizi di sistema.

Piccoli calcoli utili

Ogni vettore occupa 4 byte e la tabella parte all’indirizzo 0000:0000. Per trovare l’indirizzo del vettore si moltiplica il numero dell’interruzione per 4: per la 21h, 33 × 4 = 132, cioè 84 in esadecimale, quindi 0000:0084. Per la 63h, 99 × 4 = 396, cioè 018C, quindi 0000:018C.

Anche la conversione segmento:offset è spiegata bene: l’indirizzo fisico si ottiene moltiplicando il segmento per 16 e sommando l’offset. Nell’esempio del testo, 6049:891C e 6109:7D1C corrispondono entrambi all’indirizzo fisico 68DAC. Il calcolo è corretto: due coppie diverse possono indicare la stessa cella di memoria.

10. Strumenti storici citati

La lezione elenca utility per mappare la memoria, scaricarne il contenuto, analizzare file eseguibili e osservare le interruzioni. Il suo valore storico è il quadro della cultura tecnica dell’epoca: strumenti piccolissimi, spesso di pochi centinaia di byte, scritti da appassionati.

Per trovarli suggerisce Archie, un servizio nato nel 1990 per indicizzare i nomi dei file presenti sui server FTP pubblici, interrogabile anche per posta elettronica; fu considerato il primo motore di ricerca di Internet, antecedente al Web. Approfondimento storico su Archie. Poi consiglia di imparare a usare Yahoo, il tipo di directory web che all’epoca ordinava i siti per categoria.

Un’altra indicazione è però problematica: l’invito a «rubare» il codice dei migliori programmi e a modificarli senza riguardo. Una cosa è imparare dalle idee di un software, un’altra è appropriarsi del codice o delle opere altrui senza i diritti necessari.

11. L’esempio finale: la domanda del manuale

L’esempio riguarda un gioco automobilistico del 1989 che chiedeva, all’avvio, una risposta presa dal manuale cartaceo. Era una forma di protezione per copia detta manual protection o «paper protection»: il programma si poteva copiare, ma senza il manuale non si conosceva la risposta. Scheda storica del gioco.

L’autore contrappone due approcci:

La differenza è tra partire dai dati inseriti e partire dal comportamento atteso della routine. Il secondo approccio riduce lo spazio di ricerca quando si riconosce già il tipo di schema.

La logica della verifica

Il programma non conserva la risposta in chiaro, ma una forma codificata: per ogni cifra applica una trasformazione al dato memorizzato, la confronta con ciò che l’utente ha digitato e si interrompe se c’è una differenza. Dal punto di vista della sicurezza è fragile perché la risposta si può ricavare da dati che il programma custodisce e dalla stessa regola usata per verificarla.

Il testo mostra anche come neutralizzare il confronto. Qui preferisco non riportare la sequenza dei passaggi: il valore didattico sta nel capire la debolezza concettuale, non nel diffondere una modifica per un prodotto specifico.

12. «Perché si crackano i programmi»: il passaggio ideologico

L’ultimo paragrafo smette di essere tecnico. L’autore afferma di agire contro la società e contro le leggi, di considerare liberi tutti i programmi protetti e di disprezzare denaro, governi, televisioni e opinione pubblica. Sono posizioni dichiarate, non spiegazioni neutrali.

Vanno notate due cose. La prima è la tensione interna: dice che i cracker non agiscono per denaro, ma poche righe prima ammette che a volte accettano incarichi e sono costosi. La seconda è che il passaggio confonde ragioni diverse: la curiosità intellettuale per i meccanismi, la critica al modello commerciale del software e la violazione di licenze o norme sono questioni distinte.

Una persona può studiare un meccanismo di protezione senza ritenere lecito rimuoverlo da programmi altrui o distribuire copie. Il documento ha interesse come testimonianza di una cultura giovanile e conflittuale di metà anni Novanta, non come giustificazione giuridica o etica.

13. Che cosa si può imparare oggi

Glossario

Breakpoint
Punto di arresto: il debugger ferma il programma quando viene raggiunta una condizione.
MCB
Blocco di controllo della memoria usato da DOS per descrivere le aree allocate.
PSP
Area che DOS crea all’inizio di ogni programma, con informazioni sul suo avvio.
TSR
Programma residente che rimane in memoria dopo la conclusione apparente.
Segmento:offset
Modo di indicare un indirizzo nei processori x86 in modalità reale.
ASCIIZ
Stringa di caratteri terminata da un byte nullo.

Fonte e verifiche

Fonte primaria: ORC_2.DOC, copia fornita da Luigi Loreti. Le osservazioni critiche sono commenti aggiunti; i calcoli sugli indirizzi sono stati ricontrollati. Non sono stati eseguiti né modificati i programmi citati.

Leggi il documento originale: ORC_2.DOC