Seifer's Short Tutorials -- http://www.seifer666.com


====================
Winzip 6.3/7.0/8.0
====================


Download location : http://www.winzip.com
Protection : name / serial
Level [1..10] : 2 (EASY)
Tools needed : Soft Ice 3.x or better,C/C++ compiler for the keygen



===============
1)Introduction
===============


	Ok, here's the english translation of my winzip's keygenning tut, as some dudes asked me
to translate it. With this, you'll be able to keygen all winzip's version, from 6.3 to 8.0 :)



=======================
2)Une définition utile
=======================

	
	To break the prog's execution before the serial's generation, we'll use the API
GetDlgItemTextA (32 bits version), which is defined as follow in the Win32 Programmer's 
References : 



The GetDlgItemText function retrieves the title or text associated with a control in a dialog 
box. 

UINT GetDlgItemText(

    HWND hDlg,	// handle of dialog box
    int nIDDlgItem,	// identifier of control
    LPTSTR lpString,	// address of buffer for text
    int nMaxCount 	// maximum size of string
   );	
 

Parameters

hDlg

Identifies the dialog box that contains the control. 

nIDDlgItem

Specifies the identifier of the control whose title or text is to be retrieved. 

lpString

Points to the buffer to receive the title or text. 

nMaxCount

Specifies the maximum length, in characters, of the string to be copied to the buffer pointed to 
by lpString. If the length of the string exceeds the limit, the string is truncated. 

 

Return Values

If the function succeeds, the return value specifies the number of characters copied to the 
buffer, not including the terminating null character. If the function fails, the return value is 
zero. 


==========================
3)Machine code analysis
==========================


	Launch Winzip, enter a name and a serial. As i said before, we will put a breakpoint on 
GetDlgItemTextA, as the program will get the name and the serial thanks to this API. So under 
Soft Ice let's type :

bpx GetDlgItemTextA, then press on enter and F5 as usual :)

	Click on the OK button, we are back under Soft Ice. You should arrive at the offset
407F6D : 

---------------------------------------------8<--------------------------------------------------
* Reference To: USER32.GetDlgItemTextA, Ord:0104h
                                  |
:00407F6D FF1528744700            Call dword ptr [00477428]	;calls GetDlgItemTextA
:00407F73 57                      push edi			;saves the name
:00407F74 E821790300              call 0043F89A			;kills spaces after the name
:00407F79 57                      push edi			;saves the name
:00407F7A E844790300              call 0043F8C3			;kills spaces before the name
:00407F7F 59                      pop ecx
:00407F80 BEA4CD4800              mov esi, 0048CDA4
:00407F85 59                      pop ecx
---------------------------------------------8<--------------------------------------------------

Ok, the firsqt operation on the name is to check that the user didn't enter extra spaces, i.e
spaces before the first char of the name or spaces after the last char of the name...

In C/C++, we could code this as follows : 

---------------------------------------------8<--------------------------------------------------
GetDlgItemText(hwnd, IDC_NAME, name, 255);			//we get the name as winzip
len = strlen(name);						//length of the name
while(name[len-1] == ' ') len--;				//kill da fuckin space
name[len] = 0x00;						//nul terminating char

//same thing for the first char of the name

while(name[0] == ' ') for(i = 0; i < len; ++i) name[i] = name[i+1];
len = strlen(name);
---------------------------------------------8<--------------------------------------------------

Once those checks are done, our name will look like : 

entered name : "seifer"				modified name : "seifer"
entered name : "  seifer"			modified name : "seifer"
entered name : "seifer is sexy  " 		modified name : "seifer is sexy"


	The proggie then gets the serial the same way, and starts a lotta other checks 
erm, like the following ones (1 <= length <= 40), then checks length of the serial,
and compares even the entered name to names in memory (heh do they think we are silly ;), 
begin to trace with F10 until you arrive at 00407A99...

Note : to go faster, you could even put a bpr on your name, then press sometimes on F5. I give 
you the bpr's syntax again (breakpoint on memory range) :

bpr  address_of_name (address_of_name + length_of_name) - 1 rw 

Example : heer the address of the name is 0048CD78, and the length of seifer is 6 chars...
So i'll have to type : 

bpr 48cd78 48cd7d rw	, rw meaning read/write...

Let's have a lil look at the call @00407A99 : 

:00407A99 E8A9000000              call 00407B47			;enter inside with F8

We arrive then at the first part of the serial's generation's algorithm : 

