Winzip Tut #4
Key Generator
by ?ferret

Skill Level: Newbie
Attack Plan: Keygen
Target: Winzip 8.0

Tools Needed: 	Softice
		Compiler of choice

Hola Compadres!

In this tutorial, I'll be showing you how to find the serial generation algorithm, in order to make a keygen. This is probably the easiest target you'll ever run across for this method (or any method for that matter hehe).

Set a breakpoint on GetDlgItemTextA and try to reg the program. Use a name that you used in the serial fishing excercise, so you know the real serial number (but don't use the real number). Hit CTRL-D once and Softice will break again (once for name, once for serial). Hit F11 to return to Winzip code. You should land at address CS:407F95.

:00407F95 56                      push esi
:00407F96 E8FF780300              call 0043F89A
:00407F9B 56                      push esi
:00407F9C E822790300              call 0043F8C3

//is name empty?
:00407FA1 803D78CD480000          cmp byte ptr [0048CD78], 00 

:00407FA8 59                      pop ecx
:00407FA9 59                      pop ecx

//yes, jump to bad guy routine
:00407FAA 7459                    je 00408005

//is serial # slot empty?
:00407FAC 803DA4CD480000          cmp byte ptr [0048CDA4], 00

//Yes, jump to bad guy routine
:00407FB3 7450                    je 00408005

//Generate serial somewhere in here, compare to ours, return value in EAX
:00407FB5 E81BFAFFFF              call 004079D5

//is EAX 0?
:00407FBA 85C0                    test eax, eax

Yes, jump to bad guy routine
:00407FBC 7447                    je 00408005

Well, that was easy to find the call we need to dive into. Let's do just that.

F10 through this call watching the registers. At CS:407AAA, EAX is pushed. It contains the real serial. Oops, we went too far.

Just above this, we see LEA EAX, [EBP-0140]....twice....with a CALL between them. Since the second occurence must put our real number in EAX for the PUSH, let's go through the routine again & see what it is at the first occurence. Set a breakpoint on this line (CS:407A91).

//hmmm...what's in ebp-0140 here? :00407A91 8D85C0FEFFFF lea eax, dword ptr [ebp-0140] :00407A97 50 push eax :00407A98 57 push edi :00407A99 E8A9000000 call 00407B47 //puts our name in esi :00407A9E BEA4CD4800 mov esi, 0048CDA4 //puts real serial in eax :00407AA3 8D85C0FEFFFF lea eax, dword ptr [ebp-0140]

Nothing interesting in EAX after CS:407A91. That means our serial is placed in ebp-0140 somewhere in the call at address CS:407A99. Dive into this call.

//Place 1st char of name in DL
:00407B50 8A11                    mov dl, byte ptr [ecx]

//set registers to 0
:00407B52 33DB                    xor ebx, ebx
:00407B54 33C0                    xor eax, eax

//Put name in ESI
:00407B56 8BF1                    mov esi, ecx

//Set EDI to 0
:00407B58 33FF                    xor edi, edi

//Start a loop
//Is DL empty?
:00407B5A 84D2                    test dl, dl

//If yes, done with loop, jump out
:00407B5C 7410                    je 00407B6E

//If no, continue loop
:00407B5E 660FB6D2                movzx dx, dl

//char * position in string (ie name ="char", 0x63 * 0; "c", position)
:00407B62 0FAFD7                  imul edx, edi

//Add the result to an accumulator
:00407B65 03DA                    add ebx, edx

//Put next char of name in DL
:00407B67 8A5601                  mov dl, byte ptr [esi+01]

//Increment the counter
:00407B6A 47                      inc edi

//Move ESI to the next char of the name string
:00407B6B 46                      inc esi

//Back to the beginning of the loop
:00407B6C EBEC                    jmp 00407B5A

This code quite simply takes each character, multiplies it by the
position in the string, and adds the result to an accumulator.
The hex notation for the finished product of this loop (in EBX)
is the last 4 characters of the serial.

OK, now there's not much that we need to do except decipher the ASM code for the rest of the serial. So, I'm not going to write alot. I'll simply paste the ASM and comment the hell out of it to help you understand what's going on ;-)

:00407B78 8BF1                    mov esi, ecx
//Put next char of name into CL
:00407B7A 8A09                    mov cl, byte ptr [ecx]

