Straus 7
quando una hasp diventa un ferma-carte...

Data

by "haec_est"

 

07/02/2002

UIC's Home Page

Published by Quequero


Dal manuale hasp:

"HASP software uses state-of-the-art proprietary code protection algorithms, plus the most advanced anti-debugging technology in the industry..."

Bhe che dire? GRANDE tutorial che e' un piacere leggere, finalmente qualcuno ha deciso di andare oltre il classico cmp then jump e ha deciso di fare qualcosa di piu' serio, tutti i miei complimenti

"...special anti-hacking features implemented in the HASP software create practically impenetrable obstacles for would-be hackers."

detto così sembrerebbe che possano anche funzionare... naaaahh :-)))

....

Home page se presente: http://xxxxxxx.xxx.xxx
E-mail: [email protected]
Nick, UIN, canale IRC/EFnet frequentato

....

Difficoltà

( )NewBies (x)Intermedio ( )Avanzato ( )Master

 

...dunque, oggi vediamo di far ricredere il signor hasp sull'impenetrabilità degli ostacoli, posti dalle sue chiavi hardware, agli aspiranti hackers, (cosa c'entrano poi gli hackers col reversing...) e di far sapere ai programmatori della G+D Computing che non serve necessariamente una hasp per far girare il loro programma (al contrario, senza funziona anche meglio :-)))


Straus 7
quando una hasp diventa un ferma-carte...
written by haec_est

Introduzione

Qui ci occuperemo principalmente della parte inerente la chiave hardware, per la quale creeremo una emulazione, cioè una subroutine che risponde al programma nello stesso modo in cui risponderebbe la vera chiave, facendo credere allo straus che ci sia veramente una hasp attaccata alla porta parallela.

La parte che attiene alla password verrà trattata in modo più sbrigativo (ci sono ottimi tutes che si occupano di keygening...).

Ad ogni modo i nostri obiettivi sono :

  • emulare la chiave hardware all'interno dell'eseguibile
  • trovare una password valida
  • trasformare lo straus in un auto-keygenerator
  • eliminare la protezione anche dagli altri files
  • eliminare il controllo sulla presenza del driver hasp e poi cancellare il driver dal nostro hd :-P

Tools usati

ida pro 4.04il disassembler (o è una lei ???)
hiew 6.55naturalmente qualsiasi altra versione andava bene
softice (x nt) 4.01idem...
masm32 v6.15come sopra...
win32.hlpquesto ci serve per avere info sulle api di windows

URL o FTP del programma

Il sito del distributore per l'italia (la HSH) è : http://www.campiello.it/HSH

mentre per la versione non europea (che di nome fa Strand 7) abbiamo :

STRAND 7 - © 1999 G+D Computing Pty Ltd

Address: Suite1, Level7, 541 Kent Street, Sydney, 2000. Australia.
email: [email protected]
web: www.strand.aust.com
fax: +61 2 9264 2066
tel: +61 2 9264 2977


Qui c'è solo una versione demo, anche perchè è un programma che di listino fa 7.900.000+iva e non credo lo si trovi in rete, a me l'ha passato un amico.

Notizie sul programma

Straus 7 è un sistema generale per l'analisi ad elementi finiti, può svolgere vari tipi di analisi tra i quali :

- analisi statica lineare e non lineare
- analisi dinamica nel transitorio lineare e non lineare
- stabilità dell'equilibrio
- calcolo delle frequenze naturali
- analisi della risposta armonica
- analisi della risposta spettrale

insomma è un bel programmino :-)))

L'ha usato anche Renzo Piano per il calcolo della torre "Aurora Place" a Sydney (Australia)

La versione 7 (che in realtà poi è la 1.05.6) è la prima versione scritta per funzionare in ambiente windows, infatti le precedenti giravano solamente sotto unix.
Che si tratta di un porting lo si nota subito dalla struttura della directory di installazione, dove troneggia una "\bin" tipica degli ambienti unix/linux, anche se nel manuale dicono :

"Strand7 is a true WIN32 application. It is not simply a port of an old DOS or Unix based code. Strand7 is a completely new development that provides a modern and consistent Windows interface with all the benefits of working under the Windows environment; features such as cut-copy-paste, drag-and-drop and print previewing are seamlessly integrated in the system."

Purtroppo questo gioiellino ha solo un piccolo problema : vuole una hasp !!!

Non contento poi vuole anche una password calcolata su di un serial a sua volta generato sul numero di serie della partizione in cui è installato windows, sull'unità di installazione, e sui dati letti dalla chiave hardware... salvo poi chiamare cmpstr() :-)))

Essay


Innazitutto installiamolo, e proviamo ad eseguirlo, (ci accorgiamo ben presto che 'sto pezzente installa i driver HASP senza chiedere il permesso, ma di questo ci occuperemo più tardi) e vediamo che se ne esce con una MessageBox di errore :

"Cannot find the Straus7 Hardware Lock.
Please insert the Hardware Lock in the parallel port."


La prima cosa che ci viene di fare (almeno a me era venuta la tentazione) è un bel bpx messageboxa ma generalmente quando ci sono di mezzo le chiavi hardware lo schema di protezione non si riduce quasi mai ad un solo check, ed è buona norma dare un occhiata al disasm della vittima per avere una visione più generale possibile di quello che stà accadendo.

C'e da dire che in questi casi non si va mai ad attaccare il codice della protezione in se ma è molto più semplice attaccare l'interfaccia (le api) tra il programma e la chiave; in parole più semplici, il programmatore non sa e non deve sapere come funziona la chiave, gli basta avere a disposizione delle api ed agire sui valori che queste gli restituiscono.

Sfruttando questo fatto basterà riscrivere il codice delle api in questione facendo ritornare i valori che il programmatore si aspetta, quindi noi faremo credere al programma che la chiave è presente rispondendo alle varie richieste per conto di quest'ultima.

Dunque ci servono informazioni su come funziona una chiave hardware e a tal proposito sappiamo già che si tratta di una HASP (ricordate c'e l'aveva detto il programma stesso quando è partito la prima volta) e quindi andiamo al sito della Alladdin e scarichiamo il manuale 'haspman.pdf' nel quale troviamo :

"The HASP family of software protection keys includes the following groups:

HASP4 without memory
MemoHASP (including HASP4 Time)
NetHASP

(...)

MemoHASP

There are three types of MemoHASPs. HASP4 M1 has 112 bytes of read/write memory and HASP4 M4 has 496 bytes of read/write memory. The third type of MemoHASP is the HASP4 Time. The HASP4 Time keys contain an internal real-time clock. HASP4 Time is especially designed for time-limited demos or leased versions of an application. Like the HASP4 M4, HASP4 Time incorporates 496 bytes of read/write memory. In addition, it has 16 bytes of memory for storing expiration dates.

NetHASP

The ultimate software protection solution for PC-based network environments. Connect a single NetHASP to any network station to protect your application and to limit the number of stations using it simultaneously. NetHASP provides all the protection features of HASP4 M4."

Ricapitolando ci sono hasp senza memoria, con 112 bytes di memoria, e con 496 bytes di memoria accessibile sia in lettura che in scrittura, ora dobbiamo scoprire quale di queste lo Straus7 pretende che gli forniamo :-)

Tra le altre cose il manuale hasp ci dice anche :

"The HASP API is implemented with an object file that you link to your application, or a DLL that must be called from within your application. Since the API is both protected and encoded, it offers a high degree of protection."

(...)

"Before protecting with the HASP API, we recommend that you check the API files for your compiler. Each HASP interface includes a sample application demonstrating API usage."

(...)

"Communication with HASP is performed by calling the hasp( ) routine as follows:

hasp (Service, SeedCode, LptNum, Password1, Password2, Par1, Par2, Par3, Par4)"

A questo punto sappiamo che tutta la protezione delle chiavi hasp è insita all'interno della chiamata alla funzione hasp(), che il codice relativo alle api è in un file .obj e quindi linkato staticamente al programma, oppure in una dll esterna, e che esistono degli esempi di utilizzo delle api per ogni compilatore, quindi ci serve sapere con che compilatore è stato scritto lo Straus7.

Disassembliamo l'eseguibile 'straus7.exe' con ida ... lasciamola lavorare (per ore) quando ha finito andiamo a curiosare in giro e scopriamo che lo straus è stato scritto con il borland CBuilder 4 o 5, ad ogni modo usa in modo massiccio le VCL questo ci complica un pochino le cose, ma non tanto.

Torniamo al sito della hasp e scarichiamo gli esempi per il compilatore della Borland, troviamo alcuni files di notevole interesse, tra i quali :

  • haspapi.h  (dentro ci sono i valori delle cosanti e dei codici di errore)
  • haspbc.asm  (hanno pensato anche a noi, non sono gentili ?)
  • haspbc32.obj
diamo un occhiata al file haspbc.asm :

Haspbc.asm

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;;  HASPBC32.ASM
;;
;;
;;  Description:
;;        This file links the application to the procedure that checks
;;        the HASP key. This file performs the following:
;;
;;          a. Gets the parameters from the application stack.
;;          b. Initialize the appropriate registers.
;;          c. Calls haspreg, procedure that checks the HASP key.
;;	    d. Receives the return values from haspreg and moves them to.
;;	       the stack.
;;
;;  Compilation instructions:
;;
;;        tasm32 -mx haspbc32.asm
;;    
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

.386P

_TEXT SEGMENT  BYTE PUBLIC USE32 'CODE'
	ASSUME   CS:_TEXT

	extrn haspreg : near
	public _hasp		

;
; Frame structure after pushing EBP.
;
RetCode4	equ	[EBP+40]    	
RetCode3	equ	[EBP+36]    	
RetCode2	equ	[EBP+32]    	
RetCode1	equ	[EBP+28]    	
PlugNameHi	equ	[EBP+24]    	
PlugNameLow	equ	[EBP+20]    	
Lptnum		equ	[EBP+16]    	
SeedCode	equ	[EBP+12]    	
Cmd		equ	[EBP+8 ]    	

_hasp proc near

	push	Ebp
	mov	Ebp, Esp
	
	push	Eax
	push	Ebx
	push	Ecx
	push	Edx
	push	Edi  
	push	Esi

	mov	Esi, RetCode1
	mov	Edi, [Esi]

	mov	Ebx, 0
	mov	Ebx, Cmd
	mov	bh, bl
	mov	bl, 0
	add	Ebx, LptNum

	mov	Eax, SeedCode
	mov	Ecx, PlugNameLow
	mov	Edx, PlugNameHi

	cmp	bh,50	       
	jb	NotBlockOperation  
 	mov	Esi, RetCode4
	mov	Eax, [Esi]

NotBlockOperation:

	mov	Esi, RetCode2
	mov	Esi, [Esi]

	push	Ebp
	call	haspreg
	pop	Ebp

	mov	Edi, RetCode1
	mov	[Edi], Eax
	mov	Edi, RetCode2
	mov	[Edi], Ebx
	mov	Edi, RetCode3
	mov	[Edi], Ecx
	mov	Edi, RetCode4
	mov	[Edi], Edx

	pop	Esi
	pop	Edi
	pop	Edx
	pop	Ecx
	pop	Ebx
	pop	Eax

	pop	Ebp

	ret

_hasp endp

