Information
Title Registry Crawler 2.1
Developers 4Developers
Home page http://www.4developers.com
Author Gem 'n' Eye
When? June 2000
Where? The Netherlands
What? Serial fishing (a bit), Key Generator
Why? Knowledge sharing
Notes I am still a newby in the world of craking. And to be honest this is my first tutorial I have ever written
Where is I? the_anomaly@hotmail.com
Tools SoftIce 4.00 by Numega
WinDasm 8.9 by UrSoft
Turbo Pascal 6.0 by Borland (for the keygen)
Rating Very Easy (X) Medium ( ) Hard ( ) Pro ( )
Disclaimer
This TUTORIAL is meant only for those who want to learn
the art of reverse engineering . As the word tutorial says learn from it, but
if you continue to use this application buy it.

About this protection
When the application is ran it will display a nag screen
with a title saying: "Registry Crawler 2.1 (30 Day trial version).
Well, at least we now know we have the correct application...

As you, most likely, already noticed the application presented us
with the ability to unlock (yep, the button saying unlock) this so
called '30 Day trial'. So fill out a username and a registration
code and press the unlock button

My oh My, we get a messagebox saying our registration is incorrect.
[if you god by an chance a messagebox saying you registered this
aplication then you just did some zen-cracking]

I like that messagebox it is at least a point from where we can start
cracking this baby

The Essay
I'll just start by telling you that should begin with running the
application. Followed by entering a username and a fake registration
code. I used

Username : Gem 'n' Eye
Registration code : 123321

Fire up Sice with "CTRL-D" and setting a breakpoint at
GetWindowTextA [ bpx GetWindowTextA ].
Leave Sice by pressing "CTRL-D"
Press the unlock button to verify if our regcode is correct

Sice now breaks at the beginning of the GetWindowTextA function
Leave Sice by pressing CTRL-D and it should break again
Press F11
Press F12
Press F10 14 times
Now we are are at call call 00404BFF. This call calls the function which
generates our registration code.
Ok, lets trace into this call by pressing F8
This is were we stand now. I'll explain the following code as far as it is
interesting to us. I marked the interesting part in red.

:004048C0 64A100000000     mov eax, dword ptr fs:[00000000]
:004048C6 6AFF             push FFFFFFFF
:004048C8 68803E4300       push 00433E80
:004048CD 50               push eax
:004048CE 64892500000000   mov dword ptr fs:[00000000], esp
:004048D5 83EC10           sub esp, 00000010
:004048D8 83C9FF           or ecx, FFFFFFFF   ; this piece of code calcs length of a string
:004048DB 33C0             xor eax, eax
:004048DD 56               push esi
:004048DE 57               push edi
:004048DF BF205E4400       mov edi, 00445E20  ; edi = username
:004048E4 F2               repnz
:004048E5 AE               scasb
:004048E6 F7D1             not ecx
:004048E8 49               dec ecx            ; ecx = length of username

:004048E9 83F908           cmp ecx, 00000008  ; if length username < 8 then error
:004048EC 7311             jnb 004048FF       ; if not below [nb] then calculate serial

Well what is happening here? You ask! First of all this piece of code retrieves
the length of your username and checks wether or not your username is at least
8 characters long. Remember this, because we will need it for our key generator
If our name has the correct size then jump to the code for serial generation...

Here our pretty code starts. Lets continue reversing this nice application.

* Possible StringData Ref from Data Obj ->"8267-"
                           |
:004048FF 68B8374400       push 004437B8
:00404904 8D4C2414         lea ecx, dword ptr [esp+14]
:00404908 E884A70100       call 0041F091

What happens here? Look it over a bit again and think: "could it be that the string
reference here is the start of our registration code or is...." Halt, stop, quit do
not continue thinking. You are correct, all regcodes start with '8267-' .
This will be shown later on... Lets continue...

:0040490D 8BF0             mov esi, eax
:0040490F BF205E4400       mov edi, 00445E20  ; edi = name
:00404914 83C9FF           or ecx, FFFFFFFF   ; start calculating name length again
:00404917 33C0             xor eax, eax
:00404919 33D2             xor edx, edx
:0040491B F2               repnz
:0040491C AE               scasb
:0040491D F7D1             not ecx
:0040491F 49               dec ecx            ; ecx = length name
:00404920 C744242000000000 mov [esp+20], 00000000
:00404928 8BC1             mov eax, ecx       ; eax = length name
:0040492A B90C000000       mov ecx, 0000000C  ; ecx = 12
:0040492F F7F1             div ecx            ; eax = eax div 12, mod is placed in edx

* Possible StringData Ref from Data Obj ->"YMA19X@24$Z%" ; lets call this string strCode
                           |
