Sentinel License Manager Cracking
Removing need for dongle in Sentinel LM Wlscgen
 
21-NOV-2000
by CyberHeg
Courtesy of Fravia's page of reverse engineering
slightly edited
by +Tsehp
There is a crack, a crack in everything That's how the light gets in
Rating
( )Beginner (X)Intermediate (X)Advanced ( )Expert

The target audience for this essay is reasonably experienced crackers with some ASM coding experience who wish to generate keys for Sentinel License Manger protected products.
Removing need for dongle in Sentinel LM Wlscgen

Written by CyberHeg


Introduction
Wslcgen is a part of Sentinel LM SDK v7.1.0 and it is the license generator that makes
Sentinel LM protected programs activated to a usable state. Reading the manuals of the
SDK says that it needs a special "License meter key". Unless you have this you can not
make any licenses and you will have to buy one from Rainbow Tech. Also when you have
exhausted your key you will have to buy a new one.
My initial guess was that it was a standard Sentinel Super Pro dongle with a counter
and I was right. What makes this target interesting is to see how well Rainbow Tech.
managed to use their own protection methods.



Tools required
Wslcgen.exe from Sentinel LM SDK v7.1.0, IDA v4.04+, Softice v4.05, hex editor (I use hiew and
hexworkshop), SuperPro Developer's Guide from www.rainbow.com, api.txt from the C/C++
interface availble from www.rainbow.com and Killer_3K's superpro flirt sig for IDA
(from CrackZ site) and the Sentinel LM sigs:
w32mcdll.sig ,w32mcst1.sig


Target's URL/FTP
www.rainbow.com

Program History
Uncertain - this appears to be a descendant of the earlier Sentinel License Manager
and the Elan license manager. The models for licensing appear to come from the ancient
"netls" package, but the key generation appears to be totally different.


Essay
Here's a little info on how the dongle and api works. This is by no means a 
replacement for the real manual which is really a must to read in order to 
understand the protection scheme.

Super Pro has 128 bytes of memory. This is split into 64 WORDs (2 bytes long), 
also called cell's counted from 0-63. Since we are talking about WORDs the 
values of these go from 0x0000 (0) till 0xFFFF (65635). The first 8 cells are 
nonwriteable.
Heres a little table of the use of those 8:

Cell no. | use
--------------
0        | Dongle serial number which is unique for every dongle (the number just gets incremented
         | in production of the dongles) (readonly)
1        | Developer ID (to ensure you get hold of a dongle from the right vendor) (readonly)
2        | Overwrite password 1 (no access)
3        | Overwrite password 2 (no access)
4        | Write password (no access)
5        | Reserved by Rainbow Tech. (no access)
6        | Reserved by Rainbow Tech. (no access)
7        | Reserved by Rainbow Tech. (no access)
8-63     | User read and writeable (full access)

Each cell has a value of it's access type
The following table shows the different types:

No. | Type
----------
0   | Read/writeable cell
1   | Readonly cell
2   | Counter cell which can only be decremented
3   | Locked and hidden/algorithm

Once an api calls, a return variable will hold the error code. At 
assembler level this allways refers to EAX. What is important to know is 
that if it is 0 then dongle is found and 3 dongle is not found 
and everything else also means error of some kind.

Here below is a list of the most important api's needed to emulate in general:

SproFindFirstUnit() is the first api called when looking for the dongle. It will 
take the Developer ID as a parameter in order to look for the correct dongle.

SproRead() will read a WORD value from a specific cell. The cell number and 
store address of the read is specified as input parameters.

SproQuery() is a algorithm function. You put in a query value and get a 
response. Since the DWORD algorithm is access code 3 you can't read it out of 
the dongle. That is why emulation of it is different for every program. The 
query value and store addresses used are put in as input parameters.

SproDecrement() decrements a counter cell (access code 2) and takes cell address 
as parameter.

SproActivate() activates a algorithm which was deactivated before.

And now on with the real essay...


When running the program we get a error msg with a problem accesing the meter 
key.