_TEXT   ENDS

       END
si nota subito che la routine hasp() è solo un wrapper per un'altra funzione che stavolta si chiama haspreg e che risiede in un altro file, essendoci solo un altro .obj ho provato a decompilarlo ed effettivamente haspreg si trova in hasp32b.obj, però a noi interessa solo la routine hasp() perchè è quella che il programma chiama ogni volta che vuole parlare con la chiave.

Quindi dobbiamo cercare dentro all'eseguibile ed a tutte le dll che importa dove è situata questa maledetta routine.

Si pone il problema di come cercarla, ovvero di quali parametri usare per la ricerca, bè non e difficile, da Ida apriamo haspbc32.obj, dal menù 'Options' selezioniamo la voce 'General' e sotto 'Number of opcode bytes' impostiamo 6 o 7 per dire ad ida che vogliamo vedere gli opcode delle istruzioni assembler così :
_TEXT:00000030 8B 4D 14                          mov     ecx, [ebp+arg_C]
_TEXT:00000033 8B 55 18                          mov     edx, [ebp+arg_10]
_TEXT:00000036 80 FF 32                          cmp     bh, 32h 
_TEXT:00000039 72 09                             jb      short loc_0_44
_TEXT:0000003B 90                                nop
_TEXT:0000003C 90                                nop
_TEXT:0000003D 90                                nop
_TEXT:0000003E 90                                nop
_TEXT:0000003F 8B 75 28                          mov     esi, [ebp+arg_20]
_TEXT:00000042 8B 06                             mov     eax, [esi]
_TEXT:00000044                   
_TEXT:00000044                   loc_0_44:                        ; CODE XREF: _hasp+29.j
_TEXT:00000044 8B 75 20                          mov     esi, [ebp+arg_18]
_TEXT:00000047 8B 36                             mov     esi, [esi]
_TEXT:00000049 55                                push    ebp
_TEXT:0000004A E8 B1 FF FF FF                    call    haspreg
_TEXT:0000004F 5D                                pop     ebp
_TEXT:00000050 8B 7D 1C  
ora se guardate l'istruzione 'cmp bh,32h' vedete che ad essa corrispondono i bytes
0x80, 0xFF, 0x32
mentre alla successiva 'jb short loc_0_44' corrispondono i bytes
0x72, 0x09
bene noi useremo questi bytes (tranne il 0x09 che può variare) come stringa di ricerca all'interno dell'eseguibile e di tutte le dll che esso usa.

*** piccola parentesi ***

N.B. : 32h corrisponde al numero decimale 50 che è il numero del servizio 'ReadBlock' (vedi manuale) infatti se guardate al sorgente asm fornitoci dalla hasp vedete che
	cmp	bh,50	       
	jb	NotBlockOperation  
 	mov	Esi, RetCode4
	mov	Eax, [Esi]

NotBlockOperation:

	mov	Esi, RetCode2
	mov	Esi, [Esi]
in pratica il la funzione hasp() controlla se si stà per effettuare una lettura di un blocco di celle dalla chiave e nel caso mette in eax il puntatore al buffer che conterrà tali dati.

questo non è parte del programma ma è un comportamento standard del software hasp, in altre parole TUTTE le hasp per essere usate devono passare dalla funzione hasp() che a sua volta deve SEMPRE controllare quando si vuole usare il servizio 50; ne segue che '0x80 0xFF 0x32 0x72' è una stringa di ricerca valida per tutti i programmi che usano le hasp (in pratica è la firma del signor hasp !!!)

*** fine parentesi ***

Apriamo Straus7 in hiew F4 F2 per passare in modalità 'Hex', F7 per effettuare una ricerca, ed immettiamo (dove c'e scritto Hex) i bytes da cercare 80FF3272 e... trovati!!!

Ora F4 F3 per passare in modalità 'Decode' e vediamo che siamo all'indirizzo 65BD7F, torniamo in Ida e portiamoci a quell'indirizzo, ci troviamo in questa routine :
0065BD5C sub_65BD5C      proc near               ; CODE XREF: sub_64CD40+38.p
0065BD5C                                         ; sub_64CE90+39.p ...
0065BD5C 
0065BD5C arg_0           = dword ptr  8
0065BD5C arg_4           = dword ptr  0Ch
0065BD5C arg_8           = dword ptr  10h
0065BD5C arg_C           = dword ptr  14h
0065BD5C arg_10          = dword ptr  18h
0065BD5C arg_14          = dword ptr  1Ch
0065BD5C 
0065BD5C                 push    ebp
0065BD5D                 mov     ebp, esp
0065BD5F                 push    eax
0065BD60                 push    ebx
0065BD61                 push    ecx
0065BD62                 push    edx
0065BD63                 push    edi
0065BD64                 push    esi
0065BD65                 mov     esi, [ebp+arg_C]
0065BD68                 mov     edi, [esi]
0065BD6A                 mov     ebx, 0
0065BD6F                 mov     ebx, eax
0065BD71                 mov     bh, bl
0065BD73                 mov     bl, 0
0065BD75                 add     ebx, ecx
0065BD77                 mov     eax, edx
0065BD79                 mov     ecx, [ebp+arg_14]
0065BD7C                 mov     edx, [ebp+arg_10]
0065BD7F                 cmp     bh, 32h
0065BD82                 jb      short loc_65BD89
0065BD84                 mov     esi, [ebp+arg_0]
0065BD87                 mov     eax, [esi]
0065BD89 
0065BD89 loc_65BD89:                             ; CODE XREF: sub_65BD5C+26.j
0065BD89                 mov     esi, [ebp+arg_8]
0065BD8C                 mov     esi, [esi]
0065BD8E                 push    ebp
0065BD8F                 call    sub_65DC22
0065BD94                 pop     ebp
0065BD95                 mov     edi, [ebp+arg_C]
0065BD98                 mov     [edi], eax
0065BD9A                 mov     edi, [ebp+arg_8]
0065BD9D                 mov     [edi], ebx
0065BD9F                 mov     edi, [ebp+arg_4]
0065BDA2                 mov     [edi], ecx
0065BDA4                 mov     edi, [ebp+arg_0]
0065BDA7                 mov     [edi], edx
0065BDA9                 pop     esi
0065BDAA                 pop     edi
0065BDAB                 pop     edx
0065BDAC                 pop     ecx
0065BDAD                 pop     ebx
0065BDAE                 pop     eax
0065BDAF                 pop     ebp
0065BDB0                 retn    18h
0065BDB0 sub_65BD5C      end
vi ricorda niente ??? :-))))))

bene, mettiamo i nomi dei parametri che abbiamo visto prima nel manuale e che vengono passati alla routine wrappata, tenendo conto del fatto che questa funzione non viene chiamata secondo la convenzione __stdcall tipica dei programmi windows, infatti se diamo un occhiata al file haspapi.h vediamo :
void HASPAPI hasp( int service,
                   int seed,
                   int lptnum,
                   int pass1,
                   int pass2,
                   int HASP_FAR* p1,
                   int HASP_FAR* p2,
                   int HASP_FAR* p3,
                   int HASP_FAR* p4 );
la chiamata è di tipo HASPAPI il quale è a sua volta definito nel file haspconf.h
#if !defined (HASPAPI)
#  if defined (HASP_DLL)
#    if defined (HASP_32BIT)
#      define HASPAPI __stdcall
#    else
#      define HASPAPI HASP_FAR pascal
#    endif
#  else
#    if defined (HASP_32BIT)
#      define HASPAPI
#    else
#      define HASPAPI __cdecl HASP_FAR
#    endif
#  endif
#endif
quindi i parametri sono passati sullo stack in ordine da sinistra a destra (e non come accade di solito da destra a sinistra) tranne che per i primi tre che vengono passati mediante i registri eax,ecx,edx

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

0065BD5C
0065BD5C hasp            proc near               ; CODE XREF: sub_64CD40+38.p
0065BD5C                                         ; sub_64CE90+39.p ...
0065BD5C 
0065BD5C p4              = dword ptr  8
0065BD5C p3              = dword ptr  0Ch
0065BD5C p2              = dword ptr  10h
0065BD5C p1              = dword ptr  14h
0065BD5C pass2           = dword ptr  18h
0065BD5C pass1           = dword ptr  1Ch
0065BD5C 
0065BD5C                 push    ebp
0065BD5D                 mov     ebp, esp
0065BD5F                 push    eax
0065BD60                 push    ebx
0065BD61                 push    ecx
0065BD62                 push    edx
0065BD63                 push    edi
0065BD64                 push    esi
0065BD65                 mov     esi, [ebp+p1]   ; esi = ptr Par1        
0065BD68                 mov     edi, [esi]      ; edi = Par1
0065BD6A                 mov     ebx, 0
0065BD6F                 mov     ebx, eax        ; ebx = eax = Service
0065BD71                 mov     bh, bl          ; bh = Service
0065BD73                 mov     bl, 0
0065BD75                 add     ebx, ecx        ; ecx = LptNum
0065BD75                                         ; bh = Service
0065BD75                                         ; bl = LptNum
0065BD77                 mov     eax, edx        ; eax = edx = seed
0065BD79                 mov     ecx, [ebp+pass1]
0065BD7C                 mov     edx, [ebp+pass2]
0065BD7F                 cmp     bh, 50
0065BD82                 jb      short NotBlockOperation
0065BD84                 mov     esi, [ebp+p4]   ; esi = ptr Par4
0065BD87                 mov     eax, [esi]      ; eax = Par4 = lp_buffer
0065BD89 
0065BD89 NotBlockOperation:                      ; CODE XREF: hasp+26.j
0065BD89                 mov     esi, [ebp+p2]   ; esi = ptr Par2
0065BD8C                 mov     esi, [esi]      ; esi = Par2
0065BD8E                 push    ebp
0065BD8F                 call    haspreg         ; interroga la chiave
0065BD94                 pop     ebp
0065BD95                 mov     edi, [ebp+p1]
0065BD98                 mov     [edi], eax      ; par1 = eax
0065BD9A                 mov     edi, [ebp+p2]
0065BD9D                 mov     [edi], ebx      ; par2 = ebx
0065BD9F                 mov     edi, [ebp+p3]
0065BDA2                 mov     [edi], ecx      ; par3 = ecx
0065BDA4                 mov     edi, [ebp+p4]
0065BDA7                 mov     [edi], edx      ; par4 = edx
0065BDA9                 pop     esi
0065BDAA                 pop     edi
0065BDAB                 pop     edx
0065BDAC                 pop     ecx
0065BDAD                 pop     ebx
0065BDAE                 pop     eax
0065BDAF                 pop     ebp
0065BDB0                 retn    18h
0065BDB0 hasp            endp
0065BDB0
quello che a noi conviene fare è riscrivere la parte iniziale della routine haspreg() per fare in modo che i valori ritornati in par1...par4 siano gli stessi nel caso la hasp fosse effettivamente presente. Quando arriviamo sulla chiamata a haspreg() abbiamo :

Contenuto registri

  eax 	 	seed / lp_buffer (bh >= 50)
  bh 		service
  bl		lptnum
  ecx		password 1
  edx		password 2
  edi		par1 
  esi		par2