:00404931 A15C344400       mov eax, dword ptr [0044345C]
:00404936 8A0C02           mov cl, byte ptr [edx+eax]   ; cl = strCode[edx]
:00404939 8D542408         lea edx, dword ptr [esp+08]  ; edx = pointer to regcode
:0040493D 51               push ecx
:0040493E 56               push esi
:0040493F 52               push edx
:00404940 E8CD3A0100       call 00418412
:00404945 8D4C2410         lea ecx, dword ptr [esp+10]
:00404949 C644242002       mov [esp+20], 02
:0040494E E8D0A60100       call 0041F023
:00404953 BF205E4400       mov edi, 00445E20            ; edi = name
:00404958 83C9FF           or ecx, FFFFFFFF             ; start calculating length name
:0040495B 33C0             xor eax, eax
:0040495D 33F6             xor esi, esi
:0040495F F2               repnz
:00404960 AE               scasb
:00404961 F7D1             not ecx
:00404963 49               dec ecx                      ; ecx = length name
:00404964 0F8498000000     je 00404A02                  ; if zero (no name) then exit

This piece of code, in case you missed it, calculates then length of your
username and divides it by 12. The modulo is placed into edx. Ok this is all
nice but what do I care! Well, you care, why? because the modulo is used
to get the first 'dynamic' char for our regcode. The string I called strCode
is used throughout the regcode calculation code for your regcode. Here the
modulo is used to get the first char from strCode and this char is appended
to our regcode. If you do not understand what is happening here then sit back
relax and look at it again. When you understand this then the rest of the code
is easy.

Here starts the code that further generates the serial. This is quite a big
loop and I shall only comment on the assembler lines and afterwards discuss
it with you. Explaining what it excatly does.

A note: esi is used as counter

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:004049FC(C)
|
:0040496A 8BC6             mov eax, esi         ; eax = counter
:0040496C 33D2             xor edx, edx         ; edx = 0
:0040496E B903000000       mov ecx, 00000003    ; ecx = 3
:00404973 F7F1             div ecx              ; eax = eax / 3, modulo in edx
:00404975 85D2             test edx, edx        ; is edx zero (=> counter mod 3 = 0?)
:00404977 7571             jne 004049EA         ; if not then do not use this character
:00404979 8A86205E4400     mov al, byte ptr [esi+00445E20]  ; al = name[counter]
:0040497F 3C7F             cmp al, 7F           ; is al > 7Fh?
:00404981 0F8F02010000     jg 00404A89          ; yes then error
:00404987 3C20             cmp al, 20           ; is al < 20h
:00404989 0F8CFA000000     jl 00404A89          ; yes then error
:0040498F 0FBEC0           movsx eax, al
:00404992 99               cdq
:00404993 2BC2             sub eax, edx
:00404995 D1F8             sar eax, 1           ; eax = eax div 2
:00404997 0422             add al, 22           ; al = al + 22
:00404999 3C5A             cmp al, 5A           ; is al > 5Ah
:0040499B 8844240C         mov byte ptr [esp+0C], al  ; save char in [esp+c]
:0040499F 7E0A             jle 004049AB         ; yes then jump
:004049A1 3C61             cmp al, 61           ; if al < 61h
:004049A3 7D06             jge 004049AB         ; yes then jump
:004049A5 0406             add al, 06           ; al = al + 6
:004049A7 8844240C         mov byte ptr [esp+0C], al  ; save char in [esp+c]

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:0040499F(C), :004049A3(C)
|
:004049AB 3C39             cmp al, 39           ; if al > 39h
:004049AD 7E0A             jle 004049B9         ; yes then jump
:004049AF 3C41             cmp al, 41           ; if al < 41h
:004049B1 7D06             jge 004049B9         ; yes then jump
:004049B3 0408             add al, 08           ; al = al + 8
:004049B5 8844240C         mov byte ptr [esp+0C], al  ; save char in [esp+c]

* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:004049AD(C), :004049B1(C)
|
:004049B9 8B54240C         mov edx, dword ptr [esp+0C]  ; edx = calculated char
:004049BD 8D442408         lea eax, dword ptr [esp+08]  ; eax = pointer to regcode
:004049C1 52               push edx
:004049C2 8D4C2418         lea ecx, dword ptr [esp+18]
:004049C6 50               push eax
:004049C7 51               push ecx
:004049C8 E8453A0100       call 00418412                ; append char to end of regcode
:004049CD 50               push eax
:004049CE 8D4C240C         lea ecx, dword ptr [esp+0C]
:004049D2 C644242403       mov [esp+24], 03
:004049D7 E834A70100       call 0041F110
:004049DC 8D4C2414         lea ecx, dword ptr [esp+14]
:004049E0 C644242002       mov [esp+20], 02
:004049E5 E839A60100       call 0041F023

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00404977(C)
|
:004049EA BF205E4400       mov edi, 00445E20                   ; edi = name
:004049EF 83C9FF           or ecx, FFFFFFFF                    ; calculate length name
:004049F2 33C0             xor eax, eax
:004049F4 46               inc esi                             ; increase counter
:004049F5 F2               repnz
:004049F6 AE               scasb
:004049F7 F7D1             not ecx
:004049F9 49               dec ecx                             ; ecx = length name
:004049FA 3BF1             cmp esi, ecx                        ; Reached the length of name?
:004049FC 0F8268FFFFFF     jb 0040496A                         ; if not then loop again