So first we disassemble wlscgen.exe in IDA and apply the sspro.sig. Be sure to 
make a wlscgen.map file, and convert it so we use the map with the 
symbol loader making it more clear what we do when working in Softice.

First we will find SproFindFirstUnit at 00423495. Using api.txt we can also identify
the parameters:

:0042348F                 push    0A870h              ; Developer ID of the Meter Key
:00423494                 push    esi                 ; packet record
:00423495                 call    sproFindFirstUnit

and inside:

:00435B00 arg_0           = dword ptr  0Ch
:00435B00 arg_4           = word ptr  10h
:00435B00 
:00435B00                 push    ebx
:00435B01                 push    esi
:00435B02                 mov     eax, [esp+arg_0]
:00435B06                 or      eax, eax
:00435B08                 jnz     short loc_435B13
:00435B0A                 mov     ax, 2
:00435B0E                 pop     esi
:00435B0F                 pop     ebx
:00435B10                 retn    8

Replacing the code at address 00435B06 with a xor eax, eax and NOP'ing the 
jump and move right after returns SP_SUCCESS.  First Api done. 

Next we will look at SproRead. Putting a bpx on it in Softice makes it break
at 00423079. By identifying the parameters we will get this:

:0042306C                 lea     eax, [ebp+var_4]     ; prepare parameters
:0042306F                 lea     ecx, [ebp+var_80C]
:00423075                 push    eax                  ; store address
:00423076                 push    0Fh                  ; cell to read
:00423078                 push    ecx                  ; packet record
:00423079                 call    sproRead
:0042307E                 test    ax, ax               ; check return code
:00423081                 jz      short loc_423089     ; if its 0 then continue
:00423083                 mov     [ebp+var_4], 0

Having read Goatass's essay on FlexiSIGN along with Crackz's on other Sentinel
targets makes this a easy task. The following emulation made in hiew showing that
its almost the same as theirs:

:00435CEB: 66813E4272      cmp w,[esi],07242          ; validate packet record
:00435CF0: 7400            je 000435CF2
:00435CF2: 55              push ebp                   ; save EBP
:00435CF3: E800000000      call 000435CF8             ; gets Delta offset
:00435CF8: 5D              pop ebp                    ; the Delta offset will be placed in EBP now
:00435CF9: 8D551E          lea edx,[ebp][0001E]       ; the displacement 0x1E will be added and
                                                            the address in EDX will point to the entry
                                                            of our memory array
:00435CFC: 5D              pop ebp                    ; fix stack
:00435CFD: 0FB74C2410      movzx ecx,w,[esp][00010]   ; get the cell address to read from from stack
:00435D02: D1E1            shl ecx,1                  ; Because a cell/WORD is 2 bytes long we need to
                                                            multiply with 2 to get the correct entry
:00435D04: 0FB7040A        movzx eax,w,[edx][ecx]     ; move the WORD (entry address + calculated 
                                                            displacement) into AX
:00435D08: 8B7C2414        mov edi,[esp][00014]       ; get the store address from the stack and put 
                                                            it in EDI
:00435D0C: 668907          mov [edi],ax               ; move WORD to the store address
:00435D0F: 33C0            xor eax,eax                ; SP_SUCCESS
:00435D11: 5F              pop edi                    ; clean exit
:00435D12: 5E              pop esi
:00435D13: C20C00          retn 0000C
:00435D16: 0000            add       [eax],al         ; our dongle memory will start from here
:00435D18: 0000            add       [eax],al
:00435D1A: 0000            add       [eax],al
:00435D1C: 0000            add       [eax],al
:00435D1E: 0000            add       [eax],al

The emulation is pretty much the same every time. The stack displacements differ a
bit usually from program to program, but looking at the registers just before they
get pushed on stack (like at 00423075) makes its easier to see what we are looking
for. Finding the displacement value is done in Softice using "d esp" and looking
how far ahead the data we need is from the current address.  Our dongle memory cell's
will start at 435D16 so be sure to clean the bytes from there and max 128 bytes ahead.