Per sapere cosa scrivere nella nostra versione di haspreg() dobbiamo sapere cosa vuole il programma dalla chiave e cioè quali servizi sono invocati (e che quindi dobbiamo emulare), questo lo scopriamo andando a vedere le references per la funzione hasp(), ida ci dice :

xrefs hasp()

hasp (Service, Seed, LptNum, Pwd1, Pwd2, Par1, Par2, Par3, Par4)

1 - 0064CD7D	hasp (29, 7AE3, 1, ...)
2 - 0064CEC9	hasp (2B, 7AE3, 1, ...)
3 - 0064D058	hasp (05, 012C, 0, ...)
4 - 0064D364	hasp (2A, 7AE3, 1, ...)
5 - 0064D3F3	hasp (28, 7AE3, 1, ...)
6 - 0065B4E6	hasp (34, 7AE3, 1, ...)
7 - 0065B531	hasp (34, 7AE3, 1, ...)
8 - 0065B579	hasp (32, 012C, 0, ...)
vediamo subito che la funzione hasp() viene chiamata con due seed diversi e servizi che sono propri di chiavi di tipo diverso, infatti
28, 29, 2A, 2B, 34 	-> NetHasp	seed : 7AE3
05, 32			-> MemoHasp	seed : 012C
quindi possiamo pensare che lo Straus7 funzioni con due diversi tipi di chiave a seconda del tipo di ambiente e del tipo di licnza con cui viene acquistato, stà a noi scegliere quello che ci è più semplice da emulare, io scelgo la MemoHasp (perchè è chiamata con due servizi soltanto). Apriamo il manuale della hasp e cerchiamo inofrmazioni più approfondite sui servizi 05h e 32h troviamo :

Service 5: HaspStatus


Description	Check the type of HASP connected to the computer. Also
		check to which port the key is connected.

Relevant Keys	HASP4 without memory, HASP4 M1, HASP4 M4, HASP4 Time

Syntax		hasp (Service, SeedCode, LptNum, Password1, Password2, Par1, Par2, Par3, Par4)

Parameters Used

	Service 	5
	LptNum 		Value indicating the port to search for the HASP.
			Recommended value: 0 (see “Specifying the Port” in Chapter 6
			for a list of possible values).
	Password1 	First HASP password.
	Password2 	Second HASP password.

Return Values

	Par1 		Memory Size
			• 1 – HASP4 M1
			• 4 – HASP4 M4
			• 0 – other keys
	Par2 		HASP Type
			• 0 – HASP4 without memory
			• 1 – HASP4 M1 or HASP4 M4
			• 5 – HASP4 Time
	Par3 		Actual LptNum – The port to which the HASP is connected
			(200 or above for USB HASPs).
	Par4 		HASP Object Version – The current API version.

Comments

• To minimize search time, use the value of the Actual
LptNum received in Par3, and pass it via the LptNum
parameter in subsequent calls to the hasp( ) routine.

• If a NetHASP is connected to the local parallel port,
HaspStatus identifies it as a HASP4 M4, that is, the
memory size returned in Par1 is 4, and the HASP type
returned in Par2 is 1. 


Service 50: ReadBlock


Description	Read one block of data from the HASP memory.

Relevant Keys	HASP4 M1, HASP4 M4, HASP4 Time

Syntax		hasp (Service, SeedCode, LptNum, Password1, Password2, Par1, Par2, Par3, Par4)

Parameters Used	

	Service 	50
	LptNum 		Value indicating the port to search for the HASP.
			Recommended value: 0 (see “Specifying the Port”, in Chapter
			6 for a list of possible values).
	Password1 	First HASP password.
	Password2 	Second HASP password.
	Par1 		Start Address – Defines the initial HASP memory address
			for reading the block:
			• 0 to 55 – HASP4 M1
			• 0 to 247 – HASP4 M4
			• 0 to 247 – HASP4 Time
	Par2 		Block Length – The block size, in words.
	Par3 		Buffer Segment – Segment address of a program buffer (variable).
			You do not need to specify the Buffer Segment parameter in 
			32-bit applications.
	Par4 		Buffer Offset – Offset address of a program buffer (variable).
			The buffer size must be at least as large as the block size.
Return Values

	Par3 		Status – A code indicating the status of the memory
			operation (for possible values, see Chapter 11).


Ricapitolando, abbiamo due servizi da emulare il 5 ed il 50, quando arriviamo sulla chiamata alla funzione hapreg() nei registri abbiamo i valori visti prima, e quando ci viene chiesto il serivizio 5 dobbiamo rispondere che attaccata alla porta LPT1 c'è una MemoHasp (M1 o M4 non fa differenza) così :

Service 5: expected return values

	Par1 (Memory Size)	   	: 1 – Hasp4 M1
	Par2 (HASP Type)	  	: 1 – MemoHasp 
	Par3 (Actual LptNum)  	  	: 1 - LPT1 (vedi manuale hasp)
	Par4 (HASP Object Version)	: numero a caso (il prog. non lo controlla :-))
cominciamo a buttar giù un po' di codice all'inizio della routine haspreg() 0065DC22 per fare ciò apriamo hiew 6.55 e passiamo in modalità 'Decode' poi con F5 andiamo all'indirizzo che ci interessa (ricordatevi di metterci un punto altrimenti hiew non lo prende) così .0065DC22 e cominciamo ad editare il codice presente :
0065DC22                   haspreg         proc near             ; CODE XREF: hasp+33.p
0065DC22 60                                pusha
0065DC23 BE 4E 25 73 00                    mov     esi, offset dword_73254E
0065DC28 83 3E FF                          cmp     dword ptr [esi], 0FFFFFFFFh
0065DC2B 74 10                             jz      short loc_65DC3D
0065DC2D 60                                pusha
0065DC2E BE 43 22 73 00                    mov     esi, offset unk_732243
0065DC33 FF 16                             call    dword ptr [esi]
0065DC35 BE 42 25 73 00                    mov     esi, offset unk_732542
0065DC3A 89 06                             mov     [esi], eax
0065DC3C 61                                popa
0065DC3D                   
0065DC3D                   loc_65DC3D:                           ; CODE XREF: haspreg+9.j
0065DC3D BE 4E 25 73 00                    mov     esi, offset dword_73254E
0065DC42 83 3E FF                          cmp     dword ptr [esi], 0FFFFFFFFh
0065DC45 0F 85 52 01 00 00                 jnz     loc_65DD9D
0065DC4B B8 CF 20 73 00                    mov     eax, offset unk_7320CF
0065DC50 50                                push    eax
0065DC51 E8 2A 9F DA FF                    call    j_GetModuleHandleA_0
0065DC56 BE DC 20 73 00                    mov     esi, offset unk_7320DC
0065DC5B 89 06                             mov     [esi], eax
0065DC5D E8 0C F1 FF FF                    call    sub_65CD6E
0065DC62 BE 43 22 73 00                    mov     esi, offset unk_732243
0065DC67 FF 16                             call    dword ptr [esi]
0065DC69 BE 42 25 73 00                    mov     esi, offset unk_732542
0065DC6E 89 06                             mov     [esi], eax
0065DC70
sovrascrivendolo con la nostra routine di emulazione :

Emulazione haspreg() - prima parte

0065DC22
0065DC22             haspreg      proc near      		; CODE XREF: hasp+33.p
0065DC22 80 FF 05                 cmp	   bh, 05h		; HaspStatus
0065DC25 74 10                    jz      short hasp_status	      
0065DC27 80 FF 32                 cmp     bh, 32h       	; ReadBlock
0065DC2A 74 00                    jz      short loc_65DC2C	; Questo lo fixiamo dopo 
0065DC2A							; aver scritto il service 5.
0065DC2C
0065DC2C             loc_65DC2C:
0065DC2C 33 C0                    xor     eax, eax  		; Arriviamo qui se NON è uno
0065DC2E 33 DB                    xor     ebx, ebx		; dei servizi emulati e quindi
0065DC30 33 C9                    xor     ecx, ecx		; ritorniamo 0 in par1...par4
0065DC32 33 D2                    xor     edx, edx		; per seganlare l'errore.
0065DC34 C3                       retn
0065DC35
0065DC35             hasp_status:
0065DC35 33 C0	                  xor     eax, eax	
0065DC37 40			  inc     eax			; par1 = 1 (MemoHasp)
0065DC38 33 DB                    xor     ebx, ebx
0065DC3A 43			  inc     ebx			; par2 = 1 (M1, 112 bytes)
0065DC3B 33 C9                    xor     ecx, ecx
0065DC3D 41			  inc     ecx			; par3 = 1 (porta LPT1)
0065DC3E 33 D2                    xor     edx, edx
0065DC40 B2 21			  mov	  dl, 66h		; par4 = 66h (versione api a caso)
0065DC42 C3                       retn				; missione compiuta :-)
0065DC43 
ora usciamo da hiew (F9 per salvare le modifiche, F10 per uscire) e proviamo come funziona il pezzetto di emulatore che abbiamo scritto fino ad ora.

Bene, direi, ora lo Straus7 ci dice :

"The attached hardware lock is not compatible with Straus7.
  Please connect the correct hardware lock."


infatti il pargolo è convinto che ora ci sia effettivamente una chiave hardware connessa alla porta LPT1, solo che eseguendo altri controlli si accorge che non è la sua (ricordate che dobbiamo ancore emulare il servizio 50).

Toraniamo dalla buona vecchia cara Ida, e cerchiamo le references alla subroutine hasp(), l'ultima, la numero 8 è quella che ci interessa perchè viene chiamata con Service = 32h (50) ma diamogli un occhiata più da vicino...

Se rinominiamo un po' di parametri (grande ida:-)) il tutto ci appare ancora più chiaro e semplice, infatti basta un doppio click su 'var_4' e poi col tasto 'n' gli mettiamo il nome che gli compete "par1", poi andiamo su 'var_8' e gli diamo il nome "par2" idem per var_C e var_10, che diverranno par3 e par4 rispettivamente.

A questo punto diamo un occhiata al manuale hasp e vediamo che par1 contiene la cella da cui iniziare a leggere, mentre a par2 corrsponde la dimensione del blocco di celle da leggere espressa in numero di celle (ogni cella sono 2 byte cioè una word) e a questo viene assegnato il valore 38h (56) che moltiplicato per due ci dà 112 bytes (in pratica qui viene letta TUTTA la meoria della chiave), sempre dal manuale hasp vediamo che par3 DEVE essere 0 (win32) e par4 contiene un puntatore al buffer dove verranno messi i valori letti dalla chiave, quindi doppo click su 'byte_767660' e rinominiamolo con un nome più appropriato per un buffer, che ne dite di 'buffer' :-)

Ultima cosuccia rinominiamo le chiamate alle subroutine che generano le password, come 'make_pwd1' e 'make_pwd2'.

