Un breve tutorial sui generatori di chiavi
Nota introduttiva: questo tutorial, datato originariamente ottobre 1997, e' stato il mio primo tute pubblicato da +Fravia e la versione originale e' ancora presente, in lingua inglese, all'indirizzo http://www.fravia.org/nscekey.htm. Qua e la' sono stati aggiunti dei commenti... la traduzione comunque e' abbastanza affidabile, in quanto affidata... al sottoscritto =)
Non fatevi ingannare dalla data di questo tute! Per il dovere che deve assolvere (spiegare lo studio di un algoritmo di codifica e la creazione di un generatore di chiavi) e' piu' che adatto allo scopo. Inoltre, per quanto ne so, i programmi citati utilizzano ancora lo stesso algoritmo con alcuni piccoli cambiamenti. Ricordate che in questo e in molti altri settori certe abitudini sono dure a morire! :)
Salve ragazzi, questo e' un breve tutorial su nsce. Questo programma e' un cache explorer, che mostra tutti i siti che sono stati salvati nella cache di Netscape (c'e' una versione anche per MSIE) divisi per dominio e consente di rivisitarli una volta offline. Io lo trovo particolarmente utile perche' consente anche di salvare questi siti con tutte le immagini e le pagine collegate ricostruendo automaticamente i link in esse presenti affinche' funzionino in locale. Suppongo che siate riusciti a raggiungere questa porzione di codice (ammetto di aver avuto qualche problema, a suo tempo, a trovare il punto giusto - eh, la gioventu'! - anche se era proprio sotto il mio naso... ed era sufficiente utilizzare un bpx messageboxa! :) GRAZIE +Zer0 e +ReZiDeNt per il vostro aiuto!). Ecco il codice che si occupa del controllo:
:0041F88F 6A09 push 00000009
:0041F891 8D45F4 lea eax, dword ptr [ebp-0C]
:0041F894 50 push eax ** PUSH indirizzo della PW
:0041F895 FF7510 push [ebp+10]
* Reference To: USER32.GetWindowTextA, Ord:0000h
|
:0041F898 E84A560000 Call 00424EE7
:0041F89D 83F808 cmp eax, 00000008 ** la pw e' lunga 8 caratteri?
:0041F8A0 0F85D5000000 jne 0041F97B ** NO, salta
:0041F8A6 6A1F push 0000001F ** SI, vai avanti
:0041F8A8 8D45D4 lea eax, dword ptr [ebp-2C] ** indirizzo del NOME
:0041F8AB 50 push eax
:0041F8AC 6A70 push 00000070
:0041F8AE 56 push esi
Breve nota. Non so se ve lo ricordate, ma la funzione API getdlgitemtexta funziona cosi':
Declare Function GetDlgItemText Lib "user32" Alias "GetDlgItemTextA"
(ByVal hDlg As Long,
ByVal nIDDlgItem As Long,
ByVal lpString As String,
ByVal nMaxCount As Long) As Long
la locazione che ci interessa (quella del NOME) e' esattamente il terzo parametro della chiamata alla funzione e, come tale, lo possiamo trovare come terzultima PUSH prima della CALL: quel "PUSH EAX" e' esattamente il terzo parametro della funzione getdlgitemtexta!!!
* Reference To: USER32.GetDlgItemTextA, Ord:0000h
|
:0041F8AF E80B570000 Call 00424FBF ** leggi il nome
:0041F8B4 85C0 test eax, eax ** nome=0 caratteri?
:0041F8B6 0F84BF000000 je 0041F97B ** continua a chiedere il nome
:0041F8BC 8D45F4 lea eax, dword ptr [ebp-0C]
:0041F8BF 50 push eax ** salva la PW
:0041F8C0 8D45D4 lea eax, dword ptr [ebp-2C]
:0041F8C3 50 push eax ** salva il NOME
:0041F8C4 E887020000 call 0041FB50 ** PRIMA CALL IMPORTANTE
:0041F8C9 83C408 add esp, 00000008
:0041F8CC 8D45F4 lea eax, dword ptr [ebp-0C]
:0041F8CF 50 push eax
:0041F8D0 E8FA020000 call 0041FBCF ** SECONDA CALL IMPORTANTE
:0041F8D5 59 pop ecx
:0041F8D6 A320204300 mov [00432020], eax ** salva il valore
:0041F8DB E99B000000 jmp 0041F97B ** torna al programma
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0041F86F(C)
|
:0041F8E0 A120204300 mov eax, [00432020] ** valore salvato in precedenza
:0041F8E5 3B0510204300 cmp eax, dword ptr [00432010] ** IMPORTANTE!
:0041F8EB 743B je 0041F928 *** QUESTO E' IL SALTO BUONO!
:0041F8ED 6A50 push 00000050 **** BAD GUY!!!
:0041F8EF 8D4584 lea eax, dword ptr [ebp-7C]
:0041F8F2 50 push eax
:0041F8F3 56 push esi
Ok, diamo un'occhiata a queste righe di codice... innanzitutto c'e' un controllo relativo alla password: se essa raggiunge gli 8 caratteri allora comincia l'algoritmo, altrimenti il controllo ritorna all'utente, che puo' inserire altri caratteri per la password. Nel momento in cui la password raggiunge gli 8 caratteri, il programma legge il nome inserito e fa DUE CHIAMATE MOLTO IMPORTANTI: la prima richiede 2 argomenti, ovvero gli indirizzi in cui sono stati salvati il nome e la password, mentre la seconda richiede solo l'indirizzo del nome. Dopo queste due chiamate il controllo viene restituito all'utente e, quando il pulsante Ok viene selezionato, il programma salta all'indirizzo 41f8e0, dove controlla il valore salvato con un altro valore salvato in precedenza alla locazione di memoria 432010. Se date un'occhiata a questa locazione (a vostra scelta, utilizzando l'approccio "live" con SoftIce o il metodo dead listing con Wdsam o IDA), potrete notare che questo valore e' 190h, ovvero 400d.
Altra nota: questo valore e' la chiave su cui si basa l'intero algoritmo di protezione. Essa cambia da una versione all'altra del programma, intenzionalmente in questa sede ho deciso di darvi una chiave vecchia... funziona, ma con una versione del programma che forse non troverete neanche piu' in giro! Tanto non vi interessa sproteggere il programma, ma volete solo imparare le tecniche... vero? :)
Ora sappiamo che c'e' un algoritmo di codifica che utilizza sia il nome sia la pw per creare un solo numero, che dev'essere per forza 400. Hmmm... allora in questo caso NON ABBIAMO la password gia' pronta (ahivoi, in questo caso non funziona il trucco "data window" :)), ma dobbiamo fare un patch del programma O creare un generatore di chiavi. Poiche' il modo piu' semplice sembrava essere l'approccio "distruttivo", decisi di cambiare il "74 3b" alla locazione di memoria 41f8eb in un "eb 3b" e provai a registrare il programma con una pw qualsiasi: beh, la dialog box mi disse che la pw era giusta (adoro questo lavoro!)... ma sfortunatamente il programma NON era registrato... argh... forse qualche altro controllo... ma, essendo davvero TERRIBILMENTE pigro, ho deciso di cambiare approccio e cercare di dare un'occhiata all'algoritmo di codifica... BAMBINI, NON FATELO A CASA!!! :) Questa e' una PESSIMA pratica, nel cracking e nella programmazione! Proseguite sempre nella convinzione delle vostre decisioni, come insegna +ORC! (piccolo inciso: "ah, quante volte in gioventu' ci si e' trovati davanti ad esami in cui si cambiava esercizio di continuo, e alla fine non si combinava niente! :)). Infatti, il lavoro non e' stato affatto semplice come speravo, ma ho imparato un sacco da esso: quindi ho deciso di insegnarvi il modo in cui si prepara un bellissimo generatore di chiavi, sperando che questo algoritmo venga utilizzato ancora :) (un suggerimento: se date un'occhiata a MSIE cache explorer, noterete che esso utilizza lo STESSO IDENTICO algoritmo, semplicemente con una chiave finale diversa: 192h anziche' 190h... si sono sforzati, eh! ;)).
Ora parliamo dei generatori di chiavi: la prima cosa che bisogna fare e' leggere il codice e cercare di capire esattamente cosa succede. Ora, diamo un'occhiata alla PRIMA delle due call importanti, quella all'indirizzo 41fb50:
:0041FB50 55 push ebp
:0041FB51 8BEC mov ebp, esp
:0041FB53 83C4F8 add esp, FFFFFFF8
:0041FB56 53 push ebx
:0041FB57 56 push esi
:0041FB58 57 push edi
:0041FB59 8B7508 mov esi, dword ptr [ebp+08]
:0041FB5C 8BDE mov ebx, esi
:0041FB5E 56 push esi
* Reference To: KERNEL32.lstrlenA, Ord:0000h
|
:0041FB5F E8CF520000 Call 00424E33
:0041FB64 83F808 cmp eax, 00000008 ** eax=lunghezza del nome
:0041FB67 7305 jnb 0041FB6E
* Possible Reference to String Resource ID=00008: "%d Object(s) selected"
|
:0041FB69 B808000000 mov eax, 00000008
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0041FB67(C)
|
:0041FB6E 33C9 xor ecx, ecx
:0041FB70 8D55F8 lea edx, dword ptr [ebp-08] ** NUOVO INDIRIZZO
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0041FB7B(C)
|
:0041FB73 C60200 mov byte ptr [edx], 00
:0041FB76 41 inc ecx
:0041FB77 42 inc edx
:0041FB78 83F908 cmp ecx, 00000008
:0041FB7B 72F6 jb 0041FB73
** Con questo ciclo il programma libera dello spazio a partire da un nuovo indirizzo in
memoria "azzerando" le locazioni... uhmmm... perche'? Quando si usa il comando "mov" non
e' necessario liberare prima dello spazio... proseguiamo:
:0041FB7D 33C9 xor ecx, ecx
:0041FB7F 3BC1 cmp eax, ecx
:0041FB81 761A jbe 0041FB9D
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0041FB9B(C)
|
:0041FB83 8BD1 mov edx, ecx
:0041FB85 83E207 and edx, 00000007
:0041FB88 8D7C15F8 lea edi, dword ptr [ebp + edx - 08]
:0041FB8C 8A13 mov dl, byte ptr [ebx]
:0041FB8E 0017 add byte ptr [edi], dl
** Ecco perche'!!! Il programma non usa MOV, usa un ADD!!! :)
:0041FB90 43 inc ebx
:0041FB91 803B00 cmp byte ptr [ebx], 00
:0041FB94 7502 jne 0041FB98
:0041FB96 8BDE mov ebx, esi
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0041FB94(C)
|
:0041FB98 41 inc ecx
:0041FB99 3BC1 cmp eax, ecx
:0041FB9B 77E6 ja 0041FB83
OK... cosa fa questo ciclo? L'istruzione alla locazione di memoria 41fb8c copia il valore della n-esima lettera della stringa nel registro DL, quindi questo valore viene aggiunto nelle locazioni contenute in edi (queste sono esattamente le locazioni che erano state azzerate in precedenza!). Questa procedura viene ripetuta finche' non si raggiungono 8 caratteri (se la stringa e' di lunghezza inferiore o uguale a 8 caratteri), o finche' TUTTI i caratteri della stringa non vengono inseriti nello spazio in memoria, ricominciando dalla prima locazione di memoria quando se ne raggiunge il fondo.
+Zer0 --> becomes in memory --> +Zer0+Ze
MaLaTTiA --> remains --> MaLaTTiA
+ReZiDeNt --> becomes in memory --> *ReZiDeN
Il carattere rappresentato dall'asterisco corrisponde al valore ASCII 159, cioe' 43 ("+") sommato a 116 ("t"). Ne risulta che alla fine di questa porzione di codice abbiamo una stringa di 8 caratteri che e' stata costruita a partire dal nome che abbiamo inserito...
Vediamo cosa fa il programma adesso:
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0041FB81(C)
|
:0041FB9D 33C9 xor ecx, ecx
:0041FB9F 8B450C mov eax, dword ptr [ebp+0C] ** INDIRIZZO DELLA PW
:0041FBA2 8BD8 mov ebx, eax
:0041FBA4 8D75F8 lea esi, dword ptr [ebp-08] ** NUOVA STRINGA
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0041FBC6(C)
|
:0041FBA7 33C0 xor eax, eax
:0041FBA9 8A06 mov al, byte ptr [esi] ** sposta l'n-esima lettera
:0041FBAB BF1A000000 mov edi, 0000001A
:0041FBB0 99 cdq
:0041FBB1 F7FF idiv edi ** al=al/1a dl=dl%1a
:0041FBB3 80C241 add dl, 41
:0041FBB6 2813 sub byte ptr [ebx], dl
:0041FBB8 803B00 cmp byte ptr [ebx], 00
:0041FBBB 7D03 jge 0041FBC0
:0041FBBD 80031A add byte ptr [ebx], 1A
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0041FBBB(C)
|
:0041FBC0 41 inc ecx
:0041FBC1 43 inc ebx
:0041FBC2 46 inc esi
:0041FBC3 83F908 cmp ecx, 00000008
:0041FBC6 72DF jb 0041FBA7
Dopo il ciclo c'e' subito un'istruzione "ret": tutta la prima parte dell'algoritmo di codifica e' qui, quindi state attenti! Il programma prende l'n-esimo valore della stringa appena creata, lo divide per il valore 1Ah (che corrisponde a 26d) ed inserisce il resto in dl. Questo e' mostrato anche nel commento che ho inserito all'indirizzo 41fbb1: usando la notazione C, abbiamo al=al/1a (divisione intera) e dl=dl%1a (resto della divisione intera). In seguito, il valore 41h (65d) viene sommato a dl e il nuovo dl viene sottratto dal carattere n-esimo DELLA PASSWORD INSERITA IN PRECEDENZA. Se il valore finale e' minore di 0, allora viene aggiunto 1Ah (26d). Alla fine di questo ciclo, la procedura termina.
Ora tocca alla SECONDA call importante, all'indirizzo 41fbcf. Eccone la "versione completa":
:0041FBCF 55 push ebp
:0041FBD0 8BEC mov ebp, esp
:0041FBD2 83C4F8 add esp, FFFFFFF8
:0041FBD5 53 push ebx
:0041FBD6 56 push esi
:0041FBD7 57 push edi
:0041FBD8 33FF xor edi, edi
* Reference To: KERNEL32.GetTickCount, Ord:0000h
|
:0041FBDA E8EE510000 Call 00424DCD
:0041FBDF 8945FC mov dword ptr [ebp-04], eax
:0041FBE2 33F6 xor esi, esi
:0041FBE4 8B4508 mov eax, dword ptr [ebp+08]
:0041FBE7 8BD8 mov ebx, eax
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0041FC2C(C)
|
* Reference To: KERNEL32.GetTickCount, Ord:0000h
|
:0041FBE9 E8DF510000 Call 00424DCD
:0041FBEE 8945F8 mov dword ptr [ebp-08], eax
:0041FBF1 33C0 xor eax, eax
:0041FBF3 8A03 mov al, byte ptr [ebx]
:0041FBF5 03C6 add eax, esi
* Possible Reference to Dialog: DialogID_000A
|
* Possible Reference to String Resource ID=00010: "(Empty)"
|
:0041FBF7 B90A000000 mov ecx, 0000000A
:0041FBFC 99 cdq
:0041FBFD F7F9 idiv ecx
:0041FBFF 8BCA mov ecx, edx
:0041FC01 33C0 xor eax, eax
:0041FC03 8A4301 mov al, byte ptr [ebx+01]
:0041FC06 03C6 add eax, esi
:0041FC08 40 inc eax
:0041FC09 51 push ecx
* Possible Reference to Dialog: DialogID_000A
|
* Possible Reference to String Resource ID=00010: "(Empty)"
|
:0041FC0A B90A000000 mov ecx, 0000000A
:0041FC0F 99 cdq
:0041FC10 F7F9 idiv ecx
:0041FC12 59 pop ecx
:0041FC13 0FAFCA imul ecx, edx
:0041FC16 03F9 add edi, ecx
* Reference To: KERNEL32.GetTickCount, Ord:0000h
|
:0041FC18 E8B0510000 Call 00424DCD
:0041FC1D 2B45F8 sub eax, dword ptr [ebp-08]
:0041FC20 C1E80A shr eax, 0000000A
:0041FC23 03C7 add eax, edi
:0041FC25 8BF8 mov edi, eax
:0041FC27 46 inc esi
:0041FC28 43 inc ebx
:0041FC29 83FE07 cmp esi, 00000007
:0041FC2C 7CBB jl 0041FBE9
* Reference To: KERNEL32.GetTickCount, Ord:0000h
|
:0041FC2E E89A510000 Call 00424DCD
:0041FC33 2B45FC sub eax, dword ptr [ebp-04]
:0041FC36 C1E80B shr eax, 0000000B
:0041FC39 03C7 add eax, edi
:0041FC3B 5F pop edi
:0041FC3C 5E pop esi
:0041FC3D 5B pop ebx
:0041FC3E 59 pop ecx
:0041FC3F 59 pop ecx
:0041FC40 5D pop ebp
:0041FC41 C3 ret
Tutte queste chiamate a GetTickCount sono una protezione: probabilmente, se utilizzassimo un debugger "normale", il conto dei ticks sarebbe sbagliato e verrebbe restituito qualche messaggio d'errore... ma noi non usiamo un debugger normale: usiamo Winice! (TADAAA!) :))
Quindi, in realta' ci interessa solo una "versione breve" di questa procedura... in seguito a un'operazione di "sfoltimento" questo e' cio' che appare:
:0041FBE4 8B4508 mov eax, dword ptr [ebp+08] ** indirizzo della pw modificata
:0041FBE7 8BD8 mov ebx, eax
:HERE THE BIG LOOP STARTS:
:0041FBF3 8A03 mov al, byte ptr [ebx] ** n-esimo valore
:0041FBF5 03C6 add eax, esi ** inizialmente 0
:0041FBF7 B90A000000 mov ecx, 0000000A
:0041FBFC 99 cdq
:0041FBFD F7F9 idiv ecx ** eax=eax/10
:0041FBFF 8BCA mov ecx, edx ** ecx=edx=edx%10
:0041FC01 33C0 xor eax, eax ** eax=0
:0041FC03 8A4301 mov al, byte ptr [ebx+01] **(n+1)esimo valore
:0041FC06 03C6 add eax, esi ** ancora 0
:0041FC08 40 inc eax ** eax=(n+1)esimo valore+1
:0041FC09 51 push ecx ** salva il primo resto
:0041FC0A B90A000000 mov ecx, 0000000A
:0041FC0F 99 cdq
:0041FC10 F7F9 idiv ecx ** eax=eax/10
:0041FC12 59 pop ecx ** carica il primo resto
:0041FC13 0FAFCA imul ecx, edx ** ecx=primo resto*secondo resto
:0041FC16 03F9 add edi, ecx ** edi=edi+ecx
EDI CRESCE!
:0041FC20 C1E80A shr eax, 0000000A ** eax diventa 0
:0041FC23 03C7 add eax, edi
:0041FC25 8BF8 mov edi, eax ** nessun cambiamento :)
:0041FC27 46 inc esi
:0041FC28 43 inc ebx
:0041FC29 83FE07 cmp esi, 00000007 ** ho scandito tutta la pw?
:0041FC2C 7CBB jl 0041FBE9 ** no. Salta all'inizio.
:HERE THE BIG LOOP ENDS:
:0041FC36 C1E80B shr eax, 0000000B
:0041FC39 03C7 add eax, edi ** ax=valore finale
Come potete vedere, l'algoritmo dopotutto non e' COSI' difficile da comprendere... magari sara' piu' complicato fare il generatore di chiavi, ma ora possiamo ancora capire cosa sta succedendo. Il programma raccoglie i valori n-esimo e (n+1)-esimo della pw modificata, li divide per 10, quindi moltiplica il resto del valore n-esimo con quello del valore (n+1)-esimo incrementato di 1. Se date un'occhiata alle ultime righe, potete notare che il valore finale di questa somma viene salvato in eax e, se guardate la porzione di codice che compare subito dopo la call potete notare QUESTA riga:
:0041F8D6 A320204300 mov [00432020], eax ** salva il valore
che SALVA IL VALORE IN EAX per effettuare una successica "cmp". Quindi, QUESTO e' il valore che dev'essere uguale a 190h, cioe' 400d. Ecco il modo in cui tale numero viene costruito:
r1*r2 + r2*r3 + r3*r4 + r4*r5 + r5*r6 + r6*r7 + r7*r8 =190h=400d
dove
r1=p1+0%10
r2=p2+1%10
r3=p3+2%10
r4=p4+3%10
r5=p5+4%10
r6=p6+5%10
r7=p7+6%10
r8=p8+7%10
e dove px e' l'x-esimo elemento della password modificata, cioe':
(valore dell'x-esimo elemento della password originale-((x-esimo elemento del nome "scritto su se stesso")%26)+65)+26 (se il valore e' < 0).
Un casino, dite? Beh, non avete mai visto camera mia :)
Comunque lo ammetto, questa situazione e' abbastanza imbarazzante, specialmente se vogliamo cercare di invertire la "funzione" (e noi lo vogliamo!): questo perche' una funzione di questo tipo, ovvero che fa uso dell'operatore resto, puo' avere un numero infinito di soluzioni (ad esempio, il resto "5" di una divisione per 10 puo' essere stato generato dai numeri "5", "15", "25" e cosi' via). Tutto cio' che dobbiamo fare, a questo punto, e' usare i nostri cervelli... dobbiamo creare _DA_ZERO_ UNA delle INFINITE soluzioni, possibilmente la piu' semplice per noi! :)
Nota: in realta' non sono infinite - i numeri in gioco NON sono infiniti - pero' sono tante, abbastanza da occupare tutti i nostri futuri weekend liberi :)
D'ora in poi, leggerete LA MIA soluzione, ma la vostra puo' essere diversa dalla mia... Se avete qualche idea che pensate sia migliore delle mie, PER FAVORE fatemelo sapere, anche io sono qui per imparare! Potete vedere il mio indirizzo email alla fine del testo.
Ok, la PRIMA cosa che dobbiamo fare e' buttare giu' alcune idee:
1) Dobbiamo ridurre il numero di parametri che cambiano.
2) Dobbiamo costruire quel dannato numero 400d.
3) Dobbiamo fare un generatore di chiavi funzionante :)
A questo punto, ho deciso di cominciare dal fondo: il numero 400. Come viene costruito? Beh, lo potete vedere voi stessi dalle righe precedenti... e' la somma di sette prodotti in cui il secondo membro e' il primo membro del prodotto successivo. Quindi, cerchiamo di dare alcuni valori a quegli rx per creare il numero giusto. QUESTO e' un casino... ed ecco che entra in gioco lo Zen :)
Ho pensato: "Ehi, questi programmatori sono esseri umani come me, probabilmente avranno pensato a qualche numero SEMPLICE"... inoltre, i numeri da r2 a r7 vengono moltiplicati DUE volte, quindi probabilmente i programmatori avranno trovato piu' semplice utilizzare delle potenze di 2. Quindi:
1) Usa numeri facili/moltiplica 2 volte --> Ripeti lo STESSO numero il piu' volte possibile
2) Potenze di due --> parti con la potenza di 2 piu' grande che sia pero' < 10, ovvero 8
In questo modo ho scoperto che il numero 400 e' costituito da (8*8)*6 + (8*2). La riga scritta in precedenza diventa:
08*08 + 08*08 + 08*08 + 08*08 + 08*08 + 08*08 + 08*02 =190h=400d
r1*r2 + r2*r3 + r3*r4 + r4*r5 + r5*r6 + r6*r7 + r7*r8 =190h=400d
E:
r1=p1+0%10=8 -> p1=8
r2=p2+1%10=8 -> p2=7
r3=p3+2%10=8 -> p3=6
r4=p4+3%10=8 -> p4=5
r5=p5+4%10=8 -> p5=4
r6=p6+5%10=8 -> p6=3
r7=p7+6%10=8 -> p7=2
r8=p8+7%10=2 -> p8=5 (5+7=12 ; 12%10=2)
Ora conosciamo i valori che la pw modificata dovrebbe avere. Ma cosa dire della password ORIGINALE? Da cosa e' costituita? Numeri? Lettere? Ora dobbiamo semplificare il nostro lavoro il piu' possibile: in un primo momento avevo pensato di utilizzare semplicemente dei numeri, ma ho notato che, se chiamiamo "x" il valore di un carattere della pw e "c" il valore di un carattere del nome "ripiegato su se stesso", si ottiene:
p=(x-(65+c%26)(+26?))%10
e, se x e' un numero (ascii 48-57) e io sottraggo ad essoun valore compreso fra 65 e 65+26, anche se ad esso aggiungessi sempre 26 il valore risultante sarebbe talvolta maggiore di 0 e talvolta minore di 0. MALE: se devo lavorare con dei resti non voglio che fra essi ci siano dei "salti" come quello fra 0 (resto 0) e -1 (=255, resto 255). Per questo motivo ho deciso di utilizzare per la password le lettere dalla a alla z, in modo da avere SEMPRE dei numeri positivi e non dover neanche avere a che fare con quel (26?) :)
Quindi, le p vengono costruite grazie alla seguente equazione:
p=(x-(65+c%26))%10
Benissimo, ho risolto un problema ma ora ne ho un altro: come posso fare in modo che le "p" siano esattamente come quelle di cui ho bisogno? Infatti, se ho bisogno di un resto, sia la lettera n-esima sia la (n+10)-esima possono andare bene. Ho deciso di cominciare da una pw costituita da sole "a", quindi calcolare i suoi valori "p", quindi aggiungere la "differenza" fra i "p" che ho calcolato e quelli di cui ho bisogno. Proviamo con un nome semplice: MaLaTTiA :)
1) Ripiega la stringa M a L a T T i A
(qui non e' necessario)
2) Dec 77 97 76 97 84 84 105 65
3) p=(97-(65+c%26))%10 07 03 08 03 06 06 01 09
(97 e' "a")
4) Valori richiesti 08 07 06 05 04 03 02 05
5) Aggiungi ad "a" 01 04 08 02 08 07 01 06
6) PASSWORD: b e i c i h b g
Ehi... FUNZIONA!!! :)
Questo e' tutto. Ora, ecco il sorgente C per questo generatore di chiavi (abbiate pazienza, il mio C non e' un granche'... ;)
*****************************************************************************
#include
unsigned char s[8]={0,0,0,0,0,0,0,0};
char string [80];
int values[8], required[8]={8,7,6,5,4,3,2,5};
int i,j,ok=0,ok2=0;
void main (){
printf ("\n...//\\Oo/\\\\ Netscape Cache Explorer v1.26 KeyGen - by
.MaLaTTiA. //\\Oo/\\\\...\n\n");
printf ("\nUser name: ");
gets (string);
i=j=0;
if (string[0]==0) exit(0);
while ((!ok)||(!ok2)){
if (string[j]==0){
if (!ok2) j=0;
ok=1;
}
s[i]=s[i]+string[j];
j++;i++;
if (i>7){
ok2=1;
i=0;
}
}
for (i=0;i< =7;i++){
values[i]=required[i]-((32-(s[i]%26))%10);
if (values[i]< 0) values[i]=values[i]+10;
}
printf ("Key: ");
for (i=0;i< =7;i++){
printf ("%c", 97+values[i]);
}
printf ("\n");
}
*****************************************************************************
Noto ora che non e' neanche ottimizzato... bah, funziona! :)) Credo che questo sorgente sia abbastanza semplice da comprendere, tuttavia se non comprendete qualcosa mandatemi una mail e saro' felice di aiutarvi! Ora, potete anche costruire il generatore di chiavi per MSICE, tenendo presente che il valore necessario non e' 190h ma e' 192h. Provatelo, l'algoritmo e' lo stesso! :)
byez,
.+MaLaTTiA. (malattia@usa.net)