Once this is done and starting the program again we see a new error message "LM 7.x
meter key not found." So far so good. Now it will be time for recovering the dongle
WORDs needed in order to continue.  By setting a bpx on SproRead we see many breaks
on it. Starting with the first one I pasted above, reading from 0xFh and putting a
bpm on the store address will sooner or later get us to this to the following code:

:0041E1DA                 call    sub_42303F
:0041E1DF                 mov     eax, [esp+3E4h+var_3CC]    ; get the WORD which is stored at 0xFh
:0041E1E3                 add     esp, 4                     ; fix the stack from previous call
:0041E1E6                 cmp     eax, 700h                  ; is our WORD 0x700h?
:0041E1EB                 jz      short loc_41E208           ; it must be
:0041E1ED                 push    0FFFFFFFFh
:0041E1EF                 call    ds:MessageBeep             ; else do bad stuff

Putting 0700 at the address matching 0x1E bytes ahead from our entry at 435D16
will make the conditional jump go in the right direction.  Starting the program
again we will get no more nags and a nice login screen will show instead.  Even
though there are more then just the address 0xFh getting called as address to
read from it is not possible to find any real compares like the one showed above.
So lets save them for later and continue.

At this stage it will still take some time before the program starts because
of the delay called sentinel lag. This is a clear indication to us that we are
not finished.  The last and most tricky api is still needed to work on. Putting
a bpx on SproQuery shows that it gets called plenty of times.  The first instance
of this is showed below:

:00423668                 push    4                    ; length
:0042366A                 push    ecx                  ; response32
:0042366B                 push    eax                  ; response
:0042366C                 lea     ecx, [ebp+var_4D4]
:00423672                 push    edx                  ; query data
:00423673                 push    0Ch                  ; algorithm starting at 0Ch is the 
							   place to do query
:00423675                 push    ecx                  ; packet record
:00423676                 call    sproQuery
:0042367B                 lea     ecx, [ebp+var_58]
:0042367E                 lea     edx, [ebp+var_94]
:00423684                 push    ecx
:00423685                 push    4
:00423687                 push    edx
:00423688                 call    sub_42394F
:0042368D                 add     esp, 0Ch
:00423690                 lea     ecx, [ebp+var_58]
:00423693                 movzx   eax, [ebp+var_A]
:00423697                 imul    eax, 9
:0042369A                 push    4
:0042369C                 add     eax, offset a3a70b51a ; "3A70B51A"
:004236A1                 push    eax
:004236A2                 push    ecx
:004236A3                 call    _strncmp              ; string compare
:004236A8                 add     esp, 0Ch
:004236AB                 mov     edi, eax
:004236AD                 test    edi, edi
:004236AF                 jz      short loc_4236C4

Thinking back on CrackZ essay on 3dsmax my first thought was that we had
a situation just like this. After making a emulation like his it didnt work
at all.  Since this program is not crc protected like 3dsmax was patching
the jump after the string compare should be enough. Unfortunatly it would
have had to be patched multiple times as the query on address 0Ch is called
from multiple places.  Besides, a emulation is much better.

So lets see what went wrong. In 3dsmax there was a plain string compare with
the response value from the dongle.  This doesn't seem to be the case here.
The values compared are double DWORD's so what is important is to see how
they get made.

Notice the call after SproQuery. With a little debugging we will see that it
converts a DWORD to ascii.  A simple example of this is '1234' which will get
converted to '31323334'.  By putting in some recoqnizable DWORD's into response
and response32 we will easily see that response32 is the store address which
gets used for this conversion.

After this it is easy to identify the parameters of this conversion rutine:

:00423676                 call    sproQuery           
:0042367B                 lea     ecx, [ebp+var_58]  ; get the address to store the converted string
:0042367E                 lea     edx, [ebp+var_94]  ; get response32
:00423684                 push    ecx                ; output, address where the converted string will
							be placed
:00423685                 push    4
:00423687                 push    edx                ; input, our response value placed in response32
:00423688                 call    sub_42394F         ; convert string
:0042368D                 add     esp, 0Ch           ; fix stack