A questo punto abbiamo :
0065B538 
0065B538 loc_65B538:                                       ; CODE XREF: sub_65B468+34.j
0065B538                 xor     eax, eax
0065B53A                 mov     [ebp+par1], eax           ; leggi dalla cella 0x00
0065B53D                 mov     [ebp+par2], 38h           ; alla cella 0x38
0065B544                 xor     eax, eax
0065B546                 mov     [ebp+par3], eax           ; par3 = 0
0065B549                 mov     eax, offset buffer
0065B54E                 mov     [ebp+par4], eax           ; par4 = ptr buffer
0065B551                 call    make_pwd1                 ; ritorna Password1 in eax
0065B556                 push    eax
0065B557                 call    make_pwd2                 ; ritorna Password2 in eax
0065B55C                 push    eax
0065B55D                 lea     eax, [ebp+par1]           ; par1
0065B560                 push    eax
0065B561                 lea     eax, [ebp+par2]           ; par2
0065B564                 push    eax
0065B565                 lea     eax, [ebp+par3]           ; par3
0065B568                 push    eax
0065B569                 lea     eax, [ebp+par4]           ; par4
0065B56C                 push    eax
0065B56D                 xor     ecx, ecx                  ; lptnum = 0
0065B56F                 mov     edx, 12Ch                 ; seed = 12Ch
0065B574                 mov     eax, 32h                  ; service = 50
0065B579                 call    hasp                      ; hasp (...)
0065B57E
questo corrisponde in c++ alla chiamata :
	hasp(50, 300, 0, make_pdw1(), make_pwd2(), 0, 56, 0, & buffer);

ora però dobbiamo scoprire cosa diavolo deve esserci nei 112 bytes di memoria della chiave, e per saperlo andiamo a dare un'occhiata a cosa succede dopo questa chiamata, sempre da ida vediamo (subito sotto) :
0065B57E 
0065B57E loc_65B57E:                                       ; CODE XREF: sub_65B468+CE.j
0065B57E                 mov     eax, offset dword_7676D0
0065B583                 call    @System@@LStrClr$qqrr17System@AnsiString
0065B588                 mov     ebx, 7
0065B58D                 mov     esi, offset buffer+3
0065B592 
0065B592 loc_65B592:                                       ; CODE XREF: sub_65B468+14A.j
0065B592                 mov     al, [esi]
0065B594                 call    @System@UpCase$qqrc       ; System::UpCase(char)
0065B599                 mov     edx, eax
0065B59B                 lea     eax, [ebp+var_14]
0065B59E                 call    Pchar_to_Lstr_1
0065B5A3                 mov     edx, [ebp+var_14]
0065B5A6                 mov     eax, offset dword_7676D0
0065B5AB                 call    @System@@LStrCat$qqrv     ; System __linkproc__ LStrCat(void)
0065B5B0                 inc     esi
0065B5B1                 dec     ebx
0065B5B2                 jnz     short loc_65B592
0065B5B4
la prima chiamata crea una stringa vuota e ne mette il puntatore nella dword_7676D0 poi vengono presi i bytes letti dalla chiave dal quarto al decimo (saltando i prim i tre, che vedremo usati più tardi) portati in maiuscolo (e quindi ci aspettiamo che siano caratteri a-z) e poi concatenati nella stringa creata poco fa.

Noi per non fare casino rinominiamo la 'dword_7676D0' in 'hasp_strign_07' nel caso la reincontrassimo più tardi, mentre la variabile locale 'var_14' essendo usata come puntatore temporaneo la chiamiamo 'lp_str_tmp'.

Andiamo avanti, subito dopo fa lo stesso con una stringa di 20h (32) bytes, solo che questa volta non la converte in maiuscolo, taglia gli spazi all'inizio ed alla fine, e la prende a partire dalla posizione 40h (64) del buffer.

Questa la chiamiamo 'hasp_string_32'.
0065B5B4                 mov     eax, ds:hasp_string_32
0065B5B9                 call    @System@@LStrClr$qqrr17System@AnsiString
0065B5BE                 mov     ebx, 20h
0065B5C3                 mov     esi, offset buffer+40h
0065B5C8 
0065B5C8 loc_65B5C8:                                       ; CODE XREF: sub_65B468+17E.j
0065B5C8                 lea     eax, [ebp+lp_str_tmp]
0065B5CB                 mov     dl, [esi]
0065B5CD                 call    Pchar_to_Lstr_1
0065B5D2                 mov     edx, [ebp+lp_str_tmp]
0065B5D5                 mov     eax, ds:hasp_string_32
0065B5DA                 call    @System@@LStrCat$qqrv     ; System __linkproc__ LStrCat(void)
0065B5DF                 mov     eax, ds:hasp_string_32
0065B5E4                 inc     esi
0065B5E5                 dec     ebx
0065B5E6                 jnz     short loc_65B5C8
0065B5E8                 lea     edx, [ebp+lp_str_tmp]
0065B5EB                 mov     eax, ds:hasp_string_32
0065B5F0                 mov     eax, [eax]
0065B5F2                 call    @Sysutils@Trim$qqrx17System@AnsiString ; Sysutils::Trim(System::AnsiString)
0065B5F7                 mov     edx, [ebp+lp_str_tmp]
0065B5FA                 mov     eax, ds:hasp_string_32
0065B5FF                 call    @System@@LStrAsg$qqrv     ; System __linkproc__ LStrAsg(void)
0065B604
Proseguendo ancora ritroviamo i tre byte all'inizio del buffer che prima avevamo saltato, questi naturalmente li chiamiamo 'hasp_string_3' :
0065B604                 mov     eax, ds:hasp_string_3
0065B609                 call    @System@@LStrClr$qqrr17System@AnsiString 
0065B60E                 mov     ebx, 3
0065B613                 mov     esi, offset buffer
0065B618 
0065B618 loc_65B618:                                       ; CODE XREF: sub_65B468+1D5.j
0065B618                 mov     al, [esi]
0065B61A                 call    @System@UpCase$qqrc       ; System::UpCase(char)
0065B61F                 mov     edx, eax
0065B621                 lea     eax, [ebp+lp_str_tmp]
0065B624                 call    Pchar_to_Lstr_1
0065B629                 mov     edx, [ebp+lp_str_tmp]
0065B62C                 mov     eax, ds:hasp_string_3
0065B631                 call    @System@@LStrCat$qqrv     ; System __linkproc__ LStrCat(void)
0065B636                 mov     eax, ds:hasp_string_3
0065B63B                 inc     esi
0065B63C                 dec     ebx
0065B63D                 jnz     short loc_65B618
0065B63F                 xor     eax, eax
0065B641                 pop     edx
0065B642                 pop     ecx
0065B643                 pop     ecx
0065B644                 mov     fs:[eax], edx
0065B647                 push    offset loc_65B65C
0065B64C 
0065B64C loc_65B64C:                                       ; CODE XREF: CODE:0065B65A.j
0065B64C                 lea     eax, [ebp+lp_str_tmp]
0065B64F                 call    @System@@LStrClr$qqrr17System@AnsiString 
0065B654                 retn
0065B654 sub_65B468      endp
Siamo arrivati alla fine della subroutine e non ci sono tracce di dove venga effettuato il controllo, allora proviamo a dare un nome a questa sub e poi ne cerchiamo le references, la chiamiamo 'get_hasp_data' (non vi sembra appropriato ?) Ida ci dà queste references :
00655899 call 	get_hasp_data
0065AA80 call	get_hasp_data
0064CBC0 dd offset get_hasp_data

prendiamo la prima e troviamo questo regalino :
00655899                 call    get_hasp_data
0065589E                 mov     eax, ds:hasp_string_07
006558A3                 mov     edx, offset aStrand7_1    ; "STRAND7"
006558A8                 call    @System@@LStrCmp$qqrv     ; System __linkproc__ LStrCmp(void)
006558AD                 jz      short loc_6558F6
006558AF                 push    0
006558B1                 push    offset aTheAttachedHar    ; "The attached hardware lock is not compa"...
006558B6                 mov     eax, ds:off_74334C
006558BB                 push    dword ptr [eax]
006558BD                 push    offset dword_655B64
006558C2                 push    offset dword_655BC0
006558C7                 push    offset aPleaseConnec_0    ; "Please connect the correct hardware loc"...
006558CC                 lea     eax, [ebp+var_4]
006558CF                 mov     edx, 5
006558D4                 call    @System@@LStrCatN$qqrv    ; System __linkproc__ LStrCatN(void)
006558D9                 mov     eax, [ebp+var_4]
006558DC                 mov     cx, ds:word_655A9C
006558E3                 mov     dl, 2
006558E5                 call    @Dialogs@MessageDlg$qqrx17System@AnsiString1
006558EA
Bello vero :-)))))) quello che succede qui è molto semplice : lo Straus controlla che i bytes 4-10 letti dalla chiave hardware siano 'STRAND7' per avere la certezza che si tratti di una sua chiave (non è gentile a dirci cosa dobbiamo mettere nella routine di emulazione ??? ) se non coincidono ci dà il messaggio d'errore.

Bene ora che sappiamo (almeno in parte) cosa vuole, possiamo scrivere il seguito della nostra routine haspreg(); eravamo rimasti qui, proseguiamo...

Emulazione haspreg() - parte seconda

0065DC43                
0065DC43                read_block:                             ; CODE XREF: haspreg+8.j
0065DC43 8D 38                          lea     edi, [eax]      ; edi = lp_buffer
0065DC45 E8 00 00 00 00                 call    $+5             ; questo è un piccolo trucco
0065DC45                                                        ; per avere runtime l'indirizzo
0065DC45                                                        ; dell'istruzione successiva
0065DC45                                                        ; alla call
0065DC4A 5E                             pop     esi             ; dopo il pop esi = 0065DC4A
0065DC4A                                                        ; 
0065DC4B 83 C6 13                       add     esi, 13h        ; esi punta all'inizio della 
0065DC4B                                                        ; memoria emulata :-)
0065DC4E 33 C9                          xor     ecx, ecx
0065DC50 B1 70                          mov     cl, 70h         ; ecx = bytes da copiare
0065DC52 F3 A4                          repe movsb              ; copia da [esi] in [edi]
0065DC54 8B D0                          mov     edx, eax        ; ripristina par4
0065DC56 33 C0                          xor     eax, eax
0065DC58 33 DB                          xor     ebx, ebx
0065DC5A 33 C9                          xor     ecx, ecx        ; status = ok
0065DC5C C3                             retn                    ; fatto
0065DC5C                haspreg         endp
0065DC5C ; ---------------------------------------------------------------------------
0065DC5D hasp_memory     db    0,   0,   0, 53h, 54h, 52h, 41h, 4Eh
0065DC5D                 db  44h, 37h,   0,   0,   0,   0,   0,   0
0065DC5D                 db    0,   0,   0,   0,   0,   0,   0,   0
0065DC5D                 db    0,   0,   0,   0,   0,   0,   0,   0
0065DC5D                 db    0,   0,   0,   0,   0,   0,   0,   0
0065DC5D                 db    0,   0,   0,   0,   0,   0,   0,   0
0065DC5D                 db    0,   0,   0,   0,   0,   0,   0,   0
0065DC5D                 db    0,   0,   0,   0,   0,   0,   0,   0
0065DC5D                 db    0,   0,   0,   0,   0,   0,   0,   0
0065DC5D                 db    0,   0,   0,   0,   0,   0,   0,   0
0065DC5D                 db    0,   0,   0,   0,   0,   0,   0,   0
0065DC5D                 db    0,   0,   0,   0,   0,   0,   0,   0
0065DC5D                 db    0,   0,   0,   0,   0,   0,   0,   0
0065DC5D                 db    0,   0,   0,   0,   0,   0,   0,   0
0065DCCD
Spieghiamo un pochino meglio quello che abbiamo fatto qui sopra, innazitutto mettiamo in edi l'indirizzo del buffer dove dovranno essere copiati i valori letti dalla chiave, poi facciamo puntare esi all'indirizzo dove sono presenti i dati della memoria emulata, per far questo usiamo lo stratagemma 'call $+5'.

