A nice colleague of mine asked me if I could took a look at this program... I told him I would... when I had some time ... That was last december ( 98 ) Finally last week it was so far :) (begin may 99) I did look at it ... I disassembled it with win32dasm, and looked for the error message : "Registration code entry FAILED." that was the one I was getting :-) and found it :)
* Referenced by a (U)nconditional or (C)onditional Jump at Address:

* Possible StringData Ref from Data Obj ->"Registration code entry FAILED."
:0041A769 68F4734300              push 004373F4
:0041A76E 8D8588EDFFFF            lea eax, dword ptr [ebp+FFFFED88]

This line was referenced from address 41A6BC... so I went there ....

:0041A6B1 E8A2120000              call 0041B958             <<---- the call ..
:0041A6B6 33C9                    xor ecx, ecx
:0041A6B8 8AC8                    mov cl, al
:0041A6BA 85C9                    test ecx, ecx             <<---- the test ..
:0041A6BC 0F84A7000000            je 0041A769               <<---- the jump ..
:0041A6C2 8B8DB8EAFFFF            mov ecx, dword ptr [ebp+FFFFEAB8]
:0041A6C8 E80B010000              call 0041A7D8
:0041A6CD 8B85B8EAFFFF            mov eax, dword ptr [ebp+FFFFEAB8]
:0041A6D3 8B806C0A0000            mov eax, dword ptr [eax+00000A6C]
:0041A6D9 50                      push eax

* Possible StringData Ref from Data Obj ->"%s\pktsts"
:0041A6DA 6858734300              push 00437358
:0041A6DF 8D8588EDFFFF            lea eax, dword ptr [ebp+FFFFED88]
:0041A6E5 50                      push eax
:0041A6E6 E8E5D60000              call 00427DD0
:0041A6EB 83C40C                  add esp, 0000000C
:0041A6EE 8D8588EDFFFF            lea eax, dword ptr [ebp+FFFFED88]
:0041A6F4 50                      push eax
:0041A6F5 E866E50000              call 00428C60
:0041A6FA 83C404                  add esp, 00000004

* Possible StringData Ref from Data Obj ->"CONGRATULATIONS! "
:0041A6FD 6864734300              push 00437364

Look .. this view is soooo nice :-)
It is not for nothing called *reverse* engineering :-)
We start at the dialog... and go back :-)
Now ... we have landed at the final decision point before the program 
determines to show us the bad code dialog. go a bit back ( reverse ) the 
test ... a bit further back ... the call :-))
That must be the call that compares the entered serial to the true serial ...
Dive in that call :-)
Hmmm... nothing ... Only two calls ... The last call .. dive in that one....
(just a zen feeling ;-))

You will land at this point : 

* Referenced by a CALL at Addresses:
|:0041B019   , :0041B983   
:0041B766 55                      push ebp


The two references at the top makes this call suspect. A call which is 
referenced twice, can always be the keygeneration routine which is called at
registration *and* at program startup ...
Now look further ... Man oh man .... what a math ... what a logic operations.
If this is not the keygeneration routine ... Then I am not a cracker ;-)
To verify this, I had to reboot to Softice...

First, print this small call out ... will be nice to make notes while you are
in softice ...
Start this program with the softice loader.. And when the program breaks at 
the program entry point, ( that is when softice shows up after you push the 
Load button in loader ... dont you know how to ? find out ...) Since you are
now at the programs own address space, you can put your breakpoint at address
41B766 ... That is the beginning of our key generation routine .... Press F5 
and exit softice ... If it breaks, press F5 again ...
Now go and enter your name, a license number like 5 ... or something else (31?)
then your serial .... Press the OK button. You will break into softice ... 
right at the start of this routine ....

Do a d eax, and there is your name :)

Trace a few steps untill address 
:0041B772     0FBE450C    movsx eax, byte ptr [ebp+0C]
look at eax :-) there is your number of licenses ...

Trace further till address 
:0041B781     8B4508      mov eax, dword ptr [ebp+08]
do a d eax ... hey ! there is your name :-)

Trace a few steps further till address 
:0041B79A     8B4D10      mov ecx, dword ptr [ebp+10]
do a d ecx ... and just keep tracing .....
and look .. there your serial appears in the data window :-)

That is cool ... we now have the serial ... but do we want to know more ? :-)
read on then ....I will lead you into the Keygens World ;-)

If you carefully analyse the call you printed out on paper, you will see that 
things are done pretty periodically .... Would be imaginable that the code 
takes the characters from your name, and converts them to a serial ... after
applying some strange math ... This is very common ... and pretty popular 
among the programmers :-) 
So if you have seen several of these, you will recognize immediately what is 
going on :-)

