Zoom Icon

WinZip 8.1

From UIC

Keygen Fishing in WinZip v8.1

Contents


Infos
Author: Ox87k
Email: oxy87k@gmail.com
Website: http://ox87k.altervista.org
Date: 14/12/2004 (dd/mm/yyyy)
Level: Working brain required
Language: Italian Image:Flag_Italian.gif
Comments: Va che bravo che sono!



Introduction

Introduzione?? Ehm... WinZip chi non lo conosce?? Purtroppo chiede una registrazione [purtroppo??? :)))]. Non è per niente difficile, l'unico particolare che forse poteva confondere è la routine della creazione del seriale che è imboscata. Con un po' di pazienza comunque ci si arriva e devo dire che non è per niente difficile notarla... hihihihi! Vediamo quindi come WinZip genera il seriale del nome inserito, peschiamo il seriale e creiamo il keygen!


Tools

  • OllyDbg
  • Borland c++ 5 (o qualsiasi altro compilatore C)
  • Uno stereo con un po di musica.. e una brioche :)


Link e Riferimenti

Keygen


Analisi

Essay... Let's go! Avviate WinZip e subito vi appare quella noiosissima nag ke vi chiede se volete registrarvi oppure continuare da non registrati. Clickando sul pulsante Register vi appare una finestrella dove inserire nome e serial.. Va bhe tentar non nuoce: inseriamo nome e serial. Nome, Ox87k, serial, 123456789, Invalid name o serial.. dannazione! Ok ok niente paura.. avviamo OllyDbg e carichiamo il winzip. Come abbiamo notato prima, nella finestra di registrazione ci sono due textbox. Una volta caricato il programma in olly, proviamo a piazzare un break su GetDlgItemTextA [magari è la API giusta... magari.... :)], ALT+F1 (x la command line) e scriviamoci bp GetDlgItemTextA. Ok piazzato. Ora avviamo tutto, F9. Benissimo, il programma parte, clickiamo sul pulsante di registrazione, e inseriamo i dati di prima, Ox87k e 123456789 e poi OK. Olly poppa (API azzeccata chissà come mai!!!!). Usciamo dalla API con CTRL+F9. e ci ritroviamo sotto la chiamata alla API.

0040BD00 call ds:GetDlgItemTextA //tornati dalla API siamo qui.
0040BD06 push esi //esi contiene il seriale inserito
0040BD07 call sub_4607BD //fa dei controlli sul seriale. non ci interessa
0040BD0C push esi
0040BD0D call sub_4607E6 //altri controlli sul serial, nemmeno li guardiamo
0040BD12 cmp nome, 0 //controlla se il primo carattere del nome inserito è 0
0040BD19 pop ecx
0040BD1A pop ecx
0040BD1B jz short loc_40BD7C //salto relativo al controllo sul nome (vedi sopra)
0040BD1D cmp serial, 0 //controlla se il primo carattere del serial inserito è 0
0040BD24 jz short loc_40BD7C //salto relativo al confronto del serial
0040BD26 call Genera_Codice //CHIAMATA PER GENERARE IL SERIAL VERO, Entriamoci!
0040BD2B test al, al
0040BD2D jz short loc_40BD7C

Ok come notiamo in fondo c'è la chiamata alla routine della creazione del seriale valido. Posizioniamoci sulla riga relativa a questa chiamata e premiamo F7 per entrarci. Ora è la parte un pochino più complicata.. bisogna trovare la chiamata alla creazione vera e propria del serial. Evito di postare tutto il codice perché non ne vale la pena, vi riporto l'indirizzo dove troveremo la chiamata fatidica: poche righe più in giù di dove siamo arrivati entrando nella call troviamo un'altra call, esattamente a questo inidirizzo:

0040B783 CALL WINZIP32.0040B875 //CALL KE GENERA IL SERIALE

Yeah. Entriamo qui con F7. Finalmente vediamo che ci siamo.. ci sono vari calcoli che sicuramente indicano che il seriale viene generato qui. Incominciamo!