Quando si effetua una chiamata, viene pushato sullo stack l'indirizzo di ritorno, così quando di incontra un istruzione ret, il processore, riprende l'esecuzione dall'indirizzo puntato in quel momento dal registro esp.

Sucessivamente si passa il controllo all'istruzione che si trova xxxxx bytes distante l'istruzione di 'call xxxxx', ma nel nostro caso xxxxx = 00000 e quindi il controllo viene passato all'istruzione sucessiva (dal punto di vista del flusso di esecuzione è come se la call non fosse mai avvenuta).
Schemino (configurazione stack):

subito prima della chiamata	esp -> 	ultimo parametro pushato
					penultimo parametro
					terzultimo
					...

subito dopo la chiamata		esp ->	indirizzo di ritorno
					ultimo parametro pushato
					penultimo parametro
					terzultimo
					...
quando eseguiamo una istruzione 'pop ...' mettiamo nel registro specificato il valore che in quel momento è in cima allo stack, nel nostro caso l'indirizzo di ritorno.

Dopo correggiamo il valore di esi perchè punti all'indirizzo 'hasp_memory', settiamo in ecx in numero di bytes da copiare e li copiamo con l'istruzione 'repe movsb', a questo punto rimettiamo in edx l'indirizzo del buffer su cui abbiamo copiato i dati in modo che al ritorno duesto venga rimesso in 'par4', dopodichè azzeriamo eax, ebx e anche ecx che ora (manuale hasp) ha il significato di hasp_status e per dire che è tutto a posto deve valere 0.

Ora non ci resta che mettere (usando Hiew) nella posizione hasp_memory+03 i bytes corrispondenti alla stringa 'STRAND7' per far contento lo Straus, proviamo ad eseguirlo e....

"The password has not been installed or is not valid.
   Would you like to enter the password now?"


Bene, questa hasp gli garba parecchio, solo che vuole anche una password... rompipalle di programmatori che non si fidano delle hasp (a ragione :-))))

Ripendiamo ad analizzare il codice dopo il check della chiave visto prima, vediamo che setta un paio di flag e poi salta direttamente in bocca ad una subroutine mooolto interessante...
006558F6 
006558F6 loc_6558F6:                                       ; CODE XREF: check_hasp+17D.j
006558F6                 mov     eax, [ebp+arg_0]
006558F9                 mov     byte ptr [eax-5], 1
006558FD                 mov     eax, ds:off_742CAC
00655902                 xor     edx, edx
00655904                 mov     [eax], edx
00655906                 jmp     loc_655991
...
00655991 
00655991 loc_655991:                                       ; CODE XREF: check_hasp+1D6.j
00655991                                                   ; check_hasp+223.j ...
00655991                 call    sub_6F43A8
00655996                 test    al, al
00655998                 jnz     short loc_6559A7
0065599A                 mov     eax, [ebp+arg_0]
0065599D                 cmp     byte ptr [eax-5], 0
006559A1                 jnz     loc_65590B
...
0065590B 
0065590B loc_65590B:                                       ; CODE XREF: check_hasp+271.j
0065590B                 mov     eax, ds:off_742CAC
00655910                 cmp     dword ptr [eax], 0
00655913                 jnz     short loc_65592D
vediamo che, se la sub_6F43A8 ritorna in al un valore diverso da 0, allora si prosegue, altrimenti controlla i flag che prima aveva settato ad 1 ed a 0 e se questi non sono cambiati veniamo spediti al messaggio di errore
0065592D 
0065592D loc_65592D:                                       ; CODE XREF: check_hasp+1E3.j
0065592D                 push    0
0065592F                 mov     cx, ds:word_655BF8
00655936                 mov     dl, 3
00655938                 mov     eax, offset aThePasswordHas ; "The password has not been installed or "...
0065593D                 call    @Dialogs@MessageDlg$qqrx17System@AnsiString19...
rinominiamo la sub_6F43A8 come check_pwd (dal momento che decide se la password è valida o meno...) e diamogli un occhiatina veloce, notiamo subito che oltre ad essere smodatamente lunga vi sono due switch tra 13 valori, ed una call che legge dal buffer hasp per generare il serial, sul quale viene, a sua volta, calcolata la password.

Ciò avviene all'inizio (ovviamente) di questa subroutine, con una chiamata alla 'sub_6F3F1C' che noi rinominiamo 'make_serial' qui :
006F43A8 
006F43A8 check_pwd       proc near                         ; CODE XREF: sub_652B48+181.p
006F43A8                                                   ; check_hasp+24B.p ...
006F43A8 
006F43A8 var_28          = tbyte ptr -28h
006F43A8 var_1C          = dword ptr -1Ch
006F43A8 var_18          = dword ptr -18h
006F43A8 var_13          = byte ptr -13h
006F43A8 var_12          = word ptr -12h
006F43A8 var_10          = dword ptr -10h
006F43A8 var_C           = dword ptr -0Ch
006F43A8 var_8           = dword ptr -8
006F43A8 var_1           = byte ptr -1
006F43A8 
006F43A8                 push    ebp
006F43A9                 mov     ebp, esp
...
006F43D3                 lea     eax, [ebp+var_C]
006F43D6                 call    make_serial               ; calcola il serial
006F43DB                 mov     eax, offset unk_76A6C8
e dentro make serial ci troviamo alcune chiamate per ottenere la WinDir il numero di serie della partizione e qualche carattere dal buffer hasp :
006F3F54                 push    0FFh                      ; size
006F3F59                 lea     eax, [ebp+WinDir]
006F3F5F                 push    eax                       ; lpBuffer
006F3F60                 call    j_GetWindowsDirectoryA
...
006F3F92                 mov     ebx, 1
006F3F97 
006F3F97 take_8_from_hasp:                                 ; CODE XREF: make_serial+96.j
006F3F97                 mov     eax, esi
006F3F99                 call    @System@UniqueString$qqrr17System@AnsiString 
006F3F9E                 lea     edx, [ebx+0Fh]
006F3FA1                 mov     ecx, ds:lp_hasp_buffer
006F3FA7                 mov     dl, [ecx+edx]             ; legge dal buffer hasp
006F3FAA                 mov     [eax+ebx-1], dl           ; a partire dalla pos. buffer+10h
006F3FAE                 inc     ebx
006F3FAF                 cmp     ebx, 9                    ; prende otto caratteri
006F3FB2                 jnz     short take_8_from_hasp
006F3FB4                 mov     ebx, 1
006F3FB9                 mov     edi, ds:lp_hasp_buffer
006F3FBF 
006F3FBF take_3_from_hasp:                                 ; CODE XREF: make_serial+B8.j
006F3FBF                 mov     eax, esi
006F3FC1                 call    @System@UniqueString$qqrr17System@AnsiString 
006F3FC6                 lea     edx, [ebx+15h]
006F3FC9                 mov     cl, [edi]                 ; legge dall'inizio del buffer hasp
006F3FCB                 mov     [eax+edx-1], cl           ; li piazza in store address + 15h
006F3FCF                 inc     ebx
006F3FD0                 inc     edi
006F3FD1                 cmp     ebx, 4                    ; prende tre caratteri
006F3FD4                 jnz     short take_3_from_hasp
006F3FD6                 mov     eax, esi
006F3FD8                 call    @System@UniqueString$qqrr17System@AnsiString 
006F3FDD                 mov     edx, ds:lp_hasp_buffer
006F3FE3                 mov     dl, [edx+0Ah]             ; legge 1 byte dal buffer hasp pos. buffer+0Ah
006F3FE6                 mov     [eax+14h], dl             ; e lo mette in store address + 14h
006F3FE9                 mov     eax, 4
006F3FEE                 call    @System@@GetMem$qqrv      ; System __linkproc__ GetMem(void)
006F3FF3                 mov     [ebp+var_8], eax
006F3FF6                 mov     eax, [ebp+var_8]
006F3FF9                 xor     edx, edx
006F3FFB                 mov     [eax], edx
...
006F411E                 push    eax
006F411F                 call    j_GetVolumeInformationA
006F4124                 mov     eax, [ebp+var_8]
006F4127                 mov     eax, [eax]
006F4129                 cdq
006F412A                 xor     eax, edx
006F412C                 sub     eax, edx
006F412E                 cdq
006F412F                 mov     [ebp+volume_sn], eax
...
006F424D 
006F424D loc_6F424D:                                       ; CODE XREF: make_serial+35F.j
006F424D                 inc     ebx
006F424E                 lea     eax, [ebp+var_484]
006F4254                 mov     edx, offset aThisisthestran ; "ThisIsTheStrand7SystemWW"
006F4259                 mov     dl, [edx+ebx-1]
006F425D                 call    Pchar_to_Lstr_1
006F4262                 mov     edx, [ebp+var_484]
006F4268                 lea     eax, [ebp+var_4]
006F426B                 call    @System@@LStrCat$qqrv     ; System __linkproc__ LStrCat(void)
006F4270 
006F4270 loc_6F4270:                                       ; CODE XREF: make_serial+32F.j
006F4270                 mov     eax, [ebp+var_4]
006F4273                 call    strLen
006F4278                 cmp     eax, 18h		   ; Se non siamo ancora a 24 char
006F427B                 jl      short loc_6F424D	   ; prendili da "ThisIsTheStrand..." 
Qui oltre alle manie di grandezza dei programmatori, vengono letti alcuni bytes dal buffer hasp vi dico subito che negli 8 bytes alla posizione buffer+10h potete mettere quello che volete, questa sarà la prima parte del vostro serial, così come nel byte alla posizione buffer+0Ah, mentre i primi tre bytes devono essere una delle stringhe identificative dei vari distributori del programma : GDC (per la G+D Computing) HSH (per la H.S.H. srl) IUK, o JCD scegliete voi...

io ho messo questi valori :
	buffer+0x00 : 'HSH'
	buffer+0x0A : 'X'
	buffer+0x10 : 'HASPFUCK'
ed ho ottenuto questo serial :
'HASPFUCK CEOM CBUG PGHX XHSH'


mentre dalla posizione buffer+40h in poi ci vanno i dati dell'intestatario :
	buffer+0x40 : 'UIC - 2002 (by haec_est)'