Trying out the program a few times we see that the string compare will differ
even though we are at the same code.  Looking at the string compare parameters
shows that the string which our response is compared with is taken from a array.

The following shows the code which prepares for the string compare:

:00423690                 lea     ecx, [ebp+var_58]       ; our converted string
:00423693                 movzx   eax, [ebp+var_A]        ; some number which differes from time to time
:00423697                 imul    eax, 9                  ; use this number to calculate the displacement
							    in the array
:0042369A                 push    4
:0042369C                 add     eax, offset a3a70b51a   ; "3A70B51A" ; add entry/base of the array
:004236A1                 push    eax                     ; string to compare with
:004236A2                 push    ecx                     ; our string
:004236A3                 call    _strncmp                ; string compare

In order to make any of this info useful we need to find out how this number
at 00423693 gets there. What will determine its value?  The answer to this is
found just before the call to SproQuery.

00423611 loc_423611:                                ; CODE XREF: sub_423587+7Ej
00423611                 xor     ebx, ebx
00423613                 push    ebx
00423614                 call    _time              ; time
00423619                 add     esp, 4
0042361C                 push    eax
0042361D                 call    _srand             ; random
00423622                 add     esp, 4
00423625                 call    _rand              ; random
0042362A                 cdq
0042362B                 mov     ecx, 0Ah
00423630                 idiv    ecx
00423632                 movzx   eax, dx
00423635                 imul    eax, 9
00423638                 mov     [ebp+var_A], dx     ; this is the number we was looking for.

Calls to time and rand is a pure indication that the number we have is a totally
random number.

A summary of what the program does:

1. find a random number.
2. save the random number on stack.
3. call SproQuery.
4. convert our response.
5. get the random number again.
6. calculate displacement.
7. add base of array (now we will have the actual string to compare with).
8. do compare.
9. validate if it was good.

Since there is a limited number of strings in the array, we could check out each
queryvalue if we had a real dongle. But since we don't, we have no idea what the
program wants, or do we?  Yes we have a way of knowing what the program wants. This
is because we know the string which our response will be tested against!  So in
order to get everything good and make the string compare zero we have to innovate
a little.

Here is our battle plan for a successful compare:

1. get random number from stack.
2. calculate the displacement just like the program does.
3. add the base of the array (now we know which string will be compared with)
4. input this compare string to a inverted convertion rutine which will get us the
	DWORD we are looking for.
5. save this DWORD in response32.

Before we make our real emulation we must first make an inverted convert rutine.

This is one solution which was made and tested in a real assembler like masm or tasm:

_todword	proc near

instr		= dword	ptr  8

		push  esi                 ; save stack
		push  edx                 ; save registers
		push  ecx
		mov   esi, [esp+instr]    ; esi is ptr to start of string
		xor   eax, eax            ; eax = 0
		xor   ecx, ecx            ; edx = 0
loop_point:
		movzx edx, byte ptr [esi+ecx]  ; Grab first byte from string
		shl   eax, 4              ; multiply existing value by 16
		cmp   edx, 40h            ; if it's A-F, subtract 0x7 first
		jle   short noncharval
		sub   edx, 7
noncharval:
		lea   eax, [eax+edx-30h]  ; subtract 0x30 for regular numbers.
		inc   ecx
		cmp   ecx, 2              ; 2 total passes (1 time to loop point)
		jl    short loop_point
		pop   ecx                 ; restore registers
		pop   edx
		pop   esi                 ; restore stack
		retn
_todword	endp

Here we will to push a pointer to the string onto the stack. Then return value
will be the first byte of the string placed in AL. We will have to increment our
string and call this rutine 4 times inorder to get the entire DWORD constructed.

Since we dont know yet how much code will have to be written as emulating SproQuery
we have to place this code somewhere else.  Since SproFindFirstUnit was emulated
trivially we can use up rest of the space which was used for the original FindFirstUnit.

So here is how it will look when it gets implemented to our file.

