Straus 7 |
||
Data |
by "haec_est" |
|
07/02/2002 |
Published by Quequero |
|
|
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." |
.... |
|
.... |
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 :-)))
Introduzione |
Tools usati |
ida pro 4.04 | il disassembler (o è una lei ???) |
hiew 6.55 | naturalmente qualsiasi altra versione andava bene |
softice (x nt) 4.01 | idem... |
masm32 v6.15 | come sopra... |
win32.hlp | questo ci serve per avere info sulle api di windows |
URL o FTP del programma |
Notizie sul programma |
Essay |
Haspbc.asm
hasp (Service, SeedCode, LptNum, Password1, Password2, Par1, Par2, Par3, Par4)
Contenuto registri
xrefs hasp()
Service 5: HaspStatus
Service 50: ReadBlock
Service 5: expected return values
Emulazione haspreg() - prima parte
Emulazione haspreg() - parte seconda
Service 1: IsHasp 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.
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:
(...)
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 :
diamo un occhiata al file haspbc.asm :
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.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; 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
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
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 :
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
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 :
eax seed / lp_buffer (bh >= 50)
bh service
bl lptnum
ecx password 1
edx password 2
edi par1
esi par2
vediamo subito che la funzione hasp() viene chiamata con due seed diversi e servizi che
sono propri di chiavi di tipo diverso, infatti
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, ...)
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 :
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.
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ì :
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 :
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 :-))
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 :
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.
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
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...
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'.
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
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 :
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