ma dicevamo dei 2 switch, ecco il primo :
006F44E1 loc_6F44E1:                                       ; CODE XREF: check_pwd+368.j
006F44E1                 lea     eax, [ebp+var_8]
006F44E4                 mov     edx, [ebp+var_C]
006F44E7                 call    @System@@LStrLAsg$qqrv    ; System __linkproc__ LStrLAsg(void)
006F44EC                 movsx   eax, bx                   ; switch 13 cases 
006F44EF                 cmp     eax, 0Ch
006F44F2                 ja      loc_6F46C6                ; default
006F44F8                 jmp     ds:off_6F44FF[eax*4]      ; switch jump
...
006F4533 
006F4533 loc_6F4533:                                       ; CODE XREF: check_pwd+150.j
006F4533                                                   ; DATA XREF: check_pwd+157.o
006F4533                 mov     eax, ds:dword_76A6CC      ; case 0x1
006F4538                 push    eax
006F4539                 mov     al, [ebp+var_13]
006F453C                 push    eax
006F453D                 lea     eax, [ebp+var_8]
006F4540                 mov     ecx, ds:dword_76A6D0
006F4546                 mov     dl, byte ptr ds:dword_6F48F8
006F454C                 call    sub_6F38F0
006F4551                 jmp     loc_6F46C6                ; default
006F4556
la cosa strana è che per il 'case 0x1' e per tutti gli altri il codice è lo stesso, l'unica variazione è il byte pasato in dl alla sub_6F38F0, osservando questi byte vediamo che sono un array così composto :
  1, 0, 2, 4, 9, 0Ah, 0Ch, 11h, 12h, 14h, 19h, 1Ah, 1Ch
sarà mica 'na specie di funzione di hash ? mah...

dopo, invece troviamo questo meraviglioso esempio di programmazione in c++ su come implementare uno schema di protezione (d)efficente ;-P
006F46C6 loc_6F46C6:                                       ; CODE XREF: check_pwd+14A.j
006F46C6                                                   ; check_pwd+150.j ...
006F46C6                 lea     eax, [ebp+var_18]         ; default
006F46C9                 mov     edx, edi
006F46CB                 call    Pchar_to_str
006F46D0                 mov     eax, [ebp+var_18]
006F46D3                 mov     edx, [ebp+var_8]
006F46D6                 call    @System@@LStrCmp$qqrv     ; System __linkproc__ LStrCmp(void)
006F46DB                 jnz     short loc_6F46E1
006F46DD                 mov     [ebp+var_12], bx
006F46E1 
006F46E1 loc_6F46E1:                                       ; CODE XREF: check_pwd+333.j
006F46E1                 cmp     bx, 1
006F46E5                 jz      short loc_6F46FE
006F46E7                 lea     eax, [ebp+var_18]
006F46EA                 mov     edx, edi
006F46EC                 call    Pchar_to_str
006F46F1                 mov     eax, [ebp+var_18]
006F46F4                 mov     edx, [ebp+var_8]
006F46F7                 call    @System@@LStrCmp$qqrv     ; System __linkproc__ LStrCmp(void)
006F46FC                 jnz     short loc_6F470B
006F46FE 
006F46FE loc_6F46FE:                                       ; CODE XREF: check_pwd+33D.j
006F46FE                 mov     eax, offset dword_76A6C4
006F4703                 mov     edx, [ebp+var_8]
006F4706                 call    @System@@LStrAsg$qqrv     ; System __linkproc__ LStrAsg(void)
006F470B 
006F470B loc_6F470B:                                       ; CODE XREF: check_pwd+354.j
006F470B                 inc     ebx
006F470C                 cmp     bx, 0Dh
006F4710                 jnz     loc_6F44E1                ; inizio switch
006F4716                 lea     eax, [ebp+var_18]
006F4719                 mov     edx, edi
006F471B                 call    Pchar_to_str
006F4720                 mov     edx, [ebp+var_18]
006F4723                 mov     eax, ds:dword_76A6C4
006F4728                 call    @System@@LStrCmp$qqrv     ; System __linkproc__ LStrCmp(void)
006F472D                 setz    [ebp+var_1]		   ; qui decide se la password è valida
006F4731                 cmp     [ebp+var_1], 0
006F4735                 jz      short loc_6F4791
non so a voi, a me tutti quei LStrCmp() stanno urlando : 'BPX !!! BPX !!!! BPX !!!!' :-) e come non ascoltarli ??? ...dopo lo famo.

ma per il momento andiamo avanti, ancora più sotto troviamo il secondo switch, che a differenza del primo non calcola un bel niente, ma si limita a settare dei flag (per il tipo di licenza).
006F47B3 
006F47B3 loc_6F47B3:                                       ; CODE XREF: check_pwd+401.j
006F47B3                 movsx   eax, [ebp+var_12]         ; switch 13 cases 
006F47B7                 cmp     eax, 0Ch
006F47BA                 ja      loc_6F48B6                ; default
006F47C0                 jmp     ds:off_6F47C7[eax*4]      ; switch jump
...
006F47FB 
006F47FB loc_6F47FB:                                       ; CODE XREF: check_pwd+418.j
006F47FB                                                   ; DATA XREF: check_pwd+41F.o
006F47FB                 mov     eax, ds:off_742E28        ; case 0x1
006F4800                 mov     dl, byte ptr ds:dword_6F48F8
006F4806                 mov     [eax], dl
006F4808                 jmp     loc_6F48B6                ; default
e poi ritorna così :
006F48E0 
006F48E0 loc_6F48E0:                                       ; DATA XREF: check_pwd+516.o
006F48E0                 mov     al, [ebp+var_1]	   ; se pwd = ok -> al = 1
006F48E3                 pop     edi
006F48E4                 pop     esi
006F48E5                 pop     ebx
006F48E6                 mov     esp, ebp
006F48E8                 pop     ebp
006F48E9                 retn
006F48E9 check_pwd       endp
quindi che decide se la password è valida o meno, è la chiamata a LStrCmp() fatta all'indirizzo 006F4728 ed è proprio qui che dopo andremo a mettere il nostro BPX :-)
Break due to BPX #001B:006F4728  (ET=112.69 milliseconds)

:d eax L 18
0023:01A36F8C 56445743  5045544F  5957525A  4A554E48      CWDVOTEPZRWYHNUJ
0023:01A36F9C 52455450  5041545A  00744400  0000002A      PTERZTAP.Dt.*...
proviamola ... funziona !!!! o quasi, infatti se provate a creare un nuovo documento e poi andate sul menù dei solutori, ce ne sono solamente tre di abilitati, quindi spostiamo il bpx sulla LStrCmp() precedente all'indirizzo 006F46F7 :
Break due to BPX #001B:006F46F7  (ET=110.92 milliseconds)
...
:dd edx L 18
0023:01A36FDC 4D5A4B43  524C4C43  46474D52  5049584F      CKZMCLLRRMGFOXIP
0023:01A36FEC 4F444F42  4B49465A  00744400  00744478      BODOZFIK.Dt.xDt.
questa è solo una delle 13 possibili ,noi le vogliamo tutte, quindi :
:bpe 0
BPX #001B:006F46F7  DO "? bl;dd edx L 18;x;"
ed otteniamo :
00000003	CJHKBQBGRGFNZFLJXCWHYPTO
00000004	CBSKTYJUOWBDWCZBRNABMPEV
00000005	CLANDMMSIDXGPYJQCPTEPSXO
00000006	CGEHODYNYDCKWCYCQNABMPEV
00000007	CLCUNYJUOGLNGMTOUYTGOIZO
00000008	CPERHQQWMHBKTCNUGTYJUKNP
00000009	CMKNUJETEJIQCIECAPJKBSWR
0000000A	CGXPINOZTLQIBHOQWSFQBUJA
0000000B	CUTGWLLRRMGPIRCPBOCNYBQH
0000000C	CLJMDSDSDSRPBHNRVSFQBUJA
L'ultima (ma non solo) abilita tutti i solutori quindi direi che si pone come candidata ideale al nostro auto-keygen, quindi andiamo a modificare un po' di codice :
006F46F1 8B 45 E8                       mov     eax, [ebp+var_18]
006F46F4 8B 55 F8                       mov     edx, [ebp+var_8]
006F46F7 E8 80 FB D0 FF                 call    @System@@LStrCmp$qqrv
lo facciamo diventare :
006F46F1                 jmp     loc_65DCD0                ; da qui saltiamo alla fine 
006F46F1                                                   ; della routine hasp_emu
006F46F6 ; ---------------------------------------------------------------------------
006F46F6                 nop
006F46F7                 call    @System@@LStrCmp$qqrv    
ora andiamo all'indirizzo 65dcd0 e facciamo in modo che la password venga copiata negli appunti in modo che poi l'utente possa fare un paste nella edit del form di richiesta.

Ci servono un po' di informazioni su come funziona la clipboard, per questo ci viene in aiuto il mitico win32.hlp ... ci dice che per mettere dei dati negli appunti si devono chiamare nell'ordine OpenClipboard, SetClipboardData, CloseClipboard, e che ci serve pure GlobalAlloc... eccole :
HGLOBAL GlobalAlloc(

    UINT uFlags,	// object allocation attributes 
    DWORD dwBytes 	// number of bytes to allocate  
   );

BOOL OpenClipboard(

    HWND hWndNewOwner 	// handle to window opening clipboard  
   );

HANDLE SetClipboardData(

    UINT uFormat,	// clipboard format  
    HANDLE hMem 	// data handle 
   );

BOOL CloseClipboard(VOID);
ora ci servono gli indirizzi delle funzioni (fortunatamente, lo straus le importa tutte e quattro così non ci tocca romperci con GetProcAddress :-P )
00407BE0 j_GlobalAlloc
00408078 j_CloseClipboard
004083F8 j_OpenClipboard
00408498 j_SetClipboardData
e per finire i valori dei flags, questi li peschiamo dai files winbase.h e winuser.h, spero che nessuno mi venga a chiedere dove trovarli !!!
Winbase.h :

/* Global Memory Flags */
#define GMEM_FIXED          0x0000
#define GMEM_MOVEABLE       0x0002
#define GMEM_NOCOMPACT      0x0010
#define GMEM_NODISCARD      0x0020
#define GMEM_ZEROINIT       0x0040
#define GMEM_MODIFY         0x0080
#define GMEM_DISCARDABLE    0x0100

Winuser.h :

/*
 * Predefined Clipboard Formats
 */