:00435B00: 53                    push      ebx
:00435B01: 56                    push      esi
:00435B02: 8B44240C              mov       eax,[esp][0000C]
:00435B06: 33C0                  xor       eax,eax
:00435B08: 90                    nop
:00435B09: 90                    nop
:00435B0A: 90                    nop
:00435B0B: 90                    nop                           ; this is all our emulated SproFindfirstUnit
:00435B0C: 90                    nop
:00435B0D: 90                    nop
:00435B0E: 5E                    pop       esi
:00435B0F: 5B                    pop       ebx
:00435B10: C20800                retn      00008
:00435B13: 56                    push      esi                 ; here our convert rutine starts
:00435B14: 52                    push      edx
:00435B15: 51                    push      ecx
:00435B16: 8B742410              mov       esi,[esp][00010]
:00435B1A: 33C0                  xor       eax,eax
:00435B1C: 33C9                  xor       ecx,ecx
:00435B1E: 0FB6140E              movzx     edx,b,[esi][ecx]
:00435B22: C1E004                shl       eax,004
:00435B25: 83FA40                cmp       edx,040
:00435B28: 7E03                  jle       000435B2D
:00435B2A: 83EA07                sub       edx,007
:00435B2D: 8D4410D0              lea       eax,[eax][edx][-0030]
:00435B31: 41                    inc       ecx
:00435B32: 83F902                cmp       ecx,002
:00435B35: 7CE7                  jl        000435B1E
:00435B37: 59                    pop       ecx
:00435B38: 5A                    pop       edx
:00435B39: 5E                    pop       esi
:00435B3A: C3                    retn

This is the same rutine once more but written into the file this time.

Now that we have done the work needed in order to get our emulation working, lets
continue on the real emulation. Because we will have to emulate more then one address
we first have to check which address has been pushed onto the stack. After that we
can do whats needed:

:004360DD: 66813E4272           cmp       w,[esi],07242
:004360E2: 7400                 je        0004360E4               ; validate packet record
:004360E4: 66837C24140C         cmp       w,[esp][00014],00C      ; is this SproQuery called with 0Ch
								    as query address ?
:004360EA: 752E                 jne       00043611A               ; no, jump to the next compare
								    (which will be implemented next)
:004360EC: 0FB755F6             movzx     edx,w,[ebp][-000A]      ; yes it was! get the random number
								    from stack.
:004360F0: 6BD209               imul      edx,edx,009             ; calculate the displacement just
								    like the program does
:004360F3: 81C2A0CA5500         add       edx,00055CAA0           ; add entry of array so we will have
								    a pointer to the correct string
								    to compare with
:004360F9: 8B5C241C             mov       ebx,[esp][0001C]        ; get the address of response32
:004360FD: 33FF                 xor       edi,edi                 ; EDI = 0
:004360FF: 33C9                 xor       ecx,ecx                 ; ECX = 0
:00436101: 03D1                 add       edx,ecx                 ; ecx will be our counter so move
								    pointer
:00436103: 52                   push      edx                     ; push the address to WORD for convert
								    onto stack
:00436104: E80AFAFFFF           call      000435B13               ; call our conversion rutine
:00436109: 5A                   pop       edx                     ; fix stack and get our string address
								    back into edx
:0043610A: 88043B               mov       [ebx][edi],al           ; put the converted byte into response32
								    with the displacement of edi
:0043610D: 47                   inc       edi                     ; add 1 to the displacement of response32
:0043610E: B902000000           mov       ecx,000000002           ; from now on we will move our double
								    DWORD string 2 bytes ahead to make
								    succesful convertion
:00436113: 83FF04               cmp       edi,004                 ; have we converted all 4 bytes to
								    recover the DWORD?
:00436116: 72E9                 jb        000436101               ; no! lets convert ascii value to a byte
:00436118: EB7E                 jmps      000436198               ; yes! we are done now, jump to clean_exit

We need to add some space between our rutine and the clean_exit since there will
be more emulation to do.

Heres the clean_exit code:

:00436198: 33C0                 xor       eax,eax         ; SP_SUCCESS
:0043619A: 5F                   pop       edi             ; fix stack as required
:0043619B: 5E                   pop       esi
:0043619C: 5B                   pop       ebx
:0043619D: C21800               retn      00018           ; return from caller with stack displacement

