Written by CyberHeg

Jan. 2001

 

Published by +Tsehp

 

Target:

 

Commotion 3.11 update

www.puffindesigns.com

 

Tools used:

 

IDA v4.15, Softice v4.05, hex editor (I use hiew and hexworkshop),

Hasp manual from www.hasp.com and my Hasp flirt signature (from CrackZ's

site).

 

For many dongle protections, it is pretty clear that when companies protect

their products with the latest and greatest commercially available

protection

package they feel safe because no one will be able to crack it, right? This

is

also a example of commercial powers playing their tricks.  I will discuss

the

HASP (Aladdin Knowledge Systems) commercial protection, and it's

implementation

in Commotion 3.11 update.

 

In order to understand their scheme you must understand how the HASP key

works.

The manual is available for download. Without reading this manual learning

hasp is

very difficult.

 

First we install the program on top of the real program (v3.0). I won't

really

go in details with the serial as I assume that you already have a working

copy

of the program installed. By running the program we will get a message popup

saying this program is missing it's HASP key and then the program will start

up

in demo mode with disabled features.

 

We load commotion.exe into IDA.  If you want to save a bit of time, apply my

Hasp FLIRT signature after disassembling.

 

From the manual we see that there is only one Hasp api:

 

hasp(Service, SeedCode, LptNum, Password1, Password2, Par1, Par2, Par3,

Par4);

The Service code determines what function the api executes.

Depending on this Service code the rest of the parameters can either be zero

or in use.

 

This hasp() call will be located at 00b6c570.

 

As you can see on the function reference list it is called from several

places.

At the address 00b6c70f we notice the main reference to this call:

 

.text:00B6C6E3                 lea     eax, [esi+24h]

.text:00B6C6E6                 push    eax                ; Par4

.text:00B6C6E7                 lea     ecx, [esi+20h]

.text:00B6C6EA                 push    ecx                ; Par3

.text:00B6C6EB                 mov     ecx, [esi+14h]

.text:00B6C6EE                 lea     edx, [esi+1Ch]

.text:00B6C6F1                 push    edx                ; Par2

.text:00B6C6F2                 mov     edx, [esi+10h]

.text:00B6C6F5                 lea     eax, [esi+18h]

.text:00B6C6F8                 push    eax                ; Par1

.text:00B6C6F9                 mov     eax, [esi+0Ch]

.text:00B6C6FC                 push    ecx                ; Password2

.text:00B6C6FD                 mov     ecx, [esi+8]

.text:00B6C700                 push    edx                ; Password1

.text:00B6C701                 mov     edx, [esi+4]

.text:00B6C704                 push    eax                ; LptNum

.text:00B6C705                 push    ecx                ; SeedCode

.text:00B6C706                 push    edx                ; Service

.text:00B6C707                 mov     [esp+34h+var_4], 0

.text:00B6C70F                 call    _hasp

 

The code only pushes the registers so there isn't much else to do then to

make a map

file and start using softice.

We break at the call at address 00B6EC93 which is where we want to place our

emulation.

 

At the first break the bh register will be 1. This register always contains

the

Service number so by looking it up in the manual we will see that this is

the

IsHASP function getting called. Par1-4 always references to EAX-EDX

regarding

the return values, so in order to get a correct return value like the manual

states we need to make EAX = 1.

 

So this short piece of code should take care of it:

 

cmp bh, 1

jne <next api service>

mov ax, 1

ret

 

(At the end of this essay the full emulation is posted.)

 

Ok, easily fixed onto next service code. Breaking the same place bh will be

5,

HaspStatus. Because I had no access to a real dongle, I did not know which

type

it was. However that does not stop us. We will try to see if it accepts a

Memo-1

key. Doing so we see in the manual that at the return codes must be Par1(mem

size): 1, Par2(HASP type): 1 and Par3(actual LptNum): 1 for lpt1.

 

Emulation code:

 

cmp bh, 5

jne <next api service>

mov ax, 1

mov bx, ax

mov cx, ax

ret

 

This code is also self explanatory and testing it shows that the program

accepts

our choice as it will give us the next service and not stop on this one.

 

Next api service will be 2, HaspCode. To use the HaspCode emulation made by

UCL

we need to find the two passwords and the SeedCode. We will see that the

SeedCode pushed is 0x82DC and the passwords are Passwd1: 0x63A9 and Passwd2:

0x3EF8 Using the HaspCode emulation we will get: AX: 0xFC1F BX: 0xBFF1 CX:

0x7DED DX: 0xFDFB.

 

The emulation code should be:

 

cmp bh, 2

jne <next api service>

cmp ax, 82dc          ; SeedCode compare

jne <next SeedCode>

mov ax, FC1F

mov bx, BFF1

mov cx, 7DED

mov dx, FDFB

ret

 

Doing so should make a workable emulation, but it did not.

I retried a few times but was not able to get it work.

 

So on with the manual tracing. Shortly after we reach a call where inside is

this check:

 