//Call relativa all'opcode :0040B783
0040B875 push ebp //siamo sbarcati qui :)
0040B876 mov ebp, esp
0040B878 mov ecx, dword ptr ss:[ebp+8] //ecx contiene il nome
0040B87B push ebi
0040B87C push esi
0040B87D push edi
0040B87E mov dl, byte ptr ds:[ecx] //dl contiene il primo carattere del nome ke è in ecx
0040B880 xor ebx, ebx //azzero ebx
0040B882 xor eax, eax //azzero l'accumulatore eax
0040B884 mov esi, ecx //copio il nome che è in ecx anche in esi
0040B886 xor edi, edi //azzero l'indice di destinazione

//Primo ciclo per il seriale:
0040B888 +-> test dl, dl //sono arrivato all'ultimo carattere del nome?
0040B88A | je short Fine_primo_ciclo //se si esco dal primo ciclo e proseguo sotto
0040B88C | movzx dx, dl //metto il carattere che ho in dl nella parte bassa di dx
0040B890 | imul edx, edi //moltiplica il edx che contiene il carattere con il
| //contatore dei caratteri del nome che ogni ciclo viene inc.
0040B893 | add ebx, edx //aggiunge al primo pezzo del serial il risultato
0040B895 | mov dl, byte ptr ds:[esi+1] //dl punta al carattere successivo
0040B898 | inc edi //incremento il contatore di caratteri
0040B899 | inc esi //esi punta quindi al carattere successivo
0040B89A +-> jmp short Inizio_primo_ciclo //torno su
//Fine primo ciclo

Ora, l'algoritmo ha generato metà del seriale valido. Specificamente ha generato la parte bassa. Prende ogni carattere del nome inserito e lo moltiplica per l'indice che funge da contatore di caratteri. Vediamo come sarebbe questa parte in c;

//primo pezzo del serial, parte bassa
for (int i=0;name[i]!=0;i++)
{
serial1+=(name[i] * i);
}

Ok, continuiamo l'analisi della seconda parte del serial. Il codice che segue sotto è la parte seguente all'algoritmo sopra esaminato:

0040B89C mov byte ptr ds:[4CC78B],1
0040B8A3 mov esi, ecx //copio il nome contenuto in ecx in esi
0040B8A5 mov cl, byte ptr ds:[ecx] //cl contiene il primo carattere del nome

//secondo ciclo:
0040B8A7 +-> test cl, cl //sono arrivato all'ultimo carattere del nome?
0040B8A9 | je short Fine_secondo_ciclo //se si esco dal secondo ciclo e proseguo sotto
0040B8AB | movzx cx, cl
0040B8AF | push 1021 //prendo questo valore 00001021, poi vedremo a cosa serve
0040B8B4 | push ecx
0040B8B5 | push eax
0040B8B6 | call Calcoli_del_serial //VEDIAMO SOTTO COSA ACCADE IN QUESTA CALL
0040B8BB | mov cl, byte ptr ds:[esi+1] //finita la call, carico in cl il carattere successivo
0040B8BE | add esp, 0c
0040B8C1 | inc esi
0040B8C2 +-> jmp short Inizio_secondo_ciclo //torno su all'inizio x ripetere tutto
//fine secondo ciclo

Notiamo quella chiamata, andiamo a vedere cosa succede quando arriviamo li. Una volta arrivati sopra la call, premiamo F7 ed entriamo anche qui.

//Call relativa all'opcode :0040B8B6
0040B8E4 push ebp
0040B8E5 mov ebp, esp
0040B8E7 mov eax, dword ptr ss:[ebp+8]
0040B8EA push esi //nello stack il nome
0040B8EB xor ecx, ecx //azzero il contatore ecx
0040B8ED push 8 //nello stack il valore 8
0040B8EF mov ch, byte ptr ss:[ebp+C] //in ch il primo carattere del nome
0040B8F2 pop edx //edx=8 cioe' le volte ke ripete il seguente ciclo