When emulating this address we first have to check if its the correct one. If it
is then we must find out where to get the string from and where to place it, by
finding response32 from the stack. Then we initialize 2 counters. One for the input
string and a second for the output. We make a loop with 4 runs to recreate the DWORD.
This is done by pushing the offset of the correct string into our rutine. The byte
located at the first 2 bytes in the string will be placed in AL. After that we move
the value in AL to our response address and increment the counters/pointers for next
loop.  At address 004360EA we have a jump to the next compare which will be implemented
next. Since its not implemented now you might want to change it temporarily to the
clean_exit code to avoid program crashes.

Ok so far so good. By breaking on SproQuery again we will see that the strncmp() will
get good.  But now it seems the program does not want to start anymore.

We know that SproRead is involved as SproQuery works so far.
If you're lazy like me you get it to work the easy way: by trial and error. The WORDs
which are missing all either have to do with the program startup or the meter count.
5 minutes time spent on experimenting will make you see that the program will start
and the meter will be filled up by inserting a 0xFFFF in the following cells:
0x8h, 0x9h, 0xBh and 0x24h.

However we need to finish the emulation of the SproQuery. By breaking on SproQuery
in Softice again we will see that besides 0Ch then also 20h is called many times.
This should be our next target to emulate.

The first call to this cell occur at 00423CAE:

:00423C51 loc_423C51:                                    ; CODE XREF: sub_423BEA+5Bj
:00423C51                 push    0
:00423C53                 call    _time
:00423C58                 add     esp, 4
:00423C5B                 push    eax
:00423C5C                 call    _srand
:00423C61                 add     esp, 4
:00423C64                 call    _rand                  ; find random number
:00423C69                 cdq
:00423C6A                 mov     ecx, 0Ah
:00423C6F                 idiv    ecx
:00423C71                 movzx   esi, dx                ; put random number into ESI
:00423C74                 imul    esi, 9                 ; calculate displacement
---
:00423C94                 lea     ecx, [ebp+var_4]
:00423C97                 lea     eax, [ebp+var_7C]
:00423C9A                 lea     edx, [ebp+var_B8]
:00423CA0                 push    4                      ; length
:00423CA2                 push    ecx                    ; response32
:00423CA3                 push    eax                    ; response
:00423CA4                 lea     ecx, [ebp+var_4BC]
:00423CAA                 push    edx                    ; query data
:00423CAB                 push    20h                    ; algorithm starting at 20h is the place to do query
:00423CAD                 push    ecx                    ; packet record
:00423CAE                 call    sproQuery
:00423CB3                 lea     ecx, [ebp+var_40]      ; get the address to store the converted string
:00423CB6                 lea     edx, [ebp+var_7C]      ; get response32
:00423CB9                 push    ecx                    ; output, address where the converted string will
							   be placed
:00423CBA                 add     esi, offset a5a8c98f9  ; "5A8C98F9" ; add entry of the array to the
							   displacement
:00423CC0                 push    4
:00423CC2                 push    edx                    ; input, our response value placed in response32
:00423CC3                 call    sub_42394F             ; run conversion rutine
:00423CC8                 add     esp, 0Ch               ; fix stack
:00423CCB                 lea     ecx, [ebp+var_40]    
:00423CCE                 push    4
:00423CD0                 push    esi                    ; correct double DWORD
:00423CD1                 push    ecx                    ; converted response32  from the dongle
:00423CD2                 call    _strncmp

Looking at this we instantly see that very little has been changed to the 0Ch
cell call. The method used for compare is still the same, and now that we have
our inverted conversion routine this should be an easy task. However, the way
the random number is saved has changed. There is no more multiplying going on
after the SproQuery and the entry to the compare string array is added in the
middle of the initialization of the string conversion rutine.  At 00423C71 we
see that the random number we need is copied into ESI. The instruction after
calculates our real displacement. Tracing ESI in Softice from that address shows
that no other instruction will touch the value of this. The first change happening
to ESI will be when we push it onto the stack as a default operation of the
original SproQuery rutine:

:004360C0                 push    ebx
:004360C1                 push    esi                   ; push the displacement onto stack
:004360C2                 push    edi
:004360C3                 mov     eax, [esp+arg_0]

All this will make it easier for us as the program takes care of the displacement
calculation now.  So lets revise our battle plan and start our emulation:

1. get displacement from stack.
2. add the base of the array so we will get the string the strncmp will compare	with.
3. input this compare string to our inverted convertion rutine which will get us the DWORD we are looking for.
4. save this DWORD in response32.

This emulation will be placed right after the 0Ch emulation. So incase its not
the 0Ch cell that will be called then we should find out if its 20h.

Our cell 20h emulation:

:0043611A: 66837C241420        cmp       w,[esp][00014],020   ; is this SproQuery called with 20h as query
                                                                    address ?
:00436120: 7576                jne       000436198            ; no! jump to clean_exit
:00436122: 8B5C241C            mov       ebx,[esp][0001C]     ; yes it was! get the address of response32
:00436126: 0FB61424            movzx     edx,b,[esp]          ; get the displacement from stack.
:0043612A: 81C260CB5500        add       edx,00055CB60        ; add entry of array so we will have a pointer
                                                                to the correct string to compare with
:00436130: 33FF                xor       edi,edi              ; EDI = 0
:00436132: 33C9                xor       ecx,ecx              ; ECX = 0
:00436134: 03D1                add       edx,ecx              ; ecx will be our counter so move pointer
:00436136: 52                  push      edx                  ; push the address to WORD for convert onto
                                                                stack
:00436137: E8D7F9FFFF          call      00435B13             ; call our conversion rutine
:0043613C: 5A                  pop       edx                  ; fix stack and get our string address back
                                                                into edx
:0043613D: 88043B              mov       [ebx][edi],al        ; put the converted byte into response32 with
                                                                the displacement of edi
:00436140: 47                  inc       edi                  ; add 1 to the displacement of response32
:00436141: B902000000          mov       ecx,000000002        ; from now on we will move our double DWORD
                                                                string 2 bytes ahead to make succesful
                                                                convertion
:00436146: 83FF04              cmp       edi,004              ; have we converted all 4 bytes to recover
                                                                the DWORD?
:00436149: 72E9                jb       000436134             ; no! lets convert another ascii value to a byte
:0043614B: EB4B                jmps     000436198             ; yes! we are done now, jump to clean_exit

This emulation is almost like the other with the exception of the displacement
which was kindly done for us allready. The clean_exit is of course the same where
we set SP_SUCCESS and fix the stack as required.

Now our 2nd cell compare is also working as required.

Our program will start now, meter is filled up thanks to the WORD's which was
inserted and used by SproRead.

Having spent hours or even days looking in softice at this program going in and
out of calls, tracing response addresses and so on, we will notice strange call's
which were not identified by Killer_3k's flirt sig.

A example of this is the code just after the strncmp at the first SproQuery of
the 20h cell:

:00423CD2                 call    _strncmp                 ; the first 20h cell call
:00423CD7                 add     esp, 0Ch                 ; fix stack
:00423CDA                 mov     esi, eax                 ; store return code from the string compare
:00423CDC                 lea     eax, [ebp+var_4BC]
:00423CE2                 push    eax
:00423CE3                 call    sub_435BF0               ; unknown call
:00423CE8                 test    ax, ax                   ; what testing ?
:00423CEB                 jnz     loc_423D7C

Looking at this and looking inside the call shows code that is unlike the other
API's. What makes me stop at this is the return code in EAX which is 0x00120003.
Does this mean return code 3,  no dongle found?
This return code even gets tested if its zero so this should really turn on our
warning lights.

Applying the Sentinel LM flirt signatures makes IDA identify this function:

00423CD2                 call    _strncmp
00423CD7                 add     esp, 0Ch
00423CDA                 mov     esi, eax
00423CDC                 lea     eax, [ebp+var_4BC]
00423CE2                 push    eax
00423CE3                 call    _RNBOsproFindNextUnit@4
00423CE8                 test    ax, ax
00423CEB                 jnz     loc_423D7C