//Is CL empty? (past end of name)
:00407B7C 84C9                    test cl, cl

//Yes, exit loop & continue with program
:00407B7E 7419                    je 00407B99

//No, continue loop
//Put the char in CX
:00407B80 660FB6C9                movzx cx, cl

// push 0x1021h
:00407B84 6821100000              push 00001021
:00407B89 51                      push ecx
:00407B8A 50                      push eax

//Dive into this call, it's in a loop that executes 
//based on number of chars in name, must be important ;-)
:00407B8B E829000000              call 00407BB9

:00407BB9 55                      push ebp
:00407BBA 8BEC                    mov ebp, esp

//Product of the operations carried out to 
//figure 1st half of serial thus far is accumulated in EAX
:00407BBC 8B4508                  mov eax, dword ptr [ebp+08]

:00407BBF 56                      push esi
:00407BC0 33C9                    xor ecx, ecx                        |
:00407BC2 6A08                    push 00000008
//put char in CH
:00407BC4 8A6D0C                  mov ch, byte ptr [ebp+0C]

//Put the 08 that was pushed into EDX (another loop counter)
:00407BC7 5A                      pop edx

//use ESI as a temporary place to store EAX 
//and do math operations on it
:00407BC8 8BF0                    mov esi, eax

//XOR ESI with (char * 100)(CH is the left byte of the lower word of ECX....xx=CH, xx00=ECX)
:00407BCA 33F1                    xor esi, ecx

//Is the leftmost bit of SI > or < 7?
:00407BCC 66F7C60080              test si, 8000

//Less than 7, jump
:00407BD1 7407                    je 00407BDA

//Greater than 7, keep going
//Add Accumulator to itself
:00407BD3 03C0                    add eax, eax

//XOR accumulator with 0x1021 that was pushed before entering this call
:00407BD5 334510                  xor eax, dword ptr [ebp+10]

//Skip the next line
:00407BD8 EB02                    jmp 00407BDC

//EAX = EAX * 2
:00407BDA D1E0                    shl eax, 1

//ECX=ECX * 2
:00407BDC D1E1                    shl ecx, 1

//Decrement the counter
:00407BDE 4A                      dec edx

//If not down to 0, jump to beginning of loop
:00407BDF 75E7                    jne 00407BC8
:00407BE1 5E                      pop esi
:00407BE2 5D                      pop ebp
:00407BE3 C3                      ret

//Add 0x63h to the accumulator
:00407B99 83C063                  add eax, 00000063

//Put the 2nd 1/2 of serial in ECX
:00407B9C 0FB7CB                  movzx ecx, bx

//Put 1st 1/2 of serial in EAX
:00407B9F 0FB7C0                  movzx eax, ax
:00407BA2 51                      push ecx
:00407BA3 50                      push eax the commented code above forn awhile, and probably go thrugh it in softice so you can actually see the changes being made. After you think you understand what's going on, you can try to write your own routine to do this. I have included my C++ generation routine from my keygen as an example.

void CZipCodeDlg::OnGenerate() 
{ //Generate 1st half of serial

	int count=0;
	int NameCount=0;
	int Temp=0;
	int Accumulator=0;
	int iCX=0;
	char TempChar;
	char m_cHexString[9];
	m_iNameLength =strlen(m_strName);              

	for (NameCount = 0; NameCount < m_iNameLength; NameCount ++)      
		TempChar = (m_strName.GetAt(NameCount));
		iCX= (TempChar * 0x100);
		for (count = 8; count ">" 0; count--)  
			if (Temp & 0x8000)
				Accumulator += Accumulator;
				Accumulator = Accumulator ^ 0x1021;
				iCX *=2;

				Accumulator *= 2;
				iCX *=2;
	Accumulator += 0x63;

	//Generate second half of serial


	for (count=0;count "<" m_iNameLength;count++)      
		m_iDecTotal += (m_strName[count] * count); 
	if(m_iDecTotal < 4096)							
		sprintf(m_cHexString,"%04X0%X",Accumulator & 0xffff,m_iDecTotal);      
		sprintf(m_cHexString,"%04X%X",Accumulator & 0xffff,m_iDecTotal);      


GREETZ & THANX to all of the people who've helped me @ the Newbies Forum. (I'm too damn lazy to type all the names ;-)) push eax