#define CF_TEXT             1
#define CF_BITMAP           2
#define CF_METAFILEPICT     3
#define CF_SYLK             4
#define CF_DIF              5
mescoliamo bene il tutto, per almeno un minuto a fuoco lento (altrimenti si brucia :-p), ed otteniamo :
0065DCD0 ; ---------------------------------------------------------------------------
0065DCD0 
0065DCD0 set_clipboard_pwd:                      ; CODE XREF: sub_6F43A8+349.j
0065DCD0                 cmp     bl, 0Ch         ; controlliamo che sia la pwd giusta
0065DCD3                 jnz     short not_last  ; vogliamo l'ultima (ebx=0x0C)
0065DCD3                                         ; 
0065DCD5                 push    esi             ; salviamo i registri che usiamo
0065DCD6                 push    edi
0065DCD7                 push    ecx             ; 
0065DCD7                                         ; si comincia
0065DCD7                                         ; 
0065DCD8                 push    19h             ; allochiamo 24+1 bytes  (ascii string)
0065DCDA                 push    40h             ; GMEM_FIXED | GMEM_ZEROINIT
0065DCDC                 call    j_GlobalAlloc   ; 
0065DCDC                                         ; ora copiamo la pwd
0065DCDC                                         ; 
0065DCE1                 mov     esi, [ebp-8]    ; pwd in esi
0065DCE4                 mov     edi, eax        ; hMem in edi
0065DCE6                 xor     ecx, ecx
0065DCE8                 mov     cl, 18h         ; inizializza il contatore ecx
0065DCEA                 repne movsb             ; copia la pwd nella mem. allocata
0065DCEA                                         ; 
0065DCEC                 push    eax             ; salva eax, ci servirà dopo
0065DCEC                                         ; 
0065DCED                 push    0               ; parametro hWndNewOwner
0065DCEF                 call    j_OpenClipboard
0065DCF4                 test    eax, eax        ; controlla che la clipboard sia disponibile
0065DCF4                                         ; 
0065DCF6                 pop     eax             ; ripristina lo stack
0065DCF6                                         ; 
0065DCF7                 jz      short no_clipboard
0065DCF9                 push    eax             ; parametro hMem
0065DCFA                 push    1               ; parametro uFormat
0065DCFC                 call    j_SetClipboardData
0065DD01                 call    j_CloseClipboard
0065DD06 
0065DD06 no_clipboard:                           ; CODE XREF: CODE:0065DCF7.j
0065DD06                 pop     ecx             ; ripristina i registri usati
0065DD07                 pop     edi
0065DD08                 pop     esi
0065DD09 
0065DD09 not_last:                               ; CODE XREF: CODE:0065DCD3.j
0065DD09                 mov     eax, [ebp-18h]  ; ripristiniamo le istruzioni sostituite
0065DD0C                 mov     edx, [ebp-8]
0065DD0F                 jmp     loc_6F46F7      ; e ritorniamo a dove eravamo rimasti...
0065DD0F ; ---------------------------------------------------------------------------
poi andiamo a modificare il messaggio di 'password errata' che si trova qui :
00655C04 aThePasswordHas db 'The password has not been installed or is not valid.',0Dh,'W'
00655C04                                                   ; DATA XREF: check_hasp+208.o
00655C04                 db 'ould you like to enter the password now?',0
per avvisare l'utente che che la password è presente negli appunti, mettiamoci qualcosa del tipo :
00655C04 aThePasswordHas db 'The password has been copied into clipboard,',0Dh,'when prom'
00655C04                                                   ; DATA XREF: check_hasp+208.o
00655C04                 db 'pted just press Ctrl+V. ',0Dh,0Dh,'Continue ?    ',0
e per quanto riguarda lo Straus siamo a posto...

Ora proviamolo, creiamo un elemento beam, e lanciamo il solutore... SORPRESA !!!

"Cannot find the Straus7 Hardware Lock.
Please insert the Hardware Lock in the parallel port."


Ok, nessun problema, evidentemente da qualche parte in qualche altro file, c'è un altro controllo, cerchiamolo ... apriamo tutti i files nella cartella con hiew e cerchiamo la firma del signor hasp :-P la troviamo in Slvpanel.dll.

Decompiliamola con ida, mettiamoci qualche nome quà e la, e cerchiamone le references, vediamo che la hasp() viene chiamata solo da due locazioni, qui :
0044D495                 xor     ecx, ecx
0044D497                 mov     edx, 12Ch
0044D49C                 mov     eax, 1
0044D4A1                 call    hasp
0044D4A6
e qui...
0044D511                 xor     ecx, ecx
0044D513                 mov     edx, 12Ch
0044D518                 mov     eax, 5
0044D51D                 call    hasp
0044D522
il service 5 lo abbiamo già visto (HaspStatus) mentre il service 1 lo andiamo a vedere sul solito manuale hasp :

Service 1: IsHasp


Description	Check whether any HASP is connected to the computer.

Relevant Keys	HASP4 without memory, HASP4 M1, HASP4 M4, HASP4 Time

Syntax		hasp (Service, SeedCode, LptNum, Password1, Password2, Par1, Par2, Par3, Par4)

Parameters Used

	Service 	1	   
	LptNum 		Value indicating the port to search for the HASP.
			Recommended value: 0 (see “Specifying the Port” in Chapter 6
			for a list of possible values).

Return Values

	Par1 		HASP found – A value indicating whether a HASP was
			found
			• 0 – no HASP is connected to the computer
			• 1 – a HASP is connected to the computer				 
	Par3 		Status - A code indicating the status of the operation (for
			possible values, see Chapter 11).

Comments

• Service 1, IsHasp, is a service used to check if any HASP is
connected to your computer. 

• Always use IsHasp in conjunction with other HASP API services. 

• To verify that the correct HASP (with your developer code) is connected, use
Service 61, HaspDecodeData. For a simple presence check, use Service 5, HaspStatus.



ora scommetto che state già pensando a come emularlo sovrascrivendo la routine haspreg, come avevamo fatto prima... SBAGLIATO !!!

Se date un occhiata all'imagebase di questa dll vedrete che 'sti incompetenti di programmatori l'hanno impostato a 0x00400000 e quindi la dll, non appena il s.o. tenta di caricarla in memoria si accorge che a quell'indirizzo c'è già l'immagine dell'eseguibile straus7.exe e che quindi la dll và rilocata.