.text:00B6B070 loc_B6B070:                             ; DATA XREF:

.rdata:00C7FF90o

.text:00B6B070                 cmp     dword ptr [ecx+8], 8C6Fh

.text:00B6B077                 jnz     short loc_B6B09A          ; bad code

jump

.text:00B6B079                 cmp     dword ptr [ecx+0Ch], 0C7CFh

.text:00B6B080                 jnz     short loc_B6B09A          ; bad code

jump

.text:00B6B082                 cmp     dword ptr [ecx+10h], 73B7h

.text:00B6B089                 jnz     short loc_B6B09A          ; bad code

jump

.text:00B6B08B                 cmp     dword ptr [ecx+14h], 94D9h

.text:00B6B092                 jnz     short loc_B6B09A          ; bad code

jump

.text:00B6B094                 mov     eax, 1                    ; good

return

.text:00B6B099                 retn

.text:00B6B09A ;

---------------------------------------------------------------------------

.text:00B6B09A

.text:00B6B09A loc_B6B09A:                             ; CODE XREF:

sub_B6AEC0+1B7j

.text:00B6B09A                                         ; sub_B6AEC0+1C0j

...

.text:00B6B09A                 xor     eax, eax                  ; bad

return

.text:00B6B09C                 retn

 

We see here that this is where the program is failing. Changing our

emulation to

use these values makes the checks go good and we get a valid return. My

suspicion was that this is a HASP 4 dongle but since there are no public

HASP4

HaspCode emulation available at the time of writing I was not able to

confirm

this.

 

Well so far so good. The program will now give us the next service 0x32,

ReadBlock.

Reading the dongle essay section on CrackZ site will make this a easy task

(thanks CrackZ).

 

What is important to know is what registers hold what:

 

esi: length of the read in WORDs

edi: start address

eax: pointer to buffer

Our emulation will then be (a little changed from the one I saw on CrackZ's

site):

 

cmp bh, 32

jne <next api service>

push ebp                ; save the value of ebp

call $+5          ; call delta

pop ebp                 ; put current address (IP) in ebp

lea ebx, [ebp+14]       ; get the start of our dongle memory

pop ebp                 ; get original value of ebp back

mov ecx, esi            ; set up counter

shl edi, 1              ; multiply by 2

lea esi, [ebx][edi]     ; so we get the correct address to read from

shr edi, 1              ; divide by 2

xchg edi, eax           ; buffer pointer into edi

repe movsw              ; copy with the length of ecx

xchg edi, eax           ; retore eax

ret

DB dup 112(0)

 

After this ecx will be zero which matches that Par3 must be zero for

success.

Running the prog still gives us a error. Since we set the type earlier to a

Memo-1 key we need to make room for the 112 byte long memory array, for that

is

the size of the array for Memo-1 keys.

 

Now it's time to see what the program wants.

 

We see when emulating this service that the read address is zero and we

read 0x18 WORD's ahead. To making the solving of the words easy it is a good

idea to name each byte as 00-18 to clear up the meaning.

 

Doing so and following the code after that call we reach here:

 

.text:00B6B4B8 loc_B6B4B8:                             ; CODE XREF:

sub_B6B430+67j

.text:00B6B4B8                 cmp     word ptr [esi+32h], 4450h ; is cell

0x18 4450h ?

.text:00B6B4BE                 jnz     loc_B6B580                ; no do bad

return

.text:00B6B4C4                 test    byte ptr [edi], 8         ; yes it

was

.text:00B6B4C7                 jz      loc_B6B580                ; another

bad return

.text:00B6B4CD                 xor     ecx, ecx

.text:00B6B4CF                 mov     cx, [esi+30h]

.text:00B6B4D3                 mov     esi, 64h

.text:00B6B4D8                 mov     eax, ecx

.text:00B6B4DA                 cdq

.text:00B6B4DB                 idiv    esi

.text:00B6B4DD                 mov     eax, 51EB851Fh

.text:00B6B4E2                 mov     [ebp+var_14], edx

.text:00B6B4E5                 imul    ecx

.text:00B6B4E7                 fild    [ebp+var_14]

.text:00B6B4EA                 sar     edx, 5

.text:00B6B4ED                 mov     eax, edx

.text:00B6B4EF                 shr     eax, 1Fh

.text:00B6B4F2                 add     edx, eax

.text:00B6B4F4                 mov     [ebp+var_14], edx

.text:00B6B4F7                 fmul    ds:dbl_C57778

.text:00B6B4FD                 fild    [ebp+var_14]

.text:00B6B500                 faddp   st(1), st

.text:00B6B502                 fcomp   ds:dbl_C7FF78

.text:00B6B508                 fnstsw  ax

.text:00B6B50A                 test    ah, 1                    ; last compare

.text:00B6B50D                 jnz     short loc_B6B580          ; and bad

return

.text:00B6B50F                 mov     al, 1                     ; good

return

.text:00B6B511                 mov     ecx, [ebp+var_C]

