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
OK...study 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];
UpdateData(TRUE);
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--)
{
Temp=Accumulator;
Temp=Temp^iCX;
if (Temp & 0x8000)
{
Accumulator += Accumulator;
Accumulator = Accumulator ^ 0x1021;
iCX *=2;
}
else
{
Accumulator *= 2;
iCX *=2;
}
}
}
Accumulator += 0x63;
//Generate second half of serial
m_iDecTotal=0;
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);
}
else
{
sprintf(m_cHexString,"%04X%X",Accumulator & 0xffff,m_iDecTotal);
}
m_strSerial=m_cHexString;
UpdateData(FALSE);
}
GREETZ & THANX to all of the people who've helped me @ the Newbies Forum. (I'm too damn lazy to type all the names ;-))