//Ciclo che viene ripetuto 8 volte per ogni singolo carattere del nome
0040B8F3 +-> mov esi, eax //esi = eax
0040B8F5 | xor esi, ecx //esi ^= ecx (ecx, nella parte alta, contiene il carattere)
0040B8F7 | test si, 8000 //if (esi & 0xFFFF) >= 8000
0040B8FC | je short <font color="#FF0000">2°</font> //se esi & 0xFFFF è minore di 8000 salta alla parte 2°
0040B8FE |<font color="#FF00FF">1°</font>: add eax, eax //altrimenti [se è maggiore o uguale a 8000] eax = eax+eax
0040B900 | xor eax, dword ptr ss:[ebp+10] //e poi SS:[EBP+10] = 1021 ---> eax ^= 1021
0040B903 | jmp short <font color="#0000FF">3°</font> //salta alla parte 3° [siamo fuori dal if/else]
0040B905 |<font color="#FF0000">2°</font>: shl eax, 1 //eax = eax<<1; shifto eax di 1
0040B907 |<font color="#0000FF">3°</font>: shl ecx, 1 //[FUORI IF ELSE] ecx = ecx<<1; shifto ecx di 1
0040B909 | dec edx //decremento le 8 volte iniziali
0040B90A +-> jnz short Inizio_ciclo //torno all'inizio del ciclo
//Fine ciclo

0040B90C pop esi
0040B90D pop ebp
0040B90E retn
//Finita la CALL torno al ciclo di prima ovvero all'opcode 0040B8BB

Ok.. siamo più o meno sani e salvi!! Tutto il codice in C che riporto di seguito è il secondo pezzo del serial, ovvero la parte alta questa volta. Vediamo tutto questo macello in C per cercare di capire meglio: [non ho messo commenti perché mi pare di averne messi fin troppi sopra... poi magari il Que s'incaz!!!]

//Genera serial2 [seconda parte del serial]:
for (int x=0;name[x]!=0;x++)
{
ecx=(name[x]<<8);

for (z=0;z<8;z++)
{
esi=eax;
esi^=ecx;

if ((esi & 0xFFFF)>=0x8000)
{
eax+=eax;
eax^=0x1021;
}
else
{
eax<<=1;
}
ecx<<=1;
}
}

Ora andiamo a completare l'opera. Analizziamo le righe finali situate sotto la primissima call che abbiamo visto prima, all'inizio del tute:

0040B8C4 add eax, 63 //Aggiunge ad eax 63 ---> eax += 0x63;
0040B8C7 movzx ecx, bx
0040B8CA movzx eax, ax //metto nella parte bassa di eax il valore di ax, ovvero la
//1° metà del mio serial
0040B8CD push ecx //ecx contiene la seconda meta del mio serial
0040B8CE push eax //eax contiene la prima parte del serial
0040B8CF push %04X%04X //prelevo questi caratteri dallo stack --> %04X%04X
0040B8D4 push dword ptr ss:[ebp+C]
0040B8D7 call winzip32.00493104
0040B8DC add esp, 10
0040B8DF pop edi
0040B8E0 pop esi
0040B8E1 pop ebx
0040B8E2 pop ebp
0040B8E3 retn //ritorno della prima call
name: Ox87k
serial: 9F4F0339


Soluzione

The End! Finito di esaminare il proggy, prendiamo un qualsiasi compilatore C [io ho usato Borland 5] ed eccovi il keygen:

//KEYGEN WINZIP 8.1
#include <conio.h>
#include <stdio.h>

char name[100];
char serial[9];

unsigned int serial1 = 0;
unsigned int serial2 = 0;

unsigned int esi = 0;
unsigned int eax = 0;
unsigned int ecx = 0;

void main()
{
printf(" [ WinZip v8.1 kEYGEN ]\n\n");
printf("name: ");
gets(name);
for (int i=0;name[i]!=0;i++)
{
serial1+=(name[i] * i);
}
for (int x=0;name[x]!=0;x++)
{
ecx=(name[x]<<8);
for (int z=0;z<8;z++)
{
esi=eax;
esi^=ecx;
if ((esi & 0xFFFF)>=0x8000)
{
eax=eax+eax;
eax^=0x1021;
}
else
{
eax<<=1;
}
ecx<<=1;
}
}
serial2=eax+0x63;
serial2&=0xFFFF;
sprintf(serial, "%0.4X%0.4X", serial2, serial1);
printf("serial: ");
printf("%s",serial);
printf("\n\npress any key to exit....");
getch();
}
Bene.. Questo era il mio primo tute e ho preso una scannata pazzesca per scriverlo tutto, spero almeno ne sia valsa la pena.


Note Finali

Oddio la sezione più difficile di tutto il tutorial! Essendo il mio primo tute, devo ASSOLUTAMENTE ringraziare [active85k], (il mio nick è un onore a lui) che mi ha dato le prime dritte tramite email sul mondo del reverse, e sopratutto che mi ha sopportato!!!

Oltre a lui devo ringraziare, Quequero ke mi ha dato la possibiltà di studiare grazie al suo sito, ad AndreaGeddon perché mi ha aiutato anke lui, e al mitico Zero_G che nel forum è sempre un amore :)