:0041B7AE     8B4508       mov eax, dword ptr [ebp+08]
:0041B7B1     0FBE4001     movsx eax, byte ptr [eax+01]

These lines keep coming ... The only thing that changes, is the number after the
eax in : 'byte ptr [eax+xx]'
We already know that dword ptr [ebp+08] stands for our name .... so what goes on 
here is, everytime the next character in the name is taken and moved into eax.

The corresponding c code looks like this : 
eax = name[i]   ;
where i is the character number you want.

Also lines that keep repeating are these ....

:0041B7C4     8B4D10        mov ecx, dword ptr [ebp+10]
:0041B7C7     884101        mov byte ptr [ecx+01], al
:0041B7CA     0FBE450C      movsx eax, byte ptr [ebp+0C]

We know from softice that the program writes our serial to dword ptr [ebp+10]
The factor that changes here is the adder to ecx in line : 'mov byte ptr [ecx+xx]' 
All pretty familiar huh ? :-) What happens is : the newly generated character is
written to the serial :-)

The corresponding c code looks like this : 
serial[i] = (BYTE) eax ;
where i is the character number you want.

Ok.. so much about the name and serial :-)
The rest of the code does some math stuff ...
We do not have to understand this part for 100 % ...
All we have to do is being able to convert it !

I will be converting it to c code ... 
There are just a few operators that are used here ... repeatedly ...
Here I will convert the first character of the name ... the rest you can download
to view how it was done.  
I use variables of type DWORD in c for the registers ...
And renamed the the addressess of the name, serial, and number of licenses to 
variable names ...  
Just look at the code below ...

void CalculateSerial(char * name, int NumLicenses)
   DWORD eax, ebx, ecx, edx;
   char serial[20];

   //:0041B772    0FBE450C          movsx eax, NumLicenses
   //:0041B776    B903000000        mov ecx, 00000003
   //:0041B77B    83C007            add eax, 00000007
   //:0041B77E    99                cdq
   //:0041B77F    F7F9              idiv ecx

   eax = NumLicense;
   ecx = 3;
   eax = 7;
   edx = 0;        // cdq does the same as making edx = 0 
   eax = eax / ecx // result of division put in eax 
   edx = eax % ecx // rest of division put in edx
		   // one idiv does both above operations :-)
   //:0041B781     8B4508       mov eax, name
   //:0041B784     0FBE00       movsx eax, byte ptr [eax]
   //:0041B787     8D441008     lea eax, dword ptr [eax+edx+08]
   //:0041B78B     99           cdq
   //:0041B78C     33C2         xor eax, edx
   //:0041B78E     2BC2         sub eax, edx
   //:0041B790     83E007       and eax, 00000007
   //:0041B793     33C2         xor eax, edx
   //:0041B795     2BC2         sub eax, edx
   //:0041B797     83C032       add eax, 00000032
   //:0041B79A     8B4D10       mov ecx, serial
   //:0041B79D     8801         mov byte ptr [ecx], al

   eax = name[0];         // that means the result of the division 
                          // is not important ! see above ...
                          // so the relevan c code above was : eax = eax % ecx 
   eax = eax + edx;       // the rest is used though        :-)
   eax = eax + 8; 
   edx = 0;
   eax = eax ^ edx;             // the xor in c
   eax = eax - edx;
   eax = eax & 7;               // the and operator in c 
   eax = eax ^ edx;
   eax = eax - edx;
   eax = eax + 0x032;           // to tell the c compiler you are using a hex number,
                                // place a 0x0 infront of the hex number
   serial[0] = (BYTE) eax;      // just take the first byte ... cause eax
                                // consists of 8 bytes :)
   return serial;
Well ... As I said ... what we do here is : Take the license number, do        
some math with it..
Combine the result of this math with the i'th character you get from the name...
Perform some other math stuff ... And there you go .. there is your number for the
i'th character of your serial :-)
That is it what you are getting from me people :-)
The rest of this routine is almost the same ... 
I include here several different versions which I have built up consecutively...
The final version is optimized ... Look through it, and you will understand
how and why :-)

The Keygenerator can also be downloaded from this page. The c code will  
compile with any c compiler that produces win32 pe files ...
You can download a copy of lcc32 from my homepage...

In this tutorial we saw that to reverse engineer, you really have to go in reverse :-)
That once you have found a serial routine, you can reproduce it, so that it does the same
for you in your code :-) Only this time to show the calculated serial, instead        
of comparing it to some entered number. A call with two references are suspicious...  
esspecially when they contain some math stuff ...look better into them ... That the 
characters of your name have to be read ... and this is always a recognizable pattern...