Yes, here we have got the 'main' for the regcode generation routine.
This routine uses your name and calculates and appends characters.
How is this done? Well it is pretty easy. First of all, only characters
which have an index dividable by 3 are used, the others are discarded.
Then it checks if the character is within range ($20 <= char <= &7F)
if not then an error is generated [@:0040497F]
Then the character is taken and divided by 2 [@:00404995] and
incremented by 22h [@::00404997].
Then it checks if the character is not an alphabatic character (upper
and lowercase) or a digit. If not then it adjusts the character so
the regcode only exists of alphabatic characters and digits. At last
at this character to the regcode (call @ :004049C8).
Finally it gets the length of the username and compares it with our
counter (esi) if it has reached the end of our name then do not loop.

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00404964(C)
|
:00404A02 8B542408         mov edx, dword ptr [esp+08]         ; edx = pointer to regcode
:00404A06 B90C000000       mov ecx, 0000000C                   ; ecx = 12
:00404A0B 8B42F8           mov eax, dword ptr [edx-08]         ; eax = length regcode
:00404A0E 99               cdq
:00404A0F F7F9             idiv ecx                            ; eax = eax / 12, modulo in edx

* Possible StringData Ref from Data Obj ->"YMA19X@24$Z%" ; Hey its strCode again
                           |
:00404A11 A15C344400       mov eax, dword ptr [0044345C]       ; eax = strCode
:00404A16 8A0C02           mov cl, byte ptr [edx+eax]          ; cl = strCode[edx]
:00404A19 8D542408         lea edx, dword ptr [esp+08]         ; edx = pointer to regcode
:00404A1D 51               push ecx
:00404A1E 8D442418         lea eax, dword ptr [esp+18]
:00404A22 52               push edx
:00404A23 50               push eax
:00404A24 E8E9390100       call 00418412                       ; add last char to regcode
:00404A29 50               push eax
:00404A2A 8D4C240C         lea ecx, dword ptr [esp+0C]
:00404A2E C644242404       mov [esp+24], 04
:00404A33 E8D8A60100       call 0041F110
:00404A38 8D4C2414         lea ecx, dword ptr [esp+14]
:00404A3C C644242002       mov [esp+20], 02
:00404A41 E8DDA50100       call 0041F023

Well this is the last piece of code we have to reverse te complete the
registration code generation routine. What is done here? The same as the
first 'dynamic' char added to our regcode. Only now the length of the
regcode is used instead of the length of our name.
I think you can handle this one on your own, just look at the first character generation...

:00404A46 8B4C2408         mov ecx, dword ptr [esp+08]          ecx = pointer to regcode
:00404A4A 68205F4400       push 00445F20
:00404A4F 51               push ecx
:00404A50 E8C9470000       call 0040921E
     °        °                °
     °        °                °
     °        °                °
     °        °                °

Above piece of code is used to fish the serial

Serial fishing
Put a breakpoint on 00404a50 [ bpx 00404a50 ]. When a break in Sice
Step over by pressing F10
Type: d ecx    <--- shows us the memory location of the regcode
Type: d [memory adress shown with above dump]

Key generator code
Here I present my Pascal source that generates the registration codes
according to the above explained serial generation routine.

{$X+}
Program Registry_Crawler_Key_Generator;

Uses
  Crt;

Const
  strCode : string = 'YMA19X@24$Z%';

var
  name : string;
  CorrectSerial : string;
  i : integer;
  c : byte;

begin
  WriteLn('Registry Crawler 2.1 Key Generator');
  WriteLn('');
  WriteLn(' By -= Gem ''n'' Eye =-');
  WriteLn('');
  write('User Name: ');
  readln(name);
  WriteLn('');

{ check if username is at least 8 characters in length }
  if (length(name) < 8) then begin
    writeln('Your name must be at least 8 characters long.');
    halt;
  end;

  CorrectSerial := '8267-' + strCode[(length(name) mod 12) + 1];
  for i := 0 to length(name) - 1 do begin
    if (i mod 3 = 0) then begin
      c := ord(name[i + 1]);

      { check if char between $20 and $7F if not then error and halt}
      if (c < $20) or (c > $7F) then begin
        Write(chr(7));
        WriteLn('Invalid characters used in UserName.');
        halt;
      end;

      c:= (c shr 1) + $22;
      if (c > $5A) and (c < $61) then
        c := c + 6;
      if (c > $39) and (c < $41) then
        c := c + 8;
      CorrectSerial := CorrectSerial + chr(c);
    end;
  end;
  CorrectSerial := CorrectSerial + strCode[(length(CorrectSerial) mod 12) + 1];

  WriteLn('Your serial number: ' + CorrectSerial);
  WriteLn('');
  WriteLn('Hit any key to close down key generator');
  readkey;
end.

Quote

What crawls closer every second, but does never reach you...

Greetz
Thanks to: +ORC, Sandman, HarvestR, tKC, ytc_, Punisher, Kwai_Lo, TORN@DO, Crackz, cLUSTER, LaZaRuS, mrfanatic, Shadow, ManKind and other crackers and individuals who provide me with their tutorials and tools.