Courtesy of Fravia's page
of reverse engineering
A complete, nake protection code: Cracking WinZip32
CapedCrusader is slicing here all "the meat" out of a fat protection scheme, yummm, quite interesting reading IMHO... I get hundred of letters by people
that ask me "Where can I learn assembly?" or "How can I understand and feel this code?"
or "Could you please explain me the real meaning of this code..."
Stupid lamer questions? I don't think so: go in your local bookstores and
have a look at the shelves: count the poor half-forgotten books about assembly! You'll be lucky if
you find any at all!
Compare with the infinite (and useless) books about,
say, "How to understand MSIE during 12 dinner meals" or "Visual C++ for fat
readers"
And yet it's so easy to learn assembly... It's so easy to get the power you
want! Study
commented ASM! Take a small windozed program and reverse it to the bones! Study the old DOS
viruses, THEY used at times code tricks you can only dream of
in these dire days of software programming
decadence!
Produce your own c programs and THEN have a look at them through
winice and through wdasm, for Godzilla's sake!
And, right now, read the
comments to the keygen by CapedCrusader below. Have a good reading, there is a lot
to learn and understand for beginners and interemediate crackers and protectors alike in here!
~
31 march 1998, slightly edited
by your truly+
Not Assigned
Cracking WinZip32 v6.3, by CapedCrusader
Back in the DOS days, I thought I was quite the cracker. My friend ran
a BBS and I got him all the reg codes for his games. Back then all a
rookie like myself had access to was Turbo Debugger for DOS. But things
change. Now that Windoze is around (probably to stay :( and now that
I've finally learned how to crack Windoze stuff, it seems I may be able
to catch up and relearn how to crack programs successfully.
So for my first (what I thought would be) difficult program, I decided
to develop a crack for WinZip. After all, unless you want to spend time
opening and closing DOS windows, everybody will want a decent Windoze
zip program, right? Most people I know have WinZip, so it seemed the
perfect target. I even thought it would be a challenge. Not quite, so
I guess this will have to be considered a basic crack.
Where do you start with a program like WinZip? At first glance, it
looks like all you can do with it is remove the nag screen, because it
never openly asks you for a registration code. The programmer obviously
must have been quite proud of this "protection" scheme. Of course, for
any moron who uses WDasm, you quickly find that there are several
occurences of the word "register":
Name: DialogID_0C82, # of Controls=009, Caption:"Register WinZip"
008 - ControlID:0C84, Control Class:"" Control Text:"Continue
Unregistered"
and if that wasn't enough to convince you, along with many other
listings, I found this:
* Possible StringData Ref from Data Obj ->"Registered users, please enter "
->"your name and registration number "
->"EXACTLY as they appear on the"
->"instructions."
So I looked around thinking, "Maybe they're just trying to confuse
crackers? After all, who'd actually try to hide something as obvious as
a registration command?" For the sake of the programmers as well as
humanity, I pray that they simply couldn't find a better place to put
the registration information... yet the fact that their help files don't
give you any information about the instant registration makes me more
than a little skeptical.
Well, after a whopping 5 minutes I checked the help menu and "about".
Lo and behold, there it was, the "Register" command. Jackpot!! As you
would expect of me, I decided to try out a name and registration code.
To make things easier, it allows you to enter a maximum of 8 digits into
the reg code line. Guess how many characters the reg code is?
Anyway, after entering the incorrect info, it yelled at me, saying the
following, "Incomplete or incorrect information. Please press F1 for
help." What a joke! Not even a generic or hard to find message, but a
one-of-a-kind:
* Reference to String Resource ID=00654: "Incomplete or incorrect information"
|
:00409D97 688E020000 push 0000028E
:00409D9C E82EB10100 call 00424ECF
well, let's not make this too hard. Let's see what's right before this
code. Maybe I'll even comment it for you... not that it needs it...
* Reference To: USER32.GetDlgItemTextA, Ord:00F5h
|
:00409D58 FF15C86A4700 Call dword ptr [00476AC8]
:00409D5E 6A0A push 0000000A
:00409D60 6878F54600 push 0046F578
* Possible Reference to Dialog: DialogID_0C82, CONTROL_ID:0C81, ""
|
:00409D65 68810C0000 push 00000C81
:00409D6A FF7508 push [ebp+08]
* Reference To: USER32.GetDlgItemTextA, Ord:00F5h
|
:00409D6D FF15C86A4700 Call dword ptr [00476AC8]
let me guess, we just got the name and reg code dialog boxes and saved
them into 471258 and 46F578, right? In fact, 471258 is your name and
46F578 is your reg code.
:00409D73 0FB60558124700 movzx eax, byte ptr [00471258]
:00409D7A 85C0 test eax, eax
:00409D7C 7414 je 00409D92
if first letter of your name doesn't exist, goto "incomplete..." message
box, else pass to next check
:00409D7E 0FB60578F54600 movzx eax, byte ptr [0046F578]
:00409D85 85C0 test eax, eax
:00409D87 7409 je 00409D92
if first letter of reg code doesn't exist, goto "incomplete..." message
box, else pass to next check
:00409D89 E85CF9FFFF call 004096EA
:00409D8E 85C0 test eax, eax
:00409D90 7541 jne 00409DD3
and if eax is zero after a single, lone call, we go down to the bad
message. Too easy.
* Referenced by a Jump at Addresses:00409D7C(C), :00409D87(C)
|
:00409D92 E805020000 call 00409F9C
* Reference to String Resource ID=00654: "Incomplete or incorrect information"
|
:00409D97 688E020000 push 0000028E
:00409D9C E82EB10100 call 00424ECF
So here we are again, and all we can do is wonder, what's in that
function that makes eax be 0 or non-zero?? Well, if you want to waste
time reading through the code, go ahead. I just ran softice, setting a
break point at the above function, and traced through it watching for
suspicious memory reading/writing. I noticed lots of weird stuff going
on, but one really interesting thing caught my eye:
:004097DE 8D85F8FDFFFF lea eax, dword ptr [ebp+FFFFFDF8]
:004097E4 50 push eax
:004097E5 6858124700 push 00471258
:004097EA E8D4000000 call 004098C3
No, this wasn't the only function to send our friend 471258 (your name,
remember?) off to another call. However, the eax that was pushed held a
memory address, and being the semi-intelligent person that I am, I
always observe memory addresses before and after a call, and I noticed
that this memory address suddenly contained a null-terminated 8-digit
string. Gee, I wonder what this could be? I punched in the code along
with my name and bingo, it worked. How stupid could we get? I spent
probably an hour to this point and I already had my very own reg code.
It wasn't even satisfying. My string was 8 digits, of course, and had
numbers and a few letters: "B97719CA". Looks much like a hex string,
no?
That's when I decided to get myself a reg code generator... stupid protection
schemes deserve it, besides there are already many ready-made keygen on the web
for the lemurs-lamers that want to steal this target, and so this one here,
that I will explain you for studying purposes, won't damage anybody.
Let's look at the code that last function called.
:004098C3 55 push ebp
:004098C4 8BEC mov ebp, esp
:004098C6 83EC10 sub esp, 00000010
:004098C9 668365F400 and word ptr [ebp-0C], 0000
:004098CE 668365F800 and word ptr [ebp-08], 0000
:004098D3 8B4508 mov eax, dword ptr [ebp+08] ; get your name
:004098D6 8945FC mov dword ptr [ebp-04], eax ; and save it locally
:004098D9 668365F000 and word ptr [ebp-10], 0000 ; set counter = 0
:004098DE EB13 jmp 004098F3 ; skip increment code
* Referenced by a Jump at Address:00409915(U)
|
:004098E0 668B45F0 mov ax, word ptr [ebp-10]
:004098E4 66050100 add ax, 0001 ; add one to the
:004098E8 668945F0 mov word ptr [ebp-10], ax ; counter
:004098EC 8B45FC mov eax, dword ptr [ebp-04]
:004098EF 40 inc eax ; go to next letter
:004098F0 8945FC mov dword ptr [ebp-04], eax ; of your name
* Referenced by a at Address:004098DE(U)
|
:004098F3 8B45FC mov eax, dword ptr [ebp-04] ; get your name
:004098F6 0FB600 movzx eax, byte ptr [eax] ; get one letter
:004098F9 85C0 test eax, eax ; are we done?
:004098FB 741A je 00409917 ; jump to end if so
:004098FD 8B45FC mov eax, dword ptr [ebp-04] ; get your name again
:00409900 0FB600 movzx eax, byte ptr [eax] ; get letter again
:00409903 0FB74DF0 movzx ecx, word ptr [ebp-10] ; and get counter
:00409907 0FAFC1 imul eax, ecx ; letter
* counter
:0040990A 668B4DF4 mov cx, word ptr [ebp-0C] ; and save result
:0040990E 6603C8 add cx, ax ; into ongoing
:00409911 66894DF4 mov word ptr [ebp-0C], cx ; secondary counter
:00409915 EBC9 jmp 004098E0 ; repeat until done
once we're done with that last step, we can look ahead and see that the
number we just got (ebp-0C) is combined with another (ebp-08) in a
function that has a printf string as a variable... an 8-digit
hexadecimal string. Very clever of the WinZip team, converting a number
into hex and calling it a registration code. The next few lines simply
prepare your name for the next function, which produces our last 4
digits.
* Referenced by a at Address:004098FB(C)
|
:00409917 C7055C1C470001000000 mov dword ptr [00471C5C], 00000001
:00409921 8B4508 mov eax, dword ptr [ebp+08] ; get name again
:00409924 8945FC mov dword ptr [ebp-04], eax ; save it again
:00409927 EB07 jmp 00409930 ; skip increment code
* Referenced by a at Address:00409956(U)
|
:00409929 8B45FC mov eax, dword ptr [ebp-04] ; get name
:0040992C 40 inc eax ; increase letter
:0040992D 8945FC mov dword ptr [ebp-04], eax ; and save
* Referenced by a at Address:00409927(U)
|
:00409930 8B45FC mov eax, dword ptr [ebp-04] ; get name
:00409933 0FB600 movzx eax, byte ptr [eax] ; get a letter
:00409936 85C0 test eax, eax ; no letter left?
:00409938 741E je 00409958 ; jump to end if none
:0040993A 6821100000 push 00001021 ; ???
:0040993F 8B45FC mov eax, dword ptr [ebp-04] ; get name
:00409942 660FB600 movzx ax, byte ptr [eax] ; get a letter
:00409946 50 push eax ; push the letter
:00409947 FF75F8 push [ebp-08] ; push new ongoing
:0040994A E831000000 call 00409980 ; counter and call...
:0040994F 83C40C add esp, 0000000C
:00409952 668945F8 mov word ptr [ebp-08], ax ; save counter
:00409956 EBD1 jmp 00409929 ; jump to top again
* Referenced by a at Address:00409938(C)
|
:00409958 668B45F8 mov ax, word ptr [ebp-08] ; get counter
:0040995C 66056300 add ax, 0063 ; add 0x0063
:00409960 668945F8 mov word ptr [ebp-08], ax ; save again
:00409964 0FB745F4 movzx eax, word ptr [ebp-0C] ; get last result,
:00409968 50 push eax ; push it,
:00409969 0FB745F8 movzx eax, word ptr [ebp-08] ; get this result,
:0040996D 50 push eax ; push it,
* Possible StringData Ref from Data Obj ->"%04X%04X"
|
:0040996E 6854394600 push 00463954 ; push a printf code,
:00409973 FF750C push [ebp+0C]
:00409976 E865B90300 call 004452E0 ; and call function
:0040997B 83C410 add esp, 00000010
:0040997E C9 leave
:0040997F C3 ret
So we have the first result being used as the last four digits of the
reg code, and some second result as the first four. After looking at
the function this will make more sense, but for now take my word. The
function uses an ongoing counter (ebp-08) to play with the letters of
your name, and also this strange 1021. Why they would push a constant
number for a function call when this number is never changed, either in
or out of the function is beyond me, unless they were REALLY stupid and
didn't even use their own function. My guess is this is some coding
function, written in assembly by somebody semi intelligent, given to the
winzip people. 1021 must be some sort of key for their reg code
generator. It hurts my head to think about the morons who programmed
this, so let's look at that function...
:00409980 55 push ebp
:00409981 8BEC mov ebp, esp
:00409983 51 push ecx
:00409984 668B450C mov ax, word ptr [ebp+0C] ; get letter (00xx)
:00409988 66C1E008 shl ax, 08 ; move it over (xx00)
:0040998C 6689450C mov word ptr [ebp+0C], ax ; and save it
:00409990 8365FC00 and dword ptr [ebp-04], 0 ; counter = 0
:00409994 EB07 jmp 0040999D ; skip counter++
* Referenced by a at Address:004099DE(U)
|
:00409996 8B45FC mov eax, dword ptr [ebp-04]
:00409999 40 inc eax ; counter++
:0040999A 8945FC mov dword ptr [ebp-04], eax
* Referenced by a at Address:00409994(U)
|
:0040999D 837DFC08 cmp dword ptr [ebp-04], 8 ; counter>=8?
:004099A1 7D3D jge 004099E0 ; if so, go to end
:004099A3 0FB74508 movzx eax, word ptr [ebp+08] ; get ongoing counter
:004099A7 0FB74D0C movzx ecx, word ptr [ebp+0C] ; get letter
:004099AB 33C1 xor eax, ecx ; xor them
:004099AD 2500800000 and eax, 00008000
:004099B2 85C0 test eax, eax ; is eax >= 8000?
:004099B4 7412 je 004099C8
:004099B6 0FB74508 movzx eax, word ptr [ebp+08] ; if so, take the
:004099BA D1E0 shl eax, 1 ; ongoing counter*2
:004099BC 0FB74D10 movzx ecx, word ptr [ebp+10] ; get 1021
:004099C0 33C1 xor eax, ecx ; xor them
:004099C2 66894508 mov word ptr [ebp+08], ax ; save into ongoing
:004099C6 EB0B jmp 004099D3 ; counter & go down
* Referenced by a at Address:004099B4(C)
|
:004099C8 668B4508 mov ax, word ptr [ebp+08] ; eax was 0, so let's
:004099CC 66D1E0 shl ax, 1 ; just mult. ongoing
:004099CF 66894508 mov word ptr [ebp+08], ax ; counter * 2, no xor
* Referenced by a at Address:004099C6(U)
|
:004099D3 668B450C mov ax, word ptr [ebp+0C] ; get letter and
:004099D7 66D1E0 shl ax, 1 ; multiply by 2
:004099DA 6689450C mov word ptr [ebp+0C], ax
:004099DE EBB6 jmp 00409996 ; go to top
* Referenced by a at Address:004099A1(C)
|
:004099E0 668B4508 mov ax, word ptr [ebp+08] ; return ongoing
:004099E4 C9 leave ; counter and leave
:004099E5 C3 ret
WOW!! Yeah, that definately wasn't coded by the losers at WinZip. Too
complicated.
When I made code generators for dos bbs games, I didn't know the first
thing about writing, compiling, or doing much of anything with my own
assembly routines. I translated the assembly into C and wrote code
generators that way. With the first function that wasn't too
tough, but with this one, we'll have to copy the assembly over, make a
few modifications, and either import it into a C program (as I did) or
include the first function and write the whole thing in assembly (much
more stylish, but I'm obsessive about coloring my regcode generators,
which is much easier in C than in assembly... to be honest I don't even
know how in assembly).
The first function goes like this: get a name, letter by letter,
multiply the letter by its subscript number and add to an ongoing
counter. Convert the counter into a hex string and voila! You have the
last 4 digits of your reg code!
The second function is much more involved, so take the following
assembly listing from me, or skip this and make your own. You might
learn something that way...
--------------------------------------------------------------------------------
.MODEL MEDIUM
.CODE
.386
.RADIX 16
PUBLIC _function
_function PROC
ARG OldNumber:WORD, NewLetter:WORD, KeyCode:WORD
push bp
mov bp, sp
push ecx ; get space for bp-04 in a stylish way
mov ax, NewLetter
shl ax, 08
mov NewLetter, ax
and dword ptr [bp-04], 00000000
jmp @POS1
@LOOP1: mov eax, dword ptr [bp-04]
inc eax
mov dword ptr [bp-04], eax
@POS1: cmp dword ptr [bp-04], 00000008
jge @END
movzx ax, OldNumber
movzx cx, NewLetter
xor ax, cx
and ax, 00008000
test ax, ax
je @NO8000
movzx ax, OldNumber
shl ax, 1
movzx cx, KeyCode
xor ax, cx
mov OldNumber, ax
jmp @YES8000
@NO8000:mov ax, OldNumber
shl ax, 1
mov OldNumber, ax
@YES8000:mov ax, NewLetter
shl ax, 1
mov NewLetter, ax
jmp @LOOP1
@END:
mov ax, OldNumber
pop ecx
pop bp
ret
_function endp
END
--------------------------------------------------------------------------------
This function can then be imported into a C program:
--------------------------------------------------------------------------------
#include
#include
#include
extern "C" int function(int OldNumber, int NewLetter, int KeyWord);
void main()
{
char Name[50];
short Counter;
short Return1 = 0, Return2 = 0;
short KeyWrd = 0x1021; // We could make our own reg code generator
// for our own lame programs just by changing
// this number!!
clrscr();
textcolor(4);
cprintf("THIS PROGRAM IS ILLEGAL. HIT ENTER TO ABORT IF YOU HAVE ANY\r\n");
cprintf("PROBLEM WITH THAT FACT.\r\n\n");
textcolor(2);
cprintf("WinZip32 Registration Generator by the Caped Crusader!\r\n");
textcolor(3);
cprintf("Please enter your name: ");
gets(Name);
if (Name[0] == '\0') return;
for (Counter = 1; Counter < (int)strlen(Name); Counter++)
{
Return2 += Name[Counter] * Counter;
}
for (Counter = 0; Counter < (int)strlen(Name); Counter++)
{
Return1 = function(Return1, Name[Counter], KeyWrd);
}
Return1 += 0x0063;
textcolor(14);
printf("Your serial number is: %04X%04X", Return1, Return2);
}
--------------------------------------------------------------------------------
And there you have it, a fully explained registration code
generator! This being my first published crack for the +HCU, I refuse
to have my name, coding style, obsessive use of color in a reg code
generator, or lack of proper cracking terminology criticized without
my prior consent.
Enjoy,
--CapedCrusader--
homepage
links
anonymity
+ORC
students' essays
academy database
tools
cocktails
antismut CGI-scripts
search_forms
mail_Fravia
Is reverse engineering legal?