|
||
08/10/99 |
by "Int19" |
|
Published by Quequero |
||
Use the Force Luke Obi Wan |
Complimenti anche stavolta Int19, hai fatto un tutorial davvero impeccabile e spiegato nei più minimi dettagli, bravo |
Quanta legna potrebbe rodere un rroditore Guybrush. |
UIC's form |
|
UIC's form |
Difficoltà |
|
Questa volta avremo a che fare con codice automodificante, lo crackeremo e creeremo un KeyMaker.
UIC
Lezione 3
Codice SMC
Written by Int19
Introduzione |
Ciao a tutti, qui e Il.Socio che scrive,
premetto che trattandosi di codice auto-modificante (SMC) un approccio "live" e' quantomeno essenziale, per non lasciarsi ingannare da come il prog. si presenta "in apparenza", quindi scordatevi Win32Dasm e tenete SoftICE sottomano.
Leggete la lezione di Quequero riguardante proprio questo corso per degli approfondimenti relativi al SMC.
Inoltre tutto il codice presentato in questo tute, sara' riferito al codice che viene visualizzato da SoftICE.
Tools usati |
Il classico e indispensabile SoftICE 4.0, Hiew 6.16 per patchare l'exe in modo da accettare come valida qualsiasi coppia nome/seriale e il Borland TurboC per il KeyMaker.
Inoltre ho utilizzato Base Calculator http://biosys12.ee.unsw.edu.au/personal/software/basecalc/ per il calcolo del serial.
URL o FTP del programma |
Beh, se state leggendo questo credo che tutti lo conosciate ormai : http://quequero.cjb.net seguite i link verso il Terzo Corso e troverete sicuramente il file di esempio
Notizie sul programma |
Il programma presenta due box nei quali inserire nome e numero di serie, ed un bottone per la registrazione, inserendo un
Serial errato non compare nulla, inoltre ce un bottone about che a quanto pare non fa nulla ( Quequero che cera per caso qualche routine imboscata che mi e sfuggita ? J ) Ecco cosa mi ero dimenticato, il pulsante About, grazie int19 per avermelo ricordato NdQue
Inserendo un serial corretto il prog. mostra un messaggio di congraturazioni.
Essay |
Obiettivi:
1) Trovare il codice corrispondente al nostro nick
2) Crackare il programma in modo da ottenere codici sempre validi
3) Analizzare l'algoritmo di generazione del seriale
4) Scrivete un key-maker per il programma
Spero che Que non mi flagelli (splat..splat..splat :)) NdQue) ma personalmente non mi sono attenuto rigorosamente a questo ordine,
quindi vi presentero' l'ordine che a me e' venuto piu' naturale quando ho crackato il prog. di esempio.
1) Individuare la protezione
2) Crackare il programma in modo da ottenere codici sempre validi
3) Trovare il codice corrispondente al nostro nick
4) Analizzare l'algoritmo di generazione del seriale
5) Scrivere un key-maker per il programma
1) Individuare la protezione
Per conseguire il primo obbiettivo dobbiamo ovviamente come prima cosa individuare la protezione, ovvero il luogo in cui il serial che andremo ad immettere viene confrontato con quello calcolato.
Iniziamo con il piazzare i classici breakpoint "BPX GetDlgItemTextA" e "BPX GetWindowTextA", inseriamo un nome (Int19) e un serial (12244331) ed appena pigiamo il bottone di registrazione arriva SoftICE in nostro aiuto, come potrete notare premendo F5, la funzione viene richiamata per ben quattro volte, ma solo alla quarta viene letto il seriale, le prime tre si riferiscono sempre al nome, e piu' precisamente il nostro codice seriale appena immesso, verra' salvato nella locazione 4020AF (come si deduce dai parametri passati alla funzione).
Ora la logica suggerisce di piazzare un bel BPM 4020AF 4020AF+8 rw, ma se date ancora un'ulteriore sguardo al codice che segue, vedrete che il serial inserito viene immediatamente manipolato...
:0040132D push 00000018
:0040132F push 004020AF <--- Locazione nella quale sara' salvato il serial inserito
:00401334 push dword ptr [00402050]
* Reference To: USER32.GetWindowTextA, Ord:0000h
|
:0040133A Call 0040165B
:0040133F xor ecx, ecx <--- Azzera la posizione per analizzare il primo char del serial
:00401341 xor eax, eax
:00401343 mov al, byte ptr [ecx+004020AF] <--- Preleva un char dal serial inserito usando ecx
come contatore
:00401349 xor al, 81
:0040134B xor al, 40
:0040134D mov bl, byte ptr [ecx+004020C7] <--- Preleva un char dalla locazione 4020C7 usando ecx
come contatore
:00401353 cmp al, bl <--- Esegue il confronto tra il char del serial e il char
della locazione 4020C7
:00401355 jne 00401170 <--- e salta se sono diversi
:0040135B inc ecx <--- incrementa il contatore (fino a qui sono stati
analizzati ecx chars del serial)
:0040135C cmp ecx, 00000004 <--- Se non sono stati ancora analizzati i primi 4 chars
:0040135F jne 00401341 <--- allora salta
:00401361 nop
:00401362 nop
Il ciclo qui sopra (da 401343 a 40135F) esegue un confronto ad uno ad uno dei primi 4 chars del serial opportunamente manipolati, con i primi 4 chars della locazione 4020C7 (nel mio caso questa locazione contiene i seguenti valori F9 F3 F5 F1 3A 34 essi sono certamente ricavati da una codifica del nome inserito poiche' variano al variare del nome, vedremo in seguito) e se tutti i quattro confronti della linea 401353 hanno avuto successo allora si continua alla linea 401361 (Se ne deduce che il serial dovra' avere almeno 4 chars).
Per forzare il successo di questo check e' sufficiente sostituire il cmp al, bl con un cmp al, al
:00401363 mov esi, 004015C3
:00401368 mov edi, 00401374
:0040136D mov ecx, 00000049
:00401372 repz movsb
Le linee qui sopra servono per poter sovrascrivere 49hex bytes della zona di codice a partire dalla locazione 401374 con il codice presente alla locazione 4015C3, infatti se lo aprite con Win32Dasm osserverete il codice seguente...
:00401374 mov eax, 00000008
:00401379 xor edx, eax
:0040137B mov bl, byte ptr [ecx+0040209B]
:00401381 cmp edx, dword ptr [0040209B]
(NOTA: Essendo questo un esempio e trattandosi di zone di codice relativamente piccole in un codice complessivo di dimensioni esigue, il reversing non risulta particolarmente ostico e fortunatamente Que non ha voluto renderci le cose troppo difficili, purtroppo pero' non credo vi capitera' mai ad aver a che fare con codice di 8kb. Lascio a voi immaginare le potenzialita' del SMC quando implementato in un bel eseguibile da 2Mega e magari con qualche parte crittografata qua e la. Vi assicuro che fa girare altamente le palle il fatto di aspettarsi un certo comportamento dal programma e rimanerne delusi)
Ma dopo l'esecuzione runtime della linea 401372 il codice si e' "magicamente" trasformato in questo modo...
:00401374 mov ecx, 00000004 <--- I primi 4 chars sono gia' stati analizzati
:00401379 xor eax, eax <--- Credo non sia indispensabile
:0040137B mov al, byte ptr [ecx+004020AF] <--- Carica il prossimo char dal serial immesso
(dal 5° char in poi)
:00401381 cmp ecx, dword ptr [0040210F] <--- Se ha analizzato lunghezza_nome+1 chars
:00401387 je 004013A5 <--- Allora salta a MessageBoxA "Successo"
:00401389 xor al, 82 <---| Esegue alcune
:0040138B shl al, 02 | manipolazioni sul char
:0040138E ror al, 02 <---| corrente
:00401391 mov bl, byte ptr [ecx+004020C7] <--- Lo confronta con un char del nome codificato
:00401397 cmp al, bl <--- della locazione 4020C7 usando ecx come contatore
:00401399 jne 00401170 <--- e salta se sono diversi
:0040139F inc ecx <--- passa al prossimo char
:004013A0 cmp ecx, 00000008 <--- se non ha ancora analizzato 8 chars
:004013A3 jne 00401379 <--- allora continua il ciclo alla linea 401379
* Referenced by a Jump at Address 004015D6
|
:004013A5 push 00000020
* Possible StringData Ref ->"SMC - - by Quequero for UIC"
|
:004013A7 push 00402061
* Possible StringData Ref ->"Serial esatto....Bravo!!!!"
|
:004013AC push 00402113
:004013B1 push 00000000
:004013B3 call USER32!MessageBoxA
:004013B8 jmp 00401170
Osservate che la locazione [40210F] contiene la lunghezza del nome inserito + 1 quindi... se e' stato inserito un nome di 3 chars il check della linea 401381 sara' verificato al primo ciclo (poiche' inizialmente ecx=4) ed il prog. saltera' all'indirizzo 4015D6 che come potete vedere mostra il messagebox di successo. Da questo deduciamo che il serial da inserire dovra' essere piu' lungo di un char rispetto alla lunghezza del nome.
Se forziamo il confronto della linea 401397 con cmp al, al potremo finalmente vedere il MessageBoxA...
Nel mio caso la locazione 4020C7 contiene i seguenti valori F9 F3 F5 F1 3A 34 quindi la linea 40137B con [ecx+4020C7] fa riferimento al valore 3A inizialmente, poi 34 al secondo ciclo ed esce al terzo ciclo poiche' ecx=6 ed e' uguale a lung_nome+1.
Bene, ora abbiamo individuato la protezione e patchato il prog. in modo da ottenere seriali sempre validi...PERO' se ora premiamo nuovamente il pulsante di registrazione non comparira' piu' alcun messaggio di congraturazioni, questo a causa del SMC di Que, in pratica il prog si e' "Aggiustato" e il cmp al,al che noi avevamo inserito e' nuovamente tornato cmp al,bl (Ecco un esempio di quando ho detto che il prog. non si comporta come ci si aspetterebbe)
Adesso possiamo passare al punto successivo...
2) Crackare il programma in modo da ottenere codici sempre validi
Come abbiamo visto il prog. si "corregge" o meglio si auto-modifica appunto...
Come possiamo procedere ? Dunque, l'idea e' quella di individuare le locazioni di memoria "sorgente" dalle quali vengono copiati i bytes che vanno a sovrascrivere le nostre locazioni incriminate, modificarle, e incrociando le dita sperare che le cose non siano piu' complicate (ad esempio le locazioni "sorgente" potrebbero a loro volta essere modificate prima di essere copiate, vi dico subito che non e' il nostro caso quindi rilassatevi)
Il codice che vi presento qui' sotto e' il codice che precede la chiamata alla GetWindowTextA che legge il seriale, ed ecco li la locazione "sorgente" 4014E5.
Esso copia 22hex bytes da 4014E5 a 40133F (sovrascrivendo il nostro primo check)
:0040131C mov esi, 004014E5
:00401321 mov edi, 0040133F
:00401326 mov ecx, 00000022
:0040132B repz movsb
Quindi andando alla locazione 4014E5 troviamo la routine di check dei primi 4 chars e ora possiamo cambiare la locazione "sorgente"
:004014F9 cmp al, bl
con
:004014F9 cmp al, al
La prossima volta che verra' eseguito il codice SMC verra' copiato il codice da noi gia' patchato.
Procedendo in modo analogo per il secondo check (che controlla dal 5° char in poi)
:00401363 mov esi, 004015C3
:00401368 mov edi, 00401374
:0040136D mov ecx, 00000049
:00401372 repz movsb
Andiamo alla locazione 4015C3 e cambiamo la locazione "sorgente"
:004015E6 cmp al, bl
con
:004015E6 cmp al, al
Ora premendo il pulsante di registrazione piu' volte il progr. da sempre esito positivo.
Modificando il programma con un editor esadecimale le modifiche saranno permanenti ed il programma accettera' qualunque coppia nome/seriale come esatta. Sicuramente si sarebbe potuto crackare in altri modi, ma questo e' quello che personalmente mi e' sembrato il piu' "elegante" e meno invasivo.
3) Trovare il codice corrispondente al nostro nick
Bene, per questa parte vi consiglio di munirvi di calcolatrice e del codice che ho presentato poco sopra (mi riferisco a quello che parte da 40133F ovviamente)
Io ho inserito "Int19" come nome ed ho ottenuto i seguenti valori alla locazione 4020C7: F9 F3 F5 F1 3A 34
Ed ora diamo un'occhiata al primo check (quello relativo ai primi 4 chars del serial)
Indichero' con n_serial l'n-esimo char del seriale immesso quindi 1_serial si riferira' al 1°char del seriale 2_serial al secondo char ecc..
Prendendo in considerazione le manipolazioni che vengono effettuate sui char del seriale immesso concludiamo che per soddisfare il primo check si devono verificare le seguenti condizioni:
(1_Serial xor 81) xor 40 deve essere uguale a F9
Per le proprieta' dell'xor (vedi il tute di Quequero sullo xor per ulteriori approfondimenti) possiamo convertirlo nel seguente modo...
(1_Serial xor 81) xor 40 = 1_Serial xor (81 xor 40) = 1_Serial xor (C1) qiundi...
1_Serial xor C1 deve essere uguale a F9
2_Serial xor C1 deve essere uguale a F3
3_Serial xor C1 deve essere uguale a F5
4_Serial xor C1 deve essere uguale a F1
Sempre grazie alle proprieta' dell'xor deduciamo che...
1_Serial dovra' essere uguale a F9 xor C1 -> 1_Serial=38 "8"
2_Serial dovra' essere uguale a F3 xor C1 -> 2_Serial=32 "2"
3_Serial dovra' essere uguale a F5 xor C1 -> 3_Serial=34 "4"
4_Serial dovra' essere uguale a F1 xor C1 -> 4_Serial=30 "0"
Infatti se inseriamo questo come seriale "8240" riusciamo a passare il primo check con successo.
Adesso vediamo il secondo...
Vengono effettuate queste operazioni sul char in al...
((al xor 82) shl 02) shr 02
Che come ora vedremo possono essere ricondotte ad
(al xor 02) and 3F
Quando viene eseguito lo shift a sinistra di 2 bit, i 2 bit piu' significativi di al vengono persi quindi qualunque sia il loro valore e' ininfluente, da questo deriva che xor al,82 (82 = 10000010) puo' essere ridotto
ad xor al,2 ( 2 = 00000010) e l'operazione di shift e rotate in un And 3F (per azzerare i 2 bit piu' significativi
ricapitolando *** ((al xor 82) shl 02) shr 02 ***
lo trasformiamo in *** (al xor 2) and 3F ***
Quindi per passare il 2° check dovranno essere verificate queste condizioni:
5_Serial dovra' essere uguale a (3A xor 2) and 3F -> 5_Serial=38="8"
6_Serial dovra' essere uguale a (34 xor 2) and 3F -> 6_Serial=36="6"
Infatti se inseriamo questo come seriale "824086" riusciamo a passare entrambi i check.
Ecco trovato il seriale per il mio nick.
NOTA:
Una conseguenza del fatto che i 2 bit significativi non vengono presi in considerazione diviene evidente perche' ad uno stesso nome vi possono essere abbinati piu' numeri di serie ad esempio prendendo il seriale "824086" e settando ad 1 il secondo bit piu' significativo dei numeri coinvolti nel 2° check si ottiene un nuovo seriale (8240xv) che e' anch'esso considerato come valido...
Poiche' i 2 bit significativi saranno scartati (riconducendo "8240xv" a "824086")
Mi rendo conto che queste manipolazioni sui bit possano sembrare un po' ostiche per i principianti, ma e' fondamentale sapersi "destreggiare" tra i vari xor shift ecc... per poter affrontare schemi di "crittografia" sempre piu' complessi.
Ora che abbiamo qualche elemento possiamo iniziare ad abbozzare quello che sara' il nostro keymaker...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char name[9]={"Int19"};
char cryptname[9]={0xF9, 0xF3, 0xF5, 0xF1, 0x3A, 0x34};
int c;
int namelength;
void main(void)
{
randomize();
namelength=strlen(name);
printf("Third Lesson Pre-KeyMaker by Int19\n");
c=0;
for (c=0; c<4; c++)
printf("%c", cryptname[c]^0xc1);
for (c=4; c<namelength+1; c++)
{
if (random(2)==1) // per rendere la generazione degli ultimi chars del seriale semi-casuale
printf("%c", (cryptname[c]^2)& 0x3f);
else
printf("%c", (cryptname[c]^2)& 0x3f | 0x40 );
}
}
Questo programma si limita a stampare il numero di serie per il mio nick.
L'ultimo if.then.else e' "facoltativo", esso si limita a settare ad 1 il secondo bit piu' significativo, in modo da generare seriali diversi per lo stesso nome.
4) Analizzare l'algoritmo di generazione del seriale
Quello che faremo in questa parte sara' scoprire il modo in cui vengono generati i bytes della locazione 4020C7 (quella che ho chiamato cryptname) in modo da cambiare i valori di cryptname a seconda del nome inserito, cosi' da realizzare un keymaker.
Iniziamo pure piazzando un "BPR 4020C7 4020C7+6 rw" pigiamo il bottone di registrazione e SoftICE poppa all'interno di questo ciclo
:00401277 mov dword ptr [0040210F], eax
:0040127C xor ecx, ecx
* Referenced by a Jump at Address 004012A9
|
:0040127E mov al, byte ptr [ecx+0040209B] <--- Carica il prossimo char del Nome
:00401284 mov edi, 0000000B <--- si prepara per la divisione
:00401289 cdq <--- azzera edx (che conterra' il resto della divisione)
:0040128A idiv edi <--- esegue la divisione del char per 0B (edx contiene il resto)
:0040128C add eax, edx <--- somma il resto della divisione al risultato
:0040128E shl eax, 1
:00401290 mov dl, al
:00401292 call 0040160C <--- "Normalizza" il char contenuto in dl
(routine analizzata sotto)
:00401297 xor eax, eax
:00401299 mov al, dl <---| Manipola il char
:0040129B xor al, 81 |
:0040129D xor al, 40 <---|
:0040129F mov byte ptr [ecx+004020C7], al <--- Il char manipolato viene salvato
(ed e' qui' che poppa SoftICE)
:004012A5 inc ecx <--- Incrementa la posizione corrente per
convertire il prossimo char
:004012A6 cmp ecx, 00000004
:004012A9 jne 0040127E <--- salta se non sono ancora stati convertiti i primi 4 chars
:004012AB nop
:004012AC nop
Ecco qui la routine di "normalizzazione" che prende un char tramite dl e fa in modo che all'uscita dalla routine il char in dl sia compreso tra "0" e "z"
:0040160C cmp dl, 7A
:0040160F jg 00401617 <--- salta se il char e' > "z"
:00401611 cmp dl, 30
:00401614 jl 00401621 <--- salta se il char e' < "0"
* Referenced by Jump at Addresses 0040161F, 00401629
|
:00401616 ret <--- all'uscita il char e' per forza compreso tra "0" e "z"
* Referenced by a Jump at Addresses 0040160F, 0040161D
|
:00401617 sub dl, 0A <---|
:0040161A cmp dl, 7A |
:0040161D jg 00401617 <---| Sottrae 0A al char fino a che non e' <= di "z"
:0040161F jmp 00401616
* Referenced by a Jump at Addresses 00401614, 00401627
|
:00401621 add dl, 0A <---|
:00401624 cmp dl, 30 |
:00401627 jl 00401621 <---| Somma 0A al char fino a che non e' >= di "0"
:00401629 jmp 00401616
Adesso che abbiamo analizzato il modo in cui vengono generati i primi 4 chars di cryptname andiamo a vedere come si comporta con i restanti dando uno sguardo al codice che si presenta poche linee dopo
:004012C0 mov ecx, 00000004
* Referenced by a Jump at Address 00401315
|
:004012C5 xor eax, eax
:004012C7 mov al, byte ptr [ecx+0040209B] <--- carica un char dal nome (a partire dal 5°)
:004012CD cmp ecx, dword ptr [0040210F] <--- 40210F contiene lung_nome+1
:004012D3 je 0040131C <--- Salta se sono gia' stati convertiti
lung_nome+1 chars
:004012D5 mov edi,00000008 <--- si prepara per la divisione per 8
:004012DA cdq <--- azzera edx che conterra' il resto
:004012DB idiv edi <--- divide il char per 8
:004012DD add al, dl <--- somma il risultato della divisione ed il resto
:004012DF shl al, 03 <---| varie
:004012E2 ror al, 1 | manipolazioni
:004012E4 add al, 06 | aritmetiche
:004012E6 shl al, 1 <---| sul char
:004012E8 xor al, byte ptr [0040210F] <--- xor con lung_nome+1
(variando la lunghezza del nome
veriera' anche il nome crittografato)
:004012EE mov edi,00000003 <---| una ulteriore
:004012F3 cdq | divisione per 3
:004012F4 idiv edi |
:004012F6 add al, dl <---|
:004012F8 mov dl, al
:004012FA call 0040160C <--- "normalizza" il char contenuto in dl
:004012FF mov al, dl
:00401301 xor al, 82 <---|
:00401303 shl al, 02 | manipolazioni finali del char
:00401306 ror al, 02 <---|
:00401309 mov byte ptr [ecx+004020C7], al <--- salva il risultato
:0040130F xor eax, eax
:00401311 inc ecx <--- incrementa la posizione corrente per analizzare il prossimo char
:00401312 cmp ecx, 00000008
:00401315 jne 004012C5 <--- se non sono stati esaminati ancora 8 chars continua il ciclo
:00401317 nop
:00401318 nop
:00401319 nop
:0040131A nop
:0040131B nop
:0040131C <---- arriveremo qui se sono stati convertiti 8 chars del nome oppure
se ne sono stati convertiti lung_nome+1 (se ne deduce che se il nome e'
piu' corto di 8 chars allora anche il char di fine stringa 0 verra' convertito)
5) Scrivere un key-maker per il programma
Vorrei farvi notare la forte analogia che esiste tra le operazioni
aritmetiche che vengono effettuate dopo la chiamata alla funzione di normalizzazione
e le operazioni che troviamo nella routine di check del seriale....
Come potete vedere per quel che riguarda i primi 4 chars
vi sono queste operazioni in comune
xor al, 81 <---| Ed abbiamo visto che possono essere
xor al, 40 <---| unificate con "al xor C1"
Mentre per i restanti chars sono queste le operazioni in comune
xor al, 82 <---| Che abbiamo
shl al, 02 | trasformato
ror al, 02 <---| in "(al xor 2) and 0x3f"
In effetti il codice corretto lo otteniamo gia' al termine delle call alla routine di normalizzazione,
"scartando" i 2 bit piu' significativi, l'xor serve solo per "mascherare" ulteriormente il codice corretto,
quindi eliminando le operazioni inutili il nostro keygen possiamo scriverlo cosi'....
// Iniziamo con il tradurre la routine di normalizzazione....
void normalize(char *ch) <--- Call dell'indirizzo 40160C
{
while (*ch>'z') *ch = *ch - 0x0a;
while (*ch<'0') *ch = *ch + 0x0a;
}
char name[10]={"Int19"};
char serial[10]={0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
int c;
int namelength;
char currchar;
div_t res;
void main(void)
{
printf("Third Lesson KeyMaker by Int19\n");
printf("Name: ");
scanf("%s", name);
namelength=strlen(name);
// Ora traduciamo la routine che crittografa i primi 4 chars del nome...
for (c=0; c<4; c++) <--- Ciclo 40127E...4012A9
{
currchar=name[c];
res=div(currchar, 0x0b); <--- 401284...
currchar= res.quot + res.rem; <--- ...40128C
currchar= currchar << 1; <--- 40128E
normalize(&currchar); <--- 401290...401297
serial[c]=currchar; <--- 40129F
}
// E adesso occupiamoci dei restanti
c=4;
while ((c<8) && (c<namelength+1)) <--- Ciclo 4012C5...401315
{
currchar=name[c];
res=div(currchar, 0x08); <--- 4012D5...
currchar= res.quot + res.rem; <--- ...4012DD
currchar= (currchar<<2) & 0x7c; <--- 4012DF...4012E2 shl 3 ror 1 --> 0xxxxx00
currchar= currchar+6; <--- 4012E4
currchar= currchar<<1; <--- 4012E6
currchar= currchar^(namelength+1); <--- 4012E8
res=div(currchar, 0x03); <--- 4012EE...
currchar= res.quot + res.rem; <--- ...4012F6
normalize(&currchar); <--- 4012F8...4012FF
if (random(2)==1) // Rende la generazione degli ultimi chars del seriale semi-casuale
currchar=(currchar & 0x3f); <--- 401301...401306
else
currchar=(currchar & 0x3f) | 0x40;
serial[c]=currchar; <--- 401309
c++;
}
// Ora stampiamo il serial
printf("S/N: %s\n", serial);
}
Di cosa trattera il quarto corso ? mah!
Note finali |
Leggiti tutti i saluti del mio ultimo tute (mqmc4) ed aggiungi questi George Lucas: Per le grandi sage che e riuscito a sfornare, da Indy a Monkey a StarWars. Dagolith perche e un buon-uomo. +Xoanon perche anche se non lo sa, e grazie a lui che mi sono avvicinato al mondo del cracking o meglio, grazie alla sua guida per aspiranti crackers, tnx Xoa. Centra un caxxo col cracking ma un grande fanculo al gruppo Twilight CD poiche non sprecano uno straccio di righe per ringraziare i gruppi (leggasi pdm rzr ogn ecc..) grazie ai quali hanno potuto guadagnare un bel po di soldoni, byez Il.Socio.
Disclaimer |
Vorrei ricordare che il software va comprato e non rubato, dovete registrare il vostro prodotto dopo il periodo di valutazione. Non mi ritengo responsabile per eventuali danni causati al vostro computer determinati dall'uso improprio di questo tutorial. Questo documento è stato scritto per invogliare il consumatore a registrare legalmente i propri programmi, e non a fargli fare uso dei tantissimi file crack presenti in rete, infatti tale documento aiuta a comprendere lo sforzo immane che ogni singolo programmatore ha dovuto portare avanti per fornire ai rispettivi consumatori i migliori prodotti possibili.
Noi reversiamo al solo scopo informativo e di miglioramento del linguaggio Assembly.
Capitoooooooo????? Bhè credo di si ;))))