---------------------------------------------8<--------------------------------------------------
:00407B47 55                      push ebp
:00407B48 8BEC                    mov ebp, esp
:00407B4A 8B4D08                  mov ecx, dword ptr [ebp+08]	;ecx points to the name
:00407B4D 53                      push ebx			;saves ebx
:00407B4E 56                      push esi			;esi
:00407B4F 57                      push edi			;and edi
:00407B50 8A11                    mov dl, byte ptr [ecx]	;dl contains the char pointed by ecx
:00407B52 33DB                    xor ebx, ebx			;initializes ebx	
:00407B54 33C0                    xor eax, eax			;eax to 0
:00407B56 8BF1                    mov esi, ecx			;esi has the same value than ecx	
:00407B58 33FF                    xor edi, edi			;edi initialized to 0

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00407B6C(U)
|
:00407B5A 84D2                    test dl, dl			;dl = 0 --> no more chars
:00407B5C 7410                    je 00407B6E			;jumps after the loop
:00407B5E 660FB6D2                movzx dx, dl			;dx contains dl...
:00407B62 0FAFD7                  imul edx, edi			;edx is multiplied by the char's position
:00407B65 03DA                    add ebx, edx			;and added to ebx
:00407B67 8A5601                  mov dl, byte ptr [esi+01]	;next char
:00407B6A 47                      inc edi			;edi is incremented
:00407B6B 46                      inc esi			;as esi
:00407B6C EBEC                    jmp 00407B5A			;jumps to the beginning of the loop
---------------------------------------------8<--------------------------------------------------


	Now the proggie has calculated the first part of the serial (in fact it is the last
one, but it's calculated in first, do you follow ?). The algo is quite simple,
it just adds the ascii codes of the chars of the name multiplied by their position in the name.

Still in C, we could write this as follows : 

unsigned long sum = 0;
for(unsigned i = 0; i < len; ++i) sum += name[i] * i;

As you can see it, it isn't very strong... Now comes the first part of the serial's calculation
(which is calculated in last, do ya remember ?) : 
I also remember you that ebx contains atm the result of the first calculation...

---------------------------------------------8<--------------------------------------------------
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00407B5C(C)
|
:00407B6E C70544C8480001000000    mov dword ptr [0048C844], 00000001	
:00407B78 8BF1                    mov esi, ecx				;esi points to the name
:00407B7A 8A09                    mov cl, byte ptr [ecx]		;cl contains one char
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00407B97(U)
|
:00407B7C 84C9                    test cl, cl				;all chars done ?
:00407B7E 7419                    je 00407B99				;yah, jumps to the end of algo
:00407B80 660FB6C9                movzx cx, cl				;cx contains cl...
:00407B84 6821100000              push 00001021				;constant value used inside the call
:00407B89 51                      push ecx				;saves the char
:00407B8A 50                      push eax				;eax = 0 at beginning
:00407B8B E829000000              call 00407BB9				;let's study this after
:00407B90 8A4E01                  mov cl, byte ptr [esi+01]		;next char
:00407B93 83C40C                  add esp, 0000000C			;prepares the stack for pushes
:00407B96 46                      inc esi				;esi is incremented
:00407B97 EBE3                    jmp 00407B7C				;loops again

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00407B7E(C)
|
:00407B99 83C063                  add eax, 00000063			;99d = 63h added to the calculated number
:00407B9C 0FB7CB                  movzx ecx, bx				;ecx contains the low word of ebx
:00407B9F 0FB7C0                  movzx eax, ax				;eax contains the low word of eax
---------------------------------------------8<--------------------------------------------------

When i say cx contains one char of the name, let's look ecx'state :

the first ascii code of seifer is 73h = 115d ('s')...
mov cl, byte ptr [ecx] <---- ecx = 00000073
movzx cx, cl	       <---- ecx = 00007300

In C it is equal to :

...
unsigned char mychar = name[i];
mychar <<= 8; (equals mychar *= 0x100;)
...

Moreover a high word is equal to the 4 last figures contained in the concerned registry :

mov eax, 12345678 <---- eax = 12345678
mov eax, ax	  <---- eax = 00005678, 5678 is the low word (and 1234 the high one).

Inside the call we fond the calculation of the first part of serial, it is kinda strange ;-).


---------------------------------------------8<--------------------------------------------------
:00407BB9 55                      push ebp
:00407BBA 8BEC                    mov ebp, esp
:00407BBC 8B4508                  mov eax, dword ptr [ebp+08]
:00407BBF 56                      push esi
:00407BC0 33C9                    xor ecx, ecx

* Possible Ref to Menu: RBUTTONMENU1, Item: "Delete..."
                                  |

* Possible Reference to String Resource ID=00008: "Delete files from %s"
                                  |
:00407BC2 6A08                    push 00000008
:00407BC4 8A6D0C                  mov ch, byte ptr [ebp+0C]
:00407BC7 5A                      pop edx				;counter = 8 at beginning

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00407BDF(C)
|
:00407BC8 8BF0                    mov esi, eax				;esi = 0
:00407BCA 33F1                    xor esi, ecx				;esi xored with ecx
:00407BCC 66F7C60080              test si, 8000				;si % 2 == 0 ?
:00407BD1 7407                    je 00407BDA				;yah, jumps there
:00407BD3 03C0                    add eax, eax				;else eax += eax
:00407BD5 334510                  xor eax, dword ptr [ebp+10]		;eax is then xored with 0x1021 !
:00407BD8 EB02                    jmp 00407BDC				;jumps again

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00407BD1(C)
|
:00407BDA D1E0                    shl eax, 1				;eax <<= 1

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00407BD8(U)
|
:00407BDC D1E1                    shl ecx, 1				;ecx <<= 1
:00407BDE 4A                      dec edx				;decreases the counter
:00407BDF 75E7                    jne 00407BC8				;loops again
:00407BE1 5E                      pop esi
:00407BE2 5D                      pop ebp
:00407BE3 C3                      ret					;the number is generated...
---------------------------------------------8<--------------------------------------------------