Poi ringrazio tutti quanti voi perché solo grazie ai vostri tute ho potuto studiare e imparare sempre di più. Lonely Wolf, faina_mdc, pnluck, bender0, zarathustra, SatUrN e via via tutti gli altri, grazie ancora per i vostri tute che in un certo qual senso mi hanno cresciuto :)

Per ultimo ma non meno importante, i Dream Theater che mi hanno tenuto compagnia nel reverse e mentre scrivevo e alla brioche che nel frattempo mi sono pappato!


Disclaimer

I documenti qui pubblicati sono da considerarsi pubblici e liberamente distribuibili, a patto che se ne citi la fonte di provenienza. Tutti i documenti presenti su queste pagine sono stati scritti esclusivamente a scopo di ricerca, nessuna di queste analisi è stata fatta per fini commerciali, o dietro alcun tipo di compenso. I documenti pubblicati presentano delle analisi puramente teoriche della struttura di un programma, in nessun caso il software è stato realmente disassemblato o modificato; ogni corrispondenza presente tra i documenti pubblicati e le istruzioni del software oggetto dell'analisi, è da ritenersi puramente casuale. Tutti i documenti vengono inviati in forma anonima ed automaticamente pubblicati, i diritti di tali opere appartengono esclusivamente al firmatario del documento (se presente), in nessun caso il gestore di questo sito, o del server su cui risiede, può essere ritenuto responsabile dei contenuti qui presenti, oltretutto il gestore del sito non è in grado di risalire all'identità del mittente dei documenti. Tutti i documenti ed i file di questo sito non presentano alcun tipo di garanzia, pertanto ne è sconsigliata a tutti la lettura o l'esecuzione, lo staff non si assume alcuna responsabilità per quanto riguarda l'uso improprio di tali documenti e/o file, è doveroso aggiungere che ogni riferimento a fatti cose o persone è da considerarsi PURAMENTE casuale. Tutti coloro che potrebbero ritenersi moralmente offesi dai contenuti di queste pagine, sono tenuti ad uscire immediatamente da questo sito.

Vogliamo inoltre ricordare che il Reverse Engineering è uno strumento tecnologico di grande potenza ed importanza, senza di esso non sarebbe possibile creare antivirus, scoprire funzioni malevoli e non dichiarate all'interno di un programma di pubblico utilizzo. Non sarebbe possibile scoprire, in assenza di un sistema sicuro per il controllo dell'integrità, se il "tal" programma è realmente quello che l'utente ha scelto di installare ed eseguire, né sarebbe possibile continuare lo sviluppo di quei programmi (o l'utilizzo di quelle periferiche) ritenuti obsoleti e non più supportati dalle fonti ufficiali.