Embhè... direte voi, a noi cosa ce ne importa ? Ce ne importa eccome perche tutti gli indirizzi presenti verranno sovrascritti a runtime dal s.o. e se al posto di qualche indirizzo ci avevamo messo delle nostre istruzioni queste subiranno la stessa sorte, e come probabile conseguenza il programma crasherà :-(

Osserviamo la routine haspreg() :
00451892                   ; ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦ S U B R O U T I N E ¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦¦
00451892                   
00451892                   
00451892                   haspreg         proc near               ; CODE XREF: hasp+33.p
00451892 60                                pusha
00451893 BE E2 31 45 00                    mov     esi, offset dword_4531E2
00451898 83 3E FF                          cmp     dword ptr [esi], 0FFFFFFFFh
0045189B 74 10                             jz      short loc_4518AD
0045189D 60                                pusha
0045189E BE D7 2E 45 00                    mov     esi, offset dword_452ED7
004518A3 FF 16                             call    dword ptr [esi]
004518A5 BE D6 31 45 00                    mov     esi, offset unk_4531D6
004518AA 89 06                             mov     [esi], eax
004518AC 61                                popa
004518AD                   
004518AD                   loc_4518AD:                             ; CODE XREF: haspreg+9.j
004518AD BE E2 31 45 00                    mov     esi, offset dword_4531E2
004518B2 83 3E FF                          cmp     dword ptr [esi], 0FFFFFFFFh
004518B5 0F 85 52 01 00 00                 jnz     loc_451A0D
004518BB B8 63 2D 45 00                    mov     eax, offset unk_452D63
004518C0 50                                push    eax
004518C1 E8 8E 49 FB FF                    call    j_GetModuleHandleA
004518C6 BE 70 2D 45 00                    mov     esi, offset dword_452D70
004518CB 89 06                             mov     [esi], eax
004518CD E8 0C F1 FF FF                    call    sub_4509DE
004518D2 BE D7 2E 45 00                    mov     esi, offset dword_452ED7
004518D7 FF 16                             call    dword ptr [esi]
004518D9 BE D6 31 45 00                    mov     esi, offset unk_4531D6
004518DE 89 06                             mov     [esi], eax
004518E0 BE 17 2C 45 00                    mov     esi, offset unk_452C17
004518E5 C7 06 94 00 00 00                 mov     dword ptr [esi], 94h
004518EB 56                                push    esi
004518EC BE 93 2F 45 00                    mov     esi, offset dword_452F93
004518F1 FF 16                             call    dword ptr [esi]
004518F3 BE 17 2C 45 00                    mov     esi, offset unk_452C17
004518F8 8B 46 10                          mov     eax, [esi+10h]
004518FB BE E2 31 45 00                    mov     esi, offset dword_4531E2
00451900 89 06                             mov     [esi], eax
00451902
...
i bytes evidenziati sono offset e cambieranno a runtime, quindi non li possiamo toccare, e quindi niente emulazione... non qui almeno :-P

Scriveremo la nostra hasp_emu alla fine della sezione code, sui bytes che sono usati dal compilatore per allineare le sezioni (tanto i servizi da emulare non richiedono molto spazio).

Apriamo hiew e vediamo che la sezione .CODE finisce a 00451BED, noi saltiamo qualche byte per sicurezza, e cominciamo a buttar giù codice dall'indirizzo 00451C00 :
00451C00          hasp_emu        proc near
00451C00 80 FF 05                 cmp     bh, 5
00451C03 74 0E                    jz      short hasp_status
00451C05 80 FF 01                 cmp     bh, 1
00451C08 74 17                    jz      short is_hasp
00451C0A 33 C0                    xor     eax, eax
00451C0C 33 DB                    xor     ebx, ebx
00451C0E 33 C9                    xor     ecx, ecx
00451C10 33 D2                    xor     edx, edx
00451C12 C3                       retn
00451C13          ; ---------------------------------------------------------------------------
00451C13          
00451C13          hasp_status:                            ; CODE XREF: hasp_emu+3.j
00451C13 33 C0                    xor     eax, eax
00451C15 40                       inc     eax
00451C16 33 DB                    xor     ebx, ebx
00451C18 43                       inc     ebx
00451C19 33 C9                    xor     ecx, ecx
00451C1B 41                       inc     ecx
00451C1C 33 D2                    xor     edx, edx
00451C1E B2 66                    mov     dl, 66h
00451C20 C3                       retn
00451C21          ; ---------------------------------------------------------------------------
00451C21          
00451C21          is_hasp:                                ; CODE XREF: hasp_emu+8.j
00451C21 33 C0                    xor     eax, eax
00451C23 40                       inc     eax             ; par1 = 1 (hasp found)
00451C24 33 DB                    xor     ebx, ebx
00451C26 33 C9                    xor     ecx, ecx        ; par3 = 0 (status ok)
00451C28 33 D2                    xor     edx, edx
00451C2A C3                       retn
00451C2A          hasp_emu        endp
00451C2A
adesso non ci resta che deviare il flusso del programma dalla funzione hasp() quando chiama la funzione haspreg() e fargli invece chiamare la nostra hasp_emu(), quindi all'indirizzo 0044F9FF scriviamo :
.0044F9FF: E8FC210000		call	.000451C00
proviamo... FUNZIONA !!!! :-p

Il nostro compito potrebbe terminare qui ma abbiamo lasciato un conto in sospeso col driver hasp, vi ricordate che 'sto fetente si è installato senza chiedere il permesso ?

Sistemiamolo...

Andiamo a dare un occhio al file Hinstd.dll è una dll da 1Mb che esporta tante belle funzioni, a noi interessano solo le tre importate dallo straus :
0077994C ; 
0077994C ; Imports from HINSTD.dll
0077994C ; 
0077994C HIGetInfo       dd 37B9EAh                        ; DATA XREF: j_HIGetInfo.r
00779950 HDDInstall      dd 37B9F6h                        ; DATA XREF: j_HDDInstall.r
00779954 HILastError     dd 37BA04h                        ; DATA XREF: j_HILastError.r
Sempre dalla documentazione fornitaci gentilmente dalla hasp vediamo (nel file hapi.h) che la funzione HIGetInfo ci svela tante cose sul driver installato, sempre che ce ne sia uno di installato.
DWORD PASCAL HIGetInfo(
	LPHDDINFO	IpHDDInfo,	//Address of structure for device information
	LPHDDSYSINFO	IpHDDSysInfo	//Address of structure for operating system information
);

typedef struct _HDDINFO
{
    CHAR 	HIVersion [HI_ITEM_LEN]; 
    CHAR 	HIInstallDate [HI_DATE_STR_LEN];
    DWORD 	HIComputerType;
    CHAR 	HIProcessorType [HI_ITEM_LEN];
    DWORD 	DriverStatus;
    DWORD 	LoaderStatus;
    CHAR 	HIImagePath [HI_ITEM_LEN];
    DWORD 	HIPortMode;
    DWORD 	HILPT1;
    DWORD 	HILPT2;
    DWORD 	HILPT3;
    CHAR 	HIServerPath [HI_ITEM_LEN];
    CHAR 	HIServerSwitches [HI_ITEM_LEN];
    DWORD 	HIKeySearch;
} HDDINFO;

/*
 *  Information structures definition.
 */
#define HI_ITEM_LEN         128
#define HI_DATE_STR_LEN     20

/*
 * The device driver is or is not installed.
 */
#define HI_DRIVER_INSTALLED 		1
#define HI_DRIVER_NOT_INSTALLED 	0
#define HL_DRIVER_INSTALLED 		2

typedef struct _HDDSYSINFO
{
    DWORD HISystemType;
} HDDSYSINFO;

#define VER_PLATFORM_WIN32s 		0
#define VER_PLATFORM_WIN32_WINDOWS 	1
#define VER_PLATFORM_WIN32_NT 		2
Cerhiamo le references per la funzione j_HIGetInfo, Ida ce ne dà solamente una, a 00643FE1, mettiamoci un bpx quando softice poppa eseguiamo la chiamata (F10), ora sempre da softice diamo un occhiata all'offset della struttura lpHDDInfo (00767384) :
0023:00767384 39392E33  00390000  00000000  00000000      3.99..9.........
0023:00767394 00000000  00000000  00000000  00000000      ................
...
0023:00767404 756E614A  20797261  202C3332  30303220      January 23,  200
0023:00767414 00000032  00000001  00363858  00000036      2.......X86.6...
...
0023:00767494 00000000  00000000  00000001  00000000      ................
0023:007674A4 5C3F3F5C  575C3A43  544E4E49  7379535C      \??\C:\WINNT\Sys
0023:007674B4 336D6574  72645C32  72657669  61485C73      tem32\drivers\Ha
0023:007674C4 746E7073  7379732E  005C0000  00720064      spnt.sys..\.d.r.
0023:007674D4 00760069  00720065  005C0073  00610048      i.v.e.r.s.\.H.a.
0023:007674E4 00700073  0074006E  0073002E  00730079      s.p.n.t...s.y.s.
...
0023:00767524 00000008  00000378  00000000  00000000      ....x...........
...
0023:00767624 00000000  00000000  00000000  00000000      ................
se ne ricavano info molto interessanti, tornando in ida (mettendo i nomi ai parametri, creando le due strutture HDDInfo e HDDSysInfo, ed aggiungendo qualche commento) otteniamo :
00643FD5                 xor     ebx, ebx
00643FD7                 push    offset lpHDDSysInfo
00643FDC                 push    offset lpHDDInfo
00643FE1                 call    j_HIGetInfo
00643FE6                 test    eax, eax
00643FE8                 jnz     short Hi_error
00643FEA                 cmp     ds:lpHDDInfo.DriverStatus, 1
00643FF1                 jnz     short _ritorna
00643FF3                 mov     ebx, offset lpHDDInfo     ; "3.99"
00643FF8                 lea     eax, [ebp+var_C]
00643FFB                 mov     edx, ebx
00643FFD                 call    unknown_libname_17
00644002                 mov     eax, [ebp+var_C]
00644005                 call    strLen
0064400A                 mov     esi, eax                  ; num. char = 4
0064400C                 test    esi, esi
0064400E                 jle     short loc_644037
00644010                 mov     ebx, 1
00644015 
00644015 loop_cut_dot:                                     ; CODE XREF: drv_HIGetInfo+7D.j
00644015                 mov     eax, [ebp+var_C]
00644018                 cmp     byte ptr [eax+ebx-1], 2Eh ; 2Eh = "."
0064401D                 jnz     short next
0064401F                 lea     eax, [ebp+var_C]
00644022                 call    @System@UniqueString$qqrr17System@AnsiString 
00644027                 mov     edx, ds:ptr_comma         ; ","
0064402D                 mov     dl, [edx]
0064402F                 mov     [eax+ebx-1], dl           ; "3.99" -> "3,99"
00644033 
00644033 next:                                             ; CODE XREF: drv_HIGetInfo+65.j
00644033                 inc     ebx
00644034                 dec     esi
00644035                 jnz     short loop_cut_dot
00644037 
00644037 loc_644037:                                       ; CODE XREF: drv_HIGetInfo+56.j
00644037                 mov     eax, [ebp+var_C]          ; eax -> "3,99"
0064403A                 call    @Sysutils@StrToFloat$qqrx17System@AnsiString ; converte "3,99" -> 3.99
0064403F                 fstp    [ebp+var_8]               ; store 3.99
00644042                 wait
00644043                 fld     ds:dt_3_74                ; load 3.74
00644049                 fcomp   [ebp+var_8]               ; li confronta
0064404C                 fnstsw  ax
0064404E                 sahf
0064404F                 setbe   bl                        ; imposta bl = 1
00644052                 jmp     short _ritorna
00644054
vediamo che oltre a controllare se il driver è o meno installato, verifica anche che la stringa relativa alla versione, trasformata in float dia un numero maggiore di 3.74, e poi se ne ritorna tutto felice... senza mai chiamare le funzioni HDDInstall e HILastError.

Noi faremo in modo ghe la HIGetInfo restituisca sempre
HDDInfo.HIVersion = "3.99"
HDDInfo.DriverStatus = HI_DRIVER_INSTALLED
Solo che a noi dà fastidio anche solo il fatto di avere sull'hard disk una dll hasp, e quinidi sotituiamo la Hinstd.dll (1.064.468 bytes) con una nostra versione più compatta, che naturalmente codiamo in assembler.
; hinstd.asm - hasp driver install emulated dll
;
.386
.model flat, stdcall
option casemap:none

include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib

.code

LibMain proc, hInstDLL:DWORD, reason:DWORD, unused:DWORD
        .if reason == DLL_PROCESS_ATTACH
		mov eax, TRUE
	.endif
        ret
LibMain endp


HIGetInfo proc, lpHDDInfo:DWORD, lpHDDSysInfo:DWORD

	mov eax, lpHDDInfo					; HDDInfo.HIVersion
	mov dword ptr[eax], 39392E33h				; "3.99"
	add eax, 118h						; HDDInfo.DriverStatus
	mov dword ptr[eax], 01h					; HI_DRIVER_INSTALLED

	xor eax,eax
	ret
HIGetInfo endp


HDDInstall proc, arg_0:DWORD					; preso da ida (hinstd.dll)
	xor eax,eax
	ret
HDDInstall endp


HILastError proc, arg_0:DWORD, arg_4:DWORD, arg_8:DWORD 	; preso da ida (hinstd.dll)
	xor eax,eax
	ret
HILastError endp

end LibMain
notate che manca la sezione .data (perchè non ci serve :-P), ora compiliamola a dovere con il buon vecchio masm (lo uso perchè ho imparato con i tutes di iczelion) :
ml.exe /c /coff hinstd.asm
poi creiamo il file dll.def (ci serve per dire al link quali funzioni dovrà esportare la nostra dll) :
LIBRARY hinstd
EXPORTS 
HIGetInfo
HDDInstall
HILastError
e quindi creiamo la dll :
link.exe /SUBSYSTEM:WINDOWS /DLL /DEF:dll.def hinstd.obj
et voillà... la nostra hinstd.dll è pronta ed occupa solamente 2.560 bytes (se volete proprio sprecare spazio ci potete mettere una sezione .res con il vostro copyright :-P)

copiamola nella dir dov'è installato lo straus, eliminiamo il driver hasp, prima però dobbiamo fermare il servizio (io lavoro sotto nt) con :
net stop haspnt
net stop hardlock
e finalmente cancelliamo haspnt.sys, hardlock.sys e relative voci dal registro, quindi proviamo lo straus... funziona che è una meraviglia.

Ultimo tocco di classe, gli diamo una passata di upx che non fa mai male e ne riduce i tempi di caricamento :
upx --best nomefile.ext

                     Ultimate Packer for eXecutables
            Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001
UPX 1.20w        Markus F.X.J. Oberhumer & Laszlo Molnar        May 23rd 2001

        File size         Ratio      Format      Name
   --------------------   ------   -----------   -----------
    430592 ->    156672   36.38%    win32/pe     st7solve.exe
   5372416 ->   1464320   27.25%    win32/pe     straus7.exe
    851968 ->    339968   39.90%    win32/pe     assembl7.dll
     61440 ->     28160   45.83%    win32/pe     init7.dll
     86016 ->     39424   45.83%    win32/pe     lf90wiod.dll
    131343 ->     58368   44.43%    win32/pe     mesh2d.dll
     45056 ->     20480   45.45%    win32/pe     saveres.dll
    458752 ->    203776   44.42%    win32/pe     Slvpanel.dll
    176128 ->     57344   32.55%    win32/pe     solve7io.dll
    146432 ->     57856   39.51%    win32/pe     st6list.dll
a questo punto lo straus funziona, non rompe le palle con le password, non ci sono tracce di files hasp sull'hard disk ed il tutto occupa solo 2,5Mb :
 Il volume nell'unità ...
 Numero di serie del volume: ...

 Directory di ...\Straus 7\bin

07/02/02  00.00         <DIR>          .
07/02/02  00.00         <DIR>          ..
07/02/02  00.00                339.968 assembl7.dll
07/02/02  00.00                  2.560 hinst.dll
07/02/02  00.00                 28.160 init7.dll
07/02/02  00.00                 39.424 lf90wiod.dll
07/02/02  00.00                 58.368 mesh2d.dll
07/02/02  00.00                 20.480 saveres.dll
07/02/02  00.00                203.776 Slvpanel.dll
07/02/02  00.00                 57.344 solve7io.dll
07/02/02  00.00                 57.856 st6list.dll
07/02/02  00.00                  2.168 st7g.__x
07/02/02  00.00                  1.802 st7h.__x
07/02/02  00.00                  2.168 st7i.__x
07/02/02  00.00                  2.228 st7j.__x
07/02/02  00.00                156.672 st7solve.exe
07/02/02  00.00              1.464.320 straus7.exe
              17 File      2.437.294 byte
possiamo ritenerci soddisfatti, e darci una pacca sulla schiena, dopotutto abbiamo fatto un buon lavoro :-P

saluti, haec_est

Note finali

Note finali e qualche ringraziamento (questo campo non è obbligatorio) appunto...

Disclaimer

...vorrei ricordare che il software va comprato e  non rubato, dovete registrare il vostro prodotto dopo il periodo di valutazione. Non mi ritengo responsabile per eventuali danni causati al vostro computer determinati dall'uso improprio di questo tutorial. Questo documento aiuta a comprendere lo sforzo immane che ogni singolo programmatore ha dovuto portare avanti per fornire ai rispettivi consumatori i migliori prodotti possibili.