The operation is done 8 times for each char (look at the counter in edx)...
Let's translate this in C, always, again :-) :

unsigned i, j, arg, temp, ctrl;
unsigned long serial2 = 0;
for(i = 0; i < len; ++i)
	{
		arg = name[i] << 8;

		for(j = 0; j < 8; ++j)
		{
			temp = serial2;
			temp ^= arg;
			ctrl = temp & 0x8000;
			if(ctrl == 0)
			{
				serial2 <<= 1;
				arg <<= 1;
			}
			else
			{
				serial2 += serial2;
				serial2 ^= 0x1021;
				arg <<= 1;
			}
		}
	}

As we have seen before, eax will then be added to 99, and the first part
of the serial will be equal to the low word of eax (eax % 0x10000), simply coded : 

serial2 += 0x63; 	//99d
serial2 %= 0x10000;

But what the fuck will become those two numbers ? We land at the last part of the algorithm : 

---------------------------------------------8<--------------------------------------------------
:00407BA2 51                      push ecx				;ecx = first nuber
:00407BA3 50                      push eax				;eax = second number

* Possible StringData Ref from Data Obj ->"%04X%04X"
                                  |
:00407BA4 688CE34700              push 0047E38C				;points to "%04X%04X"
:00407BA9 FF750C                  push [ebp+0C]				;points to real serial
:00407BAC E803FF0500              call 00467AB4				;calls wsprintfa
:00407BB1 83C410                  add esp, 00000010
---------------------------------------------8<--------------------------------------------------

The two numbers are then concatenated in hexadecimal in the reversed order of their calculation
in the algorithm. So the serial has 8 chars...

name : Seifer
serial : ADFE0637


===================
4)Source code in C
===================

	To finish, here comes the int keygen(HWND) function coming from my win32C keygen 
which works for each Winzip version, from 6.3 to 8.0 : 

---------------------------------------------8<--------------------------------------------------
int keygen(HWND hwnd)
{
	
	char name[42] = "";
	char result[9] = "";
	int i, j, len;
	unsigned long serial1 = 0, serial2 = 0;
	unsigned long temp = 0, arg = 0, shit = 0, ctrl = 0;

	GetDlgItemText(hwnd, IDC_NAME, name, 42);
	len = strlen(name);

	while(name[len-1] == ' ') len--;

	name[len] = 0x00;

	while(name[0] == ' ') for(i = 0; i < len; ++i) name[i] = name[i+1];

	len = strlen(name);

	if(len == 0)
	{
		SetDlgItemText(hwnd, IDC_SERIAL, "Please enter your name/don't just put spaces.");
		return 1;
	}
	else if(len > 40)
	{
		SetDlgItemText(hwnd, IDC_SERIAL, "Name has to be less than 41 chars.");
		return 1;
	}

	for(i = 0; i < len; ++i) serial1 += name[i] * i;

	for(i = 0; i < len; ++i)
	{
		arg = name[i] << 8;

		for(j = 0; j < 8; ++j)
		{
			temp = serial2;
			temp ^= arg;
			ctrl = temp & 0x8000;
			if(ctrl == 0)
			{
				serial2 <<= 1;
				arg <<= 1;
			}
			else
			{
				serial2 += serial2;
				serial2 ^= 0x1021;
				arg <<= 1;
			}
		}
	}

	serial2 += 0x63;
	serial2 %= 0x10000;

	wsprintf(result, "%.4X%.4X", serial2, serial1);
	SetDlgItemText(hwnd, IDC_SERIAL, result);

	return 0;
}
---------------------------------------------8<--------------------------------------------------

Voila ! ;-)


============
5)Greetings
============

Here comes the longest part of the tutorial, the greetings !

Greets go to : tHE Analyst, Warez Pup, risc, lazarus, mercution, falcon, f0dder, MagicRaphoun,
Tamambolo, TSCube, JB007, vrom ohh mon vrom, kahel, GanJaMan, Inferno, zoltan, Phoenix, wizdaz, 
promethee, stealthy, Syntax3rr, roy|, BlndAngl, StatMan, LutinNoir, Fenorez, Vylent, Cell, 
BMonkey, Robocop, TeChNiCh, Volatility, Am4nte, SantMat, FatBoyJoe, vissie, grugq, JosephCo, 
yAtEs, C_DKnight, sinn0r, eternal-bliss, Aragonx, Lucifer48, Blaze, Bladey, KaKtuZ, Grunt, NamNT,
BoomBox, KarlitoxZ, Crudd, LordOfLa, alpine, Sushi, xor, AbsoluteB, norika, Tresni, webmasta
DnNuke... and all i dare have forgotten :p

				
				Seifer[ECLiPSE/HellForge]

seifer666@caramail.com
#ICQ : 61545376