.text:00B6B514                 mov     large fs:0, ecx

.text:00B6B51B                 pop     edi

.text:00B6B51C                 pop     esi

.text:00B6B51D                 pop     ebx

.text:00B6B51E                 mov     esp, ebp

.text:00B6B520                 pop     ebp

.text:00B6B521                 retn

.text:00B6B522 ;

---------------------------------------------------------------------------

 

.text:00B6B580 loc_B6B580:                             ; CODE XREF:

sub_B6B430+8Ej

.text:00B6B580                                         ; sub_B6B430+97j ...

.text:00B6B580                 mov     ecx, [ebp+var_C]

.text:00B6B583                 pop     edi

.text:00B6B584                 pop     esi

.text:00B6B585                 xor     al, al          ; bad return

.text:00B6B587                 mov     large fs:0, ecx

.text:00B6B58E                 pop     ebx

.text:00B6B58F                 mov     esp, ebp

.text:00B6B591                 pop     ebp

.text:00B6B592                 retn

.text:00B6B592 sub_B6B430      endp

 

We will see from this that the first compare tests if the cell at address

0x18

is 4450 if not then go to the bad return code. Then test byte ptr [edi], 8

is

performed. Edi is pointing at the first cell and the test operation is a

logical

AND function without returning the result. The conditional jump afterwards

is by

testing on the zero flag. In my case AND'ing 00 with 08 gives zero and

thereby

the zeroflag gets set and compare will fail. A valid candidate is 08 itself

since 08 AND 08 will give 1. So a valid word will be 0008. The last compare

uses

cell 0x17 does some math before the real compare. By setting the WORD to

1717

like suggested before, the compare goes good.

 

Now the program starts with no more warnings and nag screens. Checking out

if it

would break anymore places on the dongle code showed that it would do

another

HaspCode call when rendering and saving (the two formerly disabled

functions).

However this was with the same SeedCode as before so no need to do more

emulation.

 

Conclusion:

 

This protection was not hard to defeat even though it was protected by the

infamous HASP key. The emulation was pretty straight forward where the only

somewhat hard places was the HaspCode emulator not working and recovering

the

words of the dongle memory array. Again it shows how commercial protections

companies fool sofware developers to believe that their protection product

is

safe and indestructible.

 

Some parts of this essay could have been better explained, thats why I

advise you to

get hold of this program and try it yourself if you really want to learn the

stuff

you missed here.

 

Here is my full emulation:

 

.00B6EC93: 80FF01                       cmp       bh,001   ; IsHASP service?

.00B6EC96: 7505                         jne      .000B6EC9D; No, try next

service

.00B6EC98: 66B80100                     mov       ax,00001 ; Yes and a HASP

key is connected

.00B6EC9C: C3                           retn

.00B6EC9D: 80FF05                       cmp       bh,005   ; HaspStatus

service?

.00B6ECA0: 750B                         jne      .000B6ECAD; No, try next

service

.00B6ECA2: 66B80100                     mov       ax,00001 ; Yes, Memo-1

.00B6ECA6: 668BD8                       mov       bx,ax    ; Memo-1

.00B6ECA9: 668BC8                       mov       cx,ax    ; Lpt1 is used

.00B6ECAC: C3                           retn

.00B6ECAD: 80FF02                       cmp       bh,002   ; HaspCode

service?

.00B6ECB0: 7517                         jne      .000B6ECC9; No, try next

service

.00B6ECB2: 663DDC82                     cmp       ax,082DC ; Yes, is it

SeedCode 0x82DC?

.00B6ECB6: 7510                         jne      .000B6ECC8; No

.00B6ECB8: 66B86F8C                     mov       ax,08C6F ; Yes, return the

right values

.00B6ECBC: 66BBCFC7                     mov       bx,0C7CF

.00B6ECC0: 66B9B773                     mov       cx,073B7

.00B6ECC4: 66BAD994                     mov       dx,094D9

.00B6ECC8: C3                           retn

.00B6ECC9: 80FF32                       cmp       bh,032   ; ReadBlock

service?

.00B6ECCC: 7518                         jne      .000B6ECE6; No

.00B6ECCE: 55                           push      ebp      ; Yes do

ReadBlock emulation

.00B6ECCF: E800000000                   call     .000B6ECD4

.00B6ECD4: 5D                           pop       ebp

.00B6ECD5: 8D5D14                       lea       ebx,[ebp][00014]

.00B6ECD8: 5D                           pop       ebp

.00B6ECD9: 8BCE                         mov       ecx,esi

.00B6ECDB: D1E7                         shl       edi,1

.00B6ECDD: 8D343B                       lea       esi,[ebx][edi]

.00B6ECE0: D1EF                         shr       edi,1

.00B6ECE2: 97                           xchg      edi,eax

.00B6ECE3: F366A5                       repe      movsw

.00B6ECE6: 97                           xchg      edi,eax

.00B6ECE7: C3                           retn

.00B6ECE8: DB dup 112 (0)                                  ; Start for the

memory array