Yes! it was a dongle check. Notice that the api differs alot from the other
dongle api's which all starts with Spro*. This must be a api which only comes
from Sentinel LM and not the Super Pro library itself. Lets remove it while
we're at it. Replacing the first code inside with a XOR EAX, EAX and RET should
do the trick.  SP_SUCCESS and no more bad stuff.

Testing the other Sentinel LM api's in softice which starts with _RNBO* gives
nothing so we will leave those.

Making a random license and wanting to calculate the license code gives us another
error.  Looking again at SproRead and SproQuery didnt give any results. The
SproRead should have been fixed fully by now.  Checking at what addresses SproQuery
would be called from in IDA gives us a few more placed which ought to be emulated.
However I could not get it to break any of these places like 0043C5D2.

Lets go back to the start and try to get a overview of this mess. The meter key
is a dongle. Its actually a Sentinel Super Pro. If there is a meter, it has to
be decremented at some point in  time when you have made a key.  So actually our
initial problem is our solution! Somewhere in time the dongle WORD's has to be changed.
Looking at our api list and seeing what api's are left makes us see two api's 
SproOverWrite and SproDecrement.

First we take SproOverWrite and set a breakpoint on it in softice but no breaks.
Then SproDecrement and we will see that during the license generation it  will be
called several times.  Here is a example:

004242F9                 push    9                        ; address
004242FB                 lea     eax, [ebp+var_410]
00424301                 push    34C8h                    ; write password
00424306                 push    eax                      ; packet record
00424307                 call    sproDecrement
0042430C                 test    ax, ax
0042430F                 jnz     short loc_42433A         ; if not SP_SUCCESS then do bad stuff

Return code 3 is no good to us! We can do better then that. Since we don't have
any dongle and we don't want to decrement anything, emulating it trivially like
with SproFindFirstUnit should be enough! Doing so makes the program generate a
nice license string to us and we can finally conclude that we won over the program.


Final Notes
This program wasn't easy to reverse. However, it could have been alot harder
then it is. A very big implementation weakness is the checking of SproQuery right
after the call. SproQuery is the hardest api to reverse as it will differ for every
program, so making direct compares like strncmp() is like doing a favour to the
Fravia. So all in all we must conclude that Rainbow Tech. failed at protecting this.

The keygen makes keys now but there is more which I havent discussed here. Rainbow
Tech. wisely did a protection against abuse by supplying every company which buys
Sentinel LM SDK with a specific serial. This is used while installing the SDK.
The serial holds a unique key called a Vendor ID. This Vendor ID can be compared
like FLEXlm's seeds which are unique for every vendor. The SDK installer patches
all files which has something to do with generation of code/keys so our Wlscgen.exe
also gets patched. Making new licenses will use that Vendor ID, so at the current
state you can't use this for more then your own programs which ofcourse use the same
Vendor ID as Wlscgen.

The emulation makes the program able to make keys. This program was reversed under
Windows 2000 SP0 so if you use another OS there might be another program flow. Looking
at the api's and where they get called from shows that I did not "secure" every call.
There are still addresses which might need more, especially SproQuery addresses. I
could not get any of these to get run but it might have something to do with special
licensing options getting set before they get triggered. If you should experience that
you should have a good chance of fixing them, as they use the same method of calling
and comparing.  The code in my emulation also could need a bit of optimizing to
reduce size. This could be a challenge for you!



Ob Duh
I wont even bother explaining you that you should BUY this target program if you intend to use it for a longer period than the allowed one. Should you want to STEAL this software instead, you don't need to crack its protection scheme at all: you'll find it on most Warez sites, complete and already regged, farewell, don't come back.

You are deep inside Fravia's page of reverse engineering, choose your way out:


redhomepage redlinks redsearch_forms red+ORC redhow to protect redacademy database
redreality cracking redhow to search redjavascript wars
redtools redanonymity academy redcocktails redantismut CGI-scripts redmail_Fravia+
redIs reverse engineering legal?